HotPDF tạo, phát hiện và xác thực các hóa đơn điện tử ZUGFeRD và Factur-X từ Delphi và C++Builder. AddFacturXAssociatedFile nhúng XML hóa đơn EN 16931 vào một container PDF/A-3, DetectFacturXInvoice nhận diện hóa đơn hỗn hợp (hybrid invoice) đi vào, và HPDFValidateEInvoice chạy các quy tắc nghiệp vụ Schematron EN 16931 chính thức đối với XML được nhúng. Bài viết này sẽ hướng dẫn cả ba bước và nêu rõ những giới hạn của engine xác thực
Áp lực đằng sau bộ tính năng này mang tính chất pháp lý chứ không phải kỹ thuật. Đức và Pháp đang dần bắt buộc áp dụng hóa đơn điện tử có cấu trúc cho các giao dịch B2B trong nước, và cả hai quy định đều hội tụ về cùng một mô hình hỗn hợp: hóa đơn PDF mà hệ thống ERP của bạn tạo ra phải mang theo một bản sao XML có cấu trúc mà máy móc có thể đọc được bên trong nó. Một ứng dụng kế toán hoặc ERP Delphi hiện đang phát hành hóa đơn PDF trần có thời hạn nghiêm ngặt phải bắt đầu phát hành Factur-X hoặc ZUGFeRD, và sự khác biệt giữa hai tên gọi này nhỏ hơn so với những gì tiếp thị gợi ý — Factur-X và ZUGFeRD là cùng một tiêu chuẩn Pháp-Đức được xuất bản dưới hai nhãn hiệu khác nhau
Điều gì làm cho một file PDF trở thành hóa đơn Factur-X hợp lệ?
Một hóa đơn Factur-X hợp lệ là một file PDF/A-3 (ISO 19005-3) nhúng chính xác một file XML hóa đơn có cấu trúc dưới dạng tệp đính kèm liên kết, được liên kết từ catalog tài liệu, và được khai báo trong siêu dữ liệu XMP extension. Ba lớp này phải thống nhất. Thứ nhất, container phải tuân thủ PDF/A-3, profile lưu trữ cho phép nhúng các file thuộc bất kỳ loại nào — khác với PDF/A-1 và PDF/A-2. Thứ hai, tệp XML hóa đơn, được đặt tên là factur-x.xml cho các profile tiêu chuẩn hoặc xrechnung.xml cho biến thể XRechnung của Đức, phải được đăng ký trong mảng /AF của catalog và cây /Names /EmbeddedFiles, với một mục AFRelationship nêu rõ tệp đính kèm là gì: Alternative có nghĩa là XML là một biểu diễn tương đương của hóa đơn hiển thị trực quan, đây là ngữ nghĩa mà hầu hết các profile yêu cầu. Thứ ba, Factur-X XMP extension schema phải đặt tên cho file được nhúng, mức độ tuân thủ của nó, và loại tài liệu, để hệ thống nhận có thể phân loại hóa đơn mà không cần phân tích cú pháp XML
Bản thân dữ liệu XML tuân theo mô hình ngữ nghĩa EN 16931, Tiêu chuẩn Châu Âu định nghĩa hóa đơn cốt lõi: người mua, người bán, các dòng mục, bảng phân tích thuế, số tiền phải thanh toán, mỗi mục có một mã điều khoản nghiệp vụ (BT-1 cho số hóa đơn, BT-5 cho tiền tệ, v.v.). Do đó, việc xác thực EN 16931 diễn ra trên hai lớp riêng biệt. Xác thực XML schema kiểm tra xem tài liệu có phải là CII có cấu trúc tốt với các tên phần tử chính xác hay không. Quy tắc nghiệp vụ Schematron kiểm tra xem nội dung có nhất quán hay không — ví dụ như quy tắc BR-CO-15 có được đảm bảo hay không và tổng hóa đơn có thực sự bằng tổng số tiền ròng của các dòng cộng với thuế GTGT hay không, các điều khoản bắt buộc có mặt đối với profile được khai báo hay không. Một tệp có thể vượt qua xác thực schema nhưng vẫn là một hóa đơn không hợp lệ, đó là lý do tại sao lớp quy tắc nghiệp vụ tồn tại
Tại sao ZUGFeRD 2.5 không cần một container mới
ZUGFeRD 2.5 không thay đổi gì ở cấp độ container PDF: các URN hướng dẫn, XMP schema và token phiên bản đều giống hệt với ZUGFeRD 2.3 / Factur-X 1.0. Điều này làm ngạc nhiên hầu hết các nhà phát triển, vì vậy việc trích dẫn nguồn là rất đáng giá. Đặc tả Factur-X 1.09 tuyên bố rằng số phiên bản trong URI của PDF/A extension schema không liên quan đến số phiên bản của đặc tả dữ liệu XML, và phiên bản của các instance hóa đơn Factur-X vẫn là 1.0 — trong XMP fx:Version và trong BT-24 của factur-x.xml tương tự. Bộ mẫu ZUGFeRD 2.5 chính thức xác nhận điều đó: mọi mẫu đều nhúng factur-x.xml dưới một URN hướng dẫn urn:factur-x.eu:1p0 với fx:Version là 1.0. Những gì 2.5 thực sự bổ sung nằm bên trong mô hình dữ liệu XML — các phần tử mới như dòng hóa đơn phụ và mã định danh lô hàng — đây là lớp nghiệp vụ mà ứng dụng của bạn tạo ra, không phải lớp container mà thư viện PDF quản lý
HotPDF mã hóa trực tiếp phát hiện này. AddFacturXAssociatedFile chấp nhận một đối số Version và chuẩn hóa nó thông qua HPDFFacturXNormalizeVersion trước khi ghi XMP, do đó một bên gọi truyền bí danh 2p5 vẫn phát ra token tuân thủ đặc tả thay vị tự chế ra một phiên bản XMP 2p5 mà không bộ xác thực nào chấp nhận. Các helper phát hiện coi 2p5 là tương đương khi đọc, nhờ đó các vòng lặp xử lý vẫn đối xứng. Nếu một thư viện hoặc công cụ nói với bạn rằng nó viết một container ZUGFeRD 2.5 đặc biệt, thì bản thân đặc tả nói rằng không có thứ đó tồn tại
Nhúng XML hóa đơn từ Delphi
THotPDF.AddFacturXAssociatedFile biến một lượt chạy tạo PDF/A-3 thông thường thành một hóa đơn Factur-X chỉ với một lệnh gọi: nó nhúng các byte XML dưới dạng file đính kèm liên kết, đăng ký factur-x.xml trong mảng /AF của catalog và cây /Names /EmbeddedFiles, và phát ra siêu dữ liệu Factur-X XMP extension tương ứng. Bạn vẽ trang hóa đơn mà con người đọc được bằng API canvas thông thường của HotPDF; công việc container là một dòng duy nhất giữa BeginDoc và 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;
Các giá trị mặc định tuân theo đặc tả: tên file factur-x.xml, mối quan hệ Alternative, phiên bản 1.0. AddUBLSupplementaryFile triển khai phần 6.4 của đặc tả Factur-X 1.09, cho phép một biểu diễn UBL của cùng một hóa đơn đi kèm dưới dạng tệp đính kèm thứ hai có tên factur-xubl.xml với MIME type application/xml và mối quan hệ Alternative. Thứ tự thực hiện rất quan trọng, và HotPDF thực thi ràng buộc mà đặc tả ngụ ý: file UBL là một chế độ xem bổ sung, không bao giờ là hóa đơn chính, vì vậy nó phải được thêm sau khi AddFacturXAssociatedFile đã đăng ký CII chính. Lưu ý rằng trang được vẽ và XML được nhúng phải thể hiện cùng một hóa đơn — một file PDF hiển thị một tổng số tiền nhưng lại mã hóa một tổng số tiền khác chính là chế độ lỗi mà hóa đơn hỗn hợp được thiết kế để ngăn chặn. Khía cạnh PDF/A-3 của quy trình này, bao gồm cả OutputIntent và nhúng font, được trình bày chi tiết hơn trong bài viết đi kèm về xác thực PDF/A, PDF/X và PDF/UA trong Delphi
Làm thế nào để phát hiện một hóa đơn Factur-X trong một file PDF đi vào?
THotPDF.DetectFacturXInvoice đáp ứng khía cạnh nhận của quy định: tải bất kỳ file PDF nào và biết ngay trong một lệnh gọi xem nó có phải là hóa đơn hỗn hợp hay không, nó khai báo profile nào, và mang những tệp đính kèm nào. Hàm trả về một bản ghi THPDFFacturXInvoiceInfo với tên file XML được nhúng, tên và loại file tài liệu được khai báo trong XMP, token phiên bản, mức độ tuân thủ, URN hướng dẫn, và giá trị AFRelationship. Hai trường bổ sung báo cáo các chế độ xem bổ sung: UBLFileName được điền khi có tệp đính kèm factur-xubl.xml, và EDIFACTFileName khi có chế độ xem EDIFACT đi kèm — không trường nào ảnh hưởng đến kết quả phát hiện, vì một biểu diễn bổ sung không thể tồn tại nếu không có hóa đơn CII chính
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;
Việc phát hiện đọc cùng các tín hiệu mà một trình xác thực tuân thủ đọc — XMP extension schema và đăng ký associated-file — thay vì chỉ đoán từ tên file đơn thuần. Cùng một đối tượng tài liệu đã tải cũng hiển thị siêu dữ liệu XMP và Info-dictionary để kiểm tra hoặc sửa đổi, một quy trình làm việc được mô tả trong bài viết về chỉnh sửa siêu dữ liệu trong tài liệu PDF đã tải
Quy tắc nghiệp vụ hay schema: xác thực EN 16931 có nghĩa là gì?
HPDFValidateEInvoice, một hàm độc lập trong unit HPDFEInvoiceValidator, chạy cả hai lớp xác thực theo thứ tự: đầu tiên là kiểm tra cấu trúc container thông qua ValidateFacturXInvoice, sau đó là các quy tắc nghiệp vụ Schematron EN 16931 đối với XML hóa đơn được trích xuất. HotPDF cung cấp năm file quy tắc Factur-X 1.09 chính thức — MINIMUM, BASIC WL, BASIC, EN 16931, và EXTENDED — dưới thư mục Lib/resources/Schematron/, và HPDFSchematronFileForProfile chọn file khớp với mức độ tuân thủ phát hiện được, do đó một hóa đơn EN 16931 sẽ tự động được kiểm tra đối với Factur-X_1.09_EN16931.sch. Hàm chỉ trả về True khi container hợp lệ và không có quy tắc nghiệp vụ nào bị vi phạm
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;
Mỗi mục trong BusinessRules mang mã định danh quy tắc được phân tích từ văn bản assertion — BR-45, BR-CO-17, và phần còn lại của sơ đồ đặt tên EN 16931 — cộng với ngữ cảnh XPath của quy tắc, biểu thức kiểm thử và mức độ nghiêm trọng phản ánh thuộc tính flag của Schematron. Điều đó giúp báo cáo có giá trị hành động: thay vì một kết quả pass/fail trần trụi, bạn nhận được chính xác điều khoản nghiệp vụ không nhất quán, đây là điều mà đội ngũ hỗ trợ của bạn cần khi khách hàng hỏi tại sao hóa đơn bị từ chối. Các nhóm đã tạo báo cáo tuân thủ có cấu trúc cho các PDF lưu trữ có thể đưa các kết quả này vào cùng một quy trình được mô tả trong bài viết về tự động hóa báo cáo preflight PDF
Điểm dừng thực tế của trình xác thực
Engine Schematron được xây dựng trên MSXML, vốn đánh giá XPath 1.0. File quy tắc EN 16931 khai báo queryBinding="xslt2" và mang 424 assertion, hầu hết trong số đó chỉ sử dụng các hàm XPath 1.0 như string-length và substring-after — những hàm này chạy nguyên bản. Một số ít dựa trên các cấu trúc XPath 2.0 như ép kiểu xs:decimal hoặc exists(); HotPDF quét trước mỗi biểu thức kiểm thử để tìm các token XPath 2.0 đã biết và đánh dấu các quy tắc đó là Skipped với mức độ nghiêm trọng info thay vì cố gắng đánh giá có thể gây ra lỗi ngầm. Bản ghi tóm tắt báo cáo số lượng quy tắc được đánh giá và bị bỏ qua riêng biệt, do đó nhật ký của bạn luôn nêu rõ chính xác bao nhiêu phần của bộ quy tắc đã được thực thi. Trên Delphi 7, nơi không có liên kết MSXML, unit sẽ biên dịch một stub báo cáo engine không khả dụng mà không ảnh hưởng đến kết luận container; engine đầy đủ yêu cầu Delphi XE2 trở lên
Giới hạn thứ hai thậm chí còn quan trọng hơn
Kết quả màu xanh từ HPDFValidateEInvoice có nghĩa là cấu trúc container tuân thủ và không có quy tắc nghiệp vụ nào được đánh giá bị vi phạm — nó không phải là một sự chứng thực tuân thủ thuế. Các quy tắc mở rộng quốc gia, các yêu cầu cụ thể của người nhận và các nghĩa vụ pháp lý xung quanh nội dung hóa đơn nằm trên lõi EN 16931 và nằm ngoài bất kỳ thư viện nào. Hãy coi trình xác thực như một cánh cổng ngăn chặn các hóa đơn bị hỏng cấu trúc ra khỏi outbox của bạn và gắn cờ những hóa đơn không nhất quán trong inbox của bạn, hãy chạy một trình xác thực XPath 2.0 đầy đủ trong release pipeline của bạn khi yêu cầu chứng nhận đòi hỏi, và hãy để các cố vấn thuế của bạn chịu trách nhiệm về câu hỏi tuân thủ pháp lý. Các API nhúng, phát hiện và xác thực được hiển thị ở đây là một phần của HotPDF Component tiêu chuẩn dành cho Delphi và C++Builder, với một mẫu E-Invoice đầy đủ bao gồm tất cả bảy profile ZUGFeRD và Factur-X