From eeb15a97870ab3458abc64f34682ab32921fcf72 Mon Sep 17 00:00:00 2001 From: Areeb Jamal Date: Tue, 2 Jun 2020 07:22:24 +0530 Subject: [PATCH] feat: Order Amount calculation integration (#4427) --- app/components/public/ticket-list.js | 108 +++++++++++------ app/helpers/currency-symbol.js | 2 +- app/styles/partials/utils.scss | 4 + .../components/public/ticket-list.hbs | 114 +++++++++--------- app/utils/dictionary/payment.ts | 2 +- .../helpers/currency-symbol-test.js | 2 +- 6 files changed, 131 insertions(+), 101 deletions(-) diff --git a/app/components/public/ticket-list.js b/app/components/public/ticket-list.js index 4caefbd017f..efa102a3a33 100644 --- a/app/components/public/ticket-list.js +++ b/app/components/public/ticket-list.js @@ -1,5 +1,6 @@ import Component from '@ember/component'; import { computed } from '@ember/object'; +import { debounce } from '@ember/runloop'; import FormMixin from 'open-event-frontend/mixins/form'; import { inject as service } from '@ember/service'; import { sumBy, merge } from 'lodash-es'; @@ -10,6 +11,8 @@ export default Component.extend(FormMixin, { promotionalCodeApplied: false, + orderAmount: null, + isUnverified: computed('session.isAuthenticated', 'authManager.currentUser.isVerified', function() { return this.session.isAuthenticated && !this.authManager.currentUser.isVerified; @@ -25,21 +28,31 @@ export default Component.extend(FormMixin, { } }), - showTaxIncludedMessage: computed('taxInfo.isTaxIncludedInPrice', function() { - if (this.taxInfo !== null) { - return (this.taxInfo.isTaxIncludedInPrice); - } - return false; - }), - accessCodeTickets : A(), discountedTickets : A(), invalidPromotionalCode: false, - tickets: computed(function() { - return this.data.sortBy('position'); + tickets: computed('orderAmount', function() { + const ticketMap = {}; + if (this.orderAmount) { + this.orderAmount.tickets.forEach(ticket => { + ticketMap[ticket.id] = ticket; + }); + } + + return this.data.sortBy('position').map(ticket => { + const ticketExtra = ticketMap[ticket.id]; + + if (ticketExtra) { + ticket.set('subTotal', ticketExtra.sub_total); + ticket.set('discountInfo', ticketExtra.discount); + } + + return ticket; + }); }), + hasTicketsInOrder: computed('tickets.@each.orderQuantity', function() { return sumBy(this.tickets.toArray(), ticket => (ticket.orderQuantity || 0) @@ -68,6 +81,19 @@ export default Component.extend(FormMixin, { ticket => ((ticket.price || 0) - (ticket.discount || 0)) * (ticket.orderQuantity || 0) ); }), + + orderAmountInput: computed('tickets.@each.price', 'order.tickets.@each.orderQuantity', 'order.discountCode', function() { + + return { + tickets: this.order.tickets.toArray().map(ticket => ({ + id : ticket.id, + quantity : ticket.orderQuantity, + price : ticket.price + })), + 'discount-code': this.order.get('discountCode.id') + }; + }), + actions: { async togglePromotionalCode(queryParam) { this.toggleProperty('enterPromotionalCode'); @@ -82,24 +108,16 @@ export default Component.extend(FormMixin, { this.set('code', null); this.order.set('accessCode', undefined); this.order.set('discountCode', undefined); + this.tickets.forEach(ticket => { + ticket.set('discount', null); + }); this.accessCodeTickets.forEach(ticket => { ticket.set('isHidden', true); this.tickets.removeObject(ticket); }); - this.discountedTickets.forEach(ticket => { - let taxRate = ticket.get('event.tax.rate'); - let ticketPrice = ticket.get('price'); - if (taxRate && !this.showTaxIncludedMessage) { - let ticketPriceWithTax = ticketPrice * (1 + taxRate / 100); - ticket.set('ticketPriceWithTax', ticketPriceWithTax); - } else if (taxRate && this.showTaxIncludedMessage) { - let includedTaxAmount = (taxRate * ticketPrice) / (100 + taxRate); - ticket.set('includedTaxAmount', includedTaxAmount); - } - ticket.set('discount', 0); - }); this.accessCodeTickets.clear(); this.discountedTickets.clear(); + this.send('updateOrderAmount'); } } @@ -126,22 +144,11 @@ export default Component.extend(FormMixin, { const discountCode = await this.store.queryRecord('discount-code', { eventIdentifier: this.event.id, code: this.promotionalCode, include: 'event,tickets' }); const discountCodeEvent = await discountCode.event; if (this.currentEventIdentifier === discountCodeEvent.identifier) { - const discountType = discountCode.type; - const discountValue = discountCode.value; this.order.set('discountCode', discountCode); const tickets = await discountCode.tickets; tickets.forEach(ticket => { - const ticketPrice = ticket.price; - const taxRate = ticket.get('event.tax.rate'); - const discount = discountType === 'amount' ? Math.min(ticketPrice, discountValue) : ticketPrice * (discountValue / 100); + const discount = discountCode.type === 'amount' ? Math.min(ticket.price, discountCode.value) : ticket.price * (discountCode.value / 100); ticket.set('discount', discount); - if (taxRate && !this.showTaxIncludedMessage) { - const ticketPriceWithTax = (ticketPrice - ticket.discount) * (1 + taxRate / 100); - ticket.set('ticketPriceWithTax', ticketPriceWithTax); - } else if (taxRate && this.showTaxIncludedMessage) { - const includedTaxAmount = (taxRate * (ticketPrice - discount)) / (100 + taxRate); - ticket.set('includedTaxAmount', includedTaxAmount); - } this.discountedTickets.addObject(ticket); this.set('invalidPromotionalCode', false); }); @@ -163,22 +170,39 @@ export default Component.extend(FormMixin, { this.set('promotionalCodeApplied', true); this.set('promotionalCode', 'Promotional code applied successfully'); } - this.order.set('amount', this.total); - + this.send('updateOrderAmount'); }, - updateOrder(ticket, count) { + + async updateOrder(ticket, count) { ticket.set('orderQuantity', count); - this.order.set('amount', this.total); - if (!this.total) { - this.order.set('amount', 0); - } if (count > 0) { this.order.tickets.addObject(ticket); } else { + ticket.set('subTotal', null); + ticket.set('discountInfo', null); if (this.order.tickets.includes(ticket)) { this.order.tickets.removeObject(ticket); } } + + this.send('updateOrderAmount'); + }, + + async updateOrderAmount() { + if (this.shouldDisableOrderButton) { + this.set('orderAmount', null); + return; + } + + try { + this.set('orderAmount', await this.loader.post('/orders/calculate-amount', this.orderAmountInput)); + this.order.amount = this.orderAmount.total; + } catch (e) { + console.error('Error while calculating order amount', e); + this.notify.error(e.response.errors[0].detail, { + id: 'order-amount-error' + }); + } }, handleKeyPress() { @@ -186,6 +210,10 @@ export default Component.extend(FormMixin, { this.send('applyPromotionalCode'); this.set('code', this.promotionalCode); } + }, + + onChangeDonation() { + debounce(this, () => this.send('updateOrderAmount'), this.tickets, 250); } }, didInsertElement() { diff --git a/app/helpers/currency-symbol.js b/app/helpers/currency-symbol.js index e095869bd54..834b0628b0b 100644 --- a/app/helpers/currency-symbol.js +++ b/app/helpers/currency-symbol.js @@ -4,7 +4,7 @@ import { paymentCurrencies } from 'open-event-frontend/utils/dictionary/payment' export function currencySymbol(params) { const currency = find(paymentCurrencies, ['code', params[0]]); - return currency ? currency.symbol : 'UNKNOWN'; + return currency ? currency.symbol : params[0]; } export default Helper.helper(currencySymbol); diff --git a/app/styles/partials/utils.scss b/app/styles/partials/utils.scss index e0e103935e8..9b159475112 100644 --- a/app/styles/partials/utils.scss +++ b/app/styles/partials/utils.scss @@ -35,3 +35,7 @@ .mb-8 { margin-bottom: 2rem; } + +.m-0 { + margin: 0 !important; +} diff --git a/app/templates/components/public/ticket-list.hbs b/app/templates/components/public/ticket-list.hbs index cc043c7a8df..c1c0377db7b 100644 --- a/app/templates/components/public/ticket-list.hbs +++ b/app/templates/components/public/ticket-list.hbs @@ -4,8 +4,8 @@ {{t 'Type'}} - {{t 'Sales Ends'}} - {{t 'Ticket Price'}} + + {{t 'Price'}} {{t 'Quantity'}} {{t 'Subtotal'}} @@ -27,62 +27,53 @@ {{#if ticket.isDescriptionVisible}} {{ticket.description}} {{/if}} +
+ Sale ends on {{moment-format ticket.salesEndsAt 'ddd, DD MMMM YY, h:mm A'}} +
- {{moment-format ticket.salesEndsAt 'ddd, DD MMMM YY, h:mm A'}} - {{#if ticket.discount}} - -
- {{this.eventCurrency}} {{format-money ticket.price}} -
-
- {{this.eventCurrency}} {{format-money (sub ticket.price ticket.discount)}} - {{#if this.taxInfo}} - {{#if this.showTaxIncludedMessage}} - - {{t 'includes'}} {{this.eventCurrency}} {{format-money ticket.includedTaxAmount}} - - {{else}} - - + {{this.eventCurrency}} - {{format-money (add (sub ticket.ticketPriceWithTax ticket.price) ticket.discount)}} - - {{/if}} -
- ({{this.taxInfo.name}}) -
- {{/if}} -
- - {{else}} - - {{#if (eq ticket.type 'donation') }} -
+ + + {{#if (eq ticket.type 'donation') }} +
+
+
+ @value={{ticket.price}} + @keyUp={{action "onChangeDonation"}} /> +
+
+ {{else}} +
+ {{currency-symbol this.eventCurrency}} {{format-money ticket.price}} +
+ {{#if ticket.discount}} +
+ {{currency-symbol this.eventCurrency}} {{format-money (sub ticket.price ticket.discount)}}
- {{else}} - {{this.eventCurrency}} {{format-money ticket.price}} {{/if}} - {{#if (and this.taxInfo (not-eq ticket.type 'free'))}} - {{#if this.showTaxIncludedMessage}} + {{/if}} + {{#if (and this.taxInfo (not-eq ticket.type 'free'))}} +

+ {{#if this.taxInfo.isTaxIncludedInPrice}} - {{t 'includes'}} {{this.eventCurrency}} {{format-money ticket.includedTaxAmount}} + {{t 'includes'}} {{currency-symbol this.eventCurrency}} {{format-money ticket.includedTaxAmount}} {{else}} - + {{this.eventCurrency}} {{format-money (sub ticket.ticketPriceWithTax ticket.price)}} + + {{currency-symbol this.eventCurrency}} {{format-money (sub ticket.ticketPriceWithTax ticket.price)}} {{/if}} -

+ ({{this.taxInfo.name}}) -
- {{/if}} - - {{/if}} + +

+ {{/if}} +
- {{#if this.taxInfo}} - {{this.eventCurrency}} - {{format-money (mult (sub ticket.ticketPriceWithTax ticket.discount) ticket.orderQuantity)}} - {{else}} - {{this.eventCurrency}} {{format-money (mult (sub ticket.price ticket.discount) ticket.orderQuantity)}} - {{/if}} + {{currency-symbol this.eventCurrency}} {{format-money ticket.subTotal}} {{/unless}} @@ -118,15 +104,27 @@ - - - - - -
- {{t 'Total'}}: {{this.eventCurrency}} {{format-money this.total}} -
- + + + + + + {{#if orderAmount.tax}} + + + + + + {{/if}} + + +
{{t 'Sub-Total'}}{{currency-symbol this.eventCurrency}} {{format-money this.orderAmount.sub_total}}
+
{{t 'Tax'}} ({{this.orderAmount.tax.percent}}% {{this.orderAmount.tax.name}})
+ {{#if orderAmount.tax.included}} + Included in Total + {{/if}} +
{{currency-symbol this.eventCurrency}} {{this.orderAmount.tax.amount}}
{{t 'Total'}}{{currency-symbol this.eventCurrency}} {{format-money this.orderAmount.total}}
+ diff --git a/app/utils/dictionary/payment.ts b/app/utils/dictionary/payment.ts index fefc5064dc3..a6afaf811b5 100644 --- a/app/utils/dictionary/payment.ts +++ b/app/utils/dictionary/payment.ts @@ -135,7 +135,7 @@ export const paymentCurrencies: PaymentCurrency[] = [ { paypal : true, code : 'USD', - symbol : 'US$', + symbol : '$', name : 'United States dollar', stripe : true, alipay : true, diff --git a/tests/integration/helpers/currency-symbol-test.js b/tests/integration/helpers/currency-symbol-test.js index ca43da1c6ec..0cb26f4f86f 100644 --- a/tests/integration/helpers/currency-symbol-test.js +++ b/tests/integration/helpers/currency-symbol-test.js @@ -11,7 +11,7 @@ module('Integration | Helper | currency-symbol', function(hooks) { await render(hbs`{{currency-symbol inputValue}}`); - assert.equal(this.element.textContent.trim(), 'US$'); + assert.equal(this.element.textContent.trim(), '$'); }); });