identificamex 0.0.2 → 0.0.3

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.
@@ -0,0 +1,92 @@
1
+ module Identificamex
2
+ module Nombre
3
+
4
+ class RazonSocial
5
+ include Mayusculas
6
+
7
+ def initialize(razon_social)
8
+ @razon_social = normalizar(razon_social)
9
+ end
10
+
11
+ def siglas
12
+ tres_letras_de_razon_social
13
+ end
14
+
15
+ def to_s
16
+ @razon_social
17
+ end
18
+
19
+ private
20
+
21
+ def normalizar(str)
22
+ eliminar_abreviaturas(mayusculas(str))
23
+ end
24
+
25
+ def tres_letras_de_razon_social
26
+ case palabras_a_considerar.count
27
+ when 3 then siglas_tres_palabras
28
+ when 2 then siglas_dos_palabras
29
+ else siglas_palabra_unica
30
+ end
31
+ end
32
+
33
+ def palabras_a_considerar
34
+ @palabras_a_considerar ||= palabras_razon_social[0, 3]
35
+ end
36
+
37
+ def siglas_tres_palabras
38
+ palabras_a_considerar.map{|l| l[0]}.join
39
+ end
40
+
41
+ def siglas_dos_palabras
42
+ palabras_a_considerar[0][0] + palabras_a_considerar[1][0, 2]
43
+ end
44
+
45
+ def siglas_palabra_unica
46
+ letras = palabras_a_considerar.first[0, 3]
47
+ letras = letras + ("X" * (3 - letras.length)) if letras.length < 3
48
+ letras
49
+ end
50
+
51
+ def palabras_razon_social
52
+ @razon_social
53
+ .split
54
+ .select{|p| !palabras_especiales.member?(p) }
55
+ end
56
+
57
+ def eliminar_abreviaturas(str)
58
+ abreviaturas.each do |a|
59
+ str = str.gsub(a, '')
60
+ end
61
+ str.strip
62
+ end
63
+
64
+ def palabras_especiales
65
+ %w[DE Y DEL LOS LAS LA EL PARA EN]
66
+ end
67
+
68
+ def abreviaturas
69
+ [
70
+ /COMPAÑIA/,
71
+ /CIA/,
72
+ /\ASOCIEDAD/,
73
+ /\ASOC/,
74
+ / S EN N ?C\z/,
75
+ / S EN C\z/,
76
+ / S DE R ?L\z/,
77
+ / S EN C POR A\z/,
78
+ / S ?A\z/,
79
+ / S ?A DE C ?V\z/,
80
+ / S ?N ?C\z/,
81
+ / S ?C\z/,
82
+ / A ?C\z/,
83
+ / A EN P\z/,
84
+ / S ?C ?L\z/,
85
+ / S ?C DE R ?L DE C ?V\z/,
86
+ / S ?C ?S\z/
87
+ ]
88
+ end
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,209 @@
1
+ module Identificamex
2
+ module Rfc
3
+
4
+ class Homoclave
5
+
6
+ def initialize(rfc_base)
7
+ @rfc_base = rfc_base
8
+ end
9
+
10
+ def siglas
11
+ homonimia + digito_verificador
12
+ end
13
+
14
+ #=================
15
+ private
16
+ #=================
17
+
18
+ def homonimia
19
+ @homonimia ||= generar_homonimia
20
+ end
21
+
22
+ def generar_homonimia
23
+ suma = map_sequential_pair(numbers){|v1, v2| to_number(v1, v2) * to_number(v2)}.inject(:+)
24
+ numero_tres_ultimos_digitos = suma.to_s[-3..-1].to_i
25
+ cosiente = numero_tres_ultimos_digitos / 34
26
+ residuo = numero_tres_ultimos_digitos % 34
27
+ [clave_diferenciadora[cosiente], clave_diferenciadora[residuo]].join
28
+ end
29
+
30
+ def map_sequential_pair(str)
31
+ size = str.size
32
+ str.each_char.each_with_index.collect do |v, i|
33
+ yield str[v], str[i + 1] if i + 1 <= size
34
+ end
35
+ end
36
+
37
+ def to_number(*chars)
38
+ [*chars].join.to_i
39
+ end
40
+
41
+ def numbers
42
+ @numbers ||= generate_numbers
43
+ end
44
+
45
+ def generate_numbers
46
+ @rfc_base.nombre_completo
47
+ .each_char
48
+ .map{|ch| tabla_conversiones[ch]}
49
+ .unshift('0')
50
+ .join
51
+ end
52
+
53
+ def digito_verificador
54
+ residuo_digito_verificador(suma_letras_rfc(@rfc_base.siglas + homonimia))
55
+ end
56
+
57
+ def suma_letras_rfc(str)
58
+ longitud_base = 13
59
+ str = " " + str if str.length < (longitud_base - 1)
60
+ str.each_char.each_with_index.inject(0) do |total, (ch, i)|
61
+ total + (longitud_base - i) * tabla_digito_verificador[ch]
62
+ end
63
+ end
64
+
65
+ def residuo_digito_verificador(total)
66
+ modulo_base = 11
67
+ residuo = total % modulo_base
68
+ if residuo == 0
69
+ '0'
70
+ elsif residuo == 10 || (modulo_base - residuo) == 10
71
+ 'A'
72
+ else
73
+ (modulo_base - residuo).to_s
74
+ end
75
+ end
76
+
77
+ def tabla_conversiones
78
+ return @tabla_conversiones if @tabla_conversiones
79
+ @tabla_conversiones = {
80
+ ' ' => '00',
81
+ '0' => '00',
82
+ '1' => '01',
83
+ '2' => '02',
84
+ '3' => '03',
85
+ '4' => '04',
86
+ '5' => '05',
87
+ '6' => '06',
88
+ '7' => '07',
89
+ '8' => '08',
90
+ '9' => '09',
91
+ '&' => '10',
92
+ 'A' => '11',
93
+ 'B' => '12',
94
+ 'C' => '13',
95
+ 'D' => '14',
96
+ 'E' => '15',
97
+ 'F' => '16',
98
+ 'G' => '17',
99
+ 'H' => '18',
100
+ 'I' => '19',
101
+ 'J' => '21',
102
+ 'K' => '22',
103
+ 'L' => '23',
104
+ 'M' => '24',
105
+ 'N' => '25',
106
+ 'O' => '26',
107
+ 'P' => '27',
108
+ 'Q' => '28',
109
+ 'R' => '29',
110
+ 'S' => '32',
111
+ 'T' => '33',
112
+ 'U' => '34',
113
+ 'V' => '35',
114
+ 'W' => '36',
115
+ 'X' => '37',
116
+ 'Y' => '38',
117
+ 'Z' => '39',
118
+ 'Ñ' => '40',
119
+ }
120
+ @tabla_conversiones.default = '0'
121
+ @tabla_conversiones
122
+ end
123
+
124
+ def clave_diferenciadora
125
+ {
126
+ 0 => '1',
127
+ 1 => '2',
128
+ 2 => '3',
129
+ 3 => '4',
130
+ 4 => '5',
131
+ 5 => '6',
132
+ 6 => '7',
133
+ 7 => '8',
134
+ 8 => '9',
135
+ 9 => 'A',
136
+ 10 => 'B',
137
+ 11 => 'C',
138
+ 12 => 'D',
139
+ 13 => 'E',
140
+ 14 => 'F',
141
+ 15 => 'G',
142
+ 16 => 'H',
143
+ 17 => 'I',
144
+ 18 => 'J',
145
+ 19 => 'K',
146
+ 20 => 'L',
147
+ 21 => 'M',
148
+ 22 => 'N',
149
+ 23 => 'P',
150
+ 24 => 'Q',
151
+ 25 => 'R',
152
+ 26 => 'S',
153
+ 27 => 'T',
154
+ 28 => 'U',
155
+ 29 => 'V',
156
+ 30 => 'W',
157
+ 31 => 'X',
158
+ 32 => 'Y',
159
+ 33 => 'Z',
160
+ }
161
+ end
162
+
163
+ def tabla_digito_verificador
164
+ {
165
+ '0'=> 0,
166
+ '1'=> 1,
167
+ '2'=> 2,
168
+ '3'=> 3,
169
+ '4'=> 4,
170
+ '5'=> 5,
171
+ '6'=> 6,
172
+ '7'=> 7,
173
+ '8'=> 8,
174
+ '9'=> 9,
175
+ 'A'=> 10,
176
+ 'B'=> 11,
177
+ 'C'=> 12,
178
+ 'D'=> 13,
179
+ 'E'=> 14,
180
+ 'F'=> 15,
181
+ 'G'=> 16,
182
+ 'H'=> 17,
183
+ 'I'=> 18,
184
+ 'J'=> 19,
185
+ 'K'=> 20,
186
+ 'L'=> 21,
187
+ 'M'=> 22,
188
+ 'N'=> 23,
189
+ '&'=> 24,
190
+ 'O'=> 25,
191
+ 'P'=> 26,
192
+ 'Q'=> 27,
193
+ 'R'=> 28,
194
+ 'S'=> 29,
195
+ 'T'=> 30,
196
+ 'U'=> 31,
197
+ 'V'=> 32,
198
+ 'W'=> 33,
199
+ 'X'=> 34,
200
+ 'Y'=> 35,
201
+ 'Z'=> 36,
202
+ ' '=> 37,
203
+ 'Ñ'=> 38,
204
+ }
205
+ end
206
+ end
207
+
208
+ end
209
+ end
@@ -0,0 +1,32 @@
1
+ module Identificamex
2
+ module Rfc
3
+
4
+ class RfcBase
5
+ def initialize(params)
6
+ @nombre = params[:nombre] || params[:nombre_completo] || params[:razon_social]
7
+ @fecha = params[:fecha] || params[:fecha_nacimiento] || params[:fecha_creacion]
8
+ end
9
+
10
+ def siglas
11
+ @siglas ||= generar_siglas
12
+ end
13
+
14
+ def nombre_completo
15
+ @nombre.to_s
16
+ end
17
+
18
+ #=====================
19
+ private
20
+ #=====================
21
+
22
+ def generar_siglas
23
+ @nombre.siglas + fecha_formateada
24
+ end
25
+
26
+ def fecha_formateada
27
+ @fecha.strftime('%y%m%d')
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,95 @@
1
+ require_relative '../nombre/nombre_completo'
2
+ require_relative '../nombre/razon_social'
3
+ require_relative 'rfc_base'
4
+ require_relative 'homoclave'
5
+
6
+ module Identificamex
7
+ module Rfc
8
+
9
+ # Clase responsable de generar un RFC. Recibe el rfc y un `hash` con los
10
+ # valores del nombre, primer apellido, segundo apellido y fecha de nacimiento
11
+ # en caso de ser una persona física, o bien, razón social y fecha de creación
12
+ # en caso de ser una persona moral.
13
+ #
14
+ # Ejemplos:
15
+ #
16
+ # generator = RfcGenerator.new(nombre: 'Juan',
17
+ # primer_apellido: 'Barrios',
18
+ # segundo_apellido: 'Fernández',
19
+ # fecha_nacimiento: Date.new(1970, 12, 13))
20
+ # generator.rfc
21
+ # # => 'BAFJ701213'
22
+ #
23
+ # validator = RfcGenerator.new(nombre: 'Juan',
24
+ # primer_apellido: 'Martínez',
25
+ # segundo_apellido: nil,
26
+ # fecha_nacimiento: Date.new(1970, 12, 13))
27
+ # generator.rfc
28
+ # # => 'MAJU701213'
29
+ #
30
+ # generator = RfcGenerator.new(razon_social: 'Sonora Industrial Azucarera, S. de R.L',
31
+ # fecha_creacion: Date.new(1983, 03, 05))
32
+ # generator.rfc
33
+ # # => 'SIA8303054L5'
34
+ #
35
+ # generator = RfcGenerator.new(razon_social: 'Los Viajes Internacionales de Marco Polo, S.A.',
36
+ # fecha_creacion: Date.new(1983, 03, 05))
37
+ # generator.rfc
38
+ # # => 'VIM8303056B6'
39
+ class RfcGenerator
40
+
41
+ def initialize(params)
42
+ @nombre_completo = build_nombre_completo(params)
43
+ @razon_social = build_razon_social(params)
44
+ @fecha_nacimiento = params[:fecha_nacimiento]
45
+ @fecha_creacion = params[:fecha_creacion]
46
+ end
47
+
48
+ def rfc
49
+ @rfc ||= generar_rfc
50
+ end
51
+
52
+ #=====================
53
+ private
54
+ #=====================
55
+
56
+ def generar_rfc
57
+ rfc_base.siglas + homoclave.siglas
58
+ end
59
+
60
+ def build_nombre_completo(params)
61
+ hash = params_for_nombre(params)
62
+ if hash.values.any?(&:present?)
63
+ ::Identificamex::Nombre::NombreCompleto.new(hash)
64
+ end
65
+ end
66
+
67
+ def build_razon_social(params)
68
+ if params[:razon_social].present?
69
+ ::Identificamex::Nombre::RazonSocial.new(params[:razon_social])
70
+ end
71
+ end
72
+
73
+ def rfc_base
74
+ @rfc_base ||= generar_rfc_base
75
+ end
76
+
77
+ def generar_rfc_base
78
+ RfcBase.new(
79
+ nombre: @nombre_completo || @razon_social,
80
+ fecha_nacimiento: @fecha_nacimiento || @fecha_creacion
81
+ )
82
+ end
83
+
84
+ def homoclave
85
+ @homoclave ||= Homoclave.new(@rfc_base)
86
+ end
87
+
88
+ def params_for_nombre(params)
89
+ accepted_keys = %i[nombre primer_apellido segundo_apellido]
90
+ params.reject{|k, v| !(accepted_keys.member?(k)) }
91
+ end
92
+ end
93
+
94
+ end
95
+ end
@@ -1,3 +1,3 @@
1
1
  module Identificamex
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,12 +1,12 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  class RfcFormatValidator < ActiveModel::EachValidator
3
3
  def validate_each(object, attribute, value)
4
- unless value =~ regexp
4
+ unless value =~ rfc_regexp
5
5
  object.errors[attribute] << (options[:message] || "no es un RFC válido")
6
6
  end
7
7
  end
8
8
 
9
- def regexp
9
+ def rfc_regexp
10
10
  if options[:force_homoclave]
11
11
  /\A[A-ZÑ&]{3,4}[0-9]{2}[0-1][0-9][0-3][0-9][A-Z0-9]{3}\z/i
12
12
  else