Improve wrong currency handling

This commit is contained in:
Kimmo Puputti 2017-11-29 10:02:15 +02:00
parent 22b0aa512b
commit 186005dfd3
6 changed files with 139 additions and 94 deletions

View file

@ -1,10 +1,13 @@
import Decimal from 'decimal.js';
import { types } from '../../util/sdkLoader';
import * as propTypes from '../../util/propTypes';
import config from '../../config';
import BookingBreakdown from './BookingBreakdown';
const { UUID, Money } = types;
const CURRENCY = config.currency;
const exampleBooking = attributes => {
return {
id: new UUID('example-booking'),
@ -41,14 +44,14 @@ export const Checkout = {
props: {
userRole: 'customer',
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
payinTotal: new Money(9000, CURRENCY),
payoutTotal: new Money(9000, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(9000, CURRENCY),
reversal: false,
},
],
@ -65,14 +68,14 @@ export const CustomerOrder = {
props: {
userRole: 'customer',
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
payinTotal: new Money(9000, CURRENCY),
payoutTotal: new Money(9000, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(9000, CURRENCY),
reversal: false,
},
],
@ -89,20 +92,20 @@ export const ProviderSale = {
props: {
userRole: 'provider',
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(7000, 'USD'),
payinTotal: new Money(9000, CURRENCY),
payoutTotal: new Money(7000, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(9000, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -119,20 +122,20 @@ export const ProviderSaleZeroCommission = {
props: {
userRole: 'provider',
transaction: exampleTransaction({
payinTotal: new Money(9000, 'USD'),
payoutTotal: new Money(9000, 'USD'),
payinTotal: new Money(9000, CURRENCY),
payoutTotal: new Money(9000, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(2),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(9000, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(9000, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(0, 'USD'),
lineTotal: new Money(0, 'USD'),
unitPrice: new Money(0, CURRENCY),
lineTotal: new Money(0, CURRENCY),
reversal: false,
},
],
@ -149,20 +152,20 @@ export const ProviderSaleSingleNight = {
props: {
userRole: 'provider',
transaction: exampleTransaction({
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -180,20 +183,20 @@ export const ProviderSalePreauthorized = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_PREAUTHORIZE,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -211,20 +214,20 @@ export const ProviderSaleAccepted = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_ACCEPT,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -242,20 +245,20 @@ export const ProviderSaleDeclined = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_DECLINE,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -273,20 +276,20 @@ export const ProviderSaleAutoDeclined = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_AUTO_DECLINE,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -304,20 +307,20 @@ export const ProviderSaleDelivered = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_MARK_DELIVERED,
payinTotal: new Money(4500, 'USD'),
payoutTotal: new Money(2500, 'USD'),
payinTotal: new Money(4500, CURRENCY),
payoutTotal: new Money(2500, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
],
@ -335,35 +338,35 @@ export const ProviderSaleCanceled = {
userRole: 'provider',
transaction: exampleTransaction({
lastTransition: propTypes.TX_TRANSITION_CANCEL,
payinTotal: new Money(0, 'USD'),
payoutTotal: new Money(0, 'USD'),
payinTotal: new Money(0, CURRENCY),
payoutTotal: new Money(0, CURRENCY),
lineItems: [
{
code: 'line-item/night',
quantity: new Decimal(1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(4500, CURRENCY),
reversal: false,
},
{
code: 'line-item/night',
quantity: new Decimal(-1),
unitPrice: new Money(4500, 'USD'),
lineTotal: new Money(-4500, 'USD'),
unitPrice: new Money(4500, CURRENCY),
lineTotal: new Money(-4500, CURRENCY),
reversal: true,
},
{
code: 'line-item/provider-commission',
quantity: new Decimal(1),
unitPrice: new Money(-2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(-2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: false,
},
{
code: 'line-item/provider-commission',
quantity: new Decimal(-1),
unitPrice: new Money(2000, 'USD'),
lineTotal: new Money(-2000, 'USD'),
unitPrice: new Money(2000, CURRENCY),
lineTotal: new Money(-2000, CURRENCY),
reversal: true,
},
],

View file

@ -9,17 +9,22 @@ import { currencyConfig } from '../../util/test-data';
import * as validators from '../../util/validators';
import CurrencyInputField, { CurrencyInput } from './CurrencyInputField';
const defaultConfig = {
const currencyConfigUSD = {
...currencyConfig,
currency: 'USD',
};
const currencyConfigEUR = {
...currencyConfig,
currency: 'EUR',
};
// eslint-disable-next-line no-console
const onChange = price => console.log('CurrencyInput - value:', price);
// Different locales need to be initialized before their currency formatting is in use
const CurrencyInputWithIntl = ({ locale, ...rest }) => {
if (locale === 'en-US') {
if (locale === 'en') {
addLocaleData([...en]);
} else {
addLocaleData([...fi]);
@ -38,22 +43,39 @@ CurrencyInputWithIntl.propTypes = {
locale: string.isRequired,
};
// Empty field with en-US locale
export const EmptyWithEnUS = {
export const EmptyWithEnUSD = {
component: CurrencyInputWithIntl,
props: {
currencyConfig: defaultConfig,
locale: 'en-US',
currencyConfig: currencyConfigUSD,
locale: 'en',
},
group: 'custom inputs',
};
// Default value with fi-FI locale
export const defaultValueWithFiFI = {
export const defaultValueWithEnUSD = {
component: CurrencyInputWithIntl,
props: {
currencyConfig: { ...defaultConfig, currency: 'EUR' },
locale: 'fi-FI',
currencyConfig: currencyConfigUSD,
locale: 'en',
defaultValue: 9999.99,
},
group: 'custom inputs',
};
export const EmptyWithFiEUR = {
component: CurrencyInputWithIntl,
props: {
currencyConfig: currencyConfigEUR,
locale: 'fi',
},
group: 'custom inputs',
};
export const defaultValueWithFiEUR = {
component: CurrencyInputWithIntl,
props: {
currencyConfig: currencyConfigEUR,
locale: 'fi',
defaultValue: 9999.99,
},
group: 'custom inputs',
@ -71,7 +93,7 @@ const FormComponent = props => {
id={`${form}.price`}
label="Set price:"
placeholder="Type in amount in EUR..."
currencyConfig={{ ...defaultConfig, currency: 'EUR' }}
currencyConfig={currencyConfigEUR}
validate={required}
/>
</form>

View file

@ -51,8 +51,15 @@ class CurrencyInputComponent extends Component {
constructor(props) {
super(props);
const { currencyConfig, defaultValue, input, intl } = props;
const initialValue =
input.value instanceof types.Money ? convertMoneyToNumber(input.value) : defaultValue;
const initialValueIsMoney = input.value instanceof types.Money;
if (initialValueIsMoney && input.value.currency !== currencyConfig.currency) {
const e = new Error('Value currency different from marketplace currency');
log.error(e, 'currency-input-invalid-currency', { currencyConfig, inputValue: input.value });
throw e;
}
const initialValue = initialValueIsMoney ? convertMoneyToNumber(input.value) : defaultValue;
const hasInitialValue = typeof initialValue === 'number' && !isNaN(initialValue);
// We need to handle number format - some locales use dots and some commas as decimal separator
@ -82,11 +89,7 @@ class CurrencyInputComponent extends Component {
usesComma,
};
} catch (e) {
log.error(e, 'currency-input-init-failed', {
currencyConfig: currencyConfig,
defaultValue: defaultValue,
initialValue: initialValue,
});
log.error(e, 'currency-input-init-failed', { currencyConfig, defaultValue, initialValue });
throw e;
}

View file

@ -20,3 +20,7 @@
margin-bottom: 44px;
}
}
.priceCurrencyInvalid {
color: var(--failColor);
}

View file

@ -6,6 +6,8 @@ import { createSlug } from '../../util/urlHelpers';
import { NamedLink } from '../../components';
import { EditListingPricingForm } from '../../containers';
import { ensureListing } from '../../util/data';
import { types } from '../../util/sdkLoader';
import config from '../../config';
import css from './EditListingPricingPanel.css';
@ -40,19 +42,29 @@ const EditListingPricingPanel = props => {
<FormattedMessage id="EditListingPricingPanel.createListingTitle" />
);
const priceCurrencyValid =
price instanceof types.Money ? price.currency === config.currency : true;
const form = priceCurrencyValid ? (
<EditListingPricingForm
className={css.form}
initialValues={{ price }}
onSubmit={onSubmit}
onChange={onChange}
saveActionMsg={submitButtonText}
updated={panelUpdated}
updateError={errors.updateListingError}
updateInProgress={updateInProgress}
/>
) : (
<div className={css.priceCurrencyInvalid}>
<FormattedMessage id="EditListingPricingPanel.listingPriceCurrencyInvalid" />
</div>
);
return (
<div className={classes}>
<h1 className={css.title}>{panelTitle}</h1>
<EditListingPricingForm
className={css.form}
initialValues={{ price }}
onSubmit={onSubmit}
onChange={onChange}
saveActionMsg={submitButtonText}
updated={panelUpdated}
updateError={errors.updateListingError}
updateInProgress={updateInProgress}
/>
{form}
</div>
);
};

View file

@ -141,6 +141,7 @@
"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?",
"EditListingPricingPanel.listingPriceCurrencyInvalid": "Listing currency is different from the marketplace currency. You cannot edit the price.",
"EditListingPricingPanel.title": "Edit the pricing of {listingTitle}",
"EditListingWizard.saveEditDescription": "Save description",
"EditListingWizard.saveEditLocation": "Save location",