¿Que es la facturación electrónica?
Una factura es un documento que sirve para describir el costo de los servicios y desglosar los impuestos correspondientes a pagar, por tanto una factura electrónica es la versión digital de las facturas tradicionales, legalmente válido como medio de respaldo de las operaciones comerciales entre contribuyentes se expide y recibe en formato electrónico.
Facturación electrónica en Bolivia
Cada país cuenta con su propia normativa en cuanto a la implementación de facturación virtual, para Bolivia esta se encuentra en el documento alojado en la web de Impuestos nacionales (SIN) «Sistema de Facturación Virtual«. Así mismo, este tipo de facturación debe contar con medidas de seguridad, por ejemplo un Código de Control:
«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.» [SIN]
¿Qué es el Código de Control?
Es un dato alfanumérico generado por un sistema de facturación computarizada a tiempo de emitir una factura. Constituye una representación única de una factura, que será empleada por el SIN (Servicio de Impuestos Nacionales) para que junto a otra información permitan determinar la validez o no de la misma.
Este código se genera en base a información de dosificación de la factura, información de la transacción comercial, y un dato alfanumérico denominado Llave de Dosificación, que el contribuyente recibirá por Internet cada vez que solicite dosificaciones de facturas para su sistema de facturación computarizada.
Generación del Código de Control
Algoritmos utilizados
Para generar un Código de Control, se hace uso de los siguientes algoritmos informáticos:
Para generar un Código de Control, se requiere de la siguiente información
Datos de dosificación:
Datos de la transacción comercial:
Llave de Dosificación
A, B, C, D, E, F, G, H, I, J, K, L, M, N, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, m, n, p, q, r, s, t, u, v, w, x, y, z, 2, 3, 4, 5, 6, 7, 8, 9, =, #, (, ), *, +, -, _, \, @, [, ], {, }, %, $
El Código de Control generado a partir de los algoritmos mencionados, será un dato alfanumérico de hasta 10 caracteres, representado en grupos de 2 separados por el caracter «-«. Ej.: 6A-DC-53-05-14
Proyecto «Código de Control»
SIN Bolivia pone a disposición de aquel que este interesado en el tema de documentación bastante detallada el cual solo debemos seguir paso a paso. «DOCUMENTO DE ESPECIFICACIONES TÉCNICAS DEL CÓDIGO DE CONTROL – VERSIÓN 7.0«.
Algoritmo de Verhoeff (Documentación)
/** * @see <a href="http://en.wikipedia.org/wiki/Verhoeff_algorithm">More Info</a> * @see <a href="http://en.wikipedia.org/wiki/Dihedral_group">Dihedral Group</a> * @see <a href="http://mathworld.wolfram.com/DihedralGroupD5.html">Dihedral Group Order 10</a> * @author Colm Rice */ public class Verhoeff { // The multiplication table static int[][] d = new int[][] { {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} }; // The permutation table static int[][] p = new int[][] { {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} }; // The inverse table static int[] inv = {0, 4, 3, 2, 1, 5, 6, 7, 8, 9}; /* * For a given number generates a Verhoeff digit */ public static String generateVerhoeff(String num){ int c = 0; int[] myArray = stringToReversedIntArray(num); for(int i = 0; i < myArray.length; i++){ c = d[c][p[((i + 1) % 8)] [myArray[i]]]; } return Integer.toString(inv[c]); } /* * Validates that an entered number is Verhoeff compliant. * NB: Make sure the check digit is the last one. */ public static boolean validateVerhoeff(String num){ int c = 0; int[] myArray = stringToReversedIntArray(num); for (int i = 0; i < myArray.length; i++){ c = d[c][p[(i % 8)][myArray[i]]]; } return (c == 0); } /* * Converts a string to a reversed integer array. */ private static int[] stringToReversedIntArray(String num){ int[] myArray = new int[num.length()]; for(int i = 0; i < num.length(); i++){ myArray[i] = Integer.parseInt(num.substring(i, i + 1)); } myArray = reverse(myArray); return myArray; } /* * Reverses an int array */ private static int[] reverse(int[] myArray){ int[] reversed = new int[myArray.length]; for(int i = 0; i < myArray.length ; i++){ reversed[i] = myArray[myArray.length - (i + 1)]; } return reversed; } }
Algoritmo Base 64 (Documentación)
/** * @see https://www.jc-mouse.net/ * @author mouse */ public class Base64SIN { public static String convert(int value){ String[] dictionary = { "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", "+", "/" }; int quotient = 1; int remainder; String word = ""; while (quotient > 0) { quotient = value / 64; remainder = value % 64; word = dictionary[remainder] + word; value = quotient; } return word; } }
Algoritmo Alleged RC4 (Documentacion)
/** * @see https://www.jc-mouse.net/ * @author Mouse */ public class AllegedRC4 { /** * Retorna mensaje encriptado * @param message mensaje a encriptar * @param key llave * @return String mensaje encriptado */ public static String encryptMessageRC4(String message, String key){ int state[] = new int[256]; int x=0; int y=0; int index1=0; int index2=0; int nmen; String messageEncryption=""; for(int i=0;i<=255;i++){ state[i]=i; } for(int i=0;i<=255;i++){ //Index2 = ( ObtieneASCII(key[Index1]) + State[I] + Index2 ) MODULO 256 index2 = ( ( (int) key.toCharArray()[index1] ) + state[i] + index2) % 256; //IntercambiaValor( State[I], State[Index2] ) int 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(int 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] ) int aux = state[x]; state[x] = state[y]; state[y] = aux; //NMen = ObtieneASCII(Mensaje[I]) XOR State[(State[X] + State[Y]) MODULO 256] nmen = ( (int) message.toCharArray()[i]) ^ state[(state[x]+state[y]) % 256]; //MensajeCifrado = MensajeCifrado + "-" + RellenaCero(ConvierteAHexadecimal(NMen)) String nmenHex = Integer.toHexString(nmen).toUpperCase(); messageEncryption = messageEncryption + "-" + ((nmenHex.length()==1)?("0"+nmenHex):nmenHex); } //RETORNAR ObtieneSubCadena(MensajeCifrado, 1, LargoCadena(MensajeCifrado) - 1); return messageEncryption.substring(1,messageEncryption.length()); } /** * Retorna mensaje encriptado sin guion (-) * @param message mensaje a encriptar * @param key llave * @return String mensaje encriptado */ public static String encryptMessageRC4Unscripted(String message,String key){ String resul = encryptMessageRC4(message, key); resul=resul.replace("-",""); return resul; } }
Clase para generar código de control: ControlCode.java (Documentación)
Una vez que tenemos listos las clases de los diferentes algoritmos, guiándonos con la documentacion de SIN desarrollamos la siguiente clase:
import java.math.BigDecimal; import java.util.ArrayList; /** * @see https://www.jc-mouse.net/ * @author Mouse */ public class ControlCode { //datos para generar codigo de control private String authorizationNumber; //Número de Autorización private String invoiceNumber; //Número de Factura private String NITCI; private String dateOfTransaction; //Fecha de la Transacción private String transactionAmount; //Monto de la Transacción private String dosageKey;//Llave de Dosificación //otras variables 🙂 private String fiveDigitsVerhoeff; private String stringDKey; private int sumProduct; private String base64SIN; /** * Constructor de clase */ public ControlCode(){} /** * Genera el codigo de control * @param aNumber Numero de autorizacion * @param iNumber Numero de factura * @param nitci NIT o CI * @param dTransaction fecha de transaccion de la forma: * 2007/07/02 a 20070702 * @param tAmount Monto de la transacción * @param dKey Llave de dosificacion * @return String Codigo de control */ public String generate(String aNumber, String iNumber, String nitci, String dTransaction, String tAmount, String dKey){ this.authorizationNumber = aNumber; this.invoiceNumber = iNumber; this.NITCI =nitci; this.dateOfTransaction = dTransaction; this.transactionAmount = roundUp(tAmount); this.dosageKey = dKey; /* ========== PASO 1 ============= */ invoiceNumber = addVerhoeffDigit(invoiceNumber,2); NITCI = addVerhoeffDigit(NITCI,2); dateOfTransaction = addVerhoeffDigit(dateOfTransaction,2); this.transactionAmount = addVerhoeffDigit(transactionAmount,2); //se suman todos los valores obtenidos Long sumOfVariables = Long.valueOf(invoiceNumber) + Long.valueOf(NITCI) + Long.valueOf(dateOfTransaction) + Long.valueOf(transactionAmount); //A la suma total se añade 5 digitos Verhoeff String sumOfVariables5Verhoeff = addVerhoeffDigit(String.valueOf(sumOfVariables),5); /* ========== PASO 2 ============= */ fiveDigitsVerhoeff = sumOfVariables5Verhoeff.substring(sumOfVariables5Verhoeff.length()-5); String[] ary = fiveDigitsVerhoeff.split("");//java 8 int[] numbers = new int[ary.length]; for(int i=0;i<ary.length;i++){ numbers[i] = Integer.parseInt(ary[i]) + 1; } String string1 = dosageKey.substring(0, numbers[0] ); String string2 = dosageKey.substring(numbers[0], numbers[0]+numbers[1] ); String string3 = dosageKey.substring(numbers[0]+numbers[1], numbers[0]+numbers[1]+numbers[2] ); String string4 = dosageKey.substring(numbers[0]+numbers[1]+numbers[2], numbers[0]+numbers[1]+numbers[2]+numbers[3] ); String string5 = dosageKey.substring(numbers[0]+numbers[1]+numbers[2]+numbers[3], numbers[0]+numbers[1]+numbers[2]+numbers[3]+numbers[4] ); String authorizationNumberDKey = authorizationNumber + string1; String invoiceNumberdKey = invoiceNumber + string2; String NITCIDKey = NITCI + string3; String dateOfTransactionDKey = dateOfTransaction + string4; String 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 String keyForEncryption = dosageKey + fiveDigitsVerhoeff; //se aplica AllegedRC4 String allegedRC4String = AllegedRC4.encryptMessageRC4Unscripted(stringDKey, keyForEncryption); /* ========== PASO 4 ============= */ //cadena encriptada en paso 3 se convierte a un Array ArrayList<Character> chars = new ArrayList(); for (char c : allegedRC4String.toCharArray()) { chars.add(c); } //se suman valores ascii int totalAmount=0; int sp1=0; int sp2=0; int sp3=0; int sp4=0; int sp5=0; int tmp=1; for(char c:chars){ totalAmount += (int)c; switch(tmp){ case 1: sp1 += (int)c; break; case 2: sp2 += (int)c; break; case 3: sp3 += (int)c; break; case 4: sp4 += (int)c; break; case 5: sp5 += (int)c; 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) int tmp1 = totalAmount*sp1/numbers[0]; int tmp2 = totalAmount*sp2/numbers[1]; int tmp3 = totalAmount*sp3/numbers[2]; int tmp4 = totalAmount*sp4/numbers[3]; int tmp5 = 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); }//end:generateControlCode /** * 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 */ private String addVerhoeffDigit(String value,int max){ for(int i=1;i<=max;i++) value += Verhoeff.generateVerhoeff(value); return value; } /** * Redondea hacia arriba * @param value cadena con valor numerico de la forma 123 123.4 123,4 */ private String roundUp(String value){ //reemplaza (,) por (.) value = value.replace(",", "."); //redondea a 0 decimales BigDecimal valueBD = new BigDecimal(Double.parseDouble(value)); valueBD = valueBD.setScale(0, BigDecimal.ROUND_HALF_UP); return String.valueOf(valueBD); } /* metodos usados solo para realizar el testeo */ public String getFiveDigitsVerhoeff() { return fiveDigitsVerhoeff; } public String getStringDKey() { return stringDKey; } public int getSumProduct() { return sumProduct; } public String getBase64SIN() { return base64SIN; } }//end:class
Test
Impuestos brinda recursos necesarios para verificar la correcta generación del Código de Control.
Para testear este código haremos uso del archivo de texto con 5000 registros, la clase es la siguiente:
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import net.jc_mouse.controlcode.ControlCode; /** * @see https://www.jc-mouse.net/ * @author mouse */ public class Main { /** * @param args the command line arguments */ public static void main(String[] args) { ControlCode controlCode = new ControlCode(); //direccion del archivo de texto String fileName = "C:\\Users\\SOYTUBURLA\\Documents\\impuestos\\5000CasosPruebaCCVer7.txt"; int count=0; int fiveDigitsVerhoeffCount=0; int stringDKeyCount=0; int sumProductCount=0; int base64SINCount=0; int ccCount=0; try (BufferedReader br = new BufferedReader(new FileReader(fileName))) { String line; while ((line = br.readLine()) != null) { count+=1; //reemplaza "|" por "/-/" por no ser compatible con el metodo split line = line.replace("|", "/-/"); String[] ary = line.split("/-/"); //genera codigo de control String cc = controlCode.generate(ary[0], ary[1], ary[2], ary[3].replace("/", ""), ary[4], ary[5]); //controla errores if(!ary[6].equals(controlCode.getFiveDigitsVerhoeff()))fiveDigitsVerhoeffCount+=1; if(!ary[7].equals(controlCode.getStringDKey()))stringDKeyCount+=1; if(!ary[8].equals(String.valueOf(controlCode.getSumProduct())))sumProductCount+=1; if(!ary[9].equals(String.valueOf(controlCode.getBase64SIN())))base64SINCount+=1; if(!ary[10].equals(cc))ccCount+=1; } } catch (IOException e) { System.err.println(e.getMessage()); } System.out.println("Error 5 digitos Verhoeff: " + fiveDigitsVerhoeffCount); System.out.println("Error Cadena de dosificación: " + stringDKeyCount); System.out.println("Error Suma Producto: " + sumProductCount); System.out.println("Error Base64: " + base64SINCount); System.out.println("Error codigo de control: " + ccCount); System.out.println("---------------------------------------------"); System.out.println("Total Registros testeados: " + count); } }//end:class
Después de unos segundos dependiendo de la maquina que tengamos veremos algo como esto:
El código funciona 🙂
Proyecto «Control Code»
Documentación PDF
enjoy!!!
Fuente: www.impuestos.gob.bo
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! :)
La clase hashMap es muy util para almacenar objetos de la forma ( Clave, Objeto ), donde Clave es un identificador único...
Si diseñamos paginas web, nos habremos topado con el problema de colocar una imagen de fondo y que este se adapte a toda...
Hace tiempo pidieron un video tutorial sobre como crear sus propios componentes swing java, lamentablemente debo decir q...
Si nuestra aplicación tiene que llevar a cabo un cierto trabajo que no sabemos cuanto durará y que a la vez consume much...
Cuarta parte de esta pequeña serie de tutoriales sobre [Crea tu blog con el patrón MVC y php] En esta cuarta entrega ver...
Ya tengo varios post sobre conexión a base de datos en varios lenguajes y claro no podía faltar c#, el siguiente tutoria...
Los comentarios estan cerrados
Por lo general se usan transacciones a nivel base de datos y posteriormente se llaman estos a través de procedimientos a...
En este post, aprenderemos como conectar Visual Basic 6 con SQL Server, abrir una tabla, leer su contenido y mostrar est...
Lo que veremos en este post es la configuración del driver para PHP de SQL Server que ha creado Microsoft el cual permit...
Google Bard la inteligencia artificial de Google se actualiza con una mejora que entra a competir con el resto de IAs y...
Gracias sirvio de muhco eres un maestro. Haber si pasas el de la nueva normativa con el codigo QR
la que actualmente se usa y es el de esta pagina es la v7 según la pagina de impuestos. El generador de código QR lo estaré posteando en los siguientes días. saludos
Hola Mouse,
Te comento que queremos implementar la solución de timbrado con Bolivia pero no termino de tener claro cual es el documento vigente para el 2017.
¿Podrías ayudarme con ese dato?
Un saludo, muchas gracias!
adjunto en el post están los documentos de la pagina de Impuestos Bolivia, segun entiendo en la web el Codigo de Control V7 es el que esta vigente, pero le sugiero visitar la web de http://www.impuestos.gob.bo/ y preguntar ahi para una respuesta oficial. saludos
Felicidades por la gran colaboracion para los desarrolladores …. Fue de mucha ayuda
Solo una pregunta ¿El codigo qr debe estar si o si en la factura ? y si es asi donde encontrar el algoritmo para generarlo
Hola, el código QR si debe ir en la factura, te recomiendo leer la documentación de Impuestos Nacionales, ahí te indican entre otras cosas, el tamaño y calidad del QR.
Para generar el Código QR existen, según el lenguaje que utilices, existen varios plugins en la internet que te facilitaran el trabajo
Hola Mouse, Soy de Argentina, he leido con atención tu muy buena descripcion sobre los algoritmos para la generación de los códigos de control, pero estoy por instalar un software de cobro para un estacionamiento en Bolivia, y no conozco como debe ser el formato de la boleta a imprimir o si hay algun modelo de impresora en particular que este homologada o si no es necesario imprimirla.
A donde puedo recurrir o con quien podria hablar para que me asesore profesionalmente.
Grcias
le recomiendo visitar la pagina oficial de Impuestos Bolivia y descargar la documentación sobre la Facturación Virtual ahí le explican el formato de las facturas, tipo de papel aceptado y demás cosas.
saludos