br_boleto 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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