Каков наилучший метод обращения с валютой / деньгами? - PullRequest
314 голосов
/ 20 июня 2009

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

У меня есть таблица items, в которой есть столбец price типа integer.

У меня проблемы с отображением значения цены в моих представлениях для цен, которые включают как евро, так и центы. Я упускаю что-то очевидное в том, что касается обработки валюты в Rails-фреймворке?

Ответы [ 13 ]

479 голосов
/ 20 июня 2009

Возможно, вы захотите использовать тип 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 s везде, что, вероятно, станет просто болью.

Как указано в mcl, для печати цены используйте:

number_to_currency(price, :unit => "€")
#=> €1,234.01
112 голосов
/ 02 октября 2010

Вот прекрасный, простой подход, который использует composed_of (часть ActiveRecord, используя шаблон ValueObject) и гем Money

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

  • Драгоценный камень (версия 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
  • Конвертация валюты бесплатно
25 голосов
/ 20 июня 2009

Обычной практикой для обработки валюты является использование десятичного типа. Вот простой пример из «Agile Web Development с 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 

для здравомыслия - проверьте ваши значения.

7 голосов
/ 30 декабря 2014

Используйте драгоценный камень для рельсов . Он прекрасно обрабатывает деньги и валюты в вашей модели, а также имеет несколько помощников для форматирования ваших цен.

6 голосов
/ 02 февраля 2017

Если вы используете Postgres (а теперь мы находимся в 2017 году), вы можете попробовать их тип столбца :money.

add_column :products, :price, :money, default: 0
5 голосов
/ 23 сентября 2012

Используя Виртуальные атрибуты (Ссылка на пересмотренный (платный) 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: Виртуальные атрибуты : Виртуальные атрибуты - это простой способ добавления полей формы, которые не отображаются непосредственно в базе данных. Здесь я покажу, как обрабатывать проверки, ассоциации и многое другое.

2 голосов
/ 17 февраля 2015

Я использую его таким образом:

number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")

Конечно, символ валюты, точность, формат и т. Д. Зависят от каждой валюты.

2 голосов
/ 17 октября 2012

Если кто-то использует Sequel, миграция будет выглядеть примерно так:

add_column :products, :price, "decimal(8,2)"

как-то Сиквел игнорирует: точность и: масштаб

(версия сиквела: продолжение (3.39.0, 3.38.0))

2 голосов
/ 06 сентября 2012

Определенно целые числа .

И хотя технически существует BigDecimal, 1.5 все равно даст вам чистый Float в Ruby.

1 голос
/ 10 февраля 2015

Все мои API-интерфейсы использовали центы для представления денег, и я не хотел это менять. Я не работал с большими суммами денег. Поэтому я просто поместил это во вспомогательный метод:

sprintf("%03d", amount).insert(-3, ".")

Преобразует целое число в строку, содержащую не менее трех цифр (при необходимости добавляя первые нули), затем вставляет десятичную точку перед последними двумя цифрами, никогда не используя Float. Оттуда вы можете добавить любые символы валют, подходящие для вашего варианта использования.

Это определенно быстро и грязно, но иногда это просто прекрасно!

...