Pregunta

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

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

Tengo problemas para mostrar el valor del precio en mis vistas para precios que incluyen euros y centavos. ¿Me estoy perdiendo algo obvio en lo que respecta al manejo de divisas en el marco de Rails?

¿Fue útil?

Solución

Probablemente quiera usar un tipo DECIMAL en su base de datos. En su migración, haga 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 de precios.

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

Como señaló mcl, para imprimir el precio, use:

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

Otros consejos

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

Necesitarás

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

Escriba esto en su 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 cambios adicionales, 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 conviértelo por ti. No es necesario agregar controladores o atributos adicionales a su modelo, ni ayudantes desde su punto de vista.
  • 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 adecuado para la moneda
  • Si necesita enviar centavos (a una pasarela de pago que quiere centavos), product.price.cents.to_s
  • Conversión de divisas gratis

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 precios desde -999,999.99 hasta 999,999.99
También es posible que 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 

para comprobar la cordura de sus valores.

Utilice gema de dinero-rieles . Maneja muy bien el dinero y las monedas en su modelo y también tiene un montón de ayudantes para formatear sus precios.

Si está utilizando Postgres (y desde que estamos en 2017 ahora) es posible que desee probar su columna : money . Pruebe

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

Usando Atributos virtuales (Enlace a Railscast revisado (pagado)) puede almacenar su price_in_cents en un columna entera y agregue un atributo virtual price_in_dollars en su modelo de producto como getter y 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

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.

Definitivamente enteros .

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

Si alguien usa Sequel, la migración se vería así:

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))

Lo estoy usando de esta manera:

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

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

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

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

Publicado por Dylan Markow

Todas mis API subyacentes usaban centavos para representar 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 auxiliar:

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

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

Es definitivamente rápido y sucio, ¡pero a veces está bien!

Código simple para Ruby & amp; Rieles

<%= number_to_currency(1234567890.50) %>

OUT PUT => $1,234,567,890.50

Solo una pequeña actualización y una cohesión de todas las respuestas para algunos aspirantes a junior / 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 dorado 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 , lo que resuelve muchos problemas.

  • precisión y scale deben ajustarse, dependiendo de lo que esté representando

    • Si trabaja con la recepción y el envío de pagos, precisión: 8 y scale: 2 le da 999,999.99 como la cantidad más alta , lo cual está bien en el 90% de los casos.

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

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

    • más alto

Cómo generar una migración

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

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

o

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

como se explica en este blog publicación.

Formato de moneda

KISS las bibliotecas adicionales se despiden y usan ayudantes integrados. Utilice number_to_currency como sugirieron @molf y @facundofarias.

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

Por ejemplo:

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

da el siguiente resultado

2500000,61€

Verifique las otras opciones de number_to_currency ayudante.

Dónde ponerlo

Puede ponerlo en una aplicación auxiliar y usarlo dentro de las 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 Item como método de instancia y llamarlo donde necesite formatear el 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 , utilizada 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
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top