2009-11-03 8 views
9

Tengo an application que maneja las entradas de moneda. Sin embargo, si se encuentra en los EE. UU., Puede ingresar un número como 12,345.67; en Francia, podría ser 12.345,67.Manejo de la entrada de moneda internacional en Ruby on Rails

¿Existe alguna manera fácil, en Rails, de adaptar la entrada de moneda a una localidad?

Tenga en cuenta que no estoy buscando la pantalla de la moneda (ala number_to_currency), estoy tratando de hacer frente a alguien escribiendo en una cadena de moneda, y convertirla en un decimal.

+0

¿Sueles ingresar el, para separar puntajes cuando escribes un número? Yo personalmente nunca. –

+1

No, pero no puedo garantizar que nadie lo haga. Prefiero equivocarme del lado de la felicidad del usuario en lugar de la frustración. Además, si está copiando valores de, por ejemplo, su extracto bancario en línea, es posible que ya tenga el formato $ y, y. y todo tipo de otros personajes. –

Respuesta

11

Usted podría dar a este un tiro:

def string_to_float(string) 

     string.gsub!(/[^\d.,]/,'')   # Replace all Currency Symbols, Letters and -- from the string 

     if string =~ /^.*[\.,]\d{1}$/  # If string ends in a single digit (e.g. ,2) 
     string = string + "0"    # make it ,20 in order for the result to be in "cents" 
     end 

     unless string =~ /^.*[\.,]\d{2}$/ # If does not end in ,00/.00 then 
     string = string + "00"   # add trailing 00 to turn it into cents 
     end 

     string.gsub!(/[\.,]/,'')   # Replace all (.) and (,) so the string result becomes in "cents" 
     string.to_f/100     # Let to_float do the rest 
    end 

y la prueba de Casos:

describe Currency do 
    it "should mix and match" do 
    Currency.string_to_float("$ 1,000.50").should eql(1000.50) 
    Currency.string_to_float("€ 1.000,50").should eql(1000.50) 
    Currency.string_to_float("€ 1.000,--").should eql(1000.to_f) 
    Currency.string_to_float("$ 1,000.--").should eql(1000.to_f)  
    end  

    it "should strip the € sign" do 
    Currency.string_to_float("€1").should eql(1.to_f) 
    end 

    it "should strip the $ sign" do 
    Currency.string_to_float("$1").should eql(1.to_f) 
    end 

    it "should strip letter characters" do 
    Currency.string_to_float("a123bc2").should eql(1232.to_f) 
    end 

    it "should strip - and --" do 
    Currency.string_to_float("100,-").should eql(100.to_f) 
    Currency.string_to_float("100,--").should eql(100.to_f) 
    end 

    it "should convert the , as delimitor to a ." do 
    Currency.string_to_float("100,10").should eql(100.10) 
    end 

    it "should convert ignore , and . as separators" do 
    Currency.string_to_float("1.000,10").should eql(1000.10) 
    Currency.string_to_float("1,000.10").should eql(1000.10) 
    end 

    it "should be generous if you make a type in the last '0' digit" do 
    Currency.string_to_float("123,2").should eql(123.2) 
    end 
2

Tim,

Usted puede tratar de utilizar la 'aggregation' feature, combinado con una clase de delegación. Me gustaría hacer algo como:

class Product 
    composed_of :balance, 
     :class_name => "Money", 
     :mapping => %w(amount) 
end 

class Money < SimpleDelegator.new 
    include Comparable 
    attr_reader :amount 

    def initialize(amount) 
    @amount = Money.special_transform(amount) 
    super(@amount) 
    end 

    def self.special_transform(amount) 
    # your special convesion function here 
    end 

    def to_s 
    nummber_to_currency @amount 
    end 
end 

De esta manera, usted será capaz de asignar directamente:

Product.update_attributes(:price => '12.244,6') 

o

Product.update_attributes(:price => '12,244.6') 

La ventaja es que usted no tiene que modificar cualquier cosa en controladores/vistas.

2

Utilizando el translations for numbers en la incorporada en I18n debe permitir que ingrese sus precios en un formato (1234.56) y luego usando I18n traerlos de vuelta a cabo con number_to_currency a su impresión de forma automática en el entorno local correcto.

Por supuesto tendrá que configurar I18n.locale usando un before_filter, revise el I18n guide, sección 2.3.

4

Debe limpiar la entrada para que los usuarios puedan escribir casi lo que quieran, y obtendrá algo consistente para almacenar en su base de datos. Suponiendo que su modelo se llama "DoughEntry" y su atributo es "amount", y se almacena como un entero.

Aquí hay un método que convierte una entrada de cadena en centavos (si la cadena termina en dos dígitos después de un delímetro, se supone que es centavos). Es posible que desee hacer esto más inteligente, pero aquí está el concepto:

def convert_to_cents(input) 
    if input =~ /^.*[\.,]\d{2}$/ 
    input.gsub(/[^\d-]/,'').to_i 
    else 
    "#{input.gsub(/[^\d-]/,'')}00".to_i 
    end 
end 

>> convert_to_cents "12,345" 
=> 1234500 
>> convert_to_cents "12.345,67" 
=> 1234567 
>> convert_to_cents "$12.345,67" 
=> 1234567 

Luego sobrescribe el descriptor de acceso por defecto "cantidad", pasándolo a través de ese método:

class DoughEntry << ActiveRecord::Base 

    def amount=(input) 
    write_attribute(:amount, convert_to_cents(input)) 
    end 

protected 
    def convert_to_cents(input) 
    if input =~ /^.*[\.,]\d{2}$/ 
     input.gsub(/[^\d-]/,'').to_i 
    else 
     "#{input.gsub(/[^\d-]/,'')}00".to_i 
    end 
    end 
end 

Ahora que está guardando centavos en el base de datos. El radar tiene la idea correcta para sacarlo de nuevo.

+0

Cuidado, esos gsubs arrojarán el símbolo menos, por lo que si tienes cantidades negativas terminarás con el valor absoluto. –

+0

Thx Matt, fijo. – Ben

5

Nos escribió: A

class String 
    def safe_parse 
    self.gsub(I18n.t("number.currency.format.unit"), '').gsub(I18n.t("number.currency.format.delimiter"), '').gsub(I18n.t("number.currency.format.separator"), '.').to_f 
    end 
end 

Por supuesto, usted tendrá que establecer el I18n.locale antes de usar esto. Y actualmente solo convierte la cadena en un flotante para la configuración regional que se configuró.(En nuestro caso, si el usuario está en el sitio en francés, esperamos que el texto con la cantidad de moneda solo tenga símbolos y formatos relacionados con la configuración regional en francés).