Sigueme en Facebook Sigueme en Twitter Sigueme en Instagram Sigueme en Youtube
JC Mouse Bolivia
Index / Desarrollo Web / Código de Control v7 en JavaScript

Código de Control v7 en JavaScript

Autor jc mouse lunes, abril 18, 2016

En esta oportunidad, se deja a disposición de la comunidad de programadores de Bolivia y también porque no, del que quiera aprender un poquito más de Javascript y Facturación Virtual,  el generador de Código de Control versión 7.0 construido según la documentación provista por Impuestos Nacionales (SIN) Bolivia “Facturación electrónica: El Código de Control

¿Que es JavaScript?

JavaScript (abreviado comúnmente JS) es un lenguaje de programación interpretado. Se utiliza principalmente en su forma del lado del cliente (client-side), implementado como parte de un navegador web permitiendo mejoras en la interfaz de usuario y páginas web dinámicas aunque existe una forma de JS del lado del servidor (Server-side JavaScript o SSJS)

NUEVA ESPECIFICACIÓN TÉCNICA DEL CÓDIGO DE CONTROL (Ver.7.0)

En el marco del Nuevo Sistema de Facturación implementado por la Administración Tributaria, se tiene prevista la incorporación de nuevos elementos de seguridad en las facturas emitidas por sistemas de facturación computarizada. En este sentido, toda factura emitida por este medio, deberá incorporar un Código de Control generado a partir de información de la misma.

Recordemos los algoritmos utilizados por el generador de Código de Control son:

  • Alleged RC4 Un algoritmo de criptografía simétrica, basado en cifrado de flujo (stream cipher), muy utilizado por su rendimiento y simplicidad.
  • Verhoeff Algoritmo de dígito verificador que trabaja con cadenas de dígitos decimales de cualquier tamaño. Además de detectar una amplia gama de errores en datos numéricos, este algoritmo también detecta casos de
    transposición de dígitos adyacentes.
  • Base 64 Algoritmo que convierte cifras en base 10 a base 64, utilizando divisiones sucesivas además de un
    diccionario de 64 caracteres.

Nuestro proyecto visto desde netbeans es el siguiente:

JS impuestos bolivia

AllegedRC4.js

/**
 * Retorna mensaje encriptado 
 * @param {string} message mensaje a encriptar
 * @param {string} key llave para encriptar
 * @param {boolean} unscripted sin guion TRUE|FALSE 
 * @return {string} mensaje encriptado 
*/
function encryptMessageRC4(message, key,unscripted){
    var state = new Array(255);
    var x = 0;
    var y = 0;
    var index1 = 0;
    var index2 = 0;        
    var nmen = "";        
    var messageEncryption = "";
    
    for(i=0;i<=255;i++){
        state[i]=i;
    }
    
    for(i=0;i<=255;i++){
        //Index2 = ( ObtieneASCII(key[Index1]) + State[I] + Index2 ) MODULO 256
        index2 =  ( (key.charAt(index1).charCodeAt() ) +  state[i] + index2) % 256;
        //IntercambiaValor( State[I], State[Index2] )
        var aux = state[i];
        state[i] = state[index2];
        state[index2] = aux;
        //Index1 = (Index1 + 1) MODULO LargoCadena(Key)
        index1 = (index1 + 1 ) % key.length;
    }  
     
     //PARA I = 0 HASTA LargoCadena(Mensaje)-1 HACER
    for(i=0; i<message.length;i++ ){
        //X = (X + 1) MODULO 256
        x = (x + 1) % 256;
        //Y = (State[X] + Y) MODULO 256
        y = (state[x] + y) % 256;
        //IntercambiaValor( State[X] , State[Y] )
        var aux = state[x];
        state[x] = state[y];
        state[y] = aux; 
        //NMen = ObtieneASCII(Mensaje[I]) XOR State[(State[X] + State[Y]) MODULO 256]
        nmen = ( (message.charAt(i)).charCodeAt() ) ^ state[(state[x] + state[y]) % 256];
        //MensajeCifrado = MensajeCifrado + "-" + RellenaCero(ConvierteAHexadecimal(NMen))            
        var nmenHex = nmen.toString(16).toUpperCase();         
        messageEncryption = messageEncryption + ( (unscripted)?"":"-") + ((nmenHex.length===1)?('0'+nmenHex):nmenHex);            
    }    
    return ((unscripted)?messageEncryption: messageEncryption.substring(1,messageEncryption.length)); 
    
}//encryptMessageRC4:end

Base64SIN.js

