# encoding: utf-8
module BoletoBancario
module Core
# Implementação de emissão de boleto bancário pelo Itaú.
#
# === Documentação Implementada
#
# A documentação na qual essa implementação foi baseada está localizada na pasta
# 'documentacoes_dos_boletos/itau' dentro dessa biblioteca.
#
# === Contrato das classes de emissão de boletos
#
# Para ver o "contrato" da Emissão de Boletos (geração de código de barras, linha digitável, etc) veja
# a classe BoletoBancario::Core::Boleto.
#
# === Carteiras suportadas
#
# Segue abaixo as carteiras suportadas do itáu seguindo a documentação:
#
# _______________________________________________________________________________________________________
# | Carteira | Descrição | Testada/Homologada no banco |
# | 107 | Sem registro com emissão integral – 15 posições | Esperando Contribuição |
# | 109 | Direta eletrônica sem emissão – simples | Esperando Contribuição |
# | 174 | Sem registro emissão parcial com protesto borderô | Esperando Contribuição |
# | 175 | Sem registro sem emissão com protesto eletrônico | Esperando Contribuição |
# | 196 | Sem registro com emissão e entrega – 15 posições | Esperando Contribuição |
# | 198 | Sem registro sem emissão 15 dígitos | Esperando Contribuição |
# | 126, 131, 146 | -------------------------------------------------- | Esperando Contribuição |
# | 122, 142, 143 | -------------------------------------------------- | Esperando Contribuição |
# | 150, 168 | -------------------------------------------------- | Esperando Contribuição |
# --------------------------------------------------------------------------------------------------------
#
# OBS.: Seja um contribuidor dessa gem. Contribua para homologar os boletos e as
# devidas carteiras junto ao banco Itaú.
#
# === Exemplos
#
# O recomendado é criar uma subclasse de BoletoBancario::Itau
#
# class BoletoItau < BoletoBancario::Itau
# end
#
# === Criando um boleto
#
# boleto_itau = BoletoItau.new do |boleto|
# boleto.conta_corrente = '89755'
# boleto.agencia = '0097'
# boleto.carteira = '198'
# boleto.numero_documento = '12345678'
# boleto.codigo_cedente = '909014'
# end
#
# === Validações
#
# A classe Itaú possui suas próprias validações.
# Primeiramente, antes de renderizar qualquer boleto você precisar verificar se esse o boleto é válido:
#
# if boleto_itau.valid?
# # render boleto_itau
# else
# # ...
# end
#
# === Campos do Boleto
#
# boleto_itau.codigo_de_barras
#
# boleto_itau.linha_digitavel
#
# boleto_itau.nosso_numero
#
# boleto_itau.agencia_codigo_cedente
#
# boleto_itau.carteira_formatada # Formata a carteira, para mostrar no boleto.
#
# boleto_itau.numero_documento
#
# boleto_itau.valor_documento
#
# === Sobrescrevendo validações
#
# Se você quiser sobrescrever alguma validação dessa classe a gem de boleto bancário
# possui alguns modos de fazer isso.
#
# Caso você precise mudar as validações, você pode sobrescrever alguns métodos que possuem "Magic numbers".
# Foi colocado dessa forma, já que os bancos mudam bastante esse tipo de validação.
# Por exemplo, atualmente a conta corrente é validado com '5' como máximo de tamanho.
# Caso você queira que valide como 6, mude conforme abaixo:
#
# class BoletoItau < BoletoBancario::Itau
# def self.tamanho_maximo_conta_corrente
# 6
# end
# end
#
# Ou você pode desativar as validações que são feitas, sobrescrevendo os métodos de validação:
#
# class BoletoItau < BoletoBancario::Itau
# def deve_validar_agencia?
# false
# end
#
# def deve_validar_conta_corrente?
# false
# end
#
# def deve_validar_codigo_cedente?
# false
# end
#
# def deve_validar_numero_documento?
# false
# end
#
# def deve_validar_carteira?
# false
# end
# end
#
# Obs.: Mudar as regras de validação podem influenciar na emissão do boleto em si.
# Talvez você precise analisar o efeito no #codigo_de_barras e na #linha_digitável (ambos podem ser
# sobreescritos também).
#
# Caso exista algum cenário de sobrescrita de validação contate o dono dessa gem pelo github e conte um
# pouco mais sobre esses cenários.
#
class Itau < Boleto
# Usado somente em carteiras especiais para complementar o número do documento.
#
attr_accessor :seu_numero
# Tamanho máximo de uma conta corrente no Banco Itaú.
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 5
#
def self.tamanho_maximo_conta_corrente
5
end
# Tamanho máximo de uma agência no Banco Itaú.
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 4
#
def self.tamanho_maximo_agencia
4
end
# Tamanho máximo do número do documento emitido no Boleto.
# O tamanho máximo é justamente 8 porque no código de barras só é permitido 8 posições para este campo.
#
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 8
#
def self.tamanho_maximo_numero_documento
8
end
# Tamanho máximo do código do cedente emitido no Boleto.
# O tamanho máximo é justamente 5 porque no código de barras só é permitido 5 posições para este campo.
#
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 5
#
def self.tamanho_maximo_codigo_cedente
5
end
# Tamanho máximo do seu número emitido no Boleto.
# O tamanho máximo é justamente 7 porque no código de barras só é permitido 7 posições para este campo.
#
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 7
#
def self.tamanho_maximo_seu_numero
7
end
# Tamanho máximo da carteira.
# O tamanho máximo é justamente 3 porque no código de barras só é permitido 3 posições para este campo.
#
# Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.
#
# @return [Fixnum] 3
#
def self.tamanho_maximo_carteira
3
end
# Campos obrigatórios
#
# * Agencia
# * Conta Corrente
# * Dígito da conta corrente
#
validates :agencia, :conta_corrente, :digito_conta_corrente, presence: true
validates :digito_conta_corrente, length: { maximum: 1 }
# Validações de tamanho para os campos abaixo:
#
# * Número do documento
# * Conta Corrente
# * Agencia
# * Carteira
#
# Se você quiser sobrescrever os tamanhos permitidos, ficará a sua responsabilidade.
# Basta você sobrescrever os métodos de validação:
#
# class BoletoItau < BoletoBancario::Core::Itau
# def self.tamanho_maximo_agencia
# 5
# end
#
# def self.tamanho_maximo_conta_corrente
# 6
# end
#
# def self.tamanho_maximo_numero_documento
# 9
# end
# end
#
# Obs.: Mudar as regras de validação podem influenciar na emissão do boleto em si.
# Talvez você precise analisar o efeito no #codigo_de_barras e na #linha_digitável (ambos podem ser
# sobreescritos também).
#
validates :numero_documento, length: { maximum: tamanho_maximo_numero_documento }, if: :deve_validar_numero_documento?
validates :conta_corrente, length: { maximum: tamanho_maximo_conta_corrente }, if: :deve_validar_conta_corrente?
validates :agencia, length: { maximum: tamanho_maximo_agencia }, if: :deve_validar_agencia?
validates :carteira, length: { maximum: tamanho_maximo_carteira }, if: :deve_validar_carteira?
# Campos obrigatórios e validações de tamanho para os campos:
#
# * Código do cedente
# * Seu número
#
# Obs.: Essas validações só ocorrerão se a carteira do boleto for especial.
# Para mais detalhes veja o método carteiras especiais dessa classe.
#
validates :codigo_cedente, :seu_numero, presence: true, :if => :carteira_especial?
validates :codigo_cedente, length: { maximum: tamanho_maximo_codigo_cedente }, :if => :deve_validar_codigo_cedente_carteira_especial?
validates :seu_numero, length: { maximum: tamanho_maximo_seu_numero }, :if => :deve_validar_seu_numero_carteira_especial?
# Nosso número é a identificação do título no banco.
# Eu diria que há uma diferença bem sutil entre esse campo e o seu número.
#
# @return [String] 8 caracteres.
#
def numero_documento
@numero_documento.to_s.rjust(8, '0') if @numero_documento.present?
end
# Seu número é a identificação do documento na empresa emitida.
# OBS.: Campo usado somente para as carteiras_especiais.
#
# @return [String] 7 caracteres.
#
def seu_numero
@seu_numero.to_s.rjust(7, '0') if @seu_numero.present?
end
# @return [String] 4 caracteres
#
def agencia
@agencia.to_s.rjust(4, '0') if @agencia.present?
end
# @return [String] 5 caracteres.
#
def conta_corrente
@conta_corrente.to_s.rjust(5, '0') if @conta_corrente.present?
end
# Código do Cedente é o código do cliente, fornecido pelo banco.
# OBS.: Campo usado somente para as carteiras_especiais.
#
# @return [String] 5 caracteres.
#
def codigo_cedente
@codigo_cedente.to_s.rjust(5, '0') if @codigo_cedente.present?
end
# @return [String] Código do Banco descrito na documentação (Pag. 49).
#
def codigo_banco
'341'
end
# Dígito do código do banco. Precisa mostrar esse dígito no boleto.
#
# @return [String] Dígito do código do banco descrito na documentação (Pag. 49).
#
def digito_codigo_banco
'7'
end
# Agência, conta corrente and dígito da conta corrente formatado.
#
# @return [String] Campo descrito na documentação (Pag. 50).
#
def agencia_codigo_cedente
"#{agencia} / #{conta_corrente}-#{digito_conta_corrente}"
end
# Nosso Número descrito na documentação (Pag. 53).
#
# @return [String] Carteira, número do documento e calculo do dígito do nosso número.
#
def nosso_numero
"#{carteira}/#{numero_documento}-#{digito_nosso_numero}"
end
# Para a grande maioria das carteiras, são considerados para a obtenção do dígito do nosso número,
# os dados “AGÊNCIA / CONTA (sem dígito) / CARTEIRA / NOSSO NÚMERO”,
# calculado pelo critério do Módulo 10 (veja a classe Modulo10 para mais detalhes).
#
# À exceção, estão as carteiras 126 - 131 - 146 - 150 e 168 cuja obtenção
# está baseada apenas nos dados “CARTEIRA/NOSSO NÚMERO” da operação.
#
# @return [String]
#
def digito_nosso_numero
if carteira.in?(carteiras_com_calculo_junto_com_numero_documento)
Modulo10.new("#{carteira}#{numero_documento}")
else
Modulo10.new("#{agencia}#{conta_corrente}#{carteira}#{numero_documento}")
end
end
# Carteiras que devem ser calculadas o módulo 10 usando carteira e número do documento.
# Você pode sobrescrever esse método na subclasse caso for necessário
# para incluir mais carteiras nesse cenário.
#
# @return [Array] Carteiras.
#
def carteiras_com_calculo_junto_com_numero_documento
%w(126 131 146 150 168)
end
# Formata a segunda posição do código de barras.
#
# A carteira de cobrança 198 é uma carteira especial, sem registro, na qual são utilizadas 15 posições
# numéricas para identificação do título liquidado (8 do Nosso Número e 7 do Seu Número).
# Nessa mesma situação estão as carteiras 107, 122, 142, 143 e 196.
#
# === Carteiras 107, 122, 142, 143, 196 e 198
#
# Para essas carteiras o formato do código de barras é o seguinte:
#
# ________________________________________________________________
# | Posição | Tamanho | Descrição |
# |----------|---------|------------------------------------------|
# | 20-22 | 03 | Carteira |
# | 23-30 | 08 | Nosso Número (Número do documento) |
# | 31-37 | 07 | Seu número |
# | 38-42 | 05 | Código do Cliente (fornecido pelo banco) |
# | 43 | 01 | DAC dos campos acima (posições 20 a 42) |
# | 44 | 01 | Zero |
# -----------------------------------------------------------------
#
# === Demais Carteiras
#
# Para as demais carteiras o formato do código de barras é o seguinte:
#
# _________________________________________________________________________________________
# | Posição | Tamanho | Descrição |
# |----------|---------|-------------------------------------------------------------------|
# | 20-22 | 03 | Carteira |
# | 23-30 | 08 | Nosso Número (Número do documento) |
# | 31 | 01 | DAC Modulo 10 (Agencia/Conta Corrente/Carteira/Nosso Número) |
# | 32-35 | 04 | Agência |
# | 36-40 | 05 | Número da conta corrente |
# | 41 | 01 | DAC Modulo 10 [Agência/Conta Corrente] (Digito da Conta Corrente) |
# | 42-44 | 03 | Zeros |
# ------------------------------------------------------------------------------------------
#
# Algumas observações sobre as demais carteiras:
#
# * O campo DAC na posição 31 se refere ao método #digito_nosso_numero pois calcula o
# dígito verificador baseado na carteira.
#
# * O campo DAC na posição 41 se refere ao método #digito_conta_corrente pois calcula o
# dígito verificador baseado na agencia + conta corrente.
#
# * A posição 41 se refere ao dígito verificador da conta corrente.
#
def codigo_de_barras_do_banco
if carteira.in?(carteiras_especiais)
codigo = "#{carteira}#{numero_documento}#{seu_numero}#{codigo_cedente}"
"#{codigo}#{Modulo10.new(codigo)}0"
else
"#{carteira}#{numero_documento}#{digito_nosso_numero}#{agencia}#{conta_corrente}#{digito_conta_corrente}000"
end
end
# As carteiras de cobrança 107, 122, 142, 143, 196 e 198 são carteiras especiais,
# na qual são utilizadas 15 posições numéricas para identificação do título
# liquidado (8 do Nosso Número e 7 do Seu Número).
#
# Você pode sobrescrever esse método na subclasse caso haja mais carteiras nessa posição descrita acima.
#
# @return [Array] Carteiras especiais que calculam o código barras usando 'Seu número' e 'Código do Cedente'.
#
def carteiras_especiais
%w(107 122 142 143 196 198)
end
# Verifica se a carteira é especial.
# Para mais detalhes veja o método #carteiras_especiais.
#
# @return [True, False]
#
def carteira_especial?
carteira.to_s.in?(carteiras_especiais)
end
# Verifica se deve validar o código do cedente e se a carteira é especial.
#
# Métodos usado para verificar se deve realizar a validação do campo 'codigo_cedente'.
# Sobrescreva esse método na subclasse, caso você mesmo queira fazer as validações.
#
# @return [True]
#
def deve_validar_codigo_cedente_carteira_especial?
deve_validar_codigo_cedente? and carteira_especial?
end
# Verifica se deve validar o seu número e se a carteira é especial.
#
# Métodos usado para verificar se deve realizar a validação do campo 'codigo_cedente'.
# Sobrescreva esse método na subclasse, caso você mesmo queira fazer as validações.
#
# @return [True]
#
def deve_validar_seu_numero_carteira_especial?
deve_validar_seu_numero? and carteira_especial?
end
# Verifica se deve validar o seu número. O padrão é validar o seu número.
#
# Métodos usado para verificar se deve realizar a validação do campo 'seu_numero'.
# Sobrescreva esse método na subclasse, caso você mesmo queira fazer as validações.
#
# @return [True] true
#
def deve_validar_seu_numero?
true
end
end
end
end