Was ist die beste Methode im Umgang mit Währung/Geld?
-
06-07-2019 - |
Frage
Ich arbeite an einer sehr einfachen Warenkorb-system.
Ich habe eine Tabelle items
mit einer Spalte price
Typ integer
.
Ich habe Probleme mit dem Preis Wert, meine Ansichten für die Preise, die umfassen sowohl Euro und Cent.Bin ich etwas fehlt offensichtlich so weit wie handling Währung in der Rails-framework ist betroffen?
Lösung
Sie werden wahrscheinlich eine DECIMAL
Art in Ihrer Datenbank verwenden möchten. In Ihrer Migration, etwas tun, wie folgt aus:
# 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
In Rails, der :decimal
Typ wird als BigDecimal
zurückgegeben, die für die Preiskalkulation groß ist.
Wenn Sie sich mit ganzen Zahlen bestehen, werden Sie manuell überall und von BigDecimal
s umwandeln müssen, was wahrscheinlich nur ein Schmerz geworden.
Wie von mcl, um den Preis zu drucken:
number_to_currency(price, :unit => "€")
#=> €1,234.01
Andere Tipps
Hier ist ein schöner, einfacher Ansatz, den composed_of
(Teil von Active, mit dem ValueObject Muster) und das Geld gem
Sie müssen
- Die Geld gem (Version 4.1.0)
- Ein Modell, zum Beispiel
Product
- Eine
integer
Spalte in Ihrem Modell (und Datenbank), zum Beispiel:price
Schreiben Sie dies in Ihrer product.rb
-Datei:
class Product > ActiveRecord::Base
composed_of :price,
:class_name => 'Money',
:mapping => %w(price cents),
:converter => Proc.new { |value| Money.new(value) }
# ...
Was Sie erhalten:
- Ohne zusätzliche Änderungen, werden alle Ihre Formen zeigen Dollar und Cent, aber die interne Darstellung ist nach wie vor nur Cent. Die Formulare werden Werte wie „$ 12,034.95“ akzeptieren und konvertiert es für dich. Es gibt keine Notwendigkeit, zusätzliche Handler hinzuzufügen oder Attribute zu Ihrem Modell oder Helfer in der Ansicht.
-
product.price = "$12.00"
wandelt automatisch in dem Geld Klasse -
product.price.to_s
zeigt eine Dezimalzahl formatiert Nummer ( "1234,00") -
product.price.format
zeigt eine korrekt formatierte Zeichenfolge für die Währung - Wenn Sie Cent senden (zu einem Payment-Gateway, das ein paar Cent will),
product.price.cents.to_s
- Währungsumrechnung kostenlos
Gängige Praxis Währung für den Umgang mit dezimal Typen verwenden. Hier ist ein einfaches Beispiel von "Agile Web Development mit Rails"
add_column :products, :price, :decimal, :precision => 8, :scale => 2
Damit können Sie Preise von -999,999.99 zu 999,999.99
zu handhaben
Sie können auch eine Validierung in Ihrem Artikel wie
def validate
errors.add(:price, "should be at least 0.01") if price.nil? || price < 0.01
end
zur Vernunft Überprüfen Sie Ihre Werte.
Verwenden Sie Geld-Schienen gem . Es behandelt schön Geld und Währungen in Ihrem Modell und hat auch eine Reihe von Helfern Ihre Preise zu formatieren.
Wenn Sie Postgres (und da wir im Jahr 2017 nun) möchten Sie vielleicht, um Ihre :money
Spalte Typ eine versuchen.
add_column :products, :price, :money, default: 0
Virtuelle Attribute (Link zur revised (gegen Entgelt) Railscast) Sie price_in_cents in einem speichern integer-Spalte und fügen Sie ein virtuelles Attribut price_in_dollars in Ihrem Produktmodell als Getter und 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
Quelle: Railscasts # 016: Virtuelle Attribute : virtuelle Attribute sind eine saubere Art und Weise Formularfelder hinzufügen, die Karte nicht direkt auf die Datenbank. Hier zeige ich, wie Validierungen zu handhaben, Verbände und vieles mehr.
Auf jeden Fall ganzen Zahlen .
Und obwohl BigDecimal technisch existiert 1.5
werden Sie noch eine reine Float in Ruby geben.
Wenn jemand Sequel mit der Migration etwas aussehen würde:
add_column :products, :price, "decimal(8,2)"
irgendwie Sequel ignoriert: Präzision und: Skala
(Sequel Version: Fortsetzung (3.39.0, 3.38.0))
Ich benutze es auf diese Weise:
number_to_currency(amount, unit: '€', precision: 2, format: "%u %n")
Natürlich, die das Währungssymbol, Präzision, Format und so weiter auf jeder Währung abhängt.
Sie können einige Optionen übergeben number_to_currency
(ein Standard-Rails 4 View Helfer):
number_to_currency(12.0, :precision => 2)
# => "$12.00"
Wie geschrieben von Dylan Markow
Meine zugrunde liegenden APIs waren Cents alle mit Geld zu repräsentieren, und ich wollte nicht, das ändern. Auch arbeite ich mit großen Mengen an Geld. Also ich habe dies nur in einer Hilfsmethode:
sprintf("%03d", amount).insert(-3, ".")
Das wandelt die ganze Zahl in einen String mit mindestens drei Ziffern (Hinzufügen von führenden Nullen, falls erforderlich), dann fügt ein Komma vor den letzten beiden Ziffern, nie ein Float
verwenden. Von dort können Sie addieren, was Währungssymbole für Ihren Anwendungsfall geeignet sind.
Es ist auf jeden Fall schnell und schmutzig, aber manchmal das ist gut so!
Einfacher Code für Ruby & Rails
<%= number_to_currency(1234567890.50) %>
OUT PUT => $1,234,567,890.50
Nur ein kleines Update und ein Zusammenhalt aller Antworten für einige aufstrebende Junioren / Anfänger in RoR Entwicklung, die sicherlich kommen hier einige Erklärungen.
Arbeiten mit Geld
Mit :decimal
zu speichern Geld in der DB, wie @molf vorgeschlagen (und was mein Unternehmen verwendet als Goldstandard, wenn sie mit Geld arbeiten).
# 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
Ein paar Punkte:
-
:decimal
verwendet werden, wieBigDecimal
wird, die eine Menge Probleme löst. -
precision
undscale
sollte so eingestellt werden, je nachdem, was Sie repräsentieren-
Wenn Sie mit Empfangen und Senden von Zahlungen arbeiten,
precision: 8
undscale: 2
geben Sie als höchste Menge999,999.99
, die in 90% der Fälle in Ordnung ist. -
Wenn Sie den Wert einer Immobilie oder eines seltenen Auto darstellen müssen, sollten Sie eine höhere
precision
verwenden. -
Wenn Sie mit den Koordinaten (Längen- und Breitengrad) arbeiten, werden Sie sicherlich brauchen eine höhere
scale
.
-
Wie eine Migration erzeugen
Um die Migration mit dem obigen Inhalt zu erzeugen, in Terminal ausführen:
bin/rails g migration AddPriceToItems price:decimal{8-2}
oder
bin/rails g migration AddPriceToItems 'price:decimal{5,2}'
, wie in diesem Blog Post.
Währungs Formatierung
KISS die zusätzliche Bibliotheken auf Wiedersehen und verwenden eingebaute Helfer. Verwenden Sie number_to_currency
als @molf und @facundofarias vorgeschlagen.
mit number_to_currency
Helfern zu spielen, in Rails-Konsole, einen Aufruf an die ActiveSupport
Klasse, um die NumberHelper
zu senden, um die Helfer zu gelangen.
Zum Beispiel:
ActiveSupport::NumberHelper.number_to_currency(2_500_000.61, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
gibt die folgende Ausgabe
2500000,61€
Überprüfen Sie die anderen options
von number_to_currency Helfer.
Wo es setzen
Sie können es in einer Anwendung Helfer setzen und es in Ansichten für jede Menge verwenden.
module ApplicationHelper
def format_currency(amount)
number_to_currency(amount, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Oder Sie können es im Item
Modell als eine Instanz Methode setzen, und es nennen, wo Sie den Preis zu formatieren (in Ansichten oder Helfer).
class Item < ActiveRecord::Base
def format_price
number_to_currency(price, unit: '€', precision: 2, separator: ',', delimiter: '', format: "%n%u")
end
end
Und ein Beispiel, wie ich die number_to_currency
in einem contrroler verwenden (die negative_format
Option bemerken, verwendet Erstattungen darzustellen)
def refund_information
amount_formatted =
ActionController::Base.helpers.number_to_currency(@refund.amount, negative_format: '(%u%n)')
{
# ...
amount_formatted: amount_formatted,
# ...
}
end