Escribe el nombre completo y la fecha de nacimiento, o la razón social y la fecha de constitución de una empresa, y la herramienta calcula el RFC con homoclave y dígito verificador con el procedimiento del SAT. Te explica de dónde sale cada letra. Todo se calcula en tu navegador: no se envía nada.
La homoclave oficial la asigna el SAT. Este cálculo sigue su procedimiento y coincide con la mayoría de los RFC, pero si al inscribirte ya existía otra persona o empresa con el mismo RFC (homonimia), el SAT te asignó una homoclave distinta. Tu RFC válido es el que aparece en tu constancia de situación fiscal. Para comprobar un RFC que ya tienes, usa el validador de RFC.
Cómo se forma el RFC de una persona física
- Cuatro letras: la inicial del primer apellido y su primera vocal interna, la inicial del segundo apellido y la inicial del nombre. Emma Gómez Díaz: G, O, D, E → GODE.
- Seis cifras de la fecha de nacimiento, como año, mes y día: 31 de diciembre de 1956 → 561231.
- Homoclave de dos caracteres: sale del nombre completo. Cada letra se cambia por un número de dos cifras, se suman los productos de cada par de cifras seguidas por la segunda, y con las tres últimas cifras de la suma se eligen dos caracteres de una tabla. Para Emma Gómez Díaz: GR.
- Dígito verificador: un cálculo de módulo 11 sobre los doce caracteres anteriores, que detecta errores al copiar: 8. El RFC completo es GODE561231GR8, el ejemplo que publica el propio SAT.
Las reglas especiales
- Partículas: no cuentan «de», «del», «la», «las», «los», «y», «mc», «mac», «van», «von» ni «mi». Josué Zarzosa de la Torre empieza por ZATJ, con la T de «Torre».
- María y José: si el nombre es compuesto y empieza por María (o Ma.) o José, se usa el segundo nombre. José Antonio Camargo Hernández: CAHA.
- Apellidos muy cortos: si el primer apellido tiene una o dos letras, se toman su inicial, la del segundo apellido y las dos primeras letras del nombre. Ernesto Ek Rivera: ERER.
- Un solo apellido: se toman sus dos primeras letras y las dos primeras del nombre.
- Palabras inconvenientes: si las cuatro letras forman una palabra de la lista del SAT, como BUEY o PEDO, la última se cambia por una X: Ingrid Bueno Ezquerra queda en BUEX.
- Personas morales: se quitan el tipo de sociedad (S.A. de C.V., S. de R.L., A.C.…) y palabras como «el», «de», «compañía» o «sociedad». Con tres palabras o más, se toma la inicial de las tres primeras; con dos, la inicial de la primera y las dos primeras letras de la segunda; con una, sus tres primeras letras. Los números se escriben con letra («El 12» → DOC) y los símbolos sueltos también («@» → ARROBA). Después van la fecha de constitución, la homoclave y el verificador, y el RFC tiene 12 caracteres.
Calcular el RFC en Python
El mismo procedimiento, sin bibliotecas externas:
import re
import unicodedata
from datetime import date
PARTICULAS = {"DE", "LA", "LAS", "MC", "VON", "DEL", "LOS", "Y", "MAC", "VAN", "MI"}
EXCLUIDAS = set(("EL LA DE LOS LAS Y DEL MI POR CON SUS E PARA EN MC VON MAC VAN COMPANIA COMPAÑIA CIA SOCIEDAD "
"SOC COMPANY CO SC SCL SCS SNC SRL CV SA THE OF AND A").split())
INCONVENIENTES = set(("BUEI BUEY CACA CACO CAGA CAGO CAKA CAKO COGE COJA COJE COJI COJO CULO FETO GUEY JOTO KACA "
"KACO KAGA KAGO KAKA KOGE KOJO KULO MAME MAMO MEAR MEAS MEON MION MOCO MULA PEDA PEDO PENE "
"PUTA PUTO QULO RATA RUIN").split())
SIGNOS = {"@": "ARROBA", "´": "APOSTROFE", "%": "PORCIENTO", "#": "NUMERO", "!": "ADMIRACION", ".": "PUNTO",
"$": "PESOS", '"': "COMILLAS", "-": "GUION", "/": "DIAGONAL", "+": "SUMA", "(": "ABRE PARENTESIS",
")": "CIERRA PARENTESIS"}
SOCIEDADES = [r"S\.?\s?EN\s?N\.?\s?C\.?$", r"S\.?\s?EN\s?C\.?\s?POR\s?A\.?$", r"S\.?\s?EN\s?C\.?$",
r"S\.?\s?DE\s?R\.?\s?L\.?$", r"S\.?\s?DE\s?R\.?\s?L\.?\s?DE\s?C\.?\s?V\.?$",
r"S\.?\s?A\.?\s?DE\s?C\.?\s?V\.?$", r"S\.?\s?A\.?\s?P\.?\s?I\.?\s?DE\s?C\.?\s?V\.?$",
r"S\.?\s?A\.?\s?S\.?\s?DE\s?C\.?\s?V\.?$", r"A\.?\s?EN\s?P\.?$", r"S\.?\s?C\.?\s?[LPS]\.?$",
r"S\.?\s?[AC]\.?$", r"S\.?\s?N\.?\s?C\.?$", r"A\.?\s?C\.?$"]
HOMO = {c: i for i, c in enumerate(" 123456789")} | {"0": 0, "&": 10} | \
dict(zip("ABCDEFGHI", range(11, 20))) | dict(zip("JKLMNOPQR", range(21, 30))) | \
dict(zip("STUVWXYZ", range(32, 40))) | {"Ñ": 40}
TABLA = "123456789ABCDEFGHIJKLMNPQRSTUVWXYZ"
VERIFICADOR = "0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ Ñ"
UNIDADES = ("CERO UNO DOS TRES CUATRO CINCO SEIS SIETE OCHO NUEVE DIEZ ONCE DOCE TRECE CATORCE QUINCE DIECISEIS "
"DIECISIETE DIECIOCHO DIECINUEVE VEINTE VEINTIUNO VEINTIDOS VEINTITRES VEINTICUATRO VEINTICINCO "
"VEINTISEIS VEINTISIETE VEINTIOCHO VEINTINUEVE").split()
DECENAS = "_ _ _ TREINTA CUARENTA CINCUENTA SESENTA SETENTA OCHENTA NOVENTA".split()
CENTENAS = "_ CIENTO DOSCIENTOS TRESCIENTOS CUATROCIENTOS QUINIENTOS SEISCIENTOS SETECIENTOS OCHOCIENTOS NOVECIENTOS".split()
def sin_acentos(s):
s = unicodedata.normalize("NFD", s.upper()).replace("Ñ", "\x01")
return re.sub("[̀-ͯ]", "", s).replace("\x01", "Ñ")
def letra(n, corto=False):
"""Un entero en palabras (hasta 999,999,999,999), como la cantidad con letra."""
def hasta_999(n, corto):
if n == 100:
return ["CIEN"]
c, r = divmod(n, 100)
p = [CENTENAS[c]] if c else []
if r:
t = UNIDADES[r] if r < 30 else DECENAS[r // 10] + (" Y " + UNIDADES[r % 10] if r % 10 else "")
if corto and t.endswith("UNO"):
t = t[:-1] # «veintiún mil», «treinta y un mil»
p += t.split()
return p
def hasta_6(n, corto):
miles, resto = divmod(n, 1000)
p = ["MIL"] if miles == 1 else (hasta_999(miles, True) + ["MIL"] if miles else [])
return p + (hasta_999(resto, corto) if resto else [])
millones, resto = divmod(n, 10 ** 6)
if not n:
return ["CERO"]
p = (["UN", "MILLON"] if millones == 1 else hasta_6(millones, True) + ["MILLONES"]) if millones else []
return p + (hasta_6(resto, corto) if resto else [])
def romano(w):
if not w or not re.fullmatch(r"(M{0,4})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})", w):
return None
v = dict(I=1, V=5, X=10, L=50, C=100, D=500, M=1000)
return sum(-v[c] if v[c] < v.get(w[i + 1:i + 2], 0) else v[c] for i, c in enumerate(w))
def homoclave(nombre_completo):
cifras = "0" + "".join(f"{HOMO[c]:02d}" for c in sin_acentos(nombre_completo) if c in HOMO)
suma = sum(int(cifras[i:i + 2]) * int(cifras[i + 1]) for i in range(len(cifras) - 1)) % 1000
return TABLA[suma // 34] + TABLA[suma % 34]
def verificador(sin_digito):
s = sin_digito.rjust(12)
return VERIFICADOR[(11 - sum(VERIFICADOR.index(c) * (13 - i) for i, c in enumerate(s)) % 11) % 11]
def palabras(s):
s = re.sub(r"[^A-ZÑ& ]", "", sin_acentos(s).replace("-", " "))
return [p for p in s.split() if p not in PARTICULAS]
def rfc_fisica(nombre, paterno, materno, nacimiento: date):
n, pat, mat = palabras(nombre), palabras(paterno), palabras(materno)
if len(n) > 1 and n[0] in ("MARIA", "MA", "JOSE"):
n = n[1:]
if not pat or not mat:
letras = (pat or mat)[0][:2] + n[0][:2]
elif len(pat[0]) <= 2:
letras = pat[0][0] + mat[0][0] + n[0][:2]
else:
vocal = re.search("[AEIOU]", pat[0][1:])
letras = pat[0][0] + (vocal.group() if vocal else "X") + mat[0][0] + n[0][0]
letras = (letras + "XXXX")[:4]
if letras in INCONVENIENTES:
letras = letras[:3] + "X"
rfc = letras + nacimiento.strftime("%y%m%d") + homoclave(f"{paterno.strip()} {materno.strip()} {nombre.strip()}")
return rfc + verificador(rfc)
def letras_moral(razon):
t = sin_acentos(razon).strip()
for patron in SOCIEDADES:
t = re.sub(patron, "", t)
salida = []
for w in [w for w in re.split(r"[,\s]+", t) if w and w.rstrip(".") not in EXCLUIDAS]:
if len(w) == 1 and w in SIGNOS:
salida += SIGNOS[w].split()
continue
partes = [p for p in w.split(".") if p]
if len(partes) > 1 and len(partes[0]) == 1: # abreviaturas como F.A.Z. o U.S.
salida.append(partes[0])
w = "".join(partes[1:])
w = re.sub(r"[^A-ZÑ&0-9]", "", w)
if not w:
continue
if w.isdigit():
salida += letra(int(w))
elif romano(w) and len(w) > 1:
salida += letra(romano(w))
else:
salida.append(w)
if not salida:
raise ValueError("la razón social no tiene palabras que cuenten para el RFC")
if len(salida) >= 3:
return salida[0][0] + salida[1][0] + salida[2][0]
if len(salida) == 2:
return (salida[0][0] + salida[1][:2] + "XX")[:3]
return (salida[0][:3] + "XXX")[:3]
def rfc_moral(razon_social, constitucion: date):
rfc = letras_moral(razon_social) + constitucion.strftime("%y%m%d") + homoclave(razon_social)
return rfc + verificador(rfc)
print(rfc_fisica("Emma", "Gómez", "Díaz", date(1956, 12, 31))) # GODE561231GR8
print(rfc_fisica("Josué", "Zarzosa", "de la Torre", date(1987, 8, 5)))
print(rfc_moral("Sonora Industrial Azucarera, S. de R.L.", date(1982, 11, 29)))
print(rfc_moral("Editorial Siglo XXI, S.A.", date(1965, 2, 1)))$ python3 rfc_calcular.pyGODE561231GR8 ZATJ870805CK6 SIA821129S38 ESV650201QL8
Otras herramientas para México: los validadores de RFC, CURP, CLABE y NSS, la calculadora de sueldo neto, la calculadora de IVA y el validador de CFDI 4.0. Todas, en decodigo.com/herramientas.
Procedimiento: documento del SAT sobre la generación del RFC con homoclave, obtenido mediante una solicitud de acceso a la información (IFAI, folio 0610100135506), con sus anexos de valores para la homoclave y el dígito verificador, palabras excluidas y palabras inconvenientes. Ejemplo GODE561231GR8: el que usa el SAT en sus especificaciones. La herramienta reproduce los 109 casos del documento recogidos en las pruebas de rfc-facil (Apache 2.0), y se comparó con su versión de JavaScript en 30,000 nombres y razones sociales al azar: las diferencias se deben a que aquí se conserva la Ñ, se usa la lista completa de palabras inconvenientes del SAT y se rellena con X cuando faltan letras. La herramienta y la función de Python coinciden en los 30,000 casos.