boleto_bancario 0.0.1.beta → 1.0.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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog.markdown +58 -2
  3. data/README.markdown +679 -156
  4. data/lib/boleto_bancario/calculos/documento.rb +191 -0
  5. data/lib/boleto_bancario/calculos/fator_vencimento.rb +78 -31
  6. data/lib/boleto_bancario/calculos/modulo11_fator_de9a2.rb +65 -0
  7. data/lib/boleto_bancario/calculos/modulo11_fator_de9a2_resto_x.rb +5 -51
  8. data/lib/boleto_bancario/calculos/modulo_numero_de_controle.rb +117 -0
  9. data/lib/boleto_bancario/core/banco_brasil.rb +30 -5
  10. data/lib/boleto_bancario/core/banrisul.rb +182 -0
  11. data/lib/boleto_bancario/core/boleto.rb +97 -35
  12. data/lib/boleto_bancario/core/bradesco.rb +28 -16
  13. data/lib/boleto_bancario/core/c6_bank.rb +155 -0
  14. data/lib/boleto_bancario/core/caixa.rb +233 -0
  15. data/lib/boleto_bancario/core/inter.rb +155 -0
  16. data/lib/boleto_bancario/core/itau.rb +20 -10
  17. data/lib/boleto_bancario/core/nubank.rb +156 -0
  18. data/lib/boleto_bancario/core/santander.rb +19 -22
  19. data/lib/boleto_bancario/core/sicoob.rb +172 -0
  20. data/lib/boleto_bancario/core/sicredi.rb +290 -0
  21. data/lib/boleto_bancario/locales/pt-BR.yml +55 -0
  22. data/lib/boleto_bancario/renderers/base.rb +154 -0
  23. data/lib/boleto_bancario/renderers/html_renderer.rb +92 -0
  24. data/lib/boleto_bancario/renderers/pdf_renderer.rb +130 -0
  25. data/lib/boleto_bancario/renderers/png_renderer.rb +66 -0
  26. data/lib/boleto_bancario/templates/_barcode.html.erb +3 -0
  27. data/lib/boleto_bancario/templates/_cedente.html.erb +14 -0
  28. data/lib/boleto_bancario/templates/_header.html.erb +4 -0
  29. data/lib/boleto_bancario/templates/_instructions.html.erb +10 -0
  30. data/lib/boleto_bancario/templates/_payment.html.erb +36 -0
  31. data/lib/boleto_bancario/templates/_sacado.html.erb +10 -0
  32. data/lib/boleto_bancario/templates/boleto.html.erb +22 -0
  33. data/lib/boleto_bancario/templates/boleto_styles.css +18 -0
  34. data/lib/boleto_bancario/version.rb +3 -2
  35. data/lib/boleto_bancario.rb +48 -19
  36. data/lib/generators/boleto_bancario/views_generator.rb +47 -0
  37. metadata +140 -106
  38. data/.gitignore +0 -18
  39. data/.rspec +0 -1
  40. data/.rvmrc +0 -1
  41. data/Gemfile +0 -3
  42. data/Planning.markdown +0 -131
  43. data/Rakefile +0 -9
  44. data/TODO.markdown +0 -5
  45. data/boleto_bancario.gemspec +0 -25
  46. data/documentacoes_dos_boletos/Bradesco/Manual_BRADESCO.PDF +0 -0
  47. data/spec/boleto_bancario/calculos/digitos_spec.rb +0 -19
  48. data/spec/boleto_bancario/calculos/fator_vencimento_spec.rb +0 -59
  49. data/spec/boleto_bancario/calculos/fatores_de_multiplicacao_spec.rb +0 -69
  50. data/spec/boleto_bancario/calculos/linha_digitavel_spec.rb +0 -57
  51. data/spec/boleto_bancario/calculos/modulo10_spec.rb +0 -49
  52. data/spec/boleto_bancario/calculos/modulo11_fator_de2a7_spec.rb +0 -43
  53. data/spec/boleto_bancario/calculos/modulo11_fator_de2a9_resto_zero_spec.rb +0 -39
  54. data/spec/boleto_bancario/calculos/modulo11_fator_de2a9_spec.rb +0 -61
  55. data/spec/boleto_bancario/calculos/modulo11_fator_de9a2_resto_x_spec.rb +0 -37
  56. data/spec/boleto_bancario/calculos/modulo11_spec.rb +0 -19
  57. data/spec/boleto_bancario/core/banco_brasil_spec.rb +0 -383
  58. data/spec/boleto_bancario/core/boleto_spec.rb +0 -102
  59. data/spec/boleto_bancario/core/bradesco_spec.rb +0 -170
  60. data/spec/boleto_bancario/core/itau_spec.rb +0 -336
  61. data/spec/boleto_bancario/core/santander_spec.rb +0 -135
  62. data/spec/shared_examples/boleto_bancario_shared_example.rb +0 -164
  63. data/spec/spec_helper.rb +0 -14
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ module BoletoBancario
4
+ module Calculos
5
+ # Classe responsável pela validação e formatação de documentos (CPF e CNPJ).
6
+ #
7
+ # === Validação de CPF
8
+ #
9
+ # O CPF possui 11 dígitos e é validado através de dois dígitos verificadores
10
+ # calculados pelo módulo 11.
11
+ #
12
+ # === Validação de CNPJ
13
+ #
14
+ # O CNPJ possui 14 dígitos e é validado através de dois dígitos verificadores
15
+ # calculados pelo módulo 11.
16
+ #
17
+ # @example Validação
18
+ #
19
+ # Documento.valid?('111.444.777-35')
20
+ # #=> true
21
+ #
22
+ # Documento.valid?('11.222.333/0001-81')
23
+ # #=> true
24
+ #
25
+ # @example Formatação
26
+ #
27
+ # Documento.format('11144477735')
28
+ # #=> '111.444.777-35'
29
+ #
30
+ # Documento.format('11222333000181')
31
+ # #=> '11.222.333/0001-81'
32
+ #
33
+ class Documento
34
+ CPF_SIZE = 11
35
+ CNPJ_SIZE = 14
36
+
37
+ # CPF weights for first digit calculation
38
+ CPF_WEIGHTS_FIRST = [10, 9, 8, 7, 6, 5, 4, 3, 2].freeze
39
+
40
+ # CPF weights for second digit calculation
41
+ CPF_WEIGHTS_SECOND = [11, 10, 9, 8, 7, 6, 5, 4, 3, 2].freeze
42
+
43
+ # CNPJ weights for first digit calculation
44
+ CNPJ_WEIGHTS_FIRST = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2].freeze
45
+
46
+ # CNPJ weights for second digit calculation
47
+ CNPJ_WEIGHTS_SECOND = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2].freeze
48
+
49
+ class << self
50
+ # Valida se o documento é um CPF ou CNPJ válido.
51
+ #
52
+ # @param [String] documento O documento a ser validado
53
+ # @return [Boolean] true se válido, false caso contrário
54
+ #
55
+ def valid?(documento)
56
+ return false if documento.blank?
57
+
58
+ digits = only_digits(documento)
59
+
60
+ case digits.size
61
+ when CPF_SIZE
62
+ valid_cpf?(digits)
63
+ when CNPJ_SIZE
64
+ valid_cnpj?(digits)
65
+ else
66
+ false
67
+ end
68
+ end
69
+
70
+ # Formata o documento com pontuação.
71
+ #
72
+ # @param [String] documento O documento a ser formatado
73
+ # @return [String] O documento formatado
74
+ #
75
+ def format(documento)
76
+ return documento if documento.blank?
77
+
78
+ digits = only_digits(documento)
79
+
80
+ case digits.size
81
+ when CPF_SIZE
82
+ format_cpf(digits)
83
+ when CNPJ_SIZE
84
+ format_cnpj(digits)
85
+ else
86
+ documento.to_s
87
+ end
88
+ end
89
+
90
+ # Verifica se o documento é um CPF.
91
+ #
92
+ # @param [String] documento O documento a ser verificado
93
+ # @return [Boolean]
94
+ #
95
+ def cpf?(documento)
96
+ only_digits(documento).size == CPF_SIZE
97
+ end
98
+
99
+ # Verifica se o documento é um CNPJ.
100
+ #
101
+ # @param [String] documento O documento a ser verificado
102
+ # @return [Boolean]
103
+ #
104
+ def cnpj?(documento)
105
+ only_digits(documento).size == CNPJ_SIZE
106
+ end
107
+
108
+ private
109
+
110
+ # Remove caracteres não numéricos.
111
+ #
112
+ # @param [String] value
113
+ # @return [String]
114
+ #
115
+ def only_digits(value)
116
+ value.to_s.gsub(/\D/, '')
117
+ end
118
+
119
+ # Valida um CPF.
120
+ #
121
+ # @param [String] digits Os 11 dígitos do CPF
122
+ # @return [Boolean]
123
+ #
124
+ def valid_cpf?(digits)
125
+ return false if invalid_sequence?(digits)
126
+
127
+ first_digit = calculate_digit(digits[0, 9], CPF_WEIGHTS_FIRST)
128
+ second_digit = calculate_digit(digits[0, 10], CPF_WEIGHTS_SECOND)
129
+
130
+ digits[9].to_i == first_digit && digits[10].to_i == second_digit
131
+ end
132
+
133
+ # Valida um CNPJ.
134
+ #
135
+ # @param [String] digits Os 14 dígitos do CNPJ
136
+ # @return [Boolean]
137
+ #
138
+ def valid_cnpj?(digits)
139
+ return false if invalid_sequence?(digits)
140
+
141
+ first_digit = calculate_digit(digits[0, 12], CNPJ_WEIGHTS_FIRST)
142
+ second_digit = calculate_digit(digits[0, 13], CNPJ_WEIGHTS_SECOND)
143
+
144
+ digits[12].to_i == first_digit && digits[13].to_i == second_digit
145
+ end
146
+
147
+ # Verifica se todos os dígitos são iguais (sequência inválida).
148
+ #
149
+ # @param [String] digits
150
+ # @return [Boolean]
151
+ #
152
+ def invalid_sequence?(digits)
153
+ digits.chars.uniq.size == 1
154
+ end
155
+
156
+ # Calcula um dígito verificador usando módulo 11.
157
+ #
158
+ # @param [String] digits Os dígitos base
159
+ # @param [Array<Integer>] weights Os pesos para multiplicação
160
+ # @return [Integer] O dígito calculado
161
+ #
162
+ def calculate_digit(digits, weights)
163
+ sum = digits.chars.each_with_index.sum do |digit, index|
164
+ digit.to_i * weights[index]
165
+ end
166
+
167
+ remainder = sum % 11
168
+ remainder < 2 ? 0 : 11 - remainder
169
+ end
170
+
171
+ # Formata CPF com pontuação.
172
+ #
173
+ # @param [String] digits
174
+ # @return [String]
175
+ #
176
+ def format_cpf(digits)
177
+ "#{digits[0, 3]}.#{digits[3, 3]}.#{digits[6, 3]}-#{digits[9, 2]}"
178
+ end
179
+
180
+ # Formata CNPJ com pontuação.
181
+ #
182
+ # @param [String] digits
183
+ # @return [String]
184
+ #
185
+ def format_cnpj(digits)
186
+ "#{digits[0, 2]}.#{digits[2, 3]}.#{digits[5, 3]}/#{digits[8, 4]}-#{digits[12, 2]}"
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -1,33 +1,44 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
2
3
  module BoletoBancario
