Last active
February 8, 2024 19:52
-
-
Save donal56/bf43f0a2cef5eaeeb6af3e7ba378fc8d to your computer and use it in GitHub Desktop.
Validar una CURP con base en datos personales
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
_debug = true; | |
/* | |
* Validar una curp con base en datos personales | |
* | |
* @param {Object} persona - Datos de la persona | |
* nombre {String} | |
* apellidoPaterno {String} | |
* apellidoMaterno {String|null} | |
* fechaNacimiento {Date} | |
* entidadFederativaNacimiento {String} - De acuerdo al acuerdo del DOF Torno DXVII No. 17 del 23/10/1996 | |
* sexo {String} - M o F | |
* @param {String} curp - CURP a comparar | |
* @param {Boolean} validarHomoclave - Si es falso, se calcularan los primeros 16 carácteres y se verificara que coincidan con la curp provista. | |
* Si es verdadero, despues del anterior paso se comprobara la homoclave de la curp provista con el algoritmo del dígito verificador. | |
* @return {Boolean} valido o no | |
*/ | |
function validarCurp(persona = {}, curp = '', validarHomoclave = true) { | |
// Constantes | |
// Algunos datos recuperados del anexo 2 y 4 de http://www.ordenjuridico.gob.mx/Federal/PE/APF/APC/SEGOB/Instructivos/InstructivoNormativo.pdf | |
const tamanioCurp = 18; | |
const vocales = ['A', 'E', 'I', 'O', 'U']; | |
const entidadExtranjera = 'NE'; | |
const excepcionesNombresMujeres = ['MARIA']; | |
const excepcionesNombresHombres = ['JOSE']; | |
const preposicionesComunes = ["DE LA", "DE LOS", "DEL", "DE"]; | |
const palabrasInconvenientes = ['BACA', 'LOCO', 'LOCA', 'BAKA', 'LOKA', 'BUEI', 'LOKO', 'BUEY', 'MAME', 'CACA', 'MAMO', 'CACO', 'MEAR', 'CAGA', 'MEAS', 'CAGO', 'MEON', 'CAKA', 'MIAR', 'CAKO', 'MION', 'COGE', 'MOCO', 'COGI', 'MOKO', 'COJA', 'MULA', 'COJE', 'MULO', 'COJI', 'NACA', 'COJO', 'NACO', 'COLA', 'PEDA', 'CULO', 'PEDO', 'FALO', 'PENE', 'FETO', 'PIPI', 'GETA', 'PITO', 'GUEI', 'POPO', 'GUEY', 'PUTA', 'JETA', 'PUTO', 'JOTO', 'QULO', 'KACA', 'RATA', 'KACO', 'ROBA', 'KAGA', 'ROBE', 'KAGO', 'ROBO', 'KAKA', 'RUIN', 'KAKO', 'SENO', 'KOGE', 'TETA', 'KOGI', 'VACA', 'KOJA', 'VAGA', 'KOJE', 'VAGO', 'KOJI', 'VAKA', 'KOJO', 'VUEI', 'KOLA', 'VUEY', 'KULO', 'WUEI', 'LILO', 'WUEY']; | |
const entidadesFederativas = ['AS', 'BC', 'BS', 'CC', 'CL', 'CM', 'CS', 'CH', 'DF', 'DG', 'GT', 'GR', 'HG', 'JC', 'MC', 'MN', 'MS', 'NT', 'NL', 'OC', 'PL', 'QT', 'QR', 'SP', 'SL', 'SR', 'TC', 'TS', 'TL', 'VZ', 'YN', 'ZS']; | |
// Funciones | |
// La letra Ñ se reemplaza por una X | |
const estaVacio = cadena => cadena == null || cadena.toString().trim() == ''; | |
const limpiar = cadena => cadena.toUpperCase().replace('Ñ', 'X').normalize("NFD").replace(/[\u0300-\u036f]/g, ""); | |
const primeraVocalInterna = function(cadena) { | |
const auxCadena = cadena.toUpperCase().substring(1, cadena.length); | |
for(indice in auxCadena.split('')) { | |
const letra = auxCadena[indice]; | |
if(vocales.includes(letra)) return letra; | |
} | |
return 'X'; | |
}; | |
const primerConsonanteInterna = function(cadena) { | |
const auxCadena = cadena.toUpperCase().substring(1, cadena.length); | |
for(indice in auxCadena.split('')) { | |
const letra = auxCadena[indice]; | |
if(!vocales.includes(letra)) return letra; | |
} | |
return 'X'; | |
}; | |
const quitarPrimerNombre = function(nombreCompuesto, listaNegra = []) { | |
let nombres = nombreCompuesto.trim().split(/\s+/); | |
// No es un nombre compuesto | |
if(nombres.length <= 1) return nombreCompuesto; | |
let primerNombre = nombres[0].toUpperCase().trim(); | |
listaNegra.forEach(function(nombreLista) { | |
if(primerNombre == nombreLista) { | |
nombres = nombres.splice(1, nombres.length); | |
nombreCompuesto = nombres.join(" "); | |
} | |
}); | |
return nombreCompuesto; | |
}; | |
const quitarPreposiciones = function(cadena, listaNegra = []) { | |
cadena = cadena.trim().toUpperCase(); | |
listaNegra.forEach(function(nombreLista) { | |
if(cadena.startsWith(nombreLista + " ")) { | |
cadena = cadena.substring(nombreLista.length + 1, cadena.length); | |
} | |
}); | |
return cadena; | |
} | |
// Validación de entradas | |
let {nombre, apellidoPaterno, apellidoMaterno, fechaNacimiento, entidadFederativaNacimiento, sexo} = persona; | |
if(estaVacio(nombre)) throw 'El nombre de la persona es un parámetro requerido'; | |
if(estaVacio(apellidoPaterno)) throw 'El apellido paterno de la persona es un parámetro requerido'; | |
if(estaVacio(entidadFederativaNacimiento)) throw 'La entidad federativa de nacimiento de la persona es un parámetro requerido'; | |
if(estaVacio(sexo) || !['M', 'F'].includes(sexo.toUpperCase())) throw 'El sexo de la persona es un parámetro requerido (M o F)'; | |
if(fechaNacimiento == null || !['Date', 'Moment'].includes(fechaNacimiento.constructor.name)) throw 'La fecha de nacimiento de la persona es un parámetro requerido'; | |
// Limpieza de entradas | |
// Cuando una persona no cuenta con segundo apellido se utliza X en donde se requiere | |
nombre = quitarPrimerNombre(limpiar(nombre), sexo == 'M' ? excepcionesNombresHombres : excepcionesNombresMujeres); | |
apellidoPaterno = quitarPreposiciones(limpiar(apellidoPaterno), preposicionesComunes); | |
apellidoMaterno = estaVacio(apellidoMaterno) ? 'XXXXXXXX' : quitarPreposiciones(limpiar(apellidoMaterno), preposicionesComunes); | |
entidadFederativaNacimiento = entidadFederativaNacimiento.trim(); | |
sexo = sexo.toUpperCase(); | |
fechaNacimiento = fechaNacimiento.constructor.name == 'Moment' ? fechaNacimiento.toDate() : fechaNacimiento; | |
nombre = nombre.split(" ").filter(x => !preposicionesComunes.includes(x)).join(" "); | |
// Variables auxiliares | |
let curpComputada = new Array(tamanioCurp - 2); | |
curpComputada.fill(''); | |
Object.seal(curpComputada); | |
// 1.- Primera letra del primer apellido | |
curpComputada[0] = apellidoPaterno[0]; | |
// 2.- Primera vocal del primer apellido despues de la primer letra | |
curpComputada[1] = primeraVocalInterna(apellidoPaterno); | |
// 3.- Primera letra del segundo apellido | |
curpComputada[2] = apellidoMaterno[0]; | |
// 4.- Primera letra del nombre de pila. Se tomara en cuenta el segundo nombre si es un nombre compuesto y el primer nombre forma parte de la lista de excepciones | |
curpComputada[3] = nombre[0]; | |
// 5-10.- Fecha de nacimiento sin espacios en orden de año (dos dígitos), mes y día | |
const fechaComprimida = (fechaNacimiento.getYear() > 99 ? fechaNacimiento.getYear() -100 : fechaNacimiento.getYear()).toString().padStart(2, '0') + (fechaNacimiento.getMonth() + 1).toString().padStart(2, '0') + fechaNacimiento.getDate().toString().padStart(2, '0'); | |
curpComputada[4] = fechaComprimida[0]; | |
curpComputada[5] = fechaComprimida[1]; | |
curpComputada[6] = fechaComprimida[2]; | |
curpComputada[7] = fechaComprimida[3]; | |
curpComputada[8] = fechaComprimida[4]; | |
curpComputada[9] = fechaComprimida[5]; | |
// 11.- Letra del sexo o género (H para Hombre, o M para Mujer) | |
curpComputada[10] = sexo == 'M' ? 'H' : 'M'; | |
// 12-13.- Dos letras correspondientes a la entidad federativa de nacimiento (en caso de haber nacido fuera del país, se marca como NE, «Nacido en el Extranjero» | |
const claveEntidadNacimiento = entidadesFederativas.includes(entidadFederativaNacimiento) ? entidadFederativaNacimiento : entidadExtranjera; | |
curpComputada[11] = claveEntidadNacimiento[0]; | |
curpComputada[12] = claveEntidadNacimiento[1]; | |
// 13.-Primera consonante interna (después de la primera letra) del primer apellido. | |
curpComputada[13] = primerConsonanteInterna(apellidoPaterno); | |
// 14.- Primera consonante interna del segundo apellido. | |
curpComputada[14] = primerConsonanteInterna(apellidoMaterno); | |
// 15.- Primera consonante interna del nombre de pila. | |
// Solo tomar el primer nombre. P/E: El nombre 'Noé Matías' debe retornar X, no M | |
curpComputada[15] = primerConsonanteInterna(nombre.split(" ")[0]); | |
// 16.- Diferenciador de homonimia. Dígito del 0 al 9 para fechas de nacimiento hasta el año 1999 y de la A a la Z para fechas de nacimiento a partir del 2000. Primera mitad de la homoclave | |
// 17.- Dígito verificador, para comprobar la integridad. Segunda mitad de la homoclave. | |
// Excepciones | |
let curpComputadaFinal = curpComputada.join('').toUpperCase(); | |
// Se remplaza la segunda letra si las primeras 4 letras forman una palabra altisonante o 'inconveniente' | |
if(palabrasInconvenientes.includes(curpComputadaFinal.substr(0, 4))) { | |
curpComputadaFinal = curpComputadaFinal.charAt(0) + 'X' + curpComputadaFinal.substr(2, tamanioCurp - 4); | |
} | |
// Primera validación | |
if(curpComputadaFinal.length != (tamanioCurp - 2)) throw 'Ocurrio un error durante la validación de la CURP'; | |
const supuestaCurp = curp.substring(0, 16).toUpperCase(); | |
const primeraValidacion = curpComputadaFinal === supuestaCurp && curp.length == tamanioCurp; | |
if(_debug) console.debug('CURP generada: ' + curpComputadaFinal, primeraValidacion); | |
if(!validarHomoclave || !primeraValidacion) return primeraValidacion; | |
// Segunda validación | |
const regexHomonimia = fechaNacimiento.getFullYear() >= 2000 ? /[A-Z]/m : /[0-9]/m; | |
const identificadorHomonimia = curp.charAt(tamanioCurp - 2); | |
const segundaValidacion = regexHomonimia.exec(identificadorHomonimia) !== null | |
if(_debug) console.debug('Identificador de homonimía: ' + identificadorHomonimia, segundaValidacion); | |
if(!segundaValidacion) return segundaValidacion; | |
// Tercera validación | |
const caracteres = '0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ'; | |
const suma = (supuestaCurp + identificadorHomonimia).split('').reduce((acc, el, ix, arr) => (tamanioCurp - ix) * caracteres.indexOf(el) + acc, 0); | |
const modulo = suma % 10; | |
const digitoVerificador = modulo != 0 ? 10 - modulo : modulo; | |
const terceraValidacion = curp.charAt(tamanioCurp - 1) == digitoVerificador; | |
if(_debug) console.debug('Dígito verificador: ' + digitoVerificador, terceraValidacion); | |
return terceraValidacion; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment