Use the whole tx and booking objects breakdown

This commit is contained in:
Kimmo Puputti 2017-09-05 14:14:41 +03:00
parent 29278ae781
commit fb305e42fe
17 changed files with 730 additions and 442 deletions

View file

@ -3,160 +3,219 @@ import { types } from '../../util/sdkLoader';
import * as propTypes from '../../util/propTypes';
import BookingBreakdown from './BookingBreakdown';
const { UUID, Money } = types;
const exampleBooking = attributes => {
return {
id: new UUID('example-booking'),
type: 'booking',
attributes,
};
};
const exampleTransaction = params => {
const created = new Date(Date.UTC(2017, 1, 1));
return {
id: new UUID('example-transaction'),
type: 'transaction',
attributes: {
createdAt: created,
lastTransitionedAt: created,
lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE,
state: propTypes.TX_STATE_PREAUTHORIZED,
// payinTotal, payoutTotal, and lineItems required in params
...params,
},
};
};
export const Checkout = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_PREAUTHORIZED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
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'),
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
}),
},
};
export const CustomerOrder = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_PREAUTHORIZED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
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'),
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
}),
},
};
export const ProviderSale = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_PREAUTHORIZED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
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'),
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(7000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
}),
},
};
export const ProviderSaleZeroCommission = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_PREAUTHORIZED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
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'),
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(0, 'USD'),
lineTotal: new Money(0, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
}),
},
};
export const ProviderSaleSingleNight = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_PREAUTHORIZED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 15)),
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'),
transaction: exampleTransaction({
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 15)),
}),
},
};
export const ProviderSaleRejected = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_REJECTED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 15)),
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'),
transaction: exampleTransaction({
state: propTypes.TX_STATE_REJECTED,
lastTransition: propTypes.TX_TRANSITION_REJECT,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 15)),
}),
},
};
export const ProviderSaleDelivered = {
component: BookingBreakdown,
props: {
transactionState: propTypes.TX_STATE_DELIVERED,
bookingStart: new Date(Date.UTC(2017, 3, 14)),
bookingEnd: new Date(Date.UTC(2017, 3, 15)),
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'),
transaction: exampleTransaction({
state: propTypes.TX_STATE_DELIVERED,
lastTransition: propTypes.TX_TRANSITION_MARK_DELIVERED,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
},
],
}),
booking: exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 15)),
}),
},
};

View file

