sat_rfc 0.1.0 → 0.2.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 +71 -3
- data/lib/rfc/discardable_terms/legal_entity.rb +41 -0
- data/lib/rfc/generator.rb +25 -0
- data/lib/rfc/homoclave_calculator.rb +8 -1
- data/lib/rfc/legal_entity_ten_digits_code_calculator.rb +60 -0
- data/lib/rfc/natural_ten_digits_code_calculator.rb +5 -1
- data/lib/rfc/version.rb +1 -1
- data/lib/sat_rfc.rb +2 -0
- metadata +9 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d4c2e78c0e8c7490a0cc48c4f10c775d7061e6734ca28502c8c01d28ab8fd193
|
|
4
|
+
data.tar.gz: d192b112b798c0a9337d3261b61627e481630e3f5420d9748bd042212a8021a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f6469fc80b3cd16c0a5b007bc04970217af152a220675a73054887b1f2392b65593f875a6c36c933372acf6debd6098b1dd4706bfe1a913b9a8fb3aab04702b
|
|
7
|
+
data.tar.gz: 6e4eb8bb3fe4548f46959ac830bae2701c67e21e1bbeee5e913fe5b5b34a0fcbde88a289ed09becb8764ccecd32bde5d9d952607528075d88e8f90636b4a516c
|
data/README.md
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
# sat_rfc
|
|
2
2
|
|
|
3
|
-
Ruby gem to generate Mexican **RFC** tax identifiers for **natural persons** (personas físicas), following the [
|
|
4
|
-
|
|
5
|
-
Legal entity support (personas morales) is planned for a future release.
|
|
3
|
+
Ruby gem to generate Mexican **RFC** tax identifiers for **natural persons** (personas físicas) and **legal entities** (personas morales), following the SAT algorithm documented by the IFAI (Instituto Federal de Acceso a la Información). Natural person support is inspired by [rfc-facil](https://github.com/josketres/rfc-facil) and [rfc_facil](https://github.com/acrogenesis/rfc_facil).
|
|
6
4
|
|
|
7
5
|
## Installation
|
|
8
6
|
|
|
@@ -39,6 +37,32 @@ rfc = Rfc::Generator.new.for_natural_person(
|
|
|
39
37
|
puts rfc # => "PEGJ900315PE9"
|
|
40
38
|
```
|
|
41
39
|
|
|
40
|
+
To inspect individual RFC parts, use the calculators directly:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
name = "Juan Carlos"
|
|
44
|
+
first_last_name = "Perez"
|
|
45
|
+
second_last_name = "Garcia"
|
|
46
|
+
|
|
47
|
+
ten_digits = Rfc::NaturalTenDigitsCodeCalculator.new(
|
|
48
|
+
name: name,
|
|
49
|
+
first_last_name: first_last_name,
|
|
50
|
+
second_last_name: second_last_name,
|
|
51
|
+
day: 15, month: 3, year: 1990
|
|
52
|
+
).calculate
|
|
53
|
+
# => "PEGJ900315"
|
|
54
|
+
|
|
55
|
+
homoclave = Rfc::HomoclaveCalculator.new(
|
|
56
|
+
name: name,
|
|
57
|
+
first_last_name: first_last_name,
|
|
58
|
+
second_last_name: second_last_name
|
|
59
|
+
).calculate
|
|
60
|
+
# => "PE"
|
|
61
|
+
|
|
62
|
+
verification_digit = Rfc::VerificationDigitCalculator.new("#{ten_digits}#{homoclave}").calculate
|
|
63
|
+
# => "9"
|
|
64
|
+
```
|
|
65
|
+
|
|
42
66
|
You can also pass the birth date as separate values:
|
|
43
67
|
|
|
44
68
|
```ruby
|
|
@@ -54,6 +78,37 @@ Rfc::Generator.new.for_natural_person(
|
|
|
54
78
|
|
|
55
79
|
`date_of_birth` accepts `DD/MM/YYYY` format or any string parseable by Ruby's `Date`.
|
|
56
80
|
|
|
81
|
+
### Legal entity (persona moral)
|
|
82
|
+
|
|
83
|
+
```ruby
|
|
84
|
+
rfc = Rfc::Generator.new.for_legal_entity(
|
|
85
|
+
legal_name: "Mu Networks S.A.P.I. de C.V.",
|
|
86
|
+
constitution_date: "10/07/2014"
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
puts rfc # => "MNP1407108U7"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
To inspect individual RFC parts:
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
legal_name = "Mu Networks S.A.P.I. de C.V."
|
|
96
|
+
|
|
97
|
+
ten_digits = Rfc::LegalEntityTenDigitsCodeCalculator.new(
|
|
98
|
+
legal_name: legal_name,
|
|
99
|
+
day: 10, month: 7, year: 2014
|
|
100
|
+
).calculate
|
|
101
|
+
# => "MNP140710"
|
|
102
|
+
|
|
103
|
+
homoclave = Rfc::HomoclaveCalculator.new(legal_name: legal_name).calculate
|
|
104
|
+
# => "8U"
|
|
105
|
+
|
|
106
|
+
verification_digit = Rfc::VerificationDigitCalculator.new("#{ten_digits}#{homoclave}").calculate
|
|
107
|
+
# => "7"
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
`constitution_date` accepts `DD/MM/YYYY` format or any string parseable by Ruby's `Date`.
|
|
111
|
+
|
|
57
112
|
### Errors
|
|
58
113
|
|
|
59
114
|
```ruby
|
|
@@ -83,6 +138,19 @@ Rfc::Generator.new.for_natural_person(
|
|
|
83
138
|
)
|
|
84
139
|
```
|
|
85
140
|
|
|
141
|
+
## Algorithm reference
|
|
142
|
+
|
|
143
|
+
This gem implements the RFC generation algorithm with homoclave for natural persons and legal entities, based on official documentation obtained through the IFAI.
|
|
144
|
+
|
|
145
|
+
- **INFOMEX folio:** `0610100135506`
|
|
146
|
+
- **Document:** [Algoritmo para generar el RFC con homoclave para personas físicas y morales (PDF)](https://www.mariovaldez.net/files/IFAI%200610100135506%20065%20Algoritmo%20para%20generar%20el%20RFC%20con%20homoclave%20para%20personas%20fisicas%20y%20morales.pdf)
|
|
147
|
+
|
|
148
|
+
## Acknowledgments
|
|
149
|
+
|
|
150
|
+
### Natural person (persona física)
|
|
151
|
+
|
|
152
|
+
Natural person support is inspired by [rfc-facil](https://github.com/josketres/rfc-facil) by [josketres](https://github.com/josketres) and [rfc_facil](https://github.com/acrogenesis/rfc_facil) by [acrogenesis](https://github.com/acrogenesis), following the SAT algorithm documented by the IFAI.
|
|
153
|
+
|
|
86
154
|
## Contributing
|
|
87
155
|
|
|
88
156
|
Bug reports and pull requests are welcome on GitHub at [elosnaya/rfc](https://github.com/elosnaya/rfc).
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rfc
|
|
4
|
+
module DiscardableTerms
|
|
5
|
+
# Dictionary of discardable terms for legal entities (personas morales).
|
|
6
|
+
# Based on SAT specification.
|
|
7
|
+
#
|
|
8
|
+
# After normalization, terms like "S.A. de C.V." become "SA DE CV" (separate words),
|
|
9
|
+
# so individual abbreviations and words are listed here.
|
|
10
|
+
module LegalEntity
|
|
11
|
+
ARTICLES_AND_PREPOSITIONS = %w[
|
|
12
|
+
DE LA LAS DEL LOS Y EL
|
|
13
|
+
PARA POR AL E EN CON SUS
|
|
14
|
+
OF THE AND
|
|
15
|
+
].freeze
|
|
16
|
+
|
|
17
|
+
COMPANY_TYPE_ABBREVIATIONS = %w[
|
|
18
|
+
SA SC SRL SCL SNC SCS CIA COOP SOC CO COMPANY
|
|
19
|
+
CV RL
|
|
20
|
+
].freeze
|
|
21
|
+
|
|
22
|
+
SINGLE_LETTER_ABBREVIATIONS = %w[S A C V].freeze
|
|
23
|
+
|
|
24
|
+
COMPANY_TYPE_WORDS = %w[
|
|
25
|
+
COMPAÑIA SOCIEDAD COOPERATIVA
|
|
26
|
+
ANONIMA COMANDITA RESPONSABILIDAD LIMITADA
|
|
27
|
+
PROMOTORA INVERSION CAPITAL VARIABLE NOMBRE COLECTIVO
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
DISCARDABLE_TERMS = %w[MC VON MAC VAN MI].freeze
|
|
31
|
+
|
|
32
|
+
ALL = (
|
|
33
|
+
ARTICLES_AND_PREPOSITIONS +
|
|
34
|
+
COMPANY_TYPE_ABBREVIATIONS +
|
|
35
|
+
SINGLE_LETTER_ABBREVIATIONS +
|
|
36
|
+
COMPANY_TYPE_WORDS +
|
|
37
|
+
DISCARDABLE_TERMS
|
|
38
|
+
).freeze
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
data/lib/rfc/generator.rb
CHANGED
|
@@ -8,10 +8,12 @@ module Rfc
|
|
|
8
8
|
|
|
9
9
|
def initialize(
|
|
10
10
|
natural_ten_digits_calculator: NaturalTenDigitsCodeCalculator,
|
|
11
|
+
legal_entity_ten_digits_calculator: LegalEntityTenDigitsCodeCalculator,
|
|
11
12
|
homoclave_calculator: HomoclaveCalculator,
|
|
12
13
|
verification_digit_calculator: VerificationDigitCalculator
|
|
13
14
|
)
|
|
14
15
|
@natural_ten_digits_calculator = natural_ten_digits_calculator
|
|
16
|
+
@legal_entity_ten_digits_calculator = legal_entity_ten_digits_calculator
|
|
15
17
|
@homoclave_calculator = homoclave_calculator
|
|
16
18
|
@verification_digit_calculator = verification_digit_calculator
|
|
17
19
|
end
|
|
@@ -47,6 +49,29 @@ module Rfc
|
|
|
47
49
|
raise InvalidDateError, "Invalid date parameters: #{e.message}"
|
|
48
50
|
end
|
|
49
51
|
|
|
52
|
+
def for_legal_entity(
|
|
53
|
+
legal_name:,
|
|
54
|
+
day: nil,
|
|
55
|
+
month: nil,
|
|
56
|
+
year: nil,
|
|
57
|
+
constitution_date: nil
|
|
58
|
+
)
|
|
59
|
+
normalized_day, normalized_month, normalized_year = normalize_date(day, month, year, constitution_date)
|
|
60
|
+
|
|
61
|
+
ten_digits_code = @legal_entity_ten_digits_calculator.new(
|
|
62
|
+
legal_name: legal_name,
|
|
63
|
+
day: normalized_day,
|
|
64
|
+
month: normalized_month,
|
|
65
|
+
year: normalized_year
|
|
66
|
+
).calculate
|
|
67
|
+
|
|
68
|
+
homoclave = @homoclave_calculator.new(legal_name: legal_name).calculate
|
|
69
|
+
|
|
70
|
+
build_rfc(ten_digits_code, homoclave)
|
|
71
|
+
rescue ArgumentError => e
|
|
72
|
+
raise InvalidDateError, "Invalid date parameters: #{e.message}"
|
|
73
|
+
end
|
|
74
|
+
|
|
50
75
|
private
|
|
51
76
|
|
|
52
77
|
def build_rfc(ten_digits_code, homoclave)
|
|
@@ -15,10 +15,11 @@ module Rfc
|
|
|
15
15
|
"Y" => "38", "Z" => "39", "Ñ" => "40"
|
|
16
16
|
}.freeze
|
|
17
17
|
|
|
18
|
-
def initialize(name
|
|
18
|
+
def initialize(name: nil, first_last_name: nil, second_last_name: nil, legal_name: nil)
|
|
19
19
|
@name = name
|
|
20
20
|
@first_last_name = first_last_name
|
|
21
21
|
@second_last_name = second_last_name
|
|
22
|
+
@legal_name = legal_name
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
def calculate
|
|
@@ -73,9 +74,15 @@ module Rfc
|
|
|
73
74
|
end
|
|
74
75
|
|
|
75
76
|
def full_name_string
|
|
77
|
+
return @legal_name if present?(@legal_name)
|
|
78
|
+
|
|
76
79
|
"#{@first_last_name} #{@second_last_name} #{@name}"
|
|
77
80
|
end
|
|
78
81
|
|
|
82
|
+
def present?(value)
|
|
83
|
+
!value.nil? && !value.to_s.strip.empty?
|
|
84
|
+
end
|
|
85
|
+
|
|
79
86
|
def add_missing_char_to_full_name(raw_full_name, missing_char)
|
|
80
87
|
index = raw_full_name.index(missing_char)
|
|
81
88
|
until index.nil?
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "i18n"
|
|
4
|
+
|
|
5
|
+
module Rfc
|
|
6
|
+
class LegalEntityTenDigitsCodeCalculator
|
|
7
|
+
DISCARDABLE_TERMS = DiscardableTerms::LegalEntity::ALL
|
|
8
|
+
|
|
9
|
+
def initialize(legal_name:, day:, month:, year:)
|
|
10
|
+
@legal_name = legal_name
|
|
11
|
+
@day = day
|
|
12
|
+
@month = month
|
|
13
|
+
@year = year
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def calculate
|
|
17
|
+
name_code + birthday_code
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def name_code
|
|
23
|
+
normalized_name = normalize(@legal_name)
|
|
24
|
+
all_words = normalized_name.split(/\s+/).reject(&:empty?)
|
|
25
|
+
significant_words = all_words.reject { |word| DISCARDABLE_TERMS.include?(word) }
|
|
26
|
+
|
|
27
|
+
words_to_use = significant_words.empty? ? all_words : significant_words
|
|
28
|
+
|
|
29
|
+
case words_to_use.length
|
|
30
|
+
when 0
|
|
31
|
+
raise ArgumentError, "Legal name is blank or invalid"
|
|
32
|
+
when 1
|
|
33
|
+
words_to_use[0][0..2]
|
|
34
|
+
when 2
|
|
35
|
+
"#{words_to_use[0][0]}#{words_to_use[1][0..1]}"
|
|
36
|
+
else
|
|
37
|
+
"#{words_to_use[0][0]}#{words_to_use[1][0]}#{words_to_use[2][0]}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def birthday_code
|
|
42
|
+
"#{last_two_digits_of(@year)}#{formatted_in_two_digits(@month)}#{formatted_in_two_digits(@day)}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def normalize(legal_name)
|
|
46
|
+
return legal_name if legal_name.nil? || legal_name.empty?
|
|
47
|
+
|
|
48
|
+
normalized = I18n.transliterate(legal_name).upcase
|
|
49
|
+
normalized.gsub(/[^A-Z0-9\s]/, " ")
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def formatted_in_two_digits(number)
|
|
53
|
+
format("%02d", number)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def last_two_digits_of(number)
|
|
57
|
+
formatted_in_two_digits(number % 100)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -81,7 +81,11 @@ module Rfc
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
def filter_name(name)
|
|
84
|
-
|
|
84
|
+
return name if name.nil? || name.empty?
|
|
85
|
+
|
|
86
|
+
normalized = I18n.transliterate(name).upcase.strip
|
|
87
|
+
normalized = normalized.sub(/^(MA|MA\.|MARIA|JOSE)\s+/, "")
|
|
88
|
+
remove_discardable_terms(normalized, DISCARDABLE_TERMS)
|
|
85
89
|
end
|
|
86
90
|
|
|
87
91
|
def first_letter_of(word)
|
data/lib/rfc/version.rb
CHANGED
data/lib/sat_rfc.rb
CHANGED
|
@@ -7,9 +7,11 @@ I18n.default_locale = :en
|
|
|
7
7
|
|
|
8
8
|
require_relative "rfc/version"
|
|
9
9
|
require_relative "rfc/discardable_terms/natural_person"
|
|
10
|
+
require_relative "rfc/discardable_terms/legal_entity"
|
|
10
11
|
require_relative "rfc/verification_digit_calculator"
|
|
11
12
|
require_relative "rfc/homoclave_calculator"
|
|
12
13
|
require_relative "rfc/natural_ten_digits_code_calculator"
|
|
14
|
+
require_relative "rfc/legal_entity_ten_digits_code_calculator"
|
|
13
15
|
|
|
14
16
|
module Rfc
|
|
15
17
|
class Error < StandardError; end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sat_rfc
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- elOsnaya
|
|
@@ -24,8 +24,10 @@ dependencies:
|
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
25
|
version: '1.0'
|
|
26
26
|
description: Ruby library that generates RFC tax identifiers for Mexican natural persons
|
|
27
|
-
using the SAT algorithm
|
|
28
|
-
|
|
27
|
+
and legal entities using the SAT algorithm documented by the IFAI (INFOMEX folio
|
|
28
|
+
0610100135506). Natural person support is inspired by rfc-facil (josketres) and
|
|
29
|
+
rfc_facil (acrogenesis). Legal entity support is an original implementation by elOsnaya.
|
|
30
|
+
Supports name normalization, homoclave, and verification digit calculation.
|
|
29
31
|
email:
|
|
30
32
|
- luisosnet@gmail.com
|
|
31
33
|
executables: []
|
|
@@ -36,9 +38,11 @@ files:
|
|
|
36
38
|
- README.md
|
|
37
39
|
- Rakefile
|
|
38
40
|
- lib/rfc.rb
|
|
41
|
+
- lib/rfc/discardable_terms/legal_entity.rb
|
|
39
42
|
- lib/rfc/discardable_terms/natural_person.rb
|
|
40
43
|
- lib/rfc/generator.rb
|
|
41
44
|
- lib/rfc/homoclave_calculator.rb
|
|
45
|
+
- lib/rfc/legal_entity_ten_digits_code_calculator.rb
|
|
42
46
|
- lib/rfc/natural_ten_digits_code_calculator.rb
|
|
43
47
|
- lib/rfc/verification_digit_calculator.rb
|
|
44
48
|
- lib/rfc/version.rb
|
|
@@ -50,6 +54,7 @@ licenses:
|
|
|
50
54
|
metadata:
|
|
51
55
|
homepage_uri: https://github.com/elosnaya/rfc
|
|
52
56
|
source_code_uri: https://github.com/elosnaya/rfc
|
|
57
|
+
documentation_uri: https://www.mariovaldez.net/files/IFAI%200610100135506%20065%20Algoritmo%20para%20generar%20el%20RFC%20con%20homoclave%20para%20personas%20fisicas%20y%20morales.pdf
|
|
53
58
|
rdoc_options: []
|
|
54
59
|
require_paths:
|
|
55
60
|
- lib
|
|
@@ -66,5 +71,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
66
71
|
requirements: []
|
|
67
72
|
rubygems_version: 4.0.3
|
|
68
73
|
specification_version: 4
|
|
69
|
-
summary: Generate Mexican RFC codes for natural persons
|
|
74
|
+
summary: Generate Mexican RFC codes for natural persons and legal entities.
|
|
70
75
|
test_files: []
|