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

- old
+ new

@@ -1,209 +1,296 @@ # frozen_string_literal: true require 'extenso_pt/version' require 'bigdecimal/util' -# @author Hernâni Rodrigues Vaz +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 + +# @author Hernani Rodrigues Vaz module ExtensoPt class Error < StandardError; end - 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 das centenas em portugûes de portugal ou brasil. + # Produz o extenso das centenas em portugues de portugal ou brasil. # # @param [Integer] mil o valor dum grupo 3 digitos a converter # @return [String] o extenso das centenas def self.e900(mil) - A1000[@@lc][(mil > 100 ? 1 : 0) + mil / 100] + (mil > 100 && mil % 100 > 0 ? ' E ' : '') + A1000[@lc][(mil > 100 ? 1 : 0) + mil / 100] + + (mil > 100 && (mil % 100).positive? ? ' E ' : '') end - # Produz o extenso das dezenas em portugûes de portugal ou brasil. + # Produz o extenso das dezenas em portugues 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 ' : '') + A0100[@lc][mil % 100 / 10] + (mil > 20 && (mil % 10).positive? ? ' E ' : '') end - # Produz o extenso das unidades em portugûes de portugal ou brasil. + # Produz o extenso das unidades em portugues de portugal ou brasil. # - # @param [Integer] mil o valor dum grupo 3 digitos a converter + # @param [Integer] cem 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)] + A0020[@lc][(cem < 20 ? cem : cem % 10)] end - # Produz o qualificador grupo de 3 digitos em portugûes de portugal ou brasil. + # Produz extenso parte fracionaria em portugues de portugal ou brasil. # - # @param [Integer] pos posição actual nos grupos 3 digitos do valor monetário - # @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 - - # 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 + # @return [String] o extenso da parte fracionaria dum valor monetario def self.ef99 - e90(@@nf) + e9(@@nf) + (@@nf == 1 ? ' ' + @@cs : @@nf > 1 ? ' ' + @@cp : '') + if @nf.positive? + e90(@nf) + e9(@nf) + (@nf > 1 ? ' ' + @cp : ' ' + @cs) + else + '' + end end - # Produz final da moeda em portugûes de portugal ou brasil. + # Produz final da moeda em portugues de portugal ou brasil. # # @return [String] o final da moeda def self.efim - # soma todos os grupos do valor monetário - sto = @@s6 + @@m6 - - (@@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 + # proposicao DE entre parte inteira e moeda + emo = @de ? ' DE' : '' + # moeda singular/plural + emo += @tt > 1 ? ' ' + @mp : ' ' + @ms if @tt.positive? + # proposicao E entre moeda e parte fracionaria + # extenso da parte fracionaria + emo + (@tt.positive? && @nf.positive? ? ' E ' : '') + ef99 end - # Produz o extenso grupo 3 digitos em portugûes de portugal ou brasil. + # Produz separador entre grupos 3 digitos # - # @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 + # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario + # @return [String] separador entre grupos 3 digitos def self.esep(pos) - pos > 0 ? @@ai[pos - 1] > 100 ? ' ' : @@ai[pos - 1] > 0 ? ' E ' : '' : '' + if pos.positive? && @ai[pos - 1].positive? + @ai[pos - 1] > 100 ? ' ' : ' E ' + else + '' + end end - # Parametrizar grupos 3 digitos para controle de singular/plural + # Produz qualificador grupo de 3 digitos em portugues de portugal ou brasil. # - 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) + # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario + # @return [String] qualificador grupo de 3 digitos + def self.e1e24(pos) + if @ai[pos].positive? + A1E24[@lc][@ai[pos] > 1 ? 8 + pos : pos] + else + '' + end end - # Parametrizar parte inteira/fracionária do valor monetário + # Produz extenso grupo 3 digitos em portugues de portugal ou brasil. # - # @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 + # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario + # @return [String] extenso grupo 3 digitos + def self.edg3(pos) + dg3 = if pos == 1 && @ai[pos] == 1 + # caso especial MIL EUROS + '' + else + e900(@ai[pos]) + e90(@ai[pos]) + e9(@ai[pos] % 100) + end + # qualificador grupo de 3 digitos + dg3 + e1e24(pos) end - private_class_method :pintfra, :pdg3, :esep, :edg3, :e1e24, :e900, :e90, :e9, :ef99, :efim - - # Parametrizar moeda inferindo singular a partir do plural + # Parametrizar controle singular/plural & proposicoes # - # @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') + def self.pcontrolo + # soma grupos 1,2 (primeiros 6 digitos) + @s6 = @ai[0].to_i + @ai[1].to_i * 2 + # soma grupos 3.. (digitos acima de 6) + @m6 = @ai[2..-1].to_a.inject(:+).to_i * 2 + @tt = @s6 + @m6 # proposicao E & singular/plural + @de = @s6.zero? && @m6.positive? # proposicao DE end - # Parametrizar moeda inferindo plural a partir do singular + # Produz o extenso dum valor monetario em portugues 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 - # @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 + # @param [Integer] pos posicao actual nos grupos 3 digitos do valor monetario + # @param [String] ext extenso em construcao + # @return [String] o extenso dum valor monetario 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 + # testa fim do valor monetario + if pos >= @ai.count + # parametrizar controle singular/plural & proposicoes + pcontrolo # caso especial zero - @@s6 == 0 && @@m6 == 0 && @@nf == 0 ? 'ZERO ' + @@mp : ext + efim + (@tt + @nf).zero? ? 'ZERO ' + @mp : ext + efim else # tratamento do proximo grupo 3 digitos enumerico(pos + 1, edg3(pos) + esep(pos) + ext) end end - # Converte um objeto criando extenso(s) em portugûes de portugal ou brasil. + # Parametrizar parte inteira/fracionaria do valor monetario # - # @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) + # @param [String] dig string de digitos rdo valor monetario + def self.pintfra(dig) + # parte inteira do valor monetario => 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 fracionaria do valor monetario + # ex: 123022.12 => 12 + # arredondada a 2 casas decimais (centimos/centavos) + @nf = (dig[/\.\d*/].to_f * 100).round + end + + private_class_method :pintfra, :pcontrolo, :esep, :edg3, :e1e24, + :e900, :e90, :e9, :ef99, :efim + + # Converte um objeto criando extenso(s) em portugues de portugal ou brasil. + # + # @param [Object] objeto objeto a converter + # (String, Float, Integer, Array, Range, Hash) + # @return [String] extenso se objecto for (String, Float, Integer), + # Array<extensos> se objecto for (Array, Range), + # Hash<extensos> se objecto for (Hash) def self.pobjeto(obj) if obj.is_a?(Hash) - # converte os valores do Hash nos seus extensos mantendo as chaves - devolve um Hash + # converte os valores do Hash nos seus extensos - devolve um Hash 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 + elsif obj.respond_to?(:to_a) + # converte o objecto num Array com os extensos dos valores obj.to_a.map { |v| pobjeto(v) } else # 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" + # usa bigdecimal/util para evitar aritmetica binaria + # (tem problemas com valores >1e12) + # qualquer valor nao convertivel (ex: texto) resulta em "0.0" sdigitos = obj.to_d.to_s('F') - # parametrizar parte inteira/fracionária (@@ai, @@nf) do valor monetário + # parametrizar parte inteira/fracionaria (@ai, @nf) do valor monetario pintfra(sdigitos) - # processar extenso - valores superiores a 1e24 não são tratados + # processar extenso - valores superiores a 1e24 nao sao tratados sdigitos[/^\d+/].length <= 24 ? enumerico : '' end end - # Produz extenso(s) de objeto(s) em portugûes de portugal ou brasil. + # 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 - # @option moeda [String] :fsingular fração no singular + # @param [Hash] moeda as opcoes para parametrizar a moeda/fracao + # @option moeda [Symbol] :lc locale do extenso - + # portugues de portugal (:pt) ou brasil (:br) + # @option moeda [String] :msingular moeda no singular - + # inferido do plural menos "S" + # @option moeda [String] :fsingular fracao no singular - + # inferido do plural menos "S" # @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' }) - # parametrização por defeito para :br - moeda = { lc: :br, msingular: 'REAL', mplural: 'REAIS', fsingular: 'CENTAVO' } if moeda[:lc] == :br && !moeda[:msingular] && !moeda[:mplural] + # @option moeda [String] :fplural fracao 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/fracao + # @option moeda [Symbol] :lc locale do extenso - + # portugues de portugal (:pt) ou brasil (:br) + # @option moeda [String] :msingular moeda no singular + # @option moeda [String] :fsingular fracao no singular + # @option moeda [String] :mplural moeda no plural - + # inferido do singular mais "S" + # @option moeda [String] :fplural fracao no plural - + # inferido do singular mais "S" + def self.iplural(moeda) # somente [:pt, :br] - @@lc = LC.include?(moeda[:lc]) ? moeda[:lc] : :pt + @lc = LC.include?(moeda[:lc]) ? moeda[:lc] : :pt + @mp = moeda[:mplural] || @ms + 'S' + @cp = moeda[:fplural] || @cs + 'S' + end + + # Produz extenso(s) de objeto(s) em portugues de portugal ou brasil. + # + # @param [Hash] moeda as opcoes para parametrizar a moeda/fracao + # @option moeda [Symbol] :lc locale do extenso - + # portugues de portugal (:pt) ou brasil (:br) + # @option moeda [String] :msingular moeda no singular + # @option moeda [String] :fsingular fracao no singular + # @option moeda [String] :mplural moeda no plural + # @option moeda [String] :fplural fracao no plural + # @return [String] extenso se objecto for (String, Float, Integer), + # Array<extensos> se objecto for (Array, Range), + # Hash<extensos> se objecto for (Hash) + def extenso(moeda = { lc: :pt, msingular: 'EURO', fsingular: 'CÊNTIMO' }) + # parametrizacao por defeito para :br + if moeda[:lc] == :br && !moeda[:msingular] && !moeda[:mplural] + moeda.merge!(msingular: 'REAL', mplural: 'REAIS', fsingular: 'CENTAVO') + end + # parametrizar moeda ExtensoPt.isingular(moeda) ExtensoPt.iplural(moeda) # 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 + +# permite obter um Hash com valores numericos convertidos nos seus extensos +class Hash + include ExtensoPt +end + +# permite obter um Array com valores numericos convertidos nos seus extensos +class Array + include ExtensoPt +end + +# permite obter um Array com valores numericos convertidos nos seus extensos +class Range + include ExtensoPt +end + +# permite obter o extenso dum Float +class Float + include ExtensoPt +end + +# permite obter o extenso dum Integer +class Integer + include ExtensoPt +end + +# permite obter o extenso duma String de digitos +class String + include ExtensoPt +end