Validate a CIF code (Spanish)  

Send By: Fernando P. Nájera Cano
Web : http://www.q3.nu
Email: j.najera@cgac.es
Date: 19/04/01

Tip accessed 6336 times

 


(Sorry, this tip is only valid for Spanish identification codes...)

El CIF de las empresas (Código de Indentificación Fiscal) está sujeto a unas normas y a un protocolo de verificación.
En este truco te mostramos ambas cosas, y una función Delphi para validar el código de control.

Un CIF debe tener 9 cifras.
La primera cifra (Una letra), indica el tipo de sociedad al que hace referencia, según esta tabla:


 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 otros tipos no definidos
 H - Comunidades de propietarios
 P - Corporaciones locales
 Q - Organismos autónomos
 S - Organos de la administración
 K, L y M - seguramente para compatibilidad con formatos antiguos
 X - Extranjeros, que en lugar del D.N.I. tienen el N.I.E.



La ultima cifra es el dígito de control, que puede ser o bien un número o bien una letra, en función del tipo de sociedad.
A las categorias P (Ayuntamientos) y X (Extranjeros) les corresponde una letra en lugar de un número.

El dígito de control se calcula con las 7 cifras restantes del CIF (quitando la primera y la ultima), con el siguiente algoritmo:

Vamos a usar este cif como ejemplo:


 CIF: "A58818501"




- Quitamos la primera y la ultima cifra:


 5881850



- Sumamos las cifras pares:


  Suma = 8 + 1 + 5 = 14



- Ahora sumamos cada cifra impar multiplicada por dos, y, sumamos las cifras del resultado:


  5 * 2 = 10 ==> 1 + 0 = 1
  8 * 2 = 16 ==> 1 + 6 = 7
  8 * 2 = 16 ==> 1 + 6 = 7
  0 * 2 = 0 ==> 0



- y volvemos a sumar esos resultados a la suma anterior:


 Suma:=Suma+1+7+7+0;



- Al final de este proceso, tenemos que Suma=29, pues bien, nos quedamos con la cifra de las unidades (9)

- Restamos esta cifra de las unidades de 10, dándonos un 1, que es el código de control para todos los tipos de sociedades exceptuando la X y la P.

- Para las sociedades X y P, habria que sumar un 64 al digito de control que hemos calculado para hallar el ASCII de la letra de control:


 Chr(64+ (10-(Suma mod 10)))




Por último, aqui tienes una función que incorpora todo el rollo anterior, que sirve para validar CIFs:



 procedure TForm1.Button1Click(Sender: TObject);

 function ValidaCIF(Cif: string):boolean;
 var
    Suma,
    Control   : integer;
    n         : byte;
 begin
   Result:=False;
   Cif:=UpperCase(Cif);
   {El cif debe ser de 9 cifras}
   if Length(Cif)=9 then
   begin
     Suma:= StrToInt(Cif[3])+
            StrToInt(Cif[5])+
            StrToInt(Cif[7]);
     for n:=1 to 4 do
     begin
       Suma:=Suma+ ( (2*StrToInt(Cif[2*n])) mod 10  )+
                   ( (2*StrToInt(Cif[2*n])) div 10 );
     end;
     if Pos(Cif[1],'XP')<>0
       then
         {Control tipo letra}
         Result:= ( Cif[9] = Chr(64+ (10-(Suma mod 10))) )
       else
         {Control tipo número}
         Result:= ( StrToInt(Cif[9]) = (10-(Suma mod 10)) );
   end;
 end;

 begin
   If ValidaCIF('A58818501') then ShowMessage('Válido')
                             else ShowMessage('No válido');

   {El mismo Cif para un ayuntamiento:}
 //  If ValidaCIF('P5881850A') then ShowMessage('Válido')
 //                            else ShowMessage('No válido');

 end;




Juan Serrano (jserrano@tudistrito.es) nos comentaba:

He encontrado un pequeño fallo para unos casos especiales. Por ejemplo, para el CI: B14507610 el valor final de (10 - (Suma mod 10)) es 10. Con lo que el resultado de salida de la función sería False, pero este CIF es correcto.

