diff --git a/src/containers/BookingDatesForm/BookingDatesForm.js b/src/containers/BookingDatesForm/BookingDatesForm.js index 697c2bb1..88e2eaaf 100644 --- a/src/containers/BookingDatesForm/BookingDatesForm.js +++ b/src/containers/BookingDatesForm/BookingDatesForm.js @@ -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 ( - - ); -}; - export class BookingDatesFormComponent extends Component { constructor(props) { super(props); @@ -213,7 +97,7 @@ export class BookingDatesFormComponent extends Component {

- {estimatedBreakdown(unitType, startDate, endDate, unitPrice)} + {/*estimatedBreakdown(unitType, startDate, endDate, unitPrice)*/} ) : null; diff --git a/src/containers/BookingDatesForm/EstimatedBreakdownMaybe.js b/src/containers/BookingDatesForm/EstimatedBreakdownMaybe.js new file mode 100644 index 00000000..0b6f2500 --- /dev/null +++ b/src/containers/BookingDatesForm/EstimatedBreakdownMaybe.js @@ -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 ( + + ); +};