代码之家  ›  专栏  ›  技术社区  ›  Barry Gallagher

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

  •  309
  • Barry Gallagher  · 技术社区  · 17 年前

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

    我有一张桌子 items 有柱子的 price 类型的 integer .

    我无法在视图中显示包含欧元和美分的价格的价格值。在Rails框架中处理货币方面,我是否遗漏了一些明显的东西?

    13 回复  |  直到 8 年前
        1
  •  474
  •   rlandster    9 年前

    你可能会想用 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
    

    在轨道上, :decimal 类型返回为 BigDecimal 这对价格计算很有用。

    如果您坚持使用整数,则必须手动将其转换为 双小数 到处都是,这可能只是一种痛苦。

    如MCL所指出,要打印价格,请使用:

    number_to_currency(price, :unit => "€")
    #=> €1,234.01
    
        2
  •  111
  •   Peter Nixey Axe    10 年前

    这里有一个很好的、简单的方法 composed_of (ActiveRecord的一部分,使用ValueObject模式)和货币宝石

    你需要

    • 这个 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) }
      # ...
    

    你将得到:

    • 如果没有任何额外的更改,您的所有表单都将显示美元和美分,但内部表示仍然只是美分。表单将接受类似于“12034.95美元”的值,并为您转换它。不需要向模型或视图中的助手添加额外的处理程序或属性。
    • product.price = "$12.00" 自动转换为Money类
    • product.price.to_s 显示十进制格式的数字(“1234.00”)。
    • product.price.format 为货币显示格式正确的字符串
    • 如果您需要发送美分(到需要便士的支付网关), product.price.cents.to_s
    • 免费货币兑换
        3
  •  25
  •   alex.zherdev    17 年前

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

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

    这将允许您处理从-999999.99到999999.99的价格。
    您可能还希望在项目中包括验证,如

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

    为了理智,检查你的价值观。

        4
  •  7
  •   Troggy    11 年前

    使用 money-rails gem . 它可以很好地处理您的模型中的货币和货币,并且有许多帮助者来格式化您的价格。

        5
  •  5
  •   Thomas Klemm    14 年前

    使用 Virtual Attributes (Link to revised(paid) Railscast) 您可以在整型列中以美分为单位存储价格,并在产品模型中添加虚拟属性price,作为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: Virtual Attributes : 虚拟属性是添加不直接映射到数据库的表单字段的干净方法。这里我将演示如何处理验证、关联等。

        6
  •  4
  •   The Whiz of Oz    9 年前

    如果你在使用Postgres(既然我们现在在2017年),你可能会想给他们 :money 列类型A尝试。

    add_column :products, :price, :money, default: 0
    
        7
  •  2
  •   Rostyslav Dzinko Ankit    14 年前

    一定地 integers .

    即使bigdecimal技术上存在 1.5 仍然会给你一个纯的红宝石浮动。

        8
  •  2
  •   jethroo    13 年前

    如果有人正在使用Sequel,那么迁移看起来就像:

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

    不知怎的,续集忽略了:精度和:尺度

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

        9
  •  2
  •   facundofarias Ashay Batwal    11 年前

    我使用它的方式是:

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

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

        10
  •  1
  •   Community Mohan Dere    9 年前

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

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

    如张贴 Dylan Markow

        11
  •  1
  •   Becca Royal-Gordon    11 年前

    我的底层API都使用美分来表示钱,我不想改变这一点。我也没有用大量的钱工作。所以我把它放在一个助手方法中:

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

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

    它是 一定地 又快又脏,但有时也很好!

        12
  •  0
  •   Dinesh Vaitage    9 年前

    Ruby&Rails的简单代码

    <%= number_to_currency(1234567890.50) %>
    
    OUT PUT => $1,234,567,890.50
    
        13
  •  0
  •   Zalom    8 年前

    对于一些有抱负的RoR开发中的初级/初级学生来说,这只是一个小小的更新和所有答案的内聚,肯定会有一些解释。

    用钱工作

    使用 :decimal 按照@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 解决了很多问题。

    • precision 和 scale 应根据您所代表的内容进行调整

      • 如果你的工作是接收和发送付款, precision: 8 和 scale: 2 给你 999,999.99 最高金额,90%罚款。

      • 如果你需要代表一处房产或一辆珍稀汽车的价值,你应该使用更高的 精度 .

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

    如何生成迁移

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

    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控制台中,向 ActiveSupport 的 NumberHelper 类以访问帮助程序。

    例如:

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

    给出以下输出

    2500000,61€
    

    检查对方 options 属于 number_to_currency 帮手。

    把它放在哪里

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

    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
    

    举个例子,我如何使用 数字货币 在控制器内部(注意 negative_format 选项,用于表示退款)

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