BookingBreakdown can handle customer commission

This commit is contained in:
Vesa Luusua 2018-03-09 13:43:14 +02:00
parent 7100a7421a
commit 08fcfac609
8 changed files with 164 additions and 14 deletions

View file

@ -82,3 +82,20 @@
margin: 2px 0 0 10px;
}
}
.feeInfo {
@apply --marketplaceTinyFontStyles;
color: var(--matterColorAnti);
margin: 0 0 20px 0;
flex-shrink: 0;
@media (--viewportMedium) {
margin-top: auto;
margin-bottom: 20px;
}
@media (--viewportLarge) {
margin-top: 4px;
margin-bottom: 22px;
}
}

View file

@ -4,15 +4,20 @@
*/
import React from 'react';
import { oneOf, string } from 'prop-types';
import { intlShape, injectIntl } from 'react-intl';
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import classNames from 'classnames';
import { propTypes } from '../../util/types';
import {
propTypes,
LINE_ITEM_CUSTOMER_COMMISSION,
LINE_ITEM_PROVIDER_COMMISSION,
} from '../../util/types';
import LineItemUnitPrice from './LineItemUnitPrice';
import LineItemBookingPeriod from './LineItemBookingPeriod';
import LineItemUnitsMaybe from './LineItemUnitsMaybe';
import LineItemSubTotalMaybe from './LineItemSubTotalMaybe';
import LineItemCommissionMaybe from './LineItemCommissionMaybe';
import LineItemCustomerCommissionMaybe from './LineItemCustomerCommissionMaybe';
import LineItemProviderCommissionMaybe from './LineItemProviderCommissionMaybe';
import LineItemRefundMaybe from './LineItemRefundMaybe';
import LineItemTotalPrice from './LineItemTotalPrice';
import css from './BookingBreakdown.css';
@ -20,7 +25,16 @@ import css from './BookingBreakdown.css';
export const BookingBreakdownComponent = props => {
const { rootClassName, className, userRole, unitType, transaction, booking, intl } = props;
const isCustomer = userRole === 'customer';
const isProvider = userRole === 'provider';
const commissionLineItem = transaction.attributes.lineItems.find(
item =>
(item.code === LINE_ITEM_CUSTOMER_COMMISSION ||
item.code === LINE_ITEM_PROVIDER_COMMISSION) &&
!item.reversal
);
const classes = classNames(rootClassName || css.root, className);
return (
@ -32,14 +46,28 @@ export const BookingBreakdownComponent = props => {
<LineItemSubTotalMaybe
transaction={transaction}
unitType={unitType}
userRole={userRole}
intl={intl}
/>
<LineItemCustomerCommissionMaybe
transaction={transaction}
isCustomer={isCustomer}
intl={intl}
/>
<LineItemProviderCommissionMaybe
transaction={transaction}
isProvider={isProvider}
intl={intl}
/>
<LineItemCommissionMaybe transaction={transaction} isProvider={isProvider} intl={intl} />
<LineItemRefundMaybe transaction={transaction} unitType={unitType} intl={intl} />
<hr className={css.totalDivider} />
<LineItemTotalPrice transaction={transaction} isProvider={isProvider} intl={intl} />
{commissionLineItem ? (
<span className={css.feeInfo}>
<FormattedMessage id="BookingBreakdown.commissionFeeNote" />
</span>
) : null}
</div>
);
};

View file

@ -0,0 +1,60 @@
import React from 'react';
import { bool } from 'prop-types';
import { FormattedMessage, intlShape } from 'react-intl';
import { formatMoney } from '../../util/currency';
import { types as sdkTypes } from '../../util/sdkLoader';
import { LINE_ITEM_CUSTOMER_COMMISSION, propTypes } from '../../util/types';
import css from './BookingBreakdown.css';
const { Money } = sdkTypes;
// Validate the assumption that the commission exists and the amount
// is zero or positive.
const isValidCommission = commissionLineItem => {
return commissionLineItem.lineTotal instanceof Money && commissionLineItem.lineTotal.amount >= 0;
};
const LineItemStudioTimeCustomerCommissionMaybe = props => {
const { transaction, isCustomer, intl } = props;
const customerCommissionLineItem = transaction.attributes.lineItems.find(
item => item.code === LINE_ITEM_CUSTOMER_COMMISSION && !item.reversal
);
const commissionRefund = transaction.attributes.lineItems.find(
item => item.code === LINE_ITEM_CUSTOMER_COMMISSION && item.reversal
);
// If commission is passed it will be shown as a fee already reduces from the total price
let commissionItem = null;
if (isCustomer && customerCommissionLineItem) {
if (!isValidCommission(customerCommissionLineItem)) {
// eslint-disable-next-line no-console
console.error('invalid commission line item:', customerCommissionLineItem);
throw new Error('Commission should be present and the value should be zero or positive');
}
const commission = customerCommissionLineItem.lineTotal;
const formattedCommission = commission ? formatMoney(intl, commission) : null;
commissionItem = commissionRefund ? null : (
<div className={css.lineItem}>
<span className={css.itemLabel}>
<FormattedMessage id="BookingBreakdown.commission" />
</span>
<span className={css.itemValue}>{formattedCommission}</span>
</div>
);
}
return commissionItem;
};
LineItemStudioTimeCustomerCommissionMaybe.propTypes = {
transaction: propTypes.transaction.isRequired,
isCustomer: bool.isRequired,
intl: intlShape.isRequired,
};
export default LineItemStudioTimeCustomerCommissionMaybe;

View file

@ -19,7 +19,7 @@ const isValidCommission = commissionLineItem => {
);
};
const LineItemCommissionMaybe = props => {
const LineItemProviderCommissionMaybe = props => {
const { transaction, isProvider, intl } = props;
const providerCommissionLineItem = transaction.attributes.lineItems.find(
@ -55,10 +55,10 @@ const LineItemCommissionMaybe = props => {
return commissionItem;
};
LineItemCommissionMaybe.propTypes = {
LineItemProviderCommissionMaybe.propTypes = {
transaction: propTypes.transaction.isRequired,
isProvider: bool.isRequired,
intl: intlShape.isRequired,
};
export default LineItemCommissionMaybe;
export default LineItemProviderCommissionMaybe;

View file

@ -1,13 +1,36 @@
import React from 'react';
import { bool } from 'prop-types';
import { string } from 'prop-types';
import { FormattedMessage, intlShape } from 'react-intl';
import { formatMoney } from '../../util/currency';
import { propTypes } from '../../util/types';
import {
propTypes,
LINE_ITEM_CUSTOMER_COMMISSION,
LINE_ITEM_PROVIDER_COMMISSION,
} from '../../util/types';
import css from './BookingBreakdown.css';
/**
* Checks if a transaction has a commission line-item for
* a given user role.
*/
const txHasCommission = (transaction, userRole) => {
let commissionLineItem = null;
if (userRole === 'customer') {
commissionLineItem = transaction.attributes.lineItems.find(
item => item.code === LINE_ITEM_CUSTOMER_COMMISSION
);
} else if (userRole === 'provider') {
commissionLineItem = transaction.attributes.lineItems.find(
item => item.code === LINE_ITEM_PROVIDER_COMMISSION
);
}
return !!commissionLineItem;
};
const LineItemSubTotalMaybe = props => {
const { transaction, unitType, isProvider, intl } = props;
const { transaction, unitType, userRole, intl } = props;
const refund = transaction.attributes.lineItems.find(
item => item.code === unitType && item.reversal
@ -16,7 +39,7 @@ const LineItemSubTotalMaybe = props => {
// Show unit purchase line total (unit price * quantity) as a subtotal.
// PLEASE NOTE that this assumes that the transaction doesn't have other
// line item types than the defined unit type (e.g. week, month, year).
const showSubTotal = isProvider || refund;
const showSubTotal = txHasCommission(transaction, userRole) || refund;
const unitPurchase = transaction.attributes.lineItems.find(
item => item.code === unitType && !item.reversal
);
@ -34,7 +57,7 @@ const LineItemSubTotalMaybe = props => {
LineItemSubTotalMaybe.propTypes = {
transaction: propTypes.transaction.isRequired,
isProvider: bool.isRequired,
userRole: string.isRequired,
intl: intlShape.isRequired,
};

View file

@ -221,6 +221,13 @@ exports[`BookingBreakdown provider canceled transaction data matches snapshot 1`
0
</div>
</div>
<span
className={undefined}
>
<span>
BookingBreakdown.commissionFeeNote
</span>
</span>
</div>
`;
@ -317,5 +324,12 @@ exports[`BookingBreakdown provider transaction data matches snapshot 1`] = `
18
</div>
</div>
<span
className={undefined}
>
<span>
BookingBreakdown.commissionFeeNote
</span>
</span>
</div>
`;

View file

@ -38,7 +38,8 @@
"AuthenticationPage.verifyEmailTitle": "{name}, check your inbox to verify your email",
"Avatar.bannedUserDisplayName": "Banned user",
"BookingBreakdown.bookingPeriod": "{bookingStart} {bookingEnd}",
"BookingBreakdown.commission": "Saunatime fee",
"BookingBreakdown.commission": "Saunatime fee *",
"BookingBreakdown.commissionFeeNote": "* The fee helps us run the platform and provide the best possible service to you!",
"BookingBreakdown.dayCount": "{count, number} {count, plural, one {day} other {days}}",
"BookingBreakdown.nightCount": "{count, number} {count, plural, one {night} other {nights}}",
"BookingBreakdown.pricePerDay": "Price per day",

View file

@ -329,9 +329,16 @@ propTypes.review = shape({
export const LINE_ITEM_NIGHT = 'line-item/night';
export const LINE_ITEM_DAY = 'line-item/day';
export const LINE_ITEM_UNITS = 'line-item/units';
export const LINE_ITEM_CUSTOMER_COMMISSION = 'line-item/customer-commission';
export const LINE_ITEM_PROVIDER_COMMISSION = 'line-item/provider-commission';
const LINE_ITEMS = [LINE_ITEM_NIGHT, LINE_ITEM_DAY, LINE_ITEM_UNITS, LINE_ITEM_PROVIDER_COMMISSION];
const LINE_ITEMS = [
LINE_ITEM_NIGHT,
LINE_ITEM_DAY,
LINE_ITEM_UNITS,
LINE_ITEM_CUSTOMER_COMMISSION,
LINE_ITEM_PROVIDER_COMMISSION,
];
propTypes.bookingUnitType = oneOf([LINE_ITEM_NIGHT, LINE_ITEM_DAY, LINE_ITEM_UNITS]);