Преобразование валюты для сайта электронной коммерции - предотвращение неправильной общей корзины из-за округления

Я добавляю мультивалютную поддержку в приложение электронной коммерции. То, как я подошел к проблеме, заключалось в том, чтобы сохранить приложение в его базовой валюте и заставить шаблон вызывать функцию/плагин priceDisplay() каждый раз, когда он отображает цену. Таким образом, шаблон продолжает получать цены в долларовом выражении. Функция priceDisplay правильно конвертирует цену, если это необходимо, и добавляет правильный знак $ или евро в зависимости от настроек просмотра, сохраненных в сеансе. При отправке заказа приложение сохранит сумму заказа в долларах, а также валютный код и валютный курс. Кроме того, мы будем списывать средства с кредитной карты клиента в его валюте, чтобы гарантировать, что ему будет выставлен счет точно в том размере, который был показан на экране заказа.

Теперь проблема, с которой я сталкиваюсь, заключается в отображении итогов корзины в корзине, а также во время оформления заказа. В качестве примера приложение отправляет шаблону цены для отображения в корзине:

промежуточный итог: 9,75
корабль: 5,95
всего: 15,70

Шаблон берет эти суммы и вызывает функцию priceDisplay для каждого товара. Если курс валюты равен 1,1, то пользователю отображается:

промежуточный итог: 10.725 -> 10.73
корабль: 6.545 -> 6.55
всего: 17.27

Вы можете видеть, что промежуточный итог + корабль = 17,28, но общее преобразованное значение равно 17,27.

Итак, пара вариантов, которые, я думаю, могут сработать, хотя и не продуманы полностью:

  1. Обработка всех преобразований на стороне приложения
  2. Там, где товары будут суммироваться, шаблон должен отправить все отдельные сложения и общую сумму в базовой валюте в функцию priceDisplay, которая преобразует их и гарантирует, что преобразованная сумма и сумма слагаемых совпадают. В таком случае, как мне сообщить приложению, что сумма не 15,70, а, возможно, 15,71 или 15,69 (поскольку мы будем хранить заказ в базовой валюте и умножать на exchangeRate при обработке платежа).
  3. Следите за отброшенными/добавленными десятичными точками как часть преобразований и сделайте с этим что-нибудь «умное». Итак, в этом примере к 10,725 мы добавили 5 тысячных. Поэтому, когда мы конвертируем 6,545, мы должны сначала отбросить 0,005, а затем конвертировать. Возможно, это процесс, который делает вариант 2 выше?
  4. Ваше предложение здесь.

Если это имеет значение, приложение находится на PHP, а шаблон - Smarty.

Вы также можете увидеть ту же проблему при добавлении сумм строк в корзине:
3 товара x 9,75 каждый = 29,25
преобразовано:
3 товара x 10,73 (10,725) = 32,18 (32,175)
но 3 х 10,73 = 32,19 != 32,18


person safoo    schedule 17.07.2009    source источник


Ответы (4)


Я твердо в 1 лагере. Преобразование валюты является основной бизнес-логикой и относится к вашим моделям, а не к представлениям.

Кроме того, хотя деньги выглядят как числа с плавающей запятой, это не так. Деньги переходят из рук в руки в целых количествах, независимо от вашей базовой единицы. В США, например, если шарики жевательной резинки стоят 10 центов за штуку, а я покупаю десять, то я обмениваю 100 пенни на 10 шариков жевательной резинки. Даже если я даю долларовую купюру, с точки зрения программного обеспечения лучше считать ее 100 пенни.

Подробнее об этом см. в "Patterns of Enterprise Application Architecture" Мартина Фаулера и "Шаблоны анализа". Он подробно рассказывает обо всех проблемах и дает хороший пример кода. Вы также можете найти некоторую информацию в Интернете:

Если вам нужно, чтобы бухгалтерия работала, я бы также поговорил с бухгалтером. Конвертация валюты часто осложняется изменением курсов, странными сборами и прочей ерундой, и вы можете потратить много времени на погоню за копейками, чтобы сбалансировать бухгалтерские книги, если вы не сделаете это правильно с самого начала.

person William Pietri    schedule 17.07.2009
comment
Спасибо за ссылки. Я могу согласиться с тем, что преобразование должно выполняться на уровне приложения. С вашей точки зрения о торговле пенни, как я все еще могу предотвратить проблему сумм, показанных выше. 9,75 — это 975 пенсов, но в пересчете на другую валюту по-прежнему получается 1072,5 единицы. Правильно ли я храню суммы в базовой валюте? Спасибо за ссылки; Я постараюсь получить эти книги в ближайшее время. - person safoo; 17.07.2009
comment
Я бы сделал это так, чтобы использовать Banker's Rounding (также известное как получетное округление) после каждого преобразования и обязательно сохранять преобразованные значения. Поэтому вместо отображения и хранения 1072,5 единиц я бы показал и сохранил 1073 единицы. Для тележек более важно, чтобы пользователь видел согласованные значения, чем точные. Таким образом, хотя 1072,5 + 1072,5 = 2145, покупка 2 единиц будет стоить 1073 + 1073 = 2146. - person William Pietri; 20.07.2009

Если вы выставляете счета на основе конвертированной валюты, вам действительно не следует выполнять этот расчет в логике представления.

person Scott McIntyre    schedule 17.07.2009

Лично я бы просто сделал общую сумму, а промежуточную сумму добавил к сумме корабля. Это, очевидно, самый простой метод, и никто не упустит лишнюю копейку.

person Nico Burns    schedule 17.07.2009
comment
Я думаю, вы правы в том, что корректировка копейки может быть сделана в пункте стоимости доставки. Мой вопрос больше о расчете и подходе. - person safoo; 17.07.2009

Никогда не используйте числа с плавающей запятой для денег! Вы впускаете себя в мир боли с округлением без уважительной причины. Так как PHP не имеет десятичного типа с фиксированной точкой, делайте все расчеты денег с целыми числами. Смотрите ссылки Уильяма.

person Alexey Romanov    schedule 17.07.2009