br_boleto 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +15 -0
  3. data/Gemfile.lock +145 -0
  4. data/History.txt +4 -0
  5. data/LICENSE +20 -0
  6. data/README.markdown +156 -0
  7. data/Rakefile +8 -0
  8. data/br_boleto.gemspec +26 -0
  9. data/lib/br_boleto.rb +87 -0
  10. data/lib/br_boleto/calculos/digitos.rb +35 -0
  11. data/lib/br_boleto/calculos/fator_vencimento.rb +129 -0
  12. data/lib/br_boleto/calculos/fatores_de_multiplicacao.rb +67 -0
  13. data/lib/br_boleto/calculos/linha_digitavel.rb +158 -0
  14. data/lib/br_boleto/calculos/modulo10.rb +83 -0
  15. data/lib/br_boleto/calculos/modulo11.rb +54 -0
  16. data/lib/br_boleto/calculos/modulo11_fator3197.rb +88 -0
  17. data/lib/br_boleto/calculos/modulo11_fator_de2a7.rb +97 -0
  18. data/lib/br_boleto/calculos/modulo11_fator_de2a9.rb +83 -0
  19. data/lib/br_boleto/calculos/modulo11_fator_de2a9_resto_zero.rb +29 -0
  20. data/lib/br_boleto/calculos/modulo11_fator_de9a2.rb +65 -0
  21. data/lib/br_boleto/calculos/modulo11_fator_de9a2_resto_x.rb +55 -0
  22. data/lib/br_boleto/calculos/modulo_numero_de_controle.rb +117 -0
  23. data/lib/br_boleto/core/boleto.rb +558 -0
  24. data/lib/br_boleto/core/sicoob.rb +169 -0
  25. data/lib/br_boleto/version.rb +8 -0
  26. data/test/br_boleto/calculos/digitos_test.rb +15 -0
  27. data/test/br_boleto/calculos/fator_vencimento_test.rb +56 -0
  28. data/test/br_boleto/calculos/fatores_de_multiplicacao_test.rb +66 -0
  29. data/test/br_boleto/calculos/linha_digitavel_test.rb +58 -0
  30. data/test/br_boleto/calculos/modulo10_test.rb +54 -0
  31. data/test/br_boleto/calculos/modulo11_fator3197_test.rb +43 -0
  32. data/test/br_boleto/calculos/modulo11_fator_de2a7_test.rb +44 -0
  33. data/test/br_boleto/calculos/modulo11_fator_de2a9_resto_zero_test.rb +40 -0
  34. data/test/br_boleto/calculos/modulo11_fator_de2a9_test.rb +68 -0
  35. data/test/br_boleto/calculos/modulo11_fator_de9a2_resto_x_test.rb +38 -0
  36. data/test/br_boleto/calculos/modulo11_fator_de9a2_test.rb +32 -0
  37. data/test/br_boleto/calculos/modulo11_test.rb +28 -0
  38. data/test/br_boleto/calculos/modulo_numero_de_controle_test.rb +38 -0
  39. data/test/br_boleto/core/boleto_test.rb +221 -0
  40. data/test/br_boleto/core/sicoob_test.rb +138 -0
  41. data/test/factories/boleto.rb +22 -0
  42. data/test/factories/boleto_sicoob.rb +23 -0
  43. data/test/inheritance/boleto_test.rb +15 -0
  44. data/test/inheritance/sicoob_test.rb +25 -0
  45. data/test/test_helper.rb +37 -0
  46. metadata +151 -0
