HotPDF genera, detecta y valida facturas electrónicas ZUGFeRD y Factur-X desde Delphi y C++Builder. AddFacturXAssociatedFile incrusta el XML de la factura EN 16931 en un contenedor PDF/A-3, DetectFacturXInvoice reconoce una factura híbrida entrante y HPDFValidateEInvoice ejecuta las reglas de negocio Schematron oficiales de la norma EN 16931 contra el XML incrustado. Este artículo recorre los tres pasos y es sincero sobre los límites del motor de validación
La presión detrás de este conjunto de características es regulatoria, no técnica. Alemania y Francia están implementando gradualmente la facturación electrónica estructurada obligatoria para transacciones B2B nacionales, y ambos mandatos convergen en el mismo modelo híbrido: la factura PDF que su ERP ya produce debe contener un XML gemelo legible por máquina en su interior. Una aplicación de contabilidad o ERP en Delphi que hoy emite facturas PDF simples tiene un plazo estricto para comenzar a emitir Factur-X o ZUGFeRD, y la diferencia entre ambos nombres es menor de lo que sugiere el marketing: Factur-X y ZUGFeRD son la misma norma franco-alemana publicada bajo dos etiquetas
¿Qué hace que un PDF sea una factura Factur-X válida?
Una factura Factur-X válida es un archivo PDF/A-3 (ISO 19005-3) que incrusta exactamente un XML de factura estructurado como archivo asociado, lo enlaza desde el catálogo del documento y lo declara en los metadatos de extensión XMP. Tres capas deben coincidir. En primer lugar, el contenedor debe cumplir con PDF/A-3, el perfil de archivo que, a diferencia de PDF/A-1 y PDF/A-2, permite archivos incrustados de cualquier tipo. En segundo lugar, el XML de la factura, llamado factur-x.xml para perfiles estándar o xrechnung.xml para la variante alemana XRechnung, debe registrarse en la matriz /AF del catálogo y en el árbol /Names /EmbeddedFiles, con una entrada AFRelationship que indique qué es el archivo adjunto: Alternative significa que el XML es una representación equivalente de la factura visible, que es la semántica que requieren la mayoría de los perfiles. En tercer lugar, el esquema de extensión XMP de Factur-X debe identificar el archivo incrustado, su nivel de conformidad y el tipo de documento, de modo que un sistema receptor pueda clasificar la factura sin necesidad de analizar el XML
Por qué ZUGFeRD 2.5 no necesita un contenedor nuevo
ZUGFeRD 2.5 no cambia nada a nivel del contenedor PDF: las URN de directrices, el esquema XMP y el token de versión son idénticos a ZUGFeRD 2.3 / Factur-X 1.0. Esto sorprende a la mayoría de los desarrolladores, por lo que vale la pena citar la fuente. La especificación Factur-X 1.09 establece que el número de versión en el URI del esquema de extensión PDF/A no está relacionado con el número de versión de la especificación de datos XML, y que la versión de las instancias de factura Factur-X sigue siendo 1.0, tanto en el campo XMP fx:Version como en el BT-24 de factur-x.xml. El corpus de muestras oficial de ZUGFeRD 2.5 lo confirma: cada muestra incrusta factur-x.xml bajo una URN de directriz urn:factur-x.eu:1p0 con un valor de fx:Version de 1.0. Lo que realmente agrega la versión 2.5 reside dentro del modelo de datos XML (nuevos elementos como líneas de subfactura e identificadores de lote), que es la capa de negocio que genera su aplicación y no la capa de contenedor que gestiona la biblioteca PDF
HotPDF codifica este hallazgo directamente. AddFacturXAssociatedFile acepta un argumento Version y lo normaliza a través de HPDFFacturXNormalizeVersion antes de escribir en XMP, de modo que un llamador que pase el alias 2p5 siga emitiendo el token compatible con la especificación en lugar de inventar una versión XMP 2p5 que ningún validador aceptaría. Los asistentes de detección tratan la opción 2p5 como equivalente al leer, por lo que los viajes de ida y vuelta se mantienen simétricos. Si una biblioteca o herramienta le indica que escribe un contenedor ZUGFeRD 2.5 especial, la propia especificación dice que no existe tal cosa
Incrustar el XML de la factura desde Delphi
THotPDF.AddFacturXAssociatedFile convierte una generación normal de PDF/A-3 en una factura Factur-X con una sola llamada: incrusta los bytes XML como un archivo asociado, registra factur-x.xml en la matriz /AF del catálogo y en el árbol /Names /EmbeddedFiles, y emite los metadatos de extensión XMP de Factur-X correspondientes. Usted dibuja la página de la factura legible para humanos con la API de lienzo normal de HotPDF; el trabajo del contenedor consiste en una sola línea entre BeginDoc y EndDoc
var
Pdf: THotPDF;
InvoiceXML, UBLXML: TBytes;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-2026-0042.pdf';
Pdf.PDFACompliance := '3B'; // PDF/A-3b container
Pdf.BeginDoc;
// embed the EN 16931 CII invoice XML as factur-x.xml
InvoiceXML := BuildInvoiceXML; // produced by your business layer
Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
// optional: attach a UBL view as a supplementary representation
UBLXML := BuildUBLXML;
Pdf.AddUBLSupplementaryFile(UBLXML); // factur-xubl.xml, Alternative
// ... draw the visible invoice page here ...
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Los valores predeterminados siguen la especificación: nombre de archivo factur-x.xml, relación Alternative, versión 1.0. AddUBLSupplementaryFile implementa la sección 6.4 de la especificación Factur-X 1.09, que permite que una representación UBL de la misma factura acompañe al documento como un segundo archivo adjunto llamado factur-xubl.xml con el tipo MIME application/xml y la relación Alternative. El orden es importante en este caso, y HotPDF aplica la restricción que implica la especificación: el archivo UBL es una vista complementaria, nunca la factura principal, por lo que debe agregarse después de que AddFacturXAssociatedFile haya registrado el CII maestro. Tenga en cuenta que la página dibujada y el XML incrustado deben declarar la misma factura; un PDF que muestra un total y codifica otro representa exactamente el modo de fallo que la facturación híbrida está diseñada para evitar. La parte de este flujo de trabajo correspondiente a PDF/A-3, incluidos OutputIntent e incrustación de fuentes, se analiza con más detalle en el artículo complementario sobre validación de PDF/A, PDF/X y PDF/UA en Delphi
¿Cómo detectar una factura Factur-X en un PDF entrante?
THotPDF.DetectFacturXInvoice responde al lado receptor del mandato: cargue cualquier PDF y conozca en una sola llamada si es una factura híbrida, qué perfil declara y qué archivos adjuntos contiene. La función devuelve un registro THPDFFacturXInvoiceInfo con el nombre del archivo XML incrustado, el nombre y tipo de archivo de documento declarado en XMP, el token de versión, el nivel de conformidad, la URN de directrices y el valor de AFRelationship. Otros dos campos informan sobre vistas complementarias: UBLFileName se completa cuando un archivo adjunto factur-xubl.xml está presente, y EDIFACTFileName cuando acompaña una vista EDIFACT; ninguno de los dos afecta al veredicto de detección, porque una representación complementaria no puede existir sin la factura CII principal
var
Reader: THotPDF;
Info: THPDFFacturXInvoiceInfo;
begin
Reader := THotPDF.Create(nil);
try
if Reader.LoadFromFile('incoming-invoice.pdf') <= 0 then
raise Exception.Create('No pages loaded.');
if Reader.DetectFacturXInvoice(Info) then
begin
Writeln('Invoice XML: ', string(Info.FileName));
Writeln('Conformance: ', string(Info.ConformanceLevel));
Writeln('Guideline URN: ', string(Info.GuidelineID));
Writeln('Relationship: ', string(Info.AFRelationship));
if Info.UBLFileName <> '' then
Writeln('UBL view: ', string(Info.UBLFileName));
end
else
Writeln('Not a Factur-X / ZUGFeRD hybrid invoice.');
finally
Reader.Free;
end;
end;
La detección lee las mismas señales que lee un validador compatible (el esquema de extensión XMP y el registro de archivos asociados) en lugar de deducirlo únicamente a partir de los nombres de los archivos. El mismo objeto de documento cargado también expone los metadatos de XMP y del diccionario Info para inspección o corrección, un flujo de trabajo descrito en el artículo sobre la edición de metadatos en documentos PDF cargados
Reglas de negocio o esquema: ¿qué significa la validación de la norma EN 16931?
HPDFValidateEInvoice, una función independiente en la unidad HPDFEInvoiceValidator, ejecuta ambas capas de validación en secuencia: primero la comprobación de la estructura del contenedor a través de ValidateFacturXInvoice, luego las reglas de negocio Schematron de la norma EN 16931 contra el XML de la factura extraído. HotPDF distribuye los los cinco archivos de reglas oficiales de Factur-X 1.09 (MINIMUM, BASIC WL, BASIC, EN 16931 y EXTENDED) bajo Lib/resources/Schematron/, y HPDFSchematronFileForProfile elige el archivo correspondiente al nivel de conformidad detectado, de modo que una factura EN 16931 se comprueba automáticamente contra Factur-X_1.09_EN16931.sch. La función devuelve True solo cuando el contenedor es válido y ninguna regla de negocio se ha activado
uses HPDFEInvoiceValidator;
var
Reader: THotPDF;
Report: THPDFEInvoiceValidationReport;
I: Integer;
begin
Reader := THotPDF.Create(nil);
try
Reader.LoadFromFile('incoming-invoice.pdf');
if HPDFValidateEInvoice(Reader, SchematronDir, Report) then
Writeln('Container and business rules: OK')
else
Writeln('Validation found issues.');
if Report.BusinessRulesEvaluated then
begin
Writeln(Report.BusinessSummary.Evaluated, ' rules evaluated, ',
Report.BusinessSummary.Skipped, ' skipped (XPath 2.0)');
for I := 0 to High(Report.BusinessRules) do
if (not Report.BusinessRules[I].Skipped) and
(Report.BusinessRules[I].Severity = stsError) then
Writeln(string(Report.BusinessRules[I].RuleID), ': ',
string(Report.BusinessRules[I].Message));
end;
finally
Reader.Free;
end;
end;
Cada entrada en BusinessRules contiene el identificador de la regla analizado a partir del texto de aserción (BR-45, BR-CO-17 y el resto del esquema de nombres de EN 16931), más el XPath del contexto de la regla, la expresión de prueba y una gravedad que refleja el atributo de bandera de Schematron. Esto hace que el informe sea útil para tomar medidas: en lugar de un simple aprobado/suspendido, usted obtiene el término comercial exacto que es inconsistente, que es lo que su equipo de soporte necesita cuando un cliente pregunta por qué se rechazó una factura. Los equipos que ya generan informes de conformidad estructurados para archivos PDF pueden integrar estos resultados en el mismo flujo de trabajo descrito en el artículo sobre la automatización de informes de comprobación preliminar de PDF
Límites del validador, sinceramente
El motor Schematron está basado en MSXML, que evalúa XPath 1.0. El archivo de reglas EN 16931 declara queryBinding="xslt2" y contiene 424 aserciones, la mayoría de las cuales utilizan únicamente funciones XPath 1.0 como string-length y substring-after (las cuales se ejecutan de forma nativa). Una minoría depende de construcciones XPath 2.0 como conversiones xs:decimal o exists(); HotPDF preescanea cada expresión de prueba en busca de tokens XPath 2.0 conocidos y marca esas reglas como Skipped con severidad informativa, en lugar de intentar una evaluación que fallaría silenciosamente. El registro de resumen informa los conteos de evaluaciones y omisiones por separado, de modo que su registro siempre declare exactamente qué parte del conjunto de reglas se ejecutó. En Delphi 7, donde los enlaces de MSXML no están disponibles, la unidad compila un código de prueba que informa que el motor no está disponible sin alterar el veredicto del contenedor; el motor completo requiere Delphi XE2 o posterior
El segundo límite es aún más importante. Un resultado satisfactorio (verde) de HPDFValidateEInvoice significa que la estructura del contenedor cumple y ninguna regla de negocio evaluada se activó; no representa un aval de cumplimiento fiscal. Las reglas de extensión nacionales, los requisitos específicos del receptor y las obligaciones legales sobre el contenido de la factura se ubican por encima del núcleo de la norma EN 16931 y fuera de cualquier biblioteca. Considere al validador como el filtro que evita que facturas estructuralmente rotas salgan de su bandeja de salida y marca las inconsistentes en su bandeja de entrada, ejecute un validador XPath 2.0 completo en su canalización de lanzamiento cuando la certificación lo exija, y deje que sus asesores fiscales gestionen el tema de conformidad. Las API de incrustación, detección y validación que se muestran aquí forman parte del estándar de HotPDF Component para Delphi y C++Builder, con un ejemplo completo de factura electrónica que cubre los siete perfiles ZUGFeRD y Factur-X