# -*- 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