Каков наилучший метод обработки валюты/денег?

Я работаю над очень простой системой корзины покупок.

У меня есть таблица 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) и денежный камень

Вам понадобится

Запишите это в свой 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 

чтобы проверить работоспособность.

Ответ 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