lunes, 25 de marzo de 2013

Cálculo del dígito verificador utilizando la función Módulo11

El código de control es un mecanismo de detección de errores utilizado para verificar la corrección de un dato, generalmente en soporte informático [1]. Los dígitos de control o dígitos verificadores se usan principalmente para detectar errores en el tecleo o transmisión de los datos. Generalmente consisten en uno o más caracteres numéricos o alfabéticos añadidos al dato original y calculados a partir de éste mediante un determinado algoritmo. Algunos de los ejemplos de uso frecuentes son los números de identificación personal, códigos de barras, tarjetas de crédito y códigos bancarios, como es el caso del Sistema de Rentas Internas (SRI) del Ecuador que utiliza el dígito de verificación como parte de su clave de acceso dentro del proceso de solicitud de números de autorización utilizando el mecanismo del Módulo 11.
Módulo 11
En el caso del SRI el dígito verificador será aplicado sobre toda la clave de acceso (48 dígitos) y deberá ser incorporado por el contribuyente a través del método denominado Módulo 11, con un factor de chequeo ponderado (2), este mecanismo de detección de errores, será verificado al momento de la recepción del comprobante. Cuando el resultado del dígito verificador obtenido sea igual a once (11), el digito verificador será el cero (0) y cuando el resultado del dígito verificador obtenido sea igual a diez 10, el digito verificador será el uno (1).

Como podemos ver en la figura siguiente, el dígito verificador forma parte de la estructura que debemos enviarle al WS del SRI. Para este ejemplo les mostraré una manera de implementar dicha función a través de un método en Java.


El algoritmo deberá realizar el siguiente procedimiento:

public class Modulo11 {

    public String invertirCadena(String cadena) {
        String cadenaInvertida = "";
        for (int x = cadena.length() - 1; x >= 0; x--) {
            cadenaInvertida = cadenaInvertida + cadena.charAt(x);
        }
        return cadenaInvertida;
    }

    public int obtenerSumaPorDigitos(String cadena) {
        int pivote = 2;
        int longitudCadena = cadena.length();
        int cantidadTotal = 0;
        int b = 1;
        for (int i = 0; i < longitudCadena; i++) {
            if (pivote == 8) {
                pivote = 2;
            }
            int temporal = Integer.parseInt("" + cadena.substring(i, b));
            b++;
            temporal *= pivote;
            pivote++;
            cantidadTotal += temporal;
        }
        cantidadTotal = 11 - cantidadTotal % 11;
        return cantidadTotal;
    }

    public static void main(String args[]) throws Exception {
        Modulo11 a = new Modulo11();
        System.out.println(a.obtenerSumaPorDigitos(a.invertirCadena("41261533")));
    }
}
Espero que esta explicación les sea de utilidad y cualquier consulta no duden en comentarla. Saludos.

Y para los interesados, brindamos asesoría en el tema de facturación electrónica. El temario del taller lo pueden descargar aquí: Temario del Taller.