3
4
  module Calculos
4
5
  # Classe responsável pelo cálculo de Fator de Vencimento do boleto bancário.
5
6
  #
6
- # === Descricão
7
+ # === Descrição
7
8
  #
8
9
  # 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
10
  # 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
11
  # ou sem a indicação do fator de vencimento.
11
12
  #
13
+ # === Atualização FEBRABAN 2025
14
+ #
15
+ # Em 22/02/2025, o fator de vencimento atingiu o limite de 9999 (baseado em 07/10/1997).
16
+ # A partir desta data, o cálculo utiliza uma nova data base (29/05/2022) e reinicia em 1000.
17
+ #
12
18
  # === Forma para obtenção do Fator de Vencimento
13
19
  #
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:
20
+ # Calcula-se <b>o número de dias corridos</b> entre a data base e a do vencimento desejado:
21
+ #
22
+ # Para datas anteriores a 22/02/2025:
23
+ # Data base: 07/10/1997
24
+ # Vencimento: 04/07/2000 => Fator: 1001
15
25
  #
16
- # Vencimento desejado: 04/07/2000
17
- # Data base : - 07/10/1997
18
- # # => 1001
26
+ # Para datas a partir de 22/02/2025:
27
+ # Data base: 29/05/2022
28
+ # Fator inicial: 1000
29
+ # Vencimento: 22/02/2025 => Fator: 1000
19
30
  #
