首页 文章

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

提问于
浏览
306

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

我有一个表 items ,其列 price 类型为 integer .

我无法在包括欧元和美分在内的价格中显示价格值 . 就Rails框架中处理货币而言,我是否遗漏了一些明显的东西?

13 回答

  • 1

    使用money-rails gem . 它可以很好地处理您的模型中的货币和货币,还有一堆帮助器来格式化您的价格 .

  • 25

    如果你正在使用Postgres(因为我们现在是2017年),你可能想尝试一下他们的 :money 列类型 .

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

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

    你需要

    • Money gem(版本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

    • 货币兑换免费

  • 2

    Simple code for Ruby & Rails

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

    对RoR开发中的一些有抱负的初级/初学者的所有答案进行一点点更新和凝聚,这肯定会在这里得到一些解释 .

    用钱工作

    使用 :decimal 在DB中存储资金,正如@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
    

    几点:

    • :decimal 将被用作 BigDecimal ,它解决了很多问题 .

    应根据您所代表的内容调整

    • precisionscale

    • 如果您使用收款和发送付款, precision: 8scale: 2 会给您 999,999.99 作为最高金额,这在90%的情况下都没有问题 .

    • 如果您需要代表房产或稀有汽车的 Value ,您应该使用更高的 precision .

    • 如果您使用坐标(经度和纬度),您肯定需要更高的 scale .

    如何生成迁移

    要使用上述内容生成迁移,请在终端中运行:

    bin/rails g migration AddPriceToItems price:decimal{8-2}
    

    要么

    bin/rails g migration AddPriceToItems 'price:decimal{5,2}'
    

    正如blog中所解释的那样 .

    货币格式

    KISS额外的库再见并使用内置助手 . 使用 number_to_currency 作为@molf和@facundofarias建议 .

    要在Rails控制台中使用 number_to_currency 帮助程序,请调用 ActiveSupportNumberHelper 类以访问帮助程序 .

    例如:

    ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
    

    给出以下输出

    2500000,61€
    

    检查number_to_currency帮助者的另一个 options .

    把它放在哪里

    您可以将它放在应用程序帮助程序中,并在视图中使用它以获取任何数量 .

    module ApplicationHelper    
      def format_currency(amount)
        number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
      end
    end
    

    或者您可以将它作为实例方法放在 Item 模型中,并在需要格式化价格的地方(在视图或帮助程序中)调用它 .

    class Item < ActiveRecord::Base
      def format_price
        number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
      end
    end
    

    并且,我在一个控制器内使用 number_to_currency 的示例(注意 negative_format 选项,用于表示退款)

    def refund_information
      amount_formatted = 
        ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)')
      {
        # ...
        amount_formatted: amount_formatted,
        # ...
      }
    end
    
  • 471

    处理货币的常用做法是使用十进制类型 . 以下是“使用Rails进行敏捷Web开发”的简单示例

    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
    

    理智 - 检查你的 Value 观 .

  • 2

    绝对是integers .

    即使BigDecimal在技术上存在, 1.5 仍会在Ruby中为您提供纯粹的Float .

  • 0

    如果有人使用续集,迁移将看起来像:

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

    不知怎的,Sequel忽略了:精度和:规模

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

  • 110

    我这样使用它:

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

    当然,货币符号,精度,格式等取决于每种货币 .

  • 1

    我的底层API都使用美分代表金钱,我不想改变它 . 我也不是在处理大笔资金 . 所以我把它放在一个辅助方法中:

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

    将整数转换为至少包含三位数的字符串(必要时添加前导零),然后在最后两位数之前插入一个小数点,从不使用 Float . 从那里,您可以添加适合您的用例的任何货币符号 .

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

  • 0

    使用Virtual Attributes (Link to revised(paid) 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: Virtual Attributes:虚拟属性是添加不直接映射到数据库的表单字段的简洁方法 . 在这里,我将展示如何处理验证,关联等 .

  • 7

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

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

    Dylan Markow发布

  • 2

    您可能希望在数据库中使用 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
    

相关问题