2009-11-03 8 views
9

Ho an application che gestisce gli input di valuta. Tuttavia, se ti trovi negli Stati Uniti, potresti inserire un numero come 12,345.67; in Francia, potrebbe essere 12.345,67.Gestione dell'input della valuta internazionale in Ruby on Rails

C'è un modo semplice, in Rails, per adattare la voce della valuta a una locale?

Nota che non sto cercando la visualizzazione della valuta (ala number_to_currency), sto cercando di trattare con qualcuno che digita una stringa di valuta e la converta in un decimale.

+0

Immetti spesso il segno, per separare i punteggi quando digiti un numero ?? Io personalmente mai. –

+1

Io no, ma non posso garantire che nessuno lo farà. Preferirei sbagliare dalla parte della felicità dell'utente piuttosto che della frustrazione. Inoltre, se stai copiando dei valori, ad esempio, sul tuo estratto conto online, potrebbe essere già formattato con $ e, e. e ogni sorta di altri personaggi. –

risposta

11

Si potrebbe dare a questo un colpo:

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 

E i casi di test:

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,

Si può provare a utilizzare il 'aggregation' feature, combinato con una classe di delegazione. Vorrei fare qualcosa di simile:

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 

In questo modo, sarete in grado di assegnare direttamente:

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

o

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

Il vantaggio è che non c'è bisogno di modificare nulla su controller/viste.

2

L'utilizzo dello translations for numbers nell'I-18n incorporato dovrebbe consentire di inserire i prezzi in un formato (1234.56) e quindi di utilizzare I18n per riportarli automaticamente con le impostazioni internazionali corrette con number_to_currency.

Ovviamente è necessario impostare I18n.locale utilizzando before_filter, controllare I18n guide, sezione 2.3.

4

È necessario pulire l'input in modo che gli utenti possano digitare praticamente ciò che vogliono e otterrete qualcosa di coerente da memorizzare nel database. Supponendo che il tuo modello si chiami "DoughEntry" e il tuo attributo sia "amount", esso viene memorizzato come numero intero.

Ecco un metodo che converte una stringa in input in centesimi (se la stringa termina in due cifre seguendo un delimitatore, si presume che sia un centesimo). Si potrebbe desiderare di rendere questo più intelligente, ma ecco il concetto:

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 

Poi sovrascrivere la funzione di accesso di default "quantità", passando attraverso quel metodo:

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 

Ora stai memorizzazione centesimi nel Banca dati. Radar ha l'idea giusta per tirarlo fuori.

+0

Attenzione, questi gsub lasceranno cadere il simbolo meno, quindi se hai importi negativi ti ritroverai con il valore assoluto. –

+0

Thx Matt, risolto. – Ben

5

Abbiamo scritto questo:

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 

Naturalmente, si dovrà impostare il I18n.locale prima di usare questo. E attualmente converte solo la stringa in un float per le impostazioni locali impostate.(Nel nostro caso, se l'utente si trova sul sito francese, ci aspettiamo che il testo dell'ammontare della valuta abbia solo simboli e formattazione relativi al locale francese).