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

280

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

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

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

  • 0
    если кто - то использует SQL, то DECIMAL(19, 4) является популярным выбором проверить это также проверить здесь мировую валюту форматы , чтобы решить , сколько знаков после запятой для использования, надежда помогает.
Теги:
currency

12 ответов

446
Лучший ответ

Вероятно, вы захотите использовать тип 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
  • 0
    Спасибо за совет нейтрино и мольфа! У меня есть один дополнительный вопрос - как я могу отобразить мои цены с точностью до двух знаков после запятой, используя BigDecimal? В настоящее время item.price возвращает «12,5» вместо «12,50» ...
  • 13
    Используйте помощник number_to_currency, больше информации на api.rubyonrails.org/classes/ActionView/Helpers/…
Показать ещё 11 комментариев
105

Здесь тонкий простой подход, который использует 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
  • Конвертация валюты бесплатно
  • 0
    Как я могу держать баланс в валюте, отличной от доллара США, используя этот подход?
  • 14
    Мне нравится этот подход. Но, пожалуйста, обратите внимание: убедитесь, что ваша миграция для 'price' в этом примере не допускает нулевых значений и по умолчанию равна 0, чтобы вы не сошли с ума, пытаясь выяснить, почему это не работает.
Показать ещё 7 комментариев
25

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

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

  • 1
    Это решение также позволяет использовать сумму SQL и друзей.
  • 4
    Не могли бы вы сделать: validates: цена,: присутствие => истина,: численность => {: больше_отлично => 0}
6

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

  • 0
    Да, я согласен с этим. Как правило, я обращаюсь с деньгами, храня их в виде центов (целое число) и использую драгоценный камень, такой как действует как деньги или деньги (денежные рельсы), для обработки данных в памяти. Обработка его целыми числами предотвращает эти неприятные ошибки округления. Например, 0,2 * 3 => 0,6000000000000001 Это, конечно, работает, только если вам не нужно обрабатывать доли цента.
  • 0
    Это очень хорошо, если вы используете рельсы. Оставьте это и не беспокойтесь о проблемах с десятичным столбцом. Если вы используете это для представления, этот ответ также может быть полезен: stackoverflow.com/questions/18898947/…
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: Виртуальные атрибуты: Виртуальные атрибуты - это чистый способ добавления полей формы, которые не отображаются непосредственно на база данных. Здесь я покажу, как обрабатывать проверки, ассоциации и т.д.

  • 1
    это оставляет 200.0 одну цифру
3

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

add_column :products, :price, :money, default: 0
2

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

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

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

2

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

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

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

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

2

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

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

1

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

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

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

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

  • 0
    Не могу поверить, что никто не голосовал против тебя. Это было единственное, что сработало, чтобы привести мой объект Money в такую форму, чтобы его мог принять API. (Десятичный)
1

Вы можете передать некоторые параметры number_to_currency (стандартный помощник представления Rails 4):

number_to_currency(12.0, :precision => 2)
# => "$12.00"

Как отправлено Дилан Марков

0

Простой код для Ruby и Rails

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50

Ещё вопросы

Сообщество Overcoder
Наверх
Меню