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,169 @@
1
+ # encoding: utf-8
2
+ module BrBoleto
3
+ module Core
4
+ # Implementação de emissão de boleto bancário pelo Banco Sicoob.
5
+ #
6
+ # === Documentação Implementada
7
+ #
8
+ # A documentação na qual essa implementação foi baseada está localizada na pasta
9
+ # 'documentacoes_dos_boletos/sicoob' dentro dessa biblioteca.
10
+ #
11
+ # === Código da Carteira
12
+ #
13
+ # '1' - Cobrança SEM registro
14
+ # '9' - Cobrança COM registro
15
+ #
16
+ class Sicoob < Boleto
17
+ # Tamanho máximo de uma agência no Banco Sicoob.
18
+ # <b>Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.</b>
19
+ #
20
+ # @return [Fixnum] 4
21
+ #
22
+ def self.tamanho_maximo_agencia
23
+ 4
24
+ end
25
+
26
+ # Tamanho máximo do codigo cedente no Banco Sicoob.
27
+ # <b>Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.</b>
28
+ #
29
+ # @return [Fixnum] 7
30
+ #
31
+ def self.tamanho_maximo_codigo_cedente
32
+ 7
33
+ end
34
+
35
+ # Tamanho máximo do numero do documento no Boleto.
36
+ # <b>Método criado justamente para ficar documentado o tamanho máximo aceito até a data corrente.</b>
37
+ #
38
+ # @return [Fixnum] 6
39
+ #
40
+ def self.tamanho_maximo_numero_documento
41
+ 7
42
+ end
43
+
44
+ # <b>Carteiras suportadas.</b>
45
+ #
46
+ # <b>Método criado para validar se a carteira informada é suportada.</b>
47
+ #
48
+ # @return [Array]
49
+ #
50
+ def self.carteiras_suportadas
51
+ %w[1 3]
52
+ end
53
+
54
+ # Validações para os campos abaixo:
55
+ #
56
+ # * Agencia
57
+ # * Codigo Cedente
58
+ # * Número do documento
59
+ #
60
+ # Se você quiser sobrescrever os metodos, <b>ficará a sua responsabilidade.</b>
61
+ # Basta você sobrescrever os métodos de validação:
62
+ #
63
+ # class Sicoob < BrBoleto::Core::Sicoob
64
+ # def self.tamanho_maximo_agencia
65
+ # 6
66
+ # end
67
+ #
68
+ # def self.tamanho_maximo_codigo_cedente
69
+ # 9
70
+ # end
71
+ #
72
+ # def self.tamanho_maximo_numero_documento
73
+ # 10
74
+ # end
75
+ # end
76
+ #
77
+ # Obs.: Mudar as regras de validação podem influenciar na emissão do boleto em si.
78
+ # Talvez você precise analisar o efeito no #codigo_de_barras e na #linha_digitável (ambos podem ser
79
+ # sobreescritos também).
80
+ #
81
+ validates :agencia, :codigo_cedente, presence: true
82
+
83
+ validates :agencia, length: { maximum: tamanho_maximo_agencia }, if: :deve_validar_agencia?
84
+ validates :codigo_cedente, length: { maximum: tamanho_maximo_codigo_cedente }, if: :deve_validar_codigo_cedente?
85
+ validates :numero_documento, length: { maximum: tamanho_maximo_numero_documento }, if: :deve_validar_numero_documento?
86
+
87
+ validates :carteira, inclusion: { in: ->(object) { object.class.carteiras_suportadas } }, if: :deve_validar_carteira?
88
+
89
+ # @return [String] 4 caracteres
90
+ #
91
+ def agencia
92
+ @agencia.to_s.rjust(4, '0') if @agencia.present?
93
+ end
94
+
95
+ # @return [String] 7 caracteres
96
+ # O Código do cedente é o mesmo que o codigo do beneficiário
97
+ def codigo_cedente
98
+ @codigo_cedente.to_s.rjust(7, '0') if @codigo_cedente.present?
99
+ end
100
+
101
+ # @return [String] 6 caracteres
102
+ #
103
+ def numero_documento
104
+ @numero_documento.to_s.rjust(7, '0') if @numero_documento.present?
105
+ end
106
+
107
+ # @return [String] Código do Banco descrito na documentação.
108
+ #
109
+ def codigo_banco
110
+ '756'
111
+ end
112
+
113
+ # @return [String] Dígito do código do banco descrito na documentação.
114
+ #
115
+ def digito_codigo_banco
116
+ '0'
117
+ end
118
+
119
+ # Campo Agência / Código do Cedente
120
+ #
121
+ # @return [String]
122
+ #
123
+ def agencia_codigo_cedente
124
+ "#{agencia} / #{codigo_cedente}"
125
+ end
126
+
127
+ # O nosso número descrino na documentação é formado pelo dois ultimos digitos do ano autal e
128
+ # por outros 6 digitos que o clinte usara para numerar os documentos, assim sendo composto por 8 dígitos.
129
+ #
130
+ # @return [String]
131
+ #
132
+ def nosso_numero
133
+ "#{numero_documento}-#{digito_verificador_nosso_numero}"
134
+ end
135
+
136
+
137
+ # === Código de barras do banco
138
+ #
139
+ # ___________________________________________________________
140
+ # | Posição | Tamanho | Descrição |
141
+ # |---------|---------|---------------------------------------|
142
+ # | 20 - 20 | 01 | Código da carteira |
143
+ # | 21 - 24 | 04 | Código da agência |
144
+ # | 25 - 26 | 02 | Código da modalidade de cobrança (01) |
145
+ # | 27 - 33 | 07 | Código do Cedente |
146
+ # | 34 - 41 | 08 | Nosso Número do título |
147
+ # | 42 - 44 | 03 | Número da Parcela do Título (001) |
148
+ # |___________________________________________________________|
149
+ #
150
+ # @return [String]
151
+ #
152
+ def codigo_de_barras_do_banco
153
+ "#{carteira}#{agencia}#{modalidade_cobranca}#{codigo_cedente}#{nosso_numero.gsub('-','')}#{parcelas}"
154
+ end
155
+
156
+ def digito_verificador_nosso_numero
157
+ BrBoleto::Calculos::Modulo11Fator3197.new("#{agencia}#{codigo_cedente.rjust(10, '0')}#{numero_documento}")
158
+ end
159
+
160
+ def modalidade_cobranca
161
+ '01'
162
+ end
163
+
164
+ def parcelas
165
+ '001'
166
+ end
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,8 @@
1
+ module BrBoleto
2
+ module Version
3
+ MAJOR = 0 #inclui alterações de API e pode quebrar compatibilidade com versões anteriores
4
+ MINOR = 1 #inclui novas funcionalidades, sem quebrar APIs existentes
5
+ PATCH = 0 #corrige bugs ou traz melhorias em implementações já existentes
6
+ CURRENT = "#{MAJOR}.#{MINOR}.#{PATCH}"
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ describe BrBoleto::Calculos::Digitos do
5
+ (0..9).each do |number|
6
+ it "should return self when is #{number}" do
7
+ BrBoleto::Calculos::Digitos.new(number).sum.must_equal number
8
+ end
9
+ end
10
+ { 11 => 2, '18' => 9, 99 => 18, '58' => 13, 112 => 4, '235' => 10 }.each do |number, expecting|
11
+ it "should sum the sum of the digits when is '#{number}', expecting to be '#{expecting}'" do
12
+ BrBoleto::Calculos::Digitos.new(number).sum.must_equal expecting
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ describe BrBoleto::Calculos::FatorVencimento do
5
+ describe "#base_date" do
6
+ it "should be 1997-10-07" do
7
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2012-02-01")).base_date.must_equal Date.new(1997, 10, 7)
8
+ end
9
+ end
10
+
11
+ describe "#calculate" do
12
+ it 'should return an empty string when passing nil value' do
13
+ BrBoleto::Calculos::FatorVencimento.new(nil).must_equal ''
14
+ end
15
+
16
+ it 'should return an empty string when passing empty value' do
17
+ BrBoleto::Calculos::FatorVencimento.new('').must_equal ''
18
+ end
19
+
20
+ it "should calculate the days between expiration date and base date" do
21
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2012-12-2")).must_equal "5535"
22
+ end
23
+
24
+ it "should calculate equal to itau documentation example" do
25
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2000-07-04")).must_equal "1001"
26
+ end
27
+
28
+ it "should calculate equal to itau documentation last section of the docs" do
29
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2002-05-01")).must_equal "1667"
30
+ end
31
+
32
+ it "should calculate to the maximum date equal to itau documentation example" do
33
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2025-02-21")).must_equal "9999"
34
+ end
35
+
36
+ it "should calculate the days between expiration date one year ago" do
37
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2011-05-25")).must_equal "4978"
38
+ end
39
+
40
+ it "should calculate the days between expiration date two years ago" do
41
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2010-10-02")).must_equal "4743"
42
+ end
43
+
44
+ it "should calculate the days between expiration date one year from now" do
45
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2013-02-01")).must_equal "5596"
46
+ end
47
+
48
+ it "should calculate the days between expiration date eigth years from now" do
49
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("2020-02-01")).must_equal "8152"
50
+ end
51
+
52
+ it "should calculate the days between expiration date formating with 4 digits" do
53
+ BrBoleto::Calculos::FatorVencimento.new(Date.parse("1997-10-08")).must_equal "0001"
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ describe BrBoleto::Calculos::FatoresDeMultiplicacao do
5
+ context 'with factors of 2 and 1' do
6
+ let(:factors) { [2, 1] }
7
+
8
+ context 'with one digit' do
9
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(1, fatores: factors) }
10
+
11
+ it { subject.must_equal [2] }
12
+ end
13
+
14
+ context 'with four digits' do
15
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(1234, fatores: factors) }
16
+
17
+ it { subject.must_equal [1, 4, 3, 8] }
18
+ end
19
+
20
+ context 'with five digits' do
21
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(11385, fatores: factors) }
22
+
23
+ it { subject.must_equal [2, 1, 6, 8, 10] }
24
+ end
25
+
26
+ context 'with ten digits' do
27
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(1234567890, fatores: factors) }
28
+
29
+ it { subject.must_equal [1, 4, 3, 8, 5, 12, 7, 16, 9, 0] }
30
+ end
31
+ end
32
+
33
+ context 'with factors of 2..9' do
34
+ let(:factors) { (2..9).to_a }
35
+
36
+ context 'with one digit' do
37
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(4, fatores: factors) }
38
+
39
+ it { subject.must_equal [8] }
40
+ end
41
+
42
+ context 'with four digits' do
43
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(1864, fatores: factors) }
44
+
45
+ it { subject.must_equal [ 5, 32, 18, 8] }
46
+ end
47
+
48
+ context 'with ten digits' do
49
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(1234567890, fatores: factors) }
50
+ end
51
+
52
+ context 'with bradesco documentation example' do
53
+ let(:bradesco_example) { '9999101200000350007772130530150081897500000' }
54
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(bradesco_example, fatores: factors) }
55
+
56
+ it { subject.must_equal [36, 27, 18, 81, 8, 0, 6, 10, 0, 0, 0, 0, 0, 21, 30, 0, 0, 0, 14, 63, 56, 14, 6, 15, 0, 15, 6, 0, 8, 35, 0, 0, 32, 3, 16, 81, 56, 35, 0, 0, 0, 0, 0] }
57
+ end
58
+
59
+ context 'with itau documentation example' do
60
+ let(:itau_example) { '3419166700000123451101234567880057123457000' }
61
+ subject { BrBoleto::Calculos::FatoresDeMultiplicacao.new(itau_example, fatores: factors) }
62
+
63
+ it { subject.must_equal [12, 12, 2, 81, 8, 42, 36, 35, 0, 0, 0, 0, 0, 7, 12, 15, 16, 15, 2, 9, 0, 7, 12, 15, 16, 15, 12, 63, 64, 56, 0, 0, 20, 21, 2, 18, 24, 28, 30, 35, 0, 0, 0] }
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ module BrBoleto
5
+ module Calculos
6
+ describe LinhaDigitavel do
7
+ context "using the Itau documentation example" do
8
+ subject { LinhaDigitavel.new('34196166700000123451091234567880057123457000') }
9
+
10
+ it { subject.must_equal '34191.09123 34567.880058 71234.570001 6 16670000012345' }
11
+ end
12
+
13
+ context "using the Bradesco documentation example" do
14
+ subject { LinhaDigitavel.new('99991101200000350007772130530150081897500000') }
15
+
16
+ it { subject.must_equal '99997.77213 30530.150082 18975.000003 1 10120000035000' }
17
+ end
18
+
19
+ context "using the HSBC documentation example" do
20
+ subject { LinhaDigitavel.new('39998100100000311551111122222500546666666001') }
21
+
22
+ it { subject.must_equal '39991.11119 22222.500542 66666.660015 8 10010000031155' }
23
+ end
24
+
25
+ context "using the Caixa documentation example" do
26
+ subject { LinhaDigitavel.new('10491107400000160000001100128701000901200200') }
27
+
28
+ it { subject.must_equal '10490.00118 00128.701000 09012.002003 1 10740000016000' }
29
+ end
30
+
31
+ describe "when 'codigo_de_barras' invalid" do
32
+ context "when is empty" do
33
+ subject { LinhaDigitavel.new('') }
34
+
35
+ it { subject.must_equal '' }
36
+ end
37
+
38
+ context "when nil" do
39
+ subject { LinhaDigitavel.new(nil) }
40
+
41
+ it { subject.must_equal '' }
42
+ end
43
+
44
+ context "when have less than 44 positions" do
45
+ subject { LinhaDigitavel.new('121212121') }
46
+
47
+ it { subject.must_equal '' }
48
+ end
49
+
50
+ context "when have more than 44 positions" do
51
+ subject { LinhaDigitavel.new('12345678901234567890123456789012345678901234567890') }
52
+
53
+ it { subject.must_equal '' }
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ module BrBoleto
5
+ module Calculos
6
+ describe Modulo10 do
7
+ it "should accept the examples by the 'Itau documentation'" do
8
+ Modulo10.new('341911012').must_equal '1'
9
+ Modulo10.new('3456788005').must_equal '8'
10
+ Modulo10.new('7123457000').must_equal '1'
11
+ end
12
+
13
+ it "should accept the example from Banrisul" do
14
+ Modulo10.new('00009274').must_equal '2'
15
+ end
16
+
17
+ it "returns zero when number is 0" do
18
+ Modulo10.new('0').must_equal '0'
19
+ end
20
+
21
+ it "returns zero when mod 10 is zero" do
22
+ Modulo10.new('99906').must_equal '0'
23
+ end
24
+
25
+ it "calculate when number had 1 digit" do
26
+ Modulo10.new('1').must_equal '8'
27
+ end
28
+
29
+ it "calculate when number had 2 digits" do
30
+ Modulo10.new('10').must_equal '9'
31
+ end
32
+
33
+ it "calculate when number had 3 digits" do
34
+ Modulo10.new('994').must_equal '4'
35
+ end
36
+
37
+ it "calculate when number had 5 digits" do
38
+ Modulo10.new('97831').must_equal '2'
39
+ end
40
+
41
+ it "calculate when number had 6 digits" do
42
+ Modulo10.new('147966').must_equal '6'
43
+ end
44
+
45
+ it "calculate when number had 10 digits" do
46
+ Modulo10.new('3456788005').must_equal '8'
47
+ end
48
+
49
+ it "should accept numbers too" do
50
+ Modulo10.new(12345).must_equal '5'
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+ require 'test_helper'
3
+
4
+ module BrBoleto
5
+ module Calculos
6
+ describe Modulo11Fator3197 do
7
+ context 'with Sicoob documentation example' do
8
+ subject { Modulo11Fator3197.new('000100000000190000021') }
9
+
10
+ it { subject.must_equal '8' }
11
+ end
12
+
13
+ context "quando o resultado do mod_division for 0 então deve retornar zero e não o resultado do total" do
14
+ subject { Modulo11Fator3197.new('123') }
15
+ before do
16
+ Modulo11Fator3197.any_instance.stubs(:mod_division).returns(0)
17
+ Modulo11Fator3197.any_instance.stubs(:total).returns(9)
18
+ end
19
+ it { subject.must_equal '0' }
20
+ end
21
+
22
+ context "quando o resultado do mod_division for 1 então deve retornar zero e não o resultado do total" do
23
+ subject { Modulo11Fator3197.new('123') }
24
+ before do
25
+ Modulo11Fator3197.any_instance.stubs(:mod_division).returns(1)
26
+ Modulo11Fator3197.any_instance.stubs(:total).returns(9)
27
+ end
28
+ it { subject.must_equal '0' }
29
+ end
30
+
31
+ [1..9].each do |numero|
32
+ context "quando o resultado do mod_division for #{numero} então deve o resultado do total" do
33
+ subject { Modulo11Fator3197.new('123') }
34
+ before do
35
+ Modulo11Fator3197.any_instance.stubs(:mod_division).returns(numero)
36
+ Modulo11Fator3197.any_instance.stubs(:total).returns(9)
37
+ end
38
+ it { subject.must_equal '9' }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end