web-dev-qa-db-ja.com

通貨/お金を処理するための最良の方法は何ですか?

私は非常に基本的なショッピングカートシステムに取り組んでいます。

タイプitemsの列priceを持つテーブルintegerがあります。

ユーロとセントの両方を含む価格の私の見解では、価格の値を表示できません。 Railsフレームワークで通貨を処理することに関する限り、明白なことを見逃していませんか?

313
Barry Gallagher

データベースで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として返されます。これは価格計算に最適です。

整数の使用を主張するなら、どこでも手動でBigDecimalsとの間で手動で変換しなければならないでしょう、これはおそらくただ痛みになるでしょう。

Mclが指摘したように、価格を表示するには、次のようにします。

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

これは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は、10進数形式の数字( "1234.00")を表示します
  • product.price.formatは通貨に対して適切にフォーマットされた文字列を表示します
  • セントを送金する必要がある場合(ペニーが必要な支払いゲートウェイへ)、product.price.cents.to_s
  • 無料の通貨換算
112
Ken Mayer

通貨を処理するための一般的な方法は、小数型を使用することです。これが「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 

あなたの価値観を正当性をチェックするために。

25
alex.zherdev

money-Rails gem を使用してください。それはあなたのモデルのお金と通貨をうまく取り扱い、またあなたの価格をフォーマットするためのたくさんのヘルパーを持っています。

7
Troggy

もしあなたがPostgresを使っているなら(そして今私達は2017年に来ているので)あなたは彼らの:moneyカラムタイプを試してみることを望むかもしれません。

add_column :products, :price, :money, default: 0
6
The Whiz of Oz

仮想属性(改訂版(有料)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:仮想属性仮想属性はフォームを追加するためのきれいな方法ですデータベースに直接マップしないフィールド。ここでは、検証、関連付けなどを処理する方法を説明します。

5
Thomas Klemm

誰かがSequelを使用している場合、移行は次のようになります。

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

どういうわけかSequelは:precisionと:scaleを無視します

(続編:続編(3.39.0、3.38.0))

2
jethroo

間違いなく 整数

そして、BigDecimalが技術的に存在していても、1.5はRubyであなたに純粋なFloatを与えるでしょう。

2
moot

私はこの方法でそれを使っています:

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

もちろん、通貨記号、精度、フォーマットなどは各通貨によって異なります。

2
facundofarias

私の基になるAPIはすべてお金を表すためにセントを使用していました、そして私はそれを変更したくありませんでした。また、私は多額のお金を使って仕事をしていませんでした。だから私はちょうどこれをヘルパーメソッドに入れます:

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

整数を3桁以上の文字列に変換し(必要に応じて先行ゼロを追加し)、最後の2桁の前に小数点を挿入します。Floatは使用しません。そこから、ユースケースに適した通貨記号を追加できます。

それは間違いなく素早く汚いですが、時にはそれでいいのです!

1

number_to_currency(標準のRails 4ビューヘルパー)にいくつかのオプションを渡すことができます。

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

投稿者として Dylan Markow

1
blnc

Ruby&Rails用の簡単なコード

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50
0
Dinesh Vaitage

RoR開発の意欲的な後輩/初心者のためのちょっとした更新とすべての答えのまとまり。

お金を使う

@molfが示唆しているように(そして私の会社がお金を扱うときの黄金の基準として使うもの)、DBにお金を保存するために: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として使用される予定です。

  • precisionscaleは、何を表しているかに応じて調整する必要があります。

    • あなたが支払いを受け取ったり送ったりすることを扱う場合、precision: 8scale: 2はあなたに999,999.99を最高額として与えます。これは90%のケースで大丈夫です。

    • あなたが不動産や希少な車の価値を表現する必要があるなら、あなたはより高いprecisionを使うべきです。

    • 座標(経度と緯度)を扱う場合は、必ずより高いscaleが必要になります。

移行を生成する方法

上記の内容で移行を生成するには、ターミナルで実行します。

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

または

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

これで説明されているように blog 投稿。

通貨フォーマット

KISS 追加のライブラリはさようなら、組み込みのヘルパーを使います。 @molfと@facundofariasが示すようにnumber_to_currencyを使用してください。

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

そして、私がcontrrolerの中で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
0