/**
 * Codifica cadena en base 64
 * @param {string} value texto a codificar
 * @return {string} cadena en Base64
*/
function convertBase64(value){
    var dictionary = new Array("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", 
                            "A", "B", "C", "D", "E", "F", "G", "H", "I", "J",
                            "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", 
                            "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d",
                            "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", 
                            "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", 
                            "y", "z", "+", "/"); 
    var quotient = 1;                 
    var word = "";
    var remainder;
    while (quotient > 0)
    {
        quotient = Math.floor(value / 64);
        remainder = value % 64;
        word = dictionary[remainder] + word;
        value = quotient;
    }
    return word;
}

Verhoeff.js

/*
For more info on the algorithm: http://en.wikipedia.org/wiki/Verhoeff_algorithm
by Sergey Petushkov, 2014
*/
// multiplication table d
var d=[
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
    [1, 2, 3, 4, 0, 6, 7, 8, 9, 5], 
    [2, 3, 4, 0, 1, 7, 8, 9, 5, 6], 
    [3, 4, 0, 1, 2, 8, 9, 5, 6, 7], 
    [4, 0, 1, 2, 3, 9, 5, 6, 7, 8], 
    [5, 9, 8, 7, 6, 0, 4, 3, 2, 1], 
    [6, 5, 9, 8, 7, 1, 0, 4, 3, 2], 
    [7, 6, 5, 9, 8, 2, 1, 0, 4, 3], 
    [8, 7, 6, 5, 9, 3, 2, 1, 0, 4], 
    [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
];

// permutation table p
var p=[
    [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 
    [1, 5, 7, 6, 2, 8, 3, 0, 9, 4], 
    [5, 8, 0, 3, 7, 9, 6, 1, 4, 2], 
    [8, 9, 1, 6, 0, 4, 3, 5, 2, 7], 
    [9, 4, 5, 3, 1, 2, 6, 8, 7, 0], 
    [4, 2, 8, 6, 5, 7, 3, 9, 0, 1], 
    [2, 7, 9, 3, 8, 0, 6, 4, 1, 5], 
    [7, 0, 4, 6, 9, 1, 3, 2, 5, 8]
];

// inverse table inv
var inv = [0, 4, 3, 2, 1, 5, 6, 7, 8, 9];

// converts string or number to an array and inverts it
function invArray(array){
    
    if (Object.prototype.toString.call(array) == "[object Number]"){
        array = String(array);
    }
    
    if (Object.prototype.toString.call(array) == "[object String]"){
        array = array.split("").map(Number);
    }
    
	return array.reverse();
	
}

// generates checksum
function generateVerhoeff(array){
    	
	var c = 0;
	var invertedArray = invArray(array);
	
	for (var i = 0; i < invertedArray.length; i++){
		c = d[c][p[((i + 1) % 8)][invertedArray[i]]];
	}
	
	return inv[c];
}

// validates checksum
function validate(array) {
    
    var c = 0;
    var invertedArray = invArray(array);
    
    for (var i = 0; i < invertedArray.length; i++){
    	c=d[c][p[(i % 8)][invertedArray[i]]];
    }

    return (c === 0);
}

ControlCode.js

/**
 * Generar codigo de control v7 de Impuestos Nacionales de Bolivia
* @requires Base64SIN.js
* @requires AllegedRC4.js
* @param {String} authorizationNumber Numero de autorizacion
* @param {String} invoiceNumber Numero de factura
* @param {String} nitci Número de Identificación Tributaria o Carnet de Identidad
* @param {String} dateOfTransaction fecha de transaccion de la forma AAAAMMDD
* @param {String} transactionAmount Monto de la transacción 
* @param {String} dosageKey Llave de dosificación
* @return {String} Codigo de control generado
*/
function generateControlCode(authorizationNumber, invoiceNumber, nitci,
                      dateOfTransaction, transactionAmount, dosageKey){
    
    //redondea monto de transaccion 
    transactionAmount = roundUp(transactionAmount);
        
    /* ========== PASO 1 ============= */
    invoiceNumber = addVerhoeffDigit(invoiceNumber,2);
    nitci = addVerhoeffDigit(nitci,2);
    dateOfTransaction = addVerhoeffDigit(dateOfTransaction,2);
    transactionAmount = addVerhoeffDigit(transactionAmount,2);
    //se suman todos los valores obtenidos
    var sumOfVariables = Number(invoiceNumber)
                        + Number(nitci)
                        + Number(dateOfTransaction)
                        + Number(transactionAmount);
    //A la suma total se añade 5 digitos Verhoeff
    var sumOfVariables5Verhoeff = addVerhoeffDigit(sumOfVariables,5);   
    
    /* ========== PASO 2 ============= */
    var fiveDigitsVerhoeff = sumOfVariables5Verhoeff.substr(sumOfVariables5Verhoeff.length-5,5);
    var numbers = fiveDigitsVerhoeff.split("");
    for(i=0;i<5;i++){
        numbers[i] = parseInt(numbers[i]) + 1;             
    }
            
    string1 = dosageKey.substr(0, numbers[0] );
    string2 = dosageKey.substr(numbers[0], numbers[1] );
    string3 = dosageKey.substr(numbers[0]+ numbers[1], numbers[2] );
    string4 = dosageKey.substr(numbers[0]+ numbers[1]+ numbers[2], numbers[3] );
    string5 = dosageKey.substr(numbers[0]+ numbers[1]+ numbers[2]+ numbers[3], numbers[4] );        
      
    var authorizationNumberDKey = authorizationNumber + string1;
    var invoiceNumberdKey = invoiceNumber + string2;
    var NITCIDKey = nitci + string3;
    var dateOfTransactionDKey = dateOfTransaction + string4;        
    var transactionAmountDKey = transactionAmount + string5;   
    
    /* ========== PASO 3 ============= */        
    //se concatena cadenas de paso 2
    var stringDKey = authorizationNumberDKey.toString() + invoiceNumberdKey.toString() +
                 NITCIDKey.toString() + dateOfTransactionDKey.toString() + transactionAmountDKey.toString();         
    //Llave para cifrado + 5 digitos Verhoeff generado en paso 2
    var keyForEncryption = dosageKey.toString() + fiveDigitsVerhoeff.toString();              
    //se aplica AllegedRC4
    var allegedRC4String = encryptMessageRC4(stringDKey, keyForEncryption,true); 
    
     /* ========== PASO 4 ============= */
    //cadena encriptada en paso 3 se convierte a un Array         
    var chars = allegedRC4String.split("");
    //se suman valores ascii
    var totalAmount=0;
    var sp1=0;
    var sp2=0;
    var sp3=0;
    var sp4=0;
    var sp5=0;
        
    var tmp = 1;
    for(i=0; i<allegedRC4String.length;i++){
        totalAmount += chars[i].charCodeAt();//se extrae ascii y se suma
        switch(tmp){
            case 1: sp1 += chars[i].charCodeAt(); break;
            case 2: sp2 += chars[i].charCodeAt(); break;
            case 3: sp3 += chars[i].charCodeAt(); break;
            case 4: sp4 += chars[i].charCodeAt(); break;
            case 5: sp5 += chars[i].charCodeAt(); break;
        }            
        tmp = (tmp<5)?tmp+1:1;
    }
    
     /* ========== PASO 5 ============= */    
    //suma total * sumas parciales dividido entre resultados obtenidos 
    //entre el dígito Verhoeff correspondiente más 1 (paso 2)
    var tmp1 = Math.floor(totalAmount*sp1/numbers[0]);
    var tmp2 = Math.floor(totalAmount*sp2/numbers[1]);
    var tmp3 = Math.floor(totalAmount*sp3/numbers[2]);
    var tmp4 = Math.floor(totalAmount*sp4/numbers[3]);
    var tmp5 = Math.floor(totalAmount*sp5/numbers[4]);
    //se suman todos los resultados
    var sumProduct = tmp1 + tmp2 + tmp3 + tmp4 + tmp5;        
    //se obtiene base64
    var base64SIN = convertBase64(sumProduct);
    
     /* ========== PASO 6 ============= */        
    //Aplicar el AllegedRC4 a la anterior expresión obtenida
    return encryptMessageRC4(base64SIN, dosageKey + fiveDigitsVerhoeff);    
}


/**
* Añade N digitos Verhoeff a una cadena de texto
* @requires Verhoeff.js
* @param {string} value
* @param {int} max numero de digitos a agregar
* @return {String} cadena original + N digitos Verhoeff
*/
function addVerhoeffDigit(value,max){    
    for(i=1;i<=max;i++){
        val = generateVerhoeff(value);
        value += val.toString();                  
    }            
    return value;
}
    
/**
* Redondea hacia arriba
* @param {String} value cadena con valor numerico de la forma 123 | 123.4 | 123,4
* @return {String} numero redondeado
*/
function roundUp(value){        
    //reemplaza (,) por (.)        
    var value2 = value.replace(',', '.');
    //redondea a 0 decimales        
    return Math.round(value2);
}

SIN deja 5000 casos de prueba para testear el correcto funcionamiento del script, a continuación en un archivo HTML implementamos los script más arriba escritos de todos los algoritmos usados en el generador de código, ademas usamos JQuery para leer un archivo de texto 5000CasosPruebaCCVer7.txt que es donde están los casos de prueba, jQuery solo nos sirve para leer el archivo de texto no asi para ejecutar el script [importante].

<!DOCTYPE html>
<html>
    <head>
        <title>Test Código de Control</title>
        <meta charset="UTF-8">                
        <script type='text/javascript' src='js/AllegedRC4.js'></script>
        <script type='text/javascript' src='js/Base64SIN.js'></script>
        <script type='text/javascript' src='js/Verhoeff.js'></script>
        <script type='text/javascript' src='js/ControlCode.js'></script>       
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
    </head>
    <body>
    <script>        
        var file = '5000CasosPruebaCCVer7.txt';
        $.get(file, function(data) {
            var lines = data.split("\n");
            var count=0;
            document.write('<h2>Codigo de Control en JavaScript</h2>'); 
            document.write('Archivo <b>'+file+'</b><br/>');
            document.write("<table>");
            $.each(lines, function(n, elem) {
                var data = elem.split("|");
                c = generateControlCode(data[0],//Numero de autorizacion
                                        data[1],//Numero de factura
                                        data[2],//Número de Identificación Tributaria o Carnet de Identidad
                                        data[3].replace(/[/]/g,''),//fecha de transaccion de la forma AAAAMMDD
                                        data[4],//Monto de la transacción
                                        data[5]//Llave de dosificación
                        );
                
                if(data[10]===c){
                    count = count + 1;
                }          
                if( ( Math.random() * 99 )>80){
                    document.write("<tr><td>"+count + "</td><td>Codigo generado</td><td><b>" + c + "</b></td><td>Codigo SIN</td><td><b>"+ data[10] + "</b></td><td>"+ ((data[10]===c)?"IGUALES":"NO IGUALES") +"</td></tr>");    
                }
            });                        
            document.write("</table>");  
            document.write('Total registros testeados <b>'+count+'</b><br/>');                    
        });
        
    </script>      
    </body>
</html>

Ejecutamos los casos de prueba desde nuestro servidor web y tenemos:

Test javascript SIN

Eso es todo, y ya sabes, se deja el script de forma gratuita para universitarios y profesionales que quieran implementar nuevas tecnologías en sus proyectos y/o trabajos de facturación virtual.

Proyecto Código de Control JS <<DESCARGAR>>

Documentación base: <<v7 y algoritmos>>

Cualquier duda o bugs encontrado por favor dejar en comentarios

enjoy!!!

Tags

Si te ha gustado podrías compartirlo o dejar un comentario. ¡Muchas gracias!
Autor: JC Mouse

Yo soy yo :) JC Mouse, Soy orgullosamente boliviano soy fundador y CEO de la web jc-Mouse.net uno de las pocas web en emprendimiento y tecnología en Bolivia.

Toda la información que encuentres en este sitio es y sera completamente gratis siempre, puedes copiar, descargar y re-publicar si así lo deseas en otros blogs o sitios web, solo te pido a cambio que dejes una referencia a esta web. Esto nos ayuda a crecer y seguir aportando. Bye

Enjoy! :)

