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

Código de Control en PHP v7.0 Bolivia

Autor jc mouse miércoles, abril 13, 2016

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.

En un post anterior vimos la construcción paso a paso guiados por la documentación provista por Impuestos Nacionales (SIN) de este “Código de Control” en el lenguaje java. Puedes ver el post en este enlace “Facturación electrónica: El Código de Control

En esta oportunidad, solo dejaremos el código necesario para la generación del código de control, pero en lenguaje PHP (Hypertext Pre-processor).  PHP es un lenguaje de código abierto muy popular, adecuado para desarrollo web  de código abierto lo que significa que es de uso libre y gratuito para todos los programadores que quieran usarlo.

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.

A continuación el código fuente de esos algoritmos y la clase generador del código de control:

Verhoeff

<?php
# @author Semyon Velichko
# https://en.wikibooks.org/wiki/Algorithm_Implementation/Checksums/Verhoeff_Algorithm#PHP

class Verhoeff {

    static public $d = array(
        array(0,1,2,3,4,5,6,7,8,9),
        array(1,2,3,4,0,6,7,8,9,5),
        array(2,3,4,0,1,7,8,9,5,6),
        array(3,4,0,1,2,8,9,5,6,7),
        array(4,0,1,2,3,9,5,6,7,8),
        array(5,9,8,7,6,0,4,3,2,1),
        array(6,5,9,8,7,1,0,4,3,2),
        array(7,6,5,9,8,2,1,0,4,3),
        array(8,7,6,5,9,3,2,1,0,4),
        array(9,8,7,6,5,4,3,2,1,0)
    );

    static public $p = array(
        array(0,1,2,3,4,5,6,7,8,9),
        array(1,5,7,6,2,8,3,0,9,4),
        array(5,8,0,3,7,9,6,1,4,2),
        array(8,9,1,6,0,4,3,5,2,7),
        array(9,4,5,3,1,2,6,8,7,0),
        array(4,2,8,6,5,7,3,9,0,1),
        array(2,7,9,3,8,0,6,4,1,5),
        array(7,0,4,6,9,1,3,2,5,8)
    );

    static public $inv = array(0,4,3,2,1,5,6,7,8,9);

    static function calc($num) {
        if(!preg_match('/^[0-9]+$/', $num)) {
            throw new \InvalidArgumentException(sprintf("Error! Value is restricted to the number, %s is not a number.",
                                                    $num));
        }
    
        $r = 0;
        foreach(array_reverse(str_split($num)) as $n => $N) {
            $r = self::$d[$r][self::$p[($n+1)%8][$N]];
        }
        return self::$inv[$r];
    }

    static function check($num) {
        if(!preg_match('/^[0-9]+$/', $num)) {
            throw new \InvalidArgumentException(sprintf("Error! Value is restricted to the number, %s is not a number.",
                                                    $num));
        }
    
        $r = 0;
        foreach(array_reverse(str_split($num)) as $n => $N) {
            $r = self::$d[$r][self::$p[$n%8][$N]];
        }
        return $r;
    }

    static function generate($num) {
        return sprintf("%s%s", $num, self::calc($num));
    }

    static function validate($num) {
        return self::check($num) === 0;
    }
}

Alleged RC4

<?php
/**
 * @see http://www.jc-mouse.net/
 * @author Mouse
 */
class AllegedRC4 {
    
