You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
7.0 KiB
244 lines
7.0 KiB
class Dinheiro
|
|
|
|
include Comparable
|
|
|
|
attr_reader :quantia
|
|
|
|
FORMATO_VALIDO_BR = /^([R|r]\$\s*)?(([+-]?\d{1,3}(\.?\d{3})*))?(\,\d{0,2})?$/
|
|
FORMATO_VALIDO_EUA = /^([R|r]\$\s*)?(([+-]?\d{1,3}(\,?\d{3})*))?(\.\d{0,2})?$/
|
|
SEPARADOR_MILHAR = "."
|
|
SEPARADOR_FRACIONARIO = ","
|
|
QUANTIDADE_DIGITOS = 3
|
|
PRECISAO_DECIMAL = 100
|
|
|
|
def initialize(quantia)
|
|
self.quantia = quantia
|
|
end
|
|
|
|
# Retorna o valor armazenado em string.
|
|
#
|
|
# Exemplo:
|
|
# 1000.to_s ==> '1.000,00'
|
|
def to_s
|
|
inteiro_com_milhar(parte_inteira) + parte_decimal
|
|
end
|
|
|
|
# Compara com outro dinheiro se eh igual.
|
|
#
|
|
# Exemplo:
|
|
# um_real = Dinheiro.new(1)
|
|
# um_real == Dinheiro.new(1) ==> true
|
|
# um_real == Dinheiro.new(2) ==> false
|
|
def ==(outro_dinheiro)
|
|
outro_dinheiro = Dinheiro.new(outro_dinheiro) unless outro_dinheiro.kind_of?(Dinheiro)
|
|
@quantia == outro_dinheiro.quantia
|
|
end
|
|
|
|
# Compara com outro dinheiro se eh maior ou menor.
|
|
#
|
|
# Exemplo:
|
|
# 1.real < 2.reais ==> true
|
|
# 1.real > 2.reais ==> false
|
|
# 2.real < 1.reais ==> false
|
|
# 2.real > 1.reais ==> true
|
|
def <=>(outro_dinheiro)
|
|
outro_dinheiro = Dinheiro.new(outro_dinheiro) unless outro_dinheiro.kind_of?(Dinheiro)
|
|
@quantia <=> outro_dinheiro.quantia
|
|
end
|
|
|
|
# Retorna a adicao entre dinheiros.
|
|
def +(outro)
|
|
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia + quantia_de(outro)))
|
|
end
|
|
|
|
# Retorna a subtracao entre dinheiros.
|
|
def -(outro)
|
|
Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia - quantia_de(outro)))
|
|
end
|
|
|
|
# Retorna a multiplicacao entre dinheiros.
|
|
def *(outro)
|
|
return Dinheiro.new(to_f * outro) unless outro.kind_of? Dinheiro
|
|
outro * to_f
|
|
end
|
|
|
|
# Retorna a divisao entre dinheiros.
|
|
def /(outro)
|
|
raise DivisaPorNaoEscalarError unless outro.kind_of?(Numeric)
|
|
return @quantia/outro if outro == 0
|
|
soma_parcial = Dinheiro.new(0)
|
|
parcelas = []
|
|
(outro-1).times do
|
|
parcela = Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia/outro))
|
|
parcelas << parcela
|
|
soma_parcial += parcela
|
|
end
|
|
parcelas << Dinheiro.new(transforma_em_string_que_represente_a_quantia(@quantia - quantia_de(soma_parcial)))
|
|
end
|
|
|
|
# Escreve o valor por extenso.
|
|
#
|
|
# Exemplo:
|
|
# 1.real.por_extenso ==> 'um real'
|
|
# (100.58).por_extenso ==> 'cem reais e cinquenta e oito centavos'
|
|
def por_extenso
|
|
(@quantia/100.0).por_extenso_em_reais
|
|
end
|
|
|
|
# Alias para o metodo por_extenso.
|
|
alias_method :por_extenso_em_reais, :por_extenso
|
|
|
|
# DEPRECATION WARNING: use por_extenso ou por_extenso_em_reais, pois este sera removido no proximo release.
|
|
def to_extenso
|
|
warn("DEPRECATION WARNING: use por_extenso ou por_extenso_em_reais, pois este sera removido no proximo release.")
|
|
self.por_extenso
|
|
end
|
|
|
|
# Retorna um Float.
|
|
def to_f
|
|
to_s.gsub(',', '.').to_f
|
|
end
|
|
|
|
def coerce(outro)
|
|
[ Dinheiro.new(outro), self ]
|
|
end
|
|
|
|
# Retorna uma string formatada em valor monetario.
|
|
#
|
|
# Exemplo:
|
|
# Dinheiro.new(1).real ==> 'R$ 1,00'
|
|
# Dinheiro.new(-1).real ==> 'R$ -1,00'
|
|
def real
|
|
"R$ " + to_s
|
|
end
|
|
|
|
# Retorna uma string formatada em valor monetario.
|
|
#
|
|
# Exemplo:
|
|
# Dinheiro.new(1).real ==> 'R$ 1,00'
|
|
# Dinheiro.new(-1).real ==> 'R$ (1,00)'
|
|
def real_contabil
|
|
"R$ " + contabil
|
|
end
|
|
|
|
# Alias para real.
|
|
alias_method :reais, :real
|
|
|
|
# Alias para real_contabil.
|
|
alias_method :reais_contabeis, :real_contabil
|
|
|
|
# Retorna uma string formatada.
|
|
#
|
|
# Exemplo:
|
|
# Dinheiro.new(1).contabil ==> '1,00'
|
|
# Dinheiro.new(-1).contabil ==> '(1,00)'
|
|
def contabil
|
|
if @quantia >= 0
|
|
to_s
|
|
else
|
|
"(" + to_s[1..-1] + ")"
|
|
end
|
|
end
|
|
|
|
# Retorna um BigDecinal.
|
|
def valor_decimal
|
|
BigDecimal.new quantia_sem_separacao_milhares.gsub(',','.')
|
|
end
|
|
|
|
private
|
|
def quantia_de(outro)
|
|
outro = outro.to_f if outro.kind_of?(BigDecimal)
|
|
return outro.quantia if outro.kind_of?(Dinheiro)
|
|
(outro * 100).round
|
|
end
|
|
|
|
def transforma_em_string_que_represente_a_quantia(quantia)
|
|
if /^([+-]?)(\d)$/ =~ quantia.to_s
|
|
return "#{$1}0.0#{$2}"
|
|
end
|
|
/^([+-]?)(\d*)(\d\d)$/ =~ quantia.to_s
|
|
"#{$1}#{$2.to_i}.#{$3}"
|
|
end
|
|
|
|
def quantia=(quantia)
|
|
raise DinheiroInvalidoError unless quantia_valida?(quantia)
|
|
quantia = quantia.to_f if quantia.kind_of?(BigDecimal)
|
|
@quantia = (quantia * 100).round if quantia.kind_of?(Numeric)
|
|
@quantia = extrai_quantia_como_inteiro(quantia) if quantia.kind_of?(String)
|
|
end
|
|
|
|
def parte_inteira
|
|
quantia_sem_separacao_milhares[0,quantia_sem_separacao_milhares.length-QUANTIDADE_DIGITOS]
|
|
end
|
|
|
|
def parte_decimal
|
|
quantia_sem_separacao_milhares[-QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS]
|
|
end
|
|
|
|
def inteiro_com_milhar(inteiro)
|
|
return inteiro if quantidade_de_passos(inteiro) == 0
|
|
resultado = ""
|
|
quantidade_de_passos(inteiro).times do |passo|
|
|
resultado = "." + inteiro[-QUANTIDADE_DIGITOS + passo * -QUANTIDADE_DIGITOS, QUANTIDADE_DIGITOS] + resultado
|
|
end
|
|
resultado = inteiro[0, digitos_que_sobraram(inteiro)] + resultado
|
|
resultado.gsub(/^(-?)\./, '\1')
|
|
end
|
|
|
|
def quantia_sem_separacao_milhares
|
|
sprintf("%.2f", (@quantia.to_f / PRECISAO_DECIMAL)).gsub(SEPARADOR_MILHAR, SEPARADOR_FRACIONARIO)
|
|
end
|
|
|
|
def quantidade_de_passos(inteiro)
|
|
resultado = inteiro.length / QUANTIDADE_DIGITOS
|
|
resultado = (resultado - 1) if inteiro.length % QUANTIDADE_DIGITOS == 0
|
|
resultado
|
|
end
|
|
|
|
def digitos_que_sobraram(inteiro)
|
|
inteiro.length - (quantidade_de_passos(inteiro) * QUANTIDADE_DIGITOS)
|
|
end
|
|
|
|
def quantia_valida?(quantia)
|
|
return false if quantia.kind_of?(String) && !quantia_respeita_formato?(quantia)
|
|
quantia.kind_of?(String) || quantia.kind_of?(Numeric)
|
|
end
|
|
|
|
def extrai_quantia_como_inteiro(quantia)
|
|
if FORMATO_VALIDO_BR =~ quantia
|
|
return sem_milhar($2, $5, '.')
|
|
end
|
|
if FORMATO_VALIDO_EUA =~ quantia
|
|
return sem_milhar($2, $5, ',')
|
|
end
|
|
end
|
|
|
|
def sem_milhar(parte_inteira, parte_decimal, delimitador_de_milhar)
|
|
(inteiro(parte_inteira, delimitador_de_milhar) + decimal(parte_decimal)).to_i
|
|
end
|
|
|
|
def inteiro(inteiro_com_separador_milhar, separador)
|
|
return inteiro_com_separador_milhar.gsub(separador, '') unless inteiro_com_separador_milhar.blank?
|
|
""
|
|
end
|
|
|
|
def decimal(parte_fracionaria)
|
|
unless parte_fracionaria.blank?
|
|
return sem_delimitador_decimal(parte_fracionaria) if parte_fracionaria.length == 3
|
|
return sem_delimitador_decimal(parte_fracionaria) + "0" if parte_fracionaria.length == 2
|
|
end
|
|
"00"
|
|
end
|
|
|
|
def sem_delimitador_decimal(parte_fracionaria)
|
|
"#{parte_fracionaria}".gsub(/[.|,]/, '')
|
|
end
|
|
|
|
|
|
def quantia_respeita_formato?(quantia)
|
|
return true if FORMATO_VALIDO_BR.match(quantia) || FORMATO_VALIDO_EUA.match(quantia)
|
|
false
|
|
end
|
|
|
|
end
|