rfc_facil 0.1.1 → 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.
- checksums.yaml +4 -4
- data/README.md +4 -1
- data/lib/rfc_facil.rb +4 -2
- data/lib/rfc_facil/homoclave_calculator.rb +58 -56
- data/lib/rfc_facil/juristic_person.rb +12 -0
- data/lib/rfc_facil/juristic_ten_digits_code_calculator.rb +26 -0
- data/lib/rfc_facil/natural_person.rb +18 -0
- data/lib/rfc_facil/natural_ten_digits_code_calculator.rb +124 -0
- data/lib/rfc_facil/rfc.rb +31 -17
- data/lib/rfc_facil/verification_digit_calculator.rb +28 -26
- data/lib/rfc_facil/version.rb +1 -1
- data/rfc_facil.gemspec +2 -0
- data/tests/rfc_facil/test_homoclave_calculator.rb +3 -3
- data/tests/rfc_facil/test_juristic_ten_digits_code_calculator.rb +17 -0
- data/tests/rfc_facil/{test_ten_digits_code_calculator.rb → test_natural_ten_digits_code_calculator.rb} +3 -3
- data/tests/rfc_facil/test_rfc.rb +11 -2
- data/tests/rfc_facil/test_verification_digit_calculator.rb +1 -1
- data/tests/tests_helper.rb +4 -1
- metadata +25 -7
- data/lib/rfc_facil/person.rb +0 -16
- data/lib/rfc_facil/ten_digits_code_calculator.rb +0 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 13be1b95f84eeda13a0db0fef85d61561db1f8cd
|
4
|
+
data.tar.gz: a0927464ec27e36164f7a2f81b168dc6da322690
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f8e93171358b4cc72b4082235fbf2d562595caf7b7c4781319e1b43edc42b62182a4b6de36d124dc69f3de6d7d1685792ecc962b72a987bd5f23dbfaf694170
|
7
|
+
data.tar.gz: bd7f631ffce63eafde81dac49bb1c1c4b214c8cb33b3ea1121a638de7e762f168146576f9da55e33e4f7cb6270b785ddbf92addd6d8e34910cabce2b7be22b3f
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# Rfc Fácil
|
2
|
+
[](https://codeclimate.com/github/Munett/rfc_facil)
|
3
|
+
[](https://codeclimate.com/github/Munett/rfc_facil/coverage)
|
4
|
+
|
2
5
|

|
3
6
|
|
4
7
|
Libreria para calcular el Registro Federal de Contribuyentes en México (RFC) en Ruby.
|
@@ -12,7 +15,7 @@ gem 'rfc_facil'
|
|
12
15
|
```
|
13
16
|
Calcular el rfc de una persona física es muy sencillo:
|
14
17
|
```ruby
|
15
|
-
rfc = Rfc.new(name: 'Adrián Marcelo', first_last_name: 'Rangel', second_last_name: 'Araujo', day: 27, month: 11, year: 1992)
|
18
|
+
rfc = RfcFacil::Rfc.new(name: 'Adrián Marcelo', first_last_name: 'Rangel', second_last_name: 'Araujo', day: 27, month: 11, year: 1992)
|
16
19
|
rfc.to_s # => 'RAAA921127RI6'
|
17
20
|
```
|
18
21
|
|
data/lib/rfc_facil.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'rfc_facil/version'
|
2
2
|
require 'rfc_facil/homoclave_calculator'
|
3
|
-
require 'rfc_facil/
|
3
|
+
require 'rfc_facil/natural_person'
|
4
|
+
require 'rfc_facil/juristic_person'
|
4
5
|
require 'rfc_facil/rfc'
|
5
|
-
require 'rfc_facil/
|
6
|
+
require 'rfc_facil/natural_ten_digits_code_calculator'
|
7
|
+
require 'rfc_facil/juristic_ten_digits_code_calculator'
|
6
8
|
require 'rfc_facil/verification_digit_calculator'
|
7
9
|
require 'i18n'
|
8
10
|
require 'unicode_utils/upcase'
|
@@ -1,72 +1,74 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
1
|
+
module RfcFacil
|
2
|
+
class HomoclaveCalculator
|
3
|
+
HOMOCLAVE_DIGITS = '123456789ABCDEFGHIJKLMNPQRSTUVWXYZ'.freeze
|
4
|
+
FULL_NAME_MAPPING = {
|
5
|
+
' ' => '00', '0' => '00', '1' => '01', '2' => '02', '3' => '03', '4' => '04',
|
6
|
+
'5' => '05', '6' => '06', '7' => '07', '8' => '08', '9' => '09', '&' => '10',
|
7
|
+
'A' => '11', 'B' => '12', 'C' => '13', 'D' => '14', 'E' => '15', 'F' => '16',
|
8
|
+
'G' => '17', 'H' => '18', 'I' => '19', 'J' => '21', 'K' => '22', 'L' => '23',
|
9
|
+
'M' => '24', 'N' => '25', 'O' => '26', 'P' => '27', 'Q' => '28', 'R' => '29',
|
10
|
+
'S' => '32', 'T' => '33', 'U' => '34', 'V' => '35', 'W' => '36', 'X' => '37',
|
11
|
+
'Y' => '38', 'Z' => '39', 'Ñ' => '40'
|
12
|
+
}.freeze
|
13
|
+
attr_accessor :person, :full_name, :mapped_full_name, :pairs_of_digits_sum, :homoclave
|
13
14
|
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def initialize(person)
|
16
|
+
@person = person
|
17
|
+
end
|
17
18
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
def calculate
|
20
|
+
normalize_full_name
|
21
|
+
map_full_name_to_digits_code
|
22
|
+
sum_pairs_of_digits
|
23
|
+
build_homoclave
|
23
24
|
|
24
|
-
|
25
|
-
|
25
|
+
@homoclave
|
26
|
+
end
|
26
27
|
|
27
|
-
|
28
|
+
private
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
30
|
+
def build_homoclave
|
31
|
+
last_three_digits = (@pairs_of_digits_sum % 1000)
|
32
|
+
quo = (last_three_digits / 34)
|
33
|
+
reminder = (last_three_digits % 34)
|
34
|
+
@homoclave = "#{HOMOCLAVE_DIGITS[quo]}#{HOMOCLAVE_DIGITS[reminder]}"
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
def sum_pairs_of_digits
|
38
|
+
@pairs_of_digits_sum = 0
|
39
|
+
(0..@mapped_full_name.length - 2).each do |i|
|
40
|
+
num1 = @mapped_full_name[i..i + 1].to_i
|
41
|
+
num2 = @mapped_full_name[i + 1..i + 1].to_i
|
41
42
|
|
42
|
-
|
43
|
+
@pairs_of_digits_sum += num1 * num2
|
44
|
+
end
|
43
45
|
end
|
44
|
-
end
|
45
46
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
47
|
+
def map_full_name_to_digits_code
|
48
|
+
@mapped_full_name = '0'
|
49
|
+
@full_name.each_char do |c|
|
50
|
+
@mapped_full_name << map_character_to_two_digit_code(c)
|
51
|
+
end
|
50
52
|
end
|
51
|
-
end
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
def map_character_to_two_digit_code(c)
|
55
|
+
return FULL_NAME_MAPPING[c] if FULL_NAME_MAPPING.key?(c)
|
56
|
+
raise ArgumentError, "No two-digit-code mapping for char: #{c}"
|
57
|
+
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
def normalize_full_name
|
60
|
+
raw_full_name = UnicodeUtils.upcase(@person.to_s)
|
61
|
+
@full_name = I18n.transliterate(raw_full_name)
|
62
|
+
@full_name.gsub!(/[-.']/, '') # remove .'-
|
63
|
+
add_missing_char_to_full_name(raw_full_name, 'Ñ')
|
64
|
+
end
|
64
65
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
def add_missing_char_to_full_name(raw_full_name, missing_char)
|
67
|
+
index = raw_full_name.index(missing_char)
|
68
|
+
until index.nil?
|
69
|
+
@full_name[index] = missing_char
|
70
|
+
index = raw_full_name.index(missing_char, index + 1)
|
71
|
+
end
|
70
72
|
end
|
71
73
|
end
|
72
74
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module RfcFacil
|
2
|
+
class JuristicTenDigitsCodeCalculator
|
3
|
+
attr_accessor :person
|
4
|
+
|
5
|
+
def initialize(person)
|
6
|
+
@person = person
|
7
|
+
end
|
8
|
+
|
9
|
+
def calculate
|
10
|
+
words = person.legal_name.split('\\s')
|
11
|
+
"#{words[0][0]}#{words[1][0]}#{words[2][0]}-#{birthday_code}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def birthday_code
|
15
|
+
"#{last_two_digits_of(person.year)}#{formatted_in_two_digits(person.month)}#{formatted_in_two_digits(person.day)}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def formatted_in_two_digits(number)
|
19
|
+
format('%02d', number)
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_two_digits_of(number)
|
23
|
+
formatted_in_two_digits(number % 100)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module RfcFacil
|
2
|
+
class NaturalPerson
|
3
|
+
attr_accessor :name, :first_last_name, :second_last_name, :day, :month, :year
|
4
|
+
|
5
|
+
def initialize(name, first_last_name, second_last_name, day, month, year)
|
6
|
+
@name = name
|
7
|
+
@first_last_name = first_last_name
|
8
|
+
@second_last_name = second_last_name
|
9
|
+
@day = day
|
10
|
+
@month = month
|
11
|
+
@year = year
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"#{@first_last_name} #{@second_last_name} #{@name}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module RfcFacil
|
2
|
+
class NaturalTenDigitsCodeCalculator
|
3
|
+
VOWEL_PATTERN = /[AEIOU]+/
|
4
|
+
attr_accessor :person
|
5
|
+
SPECIAL_PARTICLES = %w(DE LA LAS MC VON DEL LOS Y MAC VAN MI).freeze
|
6
|
+
FORBIDDEN_WORDS = %w(
|
7
|
+
BUEI BUEY CACA CACO CAGA KOGE KAKA MAME KOJO KULO
|
8
|
+
CAGO COGE COJE COJO FETO JOTO KACO KAGO MAMO MEAR MEON
|
9
|
+
MION MOCO MULA PEDA PEDO PENE PUTA PUTO QULO RATA RUIN
|
10
|
+
).freeze
|
11
|
+
|
12
|
+
def initialize(person)
|
13
|
+
@person = person
|
14
|
+
end
|
15
|
+
|
16
|
+
def calculate
|
17
|
+
"#{obfuscate_forbidden_words(name_code)}#{birthday_code}"
|
18
|
+
end
|
19
|
+
|
20
|
+
def obfuscate_forbidden_words(name_code)
|
21
|
+
FORBIDDEN_WORDS.each do |forbidden|
|
22
|
+
return "#{name_code[0..2]}X" if forbidden == name_code
|
23
|
+
end
|
24
|
+
name_code
|
25
|
+
end
|
26
|
+
|
27
|
+
def name_code
|
28
|
+
return first_last_name_empty_form if first_last_name_empty?
|
29
|
+
return second_last_name_empty_form if second_last_name_empty?
|
30
|
+
return first_last_name_too_short_form if first_last_name_is_too_short?
|
31
|
+
normal_form
|
32
|
+
end
|
33
|
+
|
34
|
+
def second_last_name_empty_form
|
35
|
+
first_two_letters_of(@person.first_last_name) <<
|
36
|
+
first_two_letters_of(filter_name(@person.name))
|
37
|
+
end
|
38
|
+
|
39
|
+
def birthday_code
|
40
|
+
"#{@person.year.to_s[-2, 2]}#{format('%02d', @person.month)}#{format('%02d', @person.day)}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def second_last_name_empty?
|
44
|
+
normalize(@person.second_last_name).nil? || normalize(@person.second_last_name).empty?
|
45
|
+
end
|
46
|
+
|
47
|
+
def first_last_name_empty_form
|
48
|
+
first_two_letters_of(@person.second_last_name) <<
|
49
|
+
first_two_letters_of(filter_name(@person.name))
|
50
|
+
end
|
51
|
+
|
52
|
+
def first_last_name_empty?
|
53
|
+
normalize(@person.first_last_name).nil? || normalize(@person.first_last_name).empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
def first_last_name_too_short_form
|
57
|
+
first_letter_of(@person.first_last_name) <<
|
58
|
+
first_letter_of(@person.second_last_name) <<
|
59
|
+
first_two_letters_of(filter_name(@person.name))
|
60
|
+
end
|
61
|
+
|
62
|
+
def first_two_letters_of(word)
|
63
|
+
normalized_word = normalize(word)
|
64
|
+
normalized_word[0..1]
|
65
|
+
end
|
66
|
+
|
67
|
+
def first_last_name_is_too_short?
|
68
|
+
normalize(@person.first_last_name).length <= 2
|
69
|
+
end
|
70
|
+
|
71
|
+
def normal_form
|
72
|
+
first_letter_of(@person.first_last_name) <<
|
73
|
+
first_vowel_excluding_first_character_of(@person.first_last_name) <<
|
74
|
+
first_letter_of(@person.second_last_name) <<
|
75
|
+
first_letter_of(filter_name(@person.name))
|
76
|
+
end
|
77
|
+
|
78
|
+
def filter_name(name)
|
79
|
+
raw_name = normalize(name).strip
|
80
|
+
if raw_name.include?(' ') && (raw_name.start_with?('MARIA', 'JOSE'))
|
81
|
+
return raw_name.split(' ')[1]
|
82
|
+
end
|
83
|
+
name
|
84
|
+
end
|
85
|
+
|
86
|
+
def formatted_in_two_digits(number)
|
87
|
+
format('%02d', number)
|
88
|
+
end
|
89
|
+
|
90
|
+
def last_two_digits_of(number)
|
91
|
+
formatted_in_two_digits(number % 100)
|
92
|
+
end
|
93
|
+
|
94
|
+
def first_letter_of(word)
|
95
|
+
normalized_word = normalize(word)
|
96
|
+
normalized_word[0]
|
97
|
+
end
|
98
|
+
|
99
|
+
def normalize(word)
|
100
|
+
return word if word.nil? || word.empty?
|
101
|
+
normalized_word = UnicodeUtils.upcase(I18n.transliterate(word))
|
102
|
+
remove_special_particles(normalized_word, SPECIAL_PARTICLES)
|
103
|
+
end
|
104
|
+
|
105
|
+
def remove_special_particles(word, special_particles)
|
106
|
+
new_word = word
|
107
|
+
special_particles.each do |particle|
|
108
|
+
pp = "#{particle} "
|
109
|
+
while new_word.include?(pp)
|
110
|
+
i = new_word.to_s.index(pp).to_i
|
111
|
+
new_word.slice!(i..i + pp.length - 1)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
new_word.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
def first_vowel_excluding_first_character_of(word)
|
118
|
+
normalized_word = normalize(word)[1..-1]
|
119
|
+
m = VOWEL_PATTERN.match(normalized_word)
|
120
|
+
raise ArgumentError, "Word doesn't contain a vowel: #{normalized_word}" if m.nil?
|
121
|
+
normalized_word[m.to_s[0]]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/rfc_facil/rfc.rb
CHANGED
@@ -1,22 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
module RfcFacil
|
2
|
+
class Rfc
|
3
|
+
attr_accessor :name, :first_last_name, :second_last_name, :day, :month, :year,
|
4
|
+
:ten_digits_code, :homoclave, :verification_digit, :legal_name
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def initialize(args)
|
7
|
+
@name = args[:name]
|
8
|
+
@first_last_name = args[:first_last_name]
|
9
|
+
@second_last_name = args[:second_last_name]
|
10
|
+
@day = args[:day]
|
11
|
+
@month = args[:month]
|
12
|
+
@year = args[:year]
|
13
|
+
@legal_name = args[:legal_name]
|
14
|
+
if @legal_name.nil?
|
15
|
+
build_natural_person
|
16
|
+
else
|
17
|
+
build_juristic_person
|
18
|
+
end
|
19
|
+
end
|
12
20
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
def build_juristic_person
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def build_natural_person
|
26
|
+
@person = NaturalPerson.new(@name, @first_last_name, @second_last_name, @day, @month, @year)
|
27
|
+
@ten_digits_code = NaturalTenDigitsCodeCalculator.new(@person).calculate
|
28
|
+
@homoclave = HomoclaveCalculator.new(@person).calculate
|
29
|
+
@verification_digit = VerificationDigitCalculator.new("#{@ten_digits_code}#{@homoclave}").calculate
|
30
|
+
end
|
18
31
|
|
19
|
-
|
20
|
-
|
32
|
+
def to_s
|
33
|
+
"#{@ten_digits_code}#{@homoclave}#{@verification_digit}"
|
34
|
+
end
|
21
35
|
end
|
22
36
|
end
|
@@ -1,32 +1,34 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
module RfcFacil
|
2
|
+
class VerificationDigitCalculator
|
3
|
+
attr_accessor :rfc12_digits
|
4
|
+
MAPPING = {
|
5
|
+
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, '5' => 5, '6' => 6,
|
6
|
+
'7' => 7, '8' => 8, '9' => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13,
|
7
|
+
'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20,
|
8
|
+
'L' => 21, 'M' => 22, 'N' => 23, '&' => 24, 'O' => 25, 'P' => 26, 'Q' => 27,
|
9
|
+
'R' => 28, 'S' => 29, 'T' => 30, 'U' => 31, 'V' => 32, 'W' => 33, 'X' => 34,
|
10
|
+
'Y' => 35, 'Z' => 36, ' ' => 37, 'Ñ' => 38
|
11
|
+
}.freeze
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
end
|
15
|
-
|
16
|
-
def calculate
|
17
|
-
sum = 0
|
18
|
-
(0..11).each do |i|
|
19
|
-
sum += map_digit(@rfc12_digits[i]) * (13 - i)
|
13
|
+
def initialize(rfc12_digits)
|
14
|
+
@rfc12_digits = rfc12_digits
|
20
15
|
end
|
21
|
-
reminder = sum % 11
|
22
16
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
17
|
+
def calculate
|
18
|
+
sum = 0
|
19
|
+
(0..11).each do |i|
|
20
|
+
sum += map_digit(@rfc12_digits[i]) * (13 - i)
|
21
|
+
end
|
22
|
+
reminder = sum % 11
|
27
23
|
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
return '0' if reminder == 0
|
25
|
+
return 'A' if reminder == 10
|
26
|
+
(11 - reminder).to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
def map_digit(c)
|
30
|
+
return MAPPING[c] if MAPPING.key?(c)
|
31
|
+
0
|
32
|
+
end
|
31
33
|
end
|
32
34
|
end
|
data/lib/rfc_facil/version.rb
CHANGED
data/rfc_facil.gemspec
CHANGED
@@ -8,6 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.version = RfcFacil::VERSION
|
9
9
|
spec.authors = ['Adrian Rangel']
|
10
10
|
spec.email = ['adrian.rangel@gmail.com']
|
11
|
+
spec.licenses = ['MIT']
|
11
12
|
|
12
13
|
spec.summary = 'Libreria para calcular el Registro Federal de Contribuyentes en México (RFC).'
|
13
14
|
spec.homepage = 'https://acrogenesis.com/rfc_facil/'
|
@@ -23,4 +24,5 @@ Gem::Specification.new do |spec|
|
|
23
24
|
spec.add_development_dependency 'bundler', '~> 1.10'
|
24
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
25
26
|
spec.add_development_dependency 'minitest', '~> 5.8'
|
27
|
+
spec.add_development_dependency 'codeclimate-test-reporter', '~> 0.4'
|
26
28
|
end
|
@@ -50,12 +50,12 @@ class TestHomoclaveCalculator < Minitest::Test
|
|
50
50
|
|
51
51
|
def test_calculate_same_homoclave_for_different_birthdays
|
52
52
|
assert_equal(
|
53
|
-
HomoclaveCalculator.new(
|
54
|
-
HomoclaveCalculator.new(
|
53
|
+
RfcFacil::HomoclaveCalculator.new(RfcFacil::NaturalPerson.new('Juan', 'Perez', 'Garcia', 5, 8, 1987)).calculate,
|
54
|
+
RfcFacil::HomoclaveCalculator.new(RfcFacil::NaturalPerson.new('Juan', 'Perez', 'Garcia', 1, 1, 1901)).calculate
|
55
55
|
)
|
56
56
|
end
|
57
57
|
|
58
58
|
def homoclave(name, firstLastName, secondLastName)
|
59
|
-
HomoclaveCalculator.new(
|
59
|
+
RfcFacil::HomoclaveCalculator.new(RfcFacil::NaturalPerson.new(name, firstLastName, secondLastName, 1, 1, 1901)).calculate
|
60
60
|
end
|
61
61
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class TestJurisitcTenDigitsCodeCalculator < Minitest::Test
|
2
|
+
def should_take_first_letters_from_the_first_three_words_from_legal_name
|
3
|
+
assert_equal('MNE', ten_digits_code('Mu Networks S.A.P.I. de C.V.', 10, 07, 2014)[0, 3])
|
4
|
+
end
|
5
|
+
|
6
|
+
def should_take_creation_date_in_format_yy_mm_dd
|
7
|
+
assert_equal('MNE1407107', ten_digits_code('Mu Networks S.A.P.I. de C.V.', 10, 07, 2014))
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def ten_digits_code(legal_name, day, month, year)
|
13
|
+
RfcFacil::JuristicTenDigitsCodeCalculator.new(
|
14
|
+
RfcFacil::JuristicPerson.new(legal_name, day, month, year)
|
15
|
+
).calculate
|
16
|
+
end
|
17
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
class
|
1
|
+
class TestNaturalTenDigitsCodeCalculator < Minitest::Test
|
2
2
|
def test_calculate_ten_digits_code_for_simple_test_case
|
3
3
|
assert_equal('BAFJ701213', ten_digits_code('Juan', 'Barrios', 'Fernandez', 13, 12, 1970))
|
4
4
|
end
|
@@ -72,8 +72,8 @@ class TestTenDigitsCodeCalculator < Minitest::Test
|
|
72
72
|
private
|
73
73
|
|
74
74
|
def ten_digits_code(name, first_last_name, second_last_name, day, month, year)
|
75
|
-
|
76
|
-
|
75
|
+
RfcFacil::NaturalTenDigitsCodeCalculator.new(
|
76
|
+
RfcFacil::NaturalPerson.new(name, first_last_name, second_last_name, day, month, year)
|
77
77
|
).calculate
|
78
78
|
end
|
79
79
|
end
|
data/tests/rfc_facil/test_rfc.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class TestRfc < Minitest::Test
|
2
|
-
def
|
3
|
-
rfc = Rfc.new(name: 'Adrian Marcelo', first_last_name: 'Rangel',
|
2
|
+
def test_rfc_for_a_natural_person
|
3
|
+
rfc = RfcFacil::Rfc.new(name: 'Adrian Marcelo', first_last_name: 'Rangel',
|
4
4
|
second_last_name: 'Araujo', day: 27, month: 11, year: 1992)
|
5
5
|
|
6
6
|
assert_equal('RAAA921127', rfc.ten_digits_code)
|
@@ -8,4 +8,13 @@ class TestRfc < Minitest::Test
|
|
8
8
|
assert_equal('6', rfc.verification_digit)
|
9
9
|
assert_equal('RAAA921127RI6', rfc.to_s)
|
10
10
|
end
|
11
|
+
|
12
|
+
def test_rfc_for_a_juristic_person
|
13
|
+
_rfc = RfcFacil::Rfc.new(legal_name: 'Mu Networks S.A.P.I de C.V.', day: 10, month: 07, year: 2014)
|
14
|
+
|
15
|
+
# assertThat(rfc.tenDigitsCode, equalTo("SIA-821129"));
|
16
|
+
# assertThat(rfc.homoclave, equalTo("CK"));
|
17
|
+
# assertThat(rfc.verificationDigit, equalTo("6"));
|
18
|
+
# assertThat(rfc.toString(), equalTo("ZATJ870805CK6"));
|
19
|
+
end
|
11
20
|
end
|
data/tests/tests_helper.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
1
3
|
require 'minitest/autorun'
|
2
4
|
require_relative '../lib/rfc_facil'
|
3
5
|
require 'rfc_facil/test_homoclave_calculator'
|
4
6
|
require 'rfc_facil/test_rfc'
|
5
|
-
require 'rfc_facil/
|
7
|
+
require 'rfc_facil/test_natural_ten_digits_code_calculator'
|
8
|
+
require 'rfc_facil/test_juristic_ten_digits_code_calculator'
|
6
9
|
require 'rfc_facil/test_verification_digit_calculator'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rfc_facil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adrian Rangel
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: i18n
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '5.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: codeclimate-test-reporter
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.4'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.4'
|
83
97
|
description:
|
84
98
|
email:
|
85
99
|
- adrian.rangel@gmail.com
|
@@ -96,20 +110,24 @@ files:
|
|
96
110
|
- bin/setup
|
97
111
|
- lib/rfc_facil.rb
|
98
112
|
- lib/rfc_facil/homoclave_calculator.rb
|
99
|
-
- lib/rfc_facil/
|
113
|
+
- lib/rfc_facil/juristic_person.rb
|
114
|
+
- lib/rfc_facil/juristic_ten_digits_code_calculator.rb
|
115
|
+
- lib/rfc_facil/natural_person.rb
|
116
|
+
- lib/rfc_facil/natural_ten_digits_code_calculator.rb
|
100
117
|
- lib/rfc_facil/rfc.rb
|
101
|
-
- lib/rfc_facil/ten_digits_code_calculator.rb
|
102
118
|
- lib/rfc_facil/verification_digit_calculator.rb
|
103
119
|
- lib/rfc_facil/version.rb
|
104
120
|
- logo.png
|
105
121
|
- rfc_facil.gemspec
|
106
122
|
- tests/rfc_facil/test_homoclave_calculator.rb
|
123
|
+
- tests/rfc_facil/test_juristic_ten_digits_code_calculator.rb
|
124
|
+
- tests/rfc_facil/test_natural_ten_digits_code_calculator.rb
|
107
125
|
- tests/rfc_facil/test_rfc.rb
|
108
|
-
- tests/rfc_facil/test_ten_digits_code_calculator.rb
|
109
126
|
- tests/rfc_facil/test_verification_digit_calculator.rb
|
110
127
|
- tests/tests_helper.rb
|
111
128
|
homepage: https://acrogenesis.com/rfc_facil/
|
112
|
-
licenses:
|
129
|
+
licenses:
|
130
|
+
- MIT
|
113
131
|
metadata: {}
|
114
132
|
post_install_message:
|
115
133
|
rdoc_options: []
|
@@ -127,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
127
145
|
version: '0'
|
128
146
|
requirements: []
|
129
147
|
rubyforge_project:
|
130
|
-
rubygems_version: 2.
|
148
|
+
rubygems_version: 2.5.1
|
131
149
|
signing_key:
|
132
150
|
specification_version: 4
|
133
151
|
summary: Libreria para calcular el Registro Federal de Contribuyentes en México (RFC).
|
data/lib/rfc_facil/person.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
class Person
|
2
|
-
attr_accessor :name, :first_last_name, :second_last_name, :day, :month, :year
|
3
|
-
|
4
|
-
def initialize(name, first_last_name, second_last_name, day, month, year)
|
5
|
-
@name = name
|
6
|
-
@first_last_name = first_last_name
|
7
|
-
@second_last_name = second_last_name
|
8
|
-
@day = day
|
9
|
-
@month = month
|
10
|
-
@year = year
|
11
|
-
end
|
12
|
-
|
13
|
-
def to_s
|
14
|
-
"#{@first_last_name} #{@second_last_name} #{@name}"
|
15
|
-
end
|
16
|
-
end
|
@@ -1,122 +0,0 @@
|
|
1
|
-
class TenDigitsCodeCalculator
|
2
|
-
VOWEL_PATTERN = /[AEIOU]+/
|
3
|
-
attr_accessor :person
|
4
|
-
SPECIAL_PARTICLES = %w(DE LA LAS MC VON DEL LOS Y MAC VAN MI)
|
5
|
-
FORBIDDEN_WORDS = %w(
|
6
|
-
BUEI BUEY CACA CACO CAGA KOGE KAKA MAME KOJO KULO
|
7
|
-
CAGO COGE COJE COJO FETO JOTO KACO KAGO MAMO MEAR MEON
|
8
|
-
MION MOCO MULA PEDA PEDO PENE PUTA PUTO QULO RATA RUIN
|
9
|
-
)
|
10
|
-
|
11
|
-
def initialize(person)
|
12
|
-
@person = person
|
13
|
-
end
|
14
|
-
|
15
|
-
def calculate
|
16
|
-
"#{obfuscate_forbidden_words(name_code)}#{birthday_code}"
|
17
|
-
end
|
18
|
-
|
19
|
-
def obfuscate_forbidden_words(name_code)
|
20
|
-
FORBIDDEN_WORDS.each do |forbidden|
|
21
|
-
return "#{name_code[0..2]}X" if forbidden == name_code
|
22
|
-
end
|
23
|
-
name_code
|
24
|
-
end
|
25
|
-
|
26
|
-
def name_code
|
27
|
-
return first_last_name_empty_form if first_last_name_empty?
|
28
|
-
return second_last_name_empty_form if second_last_name_empty?
|
29
|
-
return first_last_name_too_short_form if first_last_name_is_too_short?
|
30
|
-
normal_form
|
31
|
-
end
|
32
|
-
|
33
|
-
def second_last_name_empty_form
|
34
|
-
first_two_letters_of(@person.first_last_name) <<
|
35
|
-
first_two_letters_of(filter_name(@person.name))
|
36
|
-
end
|
37
|
-
|
38
|
-
def birthday_code
|
39
|
-
"#{@person.year.to_s[-2, 2]}#{format('%02d', @person.month)}#{format('%02d', @person.day)}"
|
40
|
-
end
|
41
|
-
|
42
|
-
def second_last_name_empty?
|
43
|
-
normalize(@person.second_last_name).nil? || normalize(@person.second_last_name).empty?
|
44
|
-
end
|
45
|
-
|
46
|
-
def first_last_name_empty_form
|
47
|
-
first_two_letters_of(@person.second_last_name) <<
|
48
|
-
first_two_letters_of(filter_name(@person.name))
|
49
|
-
end
|
50
|
-
|
51
|
-
def first_last_name_empty?
|
52
|
-
normalize(@person.first_last_name).nil? || normalize(@person.first_last_name).empty?
|
53
|
-
end
|
54
|
-
|
55
|
-
def first_last_name_too_short_form
|
56
|
-
first_letter_of(@person.first_last_name) <<
|
57
|
-
first_letter_of(@person.second_last_name) <<
|
58
|
-
first_two_letters_of(filter_name(@person.name))
|
59
|
-
end
|
60
|
-
|
61
|
-
def first_two_letters_of(word)
|
62
|
-
normalized_word = normalize(word)
|
63
|
-
normalized_word[0..1]
|
64
|
-
end
|
65
|
-
|
66
|
-
def first_last_name_is_too_short?
|
67
|
-
normalize(@person.first_last_name).length <= 2
|
68
|
-
end
|
69
|
-
|
70
|
-
def normal_form
|
71
|
-
first_letter_of(@person.first_last_name) <<
|
72
|
-
first_vowel_excluding_first_character_of(@person.first_last_name) <<
|
73
|
-
first_letter_of(@person.second_last_name) <<
|
74
|
-
first_letter_of(filter_name(@person.name))
|
75
|
-
end
|
76
|
-
|
77
|
-
def filter_name(name)
|
78
|
-
raw_name = normalize(name).strip
|
79
|
-
if raw_name.include?(' ') && (raw_name.start_with?('MARIA') || raw_name.start_with?('JOSE'))
|
80
|
-
return raw_name.split(' ')[1]
|
81
|
-
end
|
82
|
-
name
|
83
|
-
end
|
84
|
-
|
85
|
-
def formatted_in_two_digits(number)
|
86
|
-
format('%02d', number)
|
87
|
-
end
|
88
|
-
|
89
|
-
def last_two_digits_of(number)
|
90
|
-
formatted_in_two_digits(number % 100)
|
91
|
-
end
|
92
|
-
|
93
|
-
def first_letter_of(word)
|
94
|
-
normalized_word = normalize(word)
|
95
|
-
normalized_word[0]
|
96
|
-
end
|
97
|
-
|
98
|
-
def normalize(word)
|
99
|
-
return word if word.nil? || word.empty?
|
100
|
-
normalized_word = UnicodeUtils.upcase(I18n.transliterate(word))
|
101
|
-
remove_special_particles(normalized_word, SPECIAL_PARTICLES)
|
102
|
-
end
|
103
|
-
|
104
|
-
def remove_special_particles(word, special_particles)
|
105
|
-
new_word = word
|
106
|
-
special_particles.each do |particle|
|
107
|
-
pp = "#{particle} "
|
108
|
-
while new_word.include?(pp)
|
109
|
-
i = new_word.to_s.index(pp).to_i
|
110
|
-
new_word.slice!(i..i + pp.length - 1)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
new_word.to_s
|
114
|
-
end
|
115
|
-
|
116
|
-
def first_vowel_excluding_first_character_of(word)
|
117
|
-
normalized_word = normalize(word)[1..-1]
|
118
|
-
m = VOWEL_PATTERN.match(normalized_word)
|
119
|
-
fail ArgumentError, "Word doesn't contain a vowel: #{normalized_word}" if m.nil?
|
120
|
-
normalized_word[m.to_s[0]]
|
121
|
-
end
|
122
|
-
end
|