lib/extenso_pt.rb in extenso_pt-0.5.2 vs lib/extenso_pt.rb in extenso_pt-0.5.3

- old
+ new

@@ -1,172 +1,209 @@ -require "extenso_pt/version" -require "bigdecimal/util" +# frozen_string_literal: true +require 'extenso_pt/version' +require 'bigdecimal/util' + # @author Hernâni Rodrigues Vaz module ExtensoPt class Error < StandardError; end - LC =[:pt,:br] - A0020={pt: ["","UM","DOIS","TRÊS","QUATRO","CINCO","SEIS","SETE","OITO","NOVE","DEZ","ONZE","DOZE","TREZE","CATORZE","QUINZE","DEZASSEIS","DEZASSETE","DEZOITO","DEZANOVE"], - br: ["","UM","DOIS","TRES","QUATRO","CINCO","SEIS","SETE","OITO","NOVE","DEZ","ONZE","DOZE","TREZE","QUATORZE","QUINZE","DEZESSEIS","DEZESSETE","DEZOITO","DEZENOVE"]} - A0100={pt: ["","","VINTE","TRINTA","QUARENTA","CINQUENTA","SESSENTA","SETENTA","OITENTA","NOVENTA"], - br: ["","","VINTE","TRINTA","QUARENTA","CINQUENTA","SESSENTA","SETENTA","OITENTA","NOVENTA"]} - A1000={pt: ["","CEM","CENTO","DUZENTOS","TREZENTOS","QUATROCENTOS","QUINHENTOS","SEISCENTOS","SETECENTOS","OITOCENTOS","NOVECENTOS"], - br: ["","CEM","CENTO","DUZENTOS","TREZENTOS","QUATROCENTOS","QUINHENTOS","SEISCENTOS","SETECENTOS","OITOCENTOS","NOVECENTOS"]} - A1e24={pt: ["","MIL"," MILHÃO"," MIL MILHÃO"," BILIÃO"," MIL BILIÃO"," TRILIÃO"," MIL TRILIÃO",""," MIL"," MILHÕES"," MIL MILHÕES"," BILIÕES"," MIL BILIÕES"," TRILIÕES"," MIL TRILIÕES"], - br: ["","MIL"," MILHÃO"," BILHÃO"," TRILHÃO"," QUADRILHÃO"," QUINTILHÃO"," SEXTILHÃO",""," MIL"," MILHÕES"," BILHÕES"," TRILHÕES"," QUADRILHÕES"," QUINTILHÕES"," SEXTILHÕES"]} + LC = %i[pt br].freeze + A0020 = { pt: ['', 'UM', 'DOIS', 'TRÊS', 'QUATRO', 'CINCO', 'SEIS', 'SETE', 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE', 'TREZE', 'CATORZE', 'QUINZE', 'DEZASSEIS', 'DEZASSETE', 'DEZOITO', 'DEZANOVE'], + br: ['', 'UM', 'DOIS', 'TRES', 'QUATRO', 'CINCO', 'SEIS', 'SETE', 'OITO', 'NOVE', 'DEZ', 'ONZE', 'DOZE', 'TREZE', 'QUATORZE', 'QUINZE', 'DEZESSEIS', 'DEZESSETE', 'DEZOITO', 'DEZENOVE'] }.freeze + A0100 = { pt: ['', '', 'VINTE', 'TRINTA', 'QUARENTA', 'CINQUENTA', 'SESSENTA', 'SETENTA', 'OITENTA', 'NOVENTA'], + br: ['', '', 'VINTE', 'TRINTA', 'QUARENTA', 'CINQUENTA', 'SESSENTA', 'SETENTA', 'OITENTA', 'NOVENTA'] }.freeze + A1000 = { pt: ['', 'CEM', 'CENTO', 'DUZENTOS', 'TREZENTOS', 'QUATROCENTOS', 'QUINHENTOS', 'SEISCENTOS', 'SETECENTOS', 'OITOCENTOS', 'NOVECENTOS'], + br: ['', 'CEM', 'CENTO', 'DUZENTOS', 'TREZENTOS', 'QUATROCENTOS', 'QUINHENTOS', 'SEISCENTOS', 'SETECENTOS', 'OITOCENTOS', 'NOVECENTOS'] }.freeze + A1e24 = { pt: ['', 'MIL', ' MILHÃO', ' MIL MILHÃO', ' BILIÃO', ' MIL BILIÃO', ' TRILIÃO', ' MIL TRILIÃO', '', ' MIL', ' MILHÕES', ' MIL MILHÕES', ' BILIÕES', ' MIL BILIÕES', ' TRILIÕES', ' MIL TRILIÕES'], + br: ['', 'MIL', ' MILHÃO', ' BILHÃO', ' TRILHÃO', ' QUADRILHÃO', ' QUINTILHÃO', ' SEXTILHÃO', '', ' MIL', ' MILHÕES', ' BILHÕES', ' TRILHÕES', ' QUADRILHÕES', ' QUINTILHÕES', ' SEXTILHÕES'] }.freeze - # Produz o extenso dum grupo 3 digitos em portugûes de portugal ou brasil. + # Produz o extenso das centenas em portugûes de portugal ou brasil. # # @param [Integer] mil o valor dum grupo 3 digitos a converter - # @param [Integer] pos posição deste grupo 3 digitos no valor monetário em tratamento - # @return [String] o extenso dum grupo 3 digitos - def self.e999(mil,pos=0) - cem=mil%100 - A1000[@@lc][(mil>100?1:0)+mil/100]+(mil>100&&cem>0?" E ":"")+ # extenso das centenas - A0100[@@lc][cem/10]+(mil>20&&mil%10>0?" E ":"")+ # extenso dos dezenas - A0020[@@lc][pos==1&&mil==1?0:cem<20?cem:cem%10] # extenso das unidades mais 10-19 + # @return [String] o extenso das centenas + def self.e900(mil) + A1000[@@lc][(mil > 100 ? 1 : 0) + mil / 100] + (mil > 100 && mil % 100 > 0 ? ' E ' : '') end - # Produz o extenso dum valor monetário em portugûes de portugal ou brasil. + # Produz o extenso das dezenas em portugûes de portugal ou brasil. # + # @param [Integer] mil o valor dum grupo 3 digitos a converter + # @return [String] o extenso das dezenas + def self.e90(mil) + A0100[@@lc][mil % 100 / 10] + (mil > 20 && mil % 10 > 0 ? ' E ' : '') + end + + # Produz o extenso das unidades em portugûes de portugal ou brasil. + # + # @param [Integer] mil o valor dum grupo 3 digitos a converter + # @return [String] o extenso das unidades + def self.e9(cem) + A0020[@@lc][(cem < 20 ? cem : cem % 10)] + end + + # Produz o qualificador grupo de 3 digitos em portugûes de portugal ou brasil. + # # @param [Integer] pos posição actual nos grupos 3 digitos do valor monetário - # @param [String] ext extenso actual em construção - # @return [String] o extenso dum valor monetário - def self.enumerico(pos=0,ext="") + # @return [String] o qualificador grupo de 3 digitos + def self.e1e24(pos) + A1e24[@@lc][@@ai[pos] > 0 ? @@ai[pos] > 1 ? 8 + pos : 0 + pos : 0] + end - # testa fim do valor monetário - if (pos>=@@ai.count) + # Produz o extenso da parte fracionária dum valor monetário em portugûes de portugal ou brasil. + # + # @return [String] o extenso da parte fracionária dum valor monetário + def self.ef99 + e90(@@nf) + e9(@@nf) + (@@nf == 1 ? ' ' + @@cs : @@nf > 1 ? ' ' + @@cp : '') + end - # soma grupos 3 digitos para controle de singular/plural - s06=@@ai[0].to_i+@@ai[1].to_i*2 # grupos 1,2 (primeiros 6 digitos) - sm6=@@ai[2..-1].to_a.inject(:+).to_i*2 # grupos 3.. (digitos acima de 6) + # Produz final da moeda em portugûes de portugal ou brasil. + # + # @return [String] o final da moeda + def self.efim + # soma todos os grupos do valor monetário + sto = @@s6 + @@m6 - if (s06+sm6+@@nf==0) - "ZERO "+@@mp # caso especial de zero - else - ext+=" DE" if (sm6>0&&s06==0) # proposição DE para >1e6 e zero nos primeiros 6 digitos - ext+=" "+@@ms if (s06+sm6==1) # singular da moeda - ext+=" "+@@mp if (s06+sm6>1) # plural da moeda - ext+=" E " if (s06+sm6>0&&@@nf>0) # proposição E entre parte inteira e parte fracionária - ext+=e999(@@nf) # extenso da parte fracionária - ext+=" "+@@cs if (@@nf==1) # singular da parte fracionária - ext+=" "+@@cp if (@@nf>1) # plural da parte fracionária - ext - end - else - # tratamento do grupo actual 3 digitos - dg3 =e999(@@ai[pos],pos) # extenso - dg3+=A1e24[@@lc][@@ai[pos]>0?@@ai[pos]>1?8+pos:pos:0] # qualificador + (@@s6 == 0 && @@m6 > 0 ? ' DE' : '') + # proposição DE se zero nos primeiros 6 digitos e maiores que 1e6 + (sto == 1 ? ' ' + @@ms : sto > 1 ? ' ' + @@mp : '') + # singular/plural da moeda + (sto > 0 && @@nf > 0 ? ' E ' : '') + # proposição E entre parte inteira e parte fracionária + ef99 # extenso da parte fracionária dum valor monetário + end - # convenção de separação com grupo anterior 3 digitos - if (pos>0) - dg3+=" E " if (@@ai[pos-1]<101&&@@ai[pos-1]>0) # grupo < 101 => proposição E - dg3+=" " if (@@ai[pos-1]>100) # grupo > 100 => espaço - end + # Produz o extenso grupo 3 digitos em portugûes de portugal ou brasil. + # + # @param [Integer] pos posição actual nos grupos 3 digitos do valor monetário + # @return [String] o extenso grupo 3 digitos + def self.edg3(pos) + # extenso do grupo actual 3 digitos + pos == 1 && @@ai[pos] == 1 ? e1e24(pos) : e900(@@ai[pos]) + e90(@@ai[pos]) + e9(@@ai[pos] % 100) + e1e24(pos) + end + # Produz separação entre grupos 3 digitos + # + # @param [Integer] pos posição actual nos grupos 3 digitos do valor monetário + # @return [String] separação entre grupos 3 digitos + def self.esep(pos) + pos > 0 ? @@ai[pos - 1] > 100 ? ' ' : @@ai[pos - 1] > 0 ? ' E ' : '' : '' + end + + # Parametrizar grupos 3 digitos para controle de singular/plural + # + def self.pdg3 + @@s6 = @@ai[0].to_i + @@ai[1].to_i * 2 # soma grupos 1,2 (primeiros 6 digitos) + @@m6 = @@ai[2..-1].to_a.inject(:+).to_i * 2 # soma grupos 3.. (digitos acima de 6) + end + + # Parametrizar parte inteira/fracionária do valor monetário + # + # @param [String] dig string de digitos do valor monetário + def self.pintfra(dig) + # parte inteira do valor monetário => array grupos 3 digitos ex: 123022.12 => [22, 123] + @@ai = dig[/^\d+/].to_s.reverse.scan(/\d{1,3}/).map { |i| i.reverse.to_i } + + # parte fracionária do valor monetário ex: 123022.12 => 12 + # arredondada a 2 casas decimais (cêntimos/centavos) + @@nf = (dig[/\.\d*/].to_f * 100).round + end + + private_class_method :pintfra, :pdg3, :esep, :edg3, :e1e24, :e900, :e90, :e9, :ef99, :efim + + # Parametrizar moeda inferindo singular a partir do plural + # + # @param [Hash] moeda as opcoes para parametrizar a moeda/fração + # @option moeda [Symbol] :lc locale do extenso - portugûes de portugal (:pt) ou brasil (:br) + # @option moeda [String] :msingular moeda no singular - pode ser inferido do plural menos "S" + # @option moeda [String] :fsingular fração no singular - pode ser inferido do plural menos "S" + # @option moeda [String] :mplural moeda no plural + # @option moeda [String] :fplural fração no plural + def self.isingular(moeda) + @@ms = moeda[:msingular] || (moeda[:mplural].to_s[-1] == 'S' ? moeda[:mplural][0..-2] : 'EURO') + @@cs = moeda[:fsingular] || (moeda[:fplural].to_s[-1] == 'S' ? moeda[:fplural][0..-2] : 'CÊNTIMO') + end + + # Parametrizar moeda inferindo plural a partir do singular + # + # @param [Hash] moeda as opcoes para parametrizar a moeda/fração + # @option moeda [Symbol] :lc locale do extenso - portugûes de portugal (:pt) ou brasil (:br) + # @option moeda [String] :msingular moeda no singular + # @option moeda [String] :fsingular fração no singular + # @option moeda [String] :mplural moeda no plural - pode ser inferido do singular mais "S" + # @option moeda [String] :fplural fração no plural - pode ser inferido do singular mais "S" + def self.iplural(moeda) + @@mp = moeda[:mplural] || @@ms + 'S' + @@cp = moeda[:fplural] || @@cs + 'S' + end + + # Produz o extenso dum valor monetário em portugûes de portugal ou brasil. + # + # @param [Integer] pos posição actual nos grupos 3 digitos do valor monetário + # @param [String] ext extenso actual em construção + # @return [String] o extenso dum valor monetário + def self.enumerico(pos = 0, ext = '') + # testa fim do valor monetário + if pos >= @@ai.count + # parametrizar grupos 3 digitos (@@s6, @@m6) para controle de singular/plural + pdg3 + + # caso especial zero + @@s6 == 0 && @@m6 == 0 && @@nf == 0 ? 'ZERO ' + @@mp : ext + efim + else # tratamento do proximo grupo 3 digitos - enumerico(pos+1,dg3+ext) + enumerico(pos + 1, edg3(pos) + esep(pos) + ext) end end # Converte um objeto criando extenso(s) em portugûes de portugal ou brasil. # # @param [Object] objeto o objeto a converter pode ser (String, Float, Integer, Array, Range, Hash) ou uma mistura # @return [String, Array<String>, Hash<String>] extenso se o objecto for (String, Float, Integer), Array dos extensos se o objecto for (Array, Range) ou Hash dos extensos se o objecto for (Hash) - def self.eobjeto(obj) - - if (obj.kind_of?Hash) + def self.pobjeto(obj) + if obj.is_a?(Hash) # converte os valores do Hash nos seus extensos mantendo as chaves - devolve um Hash - obj.map{|k,v|[k,eobjeto(v)]}.to_h - elsif (obj.respond_to?:to_a) + obj.map { |k, v| [k, pobjeto(v)] }.to_h + elsif obj.respond_to? :to_a # converte o objecto num Array e converte os valores do Array nos seus extensos - devolve um Array - obj.to_a.map{|a|eobjeto(a)} + obj.to_a.map { |v| pobjeto(v) } else - # converte objeto numa string de digitos + # converte objeto numa string de digitos # usa bigdecimal/util para evitar aritmética binária (tem problemas com valores >1e12) # qualquer valor não convertivel (ex: texto) resulta em "0.0" - svalor=obj.to_d.to_s('F') + sdigitos = obj.to_d.to_s('F') - # array da parte inteira do valor monetário dividido em grupos 3 digitos ex: 123022.12 => [22, 123] - # esta variavel é usada no processo de conversão - @@ai=svalor[/^\d+/].to_s.reverse.scan(/\d{1,3}/).map{|i|i.reverse.to_i} + # parametrizar parte inteira/fracionária (@@ai, @@nf) do valor monetário + pintfra(sdigitos) - # parte fracionária do valor monetário ex: 123022.12 => 12 - # arredondada a 2 casas decimais (cêntimos/centavos) - # esta variavel é usada no processo de conversão - @@nf=(svalor[/\.\d*/].to_f*100).round - - # valores >1e24 não são tratados - if (@@ai.count>8) - "" - else - # extenso dum numerico - enumerico - end + # processar extenso - valores superiores a 1e24 não são tratados + sdigitos[/^\d+/].length <= 24 ? enumerico : '' end end # Produz extenso(s) de objeto(s) em portugûes de portugal ou brasil. # # @param [Hash] moeda as opcoes para parametrizar a moeda/fração # @option moeda [Symbol] :lc locale do extenso - portugûes de portugal (:pt) ou brasil (:br) - # @option moeda [String] :msingular moeda no singular - pode ser inferido do plural menos "S" - # @option moeda [String] :fsingular fração no singular - pode ser inferido do plural menos "S" - # @option moeda [String] :mplural moeda no plural - pode ser inferido do singular mais "S" - # @option moeda [String] :fplural fração no plural - pode ser inferido do singular mais "S" + # @option moeda [String] :msingular moeda no singular + # @option moeda [String] :fsingular fração no singular + # @option moeda [String] :mplural moeda no plural + # @option moeda [String] :fplural fração no plural # @return [String, Array<String>, Hash<String>] extenso se o objecto for (String, Float, Integer), Array dos extensos se o objecto for (Array, Range) ou Hash dos extensos se o objecto for (Hash) - def extenso(moeda={lc:(:pt),msingular:"EURO",fsingular:"CÊNTIMO"}) + def extenso(moeda = { lc: :pt, msingular: 'EURO', fsingular: 'CÊNTIMO' }) + # parametrização por defeito para :br + moeda = { lc: :br, msingular: 'REAL', mplural: 'REAIS', fsingular: 'CENTAVO' } if moeda[:lc] == :br && !moeda[:msingular] && !moeda[:mplural] - # parametrização por defeito para locale => :br - moeda={lc:(:br),msingular:"REAL",mplural:"REAIS",fsingular:"CENTAVO"} if (moeda[:lc]==:br&&!moeda[:mplural]&&!moeda[:fplural]) + # somente [:pt, :br] + @@lc = LC.include?(moeda[:lc]) ? moeda[:lc] : :pt - # se locale nao for [:pt,:br] entao :pt - # esta variavel é usada no processo de conversão - if (LC.include?moeda[:lc]) - @@lc=moeda[:lc] - else - @@lc=:pt - end + # parametrizar moeda + ExtensoPt.isingular(moeda) + ExtensoPt.iplural(moeda) - # inferir singular da moeda a partir do plural - # estas variaveis são usadas no processo de conversão - if (moeda[:msingular]) - @@ms=moeda[:msingular] - elsif (moeda[:mplural].to_s[-1]=="S") - @@ms=moeda[:mplural][0..-2] - else - @@ms="EURO" - end - # inferir singular da fração a partir do plural - if (moeda[:fsingular]) - @@cs=moeda[:fsingular] - elsif (moeda[:fplural].to_s[-1]=="S") - @@cs=moeda[:fplural][0..-2] - else - @@cs="CÊNTIMO" - end - - # inferir plural da moeda a partir do singular - # estas variaveis são usadas no processo de conversão - if (moeda[:mplural]) - @@mp=moeda[:mplural] - else - @@mp=@@ms+"S" - end - # inferir plural da fração a partir do singular - if (moeda[:fplural]) - @@cp=moeda[:fplural] - else - @@cp=@@cs+"S" - end - - # extenso do objeto - ExtensoPt.eobjeto(self) + # processar objeto + ExtensoPt.pobjeto(self) end end -class Hash; include ExtensoPt;end -class Array; include ExtensoPt;end -class Range; include ExtensoPt;end -class Float; include ExtensoPt;end -class Integer;include ExtensoPt;end -class String; include ExtensoPt;end +class Hash; include ExtensoPt; end +class Array; include ExtensoPt; end +class Range; include ExtensoPt; end +class Float; include ExtensoPt; end +class Integer; include ExtensoPt; end +class String; include ExtensoPt; end