bra_documents 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.travis.yml +10 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +43 -0
- data/LICENSE +674 -0
- data/LICENSE.txt +21 -0
- data/README.md +155 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/bra_documents.gemspec +34 -0
- data/lib/bra_documents.rb +11 -0
- data/lib/bra_documents/cnpj_generator.rb +38 -0
- data/lib/bra_documents/cpf_generator.rb +32 -0
- data/lib/bra_documents/formatter.rb +21 -0
- data/lib/bra_documents/national_register_base.rb +52 -0
- data/lib/bra_documents/testing/rspec.rb +4 -0
- data/lib/bra_documents/testing/rspec/matchers/cnpj_matcher.rb +32 -0
- data/lib/bra_documents/testing/rspec/matchers/cpf_matcher.rb +32 -0
- data/lib/bra_documents/version.rb +5 -0
- metadata +126 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2020 Bruno Vicenzo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
# BraDocuments
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.com/bvicenzo/bra_documents.svg?branch=master)](https://travis-ci.com/bvicenzo/bra_documents)
|
4
|
+
|
5
|
+
This gem make us able to generate Brazilian documents, such as CPF and CNPJ.
|
6
|
+
We can generate a tottaly random number, or pass the number and the gem completes with the verification digits.
|
7
|
+
If you already have a CPF or CNPJ only their numbers, you can also put the mask using the formatter.
|
8
|
+
|
9
|
+
## Installation
|
10
|
+
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'bra_documents'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle install
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install bra_documents
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
### CPF Generation
|
28
|
+
|
29
|
+
```rb
|
30
|
+
BraDocuments::CPFGenerator.generate
|
31
|
+
#=> "86027265892"
|
32
|
+
|
33
|
+
BraDocuments::CPFGenerator.generate(formatted: true)
|
34
|
+
#=> "038.857.544-10"
|
35
|
+
|
36
|
+
BraDocuments::CPFGenerator.generate(person_number: '123123123')
|
37
|
+
#=> "12312312387"
|
38
|
+
|
39
|
+
BraDocuments::CPFGenerator.generate(person_number: '123123123', formatted: true)
|
40
|
+
#=> "123.123.123-87"
|
41
|
+
```
|
42
|
+
|
43
|
+
### CNPJ Generation
|
44
|
+
|
45
|
+
```rb
|
46
|
+
BraDocuments::CNPJGenerator.generate
|
47
|
+
#=> "62885807804809"
|
48
|
+
|
49
|
+
BraDocuments::CNPJGenerator.generate(formatted: true)
|
50
|
+
#=> "53.855.973/0664-39"
|
51
|
+
|
52
|
+
BraDocuments::CNPJGenerator.generate(company_number: '53855973')
|
53
|
+
#=> "53855973879456"
|
54
|
+
|
55
|
+
BraDocuments::CNPJGenerator.generate(company_number: '53855973', formatted: true)
|
56
|
+
#=> "53.855.973/8189-02"
|
57
|
+
|
58
|
+
BraDocuments::CNPJGenerator.generate(company_number: '53855973', matrix_subsidiary_number: '0001')
|
59
|
+
#=> "53855973000179"
|
60
|
+
|
61
|
+
BraDocuments::CNPJGenerator.generate(company_number: '53855973', matrix_subsidiary_number: '0001', formatted: true)
|
62
|
+
#=> "53.855.973/0001-79"
|
63
|
+
```
|
64
|
+
|
65
|
+
### Formatting
|
66
|
+
|
67
|
+
```rb
|
68
|
+
BraDocuments::Formatter.format('86027265892', as: :cpf)
|
69
|
+
# => "860.272.658-92"
|
70
|
+
|
71
|
+
BraDocuments::Formatter.format('53855973879456', as: :cnpj)
|
72
|
+
#=> "53.855.973/8794-56"
|
73
|
+
```
|
74
|
+
|
75
|
+
### Tests Matching
|
76
|
+
|
77
|
+
#### Config
|
78
|
+
If you use RSpec to test your apps we also provide mathers to make easy test if your field has a CPF with a valid format.
|
79
|
+
|
80
|
+
In your `spec/spec_helper.rb` file, require our helpers.
|
81
|
+
|
82
|
+
```rb
|
83
|
+
# spec/spec_helper.rb
|
84
|
+
|
85
|
+
require 'bra_documents/testing/rspec'
|
86
|
+
```
|
87
|
+
|
88
|
+
#### Using
|
89
|
+
|
90
|
+
```rb
|
91
|
+
RSpec.describe 'my tests' do
|
92
|
+
it 'is a formatted CPF' do
|
93
|
+
expect('123.456.789-00').to be_a_formatted_cpf
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'receives a formatted CPF' do
|
97
|
+
object = double(action: true)
|
98
|
+
|
99
|
+
expect(object).to receive(:action).with(a_formatted_cpf)
|
100
|
+
|
101
|
+
object.action('123.456.789-00')
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'is a raw CPF' do
|
105
|
+
expect('12345678900').to be_a_raw_cpf
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'receives a raw CPF' do
|
109
|
+
object = double(action: true)
|
110
|
+
|
111
|
+
expect(object).to receive(:action).with(a_raw_cpf)
|
112
|
+
|
113
|
+
object.action('12345678900')
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'is a formatted CNPJ' do
|
117
|
+
expect('53.855.973/8794-56').to be_a_formatted_cnpj
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'receives a formatted CNPJ' do
|
121
|
+
object = double(action: true)
|
122
|
+
|
123
|
+
expect(object).to receive(:action).with(a_formatted_cnpj)
|
124
|
+
|
125
|
+
object.action('53.855.973/8794-56')
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'is a raw CNPJ' do
|
129
|
+
expect('53855973879456').to be_a_raw_cnpj
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'receives a raw CNPJ' do
|
133
|
+
object = double(action: true)
|
134
|
+
|
135
|
+
expect(object).to receive(:action).with(a_raw_cnpj)
|
136
|
+
|
137
|
+
object.action('53855973879456')
|
138
|
+
end
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
## Development
|
143
|
+
|
144
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
145
|
+
|
146
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
147
|
+
|
148
|
+
## Contributing
|
149
|
+
|
150
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bra_documents.
|
151
|
+
|
152
|
+
|
153
|
+
## License
|
154
|
+
|
155
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "bra_documents"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative 'lib/bra_documents/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'bra_documents'
|
5
|
+
spec.version = BraDocuments::VERSION
|
6
|
+
spec.authors = ['Bruno Vicenzo']
|
7
|
+
spec.email = ['bruno@alumni.usp.br']
|
8
|
+
|
9
|
+
spec.summary = %q{A implementation for generating Brazilian CPF and CNPJ document numbers.}
|
10
|
+
spec.description = %q{Here we are able to generate CPNJ and CPF documents, only numbers or formatted.}
|
11
|
+
spec.homepage = 'https://github.com/bvicenzo/bra_documents'
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
14
|
+
|
15
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org/'
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/bvicenzo/bra_documents/blob/master/CHANGELOG.md'
|
20
|
+
|
21
|
+
# Specify which files should be added to the gem when it is released.
|
22
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
23
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
24
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
25
|
+
end
|
26
|
+
spec.bindir = 'exe'
|
27
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
|
+
spec.require_paths = ['lib']
|
29
|
+
|
30
|
+
spec.add_development_dependency 'pry'
|
31
|
+
spec.add_development_dependency 'pry-nav'
|
32
|
+
spec.add_development_dependency 'rake'
|
33
|
+
spec.add_development_dependency 'rspec'
|
34
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bra_documents/national_register_base'
|
4
|
+
require 'bra_documents/cpf_generator'
|
5
|
+
require 'bra_documents/cnpj_generator'
|
6
|
+
require 'bra_documents/formatter'
|
7
|
+
require "bra_documents/version"
|
8
|
+
|
9
|
+
|
10
|
+
module BraDocuments
|
11
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Libs are following this issue: https://github.com/rails/rails/issues/37835
|
4
|
+
# Rules to generate a CPF https://www.geradorcpf.com/algoritmo_do_cpf.htm
|
5
|
+
|
6
|
+
module BraDocuments
|
7
|
+
class CNPJGenerator < NationalRegisterBase
|
8
|
+
COMPANY_NUMBER_SIZE = 8
|
9
|
+
MATRIX_SUBSIDIARY_SIZE = 4
|
10
|
+
|
11
|
+
class << self
|
12
|
+
# Generates a random CNPJ document number or add verifying digits to one if it's given.
|
13
|
+
# It can return only numbers or formatted with mask
|
14
|
+
#
|
15
|
+
# BraDocuments::CNPJGenerator.generate # => "62885807804809"
|
16
|
+
# BraDocuments::CNPJGenerator.generate(formatted: true) # => "53.855.973/0664-39"
|
17
|
+
# BraDocuments::CNPJGenerator.generate(company_number: '53855973') # => "53855973879456"
|
18
|
+
# BraDocuments::CNPJGenerator.generate(company_number: '53855973', formatted: true) # => "53.855.973/8189-02"
|
19
|
+
# BraDocuments::CNPJGenerator.generate(company_number: '53855973', matrix_subsidiary_number: '0001') # => "53855973000179"
|
20
|
+
# BraDocuments::CNPJGenerator.generate(company_number: '53855973', matrix_subsidiary_number: '0001', formatted: true) # => "53.855.973/0001-79"
|
21
|
+
def generate(company_number: nil, matrix_subsidiary_number: nil, formatted: false)
|
22
|
+
company_number = number_for('Company', COMPANY_NUMBER_SIZE, company_number)
|
23
|
+
matrix_subsidiary_number = number_for('Matrix or subsidiary', MATRIX_SUBSIDIARY_SIZE, matrix_subsidiary_number)
|
24
|
+
numbers = company_number + matrix_subsidiary_number
|
25
|
+
|
26
|
+
full_number = complete!(numbers)
|
27
|
+
|
28
|
+
formatted ? Formatter.format(full_number, as: :cnpj) : full_number
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def verification_digit_multiplicators_for(numbers)
|
34
|
+
(2..(numbers.size - 7)).to_a.reverse + (2..9).to_a.reverse
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Libs are following this issue: https://github.com/rails/rails/issues/37835
|
4
|
+
# Rules to generate a CPF https://www.geradorcpf.com/algoritmo_do_cpf.htm
|
5
|
+
|
6
|
+
module BraDocuments
|
7
|
+
class CPFGenerator < NationalRegisterBase
|
8
|
+
PERSON_NUMBER_SIZE = 9
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Generates a random CPF document number or add verifying digits to one if it's given.
|
12
|
+
# It can return only numbers or formatted with mask
|
13
|
+
#
|
14
|
+
# BraDocuments::CPFGenerator.generate # => "86027265892"
|
15
|
+
# BraDocuments::CPFGenerator.generate(formatted: true) # => "038.857.544-10"
|
16
|
+
# BraDocuments::CPFGenerator.generate(person_number: '123123123') # => "12312312387"
|
17
|
+
# BraDocuments::CPFGenerator.generate(person_number: '123123123', formatted: true) # => "123.123.123-87"
|
18
|
+
def generate(person_number: nil, formatted: false)
|
19
|
+
numbers = number_for('Person', PERSON_NUMBER_SIZE, person_number)
|
20
|
+
full_number = complete!(numbers)
|
21
|
+
|
22
|
+
formatted ? Formatter.format(full_number, as: :cpf) : full_number
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def verification_digit_multiplicators_for(numbers)
|
28
|
+
(2..numbers.size.next).to_a.reverse
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BraDocuments
|
4
|
+
class Formatter
|
5
|
+
FORMATS = {
|
6
|
+
cpf: { pattern: /\A(\d{3})(\d{3})(\d{3})(\d{2})\z/, mask: '%s.%s.%s-%s' },
|
7
|
+
cnpj: { pattern: /\A(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})\z/, mask: '%s.%s.%s/%s-%s'}
|
8
|
+
}.freeze
|
9
|
+
|
10
|
+
# Formats a only numbers CPF or CNPJ in their own mask
|
11
|
+
#
|
12
|
+
# BraDocuments::Formatter.format('86027265892', as: :cpf) # => "860.272.658-92"
|
13
|
+
# BraDocuments::Formatter.format('53855973879456', as: :cnpj) # => "53.855.973/8794-56"
|
14
|
+
def self.format(number, as:)
|
15
|
+
format_data = FORMATS[as]
|
16
|
+
|
17
|
+
Kernel.format(format_data[:mask], *format_data[:pattern].match(number).captures)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BraDocuments
|
4
|
+
class NationalRegisterBase
|
5
|
+
class << self
|
6
|
+
NOT_NUMBER_PATTERN = /\D/.freeze
|
7
|
+
BASE = 11
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def complete!(numbers)
|
12
|
+
2.times { numbers.push(verification_digit_for(numbers)) }
|
13
|
+
numbers.join
|
14
|
+
end
|
15
|
+
|
16
|
+
def number_for(number_description, number_size, given_value)
|
17
|
+
given_value = only_digits_for(given_value)
|
18
|
+
if !given_value.to_s.empty?
|
19
|
+
raise ArgumentError, "#{number_description} number must be a number with #{number_size} digits." unless given_value.size == number_size
|
20
|
+
given_value.split('').map(&:to_i)
|
21
|
+
else
|
22
|
+
number_with(number_size)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def number_with(size)
|
27
|
+
size.times.map { rand(10) }
|
28
|
+
end
|
29
|
+
|
30
|
+
def only_digits_for(number)
|
31
|
+
number.to_s.gsub(NOT_NUMBER_PATTERN, '')
|
32
|
+
end
|
33
|
+
|
34
|
+
def verification_digit_for(numbers)
|
35
|
+
verification_digit_multiplicators = verification_digit_multiplicators_for(numbers)
|
36
|
+
sum_and_multiplication = sum_and_multiply(numbers, verification_digit_multiplicators)
|
37
|
+
verified_digit(sum_and_multiplication)
|
38
|
+
end
|
39
|
+
|
40
|
+
def verified_digit(sum_and_multiplication)
|
41
|
+
rest = sum_and_multiplication % BASE
|
42
|
+
rest < 2 ? 0 : BASE - rest
|
43
|
+
end
|
44
|
+
|
45
|
+
def sum_and_multiply(numbers, multiplicators)
|
46
|
+
multiplicators.map
|
47
|
+
.with_index { |multiplicator, position| numbers[position] * multiplicator }
|
48
|
+
.sum
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rspec/expectations'
|
4
|
+
|
5
|
+
RSpec::Matchers.define :a_formatted_cnpj do
|
6
|
+
match do |cnpj|
|
7
|
+
formatted_cnpj_pattern = /\A(\d{2}\.\d{3}\.\d{3}\/\d{4})-(\d{2})\z/
|
8
|
+
|
9
|
+
formatted_cnpj_pattern.match?(cnpj.to_s)
|
10
|
+
end
|
11
|
+
|
12
|
+
failure_message do |cnpj|
|
13
|
+
"Was expected `#{cnpj.inspect}` to be a Brazilian CNPJ document number but it isn't.\n"\
|
14
|
+
"A CNPJ has the following format XX.XXX.XXX/XXXX-XX where X are numbers from 0 to 9.\n"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
RSpec::Matchers.define :a_raw_cnpj do
|
19
|
+
match do |cnpj|
|
20
|
+
raw_cnpj_pattern = /\A\d{14}\z/
|
21
|
+
|
22
|
+
raw_cnpj_pattern.match?(cnpj.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
failure_message do |cnpj|
|
26
|
+
"Was expected `#{cnpj.inspect}` to be a raw Brazilian cnpj document number but it isn't.\n"\
|
27
|
+
"A raw CNPJ has the following format XXXXXXXXXXXXXX where X are numbers from 0 to 9.\n"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec::Matchers.alias_matcher :be_a_formatted_cnpj, :a_formatted_cnpj
|
32
|
+
RSpec::Matchers.alias_matcher :be_a_raw_cnpj, :a_raw_cnpj
|