@@ -0,0 +1,35 @@
1
+ # encoding: utf-8
2
+ require 'active_support/core_ext/enumerable'
3
+
4
+ module BrBoleto
5
+ module Calculos
6
+ # Classe responsável por lidar com os dígitos dos módulos.
7
+ #
8
+ class Digitos
9
+ # @param [Integer] number Número que servirá para os cálculo com os dígitos desse número.
10
+ #
11
+ def initialize(number)
12
+ @number = number
13
+ end
14
+
15
+ # Soma cada dígito do número passado no #initialize.
16
+ # Alguns bancos requerem esse tipo estranho de cálculo em alguns módulos.
17
+ # @return [Fixnum] Resultado da soma de cada dígito.
18
+ #
19
+ # @example
20
+ #
21
+ # Digitos.new(12).sum
22
+ # # => 3
23
+ #
24
+ # Digitos.new(2244).sum
25
+ # # => 12
26
+ #
27
+ # Digitos.new(90123451).sum
28
+ # # => 25
29
+ #
30
+ def sum
31
+ @number.to_s.split('').collect { |number| number.to_i }.sum
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,129 @@
1
+ # encoding: utf-8
2
+ module BrBoleto
3
+ module Calculos
4
+ # Classe responsável pelo cálculo de Fator de Vencimento do boleto bancário.
5
+ #
6
+ # === Descricão
7
+ #
8
+ # Conforme Carta-circular 002926 do Banco Central do Brasil, de 24/07/2000, recomenda-se a indicação do Fator de Vencimento no Código de Barras.
9
+ # A partir de 02/04/2001, o Banco acolhedor/recebedor não será mais responsável por eventuais diferenças de recebimento de BOLETOs fora do prazo,
10
+ # ou sem a indicação do fator de vencimento.
11
+ #
12
+ # === Forma para obtenção do Fator de Vencimento
13
+ #
14
+ # Calcula-se <b>o número de dias corridos</b> entre a data base (<b>“Fixada” em 07/10/1997</b>) e a do vencimento desejado:
15
+ # Alguns bancos dizem para Fazer o seguinte Cálculo: ((data de vencimento) - (03/07/2000) + 1000)
16
+ # Porém esse cálculo é a mesma coisa que fazer: ((data de vencimento) - (07/10/1997))
17
+ #
18
+ # Vencimento desejado: 04/07/2000
19
+ # Data base : - 07/10/1997
20
+ # # => 1001
21
+ #
22
+ # === Atenção
23
+ #
24
+ # Caso ocorra divergência entre a data impressa no campo “data de vencimento” e a constante no código de barras,
25
+ # o recebimento se dará da seguinte forma:
26
+ #
27
+ # * Quando pago por sistemas eletrônicos (Home-Banking, Auto-Atendimento, Internet, SISPAG, telefone, etc.), prevalecerá à representada no “código de barras”;
28
+ # * Quando quitado na rede de agências, diretamente no caixa, será considerada a data impressa no campo “vencimento” do BOLETO.
29
+ #
30
+ # @return [String] retorna o resultado do cálculo. <b>Deve conter 4 dígitos</b>.
31
+ #
32
+ # @example
33
+ #
34
+ # FatorVencimento.new(Date.parse("2012-12-02"))
35
+ # #=> "5535"
36
+ #
37
+ # FatorVencimento.new(Date.parse("1997-10-08"))
38
+ # #=> "0001"
39
+ #
40
+ # FatorVencimento.new(Date.parse("2012-12-16"))
41
+ # #=> "5549"
42
+ #
43
+ # FatorVencimento.new(Date.parse("2014-12-15"))
44
+ # #=> "6278"
45
+ #
46
+
47
+
48
+ class FatorVencimento < String
49
+
50
+ # A base é des de quando será subtraido a data de validade
51
+ attr_accessor :base_date
52
+ def base_date
53
+ @base_date || Date.new(1997, 10, 7)
54
+ end
55
+
56
+ # @param [Date] expiration_date
57
+ # @param [Date] base_date
58
+ # @return [String] retorna o resultado do cálculo. <b>Deve conter 4 dígitos</b>.
59
+ # @example
60
+ # FatorVencimento.new(Date.parse("2012-09-02"))
61
+ # #=> "5444"
62
+ #
63
+ # FatorVencimento.new(Date.parse("1999-10-01"))
64
+ # #=> "0724"
65
+ #
66
+ # FatorVencimento.new(Date.parse("2022-12-16"))
67
+ # #=> "9201"
68
+ #
69
+
70
+ #########################################################
71
+ ############ OPÇÃO PARA CUSTOMIZAÇÃO DO CÁLCULO #########
72
+ ############# MODIFIQUE APENAS SE TIVER CERTEZA #########
73
+ #########################################################
74
+ # Caso algum dia mude a forma do cálculo da data base, os valores do cálculo
75
+ # poderão ser passados por parâmetro. Dia 22/02/2025 a diferença terá 5 números
76
+ # Mas até lá não se sabe o que irá mudar
77
+ attr_accessor :subtracao
78
+ # Caso necessite subtratir o resultado entre a diferença do (vencimento - data_base)
79
+ def subtracao
80
+ @subtracao || 0
81
+ end
82
+
83
+ # Caso necessite somar o resultado entre a diferença do (vencimento - data_base)
84
+ attr_accessor :soma
85
+ def soma
86
+ @soma || 0
87
+ end
88
+
89
+ # Caso necessite modificar o número de caracteres de retorno
90
+ attr_accessor :quantidade_de_caracteres
91
+ def quantidade_de_caracteres
92
+ @quantidade_de_caracteres || 4
93
+ end
94
+ #########################################################
95
+
96
+ def initialize(expiration_date, options={})
97
+ @expiration_date = expiration_date
98
+ self.base_date = options[:base_date]
99
+ self.subtracao = options[:subtracao]
100
+ self.soma = options[:soma]
101
+ self.quantidade_de_caracteres = options[:quantidade_de_caracteres]
102
+
103
+ if @expiration_date.present?
104
+ super(calculate)
105
+ end
106
+ end
107
+
108
+
109
+
110
+ # Cálculo da data de vencimento com a data base.
111
+ #
112
+ # @return [String] exatamente 4 dígitos
113
+ #
114
+ def calculate
115
+ expiration_date_minus_base_date.to_s.rjust(quantidade_de_caracteres, '0')
116
+ end
117
+
118
+ # @api private
119
+ #
120
+ # Cálculo da data de vencimento com a data base.
121
+ # Chamando #to_i para não retornar um Float.
122
+ # @return [Integer] diff between this two dates.
123
+ #
124
+ def expiration_date_minus_base_date
125
+ ((@expiration_date - base_date).to_i+soma)-subtracao
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: utf-8
2
+ module BrBoleto
3
+ module Calculos
4
+ # Classe responsável por multiplicar cada dígito pelos fatores de multiplicação passado como argumento.
5
+ #
6
+ # Imagine que temos o número <b>'2468'</b> e os fatores de multiplicação <b>[2, 1]</b>.
7
+ #
8
+ # Será calculado da seguinte maneira:
9
+ #
10
+ # 2 4 6 8
11
+ # * * * * ===> Multiplicação
12
+ # 1 2 1 2
13
+ #
14
+ # #=> [2, 8, 6, 16]
15
+ #
16
+ # Você pode passar outros fatores de multiplicação se você precisar.
17
+ # Por exemplo, dado o número '1234567890' e os fatores de multiplicação: <b>[2, 3, 4, 5, 6, 7, 8, 9]</b>.
18
+ # Será calculado da seguinte maneira:
19
+ #
20
+ # 1 2 3 4 5 6 7 8 9 0
21
+ # * * * * * * * * * *
22
+ # 3 2 9 8 7 6 5 4 3 2
23
+ #
24
+ # #=> [3, 4, 27, 32, 35, 36, 35, 32, 27, 0]
25
+ #
26
+ # @param [String] O número que será usado para multiplicar cada dígito.
27
+ # @param [Hash] Os fatores de multiplicação que irão ser calculados na ordem reversa.
28
+ #
29
+ # @example Calculo
30
+ #
31
+ # BrBoleto::Calculos::FatoresDeMultiplicacao.new(123, fatores: [2, 1])
32
+ # # => [1, 2, 6]
33
+ #
34
+ # BrBoleto::Calculos::FatoresDeMultiplicacao.new(123, fatores: [2, 3, 4, 5, 6, 7, 8, 9])
35
+ # # => [4, 6, 6]
36
+ #
37
+ # BrBoleto::Calculos::FatoresDeMultiplicacao.new(809070608090, fatores: [9, 8, 7, 6, 5, 4, 3, 2])
38
+ # # => [48, 0, 72, 0, 14, 0, 24, 0, 48, 0, 72, 0]
39
+ #
40
+ class FatoresDeMultiplicacao < Array
41
+ # @param [String || Integer] number
42
+ # @param [Hash] options
43
+ # @option options [Array] :fatores
44
+ # @return [Array]
45
+ # @example
46
+ #
47
+ # FatoresDeMultiplicacao.new(12, fatores: [2, 1])
48
+ # # => [1, 4]
49
+ #
50
+ # FatoresDeMultiplicacao.new(1864, fatores: [2, 3, 4, 5, 6, 7, 8, 9])
51
+ # # => [5, 32, 18, 8]
52
+ #
53
+ def initialize(number, options)
54
+ @number = number.to_s.reverse.split('')
55
+ @factors = options.fetch(:fatores).cycle.take(@number.size)
56
+ super(calculate)
57
+ end
58
+
59
+ # Para cada número realiza a multiplicação para cada dígito.
60
+ # @return [Array]
61
+ #
62
+ def calculate
63
+ @number.collect.each_with_index { |n, index| n.to_i * @factors[index] }.reverse
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,158 @@
1
+ # encoding: utf-8
2
+ require 'active_support/core_ext/object'
3
+
4
+ module BrBoleto
5
+ module Calculos
6
+ # Representação numérica do código de barras, mais conhecida como linha digitável! :p
7
+ #
8
+ # A representação numérica do código de barras é composta, por cinco campos.
9
+ # Sendo os três primeiros campos, amarrados por DAC's (dígitos verificadores),
10
+ # todos calculados pelo módulo 10.
11
+ #
12
+ # <b>OBS.:</b> Para mais detalhes deste cálculo, veja a descrição em Modulo10.
13
+ #
14
+ # === Linha Digitável
15
+ #
16
+ # A linha digitável contêm exatamente 47 posições nessa sequência:
17
+ #
18
+ # _____________________________________________________________________________________________________________
19
+ # |Campo | Posição | Tamanho | Descrição |
20
+ # |------|----------|---------|--------------------------------------------------------------------------------|
21
+ # | 1º | 01-03 | 03 | Código do banco (posições 1 a 3 do código de barras) |
22
+ # | | 04 | 01 | Código da moeda (posição 4 do código de barras) |
23
+ # | | 05-09 | 5 | Cinco primeiras posições do campo livre (posições 20 a 24 do código de barras) |
24
+ # | | 10 | 1 | Dígito verificador do primeiro campo (Módulo10) |
25
+ # |------------------------------------------------------------------------------------------------------------|
26
+ # | 2º | 11-20 | 10 | 6º a 15º posições do campo livre (posições 25 a 34 do código de barras) |
27
+ # | | 21 | 01 | Dígito verificador do segundo campo (Módulo10) |
28
+ # |------------------------------------------------------------------------------------------------------------|
29
+ # | 3º | 22-31 | 10 | 16º a 25º posições do campo livre (posições 35 a 44 do código de barras) |
30
+ # | | 32 | 01 | Dígito verificador do terceiro campo (Módulo10) |
31
+ # |------------------------------------------------------------------------------------------------------------|
32
+ # | 4º | 33 | 01 | Dígito verificador do código de barras (posição 5 do código de barras) |
33
+ # |------------------------------------------------------------------------------------------------------------|
34
+ # | 5ª | 34-37 | 04 | Fator de vencimento (posições 6 a 9 do código de barras) |
35
+ # | | 38-47 | 10 | Valor nominal do documento (posições 10 a 19 do código de barras) |
36
+ # -------------------------------------------------------------------------------------------------------------|
37
+ #
38
+ # @return [String] Contêm a representação numérica do código de barras formatado com pontos e espaços.
39
+ #
40
+ # @example
41
+ #
42
+ # LinhaDigitavel.new('34196166700000123451091234567880057123457000')
43
+ # # => '34191.09123 34567.880058 71234.570001 6 16670000012345'
44
+ #
45
+ # LinhaDigitavel.new('99991101200000350007772130530150081897500000')
46
+ # # => '99997.77213 30530.150082 18975.000003 1 10120000035000'
47
+ #
48
+ # LinhaDigitavel.new('39998100100000311551111122222500546666666001')
49
+ # # => '39991.11119 22222.500542 66666.660015 8 10010000031155'
50
+ #
51
+ class LinhaDigitavel < String
52
+ attr_reader :codigo_de_barras
53
+ # @param [String] codigo_de_barras Código de Barras de 44 posições
54
+ # @return [String]
55
+ #
56
+ # Representação numérica do código de barras
57
+ #
58
+ # @example
59
+ #
60
+ # LinhaDigitavel.new('34196166700000123451091234567880057123457000')
61
+ # # => '34191.09123 34567.880058 71234.570001 6 16670000012345'
62
+ #
63
+ # LinhaDigitavel.new('99991101200000350007772130530150081897500000')
64
+ # # => '99997.77213 30530.150082 18975.000003 1 10120000035000'
65
+ #
66
+ # LinhaDigitavel.new('39998100100000311551111122222500546666666001')
67
+ # # => '39991.11119 22222.500542 66666.660015 8 10010000031155'
68
+ #
69
+ # # Retorna uma String vazia caso o código de barras esteja vazio.
70
+ # LinhaDigitavel.new('')
71
+ # # => ''
72
+ #
73
+ # # Retorna uma String vazia caso o código de barras esteja vazio.
74
+ # LinhaDigitavel.new(nil)
75
+ # # => ''
76
+ #
77
+ # # Retorna uma String vazia caso o código de barras seja menor que 44 posições.
78
+ # LinhaDigitavel.new('123456789')
79
+ # # => ''
80
+ #
81
+ # # Retorna uma String vazia caso o código de barras seja maior que 44 posições.
82
+ # LinhaDigitavel.new('12345678901234567890123456789012345678901234567890')
83
+ # # => ''
84
+ #
85
+ def initialize(codigo_de_barras)
86
+ @codigo_de_barras = codigo_de_barras.to_s
87
+
88
+ if @codigo_de_barras.present? and @codigo_de_barras.size == 44
89
+ super(representacao_numerica_do_codigo_de_barras)
90
+ else
91
+ super('')
92
+ end
93
+ end
94
+
95
+ # @return [String] Retorna todos os campos da linha digitável pegando as posições exatas do código de barras.
96
+ #
97
+ def representacao_numerica_do_codigo_de_barras
98
+ "#{primeiro_campo} #{segundo_campo} #{terceiro_campo} #{quarto_campo} #{quinto_campo}"
99
+ end
100
+
101
+ # @api private
102
+ #
103
+ # Retorna o primeiro campo da linha digitável com seu respectivo dígito verificador.
104
+ #
105
+ # @return [String]
106
+ #
107
+ def primeiro_campo
108
+ primeiro_campo_sem_digito = "#{codigo_de_barras[0..3]}#{codigo_de_barras[19..23]}"
109
+ digito_verificador = Modulo10.new(primeiro_campo_sem_digito)
110
+ "#{primeiro_campo_sem_digito}#{digito_verificador}".gsub(/^(.{5})(.{5})/, '\1.\2')
111
+ end
112
+
113
+ # @api private
114
+ #
115
+ # Retorna o segundo campo da linha digitável com seu respectivo dígito verificador.
116
+ #
117
+ # @return [String]
118
+ #
119
+ def segundo_campo
120
+ segundo_campo_sem_digito = "#{codigo_de_barras[24..33]}"
121
+ digito_verificador = Modulo10.new(segundo_campo_sem_digito)
122
+ "#{segundo_campo_sem_digito}#{digito_verificador}".gsub(/(.{5})(.{6})/, '\1.\2')
123
+ end
124
+
125
+ # @api private
126
+ #
127
+ # Retorna o terceiro campo da linha digitável com seu respectivo dígito verificador.
128
+ #
129
+ # @return [String]
130
+ #
131
+ def terceiro_campo
132
+ terceiro_campo_sem_digito = "#{codigo_de_barras[34..46]}"
133
+ digito_verificador = Modulo10.new(terceiro_campo_sem_digito)
134
+ "#{terceiro_campo_sem_digito}#{digito_verificador}".gsub(/(.{5})(.{6})/, '\1.\2')
135
+ end
136
+
137
+ # @api private
138
+ #
139
+ # Retorna o dígito verificador do código de barras (posição 5 do código de barras)
140
+ #
141
+ # @return [String]
142
+ #
143
+ def quarto_campo
144
+ "#{codigo_de_barras[4]}"
145
+ end
146
+
147
+ # @api private
148
+ #
149
+ # Retorna o quinto e último campo da linha digitável.
150
+ #
151
+ # @return [String]
152
+ #
153
+ def quinto_campo
154
+ "#{codigo_de_barras[5..8]}#{codigo_de_barras[9..18]}"
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+ module BrBoleto
3
+ module Calculos
4
+ # === Cálculo do Modulo 10
5
+ #
6
+ # === Passos
7
+ #
8
+ # 1) Multiplica-se cada algarismo do campo pela seqüência de multiplicadores <b>2, 1, 2, 1, 2, 1 ...</b>, posicionados da direita para a esquerda.
9
+ #
10
+ # 2) Some individualmente, os algarismos dos resultados dos produtos, obtendo-se o total (N).
11
+ #
12
+ # 3) Divida o total encontrado (N) por 10, e determine o resto da divisão como MOD 10 (N).
13
+ #
14
+ # 4) Encontre o DAC através da seguinte expressão:
15
+ #
16
+ # DAC = 10 - Mod 10 (n)
17
+ #
18
+ # <b>OBS.:</b> Se o resultado do passo 4 for 10, considere o DAC = 0.
19
+ #
20
+ # ==== Exemplos
21
+ #
22
+ # Considerando-se a seguinte representação numérica <b>'12345'</b>.
23
+ #
24
+ # 1) Multiplicando a seqüência de multiplicadores:
25
+ #
26
+ # 1 2 3 4 5
27
+ # * * * * * ===> Multiplicação.
28
+ # 2 1 2 1 2
29
+ #
30
+ # 2) Some, individualmente:
31
+ #
32
+ # 2 + 2 + 6 + 4 + 1 + 0 (Veja a observação abaixo explicando o '1' + '0').
33
+ # # => 15
34
+ #
35
+ # <b>OBS.:</b>: Resultado da soma que possua 2 digitos deve somar cada dígito.
36
+ # Exemplos: 10 -> 1 + 0. 11 -> 1 + 1, 28 -> 2 + 8, etc.
37
+ #
38
+ # 3) Divida o total encontrado por 10, a fim de determinar o resto da divisão:
39
+ #
40
+ # 15 % 10
41
+ # # => 5
42
+ #
43
+ # 4) Calculando o DAC:
44
+ #
45
+ # 10 - 5
46
+ # # => 5 =======> Resultado final retornado.
47
+ #
48
+ class Modulo10 < String
49
+ # @param [String ou Integer] number (Corresponde ao número a ser calculado pelo Módulo 10)
50
+ # @return [String]
51
+ # @example
52
+ #
53
+ # BrBoleto::Calculos::Modulo10.new(1233)
54
+ # # => "6"
55
+ #
56
+ # BrBoleto::Calculos::Modulo10.new(4411)
57
+ # # => "5"
58
+ #
59
+ # BrBoleto::Calculos::Modulo10.new('9000')
60
+ # # => "1"
61
+ #
62
+ # BrBoleto::Calculos::Modulo10.new('6789')
63
+ # # => "2"
64
+ #
65
+ def initialize(number)
66
+ @number = number
67
+ super(calculate.to_s)
68
+ end
69
+
70
+ # @return [String] Resultado final do cálculo do módulo 10.
71
+ #
72
+ def calculate
73
+ sum_total = FatoresDeMultiplicacao.new(@number, fatores: [2, 1]).collect do |result_of_each_sum|
74
+ Digitos.new(result_of_each_sum).sum
75
+ end.sum
76
+ total = 10 - (sum_total % 10)
77
+
78
+ return 0 if total.equal?(10)
79
+ total
80
+ end
81
+ end
82
+ end
83
+ end