处理货币/金钱的最佳方法是什么?

我正在研究一个非常基本的购物车系统。

我有一个表格的items有一个integertypes的列price

在我看来,包括欧元和美分的价格中,我无法显示价格价值。 就我在Rails框架中处理货币而言,我是否错过了一些显而易见的东西?

您可能会想在数据库中使用DECIMALtypes。 在你的迁移中,做这样的事情:

 # 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中, :decimaltypes返回为BigDecimal ,这对计算价格很有帮助。

如果你坚持使用整数,你将不得不手动转换BigDecimal到任何地方,这可能只是一个痛苦。

正如mcl指出的那样,要打印价格,请使用:

 number_to_currency(price, :unit => "€") #=> €1,234.01 

这是一个很好的,简单的方法,它利用了composed_of (ActiveRecord的一部分,使用ValueObject模式)和金钱gem

你需要

  • Moneygem (版本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显示货币格式正确的string
  • 如果您需要发送美分(到想要便士的支付网关),请inputproduct.price.cents.to_s
  • 货币兑换是免费的

处理货币的通常做法是使用十进制types。 下面是一个简单的例子,来自“使用Rails进行敏捷Web开发”

 add_column :products, :price, :decimal, :precision => 8, :scale => 2 

这将使您能够处理从-999,999.99到999,999.99的价格
你可能也想在你的项目中joinvalidation

 def validate errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01 end 

要理智,检查你的价值观。

使用钱轨gem 。 它很好地处理您的模型中的货币和货币,也有一大帮助人来格式化您的价格。

使用虚拟属性(链接到修改(付费)Railscast),您可以将price_in_cents存储在整数列中,并在您的产品模型中添加虚拟属性price_in_dollars作为getter和setter。

 # 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: 虚拟属性虚拟属性是一种干净的方式来添加不直接映射到数据库的表单域。 在这里我展示了如何处理validation,关联等等。

如果您使用的是Postgres(因为我们现在是在2017年),您可能想要尝试使用他们的:money列types。

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

绝对是整数 。

即使BigDecimal在技术上存在1.5仍然会给你一个纯粹的Ruby的Float。

如果有人使用续集,则迁移将如下所示:

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

不知何故,续集忽略:精确性和:规模

(续集版本:续集(3.39.0,3.38.0))

我正在使用这种方式:

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

当然,货币符号,精度,格式等依赖于每种货币。

您可以将一些选项传递给number_to_currency (一个标准的Rails 4视图助手):

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

由Dylan Markow发布

我的底层API都是用美分来代表金钱的,我不想改变它。 我也没有用大笔的钱工作。 所以我只是把它放在一个辅助方法中:

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

将整数转换为一个至less包含三位数字的string(如果需要,加上前导零),然后在最后两位数字之前插入一个小数点,从不使用Float 。 从那里你可以添加任何货币符号适合你的用例。

绝对是快速和肮脏的,但有时,这很好!

简单的Ruby和Rails代码

 <%= number_to_currency(1234567890.50) %> OUT PUT => $1,234,567,890.50