Solo habría que usar la variable Control que no es usada en todo el procedimiento:

 procedure TForm1.Button1Click(Sender: TObject);

 function ValidaCIF(Cif: string):boolean;
 var Suma, Control : integer;
        n : byte;
 begin
 Result:=False;
 Cif:=UpperCase(Cif);
 {El cif debe ser de 9 cifras}
 if Length(Cif)=9 then
    begin
         Suma:= StrToInt(Cif[3])+
                          StrToInt(Cif[5])+
                          StrToInt(Cif[7]);
         for n:=1 to 4 do
                 begin
                         Suma:=Suma+ ( (2*StrToInt(Cif[2*n])) mod 10 )+
                                         ( (2*StrToInt(Cif[2*n])) div 10 );
                 end;
         Control := 10-(Suma mod 10);
         if Pos(Cif[1],'XP')<>0
                 then
                         {Control tipo letra}
                         Result:= ( Cif[9] = Chr(64+ Control))
                 else
                     begin
                         {Control tipo número}
                         if Control =10 then Control := 0;
                         Result:= ( StrToInt(Cif[9]) = Control);
                    end;
    end;
 end;
 begin
         If ValidaCIF('A58818501') then ShowMessage('Válido')
                 else ShowMessage('No válido');
         {El mismo Cif para un ayuntamiento:}
         // If ValidaCIF('P5881850A') then ShowMessage('Válido')
         // else ShowMessage('No válido');
 end;




Enviado por Manuel Castaño (mcastano@teleline.es)

Según varios NIF extranjeros (que comienzan por X) el calculo es similar al de los NIF individuales segun el resto de dividir entre 23.
Os remito mi funcion en FoxPro:


 *****************************************************************************
 FUNCTION LEE (ACTNIF)
 *****************************************************************************
 STORE "TRWAGMYFPDXBNJZSQVHLCKE" TO TABLA
 STORE .T. TO ERROR
 STORE ACTNIF TO LENIF
 STORE " " TO LETRA
 STORE 0 TO DNI
 STORE 0 TO NAUX
 STORE 0 TO SUMA
 STORE "CORRECTO*"+ACTNIF TO RDONIF
 * limpiamos el nif de caracteres no deseados
 LENIF=STRTRAN(LENIF,"-","")
 LENIF=STRTRAN(LENIF,"/","")
 LENIF=UPPER(ALLTRIM(LENIF))
 IF ISALPHA(LENIF)
          LETRA=UPPER(SUBSTR(LENIF,1,1))
          IF LETRA$"ABCDEFGHPQSKLM"
                 IF LEN(LENIF)=9 THEN
                    SUMA=VAL(SUBSTR(LENIF,3,1))+VAL(SUBSTR(LENIF,5,1))+VAL(SUBSTR(LENIF,7,1))
                    *SUMAMOS CIFRAS EN LUGARES IMPARES Y PARES POR 2N
                    FOR I=1 TO 4
                            SUMA = SUMA+ MOD(2*VAL(SUBSTR(LENIF,2*I,1)),10)+ INT(2*VAL(SUBSTR(LENIF,2*I,1))/10)
                    NEXT I
                    *SI LAS LETRAS SON X O P ACABA EN LETRA QUE SE CALCULA ASI
                    IF LETRA$"PQ" THEN
                          IF SUBSTR(LENIF,9,1)=CHR(64+(10-MOD(SUMA,10))) THEN
                            STORE .F. TO ERROR
                          ELSE
                                 RDONIF="ERROR*"
                          ENDIF
                    *SI NO SON LAS LETRAS DE ARRIBA COMPROBAMOS
                    ELSE
                          IF MOD(10-MOD(SUMA,10),10)=VAL(SUBSTR(LENIF,9,1)) THEN
                            STORE .F. TO ERROR
                          ELSE
                                 RDONIF="ERROR*"
                          ENDIF
                    ENDIF
                 ELSE
                         *ERROR EN LA LONGITUD DEL NIF
                         RDONIF="ERROR*"
                 ENDIF
          ELSE
            IF LETRA="X" THEN
                   LETRA=RIGHT(LENIF,1)
                   DNI=VAL(SUBSTR(LENIF,2,8))
                   STORE MOD(DNI,23) TO NAUX
                   IF LETRA=SUBSTR(TABLA,NAUX+1,1)
                           STORE .F. TO ERROR
                           IF LEN(LENIF)<9
                                 LENIF="X"+replicate("0",9-len(alltrim(LENIF)))+alltrim(SUBSTR(LENIF,2,8))
                                 STORE .T. TO ERROR
                         RDONIF="ERROR*"+LENIF
                          ENDIF
                  ELSE
                          STORE MOD(DNI,23) TO NAUX
                          STORE "         " TO LENIF
                          STORE "X"+RTRIM(LTRIM(STR(INT(DNI))+SUBSTR(TABLA,NAUX+1,1))) TO LENIF
                          IF LEN(LENIF)<9
                                 LENIF="X"+replicate("0",9-len(alltrim(LENIF)))+alltrim(SUBSTR(LENIF,2,8))
                          ENDIF
                      RDONIF="ERROR*"+LENIF
                  ENDIF

            ELSE
                         *NO COMIENZA POR LETRA VALIDA
                     RDONIF="ERROR*"
            ENDIF
          ENDIF
 ELSE
          LETRA=RIGHT(LENIF,1)
          LETRA=UPPER(LETRA)
          DNI=VAL(LENIF)
          STORE MOD(DNI,23) TO NAUX
          IF LETRA=SUBSTR(TABLA,NAUX+1,1)
                  STORE .F. TO ERROR
                  IF LEN(LENIF)<9
                         LENIF=replicate("0",9-len(alltrim(LENIF)))+alltrim(LENIF)
                         STORE .T. TO ERROR
                 RDONIF="ERROR*"+LENIF
                  ENDIF
          ELSE
                  STORE MOD(DNI,23) TO NAUX
                  STORE "         " TO LENIF
                  STORE RTRIM(LTRIM(STR(INT(DNI))+SUBSTR(TABLA,NAUX+1,1))) TO LENIF
                  IF LEN(LENIF)<9
                         LENIF=replicate("0",9-len(alltrim(LENIF)))+alltrim(LENIF)
                  ENDIF
              RDONIF="ERROR*"+LENIF
          ENDIF
 ENDIF


 RETURN(RDONIF)