@ -16,26 +16,15 @@ export const BookingBreakdownComponent = props => {
const {
rootClassName,
className,
transactionState,
bookingStart,
bookingEnd,
payinTotal,
payoutTotal,
lineItems,
userRole,
transaction,
booking,
intl,
} = props;
const isProvider = userRole === 'provider';
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: 'short',
@ -47,20 +36,24 @@ export const BookingBreakdownComponent = props => {
values={{
bookingStart: (
<span className={css.nowrap}>
{intl.formatDate(bookingStart, dateFormatOptions)}
{intl.formatDate(booking.attributes.start, dateFormatOptions)}
</span>
),
bookingEnd: (
<span className={css.nowrap}>
{intl.formatDate(bookingEnd, dateFormatOptions)}
{intl.formatDate(booking.attributes.end, dateFormatOptions)}
</span>
),
}}
/>
);
const nightPurchase = lineItems.find(item => item.code === 'line-item/night');
const providerCommission = lineItems.find(item => item.code === 'line-item/provider-commission');
const nightPurchase = transaction.attributes.lineItems.find(
item => item.code === 'line-item/night'
);
const providerCommission = transaction.attributes.lineItems.find(
item => item.code === 'line-item/provider-commission'
);
const nightCount = nightPurchase.quantity.toFixed();
const nightCountMessage = (
@ -74,7 +67,7 @@ export const BookingBreakdownComponent = props => {
let subTotalInfo = null;
let commissionInfo = null;
if (userRole === 'provider') {
if (isProvider) {
// TODO: Calculate subtotal from provider total and the commission
const unitPriceAsNumber = convertMoneyToNumber(nightPurchase.unitPrice);
const subTotal = new Decimal(nightCount).times(unitPriceAsNumber).toNumber();
@ -103,17 +96,19 @@ export const BookingBreakdownComponent = props => {
}
let providerTotalMessageId = 'BookingBreakdown.providerTotalDefault';
if (transactionState === propTypes.TX_STATE_DELIVERED) {
if (transaction.attributes.state === propTypes.TX_STATE_DELIVERED) {
providerTotalMessageId = 'BookingBreakdown.providerTotalDelivered';
} else if (transactionState === propTypes.TX_STATE_REJECTED) {
} else if (transaction.attributes.state === propTypes.TX_STATE_REJECTED) {
providerTotalMessageId = 'BookingBreakdown.providerTotalRejected';
}
const totalLabel = userRole === 'customer'
? <FormattedMessage id="BookingBreakdown.total" />
: <FormattedMessage id={providerTotalMessageId} />;
const totalLabel = isProvider
? <FormattedMessage id={providerTotalMessageId} />
: <FormattedMessage id="BookingBreakdown.total" />;
const totalPrice = userRole === 'customer' ? payinTotal : payoutTotal;
const totalPrice = isProvider
? transaction.attributes.payoutTotal
: transaction.attributes.payinTotal;
const formattedTotalPrice = formatMoney(intl, totalPrice);
return (
@ -145,33 +140,17 @@ export const BookingBreakdownComponent = props => {
);
};
BookingBreakdownComponent.defaultProps = {
rootClassName: null,
className: null,
payinTotal: null,
payoutTotal: null,
};
BookingBreakdownComponent.defaultProps = { rootClassName: null, className: null };
const { arrayOf, instanceOf, oneOf, shape, string } = PropTypes;
const lineItem = shape({
code: string.isRequired,
quantity: instanceOf(Decimal),
unitPrice: propTypes.money,
lineTotal: propTypes.money,
});
const { oneOf, string } = PropTypes;
BookingBreakdownComponent.propTypes = {
rootClassName: string,
className: string,
transactionState: oneOf(propTypes.TX_STATES).isRequired,
bookingStart: instanceOf(Date).isRequired,
bookingEnd: instanceOf(Date).isRequired,
lineItems: arrayOf(lineItem).isRequired,
userRole: oneOf(['customer', 'provider']).isRequired,
payinTotal: propTypes.money, // required if userRole === customer
payoutTotal: propTypes.money, // required if userRole === provider
transaction: propTypes.transaction.isRequired,
booking: propTypes.booking.isRequired,
// from injectIntl
intl: intlShape.isRequired,

View file

@ -6,23 +6,54 @@ import { types } from '../../util/sdkLoader';
import * as propTypes from '../../util/propTypes';
import { BookingBreakdownComponent } from './BookingBreakdown';
const { UUID, Money } = types;
const exampleBooking = attributes => {
return {
id: new UUID('example-booking'),
type: 'booking',
attributes,
};
};
const exampleTransaction = params => {
const created = new Date(Date.UTC(2017, 1, 1));
return {
id: new UUID('example-transaction'),
type: 'transaction',
attributes: {
createdAt: created,
lastTransitionedAt: created,
lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE,
state: propTypes.TX_STATE_PREAUTHORIZED,
// payinTotal, payoutTotal, and lineItems required in params
...params,
},
};
};
describe('BookingBreakdown', () => {
it('pretransaction data matches snapshot', () => {
const tree = renderDeep(
<BookingBreakdownComponent
transactionState={propTypes.TX_STATE_PREAUTHORIZED}
bookingStart={new Date(Date.UTC(2017, 3, 14))}
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
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'),
},
]}
transaction={exampleTransaction({
payinTotal: new Money(2000, 'USD'),
payoutTotal: new Money(2000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
lineTotal: new Money(2000, 'USD'),
unitPrice: new Money(1000, 'USD'),
},
],
})}
booking={exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
})}
intl={fakeIntl}
/>
);
@ -32,19 +63,23 @@ describe('BookingBreakdown', () => {
it('customer transaction data matches snapshot', () => {
const tree = renderDeep(
<BookingBreakdownComponent
transactionState={propTypes.TX_STATE_PREAUTHORIZED}
bookingStart={new Date(Date.UTC(2017, 3, 14))}
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
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'),
},
]}
transaction={exampleTransaction({
payinTotal: new Money(2000, 'USD'),
payoutTotal: new Money(2000, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
lineTotal: new Money(2000, 'USD'),
unitPrice: new Money(1000, 'USD'),
},
],
})}
booking={exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
})}
intl={fakeIntl}
/>
);
@ -54,25 +89,28 @@ describe('BookingBreakdown', () => {
it('provider transaction data matches snapshot', () => {
const tree = renderDeep(
<BookingBreakdownComponent
transactionState={propTypes.TX_STATE_PREAUTHORIZED}
bookingStart={new Date(Date.UTC(2017, 3, 14))}
bookingEnd={new Date(Date.UTC(2017, 3, 16))}
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'),
},
]}
transaction={exampleTransaction({
payinTotal: new Money(2000, 'USD'),
payoutTotal: new Money(1800, 'USD'),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
lineTotal: new Money(2000, 'USD'),
unitPrice: new Money(1000, 'USD'),
},
{
code: 'line-item/provider-commission',
lineTotal: new Money(-200, 'USD'),
unitPrice: new Money(-200, 'USD'),
},
],
})}
booking={exampleBooking({
start: new Date(Date.UTC(2017, 3, 14)),
end: new Date(Date.UTC(2017, 3, 16)),
})}
intl={fakeIntl}
/>
);

View file

@ -3,33 +3,22 @@ import { FormattedDate, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import * as propTypes from '../../util/propTypes';
import { createSlug } from '../../util/urlHelpers';
import { ensureListing, ensureTransaction, ensureBooking, ensureUser } from '../../util/data';
import { ensureListing, ensureTransaction, ensureUser } from '../../util/data';
import { BookingBreakdown, NamedLink, ResponsiveImage, AvatarMedium } from '../../components';
import css from './OrderDetailsPanel.css';
const breakdown = transaction => {
const tx = ensureTransaction(transaction);
const booking = ensureBooking(tx.booking);
const bookingStart = booking.attributes.start;
const bookingEnd = booking.attributes.end;
const payinTotal = tx.attributes.payinTotal;
const lineItems = tx.attributes.lineItems;
const loaded = transaction && transaction.id && transaction.booking && transaction.booking.id;
if (!bookingStart || !bookingEnd || !payinTotal || !lineItems) {
return null;
}
return (
<BookingBreakdown
transactionState={tx.attributes.state}
className={css.receipt}
bookingStart={bookingStart}
bookingEnd={bookingEnd}
payinTotal={payinTotal}
lineItems={lineItems}
userRole="customer"
/>
);
return loaded
? <BookingBreakdown
className={css.receipt}
userRole="customer"
transaction={transaction}
booking={transaction.booking}
/>
: null;
};
const orderTitle = (orderState, listingLink, customerName, lastTransition) => {

View file

@ -42,6 +42,6 @@ describe('OrderDetailsPanel', () => {
});
const panel = shallow(<OrderDetailsPanel transaction={tx} />);
const breakdownProps = panel.find(BookingBreakdown).props();
expect(breakdownProps.payinTotal).toEqual(new Money(16500, 'USD'));
expect(breakdownProps.transaction.attributes.payinTotal).toEqual(new Money(16500, 'USD'));
});
});

View file

@ -89,42 +89,120 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
values={Object {}} />
</h3>
<BookingBreakdown
bookingEnd={2017-06-13T00:00:00.000Z}
bookingStart={2017-06-10T00:00:00.000Z}
lineItems={
Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
booking={
Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
}
}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"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",
},
"quantity": "3",
"unitPrice": Money {
"amount": 5500,
"payoutTotal": Money {
"amount": 16400,
"currency": "USD",
},
"state": "state/preauthorized",
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"unitPrice": Money {
"amount": -100,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
]
}
payinTotal={
Money {
"amount": 16500,
"currency": "USD",
"customer": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "customer abbreviated name",
"displayName": "customer display name",
},
},
"id": UUID {
"uuid": "customer",
},
"type": "user",
},
"id": UUID {
"uuid": "order-tx",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"open": true,
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"author": null,
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "provider abbreviated name",
"displayName": "provider display name",
},
},
"id": UUID {
"uuid": "provider",
},
"type": "user",
},
"type": "transaction",
}
}
transactionState="state/preauthorized"
userRole="customer" />
</div>
<div>
@ -186,42 +264,120 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
values={Object {}} />
</h3>
<BookingBreakdown
bookingEnd={2017-06-13T00:00:00.000Z}
bookingStart={2017-06-10T00:00:00.000Z}
lineItems={
Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
booking={
Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
}
}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"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",
},
"quantity": "3",
"unitPrice": Money {
"amount": 5500,
"payoutTotal": Money {
"amount": 16400,
"currency": "USD",
},
"state": "state/preauthorized",
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -100,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"unitPrice": Money {
"amount": -100,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
]
}
payinTotal={
Money {
"amount": 16500,
"currency": "USD",
"customer": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "customer abbreviated name",
"displayName": "customer display name",
},
},
"id": UUID {
"uuid": "customer",
},
"type": "user",
},
"id": UUID {
"uuid": "order-tx",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"open": true,
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"author": null,
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "provider abbreviated name",
"displayName": "provider display name",
},
},
"id": UUID {
"uuid": "provider",
},
"type": "user",
},
"type": "transaction",
}
}
transactionState="state/preauthorized"
userRole="customer" />
</div>
</div>

