Move breakdown estimation to a separate file

This commit is contained in:
Kimmo Puputti 2018-04-09 15:34:29 +03:00
parent 1fc23dac36
commit 322e65ef41
2 changed files with 119 additions and 120 deletions

View file

@ -6,130 +6,14 @@ import { reduxForm, formValueSelector, propTypes as formPropTypes } from 'redux-
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
import classNames from 'classnames';
import moment from 'moment';
import Decimal from 'decimal.js';
import { types as sdkTypes } from '../../util/sdkLoader';
import { required, bookingDatesRequired } from '../../util/validators';
import {
dateFromLocalToAPI,
nightsBetween,
daysBetween,
START_DATE,
END_DATE,
} from '../../util/dates';
import { unitDivisor, convertMoneyToNumber, convertUnitToSubUnit } from '../../util/currency';
import {
LINE_ITEM_DAY,
LINE_ITEM_NIGHT,
LINE_ITEM_UNITS,
TRANSITION_REQUEST,
TX_TRANSITION_ACTOR_CUSTOMER,
propTypes,
} from '../../util/types';
import { START_DATE, END_DATE } from '../../util/dates';
import { propTypes } from '../../util/types';
import config from '../../config';
import { Form, PrimaryButton, BookingBreakdown, FieldDateRangeInput } from '../../components';
import { Form, PrimaryButton, FieldDateRangeInput } from '../../components';
import css from './BookingDatesForm.css';
const { Money, UUID } = sdkTypes;
const estimatedTotalPrice = (unitPrice, unitCount) => {
const numericPrice = convertMoneyToNumber(unitPrice);
const numericTotalPrice = new Decimal(numericPrice).times(unitCount).toNumber();
return new Money(
convertUnitToSubUnit(numericTotalPrice, unitDivisor(unitPrice.currency)),
unitPrice.currency
);
};
// When we cannot speculatively initiate a transaction (i.e. logged
// out), we must estimate the booking breakdown. This function creates
// an estimated transaction object for that use case.
const estimatedTransaction = (unitType, bookingStart, bookingEnd, unitPrice, quantity) => {
const now = new Date();
const isNightly = unitType === LINE_ITEM_NIGHT;
const isDaily = unitType === LINE_ITEM_DAY;
const unitCount = isNightly
? nightsBetween(bookingStart, bookingEnd)
: isDaily ? daysBetween(bookingStart, bookingEnd) : quantity;
const totalPrice = estimatedTotalPrice(unitPrice, unitCount);
// bookingStart: "Fri Mar 30 2018 12:00:00 GMT-1100 (SST)" aka "Fri Mar 30 2018 23:00:00 GMT+0000 (UTC)"
// Server normalizes night/day bookings to start from 00:00 UTC aka "Thu Mar 29 2018 13:00:00 GMT-1100 (SST)"
// The result is: local timestamp.subtract(12h).add(timezoneoffset) (in eg. -23 h)
// local noon -> startOf('day') => 00:00 local => remove timezoneoffset => 00:00 API (UTC)
const serverDayStart = dateFromLocalToAPI(
moment(bookingStart)
.startOf('day')
.toDate()
);
const serverDayEnd = dateFromLocalToAPI(
moment(bookingEnd)
.startOf('day')
.toDate()
);
return {
id: new UUID('estimated-transaction'),
type: 'transaction',
attributes: {
createdAt: now,
lastTransitionedAt: now,
lastTransition: TRANSITION_REQUEST,
payinTotal: totalPrice,
payoutTotal: totalPrice,
lineItems: [
{
code: unitType,
includeFor: ['customer', 'provider'],
unitPrice: unitPrice,
quantity: new Decimal(unitCount),
lineTotal: totalPrice,
reversal: false,
},
],
transitions: [
{
at: now,
by: TX_TRANSITION_ACTOR_CUSTOMER,
transition: TRANSITION_REQUEST,
},
],
},
booking: {
id: new UUID('estimated-booking'),
type: 'booking',
attributes: {
start: serverDayStart,
end: serverDayEnd,
},
},
};
};
const estimatedBreakdown = (unitType, bookingStart, bookingEnd, unitPrice, quantity) => {
const isUnits = unitType === LINE_ITEM_UNITS;
const quantityIfUsingUnits = !isUnits || Number.isInteger(quantity);
const canEstimatePrice = bookingStart && bookingEnd && unitPrice && quantityIfUsingUnits;
if (!canEstimatePrice) {
return null;
}
const tx = estimatedTransaction(unitType, bookingStart, bookingEnd, unitPrice, quantity);
return (
<BookingBreakdown
className={css.receipt}
userRole="customer"
unitType={unitType}
transaction={tx}
booking={tx.booking}
/>
);
};
export class BookingDatesFormComponent extends Component {
constructor(props) {
super(props);
@ -213,7 +97,7 @@ export class BookingDatesFormComponent extends Component {
<h3 className={css.priceBreakdownTitle}>
<FormattedMessage id="BookingDatesForm.priceBreakdownTitle" />
</h3>
{estimatedBreakdown(unitType, startDate, endDate, unitPrice)}
{/*estimatedBreakdown(unitType, startDate, endDate, unitPrice)*/}
</div>
) : null;

View file

@ -0,0 +1,115 @@
import moment from 'moment';
import Decimal from 'decimal.js';
import { types as sdkTypes } from '../../util/sdkLoader';
import { dateFromLocalToAPI, nightsBetween, daysBetween } from '../../util/dates';
import {
LINE_ITEM_DAY,
LINE_ITEM_NIGHT,
LINE_ITEM_UNITS,
TRANSITION_REQUEST,
TX_TRANSITION_ACTOR_CUSTOMER,
} from '../../util/types';
import { unitDivisor, convertMoneyToNumber, convertUnitToSubUnit } from '../../util/currency';
import { BookingBreakdown } from '../../components';
import css from './BookingDatesForm.css';
const { Money, UUID } = sdkTypes;
const estimatedTotalPrice = (unitPrice, unitCount) => {
const numericPrice = convertMoneyToNumber(unitPrice);
const numericTotalPrice = new Decimal(numericPrice).times(unitCount).toNumber();
return new Money(
convertUnitToSubUnit(numericTotalPrice, unitDivisor(unitPrice.currency)),
unitPrice.currency
);
};
// When we cannot speculatively initiate a transaction (i.e. logged
// out), we must estimate the booking breakdown. This function creates
// an estimated transaction object for that use case.
const estimatedTransaction = (unitType, bookingStart, bookingEnd, unitPrice, quantity) => {
const now = new Date();
const isNightly = unitType === LINE_ITEM_NIGHT;
const isDaily = unitType === LINE_ITEM_DAY;
const unitCount = isNightly
? nightsBetween(bookingStart, bookingEnd)
: isDaily ? daysBetween(bookingStart, bookingEnd) : quantity;
const totalPrice = estimatedTotalPrice(unitPrice, unitCount);
// bookingStart: "Fri Mar 30 2018 12:00:00 GMT-1100 (SST)" aka "Fri Mar 30 2018 23:00:00 GMT+0000 (UTC)"
// Server normalizes night/day bookings to start from 00:00 UTC aka "Thu Mar 29 2018 13:00:00 GMT-1100 (SST)"
// The result is: local timestamp.subtract(12h).add(timezoneoffset) (in eg. -23 h)
// local noon -> startOf('day') => 00:00 local => remove timezoneoffset => 00:00 API (UTC)
const serverDayStart = dateFromLocalToAPI(
moment(bookingStart)
.startOf('day')
.toDate()
);
const serverDayEnd = dateFromLocalToAPI(
moment(bookingEnd)
.startOf('day')
.toDate()
);
return {
id: new UUID('estimated-transaction'),
type: 'transaction',
attributes: {
createdAt: now,
lastTransitionedAt: now,
lastTransition: TRANSITION_REQUEST,
payinTotal: totalPrice,
payoutTotal: totalPrice,
lineItems: [
{
code: unitType,
includeFor: ['customer', 'provider'],
unitPrice: unitPrice,
quantity: new Decimal(unitCount),
lineTotal: totalPrice,
reversal: false,
},
],
transitions: [
{
at: now,
by: TX_TRANSITION_ACTOR_CUSTOMER,
transition: TRANSITION_REQUEST,
},
],
},
booking: {
id: new UUID('estimated-booking'),
type: 'booking',
attributes: {
start: serverDayStart,
end: serverDayEnd,
},
},
};
};
const estimatedBreakdown = (unitType, bookingStart, bookingEnd, unitPrice, quantity) => {
const isUnits = unitType === LINE_ITEM_UNITS;
const quantityIfUsingUnits = !isUnits || Number.isInteger(quantity);
const canEstimatePrice = bookingStart && bookingEnd && unitPrice && quantityIfUsingUnits;
if (!canEstimatePrice) {
return null;
}
const tx = estimatedTransaction(unitType, bookingStart, bookingEnd, unitPrice, quantity);
return (
<BookingBreakdown
className={css.receipt}
userRole="customer"
unitType={unitType}
transaction={tx}
booking={tx.booking}
/>
);
};