20
31
  # === Atenção
21
32
  #
22
- # Caso ocorra divergência entre a data impressa no campo data de vencimento e a constante no código de barras,
33
+ # Caso ocorra divergência entre a data impressa no campo "data de vencimento" e a constante no código de barras,
23
34
  # o recebimento se dará da seguinte forma:
24
35
  #
25
- # * Quando pago por sistemas eletrônicos (Home-Banking, Auto-Atendimento, Internet, SISPAG, telefone, etc.), prevalecerá à representada no código de barras”;
26
- # * Quando quitado na rede de agências, diretamente no caixa, será considerada a data impressa no campo vencimento do BOLETO.
36
+ # * Quando pago por sistemas eletrônicos (Home-Banking, Auto-Atendimento, Internet, SISPAG, telefone, etc.), prevalecerá à representada no "código de barras";
37
+ # * Quando quitado na rede de agências, diretamente no caixa, será considerada a data impressa no campo "vencimento" do BOLETO.
27
38
  #
28
39
  # @return [String] retorna o resultado do cálculo. <b>Deve conter 4 dígitos</b>.
29
40
  #
30
- # @example
41
+ # @example Datas anteriores à transição
31
42
  #
32
43
  # FatorVencimento.new(Date.parse("2012-12-02"))
33
44
  # #=> "5535"
@@ -35,51 +46,87 @@ module BoletoBancario
35
46
  # FatorVencimento.new(Date.parse("1997-10-08"))
36
47
  # #=> "0001"
37
48
  #
38
- # FatorVencimento.new(Date.parse("2012-12-16"))
39
- # #=> "5549"
49
+ # @example Datas após a transição FEBRABAN 2025
40
50
  #
41
- # FatorVencimento.new(Date.parse("2014-12-15"))
42
- # #=> "6278"
51
+ # FatorVencimento.new(Date.parse("2025-02-21"))
52
+ # #=> "9999"
53
+ #
54
+ # FatorVencimento.new(Date.parse("2025-02-22"))
55
+ # #=> "1000"
56
+ #
57
+ # FatorVencimento.new(Date.parse("2025-02-23"))
58
+ # #=> "1001"
43
59
  #
44
60
  class FatorVencimento < String
61
+ # Data base original utilizada até 21/02/2025
62
+ OLD_BASE_DATE = Date.new(1997, 10, 7).freeze
63
+
64
+ # Nova data base utilizada a partir de 22/02/2025
65
+ NEW_BASE_DATE = Date.new(2022, 5, 29).freeze
66
+
67
+ # Data de transição para o novo cálculo
68
+ TRANSITION_DATE = Date.new(2025, 2, 22).freeze
69
+
70
+ # Fator inicial para a nova data base
71
+ NEW_BASE_FACTOR = 1000
72
+
45
73
  attr_reader :base_date
46
- # @param [Date] expiration_date
47
- # @param [Date] base_date
74
+
75
+ # @param [Date] expiration_date Data de vencimento do boleto
76
+ # @param [Date] base_date Data base para cálculo (opcional, determinado automaticamente)
48
77
  # @return [String] retorna o resultado do cálculo. <b>Deve conter 4 dígitos</b>.
49
- # @example
50
- # FatorVencimento.new(Date.parse("2012-09-02"))
51
- # #=> "5444"
52
- #
53
- # FatorVencimento.new(Date.parse("1999-10-01"))
54
- # #=> "0724"
55
78
  #
56
- # FatorVencimento.new(Date.parse("2022-12-16"))
57
- # #=> "9201"
79
+ # @example
80
+ # FatorVencimento.new(Date.parse("2025-03-01"))
81
+ # #=> "1007"
58
82
  #
