Skip to content

Instantly share code, notes, and snippets.

@rasputino
Last active August 30, 2024 09:47
Show Gist options
  • Save rasputino/380a34ab5d0bf18355431742fc4506e6 to your computer and use it in GitHub Desktop.
Save rasputino/380a34ab5d0bf18355431742fc4506e6 to your computer and use it in GitHub Desktop.
Validación NIF/CIF/NIE
using System;
using System.Text.RegularExpressions;
using System.Linq;
using System.Collections;
namespace FactuGrafic.Nucleo.Clases
{
public static class ValidacionesNIF
{
private enum TipoValidacion { NIF = 0, NIE = 1, CIF = 2}
/// <summary>
/// http://www.interior.gob.es/web/servicios-al-ciudadano/dni/calculo-del-digito-de-control-del-nif-nie
///
/// Cálculo del dígito de control del NIF/NIE
/// El artículo 11 del Real Decreto 1553/2005, de 23 de diciembre, establece que el Documento Nacional de Identidad recogerá el número personal del DNI
/// y carácter de verificación correspondiente al número de Identificación Fiscal.
/// Para verificar el NIF de españoles residentes mayores de edad, el algoritmo de cálculo del dígito de control es el siguiente:
/// Se divide el número entre 23 y el resto se sustituye por una letra que se determina por inspección mediante la siguiente tabla:
/// RESTO 0 1 2 3 4 5 6 7 8 9 10 11
/// LETRA T R W A G M Y F P D X B
/// RESTO 12 13 14 15 16 17 18 19 20 21 22
/// LETRA N J Z S Q V H L C K E
/// Por ejemplo, si el número del DNI es 12345678, dividido entre 23 da de resto 14, luego la letra sería la Z: 12345678Z.
/// Los NIE's de extranjeros residentes en España tienen una letra (X, Y, Z), 7 números y dígito de control.
/// Para el cálculo del dígito de control se sustituye:
/// X → 0
/// Y → 1
/// Z → 2
/// y se aplica el mismo algoritmo que para el NIF.
///
/// El CIF consta de 9 caracteres. El primero (posición 1) es una letra que sigue los siguientes criterios:
/// A.Sociedades anónimas.
/// B.Sociedades de responsabilidad limitada.
/// C.Sociedades colectivas.
/// D.Sociedades comanditarias.
/// E.Comunidades de bienes.
/// F.Sociedades cooperativas.
/// G.Asociaciones y fundaciones.
/// H.Comunidades de propietarios en régimen de propiedad horizontal.
/// J.Sociedades civiles.
/// N.Entidades no residentes.
/// P.Corporaciones locales.
/// Q.Organismos autónomos, estatales o no, y asimilados, y congregaciones e instituciones religiosas.
/// R.Congregaciones e instituciones religiosas (desde 2008, ORDEN EHA/451/2008)
/// S.Órganos de la Administración del Estado y comunidades autónomas
/// U.Uniones Temporales de Empresas.
/// V.Sociedad Agraria de Transformación.
/// W.Establecimientos permanentes de entidades no residentes en España
///
/// Las dos primeras indican la provincia.
///
/// Los cinco siguientes dígitos (posiciones 4 a 8) constituyen un número correlativo de inscripción de la organización en el registro provincial, y el último dígito (posición 9) es un código de control que puede ser un número o una letra:
/// Será una LETRA si la clave de entidad es P, Q, S o W.O también si los dos dígitos iniciales indican "No Residente"
/// Será un NÚMERO si la entidad es A, B, E o H.
/// Para otras claves de entidad: el dígito podrá ser tanto número como letra.
/// Las operaciones para calcular el dígito de control se realizan sobre los siete dígitos centrales y son las siguientes:
/// Sumar los dígitos de las posiciones pares.Suma = A
/// Para cada uno de los dígitos de las posiciones impares, multiplicarlo por 2 y sumar los dígitos del resultado.
/// Ej.: ( 8 * 2 = 16 --> 1 + 6 = 7 )
/// Acumular el resultado.Suma = B
/// Sumar A + B = C
/// Tomar sólo el dígito de las unidades de C.Lo llamaremos dígito E.
/// Si el dígito E es distinto de 0 lo restaremos a 10. D = 10 - E.Esta resta nos da D. Si no, si el dígito E es 0 entonces D = 0 y no hacemos resta.
/// A partir de D ya se obtiene el dígito de control.Si ha de ser numérico es directamente D y si se trata de una letra se corresponde con la relación:
/// J = 0, A = 1, B = 2, C= 3, D = 4, E = 5, F = 6, G = 7, H = 8, I = 9
///
/// </summary>
public static char CalculaDigitoDeControl(string nifNieCif)
{
if (string.IsNullOrWhiteSpace(nifNieCif))
throw new ArgumentException("El NIF/NIE/CIF no puede ser vacío");
if(nifNieCif.Length < 8 || nifNieCif.Length > 9)
throw new ArgumentException("El NIF/NIE/CIF debe tener al menos 8 caracteres + dígito de control");
nifNieCif = nifNieCif.ToUpperInvariant();
const string letrasNifNie = "TRWAGMYFPDXBNJZSQVHLCKE";
const string regExpEsNie = "^(X|Y|Z)";
bool esNie = Regex.Match(nifNieCif, regExpEsNie).Success;
const string regExpEsCif = "^[A-W]";
bool esCif = !esNie && Regex.Match(nifNieCif, regExpEsCif).Success;
if (esNie)
{
switch(nifNieCif[0])
{
case 'X': nifNieCif = ReemplazarPrimerCaracter(nifNieCif, "X", "0"); break;
case 'Y': nifNieCif = ReemplazarPrimerCaracter(nifNieCif, "Y", "1"); break;
case 'Z': nifNieCif = ReemplazarPrimerCaracter(nifNieCif, "Z", "2"); break;
}
}
int valorEntero = Convert.ToInt32(new String(nifNieCif.Where(Char.IsDigit).ToArray()));
if (esCif)
{
return CalculaDigitoControlCif(nifNieCif, valorEntero);
}
int restoDe23 = valorEntero % 23;
return letrasNifNie[restoDe23];
}
private static char CalculaDigitoControlCif(string nifNieCif, int valorEntero)
{
int valorEnteroCif = valorEntero;
if (valorEntero.ToString().Length.Equals(8)
|| (valorEntero.ToString().Length < 8 && nifNieCif[1].Equals('0') && Char.IsDigit(nifNieCif.Last())))
valorEnteroCif = valorEntero / 10;
var digitosValor = DameDigitos(valorEnteroCif);
int sumaPares =
digitosValor[1] +
digitosValor[3] +
digitosValor[5];
int sumaImparesPor2 =
SumaDigitos(digitosValor[0] * 2) +
SumaDigitos(digitosValor[2] * 2) +
SumaDigitos(digitosValor[4] * 2) +
SumaDigitos(digitosValor[6] * 2);
int suma = sumaPares + sumaImparesPor2;
int unidad = suma % 10;
int digitoCif = unidad == 0 ? unidad : 10 - unidad;
const string regExpEsSociedad = "^(A|B|E|H)";
bool esNumero = Regex.Match(nifNieCif, regExpEsSociedad).Success;
bool noResidente = valorEnteroCif < 100000;
const string regExpEsOrganismo = "^(K|P|Q|S|W)";
bool esOrganismo = Regex.Match(nifNieCif, regExpEsOrganismo).Success;
bool esLetra = esOrganismo || noResidente;
bool esIndefinido = !esLetra && !esNumero;
if (esNumero || esIndefinido)
{
return digitoCif.ToString().ToArray()[0];
}
else
{
const string letrasCIF = "JABCDEFGHI";
return letrasCIF[digitoCif];
}
}
public static bool Validar(string nifNieCif)
{
if (String.IsNullOrWhiteSpace(nifNieCif)) return false;
if (nifNieCif.Length < 8 || nifNieCif.Length > 9) return false;
return nifNieCif.Last().Equals(CalculaDigitoDeControl(nifNieCif));
}
private static string ReemplazarPrimerCaracter(string entrada, string reemplazable, string reemplazo)
{
var regex = new Regex(Regex.Escape(reemplazable));
return regex.Replace(entrada, reemplazo, 1);
}
private static int[] DameDigitos(int numero)
{
string temp = numero.ToString("0000000");
var resul = new int[temp.Length];
for (int i = 0; i < resul.Length; i++)
{
resul[i] = Convert.ToInt32(temp[i].ToString());
}
return resul;
}
private static int SumaDigitos(int v)
{
if (v < 5) return v;
return (v % 10) + (v / 10);
}
}
}
@rasputino
Copy link
Author

    [TestMethod]
    public void ValidarNifNieCif()
    {
        var nifsValidos = new string[] { "56746566T", "91311121V", "44316018D", "00080902B" };
        var niesValidos = new string[] { "Y7789182Q", "X0471969D", "Z5109432X" };
        var cifsValidos = new string[] { "G02377372", "A58818501", "G31740418", "C16567687", "P0200000H" };
        var nifsNoValidos = new string[] { "56746566M", "91311121", "80902B" };
        var niesNoValidos = new string[] { "Y7789182R", "X0471969", "5109432X" };
        var cifsNoValidos = new string[] { "A5881850A", "G3174041", "C16567677", "0200000H" };
        var entradasNoValidas = new string[] { "", null, "1234567", "1234567890", "AB1234567", "XXXXXXXXX" };

        foreach (string nif in nifsValidos)
        {
            Assert.IsTrue(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in niesValidos)
        {
            Assert.IsTrue(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in cifsValidos)
        {
            Assert.IsTrue(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in nifsNoValidos)
        {
            Assert.IsFalse(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in niesNoValidos)
        {
            Assert.IsFalse(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in cifsNoValidos)
        {
            Assert.IsFalse(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
        foreach (string nif in entradasNoValidas)
        {
            Assert.IsFalse(Nucleo.Clases.ValidacionesNIF.Validar(nif));
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment