Validadores de identificadores españoles para Kotlin Multiplatform: NIF, NIE, CIF, IBAN, cuenta bancaria, teléfono, código postal, matrícula, número de la Seguridad Social y CUPS.
Sin dependencias más allá de la biblioteca estándar de Kotlin, y con el mismo código corriendo en JVM, Android y navegador.
dependencies {
implementation("io.github.mendietagarciaalejandro:validadores-es:0.1.0")
}Lo escribí como base de un cliente multiplataforma que estoy montando: un alta de socio pide NIF, una factura pide CIF y una domiciliación pide IBAN, y todos esos algoritmos acaban copiándose de proyecto en proyecto. Al buscar algo parecido en Maven Central no encontré nada equivalente para Kotlin multiplataforma, así que lo saqué a librería.
Cada tipo se valida por su fábrica y devuelve un resultado que dice por qué falla, no solo si falla. Un formulario necesita eso para enseñar el mensaje correcto:
when (val resultado = Nif.validar(" 12345678-z ")) {
is Resultado.Valido -> println(resultado.valor) // 12345678Z
is Resultado.Invalido -> when (resultado.motivo) {
Motivo.Vacio -> "Escribe tu DNI"
Motivo.LongitudIncorrecta -> "Un DNI tiene 8 cifras y una letra"
Motivo.ControlIncorrecto -> "La letra no corresponde a ese número"
else -> "Revisa el DNI"
}
}Para el caso rápido hay un booleano:
if (Iban.esValido(entrada)) { /* ... */ }La entrada se normaliza sola: espacios, guiones, puntos y minúsculas dan igual.
| Tipo | Valida | Además |
|---|---|---|
Nif |
DNI con su letra | número y letra por separado |
Nie |
NIE de extranjeros | inicial X/Y/Z |
Cif |
identificador fiscal | tipo de organización |
Iban |
IBAN de cualquier país | formateado, y el CCC si es español |
Ccc |
cuenta bancaria de 20 dígitos | entidad, oficina, cuenta, y conversión a IBAN |
Telefono |
número español | móvil o fijo, con y sin prefijo |
CodigoPostal |
código postal | provincia a la que pertenece |
Matricula |
matrícula de vehículo | formato actual o provincial |
Nuss |
Seguridad Social | provincia y número |
Cups |
punto de suministro | distribuidora y punto frontera |
Un validador que devuelve Boolean no sirve para un formulario: si el usuario se equivoca
hay que decirle en qué. Los motivos son cinco a propósito, que son las categorías que
llevan a mensajes distintos. Más granularidad sería ruido para quien la use.
Nif, Iban y los demás tienen el constructor privado. Se llega a ellos pasando por
validar(), así que tener uno entre manos ya garantiza que es correcto y no hay que
volver a comprobarlo más adelante. Son value class, así que en tiempo de ejecución son
un String y no cuestan memoria.
Nada de data class: generaría un copy() público con el que fabricar un NIF inválido
saltándose la validación entera.
La librería es Kotlin puro y no toca ninguna API de la plataforma, así que el artefacto de JVM sirve para Android. Añadir un target aparte solo metería el SDK de Android como requisito para compilarla. Es lo mismo que hacen kotlinx-coroutines o kotlinx-serialization.
La gente escribe 12345678-Z, 12345678 z o ES91 2100 0418 4502 0005 1332. Todas son
la misma entrada, así que se limpia antes de validar.
Para decidir qué es una letra se comparan los rangos a mano en vez de usar
isLetterOrDigit(), que daría por buena la «Ñ» o una vocal acentuada. En un identificador
oficial eso no es una letra válida sino una errata.
El NIF saca la letra de "TRWAGMYFPDXBNJZSQVHLCKE"[numero % 23]. La tabla no es
alfabética, es la que fija la norma, y le faltan la I, la Ñ, la O y la U para que nadie las
confunda con el 1 y el 0 al leer un documento a mano.
El CIF usa el algoritmo de Luhn, el mismo de las tarjetas de crédito, pero escribe el control como número o como letra según el tipo de entidad: las sociedades usan dígito, los organismos públicos usan letra y un grupo intermedio admite las dos formas.
El IBAN se valida rotando el número, convirtiendo cada letra a dos cifras y comprobando
que el resto entre 97 es 1. Ese número tiene más de veinte dígitos y no cabe en un Long,
así que el resto se calcula arrastrándolo cifra a cifra, igual que se divide a mano. El 97
no es casual: está elegido para cazar el intercambio de dos cifras contiguas, que es la
errata más común al copiar un número largo.
La cuenta bancaria lleva dos dígitos de control que no son uno partido en dos: el primero protege entidad y oficina, el segundo el número de cuenta. Por eso un error al teclear la oficina no se compensa con otro en la cuenta.
El CUPS reutiliza la tabla de 23 letras del DNI, pero tomando el resto entre 529, que es 23 al cuadrado: el cociente elige la primera letra y el resto la segunda. Con 529 combinaciones, dos letras las representan todas exactamente.
./gradlew allTests
69 tests que corren en JVM y en WebAssembly, para asegurar que la lógica no depende de la plataforma. Los CIF de ejemplo son reales y comprobables (Telefónica, Inditex, la Complutense): con números inventados se corre el riesgo de que una tabla mal copiada cuadre por casualidad. De hecho me pasó al escribirlos, y los tests lo cazaron.
- Comprueba que un identificador está bien formado, no que exista. Un NIF puede ser válido y no corresponder a nadie.
- Las matrículas anteriores al año 2000 se validan solo por su forma, sin comprobar que la letra de provincia exista. Mantener esa lista al día no compensa para matrículas que ya no se emiten, y rechazar la de un clásico sería peor.
- La tabla de longitudes de IBAN cubre la zona SEPA. Con un país que no esté en ella se comprueba el control y la longitud general, pero no la exacta: prefiero eso a rechazar un IBAN que en realidad es correcto.
- No hay validador de correo electrónico. Hay demasiadas reglas contradictorias y lo único que confirma de verdad que una dirección existe es mandarle un correo.
Añadir el NIF de entidades no residentes y el código de cuenta de cotización, que son los dos que más me han faltado. Y publicar los targets de iOS cuando tenga con qué compilarlos.
MIT.