# -*- encoding: utf-8 -*-
# @author Kivanio Barbosa
module Brcobranca
module Boleto
# Classe base para todas as classes de boletos
class Base
extend Template::Base
# Configura gerador de arquivo de boleto e código de barras.
extend define_template(Brcobranca.configuration.gerador)
include define_template(Brcobranca.configuration.gerador)
# Validações do Rails 3
include ActiveModel::Validations
# REQUERIDO: Número do convênio/contrato do cliente junto ao banco emissor
attr_accessor :convenio
# REQUERIDO: Tipo de moeda utilizada (Real(R$) e igual a 9)
attr_accessor :moeda
# REQUERIDO: Carteira utilizada
attr_accessor :carteira
# OPCIONAL: Variacao da carteira(opcional para a maioria dos bancos)
attr_accessor :variacao
# OPCIONAL: Data de processamento do boleto, geralmente igual a data_documento
attr_accessor :data_processamento
# REQUERIDO: Número de dias a vencer
attr_accessor :dias_vencimento
# REQUERIDO: Quantidade de boleto(padrão = 1)
attr_accessor :quantidade
# REQUERIDO: Valor do boleto
attr_accessor :valor
# REQUERIDO: Número da agencia sem Digito Verificador
attr_accessor :agencia
# REQUERIDO: Número da conta corrente sem Digito Verificador
attr_accessor :conta_corrente
# REQUERIDO: Nome do proprietario da conta corrente
attr_accessor :cedente
# REQUERIDO: Documento do proprietario da conta corrente (CPF ou CNPJ)
attr_accessor :documento_cedente
# OPCIONAL: Número sequencial utilizado para identificar o boleto
attr_accessor :numero_documento
# REQUERIDO: Símbolo da moeda utilizada (R$ no brasil)
attr_accessor :especie
# REQUERIDO: Tipo do documento (Geralmente DM que quer dizer Duplicata Mercantil)
attr_accessor :especie_documento
# REQUERIDO: Data em que foi emitido o boleto
attr_accessor :data_documento
# OPCIONAL: Código utilizado para identificar o tipo de serviço cobrado
attr_accessor :codigo_servico
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao1
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao2
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao3
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao4
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao5
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao6
# OPCIONAL: Utilizado para mostrar alguma informação ao sacado
attr_accessor :instrucao7
# REQUERIDO: Informação sobre onde o sacado podera efetuar o pagamento
attr_accessor :local_pagamento
# REQUERIDO: Informa se o banco deve aceitar o boleto após o vencimento ou não( S ou N, quase sempre S)
attr_accessor :aceite
# REQUERIDO: Nome da pessoa que receberá o boleto
attr_accessor :sacado
# OPCIONAL: Endereco da pessoa que receberá o boleto
attr_accessor :sacado_endereco
# REQUERIDO: Documento da pessoa que receberá o boleto
attr_accessor :sacado_documento
# OPCIONAL: Nome do avalista
attr_accessor :avalista
# OPCIONAL: Documento do avalista
attr_accessor :avalista_documento
# OPCIONAL: Endereço da pessoa que envia o boleto
attr_accessor :cedente_endereco
# Validações
validates_presence_of :agencia, :conta_corrente, :moeda, :especie_documento, :especie, :aceite, :numero_documento, message: 'não pode estar em branco.'
validates_numericality_of :convenio, :agencia, :conta_corrente, :numero_documento, message: 'não é um número.', allow_nil: true
# Nova instancia da classe Base
# @param [Hash] campos
def initialize(campos = {})
padrao = {
moeda: '9', data_documento: Date.today, dias_vencimento: 1, quantidade: 1,
especie_documento: 'DM', especie: 'R$', aceite: 'S', valor: 0.0,
local_pagamento: 'QUALQUER BANCO ATÉ O VENCIMENTO'
}
campos = padrao.merge!(campos)
campos.each do |campo, valor|
send "#{campo}=", valor
end
yield self if block_given?
end
# Logotipo do banco
# @return [Path] Caminho para o arquivo de logotipo do banco.
def logotipo
File.join(File.dirname(__FILE__), '..', 'arquivos', 'logos', "#{class_name}.eps")
end
# Dígito verificador do banco
# @return [Integer] 1 caracteres numéricos.
def banco_dv
banco.modulo11
end
# Código da agencia
# @return [String] 4 caracteres numéricos.
def agencia=(valor)
@agencia = valor.to_s.rjust(4, '0') if valor
end
# Dígito verificador da agência
# @return [Integer] 1 caracteres numéricos.
def agencia_dv
agencia.modulo11
end
# Dígito verificador da conta corrente
# @return [Integer] 1 caracteres numéricos.
def conta_corrente_dv
conta_corrente.modulo11
end
# Dígito verificador do nosso número
# @return [Integer] 1 caracteres numéricos.
def nosso_numero_dv
numero_documento.modulo11
end
# @abstract Deverá ser sobreescrito para cada banco.
def nosso_numero_boleto
fail Brcobranca::NaoImplementado.new('Sobreescreva este método na classe referente ao banco que você esta criando')
end
# @abstract Deverá ser sobreescrito para cada banco.
def agencia_conta_boleto
fail Brcobranca::NaoImplementado.new('Sobreescreva este método na classe referente ao banco que você esta criando')
end
# Valor total do documento: quantidate * valor
# @return [Float]
def valor_documento
quantidade.to_f * valor.to_f
end
# Data de vencimento baseado na data_documento + dias_vencimento
#
# @return [Date]
# @raise [ArgumentError] Caso {#data_documento} esteja em branco.
def data_vencimento
fail ArgumentError, 'Data Documento não pode estar em branco.' unless data_documento.present?
return data_documento unless dias_vencimento
self.data_documento = Date.parse(data_documento) if data_documento.is_a?(String)
(data_documento + dias_vencimento.to_i)
end
# Fator de vencimento calculado com base na data de vencimento do boleto.
# @return [String] 4 caracteres numéricos.
def fator_vencimento
data_vencimento.fator_vencimento
end
# Número da conta corrente
# @return [String] 7 caracteres numéricos.
def conta_corrente=(valor)
@conta_corrente = valor.to_s.rjust(7, '0') if valor
end
# Codigo de barras do boleto
#
# O codigo de barra para cobrança contém 44 posições dispostas da seguinte forma:
# Posição |Tamanho |Conteúdo
# 01 a 03 | 3 | Identificação do Banco
# 04 a 04 | 1 | Código da Moeda (Real = 9, Outras=0)
# 05 a 05 | 1 | Dígito verificador do Código de Barras
# 06 a 09 | 4 | Fator de Vencimento (Vide Nota)
# 10 a 19 | 10 | Valor
# 20 a 44 | 25 | Campo Livre - As posições do campo livre ficam a critério de cada Banco arrecadador.
#
# @raise [Brcobranca::BoletoInvalido] Caso as informações fornecidas não sejam suficientes ou sejam inválidas.
# @return [String] código de barras formado por 44 caracteres numéricos.
def codigo_barras
fail Brcobranca::BoletoInvalido.new(self) unless self.valid?
codigo = codigo_barras_primeira_parte # 18 digitos
codigo << codigo_barras_segunda_parte # 25 digitos
if codigo =~ /^(\d{4})(\d{39})$/
codigo_dv = codigo.modulo11(
multiplicador: (2..9).to_a,
mapeamento: { 0 => 1, 10 => 1, 11 => 1 }
) { |t| 11 - (t % 11) }
codigo = "#{Regexp.last_match[1]}#{codigo_dv}#{Regexp.last_match[2]}"
codigo
else
fail Brcobranca::BoletoInvalido.new(self)
end
end
# Monta a segunda parte do código de barras, que é específico para cada banco.
#
# @abstract Deverá ser sobreescrito para cada banco.
def codigo_barras_segunda_parte
fail Brcobranca::NaoImplementado.new('Sobreescreva este método na classe referente ao banco que você esta criando')
end
private
# Monta a primeira parte do código de barras, que é a mesma para todos bancos.
# @return [String] 18 caracteres numéricos.
def codigo_barras_primeira_parte
"#{banco}#{moeda}#{fator_vencimento}#{valor_documento_formatado}"
end
# Valor total do documento
# @return [String] 10 caracteres numéricos.
def valor_documento_formatado
valor_documento.round(2).limpa_valor_moeda.to_s.rjust(10, '0')
end
# Nome da classe do boleto
# @return [String]
def class_name
self.class.to_s.split('::').last.downcase
end
end
end
end