59
- def initialize(expiration_date, base_date = Date.new(1997, 10, 7))
60
- @base_date = base_date
83
+ def initialize(expiration_date, base_date = nil)
61
84
  @expiration_date = expiration_date
85
+ @base_date = base_date || determine_base_date
62
86
 
63
87
  if @expiration_date.present?
64
88
  super(calculate)
89
+ else
90
+ super()
65
91
  end
66
92
  end
67
93
 
68
94
  # Cálculo da data de vencimento com a data base.
95
+ # Para ambos os períodos, o fator é simplesmente o número de dias desde a data base.
96
+ # A nova data base (29/05/2022) foi escolhida para que 22/02/2025 resulte em fator 1000.
69
97
  #
70
98
  # @return [String] exatamente 4 dígitos
71
99
  #
72
100
  def calculate
73
- expiration_date_minus_base_date.to_s.rjust(4, '0')
101
+ days_from_base.to_s.rjust(4, '0')
74
102
  end
75
103
 
76
- # @api private
104
+ private
105
+
106
+ # Determina qual data base usar com base na data de vencimento
77
107
  #
78
- # Cálculo da data de vencimento com a data base.
108
+ # @return [Date] a data base apropriada
109
+ #
110
+ def determine_base_date
111
+ return OLD_BASE_DATE unless @expiration_date
112
+
113
+ uses_new_calculation? ? NEW_BASE_DATE : OLD_BASE_DATE
114
+ end
115
+
116
+ # Verifica se deve usar o novo cálculo (pós-transição FEBRABAN)
117
+ #
118
+ # @return [Boolean]
119
+ #
120
+ def uses_new_calculation?
121
+ @expiration_date.is_a?(Date) && @expiration_date >= TRANSITION_DATE
122
+ end
123
+
124
+ # Calcula a diferença em dias entre a data de vencimento e a data base.
79
125
  # Chamando #to_i para não retornar um Float.
80
- # @return [Integer] diff between this two dates.
81
126
  #
82
- def expiration_date_minus_base_date
127
+ # @return [Integer] diferença em dias
128
+ #
129
+ def days_from_base
83
130
  (@expiration_date - @base_date).to_i
84
131
  end
85
132
  end
@@ -0,0 +1,65 @@
1
+ module BoletoBancario
2
+ module Calculos
3
+ # === Módulo 11 Fator de 9 a 2
4
+ #
5
+ # === Passos
6
+ #
7
+ # 1) Tomando-se os algarismos multiplique-os, iniciando-se da direita para a esquerda,
8
+ # pela seqüência numérica de 9 a 2 (9, 8, 7, 6, 5, 4, 3, 2 ... e assim por diante).
9
+ #
10
+ # 2) Some o resultado de cada produto efetuado e determine o total como (N).
11
+ #
12
+ # 3) Divida o total (N) por 11 e determine o resto obtido da divisão como Mod 11(N).
13
+ #
14
+ # ==== Exemplo Normal
15
+ #
16
+ # Considerando o seguinte número: '89234560'.
17
+ #
18
+ # 1) Multiplicando a seqüência de multiplicadores:
19
+ #
20
+ # 8 9 2 3 4 5 6 0
21
+ # * * * * * * * *
22
+ # 2 3 4 5 6 7 8 9
23
+ #
24
+ # 2) Soma-se o resultado dos produtos obtidos no item “1” acima:
25
+ #
26
+ # 16 + 27 + 8 + 15 + 24 + 35 + 48 + 0
27
+ # # => 173
28
+ #
29
+ # 3) Determina-se o resto da Divisão:
30
+ #
31
+ # 173 % 11
32
+ # # => 8 =============> Resultado final retornado.
33
+ #
34
+ # @param [String]: Corresponde ao número a ser calculado o Módulo 11 no fator de 9 a 2.
35
+ # @return [String] Retorna o resultado do cálculo descrito acima.
36
+ #
37
+ # @example
38
+ #
39
+ # BoletoBancario::Calculos::Modulo11FatorDe9a2.new('12345')
40
+ # # => '5'
41
+ #
42
+ # BoletoBancario::Calculos::Modulo11FatorDe9a2.new('246')
43
+ # # => '1'
44
+ #
45
+ class Modulo11FatorDe9a2 < Modulo11
46
+ # Sequência numérica de 9 a 2 que será feito a multiplicação de cada dígito
47
+ # do número passado no #initialize.
48
+ #
49
+ # @return [Array] Sequência numérica
50
+ #
51
+ def fatores
52
+ [9, 8, 7, 6, 5, 4, 3, 2]
53
+ end
54
+
55
+ # Calcula o número pelos fatores de multiplicação de 9 a 2.
56
+ # Depois calcula o resto da divisão por 11.
57
+ #
58
+ # @return [Fixnum]
59
+ #
60
+ def calculate
61
+ mod_division
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,37 +1,9 @@
1
1
  module BoletoBancario
