VirtualPAC es un ecosistema que tiene vida propia, con mas de 35,000 emisores registrados, emitiendo un promedio de 25 millones de facturas anuales, que son como 70,000 facturas diarias, de emisores de todo tipo, desde la pequeña tienda de la esquina, hasta el gran retailer, pasando por un sinfin de negocios y actividades profesionales.
Cada factura es un pequeño universo: que si las escuelas deben llevar el complemento concepto de IEDU, que si las agencias de coches usan 2 complementos, uno para autos nuevos y otro para autos usados, que si los honorarios médicos no llevan IVA, pero los otros honorarios llevan IVA y retención de ISR, que si los transportistas hacen retencion del 4% del IVA, que si los hoteles llevan complemento de otros y impuestos y derechos, que si las constructoras llevan un complemento especial si están construyendo vivienda, en fin, miles y miles de posibles combinaciones para un solo comprobante.
Cuando se diseñó VirtualXML, sabíamos que tarde o temprano, dado el volúmen de información que se maneja y por la ley de los grandes números, surgirían problemas y dudas en el uso de las funciones de VirtualXML para la correcta elaboración del XML del CFDI, ¿ usan los programadores correctamente las funciones ?, ¿ se utilizan valores correctos en los parámetros ?, si surge un error, ¿ es culpa del usuario o es culpa de la librería o es culpa del PAC ?; es por eso que decidimos implementar un sistema de "depuración" (debugger) en VirtualXML, que nos ayudara a dos cosas: la primera: revisar si el usuario utilizó correctamente la librería y la segunda, en caso de error, saber el como, cuando, donde y porqué del mismo. Ese sistema de depuración es una bitácora del procesamiento de un documento CFDI 3.3 y se llama: VirtualXML.LOG.
VirtualXML.LOG es un archivo de texto que comienza a generarse nada mas hacer una llamada a la funcion:
VirtualXML_New(), a partir de que tu llames a esta función, ya sea usando la DLL de funciones o bien usando el EXE externo VirtualXML.EXE, el archivo de bitácora VirtualXML.LOG se irá escribiendo en disco con importante información sobre el procesamientoo del documento que estás creando.
En este artículo del blog te mostraré como interpretar el contenido de la bitácora VirtualXML.LOG, alguno que otro truco escondido de la librería y verás que es muy sencillo localizar y solucionar tu mismo la mayoría de los errores que te pueden surgir con el uso de VirtualXML.
El archivo de bitácora VirtualXML.LOG esta dividido en 5 secciones:
- Sección de versión del producto e información general
- Sección de uso de funciones
- Sección de procesamiento del documento
- Sección del XML generado
- Sección de resultados de la operación
A continuación te explico sección por sección su contenido y como puedes utilizarlo para saber en que está mal (o bien) tu CFDI 3.3:
1. Sección de versión del producto e información general
Esta sección ocupa los primeros 5 renglones del archivo de bitácora VirtualXML.LOG y contiene algo similar a esto:
|*|VirtualXML|vxml180110v1.0.4.4 Plataforma: x86 XP2K3|*|
|*|TickCount Start|1454445656|*|
|*|Sistema|Microsoft Windows XP Professional Service Pack 3 (build 2600)|*|
|*|ejecutable|U:\Sistemas.Cla\Exe6\ZeuEkms.exe|*|
|*|hXml|30780528|*|
En esta sección la primer línea es la mas importante de todas porque nos indica LA FECHA de la DLL que estamos usando así como su número de versión:
|*|VirtualXML|vxml180110v1.0.4.4 Plataforma: x86 XP2K3|*|
Donde dice vxml180110v1.0.4.4 encontrarás la fecha de creación de la DLL, y por si no lo sabias, el 180110 es la fecha que en este caso corresponde al año 2018 (18) Enero (01) día 10, la DLL que generó este archivo VirtualXML.LOG es la publicada el 10 de Enero de 2018.
A continuación viene la versión del producto: 1.0.4.4 y finalmente para qué plataforma fue creada, en este caso x86 XP2K3 indica que estás usando la versión especial compilada para Windows XP y 2003 server, en caso de que estés usando la compilación para otros sistemas operativos, aparecerá Plataforma: x86 o Plataforma: x64, dependiendo de la versión de la DLL que estés usando.
Esto me lleva a realizar algunas pequeñas aclaraciones que me ha comentado algunos de nuestros usuarios:
- ¿ Porqué 2 versiones de la misma DLL ?.
Pues porque tuvimos que hacer una versión compilada de manera distinta para Windows XP y Windows 2003 Server. En algunas versiones de estos sistemas operativos antiguos el uso de la DLL o del EXE indicaba que no eran archivos Windows válidos cuando los ejecutabas, al final descubrimos que era la forma en que se compilaban los productos, lo que nos llevó a tener que compilar una versión para los sistemas operativos viejos, y otra versión para los sistemas operativos mas nuevos (Windows 7, 8, 10 y 2008 y 2016 Server).
Lo mas curioso es que el producto de compilación "estándar" funcionó bien en la mayoría de los Windows XP, pero en la mayoría de los 2003 Servers no funcionaba, probamos distintas combinaciones de Services Pack, y .NET framework pero nunca pudimos encontrar con lo que fallaba exactamente hasta que cambiamos los parámetros de compilación del código fuente en Visual Studio 2012 y el nuevo producto compilado funcionó sin problemas en estas versiones.
¿ Debes cambiar tu DLL "estandar" por la especial para WindowsXP/2003 Server ?, depende, prueba primero si la compilación estándar funciona con tu sistema operativo, si no lo hace, utiliza la compilación especial para los sistemas operativos antiguos.
-¿ Porqué no puedo usar la versión de 64 bits de VirtualXML.DLL si mi sistema operativo es de 64 bits ?:
El uso de las versiones de 64 bits no depende del Sistema Operativo sino del lenguaje de programación, erróneamente muchos usuarios piensan que si tienen sistemas operativos de 64 bits, solo pueden usar aplicaciones o componentes de 64 bits y eso en realidad no es cierto. El aprovechamiento de las DLLs de 32 o 64 btis depende del lenguaje de programación.
No todos los lenguajes de programación generan EXEs de 64 bits, nuestros usuarios de .NET y Delphi 7 en adelante, pueden generar exes de 64 bits que pueden utilizar las versiones de VirtualXML de 64bits, otros lenguajes como VB6, FoxPro, Harbour, etc, deberán utilizar las DLLs de 32 bits.
Regresando al tema, la siguiente linea:
|*|TickCount Start|1454445656|*|
Es un contador de "ticks" que usamos internamente para medir el tiempo que toma generar un comprobante CFDI, no tiene ningún uso fuera del interno.
La siguiente linea es obvia, versión del sistema operativo donde se ejecutó la DLL o el EXE externo de VirtualXML:
|*|Sistema|Microsoft Windows XP Professional Service Pack 3 (build 2600)|*|
Seguimos con el nombre del archivo EXE que mandó llamar a la DLL (no aparece en el caso del VirtualXML EXE externo:
|*|ejecutable|U:\Sistemas.Cla\Exe6\ZeuEkms.exe|*|
Y finalmente el valor del "handle", dirección de memoria de la computadora donde se almacena el XML que la ejecución de las funciones de VirtualXML han generado, en realidad para efectos de depuración del XML no sirve para nada y no se por qué no lo hemos quitado.
Como verás con esta información nuestra área de soporte puede saber si estás usando una DLL actualizada, en que versión del sistema operativo la estas ejecutando y si necesitarías utilizar la versión estándar de VirtualXML o la versión especial para Windows XP/2003 Server (por eso siempre les pido
"mándame el archivo VirtualXML.LOG" ).
2. Sección de uso de las funciones:
Esta segunda sección nos indica qué funciones de VirtualXML utilizaste, en que orden las mandaste llamar y que parámetros utilizaste para cada función, es una copia exacta de lo que escribiste en tu programa solo que expresado de una manera distinta:
|>|VirtualXML_New|3.3|<|
|>|VirtualXML_SetVirtualPACInfo|demo_cibertec|demo|<|
|>|VirtualXML_SetComprobanteInfo_cfdi33|ING|73|%cb_date|01||100.00||MXN||116.00|I|PUE|20010||<|
|>|VirtualXML_SetComprobanteFecha|2018-01-12T17:26:21|<|
|>|VirtualXML_SetEmisorInfo_cfdi33|AAA010101AAA|PRUEBA|601|<|
|>|VirtualXML_SetReceptorInfo_cfdi33|ASY130611V91|A&A SYNERGY SADEC.V|||G03|<|
|>|VirtualXML_AddConcepto_cfdi33|80131501||1.00|H87|PIEZA UNITARIA|RENTA DE UN MES|100.00|100.00||<|
|>|VirtualXML_AddConceptoTraslado_cfdi33|100.00|002|Tasa|0.16|16.00|<|
|*|Info|Creating Concepto.Impuestos|*|
|>|VirtualXML_AddConceptoInformacionAduanera_cfdi33|16 22 4584 7 654321|<|
|>|VirtualXML_SetImpuestosInfo_cfdi33|16.00||<|
|>|VirtualXML_AddTraslado_cfdi33|002|Tasa|0.16|16.00|<|
|>|VirtualXML_ProcesaDocumento|C:\AAA010101AAA.CER|C:AAA010101AAA.KEY|12345678a|C:\XML\AAA010101AAA_ING_73.XML|<|
La información anterior nos permite reproducir en nuestras instalaciones la secuencia exacta del uso de las funciones de VirtualXML que tu utilizaste en tu programa, ya que el orden en el que se utilizan es tan importante como la lista de parámetros que usas.
¿ Como usamos esta sección para reproducir tu error ?, muy sencillo: a través de
VirtualXML.EXE
VirtualXML.EXE es un "interprete" de las funciones de la librería VirtualXML.DLL que funciona desde la línea de comandos y utiliza un pseudolenguaje de programación basado en funciones (que son las mismas que VirtualXML.DLL) para generar un CFDI 3.3 sin necesidad de un programa o un lenguaje de programación, simplemente pones tus instrucciones en un archivo de texto, y ejecutas desde la línea de comandos:
c:\VirtualXML ArchivoDeTextoConInstrucciones.vxml
Y podemos firmar, sellar y timbrar TU documento en nuestras instalaciones.
¿ Como resolvemos el tema de los certificados ?, muy sencillo: utilizamos los certificados de demo para el RFC de pruebas AAA010101AAA, y lo que hacemos es simplemente editar el archivo de bitácora VirtualXML.LOG, seleccionar la sección de instrucciones, copiarla y pegarla en un nuevo archivo en blanco, cambiar las credenciales de VirtualPAC en la función
VirtualXML_SetVirtualPACInfo() para utilizar nuestras credenciales de prueba, cambiamos el RFC del emisor por AAA010101AAA, y sustituimos los datos del certificado en la llamada a la función
VirtualXML_ProcesaDocumento() por los certificados de pruebas.
Luego ejecutamos VirtualXML.EXE pasando como parámetro el nombre del archivo creado y vemos los resultados.
Usando esta sección podemos identificar si te equivocaste en el nombre de alguna función, mandaste un parámetro inválido, si estas usando todas las funciones correctamente y en general, damos un vistazo sobre la secuencia de instrucciones que seguiste para generar tu XML, es en esta sección donde te podemos corregir como usas la librería.
3. Sección de procesamiento del documento.
Esta sección es de uso interno para nosotros, y nos indica que pasos va siguiendo el procesamiento del XML para realizar primeramente el sellado y firmado del documento, es decir, generar el sello y la firma con tus certificados, esta sección se parece un poco a esto:
|*|finalCleanup|Inicia|*|
|*|finalCleanup.Addenda|Nodo.Removido|*|
|*|finalCleanup|Termina|*|
|*|Validando Documento|.....|*|
|*|Validacion|Addenda no encontrada ... OK|*|
|*|Firmando documento|.....|*|
|*|ValidaFechaRFC.safeRFC|GOAR720320KF7|*|
|*|Sello.cfdi33.SHA256|CxCXXy7/g4Bltqr30OlwThrvZYI5fcHzC/8t4wq63YIFCZQ+IA5Ogp8y0EnZash9IloSMXN3SLVR5fucNaSv7cehhyuu1A3X7T9XnmReg8+29Xq1ryD79fdn7kTTvUJB519jqOrZxPGb//vDmz3BUnXdEC4jHz6nGVh7Kuj93xrT+1rEnvgnewL3Bgvt0ftnndzkMrCTe/TPFGiqXbYA9F1EABuR8mKAH/MGB1WU43n18uOvEk37tgwFBdu/43J9ae4NYIi0vJCFXNISEK6PpfKSuFe2r9J/FZU0lKnlp5t58vRC/FkDsc6jlTiq+2vT4AigJkM5IVngwh9IWAvr3A==|*|
|*|SellaDocumento|OK|*|
|*|DocumentoFirmado|
Esta sección nos indica que funciones internas de la DLL va siguiendo el procesamiento del documento, como por ejemplo finalCleanup, Validacion, Firmado, obtención del sello digital en formato SHA256 y resultado del proceso de sellado digital y firmado del documento.
Si por alguna razón VirtualXML llegara a fallar durante este proceso, y el programa abortará su ejecución, esta sección nos indicará cual fue la última función que se ejecutó con éxito y podremos revisar el código fuente de la librería para ver donde y porqué no se ejecutó la función que causó el fallo.
Si todo el proceso de firmado y sellado fue exitoso, entonces tendremos la siguiente sección que es:
3. Sección del XML generado.
En las primeras versiones de VirtualXML, allá por el año 2012, 2013, esta sección no existía, asumíamos que VirtuaXML no fallaba nunca y que siempre entregaba resultados exactos (lo cual era cierto para CFDI 3.2).
Con la entrada de la Nómina 1.2, el uso de catálogos y los nuevos XMLs condicionales, nos dimos cuenta de que incluir el XML tal cual lo había generado la librería, depués de firmarlo y sellarlo digitalmente y antes de enviarlo al PAC para generar el timbre fiscal digital, sería una gran idea para ayudarnos a depurar no solo la sintáxis, sino también el contenido del XML.
Un XML de CFDI pasa por 3 controles se seguridad, el primer control te lo da VirtualXML ya que las funciones que generan los distintos nodos y atributos del XML estan basadas en los XSD publicados por el SAT para CFDI 3.3 y todos sus complementos, por lo tanto VirtualXML, si lo tienes actualizado, generará y validará nodos y atributos de acuerdo a lo publicado con el SAT.
El segundo nivel de seguridad también lo da VirtualXML, al realizar el proceso de firmado digital del documento usando el archivo .CER de CSD (Certificado de sello digital) y sellando digitalmente el documento previa generación de la cadena original, digesto SHA256 y encriptamiento del mismo usando el algoritmo RSA Encrypt.
Estas dos primeras revisiones las realiza directamente VirtualXML y son transparentes para tí, tu no tienes que saber como generar la firma digital o el sello, VirtualXML lo hace por tí.
El tercer control de seguridad lo aplica el PAC sobre un XML que ya tiene que estar firmardo y sellado digitalmente; antes de generar el timbre fiscal digital, el PAC realiza una validación de forma, sintaxis y contendido del XML enviado, validación similar a la que hacía el SAT al documento en la versión 3.2, si por alguna razón esta validación llegara a fallar como por ejemplo con el error "el sello de comprobante no es válido", contar con el XML completo nos puede ayudar a determinar donde estuvo la falla en el contenido del archivo.
Existen herramientas como el editor
oXygen XML que simplemente copiando el XML del archivo VirtualXML.LOG y pegándolo dentro de este editor te puede decir qué datos de los atributos son válidos y cuales no de acuerdo a los XSD publicados por el SAT.
Esta sección se ve mas o menos así dentro del archivo de bitácoraVirtualXML.LOG:
<CiberSAT5 PAC="VirtualPAC" usuario="demo_cibertec" servidor="demo">
<cfdi:Comprobante xmlns:cfdi="http://www.sat.gob.mx/cfd/3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv33.xsd"
Version="3.3"
Serie="ING"
Folio="73"
Fecha="2018-01-12T17:26:21"
Sello="."
FormaPago="01"
NoCertificado="12345678901234567890"
Certificado="."
SubTotal="100.00"
Moneda="MXN"
Total="116.00"
TipoDeComprobante="I"
MetodoPago="PUE"
LugarExpedicion="20010">
<cfdi:Emisor Rfc="AAA010101AAA" Nombre="PRUEBA" RegimenFiscal="601"/>
<cfdi:Receptor Rfc="ASY130611V91"
Nombre="A&amp;A SYNERGY S. A DE C.V" UsoCFDI="G03"/>
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="80131501"
Cantidad="1.00"
ClaveUnidad="H87"
Unidad="PIEZA UNITARIA"
Descripcion="RENTA DE UN MES"
ValorUnitario="100.00" Importe="100.00">
<cfdi:Impuestos>
<cfdi:Traslados>
<cfdi:Traslado Base="100.00"
Impuesto="002"
TipoFactor="Tasa"
TasaOCuota="0.16"
Importe="16.00"/>
</cfdi:Traslados>
</cfdi:Impuestos>
<cfdi:InformacionAduanera NumeroPedimento="16 22 4584 7 654321"/>
</cfdi:Concepto>
</cfdi:Conceptos>
<cfdi:Impuestos TotalImpuestosTrasladados="16.00">
<cfdi:Traslados>
<cfdi:Traslado Impuesto="002"
TipoFactor="Tasa"
TasaOCuota="0.16"
Importe="16.00"/>
</cfdi:Traslados>
</cfdi:Impuestos>
<cfdi:Complemento>
<tfd:TimbreFiscalDigital xmlns:tfd="http://www.sat.gob.mx/TimbreFiscalDigital" xsi:schemaLocation="http://www.sat.gob.mx/TimbreFiscalDigital http://www.sat.gob.mx/sitio_internet/cfd/TimbreFiscalDigital/TimbreFiscalDigitalv11.xsd" Version="1.1" UUID="00000000-0000-0000-0000-000000000000" FechaTimbrado="2011-01-01T00:00:00" RfcProvCertif="AAA010101AAA" SelloCFD="" NoCertificadoSAT="12345678901234567890" SelloSAT=""/>
</cfdi:Complemento>
</cfdi:Comprobante>
</CiberSAT5>
Digamos que esta "vista previa" del XML nos va a permitir hacer muchas cosas como por ejemplo validar un complemento o quitar y poner una addenda.
CiberTec recomienda ampliamente el uso del editor
oXygen XML para realizar la revisión de estos XML generados con VirtualXML.
Este documento XML es el que VirtualXML envía a VirtualPAC y de ahí se envía al PAC para ser timbrado, y el resultado de la operación de timbrado se reporta en la última sección del archivo:
5. Sección de resultados de la operación:
La última sección del archivo de bitácora VirtualXML.LOG contiene los resultados de la operación de timbrado, cabe mencionar que todos los mensajes de error del XML no son reportados por VirtualXML ni por el servicio de VirtualPAC, es el PAC directamente el responsable de generar los mensajes de error.
El SAT provee al PAC de una "matriz de errores", esta matriz de validación le indica al PAC que debe validar del XML, y que error reportar cuando este se genera, eso está muy bien porque POR FIN, tenemos una lista de errores estándar para todos los PACs.
La matriz de validación es simplemente un archivo en Excel que incluso está publicado por el SAT y que puedes descargar haciendo click aqui:
Matriz de errores CFDI 3.3.
Localizar el error en la matriz de errores es muy sencillo ya que solo son 98 posibles errores en todo el proceso del CFDI 3.3
Si tu XML se timbró correctamente, entonces en esta sección debería de aparecer algo como esto:
*|Timbrando documento|.....|*|
|*|MODO|##### PROD ##### PROD ##### PROD ##### PROD ##### PROD ##### PROD ##### PROD ##### PROD ##### PROD ##### PROD #####|*|
|*|TFD.Version|1.1|*|
|*|GeneraCBB.qcode|https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx?id=74413E69-3CC1-4079-B886-449CD10F00B8&re=AAA010101AAA&rr=CTE940531F58&tt=1102.000000&fe=uFiSSQ==|*|
|*|GeneraCBB.Arch.x86|Use external CiberCBB.dll|*|
|*|GeneraCBB.CiberCBB|Loaded|*|
|*|GeneraCBB.CiberCBB|Encode2File|*|
|*|GeneraCBB.CiberCBB|Free|*|
|*|GeneraCBB.CiberCBB|Bmp.Created|*|
|*|Resultado de proceso|VIRTUALXML_OK|*|
|>|VirtualXML_Free|<|
|*|BEGIN RESULTADOS|.....|*|
|*|VIRTUALXML_GET_DESCERROR|VIRTUALXML_OK|*|
|*|VIRTUALXML_GET_ERROR||*|
|*|VIRTUALXML_GET_CSDNUMBER|00001000000306430645|*|
|*|VIRTUALXML_GET_SELLO|MGDrLjB1CRBw1xRpxNw+6aG0Y962/ztZqFfqcDgU1/pmqTRt+CBr0uIjpvnCU8x0enSvJV9/YH8hi75TsrFF15Yda0uFPLWMSg8674Tc6Us8IPBQBmTi57CRVWwSNZkstvOynFXZMaX8yPx36PHAgQ20X4h5yFK/O3H5CH2t018=|*|
|*|VIRTUALXML_GET_CADENA|||3.3|A|8|2018-01-16T18:54:38|01|00001000000306430645|Contado Comercial|172.41|0.00|MXN|199.99|I|PUE|84623|MOME661206TN5|JOSE ELIGIO MORAN MIRANDA|612|ROMN711129S27|NOEMI ROJAS MERAZ|P01|30171708|1|MTR|Metro|Vidrio claro 3mm|172.41|172.41|172.41|002|Tasa|0.160000|27.58|002|Tasa|0.160000|27.58|27.58|||*|
|*|VIRTUALXML_GET_SATCSDNUMBER||*|
|*|VIRTUALXML_GET_SATSELLO||*|
|*|VIRTUALXML_GET_SATCADENA||*|
|*|VIRTUALXML_GET_SATUUID||*|
|*|VIRTUALXML_GET_SATFECHA||*|
|*|VIRTUALXML_GET_SATRFCPROVCERTIF||*|
|*|VIRTUALXML_GET_SATLEYENDA||*|
|*|VIRTUALXML_GET_CSDINI|150317201349Z|*|
|*|VIRTUALXML_GET_CSDFIN|190317201349Z|*|
|*|VIRTUALXML_GET_VPID|75419|*|
|*|VIRTUALXML_GET_DISP|95|*|
|*|VIRTUALXML_GET_DLLVERSION|vxml170816v1.0.3.5|*|
|*|VIRTUALXML_GET_FECHAXML|2018-01-16T18:54:38|*|
|*|VIRTUALXML_GET_WARN||*|
|*|VIRTUALXML_GET_PAC|DFA|*|
|*|VIRTUALXML_GET_REVISION|x86|*|
|*|END RESULTADOS|.....|*|
|*|TickCount End|5828836|*|
Este mensaje indica en que modalidad se timbró el documento: producción (PROD) o demo (DEMO), te indica también que versión del TFD (Timbre Fiscal Digital) usaste, en este caso la 1.1.
La siguiente parte de esta sección te indica como se generó el CBB (Código de Barras Bidimensional) de la factura:
|*|GeneraCBB.qcode|https://verificacfdi.facturaelectronica.sat.gob.mx/default.aspx?id=74413E69-3CC1-4079-B886-449CD10F00B8&re=AAA010101AAA&rr=CTE940531F58&tt=1102.000000&fe=uFiSSQ==|*|
|*|GeneraCBB.Arch.x86|Use external CiberCBB.dll|*|
|*|GeneraCBB.CiberCBB|Loaded|*|
|*|GeneraCBB.CiberCBB|Encode2File|*|
|*|GeneraCBB.CiberCBB|Free|*|
|*|GeneraCBB.CiberCBB|Bmp.Created|*|
Hagamos un breve paréntesis para explicar como VirtualPAC y VirtualXML manejan el tema de los códigos CBBs.
Si haz observado, cuando timbras exitosamente un documento obtienes dos códigos de barras bidimensionales (o QR Code) en 2 formatos gráficos distintos, uno en formato PNG y otro en formato BMP, ambos contienen la misma información aunque la imagen sea distinta, si tu aplicas un lector de códigos QR que puedes descargar a tu telefono (nosotros recomentadmos la app
i-nigma para la lectura de los códigos de barras, está disponbile para Android y para iOS) verás que aunque la imagen sea distinta, el contenido es el mismo.
¿ Porqué generamos 2 códigos de barras en 2 formatos gráficos distintos ?, por compatibilidad, no por gusto, créanme si les digo que yo preferiría usar un solo código de barras (el PNG) pero los lenguajes de programación antiguos no siempre ofrecen soporte para manejar otros formatos gráficos que no sean BMPs y es por esa razón que generamos 2 archivos de CBB.
Lo interesante es quién genera cada código, el PNG es generado en el servicio de VirtualPAC, es decir en nuestros servidores de producción de timbrado, y es devuelto como respuesta a la petición de timbrado si esta fue exitosa, VirtualXML, del lado del usuario, lee la respuesta de VirtualPAC y escribe en disco la imagen PNG devuelta por el servicio de VirtualPAC.
Mientras que el archivo PNG se genera de manera remota, en nuestros servidores, el archivo BMP se genera de manera local en la computadora del usuario, y para generarse tiene 2 maneras: sin dependencias o con dependencias.
VirtualXML tiene construida su propia función para generar CBBs en formato BMP, dicha función es
VirtualXML_GeneraCBB() que puedes usar desde tus programas para generar tus propios códigos de barras bidimensionales, solo sigue los parámetros de la documentación.
VirtualXML_GeneraCBB() puede generar los códigos de barras en formato BMP por sí misma ya que tiene interconstruída la rutina de generación de CBB byte por byte o bien, puede utilizar una "dependencia" en este caso de un viejo conocido que es la DLL CiberCBB.DLL misma que hemos venido utilizando desde los viejos tiempos del CFD, allá por el años 2010.
Si VirtualXML.DLL encuentra en el mismo directorio donde esté instalado la DLL CiberCBB.DLL entonces hará uso de esta para generar los códigos de barras bidimensionales (con depedencia), si no la encuentra, entonces utilizará la función interna nativa.
¿ Porqué 2 maneras de generar el mismo archivo ?, regresamos al tema de la compatibilidad, cuando desarrollamos estas funciones, detectamos que muchos lenguajes de programación viejos no podían leer el CBB en formato BMP generado por las funciones internas de VirtualXML, por lo que tuvimos que regresar a CiberCBB.DLL el cual genera BMPs compatibles con estos lenguajes viejos. Los lenguajes de programación mas modernos pueden utilizar ya sea la imagen en formato PNG o bien el BMP generado por las funciones internas de VirtualXML.
Por otro lado tenemos el tema de los 64 bits, debido a que no existe una versión de CiberCBB.DLL de 64 bits, la generación de los CBBs para los lenguajes de 64 bits tiene que hacerse mediante el uso de la función interna
VirtualXML_GeneraCBB(), lo cual por otro lado no tiene ningún caso porque un lenguaje de 64 bits, por su puesto puede leer imágenes en formato PNG, pero pues ahí está para que lo use quien lo necesite.
Finalmente, si todo salió bien entonces veremos este mensaje:
|*|Resultado de proceso|VIRTUALXML_OK|*|
VIRTUALXML_OK indica que el proceso de timbrado del documento fue exitoso y a continuación veremos la lista de valores retornados por el proceso de timbrado, mismos valores puedes recuperar desde tu programa usando la función
VirtualXML_GetValue()
|*|BEGIN RESULTADOS|.....|*|
|*|VIRTUALXML_GET_DESCERROR|VIRTUALXML_OK|*|
|*|VIRTUALXML_GET_ERROR||*|
|*|VIRTUALXML_GET_CSDNUMBER|00001000000306430645|*|
|*|VIRTUALXML_GET_SELLO|MGDrLjB1CRBw1xRpxNw+6aG0Y962/ztZqFfqcDgU1/pmqTRt+CBr0uIjpvnCU8x0enSvJV9/YH8hi75TsrFF15Yda0uFPLWMSg8674Tc6Us8IPBQBmTi57CRVWwSNZkstvOynFXZMaX8yPx36PHAgQ20X4h5yFK/O3H5CH2t018=|*|
|*|VIRTUALXML_GET_CADENA|||3.3|A|8|2018-01-16T18:54:38|01|00001000000306430645|Contado
Comercial|172.41|0.00|MXN|199.99|I|PUE|84623|MOME661206TN5|JOSE ELIGIO
MORAN MIRANDA|612|ROMN711129S27|NOEMI ROJAS
MERAZ|P01|30171708|1|MTR|Metro|Vidrio claro
3mm|172.41|172.41|172.41|002|Tasa|0.160000|27.58|002|Tasa|0.160000|27.58|27.58|||*|
|*|VIRTUALXML_GET_SATCSDNUMBER||*|
|*|VIRTUALXML_GET_SATSELLO||*|
|*|VIRTUALXML_GET_SATCADENA||*|
|*|VIRTUALXML_GET_SATUUID||*|
|*|VIRTUALXML_GET_SATFECHA||*|
|*|VIRTUALXML_GET_SATRFCPROVCERTIF||*|
|*|VIRTUALXML_GET_SATLEYENDA||*|
|*|VIRTUALXML_GET_CSDINI|150317201349Z|*|
|*|VIRTUALXML_GET_CSDFIN|190317201349Z|*|
|*|VIRTUALXML_GET_VPID|75419|*|
|*|VIRTUALXML_GET_DISP|95|*|
|*|VIRTUALXML_GET_DLLVERSION|vxml170816v1.0.3.5|*|
|*|VIRTUALXML_GET_FECHAXML|2018-01-16T18:54:38|*|
|*|VIRTUALXML_GET_WARN||*|
|*|VIRTUALXML_GET_PAC|DFA|*|
|*|VIRTUALXML_GET_REVISION|x86|*|
|*|END RESULTADOS|.....|*|
Veamos ahora que pasa si tenemos un error en el proceso de timbrado.
Si surgió un error en el proceso de timbrado, en donde debería decir VIRTUALXML_OK se verá algo como esto:
|*|Resultado de proceso|VIRTUALXML_ERROR_SERVER|*|
|*|Msg|El RFC del emisor no se encuentra en la lista de contribuyentes|*|
|>|VirtualXML_Free|<|
O algo como esto:
|*|Resultado de proceso|VIRTUALXML_ERROR_SERVER|*|
|*|Msg|El valor del campo Importe que corresponde a Retención no se encuentra entre el limite inferior y superior permitido.|*|
El favorito de esta semana:
|Resultado de proceso|VIRTUALXML_ERROR_SERVER|*|
|*|Msg|El TipoDeComprobante es I,E o N, el importe registrado en el campo no es igual a la suma de los importes de los conceptos registrados.|*|
Otro error pero ahora con un complemento:
|*|Resultado de proceso|VIRTUALXML_ERROR_SERVER|*|
|*|Msg|El atributo cce11:ComercioExterior:Destinatario:NumRegIdTrib no tiene un valor que exista en el registro del país indicado en el atributo cce11:ComercioExterior:Destinatario:Domicilio:Pais.|*
La forma en que se reportan los errores ahora es estándar para todos los PACs gracias a la matriz de errores del SAT, por lo que no importa con que PAC timbres, cuando ocurra un error siempre se reportarán los mismos textos informativos.
Ahora bien, tengo un error, ¿ cómo lo soluciono ?
Te diría que es muy facil, porque el mismo error te explica donde te están fallando las cosas, por ejemplo el error
"El RFC del emisor no se encuentra en la lista de contribuyentes" no es culpa ni tuya, ni mia, ni del PAC, en todo caso será culpa del contribuyente o bien del SAT que no actualizó correctamente la LCO, para mas información sobre este tema, lee mi artículo titulado:
Este RFC del receptor no existe en la lista de RFC inscritos no cancelados del SAT
En el caso del error "
El valor del campo Importe que corresponde a Retención no se encuentra entre el limite inferior y superior permitido" y del error "
El TipoDeComprobante es I,E o N, el importe registrado en el campo Total no es igual a la suma de los importes de los conceptos registrados", ambos se deben a un error de cálculo en los importes ya sea de totales, impuestos retenidos o trasladados (los malditos redondeos), para mas información, lee mi artículo:
Como redondear importes en CFDI 3.3 y no morir en el intento.
¿ Cuantos errores hay ?, según la matriz de errores del SAT existen 98 errores que pueden surgir por validación de CFDI 3.3, pero no son todos, los nuevos complementos como Nómina 1.2 y Comercio Exterior 1.1 tienen su propia matriz de errores, así que también te pueden aparecer errores de un complemento en específico como en el cuarto ejemplo que dice:
"El
atributo cce11:ComercioExterior:Destinatario:NumRegIdTrib no tiene un
valor que exista en el registro del país indicado en el atributo cce11:ComercioExterior:Destinatario:Domicilio:Pais.", en este caso, es un error propio del complemento de Comercio Exterior 1.1 y deberemos consultar la matriz de error de dicho complemento para saber como solucionarlo.
Como verás el archivo VirtualXML.LOG es una gran herramienta de ayuda para saber si estas haciendo bien o mal tus XMLs y te puede ayudar a solucionar tus problemas de una manera rápida.
Un par de trucos interesantes con VirtualXML.LOG:
Para finalizar este artículo te voy a mostrar un par de cosas que puedes hacer con VirtualXML.LOG.
Un truco interesante para aprovechar el archivo VirtualXML.LOG consiste en cambiarle el nombre, de tal manera que puedas identificar el archivo de bitácora para cada caso en específico; imagina este escenario: estás trabajando en red y almacenando los XMLs de timbrado en el disco duro del servidor, cada proceso de generación de CFDI genera un archivo VirtuallXML.LOG que siempre tiene el mismo nombre, si varios usuarios concurrentes están generando facturas al mismo tiempo, los archivos se sobreescribirán uno sobre otro, de tal manera que no tienes manera de saber a que operación de timbrado pertence el archivo VirtualXML.LOG, pero esto se puede evitar cambiando el nombre y la ubicación del archivo VirtualXML.LOG mediante la función
VirtualXML_SetLogFile().
VirtualXML_SetLogFile() te permite cambiar el nombre y la ubcación del archivo VirtualXML.LOG, asi podras tener un archivo de bitácora individual para cada proceso de timbrado, suamemente útil para saber exactamente que errores obtienes por documento enviado a timbrar.
VirtualXML_SetLogFile() debe usarse INMEDIATEMENTE DESPUES de la llamada a la función VirtualXML_New() y una vez que hayas obtenido el handler del documento CFDI, ya que por default, al llamar a la funciónVirtualXML_New() se comienza a crear automáticamente un archivo con el nombre VirtualXML.LOG, y todo el proceso se irá almacenando en este archivo a menos que se utilice la funcion VirtualXML_SetLogFile(), si esta función se utiliza, VirtualXML copiará el contenido que lleve hasta el momento el archivo VirtualXML.LOG al archivo que hayas especificado en la función VirtualXML_SetLogFile() y continuará utilizando el nuevo archivo para almacenar los resultados. hasta la llamada a la funcion
VirtualXML_Procesadocumento().
Usando este sencillo truco tendrás un archivo LOG distinto por cada proceso de timbrado que ejecutes.
Otro truco interesante consiste en ir guardando el contenido del archivo de bitácora "por partes" y esto se hace utilizando la función
VirtualXML_Save()
Esta función te permite guardar el contenido del archivoVirtualXML.LOG en cualquier momento en uno o varios archivos cuyo nombre tu puedes determinar usando el segundo parámetro de esta función.
Hasta aquí este artículo que espero que te sea de mucha utilidad, como dice el refrán:
"ayúdate que yo te ayudaré" y sin duda el saber como usar el archivo VirtualXML.LOG te será de gran utilidad para resolver tus dudas de timbrado