mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-31 02:26:50 +10:00
Merge pull request #257 from sharetribe/payin-payout
Use line items to show the price breakdown
This commit is contained in:
commit
3cf654849e
23 changed files with 542 additions and 170 deletions
|
|
@ -33,7 +33,7 @@
|
|||
"redux-thunk": "^2.2.0",
|
||||
"sanitize.css": "^5.0.0",
|
||||
"sharetribe-scripts": "0.9.2",
|
||||
"sharetribe-sdk": "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a3d2bfcc64e0b91c6626b959c8395fbda39fd0d8",
|
||||
"sharetribe-sdk": "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a086f37c765cf5b6034bc5f4788788237485c863",
|
||||
"source-map-support": "^0.4.14",
|
||||
"url": "^0.11.0"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ const enforceSsl = require('express-enforces-ssl');
|
|||
const path = require('path');
|
||||
const qs = require('qs');
|
||||
const sharetribeSdk = require('sharetribe-sdk');
|
||||
const Decimal = require('decimal.js');
|
||||
const auth = require('./auth');
|
||||
const renderer = require('./renderer');
|
||||
const dataLoader = require('./dataLoader');
|
||||
|
|
@ -89,6 +90,14 @@ app.get('*', (req, res) => {
|
|||
req,
|
||||
res,
|
||||
}),
|
||||
typeHandlers: [
|
||||
{
|
||||
type: sharetribeSdk.types.BigDecimal,
|
||||
customType: Decimal,
|
||||
writer: v => new sharetribeSdk.types.BigDecimal(v.toString()),
|
||||
reader: v => new Decimal(v.value),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
dataLoader
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import Decimal from 'decimal.js';
|
||||
import { types } from '../../util/sdkLoader';
|
||||
import BookingBreakdown from './BookingBreakdown';
|
||||
|
||||
|
|
@ -6,8 +7,16 @@ export const Checkout = {
|
|||
props: {
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
totalPrice: new types.Money(9000, 'USD'),
|
||||
userRole: 'customer',
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
lineTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
],
|
||||
payinTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -16,8 +25,16 @@ export const CustomerOrder = {
|
|||
props: {
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
totalPrice: new types.Money(9000, 'USD'),
|
||||
userRole: 'customer',
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
lineTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
],
|
||||
payinTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -26,9 +43,21 @@ export const ProviderSale = {
|
|||
props: {
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
commission: new types.Money(2000, 'USD'),
|
||||
totalPrice: new types.Money(7000, 'USD'),
|
||||
userRole: 'provider',
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
lineTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
unitPrice: new types.Money(-2000, 'USD'),
|
||||
lineTotal: new types.Money(-2000, 'USD'),
|
||||
},
|
||||
],
|
||||
payoutTotal: new types.Money(7000, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -37,9 +66,21 @@ export const ProviderSaleZeroCommission = {
|
|||
props: {
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
commission: new types.Money(0, 'USD'),
|
||||
totalPrice: new types.Money(7000, 'USD'),
|
||||
userRole: 'provider',
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
lineTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
unitPrice: new types.Money(0, 'USD'),
|
||||
lineTotal: new types.Money(0, 'USD'),
|
||||
},
|
||||
],
|
||||
payoutTotal: new types.Money(9000, 'USD'),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -48,8 +89,20 @@ export const ProviderSaleSingleNight = {
|
|||
props: {
|
||||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 15)),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
commission: new types.Money(2000, 'USD'),
|
||||
totalPrice: new types.Money(2500, 'USD'),
|
||||
userRole: 'provider',
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new types.Money(4500, 'USD'),
|
||||
lineTotal: new types.Money(4500, 'USD'),
|
||||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
unitPrice: new types.Money(-2000, 'USD'),
|
||||
lineTotal: new types.Money(-2000, 'USD'),
|
||||
},
|
||||
],
|
||||
payoutTotal: new types.Money(2500, 'USD'),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,13 +3,12 @@
|
|||
* I.e. dates and other details related to payment decision in receipt format.
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import { FormattedMessage, FormattedHTMLMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import Decimal from 'decimal.js';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { convertMoneyToNumber } from '../../util/currency';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { nightsBetween } from '../../util/dates';
|
||||
|
||||
import css from './BookingBreakdown.css';
|
||||
|
||||
|
|
@ -19,14 +18,23 @@ export const BookingBreakdownComponent = props => {
|
|||
className,
|
||||
bookingStart,
|
||||
bookingEnd,
|
||||
unitPrice,
|
||||
commission,
|
||||
totalPrice,
|
||||
payinTotal,
|
||||
payoutTotal,
|
||||
lineItems,
|
||||
userRole,
|
||||
intl,
|
||||
} = props;
|
||||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
if (userRole === 'customer' && !payinTotal) {
|
||||
throw new Error('payinTotal is required for customer breakdown');
|
||||
}
|
||||
|
||||
if (userRole === 'provider' && !payoutTotal) {
|
||||
throw new Error('payoutTotal is required for provider breakdown');
|
||||
}
|
||||
|
||||
const dateFormatOptions = {
|
||||
weekday: 'short',
|
||||
month: 'long',
|
||||
|
|
@ -50,33 +58,44 @@ export const BookingBreakdownComponent = props => {
|
|||
/>
|
||||
);
|
||||
|
||||
const nightCount = nightsBetween(bookingStart, bookingEnd);
|
||||
const nightPurchase = lineItems.find(item => item.code === 'line-item/night');
|
||||
const providerCommission = lineItems.find(item => item.code === 'line-item/provider-commission');
|
||||
|
||||
const nightCount = nightPurchase.quantity.toFixed();
|
||||
const nightCountMessage = (
|
||||
<FormattedMessage id="BookingBreakdown.nightCount" values={{ count: nightCount }} />
|
||||
<FormattedHTMLMessage id="BookingBreakdown.nightCount" values={{ count: nightCount }} />
|
||||
);
|
||||
|
||||
const currencyConfig = config.currencyConfig;
|
||||
const subUnitDivisor = currencyConfig.subUnitDivisor;
|
||||
|
||||
const unitPriceAsNumber = convertMoneyToNumber(unitPrice, subUnitDivisor);
|
||||
const unitPriceAsNumber = convertMoneyToNumber(nightPurchase.unitPrice, subUnitDivisor);
|
||||
const formattedUnitPrice = intl.formatNumber(unitPriceAsNumber, currencyConfig);
|
||||
|
||||
// If commission is passed it will be shown as a fee already reduces from the total price
|
||||
const commissionAsNumber = commission ? convertMoneyToNumber(commission, subUnitDivisor) : 0;
|
||||
const formattedCommission = commission
|
||||
? intl.formatNumber(new Decimal(commissionAsNumber).negated().toNumber(), currencyConfig)
|
||||
: null;
|
||||
let commissionInfo = null;
|
||||
|
||||
const commissionInfo = (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.commission" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedCommission}</span>
|
||||
</div>
|
||||
if (userRole === 'provider') {
|
||||
const commission = providerCommission.lineTotal;
|
||||
const commissionAsNumber = commission ? convertMoneyToNumber(commission, subUnitDivisor) : 0;
|
||||
const formattedCommission = commission
|
||||
? intl.formatNumber(new Decimal(commissionAsNumber).negated().toNumber(), currencyConfig)
|
||||
: null;
|
||||
|
||||
commissionInfo = (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.commission" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedCommission}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const totalPriceAsNumber = convertMoneyToNumber(
|
||||
userRole === 'customer' ? payinTotal : payoutTotal,
|
||||
subUnitDivisor
|
||||
);
|
||||
|
||||
const totalPriceAsNumber = convertMoneyToNumber(totalPrice, subUnitDivisor);
|
||||
const formattedTotalPrice = totalPriceAsNumber
|
||||
? intl.formatNumber(totalPriceAsNumber, currencyConfig)
|
||||
: null;
|
||||
|
|
@ -95,7 +114,7 @@ export const BookingBreakdownComponent = props => {
|
|||
</span>
|
||||
<span className={css.itemValue}>{nightCountMessage}</span>
|
||||
</div>
|
||||
{commission ? commissionInfo : null}
|
||||
{commissionInfo}
|
||||
<hr className={css.totalDivider} />
|
||||
<div className={css.lineItem}>
|
||||
<div className={css.totalLabel}>
|
||||
|
|
@ -112,10 +131,18 @@ export const BookingBreakdownComponent = props => {
|
|||
BookingBreakdownComponent.defaultProps = {
|
||||
rootClassName: null,
|
||||
className: null,
|
||||
commission: null,
|
||||
payinTotal: null,
|
||||
payoutTotal: null,
|
||||
};
|
||||
|
||||
const { string, instanceOf } = PropTypes;
|
||||
const { string, instanceOf, arrayOf, oneOf, shape } = PropTypes;
|
||||
|
||||
const lineItem = shape({
|
||||
code: string.isRequired,
|
||||
quantity: instanceOf(Decimal),
|
||||
unitPrice: propTypes.money,
|
||||
lineTotal: propTypes.money,
|
||||
});
|
||||
|
||||
BookingBreakdownComponent.propTypes = {
|
||||
rootClassName: string,
|
||||
|
|
@ -123,10 +150,10 @@ BookingBreakdownComponent.propTypes = {
|
|||
|
||||
bookingStart: instanceOf(Date).isRequired,
|
||||
bookingEnd: instanceOf(Date).isRequired,
|
||||
|
||||
unitPrice: propTypes.money.isRequired,
|
||||
totalPrice: propTypes.money.isRequired,
|
||||
commission: propTypes.money,
|
||||
lineItems: arrayOf(lineItem).isRequired,
|
||||
userRole: oneOf(['customer', 'provider']).isRequired,
|
||||
payinTotal: propTypes.money, // required if userRole === customer
|
||||
payoutTotal: propTypes.money, // required if userRole === provider
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import React from 'react';
|
||||
import Decimal from 'decimal.js';
|
||||
import { fakeIntl } from '../../util/test-data';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import { types } from '../../util/sdkLoader';
|
||||
|
|
@ -10,8 +11,16 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
bookingStart={new Date(Date.UTC(2017, 3, 14))}
|
||||
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
totalPrice={new types.Money(2000, 'USD')}
|
||||
payinTotal={new types.Money(2000, 'USD')}
|
||||
userRole="customer"
|
||||
lineItems={[
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new types.Money(2000, 'USD'),
|
||||
unitPrice: new types.Money(1000, 'USD'),
|
||||
},
|
||||
]}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
|
|
@ -23,8 +32,16 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
bookingStart={new Date(Date.UTC(2017, 3, 14))}
|
||||
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
totalPrice={new types.Money(2000, 'USD')}
|
||||
userRole="customer"
|
||||
payinTotal={new types.Money(2000, 'USD')}
|
||||
lineItems={[
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new types.Money(2000, 'USD'),
|
||||
unitPrice: new types.Money(1000, 'USD'),
|
||||
},
|
||||
]}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
|
|
@ -36,9 +53,22 @@ describe('BookingBreakdown', () => {
|
|||
<BookingBreakdownComponent
|
||||
bookingStart={new Date(Date.UTC(2017, 3, 14))}
|
||||
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
|
||||
unitPrice={new types.Money(1000, 'USD')}
|
||||
totalPrice={new types.Money(1800, 'USD')}
|
||||
commission={new types.Money(200, 'USD')}
|
||||
payoutTotal={new types.Money(1800, 'USD')}
|
||||
userRole="provider"
|
||||
lineItems={[
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new types.Money(2000, 'USD'),
|
||||
unitPrice: new types.Money(1000, 'USD'),
|
||||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
lineTotal: new types.Money(-200, 'USD'),
|
||||
unitPrice: new types.Money(-200, 'USD'),
|
||||
},
|
||||
]}
|
||||
intl={fakeIntl}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -32,9 +32,12 @@ exports[`BookingBreakdown customer transaction data matches snapshot 1`] = `
|
|||
</span>
|
||||
<span
|
||||
className={undefined}>
|
||||
<span>
|
||||
* 2 nights
|
||||
</span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "× 2 nights",
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
|
|
@ -89,9 +92,12 @@ exports[`BookingBreakdown pretransaction data matches snapshot 1`] = `
|
|||
</span>
|
||||
<span
|
||||
className={undefined}>
|
||||
<span>
|
||||
* 2 nights
|
||||
</span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "× 2 nights",
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
|
|
@ -146,9 +152,12 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = `
|
|||
</span>
|
||||
<span
|
||||
className={undefined}>
|
||||
<span>
|
||||
* 2 nights
|
||||
</span>
|
||||
<span
|
||||
dangerouslySetInnerHTML={
|
||||
Object {
|
||||
"__html": "× 2 nights",
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
|
|
@ -161,7 +170,7 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = `
|
|||
</span>
|
||||
<span
|
||||
className={undefined}>
|
||||
-2
|
||||
2
|
||||
</span>
|
||||
</div>
|
||||
<hr
|
||||
|
|
|
|||
|
|
@ -10,14 +10,13 @@ import css from './OrderDetailsPanel.css';
|
|||
|
||||
const breakdown = transaction => {
|
||||
const tx = ensureTransaction(transaction);
|
||||
const listing = ensureListing(tx.listing);
|
||||
const booking = ensureBooking(tx.booking);
|
||||
const bookingStart = booking.attributes.start;
|
||||
const bookingEnd = booking.attributes.end;
|
||||
const unitPrice = listing.attributes.price;
|
||||
const totalPrice = tx.attributes.total;
|
||||
const payinTotal = tx.attributes.payinTotal;
|
||||
const lineItems = tx.attributes.lineItems;
|
||||
|
||||
if (!bookingStart || !bookingEnd || !unitPrice || !totalPrice) {
|
||||
if (!bookingStart || !bookingEnd || !payinTotal || !lineItems) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
|
|
@ -25,8 +24,9 @@ const breakdown = transaction => {
|
|||
className={css.receipt}
|
||||
bookingStart={bookingStart}
|
||||
bookingEnd={bookingEnd}
|
||||
unitPrice={unitPrice}
|
||||
totalPrice={totalPrice}
|
||||
payinTotal={payinTotal}
|
||||
lineItems={lineItems}
|
||||
userRole="customer"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,6 +42,6 @@ describe('OrderDetailsPanel', () => {
|
|||
});
|
||||
const panel = shallow(<OrderDetailsPanel transaction={tx} />);
|
||||
const breakdownProps = panel.find(BookingBreakdown).props();
|
||||
expect(breakdownProps.totalPrice).toEqual(new Money(16500, 'USD'));
|
||||
expect(breakdownProps.payinTotal).toEqual(new Money(16500, 'USD'));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -47,17 +47,39 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
|
|||
<BookingBreakdown
|
||||
bookingEnd={2017-06-13T00:00:00.000Z}
|
||||
bookingStart={2017-06-10T00:00:00.000Z}
|
||||
totalPrice={
|
||||
lineItems={
|
||||
Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 16500,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "3",
|
||||
"unitPrice": Money {
|
||||
"amount": 5500,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
payinTotal={
|
||||
Money {
|
||||
"amount": 16500,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
unitPrice={
|
||||
Money {
|
||||
"amount": 5500,
|
||||
"currency": "USD",
|
||||
}
|
||||
} />
|
||||
userRole="customer" />
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,59 +1,35 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedDate, FormattedMessage } from 'react-intl';
|
||||
import Decimal from 'decimal.js';
|
||||
import classNames from 'classnames';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { types } from '../../util/sdkLoader';
|
||||
import { createSlug } from '../../util/urlHelpers';
|
||||
import { ensureListing, ensureTransaction, ensureBooking, ensureUser } from '../../util/data';
|
||||
import { convertMoneyToNumber, convertUnitToSubUnit } from '../../util/currency';
|
||||
import config from '../../config';
|
||||
import { Avatar, BookingBreakdown, NamedLink } from '../../components';
|
||||
|
||||
import css from './SaleDetailsPanel.css';
|
||||
|
||||
// TODO: This is a temporary function to calculate the booking
|
||||
// price. This should be removed when the API supports dry-runs and we
|
||||
// can take the total price from the transaction itself.
|
||||
const estimatedProviderTotalPrice = (customerTotalPrice, commission) => {
|
||||
const { subUnitDivisor, currency } = config.currencyConfig;
|
||||
if (customerTotalPrice.currency !== currency || commission.currency !== currency) {
|
||||
throw new Error('Transaction total or commission currency does not match marketplace currency');
|
||||
}
|
||||
|
||||
const numericCustomerTotalPrice = convertMoneyToNumber(customerTotalPrice, subUnitDivisor);
|
||||
const numericCommission = convertMoneyToNumber(commission, subUnitDivisor);
|
||||
const numericTotalPrice = new Decimal(numericCustomerTotalPrice)
|
||||
.minus(numericCommission)
|
||||
.toNumber();
|
||||
|
||||
return new types.Money(convertUnitToSubUnit(numericTotalPrice, subUnitDivisor), currency);
|
||||
};
|
||||
|
||||
const breakdown = transaction => {
|
||||
const tx = ensureTransaction(transaction);
|
||||
const listing = ensureListing(tx.listing);
|
||||
const booking = ensureBooking(tx.booking);
|
||||
const bookingStart = booking.attributes.start;
|
||||
const bookingEnd = booking.attributes.end;
|
||||
const unitPrice = listing.attributes.price;
|
||||
const customerTotalPrice = tx.attributes.total;
|
||||
const commission = tx.attributes.commission;
|
||||
const payinTotal = tx.attributes.payinTotal;
|
||||
const payoutTotal = tx.attributes.payoutTotal;
|
||||
const lineItems = tx.attributes.lineItems;
|
||||
|
||||
if (!bookingStart || !bookingEnd || !unitPrice || !customerTotalPrice || !commission) {
|
||||
if (!bookingStart || !bookingEnd || !payinTotal || !payoutTotal || !lineItems) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const totalPrice = estimatedProviderTotalPrice(customerTotalPrice, commission);
|
||||
|
||||
return (
|
||||
<BookingBreakdown
|
||||
className={css.breakdown}
|
||||
bookingStart={bookingStart}
|
||||
bookingEnd={bookingEnd}
|
||||
unitPrice={unitPrice}
|
||||
totalPrice={totalPrice}
|
||||
commission={commission}
|
||||
payinTotal={payinTotal}
|
||||
payoutTotal={payoutTotal}
|
||||
lineItems={lineItems}
|
||||
userRole="provider"
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -46,6 +46,6 @@ describe('SaleDetailsPanel', () => {
|
|||
const breakdownProps = panel.find(BookingBreakdown).props();
|
||||
|
||||
// Total price for the provider should be transaction total minus the commission.
|
||||
expect(breakdownProps.totalPrice).toEqual(new Money(15500, 'USD'));
|
||||
expect(breakdownProps.payoutTotal).toEqual(new Money(15500, 'USD'));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -46,23 +46,45 @@ exports[`SaleDetailsPanel matches snapshot 1`] = `
|
|||
<BookingBreakdown
|
||||
bookingEnd={2017-06-13T00:00:00.000Z}
|
||||
bookingStart={2017-06-10T00:00:00.000Z}
|
||||
commission={
|
||||
lineItems={
|
||||
Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 16500,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "3",
|
||||
"unitPrice": Money {
|
||||
"amount": 5500,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
payinTotal={
|
||||
Money {
|
||||
"amount": 1000,
|
||||
"amount": 16500,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
totalPrice={
|
||||
payoutTotal={
|
||||
Money {
|
||||
"amount": 15500,
|
||||
"currency": "USD",
|
||||
}
|
||||
}
|
||||
unitPrice={
|
||||
Money {
|
||||
"amount": 5500,
|
||||
"currency": "USD",
|
||||
}
|
||||
} />
|
||||
userRole="provider" />
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@ import css from './BookingDatesForm.css';
|
|||
// TODO: This is a temporary function to calculate the booking
|
||||
// price. This should be removed when the API supports dry-runs and we
|
||||
// can take the total price from the transaction itself.
|
||||
const estimatedTotalPrice = (startDate, endDate, unitPrice) => {
|
||||
const estimatedTotalPrice = (unitPrice, nightCount) => {
|
||||
const { subUnitDivisor } = config.currencyConfig;
|
||||
const numericPrice = convertMoneyToNumber(unitPrice, subUnitDivisor);
|
||||
const nightCount = nightsBetween(startDate, endDate);
|
||||
const numericTotalPrice = new Decimal(numericPrice).times(nightCount).toNumber();
|
||||
return new types.Money(
|
||||
convertUnitToSubUnit(numericTotalPrice, subUnitDivisor),
|
||||
|
|
@ -33,14 +32,26 @@ const breakdown = (bookingStart, bookingEnd, unitPrice) => {
|
|||
if (!bookingStart || !bookingEnd || !unitPrice) {
|
||||
return null;
|
||||
}
|
||||
const totalPrice = estimatedTotalPrice(bookingStart, bookingEnd, unitPrice);
|
||||
const nightCount = nightsBetween(bookingStart, bookingEnd);
|
||||
const totalPrice = estimatedTotalPrice(unitPrice, nightCount);
|
||||
const lineItems = [
|
||||
{
|
||||
code: 'line-item.purchase/night',
|
||||
unitPrice: unitPrice,
|
||||
quantity: new Decimal(nightCount),
|
||||
lineTotal: totalPrice,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<BookingBreakdown
|
||||
className={css.receipt}
|
||||
bookingStart={bookingStart}
|
||||
bookingEnd={bookingEnd}
|
||||
unitPrice={unitPrice}
|
||||
totalPrice={totalPrice}
|
||||
userRole="customer"
|
||||
payinTotal={totalPrice}
|
||||
lineItems={lineItems}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
@ -56,6 +67,8 @@ export const BookingDatesFormComponent = props => {
|
|||
price: unitPrice,
|
||||
submitting,
|
||||
intl,
|
||||
startDatePlaceholder,
|
||||
endDatePlaceholder,
|
||||
} = props;
|
||||
|
||||
const { startDate, endDate } = bookingDates;
|
||||
|
|
@ -97,6 +110,10 @@ export const BookingDatesFormComponent = props => {
|
|||
// https://momentjs.com/
|
||||
const dateFormatString = 'ddd, MMMM D';
|
||||
|
||||
const startDatePlaceholderText = startDatePlaceholder || moment().format(dateFormatString);
|
||||
const endDatePlaceholderText = endDatePlaceholder ||
|
||||
moment().add(1, 'days').format(dateFormatString);
|
||||
|
||||
return (
|
||||
<form className={className} onSubmit={handleSubmit}>
|
||||
<DateRangeInputField
|
||||
|
|
@ -104,10 +121,10 @@ export const BookingDatesFormComponent = props => {
|
|||
name="bookingDates"
|
||||
startDateId={`${form}.bookingStartDate`}
|
||||
startDateLabel={bookingStartLabel}
|
||||
startDatePlaceholderText={moment().format(dateFormatString)}
|
||||
startDatePlaceholderText={startDatePlaceholderText}
|
||||
endDateId={`${form}.bookingEndDate`}
|
||||
endDateLabel={bookingEndLabel}
|
||||
endDatePlaceholderText={moment().add(1, 'days').format(dateFormatString)}
|
||||
endDatePlaceholderText={endDatePlaceholderText}
|
||||
format={null}
|
||||
useMobileMargins
|
||||
validate={[
|
||||
|
|
@ -130,6 +147,8 @@ BookingDatesFormComponent.defaultProps = {
|
|||
rootClassName: null,
|
||||
className: null,
|
||||
price: null,
|
||||
startDatePlaceholder: null,
|
||||
endDatePlaceholder: null,
|
||||
};
|
||||
|
||||
const { instanceOf, shape, string } = PropTypes;
|
||||
|
|
@ -149,6 +168,10 @@ BookingDatesFormComponent.propTypes = {
|
|||
|
||||
// from inejctIntl
|
||||
intl: intlShape.isRequired,
|
||||
|
||||
// for tests
|
||||
startDatePlaceholder: string,
|
||||
endDatePlaceholder: string,
|
||||
};
|
||||
|
||||
const formName = 'BookingDates';
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ describe('BookingDatesForm', () => {
|
|||
onSubmit={v => v}
|
||||
price={new types.Money(1099, 'USD')}
|
||||
bookingDates={{}}
|
||||
startDatePlaceholder="today"
|
||||
endDatePlaceholder="tomorrow"
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
@ -32,6 +34,8 @@ describe('BookingDatesForm', () => {
|
|||
startDate: new Date(Date.UTC(2017, 3, 14)),
|
||||
endDate: new Date(Date.UTC(2017, 3, 16)),
|
||||
}}
|
||||
startDatePlaceholder="today"
|
||||
endDatePlaceholder="tomorrow"
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ exports[`BookingDatesForm matches snapshot with selected dates 1`] = `
|
|||
<DateRangeInputField
|
||||
endDateId="fakeTestForm.bookingEndDate"
|
||||
endDateLabel="BookingDatesForm.bookingEndTitle"
|
||||
endDatePlaceholderText="Sat, July 1"
|
||||
endDatePlaceholderText="tomorrow"
|
||||
format={null}
|
||||
name="bookingDates"
|
||||
startDateId="fakeTestForm.bookingStartDate"
|
||||
startDateLabel="BookingDatesForm.bookingStartTitle"
|
||||
startDatePlaceholderText="Fri, June 30"
|
||||
startDatePlaceholderText="today"
|
||||
useMobileMargins={true}
|
||||
validate={
|
||||
Array [
|
||||
|
|
@ -21,7 +21,23 @@ exports[`BookingDatesForm matches snapshot with selected dates 1`] = `
|
|||
<BookingBreakdown
|
||||
bookingEnd={2017-04-16T00:00:00.000Z}
|
||||
bookingStart={2017-04-14T00:00:00.000Z}
|
||||
totalPrice={
|
||||
lineItems={
|
||||
Array [
|
||||
Object {
|
||||
"code": "line-item.purchase/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 2198,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "2",
|
||||
"unitPrice": Money {
|
||||
"amount": 1099,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
payinTotal={
|
||||
Money {
|
||||
"amount": 2198,
|
||||
"currency": "USD",
|
||||
|
|
@ -32,7 +48,8 @@ exports[`BookingDatesForm matches snapshot with selected dates 1`] = `
|
|||
"amount": 1099,
|
||||
"currency": "USD",
|
||||
}
|
||||
} />
|
||||
}
|
||||
userRole="customer" />
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="BookingDatesForm.youWontBeChargedInfo"
|
||||
|
|
@ -57,12 +74,12 @@ exports[`BookingDatesForm matches snapshot without selected dates 1`] = `
|
|||
<DateRangeInputField
|
||||
endDateId="fakeTestForm.bookingEndDate"
|
||||
endDateLabel="BookingDatesForm.bookingEndTitle"
|
||||
endDatePlaceholderText="Sat, July 1"
|
||||
endDatePlaceholderText="tomorrow"
|
||||
format={null}
|
||||
name="bookingDates"
|
||||
startDateId="fakeTestForm.bookingStartDate"
|
||||
startDateLabel="BookingDatesForm.bookingStartTitle"
|
||||
startDatePlaceholderText="Fri, June 30"
|
||||
startDatePlaceholderText="today"
|
||||
useMobileMargins={true}
|
||||
validate={
|
||||
Array [
|
||||
|
|
|
|||
|
|
@ -50,17 +50,42 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
tx={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2017-01-15T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "1",
|
||||
"unitPrice": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": null,
|
||||
"customer": null,
|
||||
|
|
@ -102,17 +127,42 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
tx={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2016-01-15T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "1",
|
||||
"unitPrice": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": null,
|
||||
"customer": null,
|
||||
|
|
@ -231,17 +281,42 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
tx={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2017-01-15T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "1",
|
||||
"unitPrice": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": null,
|
||||
"customer": Object {
|
||||
|
|
@ -283,17 +358,42 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
tx={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2016-01-15T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "1",
|
||||
"unitPrice": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": null,
|
||||
"customer": Object {
|
||||
|
|
|
|||
|
|
@ -8,17 +8,42 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
transaction={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "3",
|
||||
"unitPrice": Money {
|
||||
"amount": 333.3333333333333,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": Object {
|
||||
"attributes": Object {
|
||||
|
|
|
|||
|
|
@ -9,17 +9,42 @@ exports[`SalePage matches snapshot 1`] = `
|
|||
transaction={
|
||||
Object {
|
||||
"attributes": Object {
|
||||
"commission": Money {
|
||||
"amount": 100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"createdAt": 2017-05-01T00:00:00.000Z,
|
||||
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
|
||||
"state": "state/preauthorized",
|
||||
"total": Money {
|
||||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"quantity": "3",
|
||||
"unitPrice": Money {
|
||||
"amount": 333.3333333333333,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
"unitPrice": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
},
|
||||
},
|
||||
],
|
||||
"payinTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
},
|
||||
"payoutTotal": Money {
|
||||
"amount": 900,
|
||||
"currency": "USD",
|
||||
},
|
||||
"state": "state/preauthorized",
|
||||
},
|
||||
"booking": Object {
|
||||
"attributes": Object {
|
||||
|
|
|
|||
16
src/index.js
16
src/index.js
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import Decimal from 'decimal.js';
|
||||
import { createInstance, types } from './util/sdkLoader';
|
||||
import { ClientApp, renderApp } from './app';
|
||||
import configureStore from './store';
|
||||
|
|
@ -25,6 +26,8 @@ import routeConfiguration from './routesConfiguration';
|
|||
|
||||
import './marketplace.css';
|
||||
|
||||
const { BigDecimal } = types;
|
||||
|
||||
const render = store => {
|
||||
// If the server already loaded the auth information, render the app
|
||||
// immediately. Otherwise wait for the flag to be loaded and render
|
||||
|
|
@ -54,7 +57,18 @@ if (typeof window !== 'undefined') {
|
|||
// eslint-disable-next-line no-underscore-dangle
|
||||
const preloadedState = window.__PRELOADED_STATE__ || '{}';
|
||||
const initialState = JSON.parse(preloadedState, types.reviver);
|
||||
const sdk = createInstance({ clientId: config.sdk.clientId, baseUrl: config.sdk.baseUrl });
|
||||
const sdk = createInstance({
|
||||
clientId: config.sdk.clientId,
|
||||
baseUrl: config.sdk.baseUrl,
|
||||
typeHandlers: [
|
||||
{
|
||||
type: BigDecimal,
|
||||
customType: Decimal,
|
||||
writer: v => new BigDecimal(v.toString()),
|
||||
reader: v => new Decimal(v.value),
|
||||
},
|
||||
],
|
||||
});
|
||||
const store = configureStore(sdk, initialState);
|
||||
|
||||
setupStripe();
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"AuthorInfo.host": "Hosted by {firstName} {lastName}",
|
||||
"BookingBreakdown.bookingPeriod": "{bookingStart} - {bookingEnd}",
|
||||
"BookingBreakdown.commission": "Saunatime fee:",
|
||||
"BookingBreakdown.nightCount": "* {count, number} {count, plural, one {night} other {nights}}",
|
||||
"BookingBreakdown.nightCount": "× {count, number} {count, plural, one {night} other {nights}}",
|
||||
"BookingBreakdown.pricePerNight": "Price per night",
|
||||
"BookingBreakdown.total": "Total price",
|
||||
"BookingDatesForm.bookingEndTitle": "End date",
|
||||
|
|
|
|||
|
|
@ -147,12 +147,12 @@ export const transaction = shape({
|
|||
id: uuid.isRequired,
|
||||
type: value('transaction').isRequired,
|
||||
attributes: shape({
|
||||
commission: money.isRequired,
|
||||
createdAt: instanceOf(Date).isRequired,
|
||||
lastTransitionedAt: instanceOf(Date).isRequired,
|
||||
lastTransition: string,
|
||||
state: oneOf(TX_STATES).isRequired,
|
||||
total: money.isRequired,
|
||||
payinTotal: money.isRequired,
|
||||
payoutTotal: money.isRequired,
|
||||
}),
|
||||
booking,
|
||||
listing,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import Decimal from 'decimal.js';
|
||||
import { types } from './sdkLoader';
|
||||
import { nightsBetween } from '../util/dates';
|
||||
|
||||
const { UUID, LatLng, Money } = types;
|
||||
|
||||
|
|
@ -80,15 +82,29 @@ export const createTransaction = options => {
|
|||
provider = null,
|
||||
lastTransitionedAt = new Date(Date.UTC(2017, 5, 1)),
|
||||
} = options;
|
||||
const nightCount = booking ? nightsBetween(booking.attributes.start, booking.attributes.end) : 1;
|
||||
return {
|
||||
id: new UUID(id),
|
||||
type: 'transaction',
|
||||
attributes: {
|
||||
commission,
|
||||
createdAt: new Date(Date.UTC(2017, 4, 1)),
|
||||
lastTransitionedAt,
|
||||
state,
|
||||
total,
|
||||
payinTotal: total,
|
||||
payoutTotal: new Money(total.amount - commission.amount, total.currency),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
quantity: new Decimal(nightCount),
|
||||
unitPrice: new Money(total.amount / nightCount, total.currency),
|
||||
lineTotal: total,
|
||||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
unitPrice: new Money(commission.amount * -1, commission.currency),
|
||||
lineTotal: new Money(commission.amount * -1, commission.currency),
|
||||
},
|
||||
],
|
||||
},
|
||||
booking,
|
||||
listing,
|
||||
|
|
|
|||
|
|
@ -6008,9 +6008,9 @@ sharetribe-scripts@0.9.2:
|
|||
optionalDependencies:
|
||||
fsevents "1.0.17"
|
||||
|
||||
"sharetribe-sdk@git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a3d2bfcc64e0b91c6626b959c8395fbda39fd0d8":
|
||||
"sharetribe-sdk@git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a086f37c765cf5b6034bc5f4788788237485c863":
|
||||
version "0.0.1"
|
||||
resolved "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a3d2bfcc64e0b91c6626b959c8395fbda39fd0d8"
|
||||
resolved "git+ssh://git@github.com/sharetribe/sharetribe-sdk-js#a086f37c765cf5b6034bc5f4788788237485c863"
|
||||
dependencies:
|
||||
axios "^0.15.3"
|
||||
js-cookie "^2.1.3"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue