with rails number money humanized google ruby-on-rails ruby currency

ruby on rails - rails - ¿Cuál es el mejor método de manejo de moneda/dinero?



number to currency rails (13)

Estoy trabajando en un sistema de carrito de compras muy básico.

Tengo una tabla de items que tiene un price de columna de tipo integer .

Tengo problemas para mostrar el valor del precio en mis visitas para los precios que incluyen tanto euros como centavos. ¿Me falta algo obvio en lo que respecta al manejo de la moneda en el marco de Rails?


Al usar los atributos virtuales (enlace a Railscast revisado (pagado)) , puede almacenar su price_in_cents en una columna de enteros y agregar un atributo virtual price_in_dollars en su modelo de producto como captador y definidor.

# 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

Fuente: RailsCasts # 016: Atributos virtuales : Los atributos virtuales son una forma limpia de agregar campos de formulario que no se asignan directamente a la base de datos. Aquí muestro cómo manejar validaciones, asociaciones y más.


Aquí hay un enfoque simple y fino que aprovecha compos_of (parte de ActiveRecord, usando el patrón ValueObject) y la gema de Money

Necesitarás

  • La gema del dinero (versión 4.1.0)
  • Un modelo, por ejemplo, Product
  • Una columna integer en su modelo (y base de datos), por ejemplo :price

Escribe esto en tu archivo product.rb :

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

Lo que obtendrás:

  • Sin ningún cambio adicional, todos sus formularios mostrarán dólares y centavos, pero la representación interna sigue siendo solo centavos. Los formularios aceptarán valores como "$ 12,034.95" y los convertirán por usted. No es necesario agregar controladores o atributos adicionales a su modelo, o ayudantes en su opinión.
  • product.price = "$12.00" se convierte automáticamente a la clase Money
  • product.price.to_s muestra un número con formato decimal ("1234.00")
  • product.price.format muestra una cadena con el formato correcto para la moneda
  • Si necesita enviar centavos (a una pasarela de pago que quiere centavos), product.price.cents.to_s
  • Conversión de moneda gratis

Definitivamente los integers .

Y a pesar de que BigDecimal técnicamente existe, 1.5 todavía te dará un flotador puro en Ruby.


La práctica común para manejar la moneda es usar el tipo decimal. Aquí hay un ejemplo simple de "Desarrollo web ágil con rieles"

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

Esto le permitirá manejar los precios desde -999,999.99 hasta 999,999.99
Es posible que también desee incluir una validación en sus elementos como

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

a la cordura-verifica tus valores.


Lo estoy usando de esta manera:

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

Por supuesto, el símbolo de moneda, la precisión, el formato, etc., dependen de cada moneda.


Mis APIs subyacentes estaban usando centavos para representar el dinero, y no quería cambiar eso. Tampoco estaba trabajando con grandes cantidades de dinero. Así que acabo de poner esto en un método de ayuda:

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

Eso convierte el número entero en una cadena con al menos tres dígitos (agregando ceros iniciales si es necesario), luego inserta un punto decimal antes de los últimos dos dígitos, nunca utilizando un Float . Desde allí puede agregar los símbolos de moneda que sean apropiados para su caso de uso.

Definitivamente es rápido y sucio, pero a veces eso está bien!


Probablemente querrá usar un tipo DECIMAL en su base de datos. En tu migración, haz algo como esto:

# 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

En Rails, el tipo :decimal se devuelve como BigDecimal , lo cual es excelente para el cálculo del precio.

Si insiste en usar números enteros, tendrá que convertir manualmente desde y hacia BigDecimal s a todas partes, lo que probablemente se convertirá en una molestia.

Como lo señala mcl, para imprimir el precio, use:

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


Puede pasar algunas opciones a number_to_currency (un asistente de vista estándar de Rails 4):

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

Según lo publicado por Dylan Markow


Si alguien está usando Sequel, la migración se vería como:

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

De alguna manera Sequel ignora: precisión y: escala.

(Versión de secuela: secuela (3.39.0, 3.38.0))


Si está utilizando Postgres (y dado que estamos en 2017 ahora) puede intentar probar su tipo de columna de :money .

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


Solo una pequeña actualización y una cohesión de todas las respuestas para algunos aspirantes a juniors / principiantes en el desarrollo de RoR que seguramente vendrán aquí para algunas explicaciones.

Trabajando con dinero

Use :decimal para almacenar dinero en la base de datos, como sugirió @molf (y lo que mi compañía usa como estándar de oro cuando trabaja con dinero).

# 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

Pocos puntos:

  • :decimal se utilizará como BigDecimal que resuelve muchos problemas.

  • precision y la scale deben ajustarse, según lo que esté representando.

    • Si trabaja para recibir y enviar pagos, la precision: 8 y la scale: 2 le otorga 999,999.99 como la cantidad más alta, lo que está bien en el 90% de los casos.

    • Si necesita representar el valor de una propiedad o un automóvil raro, debe usar una mayor precision .

    • Si trabaja con coordenadas (longitud y latitud), seguramente necesitará una scale más alta.

Cómo generar una migración.

Para generar la migración con el contenido anterior, ejecute en terminal:

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

o

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

como se explica en esta blog .

Formato de moneda

KISS adiós a las bibliotecas adicionales y utilice ayudantes incorporados. Use number_to_currency como se sugiere @molf y @facundofarias.

Para jugar con el helper number_to_currency en la consola Rails, envíe una llamada a la clase NumberHelper para acceder al helper.

Por ejemplo:

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

da la siguiente salida

2500000,61€

Marque las otras options de ayuda de number_to_currency .

Donde ponerlo

Puede ponerlo en un ayudante de aplicación y usarlo dentro de vistas por cualquier cantidad.

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

O puede ponerlo en el modelo del Item como un método de instancia, y llamarlo cuando necesite dar formato al precio (en vistas o ayudantes).

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

Y, un ejemplo de cómo uso el number_to_currency dentro de un controlador (observe la opción negative_format , que se usa para representar reembolsos)

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


Usa la rails-money . Maneja bien el dinero y las monedas en su modelo y también tiene un montón de ayudantes para dar formato a sus precios.


Código simple para Ruby & Rails

<%= number_to_currency(1234567890.50) %> OUT PUT => $1,234,567,890.50