Каков наилучший метод обработки валюты/денег?
Я работаю над очень простой системой корзины покупок.
У меня есть таблица items
, у которой есть столбец price
типа integer
.
У меня возникли проблемы с отображением цены в моих прогнозах по ценам, которые включают как евро, так и центы. Я пропустил что-то очевидное, касающееся обработки валюты в структуре Rails?
Ответы
Ответ 1
Вероятно, вы захотите использовать тип 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
В Rails тип :decimal
возвращается как BigDecimal
, что отлично подходит для расчета цены.
Если вы настаиваете на использовании целых чисел, вам придется вручную конвертировать в и из BigDecimal
всюду, что, вероятно, просто станет болью.
Как указано mcl, для печати цены используйте:
number_to_currency(price, :unit => "€")
#=> €1,234.01
Ответ 2
Здесь тонкий простой подход, который использует composed_of
(часть ActiveRecord, используя шаблон ValueObject) и денежный камень
Вам понадобится
- Деньги драгоценный камень (версия 4.1.0)
- Модель, например
Product
- Столбец
integer
в вашей модели (и базе данных), например :price
Запишите это в свой product.rb
файл:
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 = "$12.00"
автоматически преобразуется в класс Money
-
product.price.to_s
отображает десятичное форматированное число ( "1234.00" )
-
product.price.format
отображает правильно отформатированную строку для валюты
- Если вам нужно отправить центы (на платежный шлюз, который хочет гроши),
product.price.cents.to_s
- Конвертация валюты бесплатно
Ответ 3
Обычная практика обработки валюты - использовать десятичный тип.
Вот простой пример из "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
чтобы проверить работоспособность.
Ответ 4
Используйте драгоценный камень с денежными рельсами. Он прекрасно обрабатывает деньги и валюты в вашей модели, а также имеет кучу помощников для форматирования ваших цен.
Ответ 5
Используя Виртуальные атрибуты (ссылка на пересмотренный (платный) 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: Виртуальные атрибуты: Виртуальные атрибуты - это чистый способ добавления полей формы, которые не отображаются непосредственно на база данных. Здесь я покажу, как обрабатывать проверки, ассоциации и т.д.
Ответ 6
Если вы используете Postgres (и с тех пор, как мы сейчас в 2017 году), вы можете попробовать попробовать их тип столбца :money
.
add_column :products, :price, :money, default: 0
Ответ 7
Определенно целые числа.
И даже несмотря на то, что BigDecimal технически существует 1.5
, он все равно даст вам чистый Float в Ruby.
Ответ 8
Если кто-то использует Sequel, миграция будет выглядеть примерно так:
add_column :products, :price, "decimal(8,2)"
каким-то образом Sequel игнорирует: точность и масштабирование
(Продолжение версии: продолжение (3.39.0, 3.38.0))
Ответ 9
Я использую его следующим образом:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
Конечно, символ валюты, точность, формат и т.д. зависят от каждой валюты.
Ответ 10
Вы можете передать некоторые параметры number_to_currency
(стандартный помощник представления Rails 4):
number_to_currency(12.0, :precision => 2)
# => "$12.00"
Как отправлено Дилан Марков
Ответ 11
Мои базовые API-интерфейсы использовали котировки для представления денег, и я не хотел их менять. Я тоже не работал с большими деньгами. Поэтому я просто помещаю это в вспомогательный метод:
sprintf("%03d", amount).insert(-3, ".")
Это преобразует целое число в строку с по меньшей мере тремя цифрами (при необходимости добавляет ведущие нули), затем вставляет десятичную точку перед двумя последними цифрами, никогда не используя Float
. Оттуда вы можете добавить любые символы валюты, подходящие для вашего прецедента.
Это определенно быстро и грязно, но иногда это просто отлично!
Ответ 12
Простой код для Ruby и Rails
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50