2
2
  module Calculos
3
- # === Módulo 11 Fator de 9 a 2 - Resto 10, sendo X
3
+ # === Módulo 11 Fator de 9 a 2 - Resto 10, sendo X
4
+ # <b>Essa classe difere da outra com Modulo11FatorDe9a2, no momento de verificar o resto da divisão por 11.</b>
4
5
  #
5
- # === Passos
6
- #
7
- # 1) Tomando-se os algarismos multiplique-os, iniciando-se da direita para a esquerda,
8
- # pela seqüência numérica de 9 a 2 (9, 8, 7, 6, 5, 4, 3, 2 ... e assim por diante).
9
- #
10
- # 2) Some o resultado de cada produto efetuado e determine o total como (N).
11
- #
12
- # 3) Divida o total (N) por 11 e determine o resto obtido da divisão como Mod 11(N).
13
- #
14
- # <b>OBS.:</b> Se o resultado desta expressão for igual a 10, considere DAC = X.
15
- #
16
- # ==== Exemplo Normal
17
- #
18
- # Considerando o seguinte número: '89234560'.
19
- #
20
- # 1) Multiplicando a seqüência de multiplicadores:
21
- #
22
- # 8 9 2 3 4 5 6 0
23
- # * * * * * * * *
24
- # 2 3 4 5 6 7 8 9
25
- #
26
- # 2) Soma-se o resultado dos produtos obtidos no item “1” acima:
27
- #
28
- # 16 + 27 + 8 + 15 + 24 + 35 + 48 + 0
29
- # # => 173
30
- #
31
- # 3) Determina-se o resto da Divisão:
32
- #
33
- # 173 % 11
34
- # # => 8 =============> Resultado final retornado.
6
+ # <b>Para mais detalhes veja a classe Modulo11FatorDe9a2.</b>
35
7
  #
36
8
  # ==== Exemplo 10 como resto da divisão
37
9
  #
@@ -58,30 +30,12 @@ module BoletoBancario
58
30
  # resultado == 10
59
31
  # # => 'X' =============> Resultado final retornado.
60
32
  #
61
- # @param [String]: Corresponde ao número a ser calculado o Módulo 11 no fator de 9 a 2.
62
- # @return [String] Retorna o resultado do cálculo descrito acima.
63
- #
64
33
  # @example
65
34
  #
66
- # BoletoBancario::Calculos::Modulo11FatorDe9a2RestoX.new('12345')
67
- # # => '5'
68
- #
69
- # BoletoBancario::Calculos::Modulo11FatorDe9a2RestoX.new('246')
70
- # # => '1'
71
- #
72
- # BoletoBancario::Calculos::Modulo11FatorDe2a9.new('184122')
35
+ # BoletoBancario::Calculos::Modulo11FatorDe9a2RestoX.new('184122')
73
36
  # # => 'X'
74
37
  #
75
- class Modulo11FatorDe9a2RestoX < Modulo11
76
- # Sequência numérica de 9 a 2 que será feito a multiplicação de cada dígito
77
- # do número passado no #initialize.
78
- #
79
- # @return [Array] Sequência numérica
80
- #
81
- def fatores
82
- [9, 8, 7, 6, 5, 4, 3, 2]
83
- end
84
-
38
+ class Modulo11FatorDe9a2RestoX < Modulo11FatorDe9a2
85
39
  # Calcula o número pelos fatores de multiplicação de 9 a 2.
