Я'работаю над очень простой системой корзины для покупок.
У меня есть таблица items
, которая имеет столбец price
типа integer
.
У меня возникают проблемы с отображением значения цены в моих представлениях для цен, которые включают как евро, так и центы. Может быть, я упускаю что-то очевидное в том, что касается работы с валютой в Rails framework?
Вероятно, вы захотите использовать тип 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
Вот прекрасный, простой подход, использующий composed_of
(часть ActiveRecord, использующая паттерн ValueObject) и драгоценный камень Money.
Вам понадобятся
Product
.целое
в вашей модели (и базе данных), например :price
.Запишите это в вашем файле product.rb
:
class Product > ActiveRecord::Base
composed_of :price,
:class_name => 'Money',
:mapping => %w(price cents),
:converter => Proc.new { |value| Money.new(value) }
# ...
Что вы получите:
product.price = "$12.00"
автоматически конвертируется в класс Moneyproduct.price.to_s
выводит десятичное число ("1234.00")product.price.format
отображает правильно отформатированную строку для валюты.product.price.cents.to_s
.Обычно для работы с валютой используется десятичный тип. Вот простой пример из книги "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
для проверки правильности значений.
Если вы используете базы данных Postgres (и так мы'вновь в 2017 году теперь) вы, возможно, захотите, чтобы дать их `:тип столбца деньги попробовать.
add_column :products, :price, :money, default: 0
Используйте деньги-рельсы камень. Это красиво обрабатывает денег и валюты, в вашей модели, а также имеет кучу помощников для форматирования ценам.
Используя виртуальный атрибутами (связь с изменениями(платный) 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: виртуальные атрибуты: виртуальные атрибуты-это чистый способ, чтобы добавить поля, которые не отображаются непосредственно в базу данных. Здесь я показываю, как обращаться проверок, организаций, и многое другое.
Я использую его на этом пути:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
Разумеется, что символ валюты, точность, формат и так далее, зависит от каждой валюте.
Наверняка целые.
И хотя у bigdecimal технически существует 1.5
все равно даст вам чистый поплавок в Руби.
Просто немного обновить и сплоченности все ответы для некоторых начинающих юниоров/начинающих в развитии Рор, которые, несомненно, пришли сюда за разъяснениями.
Используйте :десятичное
хранить деньги в ДБ, как @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
Несколько пунктов:
:десятичное
будет использоваться как у bigdecimal
, который решает массу вопросов.
точность
и масштаб
должна быть скорректирована, в зависимости от того, что вы представляете
Если вы работаете с прием и отправка платежей точность: 8
и масштаб: 2
дает 999,999.99
как наивысшую сумму, которая в 90% случаев.
Если вам необходимо представить значение свойства или редкий автомобиль, вы должны использовать более высокую точность
.
Если вы работаете с координатами (широтой и долготой), то обязательно нужно увеличить "масштаб".
Чтобы произвести миграцию с вышеуказанным содержимым, выполните в терминале:
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 помощник в консоли рельсы, отправить вызов ActiveSupport
'класса S `NumberHelper для того, чтобы открыть помощник.
Например:
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
дает следующий результат
2500000,61€
Проверьте другие "настройки" number_to_currency помощником.
Вы можете положить его в вспомогательное приложение и использовать его внутри видом на любую сумму.
module ApplicationHelper
def format_currency(amount)
number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Или вы можете положить его в вещи
модель как метод экземпляра, и назвать его, где вам нужно отформатировать цену (в видом или помощники).
class Item < ActiveRecord::Base
def format_price
number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
И, пример, как я использую number_to_currency
внутри contrroler (обратите внимание на опцию negative_format
, используемый для представления возвратов)
def refund_information
amount_formatted =
ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)')
{
# ...
amount_formatted: amount_formatted,
# ...
}
end
Вы можете пройти некоторые варианты number_to_currency
(стандартная рельсы 4 вспомогательный вид):
number_to_currency(12.0, :precision => 2)
# => "$12.00"
Как написал Дилан Markow
Мой базовый API были все с использованием центов, чтобы представлять деньги, и я не'т хотим, чтобы это изменить. Я не работаю с большими суммами денег. Так что я просто оставлю это вспомогательный метод:
sprintf("%03d", amount).insert(-3, ".")
Которая преобразует целое в строку с по крайней мере три цифры (добавление ведущих нулей, если это необходимо), затем вставляет запятую перед последними двумя цифрами, не используя поплавок
. Оттуда вы можете добавлять любые символы валют являются подходящими для вашего случая.
Это'ы наверняка быстрый и грязный, но иногда это's просто отлично!
Простой код для Ruby & направляющие
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50