통화 / 돈을 처리하는 가장 좋은 방법은 무엇입니까?


323

저는 매우 기본적인 쇼핑 카트 시스템을 만들고 있습니다.

유형 items의 열 이있는 테이블 이 있습니다 .priceinteger

유로와 센트가 모두 포함 된 가격에 대한 견해에 가격 값을 표시하는 데 문제가 있습니다. Rails 프레임 워크에서 통화를 다루는 한 분명한 것이 빠져 있습니까?


누군가가 사용하는 SQL 있다면, DECIMAL(19, 4) 인기있는 선택입니다 확인 또한 확인 여기에 , 희망이 도움이 얼마나 많은 소수점 사용에 장소를 결정하는 세계 외화 형식.
shaijut

답변:


495

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

13
number_to_currency 도우미 사용, api.rubyonrails.org/classes/ActionView/Helpers/에
mlibby

48
실제로, acts_as_dollars와 함께 정수를 사용하는 것이 훨씬 안전하고 쉽습니다. 부동 소수점 비교에 물린 적이 있습니까? 그렇지 않다면 처음 경험하지 마십시오. :) acts_as_dollars를 사용하면 12.34 형식으로 물건을 넣고 1234로 저장하고 12.34로 나옵니다.
사라 메이

50
@Sarah Mei : BigDecimals + 10 진수 열 형식은 정확하게 피합니다.
molf 2016 년

114
이 답변을 맹목적으로 정확하게 복사하지 않는 것이 중요합니다- 스케일 8 은 최대 값 999,999.99를 제공 합니다. 백만보다 큰 숫자가 필요한 경우 정밀도를 높이십시오!
Jon Cairns

22
오만 리알이나 튀니지 디나르와 같은 일부 북아프리카 및 아랍 통화는 3의 스케일을 가지므로 정밀도 8 스케일 3 이 더 적합합니다. .
Beatartart를

117

다음은 composed_of(ValueObject 패턴을 사용하여 ActiveRecord의 일부) 및 Money 보석 을 활용하는 훌륭하고 간단한 접근법입니다.