    /**
     * Retorna mensaje encriptado
     * @param message mensaje a encriptar
     * @param key llave para encriptar
     * @param unscripted sin guion TRUE|FALSE
     * @return String mensaje encriptado
     */
    static function encryptMessageRC4($message, $key,$unscripted=false){
        $state = range(0, 255);
        $x=0;
        $y=0;
        $index1=0;
        $index2=0;        
        $nmen="";        
        $messageEncryption="";
        
        for($i=0;$i<=255;$i++){
            //Index2 = ( ObtieneASCII(key[Index1]) + State[I] + Index2 ) MODULO 256
            $index2 =  ( ord($key[$index1]) +  $state[$i] + $index2) % 256;
            //IntercambiaValor( State[I], State[Index2] )
            $aux = $state[$i];
            $state[$i] = $state[$index2];
            $state[$index2] = $aux;
            //Index1 = (Index1 + 1) MODULO LargoCadena(Key)
            $index1 = ($index1 + 1 ) % strlen($key);
        }        
        //PARA I = 0 HASTA LargoCadena(Mensaje)-1 HACER
        for($i=0; $i<strlen($message);$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] )
            $aux = $state[$x];
            $state[$x] = $state[$y];
            $state[$y] = $aux; 
            //NMen = ObtieneASCII(Mensaje[I]) XOR State[(State[X] + State[Y]) MODULO 256]
            $nmen = ( ord($message[$i])) ^ $state[($state[$x]+$state[$y]) % 256];
            //MensajeCifrado = MensajeCifrado + "-" + RellenaCero(ConvierteAHexadecimal(NMen))            
            $nmenHex = strtoupper(dechex($nmen));               
            $messageEncryption = $messageEncryption . (($unscripted)?"":"-"). ((strlen($nmenHex)==1)?('0'.$nmenHex):$nmenHex);            
        }        
        return (($unscripted)?$messageEncryption:substr($messageEncryption,1,strlen($messageEncryption))); 
    }
    
}//end:class

Base 64

<?php
/**
 * @see http://www.jc-mouse.net/
 * @author Mouse
 */
class Base64SIN {
    
    static function convert($value){
        $dictionary = 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", "+", "/");        
        $quotient = 1;                 
        $word = "";
        while ($quotient > 0)
        {
            $quotient = floor($value / 64);
            $remainder = $value % 64;
            $word = $dictionary[$remainder] . $word;
            $value = $quotient;
        }
        return $word;
    }    
}

Código de Control

<?php
include 'Verhoeff.php';
include 'AllegedRC4.php';
include 'Base64SIN.php';
/**
 * @see http://www.jc-mouse.net/
 * @author mouse
 * @version 1
 */
class ControlCode {
    
    /**
     * @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
     */
    function generate($authorizationNumber, $invoiceNumber, $nitci,
                      $dateOfTransaction, $transactionAmount, $dosageKey){        
        
        //validación de datos
        if( empty($authorizationNumber) || empty($invoiceNumber) || empty($dateOfTransaction) || 
                empty($transactionAmount) || empty($dosageKey) || (!strlen($nitci)>0 )  ){            
            throw new InvalidArgumentException('<b>Todos los campos son obligatorios</b>');
        }else{
            $this->validateNumber($authorizationNumber);
            $this->validateNumber($invoiceNumber);
            $this->validateNumber($dateOfTransaction);
            $this->validateNumber($nitci);
            $this->validateNumber($transactionAmount);
            $this->validateDosageKey($dosageKey);
        }
        
        //redondea monto de transaccion 
        $transactionAmount = $this->roundUp($transactionAmount);
                
        /* ========== PASO 1 ============= */
        $invoiceNumber = self::addVerhoeffDigit($invoiceNumber,2);
        $nitci = self::addVerhoeffDigit($nitci,2);
        $dateOfTransaction = self::addVerhoeffDigit($dateOfTransaction,2);
        $transactionAmount = self::addVerhoeffDigit($transactionAmount,2);
        //se suman todos los valores obtenidos
        $sumOfVariables = $invoiceNumber
                          + $nitci
                          + $dateOfTransaction
                          + $transactionAmount;
        //A la suma total se añade 5 digitos Verhoeff
        $sumOfVariables5Verhoeff = self::addVerhoeffDigit($sumOfVariables,5);  
        
         /* ========== PASO 2 ============= */
        $fiveDigitsVerhoeff = substr($sumOfVariables5Verhoeff,strlen($sumOfVariables5Verhoeff)-5);        
        $numbers = str_split($fiveDigitsVerhoeff);
        for($i=0;$i<5;$i++){
             $numbers[$i] = $numbers[$i] + 1;             
        }
                
        $string1 = substr($dosageKey,0, $numbers[0] );
        $string2 = substr($dosageKey,$numbers[0], $numbers[1] );
        $string3 = substr($dosageKey,$numbers[0]+ $numbers[1], $numbers[2] );
        $string4 = substr($dosageKey,$numbers[0]+ $numbers[1]+ $numbers[2], $numbers[3] );
        $string5 = substr($dosageKey,$numbers[0]+ $numbers[1]+ $numbers[2]+ $numbers[3], $numbers[4] );        
        
        $authorizationNumberDKey = $authorizationNumber . $string1;
        $invoiceNumberdKey = $invoiceNumber . $string2;
        $NITCIDKey = $nitci . $string3;
        $dateOfTransactionDKey = $dateOfTransaction . $string4;        
        $transactionAmountDKey = $transactionAmount . $string5;
        
          /* ========== PASO 3 ============= */        
        //se concatena cadenas de paso 2
        $stringDKey = $authorizationNumberDKey . $invoiceNumberdKey . $NITCIDKey . $dateOfTransactionDKey . $transactionAmountDKey;         
        //Llave para cifrado + 5 digitos Verhoeff generado en paso 2
        $keyForEncryption = $dosageKey . $fiveDigitsVerhoeff;              
        //se aplica AllegedRC4
        $allegedRC4String = AllegedRC4::encryptMessageRC4($stringDKey, $keyForEncryption,true);
        
        /* ========== PASO 4 ============= */
        //cadena encriptada en paso 3 se convierte a un Array         
        $chars = str_split($allegedRC4String);
        //se suman valores ascii
        $totalAmount=0;
        $sp1=0;
        $sp2=0;
        $sp3=0;
        $sp4=0;
        $sp5=0;
        
        $tmp=1;
        for($i=0; $i<strlen($allegedRC4String);$i++){
            $totalAmount += ord($chars[$i]);
            switch($tmp){
                case 1: $sp1 += ord($chars[$i]); break;
                case 2: $sp2 += ord($chars[$i]); break;
                case 3: $sp3 += ord($chars[$i]); break;
                case 4: $sp4 += ord($chars[$i]); break;
                case 5: $sp5 += ord($chars[$i]); 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)
        $tmp1 = floor($totalAmount*$sp1/$numbers[0]);
        $tmp2 = floor($totalAmount*$sp2/$numbers[1]);
        $tmp3 = floor($totalAmount*$sp3/$numbers[2]);
        $tmp4 = floor($totalAmount*$sp4/$numbers[3]);
        $tmp5 = floor($totalAmount*$sp5/$numbers[4]);
        //se suman todos los resultados
        $sumProduct = $tmp1 + $tmp2 + $tmp3 + $tmp4 + $tmp5;        
        //se obtiene base64
        $base64SIN = Base64SIN::convert($sumProduct);
        
        /* ========== PASO 6 ============= */        
        //Aplicar el AllegedRC4 a la anterior expresión obtenida
        return AllegedRC4::encryptMessageRC4($base64SIN, $dosageKey.$fiveDigitsVerhoeff);
    }
    
    /**
     * Añade N digitos Verhoeff a una cadena de texto
     * @param value String
     * @param max numero de digitos a agregar
     * @return String cadena original + N digitos Verhoeff
     */
    static function addVerhoeffDigit($value,$max){
        for($i=1;$i<=$max;$i++){
            $value .= Verhoeff::calc($value);            
        }            
        return $value;
    }
    
     /**
     * Redondea hacia arriba
     * @param value cadena con valor numerico de la forma 123 123.4 123,4
     */
    function roundUp($value){        
        //reemplaza (,) por (.)        
        $value2 = str_replace(',','.',$value);
        //redondea a 0 decimales        
        return round($value2, 0, PHP_ROUND_HALF_UP);
    }
    
    function validateNumber($value){
        if(!preg_match('/^[0-9,.]+$/', $value)){
            throw new InvalidArgumentException(sprintf("Error! Valor restringido a número, %s no es un número.",$value));
        }
    }
    
    function validateDosageKey($value){
        if(!preg_match('/^[A-Za-z0-9=#()*+-_\@\[\]{}%$]+$/', $value)){
            throw new InvalidArgumentException(sprintf("Error! llave de dosificación,<b> %s </b>contiene caracteres NO permitidos.",$value));
        }
    }
    
}//end:class

Implementamos las clases anteriores y testeamos con los casos de prueba que facilita Impuestos Internos (5000 casos) como se ve acontinuación:

<?php
include 'sin/ControlCode.php';
try{
    $filename="5000CasosPruebaCCVer7.txt";
    $handle = fopen($filename, "r");
    if ($handle) {        
        $controlCode = new ControlCode();
        $count=0;        
        while (($line = fgets($handle)) !== false) {        
            $reg = explode("|", $line);        
            //genera codigo de control
            $code = $controlCode->generate($reg[0],//Numero de autorizacion
                                           $reg[1],//Numero de factura
                                           $reg[2],//Número de Identificación Tributaria o Carnet de Identidad
                                           str_replace('/','',$reg[3]),//fecha de transaccion de la forma AAAAMMDD
                                           $reg[4],//Monto de la transacción
                                           $reg[5]//Llave de dosificación
                    );
            if($code===$reg[10]){                
                $count+=1;
            }
        }        
        echo 'Archivo <b>'.$filename.'</b><br/>';
        echo 'Total registros testeados <b>'.$count.'</b><br/>';        
        echo 'Errores <b>0</b><br/>';        
    fclose($handle);
    
    }else{
         throw new Exception("<b>Could not open the file!</b>");
    }
}catch ( Exception $e ){
     echo "Error (File: ".$e->getFile().", line ".
          $e->getLine()."): ".$e->getMessage();
}

En el navegador podrás ver lo siguiente:

php impuestos

Lenguaje: PHP 5.6

Codificación: UTF-8

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

Descargar en este <<enlace pobre>>

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

Cambiar API Level en Android Studio

Cambiar API Level en Android Studio

Cuando desarrollamos aplicaciones en el IDE (Entorno de Desarrollo Integrado) de Android Studio, es importante indicar e...

Libreria swing BlackTabbedPane

Libreria swing BlackTabbedPane

Continuación del tutorial [Personalizar JTabbedPane con Netbeans]. La clase BlackTabbedPaneUI que extendemos de BasicTab...

Ordenación de burbuja animado

Ordenación de burbuja animado

La Ordenación de burbuja (Bubble Sort en inglés) es un sencillo algoritmo de ordenamiento. Este algoritmo es esencialmen...

GSON: Generar objetos java desde JSON

GSON: Generar objetos java desde JSON

En un post anterior vimos como utilizar GSON para serializar un objeto java en JSON, en esta oportunidad se vera el proc...

Reportes con imagenes en Java (Video)

Reportes con imagenes en Java (Video)

Video Tutorial que muestra una de las maneras de utilizar y mostrar imagenes en iReport, pasando estas como parametros d...

Transición de imágenes al estilo PowerPoint

Transición de imágenes al estilo PowerPoint

¿Qué es una transición? Una transición es un efecto de movimiento que se da entre una imagen y la siguiente en una prese...

2 comentarios en “Código de Control en PHP v7.0 Bolivia”

  1. Henry dice:

    Una consulta ing como puedo hacer una impresion de reportes entre un periodo de fechas para que se imprima ppr cantidad,srria un buen apoyo con un ejemplo
    Saludos

  2. chavo dice:

    me sale este error en el controlCode linea 146 de la function
    function validateNumber($value){
    if(!preg_match(‘ /^[0-9,.]+$/’, $value)){
    throw new InvalidArgumentException(sprintf(“Error! Valor restringido a número, %s no es un número.”,$value));
    }

    Error! Valor restringido a número, NRO. AUTORIZACION no es un número.
    como lo corrigo gracias y muy buena su pagina ….

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