(Si alguien se anima a probarla y a pasarla a Delphi, pues que me la envie...)


Enviado por: Jose Vte. Vergara (jvergara@aaa.upv.es)

Ante todo, ehorabuena porque TRUCOMANIA es de lo mejor que hay.

Me llamo Jose, programador de profresión y Licenciado en Informática en mis ratos libres que son muy pocos :(

Uso el DELPHI como es de suponer y a la hora de necesitar algún truco uso los de TRUCOMANIA.

El caso que me ataña es que he ido a usar el truco de la Validación del CIF y me ha dado problemas en la validacion de CIF's de extranjeros (X), ya que no me valida correctamente ninguno.
Indagando un poco (o sea haciendo pruebas y comprobando resultados erroneos) me he dado cuenta que para validar el CIF de un extranjero hay que comprobar que el número y la letra restantes de eliminar la X del CIF corresponde con un NIF correcto (poniendo un cero a la izquierda claro).

O sea, en un ejemplo si tenemos el el CIF--> X1234567L deberemos comprobar que 01234567L es un NIF correcto.

He llegado ha esta conclusion usando algunos CIF's de extranjeros reales.

Espero que puedas confirmar si es correcto, ya que yo aún no lo he podido hacer (aunque sigo utilizando esta variación).

Otra cosa, realizando pruebas me ha surgido la duda de si la validación de CIF tipo P es correcta (ya que me fallaba la X), ya que si la variable
control sólo puede tomar valores de 0 a 9, el caracter de control del CIF tipo P estará entre la 'A' y la 'J' ¿es correcto?

Adjunto modificacion:


 procedure TForm1.Button1Click(Sender: TObject);

 function ValidaCIF(Cif: string):boolean;
 var Suma, Control : integer;
        n : byte;
 begin
 Result:=False;
 Cif:=UpperCase(Cif);
 {El cif debe ser de 9 cifras}
 if Length(Cif)=9 then
    begin
       { varicaión para CIF tipo X }
       if (Cif[1] = 'X') then
         begin
           { extranjero calculamos el NIF                                                    }
           { usamos la función NIF del truco [9] que calula la letra del DNI }
           result := NIF(copy(cif,2,7))=Cif[9];
         end
       else
         begin
           Suma := StrToInt(Cif[3]) +
             StrToInt(Cif[5]) +
             StrToInt(Cif[7]);
           for n := 1 to 4 do
             begin
               Suma := Suma + ((2 * StrToInt(Cif[2 * n])) mod 10) +
                 ((2 * StrToInt(Cif[2 * n])) div 10);
             end;
           Control := 10 - (Suma mod 10);

           if Cif[1] = 'P' then
             {Control tipo letra}
             begin
               Result := (Cif[9] = Chr(64 + Control))
             end
           else
             begin
               {Control tipo número}
               if Control = 10 then Control := 0;
               Result := (StrToInt(Cif[9]) = Control);
             end;
         end;
     end;
 end;
 begin
         if ValidaCIF('X1234567L') then ShowMessage('Válido')
                 else ShowMessage('No válido');
         // if ValidaCIF('A58818501') then ShowMessage('Válido')
         //        else ShowMessage('No válido');
         {El mismo Cif para un ayuntamiento:}
         // if ValidaCIF('P5881850A') then ShowMessage('Válido')
         // else ShowMessage('No válido');
 end;




Bien, parece que alguien se animó a meterse con la función Fox:

Enviado por: David Herrero (dherrero@cisspraxis.es)

Hola, indicabas en el truco 337 que si alguien se atrevía a probar y pasar una rutina que das en FoxPro a Delphi que te la hiciese llegar... pues bien, aquí hay un loco que lo ha hecho... yo, te la paso:


 {--------------------------------------------------------------------------}
 {- Inicio de la función Lee -----------------------------------------------}
 {--------------------------------------------------------------------------}

 Function Lee( ActNIF : String ) : String;

 const
   Tabla = 'TRWAGMYFPDXBNJZSQVHLCKE';

 var
   Error : Boolean;
   LeNIF, RDONIF, Letra : String;
   DNI, nAux, Suma, I : Integer;

   {--------------------------------------------------------------}

   Function StrTran( cTexto, cCharOrg, cCharChg : String ): String;
   var
     nLoop : Integer;

   begin
     for nLoop := 1 to length( cTexto ) do
       if copy( cTexto, nLoop, 1 ) = cCharOrg then
         Result := Result + cCharChg
       else
         Result := Result + copy( cTexto, nLoop, 1 );
   end;

   {--------------------------------------------------------------}

   Function IsAlpha( cNif : String ) : Boolean;
   begin
     cNif := UpperCase( cNif );
     Result := ( ( copy( cNif, 1, 1 ) >= 'A' ) and ( copy( cNif, 1, 1 ) <= 'Z' ) );
   end;

   {--------------------------------------------------------------}

   Function Replicate( cCaracter : String; nVeces : Integer ) : String;
   begin
     repeat
       Result := Result + cCaracter;
       nVeces := nVeces - 1;
     until nVeces <= 0;
   end;

   {--------------------------------------------------------------}

 begin
   Error := True;
   LENif := ActNif;
   Letra := ' ';
   Dni := 0;
   nAux := 0;
   Suma := 0;
   RDONIF := 'CORRECTO*' + ActNIF;
   { limpiamos el nif de caracteres no deseados }

   LeNIF := StrTran( LeNif, '-', '' );
   LeNIF := StrTran( LeNif, '/', '' );
   LeNIF := UpperCase( Trim( LeNif ) );

   if IsAlpha( LeNif )then
     begin
       Letra := UpperCase( Copy( LeNif, 1, 1 ) );
       if pos( Letra, 'ABCDEFGHPQSKLM' ) <> 0 then
         begin
           if Length( LeNif ) = 9 then
             begin
               Suma := StrToInt( Copy( LeNIF, 3, 1 ) ) + StrToInt( Copy( LeNIF, 5, 1 ) ) +
 	                     StrToInt( Copy( LeNIF, 7, 1 ) );

               { SUMAMOS CIFRAS EN LUGARES IMPARES Y PARES POR 2N }

               For i := 1 to 4 do
                 begin
                   Suma := Suma + ( ( 2 * StrToInt( Copy( LeNIF, 2 * I, 1 ) ) ) mod 10 );
                   Suma := Suma + ( ( 2 * StrToInt( Copy( LeNIF, 2 * I, 1 ) ) ) div 10 );
                 end;

               { *SI LAS LETRAS SON X O P ACABA EN LETRA QUE SE CALCULA ASI }

               if Pos( Letra, 'PQ' ) <> 0 then
                 if Copy( LeNIF, 9, 1) = CHR( 64 + ( 10 - ( Suma mod 10 ) ) ) then
                   Error := False
                 else
                   RDONIF := 'ERROR*'
               else    { SI NO SON LAS LETRAS DE ARRIBA COMPROBAMOS }
                 begin
                   if ( ( 10 - ( SUMA mod 10 ) ) mod 10 ) = StrToInt( Copy( LeNIF, 9, 1 ) ) then
                     Error := False
                   else
                     RDONIF := 'ERROR*';
                 end;
             end
           else
             RDONIF := 'ERROR*'; { *ERROR EN LA LONGITUD DEL NIF }
         end
       else
         if Letra = 'X' then
           begin
             Letra := Copy( LeNIF, 1, 1 );
             DNI := StrToInt( Copy( LeNIF, 2, 8 ) );
             nAux := DNI mod 23;
             if Letra = Copy( Tabla, nAux + 1, 1 ) then
               begin
                 Error := False;
                 if length( LeNif ) < 9 then
                   begin
                     LeNIF := 'X' + Replicate( '0', 9 - length( trim( LeNIF ) ) ) +
                              trim( copy( LeNIF, 2, 8 ) );
                     ERROR := True;
                     RDONIF := 'ERROR*' + LeNIF;
                   end;
               end
             else
               begin
                 nAux := DNI mod 23;
                 LeNif := Replicate( ' ', 9 );
                 LeNif := 'X' + IntToStr( DNI ) + Copy( TABLA, nAux + 1, 1 );
                 if length( LeNIF ) < 9 then
                   LeNIF := 'X' + replicate( '0', 9 - length( trim( LeNIF ) ) ) +
                            trim( Copy( LeNIF, 2, 8 ) );
                 RDONIF := 'ERROR*' + LeNIF;
               end;
           end
         else         { *NO COMIENZA POR LETRA VALIDA }
           RDONIF := 'ERROR*';
     end
   else
     begin
       Letra := UpperCase( Copy( LeNif, 1, 1 ) );
       DNI := StrToInt( LeNIF );
       nAux := DNI mod 23;
       if Letra = Copy( Tabla, nAux + 1, 1 ) then
         begin
           Error := False;
           if Length( LeNIF ) < 9 then
             begin
               LeNIF := Replicate( '0', 9 - length( trim( LeNIF ) ) ) + trim( LeNIF );
               Error := True;
               RDONIF := 'ERROR*' + LeNIF;
             end
           else
             begin
               nAux := DNI mod 23;
               LeNif := Replicate( ' ', 9 );
               LeNif := IntToStr( DNI ) + Copy( Tabla, nAux + 1, 1 );
               if Length( LeNif ) < 9 then
                 LeNif := Replicate( '0', 9 - length( trim( LeNIF ) ) ) + trim( LeNIF );
               RDONIF := 'ERROR*' + LENIF;
             end;
         end;
     end;
   Result := RdoNIF;
 end;

 {--------------------------------------------------------------------------}
 {- Fin de la función Lee --------------------------------------------------}
 {--------------------------------------------------------------------------}




Enviado por: José Antonio García Navarro (enetesystems@wanadoo.es)

Uniendo todos los mensajes aparecidos hasta el momento sobre el tema de la validación del CIF, he hecho un algoritmo entre todos ellos, que creo que funciona para todos los casos. Algunas de las funciones utilizadas están en Trucomanía.


 {Cambiar un carácter por otro en una cadena.}
 function CadCambioCar(Cadena, CarOrig, CarCambio : String) : String;
 var
   i    : Integer;
   Temp : String;
 begin
   Temp := '';
   for i := 1 to Length(Cadena) do
     if Copy(Cadena, i, 1) = CarOrig then
       Temp := Temp + CarCambio
     else
       Temp := Temp + Copy(Cadena, i, 1);
    Result := Temp;
 end;


 { Validar si un CIF introducido es correcto}
 function EsCif(Cif : String) : Boolean;
 var
    Suma, Control : Integer;
    n             : Byte;
 begin
   Result := False;

   {Se pasa todo a mayúsculas limpio de espacios y de caracteres especiales}
   Cif := UpperCase(Trim(Cif));

   {Se limpia de los caracteres '-' y '/'. }
   Cif := CadCambioCar(Cif,'-','');
   Cif := CadCambioCar(Cif,'/','');

   {El cif debe ser de 9 cifras}
   if Length(Cif) = 9 then
   begin
     {Comprobamos que sea un NIF}
     if EsNumero(Cif[1]) then
       Result := EsNif(Cif)
     else
       {Se comprueba que la letra que designa el tipo de cif sea correcta}
       if (Pos(Cif[1], 'ABCDEFGHPQSKLMX') = 0) then
         Result := False
       else
         {Se comprueba si es un extranjero,
          en ese caso se calcula el nif, cambiando la X, por 0}
         if Cif[1] = 'X' then
           Result := EsNif('0'+Copy(Cif,2,8))
         else
         begin
           Suma:= StrToInt(Cif[3])+StrToInt(Cif[5])+StrToInt(Cif[7]);
           for n := 1 to 4 do
             Suma := Suma + ((2*StrToInt(Cif[2*n])) mod 10)+((2*StrToInt(Cif[2*n])) div 10);
           Control := 10 - (Suma mod 10);
           {Se comprueba si es de tipo 'P' o 'S', es decir,
            Corporaciones Locales (Ayuntamientos, etc.)
            y Organismos públicos.}
           if Pos(Cif[1],'PS')<>0 then
             {Control tipo letra}
             Result := (Cif[9] = Chr(64+Control))
           else
           {Resto de tipos de CIF}
           begin
             {Control tipo número}
             if Control = 10 then
               Control := 0;
             Result:= ( StrToInt(Cif[9]) = Control);
           end;
         end;
   end;
 end;