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.js b/src/components/BookingBreakdown/BookingBreakdown.js
index a6e0da1d..0a3bf9ca 100644
--- a/src/components/BookingBreakdown/BookingBreakdown.js
+++ b/src/components/BookingBreakdown/BookingBreakdown.js
@@ -2,6 +2,7 @@
* This component will show the booking info and calculated total price.
* I.e. dates and other details related to payment decision in receipt format.
*/
+import _ from 'lodash';
import React, { PropTypes } from 'react';
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import Decimal from 'decimal.js';
@@ -9,7 +10,6 @@ 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,9 +19,10 @@ export const BookingBreakdownComponent = props => {
className,
bookingStart,
bookingEnd,
- unitPrice,
- commission,
- totalPrice,
+ payinTotal,
+ payoutTotal,
+ lineItems,
+ userRole,
intl,
} = props;
@@ -50,7 +51,13 @@ export const BookingBreakdownComponent = props => {
/>
);
- const nightCount = nightsBetween(bookingStart, bookingEnd);
+ const nightPurchase = _.find(lineItems, item => item.code === 'line-item.purchase/night');
+ const providerCommission = _.find(
+ lineItems,
+ item => item.code === 'line-item.commission/provider'
+ );
+
+ const nightCount = nightPurchase.quantity.toFixed();
const nightCountMessage = (
);
@@ -58,10 +65,11 @@ export const BookingBreakdownComponent = props => {
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 commission = providerCommission.lineTotal;
const commissionAsNumber = commission ? convertMoneyToNumber(commission, subUnitDivisor) : 0;
const formattedCommission = commission
? intl.formatNumber(new Decimal(commissionAsNumber).negated().toNumber(), currencyConfig)
@@ -76,7 +84,10 @@ export const BookingBreakdownComponent = props => {
);
- const totalPriceAsNumber = convertMoneyToNumber(totalPrice, subUnitDivisor);
+ const totalPriceAsNumber = convertMoneyToNumber(
+ userRole === 'customer' ? payinTotal : payoutTotal,
+ subUnitDivisor
+ );
const formattedTotalPrice = totalPriceAsNumber
? intl.formatNumber(totalPriceAsNumber, currencyConfig)
: null;
@@ -95,7 +106,7 @@ export const BookingBreakdownComponent = props => {
{nightCountMessage}
- {commission ? commissionInfo : null}
+ {userRole === 'provider' ? commissionInfo : null}
@@ -115,7 +126,14 @@ BookingBreakdownComponent.defaultProps = {
commission: 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 +141,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.isRequired,
+ payoutTotal: propTypes.money.isRequired,
// from injectIntl
intl: intlShape.isRequired,
diff --git a/src/components/OrderDetailsPanel/OrderDetailsPanel.js b/src/components/OrderDetailsPanel/OrderDetailsPanel.js
index 34c5b315..4f0006c5 100644
--- a/src/components/OrderDetailsPanel/OrderDetailsPanel.js
+++ b/src/components/OrderDetailsPanel/OrderDetailsPanel.js
@@ -10,14 +10,14 @@ 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.payinTotal;
+ const payinTotal = tx.attributes.payinTotal;
+ const payoutTotal = tx.attributes.payoutTotal;
+ const lineItems = tx.attributes.lineItems;
- if (!bookingStart || !bookingEnd || !unitPrice || !totalPrice) {
+ if (!bookingStart || !bookingEnd || !payinTotal || !payoutTotal || !lineItems) {
return null;
}
return (
@@ -25,8 +25,10 @@ const breakdown = transaction => {
className={css.receipt}
bookingStart={bookingStart}
bookingEnd={bookingEnd}
- unitPrice={unitPrice}
- totalPrice={totalPrice}
+ payinTotal={payinTotal}
+ payoutTotal={payoutTotal}
+ lineItems={lineItems}
+ userRole="customer"
/>
);
};
diff --git a/src/components/SaleDetailsPanel/SaleDetailsPanel.js b/src/components/SaleDetailsPanel/SaleDetailsPanel.js
index 6a6968d4..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 estimatedCommission = (customerTotalPrice, providerTotalPrice) => {
- const { subUnitDivisor, currency } = config.currencyConfig;
- if (customerTotalPrice.currency !== currency || providerTotalPrice.currency !== currency) {
- throw new Error('Transaction total or commission currency does not match marketplace currency');
- }
-
- const numericCustomerTotalPrice = convertMoneyToNumber(customerTotalPrice, subUnitDivisor);
- const numericProviderTotalPrice = convertMoneyToNumber(providerTotalPrice, subUnitDivisor);
- const numericCommission = new Decimal(numericProviderTotalPrice)
- .minus(numericCustomerTotalPrice)
- .toNumber();
-
- return new types.Money(convertUnitToSubUnit(numericCommission, 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.payinTotal;
- const providerTotalPrice = tx.attributes.payoutTotal;
+ const payinTotal = tx.attributes.payinTotal;
+ const payoutTotal = tx.attributes.payoutTotal;
+ const lineItems = tx.attributes.lineItems;
- if (!bookingStart || !bookingEnd || !unitPrice || !customerTotalPrice || !providerTotalPrice) {
+ if (!bookingStart || !bookingEnd || !payinTotal || !payoutTotal || !lineItems) {
return null;
}
- const commission = estimatedCommission(customerTotalPrice, providerTotalPrice);
-
return (
);
};
diff --git a/src/index.js b/src/index.js
index a82c6210..b4fc1b9a 100644
--- a/src/index.js
+++ b/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();