View file

@ -3,7 +3,7 @@ import { FormattedDate, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import * as propTypes from '../../util/propTypes';
import { createSlug } from '../../util/urlHelpers';
import { ensureListing, ensureTransaction, ensureBooking, ensureUser } from '../../util/data';
import { ensureListing, ensureTransaction, ensureUser } from '../../util/data';
import {
AvatarMedium,
BookingBreakdown,
@ -16,28 +16,15 @@ import {
import css from './SaleDetailsPanel.css';
const breakdown = transaction => {
const tx = ensureTransaction(transaction);
const booking = ensureBooking(tx.booking);
const bookingStart = booking.attributes.start;
const bookingEnd = booking.attributes.end;
const payinTotal = tx.attributes.payinTotal;
const payoutTotal = tx.attributes.payoutTotal;
const lineItems = tx.attributes.lineItems;
const loaded = transaction && transaction.id && transaction.booking && transaction.booking.id;
if (!bookingStart || !bookingEnd || !payinTotal || !payoutTotal || !lineItems) {
return null;
}
return (
<BookingBreakdown
transactionState={tx.attributes.state}
bookingStart={bookingStart}
bookingEnd={bookingEnd}
payinTotal={payinTotal}
payoutTotal={payoutTotal}
lineItems={lineItems}
userRole="provider"
/>
);
return loaded
? <BookingBreakdown
userRole="provider"
transaction={transaction}
booking={transaction.booking}
/>
: null;
};
const saleTitle = (saleState, listingLink, customerName) => {

View file

@ -60,6 +60,6 @@ describe('SaleDetailsPanel', () => {
const breakdownProps = panel.find(BookingBreakdown).props();
// Total price for the provider should be transaction total minus the commission.
expect(breakdownProps.payoutTotal).toEqual(new Money(15500, 'USD'));
expect(breakdownProps.transaction.attributes.payoutTotal).toEqual(new Money(15500, 'USD'));
});
});

View file

@ -110,48 +110,109 @@ exports[`SaleDetailsPanel matches snapshot 1`] = `
values={Object {}} />
</h3>
<BookingBreakdown
bookingEnd={2017-06-13T00:00:00.000Z}
bookingStart={2017-06-10T00:00:00.000Z}
lineItems={
Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
booking={
Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
}
}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-10T00:00:00.000Z,
"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": 16500,
"currency": "USD",
},
"quantity": "3",
"unitPrice": Money {
"amount": 5500,
"payoutTotal": Money {
"amount": 15500,
"currency": "USD",
},
"state": "state/preauthorized",
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -1000,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"unitPrice": Money {
"amount": -1000,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
]
}
payinTotal={
Money {
"amount": 16500,
"currency": "USD",
"customer": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
"id": UUID {
"uuid": "sale-tx",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"open": true,
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"author": null,
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": null,
"type": "transaction",
}
}
payoutTotal={
Money {
"amount": 15500,
"currency": "USD",
}
}
transactionState="state/preauthorized"
userRole="provider" />
</div>
<div>
@ -183,48 +244,109 @@ exports[`SaleDetailsPanel matches snapshot 1`] = `
</h3>
<div>
<BookingBreakdown
bookingEnd={2017-06-13T00:00:00.000Z}
bookingStart={2017-06-10T00:00:00.000Z}
lineItems={
Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
booking={
Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
}
}
transaction={
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-10T00:00:00.000Z,
"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": 16500,
"currency": "USD",
},
"quantity": "3",
"unitPrice": Money {
"amount": 5500,
"payoutTotal": Money {
"amount": 15500,
"currency": "USD",
},
"state": "state/preauthorized",
},
Object {
"code": "line-item/provider-commission",
"lineTotal": Money {
"amount": -1000,
"currency": "USD",
"booking": Object {
"attributes": Object {
"end": 2017-06-13T00:00:00.000Z,
"start": 2017-06-10T00:00:00.000Z,
},
"unitPrice": Money {
"amount": -1000,
"currency": "USD",
"id": UUID {
"uuid": "booking1",
},
"type": "booking",
},
]
}
payinTotal={
Money {
"amount": 16500,
"currency": "USD",
"customer": Object {
"attributes": Object {
"profile": Object {
"abbreviatedName": "customer1 abbreviated name",
"displayName": "customer1 display name",
},
},
"id": UUID {
"uuid": "customer1",
},
"type": "user",
},
"id": UUID {
"uuid": "sale-tx",
},
"listing": Object {
"attributes": Object {
"address": "listing1 address",
"description": "listing1 description",
"geolocation": LatLng {
"lat": 40,
"lng": 60,
},
"open": true,
"price": Money {
"amount": 5500,
"currency": "USD",
},
"title": "listing1 title",
},
"author": null,
"id": UUID {
"uuid": "listing1",
},
"type": "listing",
},
"provider": null,
"type": "transaction",
}
}
payoutTotal={
Money {
"amount": 15500,
"currency": "USD",
}
}
transactionState="state/preauthorized"
userRole="provider" />
</div>
</div>

View file

@ -75,11 +75,8 @@ const estimatedBreakdown = (bookingStart, bookingEnd, unitPrice) => {
<BookingBreakdown
className={css.receipt}
userRole="customer"
bookingStart={tx.booking.attributes.start}
bookingEnd={tx.booking.attributes.end}
transactionState={tx.attributes.state}
payinTotal={tx.attributes.payinTotal}
lineItems={tx.attributes.lineItems}
transaction={tx}
booking={tx.booking}
/>
);
};

View file

@ -1,9 +1,15 @@
import React from 'react';
import { shallow } from 'enzyme';
import Decimal from 'decimal.js';
import { types } from '../../util/sdkLoader';
import { renderShallow } from '../../util/test-helpers';
import { fakeIntl, fakeFormProps } from '../../util/test-data';
import * as propTypes from '../../util/propTypes';
import { BookingBreakdown } from '../../components';
import { BookingDatesFormComponent } from './BookingDatesForm';
const { Money } = types;
const noop = () => null;
describe('BookingDatesForm', () => {
@ -14,7 +20,7 @@ describe('BookingDatesForm', () => {
intl={fakeIntl}
dispatch={noop}
onSubmit={v => v}
price={new types.Money(1099, 'USD')}
price={new Money(1099, 'USD')}
bookingDates={{}}
startDatePlaceholder="today"
endDatePlaceholder="tomorrow"
@ -23,21 +29,37 @@ describe('BookingDatesForm', () => {
expect(tree).toMatchSnapshot();
});
it('matches snapshot with selected dates', () => {
const tree = renderShallow(
const price = new Money(1099, 'USD');
const startDate = new Date(Date.UTC(2017, 3, 14));
const endDate = new Date(Date.UTC(2017, 3, 16));
const tree = shallow(
<BookingDatesFormComponent
{...fakeFormProps}
intl={fakeIntl}
dispatch={noop}
onSubmit={v => v}
price={new types.Money(1099, 'USD')}
bookingDates={{
startDate: new Date(Date.UTC(2017, 3, 14)),
endDate: new Date(Date.UTC(2017, 3, 16)),
}}
price={price}
bookingDates={{ startDate, endDate }}
startDatePlaceholder="today"
endDatePlaceholder="tomorrow"
/>
);
expect(tree).toMatchSnapshot();
const breakdown = tree.find(BookingBreakdown);
const { userRole, transaction, booking } = breakdown.props();
expect(userRole).toEqual('customer');
expect(booking.attributes.start).toEqual(startDate);
expect(booking.attributes.end).toEqual(endDate);
expect(transaction.attributes.lastTransition).toEqual(propTypes.TX_TRANSITION_PREAUTHORIZE);
expect(transaction.attributes.state).toEqual(propTypes.TX_STATE_PREAUTHORIZED);
expect(transaction.attributes.payinTotal).toEqual(new Money(2198, 'USD'));
expect(transaction.attributes.payoutTotal).toEqual(new Money(2198, 'USD'));
expect(transaction.attributes.lineItems).toEqual([
{
code: 'line-item/night',
unitPrice: price,
quantity: new Decimal(2),
lineTotal: new Money(2198, 'USD'),
},
]);
});
});

View file

@ -1,72 +1,3 @@
exports[`BookingDatesForm matches snapshot with selected dates 1`] = `
<form
className={null}
onSubmit={[Function]}>
<DateRangeInputField
endDateId="fakeTestForm.bookingEndDate"
endDateLabel="BookingDatesForm.bookingEndTitle"
endDatePlaceholderText="tomorrow"
format={null}
name="bookingDates"
startDateId="fakeTestForm.bookingStartDate"
startDateLabel="BookingDatesForm.bookingStartTitle"
startDatePlaceholderText="today"
useMobileMargins={true}
validate={
Array [
[Function],
[Function],
]
} />
<div>
<h3>
<FormattedMessage
id="BookingDatesForm.priceBreakdownTitle"
values={Object {}} />
</h3>
<BookingBreakdown
bookingEnd={2017-04-16T00:00:00.000Z}
bookingStart={2017-04-14T00:00:00.000Z}
lineItems={
Array [
Object {
"code": "line-item/night",
"lineTotal": Money {
"amount": 2198,
"currency": "USD",
},
"quantity": "2",
"unitPrice": Money {
"amount": 1099,
"currency": "USD",
},
},
]
}
payinTotal={
Money {
"amount": 2198,
"currency": "USD",
}
}
transactionState="state/preauthorized"
userRole="customer" />
</div>
<p>
<FormattedMessage
id="BookingDatesForm.youWontBeChargedInfo"
values={Object {}} />
</p>
<PrimaryButton
disabled={false}
type="submit">
<FormattedMessage
id="BookingDatesForm.requestToBook"
values={Object {}} />
</PrimaryButton>
</form>
`;
exports[`BookingDatesForm matches snapshot without selected dates 1`] = `
<form
className={null}

View file

@ -164,15 +164,14 @@ export class CheckoutPageComponent extends Component {
return <NamedRedirect name="ListingPage" params={params} />;
}
const breakdown = currentTransaction.id
// Show breakdown only when transaction and booking are loaded
// (i.e. have an id)
const breakdown = currentTransaction.id && currentBooking.id
? <BookingBreakdown
className={css.bookingBreakdown}
userRole="customer"
transactionState={currentTransaction.attributes.state}
bookingStart={currentBooking.attributes.start}
bookingEnd={currentBooking.attributes.end}
lineItems={currentTransaction.attributes.lineItems}
payinTotal={currentTransaction.attributes.payinTotal}
transaction={currentTransaction}
booking={currentBooking}
/>
: null;

View file

@ -88,6 +88,7 @@ exports[`InboxPage matches snapshot 1`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-01-15T00:00:00.000Z,
"lineItems": Array [
Object {
@ -174,6 +175,7 @@ exports[`InboxPage matches snapshot 1`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2016-01-15T00:00:00.000Z,
"lineItems": Array [
Object {
@ -390,6 +392,7 @@ exports[`InboxPage matches snapshot 3`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-01-15T00:00:00.000Z,
"lineItems": Array [
Object {
@ -476,6 +479,7 @@ exports[`InboxPage matches snapshot 3`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2016-01-15T00:00:00.000Z,
"lineItems": Array [
Object {

View file

@ -47,6 +47,7 @@ exports[`OrderPage matches snapshot 1`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {

View file

@ -51,6 +51,7 @@ exports[`SalePage matches snapshot 1`] = `
Object {
"attributes": Object {
"createdAt": 2017-05-01T00:00:00.000Z,
"lastTransition": "transition/preauthorize",
"lastTransitionedAt": 2017-06-01T00:00:00.000Z,
"lineItems": Array [
Object {

View file

@ -1,6 +1,7 @@
import Decimal from 'decimal.js';
import { types } from './sdkLoader';
import { nightsBetween } from '../util/dates';
import * as propTypes from '../util/propTypes';
const { UUID, LatLng, Money } = types;
@ -96,7 +97,8 @@ export const createOwnListing = (id, open = true, price = new Money(5500, 'USD')
export const createTransaction = options => {
const {
id,
state = 'state/preauthorized',
state = propTypes.TX_STATE_PREAUTHORIZED,
lastTransition = propTypes.TX_TRANSITION_PREAUTHORIZE,
total = new Money(1000, 'USD'),
commission = new Money(100, 'USD'),
booking = null,
@ -113,6 +115,7 @@ export const createTransaction = options => {
createdAt: new Date(Date.UTC(2017, 4, 1)),
lastTransitionedAt,
state,
lastTransition,
payinTotal: total,
payoutTotal: new Money(total.amount - commission.amount, total.currency),
lineItems: [