mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Merge pull request #619 from sharetribe/daily-breakdown
Daily breakdown
This commit is contained in:
commit
8f401bc31a
27 changed files with 800 additions and 83 deletions
|
|
@ -43,12 +43,14 @@ export const Checkout = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -67,12 +69,14 @@ export const CustomerOrder = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -91,12 +95,14 @@ export const ProviderSale = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(7000, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -104,6 +110,7 @@ export const ProviderSale = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -121,12 +128,14 @@ export const ProviderSaleZeroCommission = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(9000, CURRENCY),
|
||||
|
|
@ -134,6 +143,7 @@ export const ProviderSaleZeroCommission = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(0, CURRENCY),
|
||||
lineTotal: new Money(0, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -151,12 +161,14 @@ export const ProviderSaleSingleNight = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
payoutTotal: new Money(2500, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -164,6 +176,7 @@ export const ProviderSaleSingleNight = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -181,6 +194,7 @@ export const ProviderSalePreauthorized = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -188,6 +202,7 @@ export const ProviderSalePreauthorized = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -195,6 +210,7 @@ export const ProviderSalePreauthorized = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -212,6 +228,7 @@ export const ProviderSaleAccepted = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_ACCEPT,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -219,6 +236,7 @@ export const ProviderSaleAccepted = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -226,6 +244,7 @@ export const ProviderSaleAccepted = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -243,6 +262,7 @@ export const ProviderSaleDeclined = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_DECLINE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -250,6 +270,7 @@ export const ProviderSaleDeclined = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -257,6 +278,7 @@ export const ProviderSaleDeclined = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -274,6 +296,7 @@ export const ProviderSaleAutoDeclined = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_AUTO_DECLINE,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -281,6 +304,7 @@ export const ProviderSaleAutoDeclined = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -288,6 +312,7 @@ export const ProviderSaleAutoDeclined = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -305,6 +330,7 @@ export const ProviderSaleDelivered = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_MARK_DELIVERED,
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -312,6 +338,7 @@ export const ProviderSaleDelivered = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -319,6 +346,7 @@ export const ProviderSaleDelivered = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
reversal: false,
|
||||
|
|
@ -336,6 +364,7 @@ export const ProviderSaleCanceled = {
|
|||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'provider',
|
||||
unitType: propTypes.LINE_ITEM_NIGHT,
|
||||
transaction: exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_CANCEL,
|
||||
payinTotal: new Money(0, CURRENCY),
|
||||
|
|
@ -343,6 +372,7 @@ export const ProviderSaleCanceled = {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
|
|
@ -350,6 +380,7 @@ export const ProviderSaleCanceled = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(-1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(-4500, CURRENCY),
|
||||
|
|
@ -357,6 +388,7 @@ export const ProviderSaleCanceled = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(-2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
|
|
@ -364,6 +396,7 @@ export const ProviderSaleCanceled = {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
quantity: new Decimal(-1),
|
||||
unitPrice: new Money(2000, CURRENCY),
|
||||
lineTotal: new Money(-2000, CURRENCY),
|
||||
|
|
@ -377,3 +410,55 @@ export const ProviderSaleCanceled = {
|
|||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const SingleDay = {
|
||||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: propTypes.LINE_ITEM_DAY,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(4500, CURRENCY),
|
||||
payoutTotal: new Money(4500, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/day',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(1),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(4500, CURRENCY),
|
||||
reversal: false,
|
||||
},
|
||||
],
|
||||
}),
|
||||
booking: exampleBooking({
|
||||
start: new Date(Date.UTC(2017, 3, 14)),
|
||||
end: new Date(Date.UTC(2017, 3, 15)),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
export const MultipleDays = {
|
||||
component: BookingBreakdown,
|
||||
props: {
|
||||
userRole: 'customer',
|
||||
unitType: propTypes.LINE_ITEM_DAY,
|
||||
transaction: exampleTransaction({
|
||||
payinTotal: new Money(9000, CURRENCY),
|
||||
payoutTotal: new Money(9000, CURRENCY),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/day',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
unitPrice: new Money(4500, CURRENCY),
|
||||
lineTotal: new Money(9000, CURRENCY),
|
||||
reversal: false,
|
||||
},
|
||||
],
|
||||
}),
|
||||
booking: exampleBooking({
|
||||
start: new Date(Date.UTC(2017, 3, 14)),
|
||||
end: new Date(Date.UTC(2017, 3, 16)),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,12 +3,14 @@
|
|||
* I.e. dates and other details related to payment decision in receipt format.
|
||||
*/
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { bool, oneOf, string } from 'prop-types';
|
||||
import { FormattedMessage, FormattedHTMLMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import moment from 'moment';
|
||||
import classNames from 'classnames';
|
||||
import { types } from '../../util/sdkLoader';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { daysBetween } from '../../util/dates';
|
||||
|
||||
import css from './BookingBreakdown.css';
|
||||
|
||||
|
|
@ -24,73 +26,103 @@ const isValidCommission = commissionLineItem => {
|
|||
);
|
||||
};
|
||||
|
||||
export const BookingBreakdownComponent = props => {
|
||||
const { rootClassName, className, userRole, transaction, booking, intl } = props;
|
||||
const UnitPriceItem = props => {
|
||||
const { transaction, unitType, intl } = props;
|
||||
const isNightly = unitType === propTypes.LINE_ITEM_NIGHT;
|
||||
const unitPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && !item.reversal
|
||||
);
|
||||
const formattedUnitPrice = formatMoney(intl, unitPurchase.unitPrice);
|
||||
|
||||
const isProvider = userRole === 'provider';
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
return (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage
|
||||
id={isNightly ? 'BookingBreakdown.pricePerNight' : 'BookingBreakdown.pricePerDay'}
|
||||
/>
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedUnitPrice}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
UnitPriceItem.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
unitType: oneOf([propTypes.LINE_ITEM_NIGHT, propTypes.LINE_ITEM_DAY]).isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const UnitsItem = props => {
|
||||
const { transaction, booking, unitType, intl } = props;
|
||||
|
||||
const { start: startDate, end: endDateRaw } = booking.attributes;
|
||||
const isNightly = unitType === propTypes.LINE_ITEM_NIGHT;
|
||||
const isSingleDay = !isNightly && daysBetween(startDate, endDateRaw) === 1;
|
||||
|
||||
const endDay = isNightly ? endDateRaw : moment(endDateRaw).subtract(1, 'days');
|
||||
|
||||
const dateFormatOptions = {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
};
|
||||
const bookingPeriod = (
|
||||
const bookingPeriod = isSingleDay ? (
|
||||
intl.formatDate(startDate, dateFormatOptions)
|
||||
) : (
|
||||
<FormattedMessage
|
||||
id="BookingBreakdown.bookingPeriod"
|
||||
values={{
|
||||
bookingStart: (
|
||||
<span className={css.nowrap}>
|
||||
{intl.formatDate(booking.attributes.start, dateFormatOptions)}
|
||||
</span>
|
||||
<span className={css.nowrap}>{intl.formatDate(startDate, dateFormatOptions)}</span>
|
||||
),
|
||||
bookingEnd: (
|
||||
<span className={css.nowrap}>
|
||||
{intl.formatDate(booking.attributes.end, dateFormatOptions)}
|
||||
</span>
|
||||
<span className={css.nowrap}>{intl.formatDate(endDay, dateFormatOptions)}</span>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const nightPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === 'line-item/night' && !item.reversal
|
||||
const unitPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && !item.reversal
|
||||
);
|
||||
const providerCommissionLineItem = transaction.attributes.lineItems.find(
|
||||
item => item.code === 'line-item/provider-commission' && !item.reversal
|
||||
);
|
||||
const refund = transaction.attributes.lineItems.find(
|
||||
item => item.code === 'line-item/night' && item.reversal
|
||||
);
|
||||
const commissionRefund = transaction.attributes.lineItems.find(
|
||||
item => item.code === 'line-item/provider-commission' && item.reversal
|
||||
const unitCount = unitPurchase.quantity.toFixed();
|
||||
const unitCountMessage = (
|
||||
<FormattedHTMLMessage
|
||||
id={isNightly ? 'BookingBreakdown.nightCount' : 'BookingBreakdown.dayCount'}
|
||||
values={{ count: unitCount }}
|
||||
/>
|
||||
);
|
||||
|
||||
const refundInfo = refund ? (
|
||||
return (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.refund" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formatMoney(intl, refund.lineTotal)}</span>
|
||||
<span className={css.itemLabel}>{bookingPeriod}</span>
|
||||
<span className={css.itemValue}>{unitCountMessage}</span>
|
||||
</div>
|
||||
) : null;
|
||||
);
|
||||
};
|
||||
|
||||
const nightCount = nightPurchase.quantity.toFixed();
|
||||
const nightCountMessage = (
|
||||
<FormattedHTMLMessage id="BookingBreakdown.nightCount" values={{ count: nightCount }} />
|
||||
UnitsItem.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
booking: propTypes.booking.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const SubTotalItemMaybe = props => {
|
||||
const { transaction, unitType, isProvider, intl } = props;
|
||||
|
||||
const refund = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && item.reversal
|
||||
);
|
||||
|
||||
const formattedUnitPrice = formatMoney(intl, nightPurchase.unitPrice);
|
||||
|
||||
// If commission is passed it will be shown as a fee already reduces from the total price
|
||||
let commissionInfo = null;
|
||||
|
||||
// Show night purchase line total (unit price * quantity) as a subtotal.
|
||||
// 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 nights (e.g. week, month, year).
|
||||
// line item types than the defined unit type (e.g. week, month, year).
|
||||
const showSubTotal = isProvider || refund;
|
||||
const formattedSubTotal = formatMoney(intl, nightPurchase.lineTotal);
|
||||
const subTotalInfo = showSubTotal ? (
|
||||
const unitPurchase = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && !item.reversal
|
||||
);
|
||||
const formattedSubTotal = formatMoney(intl, unitPurchase.lineTotal);
|
||||
|
||||
return showSubTotal ? (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.subTotal" />
|
||||
|
|
@ -98,6 +130,26 @@ export const BookingBreakdownComponent = props => {
|
|||
<span className={css.itemValue}>{formattedSubTotal}</span>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
SubTotalItemMaybe.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
isProvider: bool.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const CommissionItemMaybe = props => {
|
||||
const { transaction, isProvider, intl } = props;
|
||||
|
||||
const providerCommissionLineItem = transaction.attributes.lineItems.find(
|
||||
item => item.code === propTypes.LINE_ITEM_PROVIDER_COMMISSION && !item.reversal
|
||||
);
|
||||
const commissionRefund = transaction.attributes.lineItems.find(
|
||||
item => item.code === propTypes.LINE_ITEM_PROVIDER_COMMISSION && item.reversal
|
||||
);
|
||||
|
||||
// If commission is passed it will be shown as a fee already reduces from the total price
|
||||
let commissionItem = null;
|
||||
|
||||
if (isProvider) {
|
||||
if (!isValidCommission(providerCommissionLineItem)) {
|
||||
|
|
@ -109,7 +161,7 @@ export const BookingBreakdownComponent = props => {
|
|||
const commission = providerCommissionLineItem.lineTotal;
|
||||
const formattedCommission = commission ? formatMoney(intl, commission) : null;
|
||||
|
||||
commissionInfo = commissionRefund ? null : (
|
||||
commissionItem = commissionRefund ? null : (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.commission" />
|
||||
|
|
@ -119,6 +171,43 @@ export const BookingBreakdownComponent = props => {
|
|||
);
|
||||
}
|
||||
|
||||
return commissionItem;
|
||||
};
|
||||
|
||||
CommissionItemMaybe.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
isProvider: bool.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const RefundItemMaybe = props => {
|
||||
const { transaction, unitType, intl } = props;
|
||||
|
||||
const refund = transaction.attributes.lineItems.find(
|
||||
item => item.code === unitType && item.reversal
|
||||
);
|
||||
|
||||
return refund ? (
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.refund" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formatMoney(intl, refund.lineTotal)}</span>
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
|
||||
RefundItemMaybe.propTypes = {
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export const BookingBreakdownComponent = props => {
|
||||
const { rootClassName, className, userRole, unitType, transaction, booking, intl } = props;
|
||||
|
||||
const isProvider = userRole === 'provider';
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
||||
let providerTotalMessageId = 'BookingBreakdown.providerTotalDefault';
|
||||
if (propTypes.txIsDelivered(transaction)) {
|
||||
providerTotalMessageId = 'BookingBreakdown.providerTotalDelivered';
|
||||
|
|
@ -141,19 +230,16 @@ export const BookingBreakdownComponent = props => {
|
|||
|
||||
return (
|
||||
<div className={classes}>
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>
|
||||
<FormattedMessage id="BookingBreakdown.pricePerNight" />
|
||||
</span>
|
||||
<span className={css.itemValue}>{formattedUnitPrice}</span>
|
||||
</div>
|
||||
<div className={css.lineItem}>
|
||||
<span className={css.itemLabel}>{bookingPeriod}</span>
|
||||
<span className={css.itemValue}>{nightCountMessage}</span>
|
||||
</div>
|
||||
{subTotalInfo}
|
||||
{commissionInfo}
|
||||
{refundInfo}
|
||||
<UnitPriceItem transaction={transaction} unitType={unitType} intl={intl} />
|
||||
<UnitsItem transaction={transaction} booking={booking} unitType={unitType} intl={intl} />
|
||||
<SubTotalItemMaybe
|
||||
transaction={transaction}
|
||||
unitType={unitType}
|
||||
isProvider={isProvider}
|
||||
intl={intl}
|
||||
/>
|
||||
<CommissionItemMaybe transaction={transaction} isProvider={isProvider} intl={intl} />
|
||||
<RefundItemMaybe transaction={transaction} unitType={unitType} intl={intl} />
|
||||
<hr className={css.totalDivider} />
|
||||
<div className={css.lineItem}>
|
||||
<div className={css.totalLabel}>{totalLabel}</div>
|
||||
|
|
@ -165,13 +251,12 @@ export const BookingBreakdownComponent = props => {
|
|||
|
||||
BookingBreakdownComponent.defaultProps = { rootClassName: null, className: null };
|
||||
|
||||
const { oneOf, string } = PropTypes;
|
||||
|
||||
BookingBreakdownComponent.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
|
||||
userRole: oneOf(['customer', 'provider']).isRequired,
|
||||
unitType: oneOf([propTypes.LINE_ITEM_NIGHT, propTypes.LINE_ITEM_DAY]).isRequired,
|
||||
transaction: propTypes.transaction.isRequired,
|
||||
booking: propTypes.booking.isRequired,
|
||||
|
||||
|
|
|
|||
|
|
@ -36,12 +36,14 @@ describe('BookingBreakdown', () => {
|
|||
const tree = renderDeep(
|
||||
<BookingBreakdownComponent
|
||||
userRole="customer"
|
||||
unitType={propTypes.LINE_ITEM_NIGHT}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(2000, 'USD'),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new Money(2000, 'USD'),
|
||||
unitPrice: new Money(1000, 'USD'),
|
||||
|
|
@ -63,12 +65,14 @@ describe('BookingBreakdown', () => {
|
|||
const tree = renderDeep(
|
||||
<BookingBreakdownComponent
|
||||
userRole="customer"
|
||||
unitType={propTypes.LINE_ITEM_NIGHT}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(2000, 'USD'),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new Money(2000, 'USD'),
|
||||
unitPrice: new Money(1000, 'USD'),
|
||||
|
|
@ -90,12 +94,14 @@ describe('BookingBreakdown', () => {
|
|||
const tree = renderDeep(
|
||||
<BookingBreakdownComponent
|
||||
userRole="provider"
|
||||
unitType={propTypes.LINE_ITEM_NIGHT}
|
||||
transaction={exampleTransaction({
|
||||
payinTotal: new Money(2000, 'USD'),
|
||||
payoutTotal: new Money(1800, 'USD'),
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new Money(2000, 'USD'),
|
||||
unitPrice: new Money(1000, 'USD'),
|
||||
|
|
@ -103,6 +109,7 @@ describe('BookingBreakdown', () => {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
lineTotal: new Money(-200, 'USD'),
|
||||
unitPrice: new Money(-200, 'USD'),
|
||||
reversal: false,
|
||||
|
|
@ -122,6 +129,7 @@ describe('BookingBreakdown', () => {
|
|||
const tree = renderDeep(
|
||||
<BookingBreakdownComponent
|
||||
userRole="provider"
|
||||
unitType={propTypes.LINE_ITEM_NIGHT}
|
||||
transaction={exampleTransaction({
|
||||
lastTransition: propTypes.TX_TRANSITION_CANCEL,
|
||||
payinTotal: new Money(0, 'USD'),
|
||||
|
|
@ -129,6 +137,7 @@ describe('BookingBreakdown', () => {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new Money(2000, 'USD'),
|
||||
unitPrice: new Money(1000, 'USD'),
|
||||
|
|
@ -136,6 +145,7 @@ describe('BookingBreakdown', () => {
|
|||
},
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(-2),
|
||||
lineTotal: new Money(-2000, 'USD'),
|
||||
unitPrice: new Money(1000, 'USD'),
|
||||
|
|
@ -143,6 +153,7 @@ describe('BookingBreakdown', () => {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
quantity: new Decimal(1),
|
||||
lineTotal: new Money(-200, 'USD'),
|
||||
unitPrice: new Money(-200, 'USD'),
|
||||
|
|
@ -150,6 +161,7 @@ describe('BookingBreakdown', () => {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
quantity: new Decimal(-1),
|
||||
lineTotal: new Money(200, 'USD'),
|
||||
unitPrice: new Money(-200, 'USD'),
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.perNight {
|
||||
.perUnit {
|
||||
/* Font */
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
color: var(--matterColor);
|
||||
|
|
|
|||
|
|
@ -64,8 +64,8 @@ export const ListingCardComponent = props => {
|
|||
<div className={css.priceValue} title={priceTitle}>
|
||||
{formattedPrice}
|
||||
</div>
|
||||
<div className={css.perNight}>
|
||||
<FormattedMessage id="ListingCard.perNight" />
|
||||
<div className={css.perUnit}>
|
||||
<FormattedMessage id="ListingCard.perUnit" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.mainInfo}>
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ exports[`ListingCard matches snapshot 1`] = `
|
|||
</div>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="ListingCard.perNight"
|
||||
id="ListingCard.perUnit"
|
||||
values={Object {}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -220,7 +220,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.perNight {
|
||||
.perUnit {
|
||||
/* Font */
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
color: var(--matterColor);
|
||||
|
|
|
|||
|
|
@ -219,8 +219,8 @@ export const ManageListingCardComponent = props => {
|
|||
<div className={css.priceValue} title={priceTitle}>
|
||||
{formattedPrice}
|
||||
</div>
|
||||
<div className={css.perNight}>
|
||||
<FormattedMessage id="ManageListingCard.perNight" />
|
||||
<div className={css.perUnit}>
|
||||
<FormattedMessage id="ManageListingCard.perUnit" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.mainInfo}>
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ exports[`ManageListingCard matches snapshot 1`] = `
|
|||
</div>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="ManageListingCard.perNight"
|
||||
id="ManageListingCard.perUnit"
|
||||
values={Object {}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ import {
|
|||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
} from '../../components';
|
||||
import config from '../../config';
|
||||
|
||||
import css from './TransactionPanel.css';
|
||||
|
||||
// Functional component as a helper to build ActivityFeed section
|
||||
|
|
@ -85,6 +87,7 @@ export const BreakdownMaybe = props => {
|
|||
<BookingBreakdown
|
||||
className={classes}
|
||||
userRole={transactionRole}
|
||||
unitType={config.bookingUnitType}
|
||||
transaction={transaction}
|
||||
booking={transaction.booking}
|
||||
/>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -14,6 +14,13 @@ const i18n = {
|
|||
firstDayOfWeek: 0,
|
||||
};
|
||||
|
||||
// The transaction line item code for the main unit type in bookings.
|
||||
//
|
||||
// Possible values: ['line-item/night', 'line-item/day']
|
||||
//
|
||||
// TODO: Check that this applies when API adds support for daily bookings
|
||||
const bookingUnitType = 'line-item/night';
|
||||
|
||||
// To pass environment variables to the client app in the build
|
||||
// script, react-scripts (and the sharetribe-scripts fork of
|
||||
// react-scripts) require using the REACT_APP_ prefix to avoid
|
||||
|
|
@ -262,6 +269,7 @@ const config = {
|
|||
env,
|
||||
dev,
|
||||
locale,
|
||||
bookingUnitType,
|
||||
i18n,
|
||||
sdk: { clientId: sdkClientId, baseUrl: sdkBaseUrl },
|
||||
currency,
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ const estimatedNightlyTransaction = (bookingStart, bookingEnd, unitPrice) => {
|
|||
payoutTotal: totalPrice,
|
||||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
code: propTypes.LINE_ITEM_NIGHT,
|
||||
includeFor: ['customer', 'provider'],
|
||||
unitPrice: unitPrice,
|
||||
quantity: new Decimal(nightCount),
|
||||
lineTotal: totalPrice,
|
||||
|
|
@ -77,12 +78,14 @@ const estimatedBreakdown = (bookingStart, bookingEnd, unitPrice) => {
|
|||
return null;
|
||||
}
|
||||
|
||||
// TODO: estimate daily tx if config is set
|
||||
const tx = estimatedNightlyTransaction(bookingStart, bookingEnd, unitPrice);
|
||||
|
||||
return (
|
||||
<BookingBreakdown
|
||||
className={css.receipt}
|
||||
userRole="customer"
|
||||
unitType={config.bookingUnitType}
|
||||
transaction={tx}
|
||||
booking={tx.booking}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ describe('BookingDatesForm', () => {
|
|||
expect(transaction.attributes.lineItems).toEqual([
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
unitPrice: price,
|
||||
quantity: new Decimal(2),
|
||||
lineTotal: new Money(2198, 'USD'),
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
import { StripePaymentForm } from '../../containers';
|
||||
import { isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { initiateOrder, setInitialValues, speculateTransaction } from './CheckoutPage.duck';
|
||||
import config from '../../config';
|
||||
|
||||
import { storeData, storedData, clearData } from './CheckoutPageSessionHelpers';
|
||||
import LogoIcon from './LogoIcon';
|
||||
|
|
@ -203,6 +204,7 @@ export class CheckoutPageComponent extends Component {
|
|||
<BookingBreakdown
|
||||
className={css.bookingBreakdown}
|
||||
userRole="customer"
|
||||
unitType={config.bookingUnitType}
|
||||
transaction={currentTransaction}
|
||||
booking={currentBooking}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const EditListingPricingFormComponent = props => {
|
|||
updateInProgress,
|
||||
} = props;
|
||||
|
||||
const pricePerNightMessage = intl.formatMessage({ id: 'EditListingPricingForm.pricePerNight' });
|
||||
const pricePerUnitMessage = intl.formatMessage({ id: 'EditListingPricingForm.pricePerUnit' });
|
||||
const priceRequiredMessage = intl.formatMessage({ id: 'EditListingPricingForm.priceRequired' });
|
||||
const pricePlaceholderMessage = intl.formatMessage({
|
||||
id: 'EditListingPricingForm.priceInputPlaceholder',
|
||||
|
|
@ -50,7 +50,7 @@ export const EditListingPricingFormComponent = props => {
|
|||
className={css.priceInput}
|
||||
autoFocus
|
||||
name="price"
|
||||
label={pricePerNightMessage}
|
||||
label={pricePerUnitMessage}
|
||||
placeholder={pricePlaceholderMessage}
|
||||
currencyConfig={config.currencyConfig}
|
||||
validate={[required(priceRequiredMessage)]}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ exports[`EditListingPricingForm matches snapshot 1`] = `
|
|||
<label
|
||||
htmlFor="EditListingPricingForm.CurrencyInputField"
|
||||
>
|
||||
EditListingPricingForm.pricePerNight
|
||||
EditListingPricingForm.pricePerUnit
|
||||
</label>
|
||||
<input
|
||||
autoFocus={true}
|
||||
|
|
|
|||
|
|
@ -98,6 +98,10 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -111,6 +115,9 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
@ -213,6 +220,10 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -226,6 +237,9 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
@ -486,6 +500,10 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -499,6 +517,9 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
@ -601,6 +622,10 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -614,6 +639,9 @@ exports[`InboxPage matches snapshot 3`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.desktopPerNight {
|
||||
.desktopPerUnit {
|
||||
/* Font */
|
||||
@apply --marketplaceH5FontStyles;
|
||||
color: var(--matterColor);
|
||||
|
|
@ -565,7 +565,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.perNight {
|
||||
.perUnit {
|
||||
/* Font */
|
||||
@apply --marketplaceTinyFontStyles;
|
||||
color: var(--matterColor);
|
||||
|
|
|
|||
|
|
@ -505,8 +505,8 @@ export class ListingPageComponent extends Component {
|
|||
<div className={css.desktopPriceValue} title={priceTitle}>
|
||||
{formattedPrice}
|
||||
</div>
|
||||
<div className={css.desktopPerNight}>
|
||||
<FormattedMessage id="ListingPage.perNight" />
|
||||
<div className={css.desktopPerUnit}>
|
||||
<FormattedMessage id="ListingPage.perUnit" />
|
||||
</div>
|
||||
</div>
|
||||
<div className={css.heading}>
|
||||
|
|
@ -618,8 +618,8 @@ export class ListingPageComponent extends Component {
|
|||
<div className={css.priceValue} title={priceTitle}>
|
||||
{formattedPrice}
|
||||
</div>
|
||||
<div className={css.perNight}>
|
||||
<FormattedMessage id="ListingPage.perNight" />
|
||||
<div className={css.perUnit}>
|
||||
<FormattedMessage id="ListingPage.perUnit" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
</div>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="ListingPage.perNight"
|
||||
id="ListingPage.perUnit"
|
||||
values={Object {}}
|
||||
/>
|
||||
</div>
|
||||
|
|
@ -398,7 +398,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
</div>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id="ListingPage.perNight"
|
||||
id="ListingPage.perUnit"
|
||||
values={Object {}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -68,6 +68,10 @@ exports[`TransactionPage - Order matches snapshot 1`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -81,6 +85,9 @@ exports[`TransactionPage - Order matches snapshot 1`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
@ -259,6 +266,10 @@ exports[`TransactionPage - Sale matches snapshot 1`] = `
|
|||
"lineItems": Array [
|
||||
Object {
|
||||
"code": "line-item/night",
|
||||
"includeFor": Array [
|
||||
"customer",
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": 1000,
|
||||
"currency": "USD",
|
||||
|
|
@ -272,6 +283,9 @@ exports[`TransactionPage - Sale matches snapshot 1`] = `
|
|||
},
|
||||
Object {
|
||||
"code": "line-item/provider-commission",
|
||||
"includeFor": Array [
|
||||
"provider",
|
||||
],
|
||||
"lineTotal": Money {
|
||||
"amount": -100,
|
||||
"currency": "USD",
|
||||
|
|
|
|||
|
|
@ -39,7 +39,9 @@
|
|||
"Avatar.bannedUserDisplayName": "Banned user",
|
||||
"BookingBreakdown.bookingPeriod": "{bookingStart} – {bookingEnd}",
|
||||
"BookingBreakdown.commission": "Saunatime fee",
|
||||
"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",
|
||||
"BookingBreakdown.pricePerNight": "Price per night",
|
||||
"BookingBreakdown.providerTotalCanceled": "Total price",
|
||||
"BookingBreakdown.providerTotalDeclined": "You would have made",
|
||||
|
|
@ -143,7 +145,7 @@
|
|||
"EditListingPhotosPanel.payoutModalTitlePayoutPreferences": "Payout preferences",
|
||||
"EditListingPhotosPanel.title": "Edit the photos of {listingTitle}",
|
||||
"EditListingPricingForm.priceInputPlaceholder": "Choose your price…",
|
||||
"EditListingPricingForm.pricePerNight": "Price per night in euros",
|
||||
"EditListingPricingForm.pricePerUnit": "Price per night in euros",
|
||||
"EditListingPricingForm.priceRequired": "You need to add a valid price.",
|
||||
"EditListingPricingForm.updateFailed": "Failed to update listing. Please try again.",
|
||||
"EditListingPricingPanel.createListingTitle": "How much does it cost?",
|
||||
|
|
@ -234,7 +236,7 @@
|
|||
"LandingPage.schemaDescription": "Book a sauna using Saunatime or earn some income by sharing your sauna",
|
||||
"LandingPage.schemaTitle": "Book saunas everywhere | {siteTitle}",
|
||||
"ListingCard.hostedBy": "Hosted by {authorName}.",
|
||||
"ListingCard.perNight": "per night",
|
||||
"ListingCard.perUnit": "per night",
|
||||
"ListingCard.unsupportedPrice": "({currency})",
|
||||
"ListingCard.unsupportedPriceTitle": "Unsupported currency ({currency})",
|
||||
"ListingPage.bannedUserDisplayName": "Banned user",
|
||||
|
|
@ -260,7 +262,7 @@
|
|||
"ListingPage.locationTitle": "Location",
|
||||
"ListingPage.ownClosedListing": "Your listing has been closed and can't be booked.",
|
||||
"ListingPage.ownListing": "This is your own listing.",
|
||||
"ListingPage.perNight": "per night",
|
||||
"ListingPage.perUnit": "per night",
|
||||
"ListingPage.reviewsError": "Loading reviews failed.",
|
||||
"ListingPage.reviewsHeading": "Reviews ({count})",
|
||||
"ListingPage.schemaTitle": "{title} - {price} | {siteTitle}",
|
||||
|
|
@ -282,7 +284,7 @@
|
|||
"ManageListingCard.closedListing": "This listing is closed and not visible on the marketplace.",
|
||||
"ManageListingCard.edit": "Edit",
|
||||
"ManageListingCard.openListing": "Open listing",
|
||||
"ManageListingCard.perNight": "per night",
|
||||
"ManageListingCard.perUnit": "per night",
|
||||
"ManageListingCard.unsupportedPrice": "({currency})",
|
||||
"ManageListingCard.unsupportedPriceTitle": "Unsupported currency ({currency})",
|
||||
"ManageListingCard.viewListing": "View listing",
|
||||
|
|
|
|||
|
|
@ -23,6 +23,26 @@ export const nightsBetween = (startDate, endDate) => {
|
|||
return nights;
|
||||
};
|
||||
|
||||
/**
|
||||
* Calculate the number of days between the given dates
|
||||
*
|
||||
* @param {Date} startDate start of the time period
|
||||
* @param {Date} endDate end of the time period. NOTE: with daily
|
||||
* bookings, it is expected that this date is the exclusive end date,
|
||||
* i.e. the last day of the booking is the previous date of this end
|
||||
* date.
|
||||
*
|
||||
* @throws Will throw if the end date is before the start date
|
||||
* @returns {Number} number of days between the given dates
|
||||
*/
|
||||
export const daysBetween = (startDate, endDate) => {
|
||||
const days = moment(endDate).diff(startDate, 'days');
|
||||
if (days < 0) {
|
||||
throw new Error('End date cannot be before start date');
|
||||
}
|
||||
return days;
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the given date
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { fakeIntl } from './test-data';
|
||||
import { nightsBetween, formatDate } from './dates';
|
||||
import { nightsBetween, daysBetween, formatDate } from './dates';
|
||||
|
||||
describe('date utils', () => {
|
||||
describe('nightsBetween()', () => {
|
||||
|
|
@ -24,6 +24,28 @@ describe('date utils', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('daysBetween()', () => {
|
||||
it('should fail if end date is before start date', () => {
|
||||
const start = new Date(2017, 0, 2);
|
||||
const end = new Date(2017, 0, 1);
|
||||
expect(() => daysBetween(start, end)).toThrow('End date cannot be before start date');
|
||||
});
|
||||
it('should handle equal start and end dates', () => {
|
||||
const d = new Date(2017, 0, 1);
|
||||
expect(daysBetween(d, d)).toEqual(0);
|
||||
});
|
||||
it('should calculate night count for a single day', () => {
|
||||
const start = new Date(2017, 0, 1);
|
||||
const end = new Date(2017, 0, 2);
|
||||
expect(daysBetween(start, end)).toEqual(1);
|
||||
});
|
||||
it('should calculate day count', () => {
|
||||
const start = new Date(2017, 0, 1);
|
||||
const end = new Date(2017, 0, 3);
|
||||
expect(daysBetween(start, end)).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatDate()', () => {
|
||||
/*
|
||||
NOTE: These are not really testing the formatting properly since
|
||||
|
|
|
|||
|
|
@ -300,6 +300,15 @@ export const review = shape({
|
|||
subject: user,
|
||||
});
|
||||
|
||||
export const LINE_ITEM_NIGHT = 'line-item/night';
|
||||
|
||||
// TODO: This is speculative, check again when the API adds the day support
|
||||
export const LINE_ITEM_DAY = 'line-item/day';
|
||||
|
||||
export const LINE_ITEM_PROVIDER_COMMISSION = 'line-item/provider-commission';
|
||||
|
||||
const LINE_ITEMS = [LINE_ITEM_NIGHT, LINE_ITEM_DAY, LINE_ITEM_PROVIDER_COMMISSION];
|
||||
|
||||
// Denormalised transaction object
|
||||
export const transaction = shape({
|
||||
id: uuid.isRequired,
|
||||
|
|
@ -316,7 +325,8 @@ export const transaction = shape({
|
|||
|
||||
lineItems: arrayOf(
|
||||
shape({
|
||||
code: string.isRequired,
|
||||
code: oneOf(LINE_ITEMS).isRequired,
|
||||
includeFor: arrayOf(oneOf(['customer', 'provider'])).isRequired,
|
||||
quantity: instanceOf(Decimal),
|
||||
unitPrice: money.isRequired,
|
||||
lineTotal: money.isRequired,
|
||||
|
|
|
|||
|
|
@ -134,6 +134,7 @@ export const createTransaction = options => {
|
|||
lineItems: [
|
||||
{
|
||||
code: 'line-item/night',
|
||||
includeFor: ['customer', 'provider'],
|
||||
quantity: new Decimal(nightCount),
|
||||
unitPrice: new Money(total.amount / nightCount, total.currency),
|
||||
lineTotal: total,
|
||||
|
|
@ -141,6 +142,7 @@ export const createTransaction = options => {
|
|||
},
|
||||
{
|
||||
code: 'line-item/provider-commission',
|
||||
includeFor: ['provider'],
|
||||
unitPrice: new Money(commission.amount * -1, commission.currency),
|
||||
lineTotal: new Money(commission.amount * -1, commission.currency),
|
||||
reversal: false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue