Каков наилучший метод обращения с валютой / деньгами?
Я работаю над очень простой системой корзины покупок.
у меня есть таблица items Это имеет столбец price типа integer.
у меня возникли проблемы с отображением значения цены в моих представлениях для цен, которые включают в себя как евро, так и центы. Я упускаю что-то очевидное в том, что касается обработки валюты в рамках Rails?
13 ответов:
вы, вероятно, захотите использовать
DECIMALтип в базе данных. В вашей миграции, сделать что-то вроде этого:# precision is the total number of digits # scale is the number of digits to the right of the decimal point add_column :items, :price, :decimal, :precision => 8, :scale => 2в рельсах,
:decimalтип возвращается какBigDecimal, что отлично подходит для расчета цены.если вы настаиваете на использовании целых чисел, вам придется вручную конвертировать в и из
BigDecimals везде, что, вероятно, просто станет болью.как указал mcl, чтобы напечатать цену, используйте:
number_to_currency(price, :unit => "€") #=> €1,234.01
вот прекрасный, простой подход, который использует
composed_of(часть ActiveRecord, используя шаблон ValueObject) и Money gemвам понадобится
- The Money gem (версия 4.1.0)
- модель, например
Product- An
integerстолбец в вашей модели (и базе данных), например:priceнапишите это в своем
product.rbfile:class Product > ActiveRecord::Base composed_of :price, :class_name => 'Money', :mapping => %w(price cents), :converter => Proc.new { |value| Money.new(value) } # ...что вы будете получить:
- без каких-либо дополнительных изменений, все ваши формы будут показывать доллары и центы, но внутреннее представление по-прежнему только центы. Формы будут принимать значения, такие как" $12,034.95 " и конвертировать его для вас. Нет необходимости добавлять дополнительные обработчики или атрибуты к вашей модели или помощникам в вашем представлении.
product.price = ".00"автоматически преобразуется в класс Moneyproduct.price.to_sотображает десятичное число в формате ("1234.00")product.price.formatотображает правильно отформатированную строку для валюты- Если вам нужно отправить центы (на платежный шлюз, который хочет Пенни),
product.price.cents.to_s- конвертация валюты бесплатно!--18-->
обычно для обработки валюты используется десятичный тип. Вот простой пример из "Agile Web Development with Rails"
add_column :products, :price, :decimal, :precision => 8, :scale => 2Это позволит вам обрабатывать цены от -999,999. 99 до 999,999.99
Вы также можете включить проверку в свои элементы, такие какdef validate errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01 endдля здравомыслия-Проверьте свои значения.
использовать money-rails gem. Он хорошо обрабатывает деньги и валюты в вашей модели, а также имеет кучу помощников для форматирования ваших цен.
С помощью виртуальные атрибуты (ссылка на пересмотренный (платный) Railscast) вы можете хранить price_in_cents в целочисленном столбце и добавить виртуальный атрибут price_in_dollars в вашей модели продукта в качестве геттера и сеттера.
# Add a price_in_cents integer column $ rails g migration add_price_in_cents_to_products price_in_cents:integer # Use virtual attributes in your Product model # app/models/product.rb def price_in_dollars price_in_cents.to_d/100 if price_in_cents end def price_in_dollars=(dollars) self.price_in_cents = dollars.to_d*100 if dollars.present? endисточник: RailsCasts #016: Виртуальные Атрибуты:виртуальные атрибуты-это чистый способ, чтобы добавить поля, которые не отображаются непосредственно в базу данных. Здесь я покажу, как обрабатывать проверки, ассоциации и больше.
Если вы используете Postgres (и так как мы сейчас находимся в 2017 году), вы можете дать их
:moneyтип столбца попробовать.add_column :products, :price, :money, default: 0
наверняка чисел.
и хотя bigdecimal технически существует
1.5все равно даст вам чистый поплавок в Ruby.
Если кто-то использует продолжение миграции будет выглядеть примерно так:
add_column :products, :price, "decimal(8,2)"Как-то сиквел игнорирует :точность и :масштаб
(продолжение версии: Продолжение (3.39.0, 3.38.0))
Я использую его таким образом:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")конечно, что символ валюты, точность, формат и так далее, зависит от каждой валюте.
вы можете передать некоторые параметры в
number_to_currency(стандартный Rails 4 view helper):number_to_currency(12.0, :precision => 2) # => ".00"Как написал Дилан Markow
все мои базовые API использовали центы для представления денег, и я не хотел этого менять. Я не работаю с большими суммами денег. Поэтому я просто положил это в вспомогательный метод:
sprintf("%03d", amount).insert(-3, ".")который преобразует целое число в строку с не менее чем тремя цифрами (при необходимости добавляя начальные нули), а затем вставляет десятичную точку перед последними двумя цифрами, никогда не используя
Float. Оттуда вы можете добавить любые символы валюты, подходящие для вашего использования случай.Это наверняка быстро и грязно, но иногда это просто прекрасно!
просто небольшое обновление и сплоченность всех ответов для некоторых начинающих юниоров/новичков в развитии RoR, которые, несомненно, придут сюда для некоторых объяснений.
работа с деньгами
использовать
:decimalхранить деньги в БД, как предложил @molf (и что моя компания использует в качестве золотого стандарта при работе с деньгами).# precision is the total number of digits # scale is the number of digits to the right of the decimal point add_column :items, :price, :decimal, precision: 8, scale: 2несколько пунктов:
:decimalбудет использоваться какBigDecimalчто решает многое вопросов.
precisionиscaleдолжны быть скорректированы, в зависимости от того, что вы представляете
если вы работаете с получением и отправкой платежей,
precision: 8иscale: 2дает999,999.99как самая высокая сумма, которая штрафуется в 90% случаев.Если вам нужно представить значение свойства или редкого автомобиля, вы должны использовать более высокий
precision.если вы работаете с координатами (долгота и широта), вам обязательно понадобится выше
scale.как создать миграцию
чтобы создать миграцию с вышеуказанным содержимым, запустите в терминале:
bin/rails g migration AddPriceToItems price:decimal{8-2}или
bin/rails g migration AddPriceToItems 'price:decimal{5,2}'как поясняется в блог пост.
валютыпоцелуй дополнительные библиотеки до свидания и использовать встроенный прислуга. Используйте
number_to_currencyкак предложили @molf и @facundofarias.играть
number_to_currencyпомощник в консоли Rails, отправьте вызов наActiveSupport' sNumberHelperкласс для доступа к помощнику.например:
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")дает следующий результат
2500000,61€проверьте другие
optionsна number_to_currency помощником.куда его девать
вы можете поместить его в Помощник приложения и используйте его внутренние виды для любого количества.
module ApplicationHelper def format_currency(amount) number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u") end endили вы можете положить его в
Itemмодель как метод экземпляра и вызовите его там, где вам нужно отформатировать цену (в представлениях или помощниках).class Item < ActiveRecord::Base def format_price number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u") end endи, например, как я использую
number_to_currencyвнутри контролера (обратите внимание наnegative_formatопция, используемая для представления возвратов)def refund_information amount_formatted = ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)') { # ... amount_formatted: amount_formatted, # ... } end
Comments