86
40
  # Depois calcula o resto da divisão por 11.
87
41
  # Se o resultado desse cálculo for igual a 10, então o DAC = X.
@@ -0,0 +1,117 @@
1
+ module BoletoBancario
2
+ module Calculos
3
+ # === Cálculo do Módulo do Número de Controle (2 dígitos)
4
+ #
5
+ # Tipo de cálculo usado pelo Banco Banrisul.
6
+ #
7
+ # === Cálculo do Primeiro Dígito
8
+ #
9
+ # 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.
10
+ #
11
+ # 2) Some individualmente, os algarismos dos resultados dos produtos, obtendo-se o total (N).
12
+ #
13
+ # 3) Divida o total encontrado (N) por 10, e determine o resto da divisão como MOD 10 (N).
14
+ #
15
+ # 4) Encontre o DAC através da seguinte expressão:
16
+ #
17
+ # DAC = 10 - Mod 10 (n)
18
+ #
19
+ # === Cálculo do segundo dígito
20
+ #
21
+ # # 1) Tomando-se os algarismos multiplique-os, iniciando-se da direita para a esquerda,
22
+ # pela seqüência numérica de 2 a 7 (2, 3, 4, 5, 6, 7 ... e assim por diante).
23
+ #
24
+ # 2) Some o resultado de cada produto efetuado e determine o total como (N).
25
+ #
26
+ # 3) Divida o total (N) por 11 e determine o resto obtido da divisão como Mod 11(N).
27
+ #
28
+ # 4) Calcule o dígito verificador (DAC) através da expressão:
29
+ #
30
+ # DIGIT = 11 - Mod 11 (n)
31
+ #
32
+ # <b>Observações:</b>
33
+ #
34
+ # Caso o 'resto' obtido no cálculo do módulo '11' seja igual a '1', considera-se o DV inválido.
35
+ # Soma-se, então, "1" ao DV obtido do módulo "10" e refaz-se o cálculo do módulo “11”.
36
+ # Se o dígito obtido pelo módulo “10” era igual a "9", considera-se então (9+1=10) DV inválido.
37
+ # Neste caso, o DV do módulo "10" automaticamente será igual a "0" e procede-se assim
38
+ # novo cálculo pelo módulo "11".
39
+ #
40
+ # === Exemplo com Primeiro Dígito Inválido
41
+ #
42
+ # Dado o número '00009194':
43
+ #
44
+ # O somatório do primeiro cálculo é igual a '28' e o Resto é igual a '8'.
45
+ # Portanto, o primeiro DV é igual a 10 - 8 ou DV = 2.
46
+ #
47
+ # O somatório do segundo cálculo é igual a '111' e o Resto é, neste caso, igual a '1'.
48
+ # Portanto, o segundo DV é inválido (11 - 1 = 10).
49
+ #
50
+ # Neste caso, soma-se '1' ao DV obtido do primeiro cálculo:
51
+ #
52
+ # 2 + 1
53
+ # # ======> 3 # Primeiro dígito do número de controle
54
+ #
55
+ # Agora, efetua-se novo cálculo do módulo 11, agora com o novo número, ou seja, 000091943:
56
+ #
57
+ # A somatório do segundo cálculo é igual a '113' e o Resto igual a '3'.
58
+ # Portanto, o segundo DV é igual a:
59
+ #
60
+ # 11 - 3
61
+ # # ====> 8 # Segundo dígito do número de controle
62
+ #
63
+ # Neste exemplo, o número de controle será '38'.
64
+ #
65
+ class ModuloNumeroDeControle < String
66
+ attr_reader :number, :first_digit, :second_digit
67
+
68
+ def initialize(number)
69
+ @number = number
70
+ @first_digit = calculate_first_digit
71
+ @second_digit = calculate_second_digit
72
+
73
+ super(calculate)
74
+ end
75
+
76
+ # Retorna 2 dígitos verificando o segundo dígito se é válido ou não.
77
+ #
78
+ # @return [String]
79
+ #
80
+ def calculate
81
+ if second_digit_result.equal?(10)
82
+ @first_digit = first_digit.to_i + 1
83
+ @first_digit = 0 if @first_digit.equal?(10)
84
+ @second_digit = calculate_second_digit
85
+ end
86
+
87
+ "#{first_digit}#{second_digit}"
88
+ end
89
+
90
+ # Retorna a subtração de 11 pelo resto da divisão por 11 do segundo dígito.
91
+ #
92
+ # @return [Integer]
93
+ #
94
+ def second_digit_result
95
+ 11 - @second_digit.mod_division
96
+ end
97
+
98
+ # Calcula o primeiro dígito pelo módulo 10.
99
+ # Para mais detalhes veja a classe Modulo10.
100
+ #
101
+ # @return [String]
102
+ #
103
+ def calculate_first_digit
104
+ Modulo10.new(number)
105
+ end
106
+
107
+ # Calcula o segundo dígito pelo módulo 11 usando os fatores de 2 a 7.
108
+ # Para mais detalhes veja a classe Modulo11FatorDe2a7.
109
+ #
110
+ # @return [String]
111
+ #
112
+ def calculate_second_digit
113
+ Modulo11FatorDe2a7.new("#{number}#{first_digit}")
114
+ end
115
+ end
116
+ end
117
+ end
@@ -116,16 +116,25 @@ module BoletoBancario
116
116
  17