24 comentarios:

  1. Gracias me sirve pero pana, una pregunta que no entiendo todavia de donde sale la cadena de verificacion.,41261533 ?

    ResponderEliminar
  2. Creo que falta incluir algo como esto en el método obtenerSumaPorDigitos, antes del return cantidadTotal

    switch (cantidadTotal) {
    case 10:
    cantidadTotal = 1;
    break;
    case 11:
    cantidadTotal = 0;
    break;
    }

    ResponderEliminar
  3. Saludos, esa cadena es uno de los tantos numeros, llamados claves que pueden ser para pruebas o para producción,que el SRI entrega al contribuyente

    ResponderEliminar
  4. Hola Rolando:
    Grcias por la información, sin embargo me surge la siguiente duda los números base, se multiplican iniciando con 2,3,4, hasta 7, e inicia nuevamente la cadena al llegar a 7, me surgen las siguientes interrogantes:
    ¿Porque después de 7 no se continua con 8,y 9?
    ¿Existe una regla universal para el uso del Modulo 11?
    ¿Utilizando está formula en varios números, solamente valido 3 números de 6)
    ¿Pero si utilizo el número 8 después del 7 valida todos los números, 6 en total?
    ¿Es esto correcto?
    Agradeciendo de antemano las respuestas a las interrogantes planteadas, gracias anticipadas.

    Hugo R.González B.

    ResponderEliminar
  5. La cadena a validar son los 49 dígitos del contenido del campo ClaveAcceso específicamente los 48 primeros (que constituiría el dato de entrada para los métodos expuestos), el ultimo es el verificador..

    ResponderEliminar
  6. Como puedo obtener el mismo resultado pero con un código diferente?

    ResponderEliminar
  7. En Ruby podría ser algo así:

    def mod11(s)
    c = 1
    sum = s.reverse.chars.map{ |v| c = (c < 7)? c+=1 : 2; c * v.to_i }.sum
    11 - (sum % 11)
    end

    mod11 '41261533'
    => 6

    Aunque podría mejorar :)

    ResponderEliminar
  8. Si la cadena a validar son los 48 primeros digitos del campo claveAcceso, ¿por que en el ejemplo usan la cadena de verificacion 41261533 de apenas 8 digitos.? Podrían poner un ejemplo real por favor, con el ruc, la composición de los 48 digitos y luego el dígito verificador.

    ResponderEliminar
  9. El algoritmo da un ejemplo de una cadena pequeña la cual el SRI la pone como lo explica aquí http://es.wikipedia.org/wiki/C%C3%B3digo_de_control
    Debes de coger los 48 dígitos en invertirlos para la secuencia de calculo que empieza con 2 y termina en 7, después sumas todo y ejecutas la función mod(resulado_de_suma, 11) , restas 11 menos el resultado del mod() .Cuando el resultado del dígito verificador obtenido sea igual a once (11), el digito verificador será el cero (0) y cuando el resultado del dígito verificador obtenido sea igual a diez 10, el digito verificador será el uno (1).
    Suerte.

    ResponderEliminar
  10. Buenas noches una ayuda, el codigo numerico que es parte de acceso de donde lo saco. Como la genero??.Gracias por su ayuda

    ResponderEliminar
  11. Normalmente en este campo colocamos el secuencial que manejamos a nivel de nuestra aplicación (completando a 8 ceros por la izquierda, si es necesario).
    Saludos.

    ResponderEliminar
  12. Saludos, Tengo esta cadena 060820140109928098410011001001000101253002199121, haciendo el algoritmo que me indican me sale como resultado como dígito verificador de (10) esto se debe que la suma de los 48 dígitos multiplicados por los valores que van del (2) al (7) me da como resultado 518 y si a eso hago la operación de [11 - (518 mod 11)] y este resultado da 1, entonces 11 - 1 = 10.... Que esta mal....

    ResponderEliminar
    Respuestas
    1. Es porque al algoritmo le falta la ultima validacion que es la siguiente "Si el resultado es 11 el dígito de control es 0 y si el resultado es 10 el dígito de control resultante es 1."
      entonces antes del return faltaria estas dos lineas

      if (cantidadTotal==11)cantidadTotal=0;
      if (cantidadTotal==10)cantidadTotal=1;


      return cantidadTotal;

      Eliminar
  13. me podrian ayudar con la programacion en c# ... gracias

    ResponderEliminar
  14. Buenos Dias me Podrian ayudar con este codigo en lenguaje c#.. gracias

    ResponderEliminar
  15. Tengo el codigo en VB.net

    ResponderEliminar
  16. Me podian ayudar con el codigo en NSIS.
    Please--!.

    ResponderEliminar
  17. El Código en VFP

    FUNCTION MODULO11
    PARAMETER AIL
    * *
    * --------------------------------------------------------------------------------------------- *
    * ESTE PROGRAMA SE ENCARGA DE RECIBIR UNA CADENA DE 48 DIGITOS Y DEVOLVER EL DIGITO VERIFICADOR *
    * CON EL MODULO 11 CON UN CHEQUE PONDERADO DE 2 *
    * SE UTILIZA PARA LA FACTUACION ELECTRONICA *
    * --------------------------------------------------------------------------------------------- *
    * *
    LOCAL FEA01,FEA02,FEA03,FEA04,I,PIVOTE,CANTIDADTOTAL,CADENAINVERTIDA
    *
    FEA01=ALLTRIM(AIL)
    FEA02=LEN(FEA01)
    *
    CADENAINVERTIDA=''
    FOR I=FEA02 TO 1 STEP -1
    CADENAINVERTIDA=CADENAINVERTIDA+SUBSTR(FEA01,I,1)
    ENDFOR
    *
    *
    PIVOTE=2
    CANTIDADTOTAL=0
    FOR I=1 TO FEA02
    IF PIVOTE=8
    PIVOTE=2
    ENDIF
    FEA03=INT(VAL(SUBSTR(CADENAINVERTIDA,I,1)))*PIVOTE
    CANTIDADTOTAL=CANTIDADTOTAL+FEA03
    PIVOTE=PIVOTE+1
    ENDFOR
    CANTIDADTOTAL=11-MOD(CANTIDADTOTAL,11)
    IF CANTIDADTOTAL=11
    CANTIDADTOTAL=0
    ELSE
    IF CANTIDADTOTAL=10
    CANTIDADTOTAL=1
    ENDIF
    ENDIF
    RETURN(CANTIDADTOTAL)

    ResponderEliminar
  18. private String invertirCadena(String cadena) {
    String cadenaInvertida = "";
    for (int x = cadena.length() - 1; x >= 0; x--) {
    cadenaInvertida = cadenaInvertida + cadena.charAt(x);
    }
    return cadenaInvertida;
    }

    private int obtenerSumaPorDigitos(String cadena) {
    cadena = this.invertirCadena(cadena);
    int pares = 0;
    int impares = 0;
    int cantidadTotal = 0;
    int b = 1;
    int longitudCadena = cadena.length();
    for(int i = 0;i < longitudCadena;i++){
    if(b%2 == 0){
    pares = pares+Integer.parseInt(Character.toString(cadena.charAt(i)));
    }
    else{
    impares = impares+Integer.parseInt(Character.toString(cadena.charAt(i)));
    }
    b++;
    }
    impares = impares*3;
    int sumaTotal = pares+impares;
    int decena = 100;
    while(sumaTotal > decena){
    decena = decena+10;
    }
    cantidadTotal = decena-sumaTotal;
    switch (cantidadTotal) {
    case 10:
    cantidadTotal = 0;
    break;
    case 11:
    cantidadTotal = 1;
    break;
    }
    return cantidadTotal;
    }

    Mejoré un poco el código siguiendo el algoritmo para codigos de barras E.A.N,
    Salud. --anondeco!

    ResponderEliminar
  19. Gracias Ivan Lopez, de pronto tienes el codigo para firnar el archivo XML con la firma electronica en formato P12 desde vfp gracias mil

    ResponderEliminar
  20. En PHP:



    function invertirCadena($cadena)
    {
    $cadenaInvertida = "";
    for ($x = strlen($cadena) - 1; $x >= 0; $x--)
    {
    $cadenaInvertida = $cadenaInvertida.substr($cadena, $x,1);

    }
    return $cadenaInvertida;
    }

    function obtenerSumaPorDigitos($cadena)
    {
    $pivote = 2;
    $longitudCadena = strlen($cadena);
    $cantidadTotal = 0;
    $b = 1;
    for ($i = 0; $i < $longitudCadena; $i++)
    {
    if ($pivote == 8)
    {
    $pivote = 2;
    }
    //$temporal = intval(substr($cadena,$i, $b));
    $temporal = intval(substr($cadena,$i, 1));
    $b++;
    $temporal = $temporal * $pivote;
    $pivote++;
    $cantidadTotal = $cantidadTotal + $temporal;
    }
    $cantidadTotal = 11 - $cantidadTotal % 11;
    return $cantidadTotal;
    }

    ResponderEliminar
  21. En el codigo php hay un pequeño problema, en la linea:
    $temporal = intval(substr($cadena,$i, 1));
    en lugar de 1, es la variable $b
    .. Gracias por el aporte

    ResponderEliminar
  22. No me funciono el codigo en php, pero si me funciono el siguiente:

    function obtenerSumaPorDigitos($_rol) {
    /* Bonus: remuevo los ceros del comienzo. */
    while($_rol[0] == "0") {
    $_rol = substr($_rol, 1);
    }
    $factor = 2;
    $suma = 0;
    for($i = strlen($_rol) - 1; $i >= 0; $i--) {
    $suma += $factor * $_rol[$i];
    $factor = $factor % 7 == 0 ? 2 : $factor + 1;
    }
    $dv = 11 - $suma % 11;
    /* Por alguna razón me daba que 11 % 11 = 11. Esto lo resuelve. */
    $dv = $dv == 11 ? 0 : ($dv == 10 ? "K" : $dv);
    echo '
    El digito verificador es: '.$dv.'
    ';
    return $dv;
    }

    ResponderEliminar
  23. Hola el algoritmo en java está muy bien pero falta considerar 2 casos al final que es cuando el digito verificado es 10 u 11, es lo siguiente "Si el resultado es 11 el dígito de control es 0 y si el resultado es 10 el dígito de control resultante es 1."
    entonces antes del return faltaria estas dos lineas

    if (cantidadTotal==11)cantidadTotal=0;
    if (cantidadTotal==10)cantidadTotal=1;

    ResponderEliminar