Para birimini / parayı ele almanın en iyi yöntemi nedir?


323

Çok temel bir alışveriş sepeti sistemi üzerinde çalışıyorum.

itemsBir sütun pricetürü olan bir tablo var integer.

Euro ve sent içeren fiyatlar için görüşlerimde fiyat değerini görüntülemekte sorun yaşıyorum. Rails çerçevesinde para işleme konusunda açık bir şey mi eksik?


Birisi kullandığı sql, sonra DECIMAL(19, 4) popüler bir seçimdir kontrol bu da kontrol burada , umut olur kaç ondalık kullanımına yerler karar vermek Dünya Para Biçimleri.
shaijut

Yanıtlar:


495

Muhtemelen DECIMALveritabanınızda bir tür kullanmak istersiniz . Taşıma işleminizde şöyle bir şey yapın:

# 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'te, :decimaltür, BigDecimalfiyat hesaplaması için harika olarak döndürülür .

Tamsayıları kullanmakta ısrar ederseniz, BigDecimalher yerde manuel olarak ve her yerden s'ye dönüştürmeniz gerekecektir, bu muhtemelen sadece bir acı olacaktır.

MLL tarafından işaret edildiği gibi, fiyatı yazdırmak için şunu kullanın:

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

13
Kullanım number_to_currency yardımcı en fazla bilgi api.rubyonrails.org/classes/ActionView/Helpers/...
mlibby

48
Aslında, act_as_dollars ile birlikte bir tamsayıyı kullanmak çok daha güvenli ve kolaydır. Hiç kayan nokta karşılaştırmasıyla ısırıldınız mı? Değilse, bunu ilk deneyiminiz yapmayın. :) act_as_dollars ile, 12.34 formatında şeyler koymak, 1234 olarak saklanır ve 12.34 olarak çıkıyor.
Sarah Mei

50
@Sarah Mei: BigDecimals + ondalık sütun biçimi tam olarak bundan kaçınır.
molf

114
Bu cevabı körü körüne kopyalamamak önemlidir - hassasiyet 8, ölçek 2 size maksimum 999,999,99 değer verir . Bir milyondan fazla bir sayıya ihtiyacınız varsa hassasiyeti artırın!
Jon Cairns

22
Farklı para birimlerini kullanıyorsanız sadece körü körüne 2 ölçeğini kullanmamak da önemlidir - Umman Riyali veya Tunus Dinarı gibi bazı kuzey-afrika ve Arap para birimleri 3 ölçeğine sahiptir, bu nedenle hassas 8 ölçeği 3 daha uygundur .
Richartz'ı

117

