Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Order Amount calculation integration #4427

Merged
merged 4 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 68 additions & 40 deletions app/components/public/ticket-list.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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;
Expand All @@ -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)
Expand Down Expand Up @@ -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');
Expand All @@ -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');
}

}
Expand All @@ -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);
});
Expand All @@ -163,29 +170,50 @@ 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() {
if (event.code === 'Enter') {
this.send('applyPromotionalCode');
this.set('code', this.promotionalCode);
}
},

onChangeDonation() {
debounce(this, () => this.send('updateOrderAmount'), this.tickets, 250);
}
},
didInsertElement() {
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/currency-symbol.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
4 changes: 4 additions & 0 deletions app/styles/partials/utils.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,7 @@
.mb-8 {
margin-bottom: 2rem;
}

.m-0 {
margin: 0 !important;
}
114 changes: 56 additions & 58 deletions app/templates/components/public/ticket-list.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<thead class="full-width">
<tr>
<th>{{t 'Type'}}</th>
<th class="four wide">{{t 'Sales Ends'}}</th>
<th class="ui three wide single line">{{t 'Ticket Price'}}</th>
<th class="three wide"></th>
<th class="ui four wide single line">{{t 'Price'}}</th>
<th class="one wide">{{t 'Quantity'}}</th>
<th class="ui right aligned two wide">{{t 'Subtotal'}}</th>
</tr>
Expand All @@ -27,62 +27,53 @@
{{#if ticket.isDescriptionVisible}}
<small class="ui gray-text tiny">{{ticket.description}}</small>
{{/if}}
<div>
<small class="ui gray-text small">Sale ends on {{moment-format ticket.salesEndsAt 'ddd, DD MMMM YY, h:mm A'}}</small>
</div>
</td>
<td>{{moment-format ticket.salesEndsAt 'ddd, DD MMMM YY, h:mm A'}}</td>
{{#if ticket.discount}}
<td>
<div id="{{ticket.id}}_price" class="strike text">
{{this.eventCurrency}} {{format-money ticket.price}}
</div>
<div id="{{ticket.id}}_discount">
{{this.eventCurrency}} {{format-money (sub ticket.price ticket.discount)}}
{{#if this.taxInfo}}
{{#if this.showTaxIncludedMessage}}
<small class="ui gray-text small">
{{t 'includes'}} {{this.eventCurrency}} {{format-money ticket.includedTaxAmount}}
</small>
{{else}}
<small class="ui gray-text small">
+ {{this.eventCurrency}}
{{format-money (add (sub ticket.ticketPriceWithTax ticket.price) ticket.discount)}}
</small>
{{/if}}
<div>
<small class="ui gray-text tiny aligned right">({{this.taxInfo.name}})</small>
</div>
{{/if}}
</div>
</td>
{{else}}
<td id="{{ticket.id}}_price">
{{#if (eq ticket.type 'donation') }}
<div class="field">
<td></td>
<td id="{{ticket.id}}_price">
{{#if (eq ticket.type 'donation') }}
<div class="field m-0">
<div class="ui labeled input">
<label for="i_{{ticket.id}}_price" class="ui label">{{currency-symbol this.eventCurrency}}</label>
<Input
id="i_{{ticket.id}}_price"
@type="number"
@name={{ticket.id}} placeholder={{t "Enter Donation"}}
@min={{ticket.minPrice}}
@max={{ticket.maxPrice}}
@value={{ticket.price}} /><br>
@value={{ticket.price}}
@keyUp={{action "onChangeDonation"}} />
</div>
</div>
{{else}}
<div class="{{if ticket.discount 'strike text'}}">
{{currency-symbol this.eventCurrency}} {{format-money ticket.price}}
</div>
{{#if ticket.discount}}
<div>
{{currency-symbol this.eventCurrency}} {{format-money (sub ticket.price ticket.discount)}}
</div>
{{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'))}}
<p>
{{#if this.taxInfo.isTaxIncludedInPrice}}
<small class="ui gray-text small">
{{t 'includes'}} {{this.eventCurrency}} {{format-money ticket.includedTaxAmount}}
{{t 'includes'}} {{currency-symbol this.eventCurrency}} {{format-money ticket.includedTaxAmount}}
</small>
{{else}}
<small class="ui gray-text small">
+ {{this.eventCurrency}} {{format-money (sub ticket.ticketPriceWithTax ticket.price)}}
+ {{currency-symbol this.eventCurrency}} {{format-money (sub ticket.ticketPriceWithTax ticket.price)}}
</small>
{{/if}}
<div>
<span>
<small class="ui gray-text tiny aligned right">({{this.taxInfo.name}})</small>
</div>
{{/if}}
</td>
{{/if}}
</span>
</p>
{{/if}}
</td>
<td>
<div class="field">
<UiDropdown
Expand All @@ -105,28 +96,35 @@
</div>
</td>
<td id='{{ticket.id}}_subtotal' class="ui right aligned">
{{#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}}
</td>
</tr>
{{/unless}}
{{/each}}
</tbody>
<tfoot class="full-width">
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th colspan="4">
<div class="ui right aligned small primary icon">
{{t 'Total'}}: {{this.eventCurrency}} {{format-money this.total}}
</div>
</th>
<td></td>
<td></td>
<td colspan="3">
<table class="ui small" style="float: right;">
<tbody>
{{#if orderAmount.tax}}
<tr><td>{{t 'Sub-Total'}}</td><td>{{currency-symbol this.eventCurrency}} {{format-money this.orderAmount.sub_total}}</td></tr>
<tr>
<td>
<div>{{t 'Tax'}} ({{this.orderAmount.tax.percent}}% {{this.orderAmount.tax.name}})</div>
{{#if orderAmount.tax.included}}
<small class="ui gray-text small">Included in Total</small>
{{/if}}
</td>
<td>{{currency-symbol this.eventCurrency}} {{this.orderAmount.tax.amount}}</td>
</tr>
{{/if}}
<tr><td>{{t 'Total'}}</td><td>{{currency-symbol this.eventCurrency}} {{format-money this.orderAmount.total}}</td></tr>
</tbody>
</table>
</td>
</tr>
</tfoot>
</table>
Expand Down
2 changes: 1 addition & 1 deletion app/utils/dictionary/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export const paymentCurrencies: PaymentCurrency[] = [
{
paypal : true,
code : 'USD',
symbol : 'US$',
symbol : '$',
name : 'United States dollar',
stripe : true,
alipay : true,
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/helpers/currency-symbol-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(), '$');
});
});