너는 필요할거야

  • 돈 보석 (버전 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
  • 무료 통화 변환

14
나는이 접근법을 좋아한다. 그러나이 예제에서 'price'로 마이그레이션하면 null이 허용되지 않으며 기본값이 0으로 설정되어 왜 이것이 작동하지 않는지 알아 내십시오.
Cory

3
화폐 전환이 필요하지 않으면 money_column gem (Shopify에서 추출)이 돈 보석보다 사용하기가 매우 간단 하다는 것을 알았습니다 .
talyric

7
Rails 코어 팀이 프레임 워크에서 "composed_of"를 더 이상 사용하지 않는 방법과 제거하는 것에 대해 Money gem을 사용하는 모든 사람들에게 주목해야합니다. 보석이이 문제를 해결하기 위해 업데이트 될 것으로 생각되지만 Rails 4.0을보고 있다면이 가능성을 알고 있어야합니다
Peer Allan

1
composed_of 여기서 제거에 대한 @PeerAllan의 의견에 대해서는 대체 구현뿐만 아니라 더 자세히 설명합니다.
HerbCSO

3
또한, rails-money gem을 사용하면 정말 까다 롭습니다 .
fotanus

25

통화를 처리하는 일반적인 방법은 10 진수 형식을 사용하는 것입니다. 다음은 "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 

당신의 가치를 확인하십시오.


1
이 솔루션을 사용하면 SQL 합계 및 친구를 사용할 수도 있습니다.
Larry K

4
당신은 아마도 할 수 있습니다 : : price, : presence => true, : numericality => {: greater_than => 0} 유효성 검사
Galaxy

9

Postgres를 사용하고 있다면 (2017 년부터) :money열 유형을 시험해 볼 수 있습니다.

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

7

머니 레일 보석을 사용하십시오 . 모델의 돈과 통화를 잘 처리하고 가격을 형식화하는 데 많은 도우미가 있습니다.


네, 이것에 동의합니다. 일반적으로 나는 돈을 센트 (정수)로 저장하고 돈으로 행동하거나 돈 (돈 레일)과 같은 보석을 사용하여 데이터를 메모리에서 처리함으로써 돈을 처리합니다. 정수로 처리하면 불쾌한 반올림 오류가 방지됩니다. 예를 들어 0.2 * 3 => 0.6000000000000001 이것은 물론 센트를 처리 할 필요가없는 경우에만 작동합니다.
Chad M

레일을 사용하는 경우 매우 좋습니다. 그것을 넣고 소수 열의 문제에 대해 걱정하지 마십시오. 이 정보를보기와 함께 사용하면이 답변도 도움이 될 수 있습니다. stackoverflow.com/questions/18898947/…
mooreds

6

RoR 개발에서 주목받는 주니어 / 초보자에 대한 모든 답변의 약간의 업데이트와 응집력이 설명을 위해 여기에 올 것입니다.

돈으로 일하기

:decimal@molf가 제안한 것처럼 돈을 DB에 저장하는 데 사용하십시오 (그리고 돈으로 작업 할 때 회사가 황금 표준으로 사용하는 것).

# 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

몇 가지 점 :

  • :decimalBigDecimal많은 문제를 해결하는 데 사용될 것 입니다.

  • precision그리고 scale당신이 대표하는 내용에 따라 조정되어야한다

    • 귀하가 지불을 받고 송금 하는 데 가장 많은 돈 precision: 8scale: 2주면 999,999.9990 %의 경우에 해당합니다.

    • 부동산이나 희귀 자동차의 가치를 나타내려면 더 높은 값을 사용해야합니다 precision.

    • 좌표 (경도 및 위도)로 작업하는 경우 반드시 더 높아야 scale합니다.

마이그레이션을 생성하는 방법

위 내용으로 마이그레이션을 생성하려면 터미널에서 실행하십시오.

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

또는

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

블로그 게시물에 설명 된대로 .

통화 형식

KISS 추가 라이브러리가 작별 인사를하고 사용이 내장 된 헬퍼. number_to_currency@molf 및 @facundofarias 제안으로 사용하십시오 .

number_to_currencyRails 콘솔에서 도우미 와 함께 플레이하려면 도우미에 액세스하기 위해 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

5

사용 (Railscast) (지불 개정 된에 링크) 가상 속성을 당신이 정수 컬럼에 price_in_cents을 저장하고 getter 및 setter 같은 제품 모델에 가상 속성 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 : 가상 속성 : 가상 속성은 데이터베이스에 직접 매핑되지 않는 양식 필드를 추가하는 깔끔한 방법입니다. 여기에서는 유효성 검사, 연결 등을 처리하는 방법을 보여줍니다.


1
이것은 200.0 한 자리를 남긴다
ajbraus

2

확실히 정수 입니다.

BigDecimal이 기술적으로 존재하더라도 1.5루비에서 순수한 Float를 제공합니다.


2

누군가 Sequel을 사용하는 경우 마이그레이션은 다음과 같습니다.

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

어떻게 든 Sequel은 : precision과 : scale을 무시한다

(후편 버전 : 속편 (3.39.0, 3.38.0))


2

내 기본 API는 모두 돈을 나타내는 데 센트를 사용하고 있었고 변경하고 싶지 않았습니다. 나는 많은 돈을 가지고 일하지도 않았습니다. 그래서 나는 이것을 도우미 메소드에 넣었습니다.

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

정수를 3 자리 이상의 문자열로 변환하고 (필요한 경우 앞에 0을 추가) 마지막 두 자리 앞에 소수점을 삽입하고 결코 사용하지 않습니다 Float. 여기에서 사용 사례에 적합한 통화 기호를 추가 할 수 있습니다.

그것은이다 확실히 하지만 때로는 그의 잘, 신속하고 더러운!


아무도 당신을 숭배하지 않았다는 것을 믿을 수 없습니다. 이것이 내 Money 객체를 API로 가져갈 수있는 형태로 멋지게 만드는 유일한 일이었습니다. (Decimal)
Code-MonKy

2

나는 이런 식으로 사용하고 있습니다 :

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

물론 통화 기호, 정밀도, 형식 등은 각 통화에 따라 다릅니다.


1

몇 가지 옵션을 number_to_currency표준 Rails 4 뷰 도우미로 전달할 수 있습니다 .

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

게시자 : Dylan Markow


0

Ruby & Rails를위한 간단한 코드

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.