İşte composed_of(ActiveRecord'un bir parçası, ValueObject desenini kullanarak) ve Money gem'i kullanan ince ve basit bir yaklaşım

İhtiyacın olacak

  • The Money gem (sürüm 4.1.0)
  • Örneğin bir model Product
  • integerÖrneğin, modelinizdeki (ve veritabanınızdaki) bir sütun:price

Bunu product.rbdosyanıza yazın :

class Product > ActiveRecord::Base

  composed_of :price,
              :class_name => 'Money',
              :mapping => %w(price cents),
              :converter => Proc.new { |value| Money.new(value) }
  # ...

Ne alacaksınız:

  • Herhangi bir ekstra değişiklik olmadan, tüm formlarınız dolar ve sent gösterecektir, ancak iç temsil hala sadece senttir. Formlar "$ 12,034.95" gibi değerleri kabul edecek ve sizin için dönüştürecektir. Modelinize veya görünümünüzdeki yardımcılara ek işleyiciler veya özellikler eklemenize gerek yoktur.
  • product.price = "$12.00" otomatik olarak Money sınıfına dönüşür
  • product.price.to_s ondalık biçimli bir sayı görüntüler ("1234.00")
  • product.price.format para birimi için düzgün biçimlendirilmiş bir dize görüntüler
  • Kuruş göndermeniz gerekiyorsa (peni isteyen bir ödeme ağ geçidine), product.price.cents.to_s
  • Ücretsiz para birimi dönüştürme

14
Bu yaklaşımı seviyorum. Ancak lütfen unutmayın: Bu örnekteki 'fiyat' için taşıma işleminizin, neden işe yaramadığını anlamaya çalışarak delirmeye devam etmek için sıfırlara ve varsayılan değerlere izin vermediğinden emin olun.
Cory

3
Bulduğum money_column mücevher Para birimi dönüştürme gerekmiyorsa, ... kullanımına çok yalındır para mücevher daha kolay olması (Shopify çıkarılan).
talyric

7
Money gem kullanan herkes için Rails çekirdek ekibinin çerçeveden "kompleks_inti" ifadesinin kullanımdan kaldırılması ve kaldırılması konusunda görüştüğü belirtilmelidir. Gem, eğer bu işlemek için güncellenecek şüpheli, ama Rails 4.0 bakarak bu olasılık farkında olmalıdır
Peer Allan

1
@ PeerAllan'ın composed_of burada kaldırılmasıyla ilgili yorumu ile ilgili daha fazla ayrıntı ve alternatif bir uygulama.
HerbCSO

3
Ayrıca, bu gerçekten raylar-para gem kullanarak reasy .
fotanus

25

Para birimi işlemek için yaygın uygulama ondalık türü kullanmaktır. İşte "Rails ile Çevik Web Geliştirme" basit bir örnek

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

Bu, fiyatları -999,999,99 ila 999,999,99 arasında işlemenize olanak tanır.Ayrıca
, öğelerinize bir doğrulama eklemek de isteyebilirsiniz.

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

değerlerinizi akılcı kontrol etmek için.


1
Bu çözüm aynı zamanda SQL toplamını ve arkadaşlarını kullanmanızı sağlar.
Larry K

4
Muhtemelen yapabilir misiniz: validates: price,: presence => true,: numericality => {: bigger_than => 0}
Galaxy

9

Postgres kullanıyorsanız (ve şu anda 2017'de olduğumuzdan) :moneysütun türlerini denemek isteyebilirsiniz .

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

7

Para-ray mücevher kullanın . Modelinizdeki para ve para birimlerini güzelce ele alır ve ayrıca fiyatlarınızı biçimlendirmek için bir grup yardımcıya sahiptir.


Evet, buna katılıyorum. Genel olarak, parayı sentler (tamsayı) olarak saklayarak ve verileri bellekte işlemek için para gibi davranır veya para (para rayları) gibi bir mücevher kullanarak ele alırım. Tamsayılarla kullanılması, bu kötü yuvarlama hatalarını önler. Örneğin, 0.2 * 3 => 0.6000000000000001 Bu, elbette, sadece bir kuruşun kesirlerini işlemeniz gerekmiyorsa çalışır.
Chad M

Ray kullanıyorsanız bu çok hoş. Bırakın ve ondalık sütunla ilgili sorunlar hakkında endişelenmeyin. Bunu bir görünümle kullanırsanız, bu cevap da yardımcı olabilir: stackoverflow.com/questions/18898947/…
16:16

6

Sadece küçük bir güncelleme ve RoR geliştirmedeki bazı yeni başlayanlar / yeni başlayanlar için tüm cevapların bir araya getirilmesi, kesinlikle bazı açıklamalar için buraya gelecektir.

Para ile çalışma

:decimal@Molf'un önerdiği gibi (ve şirketimin para ile çalışırken altın standart olarak kullandığı şey) DB'de para depolamak için kullanın .

# 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

Birkaç puan:

  • :decimalBigDecimalbirçok sorunu çözdüğü için kullanılacak .

  • precisionve scaletemsil ettiğiniz şeye bağlı olarak ayarlanmalıdır

    • Ödeme alma ve gönderme ile çalışıyorsanız precision: 8ve scale: 2size 999,999.99en yüksek tutarı verirseniz , bu da vakaların% 90'ında iyidir.

    • Bir mülkün veya nadir bir arabanın değerini temsil etmeniz gerekiyorsa, daha yüksek bir araç kullanmalısınız precision.

    • Koordinatlarla (boylam ve enlem) çalışıyorsanız, kesinlikle daha yüksek bir değere ihtiyacınız olacaktır scale.

Taşıma nasıl oluşturulur?

Yukarıdaki içerikle taşıma oluşturmak için terminalde çalıştırın:

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

veya

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

Bu blog yazısında açıklandığı gibi .

Para birimi biçimlendirme

KISS ekstra kütüphaneleri güle güle ve kullanım yerleşik yardımcıları. Kullanım number_to_currency@molf ve @facundofarias olarak önerdi.

İle oynamak için number_to_currencyRaylar konsolundaki yardımcı, bir çağrı göndermek ActiveSupport'ın NumberHelperyardımcı erişmek için sınıfta.

Örneğin:

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

Aşağıdaki çıktıyı verir

2500000,61

Diğer kontrol optionsait number_to_currency yardımcı.

Nereye koymalı

Bir uygulama yardımcısına koyabilir ve istediğiniz miktarda görünüm içinde kullanabilirsiniz.

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

Ya Itemda bir örnek yöntemi olarak modele koyabilir ve fiyatı biçimlendirmeniz gereken yere (görünümlerde veya yardımcılarda) çağırabilirsiniz.

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

Ve number_to_currencybir contrroler içinde nasıl kullandığımın bir örneği ( negative_formatgeri ödemeleri temsil etmek için kullanılan seçeneğe dikkat edin )

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

5

Kullanılması (Railscast) (ücretli revize etmek Linki) Sanal Öznitelikler'i Eğer bir tamsayı sütununda price_in_cents depolamak ve bir alıcı ve ayarlayıcı olarak ürün modelinde bir Sanal nitelik price_in_dollars ekleyebilir.

# 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

Kaynak: RailsCasts # 016: Sanal Özellikler : Sanal özellikler, doğrudan veritabanıyla eşleşmeyen form alanları eklemenin temiz bir yoludur. Burada doğrulamaların, ilişkilendirmelerin ve daha fazlasının nasıl ele alınacağını göstereceğim.


1
bu 200,0 bir basamak bırakır
ajbraus

2

Kesinlikle tamsayılar .

Ve BigDecimal teknik olarak mevcut olsa bile 1.5, Ruby'de size saf bir Şamandıra verecektir.


2

Birisi Sequel kullanıyorsa, taşıma aşağıdaki gibi görünecektir:

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

nedense Sequel görmezden gelir: hassasiyet ve: ölçek

(Sequel Sürümü: devam filmi (3.39.0, 3.38.0))


2

Temel API'larımın tamamı parayı temsil etmek için sent kullanıyordu ve bunu değiştirmek istemedim. Ben de büyük miktarlarda parayla çalışmıyordum. Bu yüzden sadece bir yardımcı yöntem koymak:

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

Bu, tamsayıyı en az üç basamaklı bir dizeye dönüştürür (gerekirse baştaki sıfırlar ekleyerek), ardından son iki basamaktan önce ondalık işareti ekler, asla a kullanmaz Float. Buradan kullanım durumunuza uygun olan para birimi simgelerini ekleyebilirsiniz.

Bu var kesinlikle ama bazen bu en gayet hızlı ve kirli!


Kimsenin seni desteklemediğine inanamıyorum. Bu, benim Money nesnesini bir API'nin alabileceği şekilde güzel bir forma sokmaya çalışan tek şeydi. (Ondalık)
Code-MonKy

2

Bu şekilde kullanıyorum:

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

Tabii ki para birimi sembolü, kesinlik, biçim vb. Her para birimine bağlıdır.


1

Bazı seçenekleri number_to_currency(standart Rails 4 görünüm yardımcısına) iletebilirsiniz :

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

Gönderen Dylan Markow


0

Ruby & Rails için basit kod

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.