117
117
  end
118
118
 
119
- validates :codigo_cedente, :agencia, :digito_agencia, :conta_corrente, :digito_conta_corrente, presence: true
119
+ # <b>Carteiras suportadas.</b>
120
+ #
121
+ # <b>Método criado para validar se a carteira informada é suportada.</b>
122
+ #
123
+ # @return [Array]
124
+ #
125
+ def self.carteiras_suportadas
126
+ %w[12 16 17 18]
127
+ end
120
128
 
121
- validates :digito_agencia, length: { maximum: 1 }
122
- validates :digito_conta_corrente, length: { maximum: 1 }
129
+ validates :codigo_cedente, :agencia, :digito_agencia, :conta_corrente, :digito_conta_corrente, presence: true
123
130
 
124
- # Validações de Agencia e Conta corrente.
131
+ # Validações de Agencia, Conta corrente e Carteira.
125
132
  #
126
133
  validates :agencia, length: { maximum: tamanho_maximo_agencia }, if: :deve_validar_agencia?
127
134
  validates :conta_corrente, length: { maximum: tamanho_maximo_conta_corrente }, if: :deve_validar_conta_corrente?
128
135
 
136
+ validates :carteira, inclusion: { in: ->(object) { object.class.carteiras_suportadas } }, if: :deve_validar_carteira?
137
+
129
138
  # Validações do número do documento.
130
139
  #
131
140
  # === Número do Documento e Código do Cedente.
@@ -182,7 +191,7 @@ module BoletoBancario
182
191
  # @return [String]
183
192
  #
184
193
  def numero_documento
185
- if @numero_documento.present?
194
+ if @numero_documento.present? and numero_documento_esperado[tamanho_codigo_cedente].present?
186
195
  @numero_documento.to_s.rjust(numero_documento_esperado[tamanho_codigo_cedente], '0')
187
196
  else
188
197
  @numero_documento
@@ -214,6 +223,22 @@ module BoletoBancario
214
223
  '9'
215
224
  end
216
225
 
226
+ # Dígito do código da agência. Precisa mostrar esse dígito no boleto.
227
+ #
228
+ # @return [String] Dígito da agência calculado apartir do Modulo11FatorDe9a2RestoX.
229
+ #
230
+ def digito_agencia
231
+ Modulo11FatorDe9a2RestoX.new(agencia)
232
+ end
233
+
234
+ # Dígito da conta corrente. Precisa mostrar esse dígito no boleto.
235
+ #
236
+ # @return [String] Dígito da conta corrente calculado apartir do Modulo11FatorDe9a2RestoX.
237
+ #
238
+ def digito_conta_corrente
239
+ Modulo11FatorDe9a2RestoX.new(conta_corrente)
240
+ end
241
+
217
242
  # Campo Agencia / Código do Cedente
218
243
  # Retorna formatado a agência, digito da agência, número da conta corrente e dígito da conta.
219
244
  #