diff --git a/package.json b/package.json index bcf33cc6..d173c9fa 100644 --- a/package.json +++ b/package.json @@ -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" }, diff --git a/server/index.js b/server/index.js index 5618d8ad..a09e62ff 100644 --- a/server/index.js +++ b/server/index.js @@ -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 diff --git a/src/components/BookingBreakdown/BookingBreakdown.example.js b/src/components/BookingBreakdown/BookingBreakdown.example.js index bcc06026..d578b693 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.example.js +++ b/src/components/BookingBreakdown/BookingBreakdown.example.js @@ -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'), }, }; diff --git a/src/components/BookingBreakdown/BookingBreakdown.js b/src/components/BookingBreakdown/BookingBreakdown.js index a6e0da1d..4f30bdfb 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.js +++ b/src/components/BookingBreakdown/BookingBreakdown.js @@ -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 = ( - + ); 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 = ( -
- - - - {formattedCommission} -
+ 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 = ( +
+ + + + {formattedCommission} +
+ ); + } + + 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 => { {nightCountMessage} - {commission ? commissionInfo : null} + {commissionInfo}
@@ -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, diff --git a/src/components/BookingBreakdown/BookingBreakdown.test.js b/src/components/BookingBreakdown/BookingBreakdown.test.js index b326c566..9d42041a 100644 --- a/src/components/BookingBreakdown/BookingBreakdown.test.js +++ b/src/components/BookingBreakdown/BookingBreakdown.test.js @@ -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', () => { ); @@ -23,8 +32,16 @@ describe('BookingBreakdown', () => { ); @@ -36,9 +53,22 @@ describe('BookingBreakdown', () => { ); diff --git a/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap b/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap index 6f0e15fa..2d22d38e 100644 --- a/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap +++ b/src/components/BookingBreakdown/__snapshots__/BookingBreakdown.test.js.snap @@ -32,9 +32,12 @@ exports[`BookingBreakdown customer transaction data matches snapshot 1`] = ` - - * 2 nights - +

- - * 2 nights - +

- - * 2 nights - +
- -2 + 2

{ 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" /> ); }; diff --git a/src/components/OrderDetailsPanel/OrderDetailsPanel.test.js b/src/components/OrderDetailsPanel/OrderDetailsPanel.test.js index e5b35643..bee691cc 100644 --- a/src/components/OrderDetailsPanel/OrderDetailsPanel.test.js +++ b/src/components/OrderDetailsPanel/OrderDetailsPanel.test.js @@ -42,6 +42,6 @@ describe('OrderDetailsPanel', () => { }); const panel = shallow(); const breakdownProps = panel.find(BookingBreakdown).props(); - expect(breakdownProps.totalPrice).toEqual(new Money(16500, 'USD')); + expect(breakdownProps.payinTotal).toEqual(new Money(16500, 'USD')); }); }); diff --git a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap index d0d51d71..37c2fe32 100644 --- a/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap +++ b/src/components/OrderDetailsPanel/__snapshots__/OrderDetailsPanel.test.js.snap @@ -47,17 +47,39 @@ exports[`OrderDetailsPanel matches snapshot 1`] = ` + userRole="customer" /> `; diff --git a/src/components/SaleDetailsPanel/SaleDetailsPanel.js b/src/components/SaleDetailsPanel/SaleDetailsPanel.js index eac2da77..8c0a740d 100644 --- a/src/components/SaleDetailsPanel/SaleDetailsPanel.js +++ b/src/components/SaleDetailsPanel/SaleDetailsPanel.js @@ -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 ( ); }; diff --git a/src/components/SaleDetailsPanel/SaleDetailsPanel.test.js b/src/components/SaleDetailsPanel/SaleDetailsPanel.test.js index 10b6e88f..312f1ab1 100644 --- a/src/components/SaleDetailsPanel/SaleDetailsPanel.test.js +++ b/src/components/SaleDetailsPanel/SaleDetailsPanel.test.js @@ -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')); }); }); diff --git a/src/components/SaleDetailsPanel/__snapshots__/SaleDetailsPanel.test.js.snap b/src/components/SaleDetailsPanel/__snapshots__/SaleDetailsPanel.test.js.snap index b194fa7f..63bd0907 100644 --- a/src/components/SaleDetailsPanel/__snapshots__/SaleDetailsPanel.test.js.snap +++ b/src/components/SaleDetailsPanel/__snapshots__/SaleDetailsPanel.test.js.snap @@ -46,23 +46,45 @@ exports[`SaleDetailsPanel matches snapshot 1`] = ` + userRole="provider" /> `; diff --git a/src/containers/BookingDatesForm/BookingDatesForm.js b/src/containers/BookingDatesForm/BookingDatesForm.js index 48a1d614..e7af8ce4 100644 --- a/src/containers/BookingDatesForm/BookingDatesForm.js +++ b/src/containers/BookingDatesForm/BookingDatesForm.js @@ -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 ( ); }; @@ -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 (
{ 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'; diff --git a/src/containers/BookingDatesForm/BookingDatesForm.test.js b/src/containers/BookingDatesForm/BookingDatesForm.test.js index 27c52fa8..b721a022 100644 --- a/src/containers/BookingDatesForm/BookingDatesForm.test.js +++ b/src/containers/BookingDatesForm/BookingDatesForm.test.js @@ -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(); diff --git a/src/containers/BookingDatesForm/__snapshots__/BookingDatesForm.test.js.snap b/src/containers/BookingDatesForm/__snapshots__/BookingDatesForm.test.js.snap index 9cbf0688..1b62ac6a 100644 --- a/src/containers/BookingDatesForm/__snapshots__/BookingDatesForm.test.js.snap +++ b/src/containers/BookingDatesForm/__snapshots__/BookingDatesForm.test.js.snap @@ -5,12 +5,12 @@ exports[`BookingDatesForm matches snapshot with selected dates 1`] = ` + } + userRole="customer" />

{ // 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(); diff --git a/src/translations/en.json b/src/translations/en.json index e995df1a..22e70efc 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -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", diff --git a/src/util/propTypes.js b/src/util/propTypes.js index bedfa103..97e275e6 100644 --- a/src/util/propTypes.js +++ b/src/util/propTypes.js @@ -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, diff --git a/src/util/test-data.js b/src/util/test-data.js index 86d1712a..a39f6cee 100644 --- a/src/util/test-data.js +++ b/src/util/test-data.js @@ -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, diff --git a/yarn.lock b/yarn.lock index bb4a7398..0a3ba2fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"