mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-28 20:53:24 +10:00
Showing commission (and calculating subtotal and total)
This commit is contained in:
parent
a59c01486a
commit
d70b1f83cf
11 changed files with 286 additions and 32 deletions
|
|
@ -1,7 +1,7 @@
|
|||
import { types } from '../../util/sdkLoader';
|
||||
import BookingInfo from './BookingInfo';
|
||||
|
||||
export const Empty = {
|
||||
export const BeforeTX = {
|
||||
component: BookingInfo,
|
||||
props: {
|
||||
unitPrice: new types.Money(10, 'USD'),
|
||||
|
|
@ -9,3 +9,36 @@ export const Empty = {
|
|||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
},
|
||||
};
|
||||
|
||||
export const TXCustomerSide = {
|
||||
component: BookingInfo,
|
||||
props: {
|
||||
unitPrice: new types.Money(10, 'USD'),
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
totalPrice: new types.Money(20, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
export const TXProviderSide = {
|
||||
component: BookingInfo,
|
||||
props: {
|
||||
unitPrice: new types.Money(10, 'USD'),
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
subtotalPrice: new types.Money(20, 'USD'),
|
||||
commission: new types.Money(2, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
export const TXNoCalculation = {
|
||||
component: BookingInfo,
|
||||
props: {
|
||||
unitPrice: new types.Money(10, 'USD'),
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
subtotalPrice: new types.Money(20, 'USD'),
|
||||
commission: new types.Money(2, 'USD'),
|
||||
totalPrice: new types.Money(18, 'USD'),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,39 +13,80 @@ import { types } from '../../util/sdkLoader';
|
|||
import css from './BookingInfo.css';
|
||||
|
||||
const BookingInfoComponent = props => {
|
||||
const { bookingStart, bookingEnd, className, intl, totalPrice, unitPrice } = props;
|
||||
const { bookingStart, bookingEnd, className, commission, intl, subtotalPrice, totalPrice, unitPrice } = props;
|
||||
|
||||
const hasSelectedDays = bookingStart && bookingEnd;
|
||||
const bookingPeriod = hasSelectedDays
|
||||
? <FormattedMessage
|
||||
id="BookingInfo.bookingPeriod"
|
||||
values={{
|
||||
bookingStart: intl.formatDate(bookingStart),
|
||||
bookingEnd: intl.formatDate(bookingEnd),
|
||||
}}
|
||||
/>
|
||||
: '';
|
||||
const nightCount = hasSelectedDays ? moment(bookingEnd).diff(moment(bookingStart), 'days') : null;
|
||||
const nightCountMessage = nightCount
|
||||
? <FormattedMessage id="BookingInfo.nightCount" values={{ count: nightCount }} />
|
||||
: null;
|
||||
const hasSelectedNights = bookingStart && bookingEnd;
|
||||
|
||||
// If there's not enough info, we print empty container
|
||||
if (!hasSelectedNights) {
|
||||
return <div className={classNames(css.container, className)} />;
|
||||
}
|
||||
|
||||
const bookingPeriod = <FormattedMessage
|
||||
id="BookingInfo.bookingPeriod"
|
||||
values={{
|
||||
bookingStart: intl.formatDate(bookingStart),
|
||||
bookingEnd: intl.formatDate(bookingEnd),
|
||||
}}
|
||||
/>;
|
||||
|
||||
// diff gives night count between dates
|
||||
const nightCount = moment(bookingEnd).diff(moment(bookingStart), 'days');
|
||||
const nightCountMessage = <FormattedMessage id="BookingInfo.nightCount" values={{ count: nightCount }} />;
|
||||
|
||||
const currencyConfig = config.currencyConfig;
|
||||
const subUnitDivisor = currencyConfig.subUnitDivisor;
|
||||
|
||||
const unitPriceAsNumber = convertMoneyToNumber(unitPrice, subUnitDivisor);
|
||||
const formattedUnitPrice = intl.formatNumber(unitPriceAsNumber, currencyConfig);
|
||||
const calculatedTotalPriceAsNumber = !totalPrice && hasSelectedDays
|
||||
? new Decimal(unitPriceAsNumber).times(nightCount).toNumber()
|
||||
|
||||
// Subtotal price can be given (when it comes from API)
|
||||
// or calculated: unit price * booked nights
|
||||
const subtotalPriceAsNumber = subtotalPrice
|
||||
? convertMoneyToNumber(subtotalPrice, subUnitDivisor)
|
||||
: new Decimal(unitPriceAsNumber).times(nightCount).toNumber();
|
||||
const formattedSubtotal = commission
|
||||
? intl.formatNumber(subtotalPriceAsNumber, currencyConfig)
|
||||
: null;
|
||||
|
||||
// Total price can be given (when it comes from API) or calculated based on unit price and nights
|
||||
// If commission is passed it will be reduced from sub total.
|
||||
const commissionAsNumber = commission ? convertMoneyToNumber(commission, subUnitDivisor) : 0;
|
||||
const formattedCommission = commission
|
||||
? intl.formatNumber(new Decimal(commissionAsNumber).negated().toNumber(), currencyConfig)
|
||||
: null;
|
||||
|
||||
// Total price can be given (when it comes from API)
|
||||
// or calculated: sub total - commission
|
||||
const totalPriceAsNumber = totalPrice
|
||||
? convertMoneyToNumber(totalPrice, subUnitDivisor)
|
||||
: calculatedTotalPriceAsNumber;
|
||||
: new Decimal(subtotalPriceAsNumber).minus(commissionAsNumber).toNumber();
|
||||
const formattedTotalPrice = totalPriceAsNumber
|
||||
? intl.formatNumber(totalPriceAsNumber, currencyConfig)
|
||||
: null;
|
||||
|
||||
// Sub total is shown if commission is given
|
||||
const subtotalInfo = commission
|
||||
? <div className={css.row}>
|
||||
<div className={css.subtotalLabel}>
|
||||
<FormattedMessage id="BookingInfo.subtotal" />
|
||||
</div>
|
||||
<div className={css.subtotal}>
|
||||
{formattedSubtotal}
|
||||
</div>
|
||||
</div>
|
||||
: null;
|
||||
|
||||
const commisionInfo = commission
|
||||
? <div className={css.row}>
|
||||
<div className={css.commissionLabel}>
|
||||
<FormattedMessage id="BookingInfo.commission" />
|
||||
</div>
|
||||
<div className={css.commission}>
|
||||
{formattedCommission}
|
||||
</div>
|
||||
</div>
|
||||
: null;
|
||||
|
||||
return (
|
||||
<div className={classNames(css.container, className)}>
|
||||
<div className={css.row}>
|
||||
|
|
@ -66,6 +107,8 @@ const BookingInfoComponent = props => {
|
|||
{nightCountMessage}
|
||||
</div>
|
||||
</div>
|
||||
{subtotalInfo}
|
||||
{commisionInfo}
|
||||
<hr className={css.totalDivider} />
|
||||
<div className={css.row}>
|
||||
<div className={css.totalLabel}>
|
||||
|
|
@ -83,7 +126,9 @@ BookingInfoComponent.defaultProps = {
|
|||
bookingStart: null,
|
||||
bookingEnd: null,
|
||||
className: '',
|
||||
subtotalPrice: null,
|
||||
totalPrice: null,
|
||||
commission: null,
|
||||
};
|
||||
|
||||
const { instanceOf, string } = PropTypes;
|
||||
|
|
@ -92,7 +137,9 @@ BookingInfoComponent.propTypes = {
|
|||
bookingStart: instanceOf(Date),
|
||||
bookingEnd: instanceOf(Date),
|
||||
className: string,
|
||||
commission: instanceOf(types.Money),
|
||||
intl: intlShape.isRequired,
|
||||
subtotalPrice: instanceOf(types.Money),
|
||||
totalPrice: instanceOf(types.Money),
|
||||
unitPrice: instanceOf(types.Money).isRequired,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { types } from '../../util/sdkLoader';
|
|||
import BookingInfo from './BookingInfo';
|
||||
|
||||
describe('BookingInfo', () => {
|
||||
it('matches snapshot', () => {
|
||||
it('pretransaction data matches snapshot', () => {
|
||||
const tree = renderDeep(
|
||||
<BookingInfo
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
|
|
@ -16,4 +16,31 @@ describe('BookingInfo', () => {
|
|||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('customer transaction data matches snapshot', () => {
|
||||
const tree = renderDeep(
|
||||
<BookingInfo
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
bookingStart={new Date(Date.UTC(2017, 3, 14))}
|
||||
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
|
||||
totalPrice={new types.Money(2000, 'USD')}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('provider transaction data matches snapshot', () => {
|
||||
const tree = renderDeep(
|
||||
<BookingInfo
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
bookingStart={new Date(Date.UTC(2017, 3, 14))}
|
||||
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
|
||||
subtotalPrice={new types.Money(2000, 'USD')}
|
||||
commission={new types.Money(200, 'USD')}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
exports[`BookingInfo matches snapshot 1`] = `
|
||||
exports[`BookingInfo customer transaction data matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
|
|
@ -50,3 +50,135 @@ exports[`BookingInfo matches snapshot 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`BookingInfo pretransaction data matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Price per night:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
$10.00
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Booking period:
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
4/14/2017 - 4/16/2017
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
2 nights
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
className={undefined} />
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Total:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
$20.00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`BookingInfo provider transaction data matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Price per night:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
$10.00
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Booking period:
|
||||
</span>
|
||||
<br />
|
||||
<span>
|
||||
4/14/2017 - 4/16/2017
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
2 nights
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Subtotal:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
$20.00
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Charged commission:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
-$2.00
|
||||
</div>
|
||||
</div>
|
||||
<hr
|
||||
className={undefined} />
|
||||
<div
|
||||
className={undefined}>
|
||||
<div
|
||||
className={undefined}>
|
||||
<span>
|
||||
Total:
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className={undefined}>
|
||||
$18.00
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { Avatar, BookingInfo, NamedLink } from '../../components';
|
|||
import css from './SaleDetailsPanel.css';
|
||||
|
||||
const SaleDetailsPanel = props => {
|
||||
const { className, totalPrice, saleState, booking, listing, customer } = props;
|
||||
const { className, subtotalPrice, saleState, booking, listing, customer, commission } = props;
|
||||
const { firstName, lastName } = customer.attributes.profile;
|
||||
const customerName = firstName ? `${firstName} ${lastName}` : '';
|
||||
|
||||
|
|
@ -42,7 +42,8 @@ const SaleDetailsPanel = props => {
|
|||
bookingStart={booking.attributes.start}
|
||||
bookingEnd={booking.attributes.end}
|
||||
unitPrice={unitPrice}
|
||||
totalPrice={totalPrice}
|
||||
commission={commission}
|
||||
subtotalPrice={subtotalPrice}
|
||||
/>
|
||||
: <p className={css.error}>{'priceRequiredMessage'}</p>;
|
||||
|
||||
|
|
@ -66,7 +67,8 @@ const { instanceOf, string } = PropTypes;
|
|||
|
||||
SaleDetailsPanel.propTypes = {
|
||||
className: string,
|
||||
totalPrice: instanceOf(types.Money).isRequired,
|
||||
subtotalPrice: instanceOf(types.Money).isRequired,
|
||||
commission: instanceOf(types.Money).isRequired,
|
||||
saleState: string.isRequired,
|
||||
booking: propTypes.booking.isRequired,
|
||||
listing: propTypes.listing.isRequired,
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ describe('SaleDetailsPanel', () => {
|
|||
it('matches snapshot', () => {
|
||||
const { Money } = types;
|
||||
const props = {
|
||||
commission: null,
|
||||
totalPrice: new Money(16500, 'USD'),
|
||||
commission: new Money(1650, 'USD'),
|
||||
subtotalPrice: new Money(16500, 'USD'),
|
||||
saleState: 'state/preauthorized',
|
||||
booking: createBooking(
|
||||
'booking1',
|
||||
|
|
|
|||
|
|
@ -39,7 +39,13 @@ exports[`SaleDetailsPanel matches snapshot 1`] = `
|
|||
<BookingInfo
|
||||
bookingEnd={2017-06-13T00:00:00.000Z}
|
||||
bookingStart={2017-06-10T00:00:00.000Z}
|
||||
totalPrice={
|
||||
commission={
|
||||
Money {
|
||||
"amount": 1650,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
subtotalPrice={
|
||||
Money {
|
||||
"amount": 16500,
|
||||
"currency": "USD",
|
||||
|
|
|
|||
|
|
@ -19,7 +19,8 @@ export const SalePageComponent = props => {
|
|||
const title = currentListing.attributes.title;
|
||||
|
||||
const detailsProps = {
|
||||
totalPrice: currentTransaction.attributes.total,
|
||||
subtotalPrice: currentTransaction.attributes.total,
|
||||
commission: currentTransaction.attributes.commission,
|
||||
saleState: currentTransaction.attributes.state,
|
||||
listing: currentListing,
|
||||
booking: ensureBooking(currentTransaction.booking),
|
||||
|
|
|
|||
|
|
@ -15,13 +15,18 @@ exports[`SalePage matches snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
className="undefined"
|
||||
commission={
|
||||
Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
customer={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"profile": Object {
|
||||
"firstName": "customer1 first name",
|
||||
"lastName": "customer1 last name",
|
||||
"slug": "customer1-slug",
|
||||
},
|
||||
},
|
||||
"id": UUID {
|
||||
|
|
@ -54,7 +59,7 @@ exports[`SalePage matches snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
saleState="state/preauthorized"
|
||||
totalPrice={
|
||||
subtotalPrice={
|
||||
Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
"BookingInfo.commission": "Charged commission:",
|
||||
"BookingInfo.nightCount": "{count, number} {count, plural, one {night} other {nights}}",
|
||||
"BookingInfo.pricePerDay":"Price per night:",
|
||||
"BookingInfo.subtotal": "Subtotal:",
|
||||
"BookingInfo.total": "Total:",
|
||||
"CheckoutPage.initiateOrderError": "Payment request failed. Please try again.",
|
||||
"CheckoutPage.paymentInfo": "You'll only be charged if your request is accepted by the provider.",
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ export const createTransaction = options => {
|
|||
id: new UUID(id),
|
||||
type: 'transaction',
|
||||
attributes: {
|
||||
commission: null,
|
||||
commission: new Money(100, 'USD'),
|
||||
createdAt: new Date(Date.UTC(2017, 4, 1)),
|
||||
lastTransitionedAt,
|
||||
state,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue