tags:

views:

275

answers:

4

I'm adding multi-currency support to an e-commerce application. The way I approached the problem was to keep the application in it's base currency and have the template call a priceDisplay() function/plugin anytime it displays a price. So the template continues to receive prices in dollar amounts. The priceDisplay function correctly converts the price if needed and adds the correct $ or Euro sign depending on the viewers settings, stored in the session. On order submit, the application will store the order in dollar amount as well as the currencyCode and currencyRate. Additionally we will charge the customer's credit card in their currency to ensure they get billed exactly what was shown on the order screen.

Now the issue I am having is when displaying the cart totals in the cart as well as during checkout. As an example, the application sends the template the prices to display in the shopping cart:

subtotal: 9.75
ship: 5.95
total: 15.70

The template takes these amounts and calls the priceDisplay function on each item. If the currency rate is 1.1, then we get displayed to the user:

subtotal: 10.725 -> 10.73
ship: 6.545 -> 6.55
total: 17.27

You can see that the subtotal + ship = 17.28, but the total converted is 17.27.

So couple of options I think could work, though not thought all the way through:

  1. Handle all conversion on the application side
  2. Where items will be totaled, the template should send all the individual addends and total together in the base currency to the priceDisplay function, which will convert them and ensure that the converted total and sum of addends match up. In this case, then how do I communicate to the application that the total is not 15.70 but perhaps 15.71 or 15.69 (since we will be storing the order in base currency and multiply by the exchangeRate when processing the payment.)
  3. Keep track of dropped/added decimal points as part of the conversions and do something 'smart' with that. So in this example, 10.725, we added 5 thousandths. So when we convert 6.545, we should first drop .005 then convert. Perhaps this is the process that option 2 above does?
  4. Your suggestion here.

If it makes any difference, application is in PHP and template is Smarty.

You can also see the same issue in adding the line totals of the cart items:
3 items x 9.75 each = 29.25
converted:
3 items x 10.73 (10.725) = 32.18 (32.175)
but 3 x 10.73 = 32.19 != 32.18

A: 

Well personally, I would just make the total amount, the subtotal added to the ship amount. This is obviously the simplest method to use, and no-one will miss the extra penny.

Nico Burns
I think you are right that a penny adjustment can be done at the shipping cost item. My question is more about the calculation and approach.
safoo
+1  A: 

If you are billing based on the converted currency, you really shouldn't be doing that calculation in your view logic.

smackfu
yes the more I think about it, you are probably right.
safoo
+4  A: 

I'm firmly in the 1 camp. Currency conversion is core business logic, and belongs in your models, not your views.

Further, although money looks like floating-point numbers, it isn't. Money changes hands in integer quantities of whatever your base unit is. In the US, for example, If gumballs are 10 cents each and I buy ten, then I'm trading 100 pennies for 10 gumballs. Even if I give a dollar bill, from a software perspective, it's best to count that as 100 pennies.

For more on this, see Martin Fowler's "Patterns of Enterprise Application Architecture" and "Analysis Patterns". He talks through all the issues in detail and gives good sample code. You can also find some of that info on the web:

  • Quantity (with some money sample code)
  • Money (mainly a pointer into his book)

If you need the accounting to work, I'd also talk to the accountant. Currency conversion is often complicated by changing rates, weird fees, and other nonsense, and you can spend a long time chasing pennies to make the books balance if you don't get it right from the get-go.

William Pietri
Thanks for the links. I can agree that conversion should be done at the application level.With your point about trading pennies, how do I still prevent the issue of totals shown above. 9.75 is 975 pennies but converted is still 1072.5 units in the other currency. Am I correct in storing amounts in base currency?Thanks for the links; I'll look to get these books soon.
safoo
The way I'd do it is to use Banker's Rounding (aka half-even rounding) after every conversion, and be sure to store the converted values. So instead of displaying and storing 1072.5 units, I'd show and store 1073 units.For carts, it's more important that the user see consistent values than precise ones. So although 1072.5 + 1072.5 = 2145, buying 2 units would be charged at 1073 + 1073 = 2146.
William Pietri
A: 

Don't use floating point numbers for money, ever! You are letting yourself into a world of hurt with rounding for no good reason. Since PHP doesn't have a decimal fixed point type, do all money calculations with integers. See William's links.

Alexey Romanov