También Te Podría Interesar

Formulario de login personalizado

Formulario de login personalizado

Hola, en este oportunidad dejo a disposición un formulario de logueo que se me ocurrió de repente creo inspirado en las...

JCPanel 3.0

JCPanel 3.0

Nueva versión de un viejo proyecto swing “JCMousePanel” que nos permitía agregar imágenes en los contenedore...

Añadir Splash Screen en Java/Netbeans

Añadir Splash Screen en Java/Netbeans

¿Splash Screen? El splash screen o traducido al aspañol 😉  es una “Pantalla de bienvenida” que se muestra cu...

Proyecto Ascii Art (metodo de Black/white)

Proyecto Ascii Art (metodo de Black/white)

El Ascii Art o Arte Ascii, consiste en realizar dibujos con los caracteres ASCII (Código Americano Estándar de Intercamb...

Crear Miniaturas Thumbnails de Videos (Código Fuente)

Crear Miniaturas Thumbnails de Videos (Código Fuente)

VLC Media Player es un reproductor multimedia de código abierto muy popular desarrollado por el proyecto VideoLAN. VLCJ ...

ArrayDeque: Cola doblemente terminada

ArrayDeque: Cola doblemente terminada

Una cola doblemente terminada o deque es una estructura de datos lineal que permite insertar y eliminar elementos por am...

1 comentario en “Código de Control v7 en JavaScript”

  1. joel dice:

    Muy buen aporte estimado.

    Salu2.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*

Comparte lo que sabes

Categorias

Últimas entradas

PHPUnit es un framework que se utiliza para escribir tests en PHP, Netbeans nos permite configurarlo y usarlo fácilmente...

Una Prueba Unitaria, es una forma de comprobar que nuestro código, hace lo que se supone debe hacer; es decir, se asegur...

La prueba del camino básico, es una prueba de “caja blanca” que consiste en verificar el código de nuestros...

Si quieres cambiar el nombre de tus atributos sin tener que reescribir código java por X o Y razón, GSON te permite reno...

Android Bolivia

Bandera en Alto