Artikel Teknis

E-Invoice ZUGFeRD dan Factur-X di Delphi dengan HotPDF

HotPDF menghasilkan, mendeteksi, dan memvalidasi faktur elektronik (e-invoice) ZUGFeRD dan Factur-X dari Delphi dan C++Builder. AddFacturXAssociatedFile menyematkan XML faktur EN 16931 ke dalam kontainer PDF/A-3, DetectFacturXInvoice mengenali faktur hibrida yang masuk, dan HPDFValidateEInvoice menjalankan aturan bisnis Schematron EN 16931 resmi terhadap XML yang disematkan. Artikel ini membahas ketiga langkah tersebut dan secara jujur menjelaskan batas kemampuan mesin validasi

Tekanan di balik rangkaian fitur ini bersifat regulasi, bukan teknis. Jerman dan Prancis sedang menerapkan kewajiban e-invoicing terstruktur untuk transaksi B2B domestik secara bertahap, dan kedua mandat tersebut mengarah pada model hibrida yang sama: faktur PDF yang sudah dihasilkan ERP Anda harus membawa salinan XML yang dapat dibaca mesin di dalamnya. Aplikasi akuntansi atau ERP Delphi yang mengeluarkan faktur PDF biasa saat ini memiliki batas waktu ketat untuk mulai mengeluarkan Factur-X atau ZUGFeRD, dan perbedaan di antara kedua nama tersebut lebih kecil daripada yang disarankan pemasaran — Factur-X dan ZUGFeRD adalah standar Prancis-Jerman yang sama yang diterbitkan di bawah dua label

Apa yang membuat PDF menjadi faktur Factur-X yang valid?

Faktur Factur-X yang valid adalah berkas PDF/A-3 (ISO 19005-3) yang menyematkan tepat satu XML faktur terstruktur sebagai berkas terkait (associated file), menautkannya dari katalog dokumen, dan mendeklarasikannya di metadata ekstensi XMP. Tiga lapisan harus sepakat. Pertama, kontainer harus mematuhi PDF/A-3, profil pengarsipan yang — tidak seperti PDF/A-1 and PDF/A-2 — mengizinkan berkas yang disematkan dari jenis apa pun. Kedua, XML faktur, bernama factur-x.xml untuk profil standar atau xrechnung.xml untuk varian XRechnung Jerman, harus didaftarkan di array katalog /AF dan pohon /Names /EmbeddedFiles, dengan entri AFRelationship yang menyatakan apa lampiran tersebut: Alternative berarti XML adalah representasi setara dari faktur yang terlihat, yang merupakan semantik yang dibutuhkan sebagian besar profil. Ketiga, skema ekstensi XMP Factur-X harus menyebutkan berkas yang disematkan, tingkat kepatuhannya, dan jenis dokumen, sehingga sistem penerima dapat mengklasifikasikan faktur tanpa mengurai XML sekali pun

Payload XML itu sendiri mengikuti model semantik EN 16931, Norma Eropa yang mendefinisikan inti faktur: pembeli, penjual, item baris, perincian pajak, jumlah yang harus dibayar, masing-masing dengan nomor istilah bisnis (BT-1 untuk nomor faktur, BT-5 untuk mata uang, dan seterusnya). Oleh karena itu, validasi EN 16931 terjadi pada dua lapisan yang berbeda. Validasi skema XML memeriksa bahwa dokumen tersebut adalah CII yang terbentuk dengan baik dengan nama elemen yang benar. Aturan bisnis Schematron memeriksa bahwa kontennya koheren — bahwa aturan BR-CO-15 berlaku dan total faktur benar-benar sama dengan jumlah jumlah bersih baris ditambah PPN, bahwa ketentuan wajib ada untuk profil yang dideklarasikan. Sebuah berkas dapat lolos validasi skema dan tetap menjadi faktur yang tidak valid, itulah sebabnya lapisan aturan bisnis ada

Mengapa ZUGFeRD 2.5 tidak memerlukan kontainer baru

ZUGFeRD 2.5 tidak mengubah apa pun di tingkat kontainer PDF: URN pedoman, skema XMP, dan token versi identik dengan ZUGFeRD 2.3 / Factur-X 1.0. Hal ini mengejutkan sebagian besar implementator, jadi ada baiknya mengutip sumbernya. Spesifikasi Factur-X 1.09 menyatakan bahwa nomor versi dalam URI skema ekstensi PDF/A tidak terkait dengan nomor versi spesifikasi data XML, dan bahwa versi instans faktur Factur-X tetap 1.0 — baik dalam XMP fx:Version maupun dalam BT-24 dari factur-x.xml. Korpus sampel resmi ZUGFeRD 2.5 mengonfirmasinya: setiap sampel menyematkan factur-x.xml di bawah URN pedoman urn:factur-x.eu:1p0 dengan fx:Version bernilai 1.0. Apa yang sebenarnya ditambahkan 2.5 hidup di dalam model data XML — elemen baru seperti baris sub-faktur dan pengidentifikasi batch — yang merupakan lapisan bisnis yang dihasilkan aplikasi Anda, bukan lapisan kontainer yang dikelola pustaka PDF

HotPDF mengodekan temuan ini secara langsung. AddFacturXAssociatedFile menerima argumen Version and menormalisasinya melalui HPDFFacturXNormalizeVersion sebelum menulis XMP, sehingga pemanggil yang meneruskan alias 2p5 tetap memancarkan token yang mematuhi spesifikasi alih-alih menemukan versi XMP 2p5 yang tidak akan diterima oleh validator mana pun. Pembantu deteksi memperlakukan 2p5 sebagai setara saat membaca, sehingga perjalanan pulang pergi tetap simetris. Jika pustaka atau alat memberi tahu Anda bahwa ia menulis kontainer ZUGFeRD 2.5 khusus, spesifikasi itu sendiri menyatakan tidak ada hal seperti itu

Menyematkan XML faktur dari Delphi

THotPDF.AddFacturXAssociatedFile mengubah proses pembuatan PDF/A-3 biasa menjadi faktur Factur-X dengan satu panggilan: ia menyematkan byte XML sebagai berkas terkait, mendaftarkan factur-x.xml dalam array katalog /AF dan pohon /Names /EmbeddedFiles, serta memancarkan metadata ekstensi XMP Factur-X yang cocok. Anda menggambar halaman faktur yang dapat dibaca manusia dengan API kanvas HotPDF biasa; pekerjaan kontainer hanyalah satu baris di antara BeginDoc dan EndDoc

var
  Pdf: THotPDF;
  InvoiceXML, UBLXML: TBytes;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-2026-0042.pdf';
    Pdf.PDFACompliance := '3B';            // kontainer PDF/A-3b
    Pdf.BeginDoc;
    // sematkan XML faktur EN 16931 CII sebagai factur-x.xml
    InvoiceXML := BuildInvoiceXML;         // dihasilkan oleh lapisan bisnis Anda
    Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
    // opsional: lampirkan tampilan UBL sebagai representasi tambahan
    UBLXML := BuildUBLXML;
    Pdf.AddUBLSupplementaryFile(UBLXML);   // factur-xubl.xml, Alternative
    // ... gambar halaman faktur yang terlihat di sini ...
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Nilai default mengikuti spesifikasi: nama berkas factur-x.xml, hubungan Alternative, versi 1.0. AddUBLSupplementaryFile mengimplementasikan bagian 6.4 dari spesifikasi Factur-X 1.09, yang memungkinkan representasi UBL dari faktur yang sama untuk disertakan sebagai lampiran kedua bernama factur-xubl.xml dengan tipe MIME application/xml dan hubungan Alternative. Urutan sangat penting di sini, dan HotPDF memberlakukan batasan yang disiratkan spesifikasi: berkas UBL adalah tampilan tambahan, tidak pernah menjadi faktur utama, jadi harus ditambahkan setelah AddFacturXAssociatedFile mendaftarkan master CII. Perhatikan bahwa halaman yang digambar dan XML yang disematkan harus menyatakan faktur yang sama — PDF yang menampilkan satu total dan mengodekan total lainnya adalah mode kegagalan yang dirancang untuk dicegah oleh pembuatan faktur hibrida. Sisi PDF/A-3 dari alur kerja ini, termasuk OutputIntent dan penyematan font, dibahas lebih mendalam dalam artikel pendamping tentang validasi PDF/A, PDF/X, dan PDF/UA di Delphi

Bagaimana cara mendeteksi faktur Factur-X dalam PDF yang masuk?

THotPDF.DetectFacturXInvoice menjawab sisi penerima mandat: muat PDF apa pun dan ketahui dalam satu panggilan apakah itu faktur hibrida, profil apa yang dideklarasikannya, dan lampiran apa yang dibawanya. Fungsi ini mengembalikan rekaman THPDFFacturXInvoiceInfo dengan nama berkas XML yang disematkan, nama berkas dan jenis dokumen yang dideklarasikan XMP, token versi, tingkat kepatuhan, URN pedoman, dan nilai AFRelationship. Dua bidang berikutnya melaporkan tampilan tambahan: UBLFileName diisi ketika lampiran factur-xubl.xml ada, dan EDIFACTFileName ketika tampilan EDIFACT disertakan — keduanya tidak memengaruhi putusan deteksi, karena representasi tambahan tidak dapat ada tanpa faktur CII utama

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;

Deteksi membaca sinyal yang sama yang dibaca oleh validator yang patuh — skema ekstensi XMP dan pendaftaran berkas terkait — alih-alih menebak dari nama berkas saja. Objek dokumen yang dimuat yang sama juga mengekspos metadata kamus info dan XMP untuk pemeriksaan atau koreksi, alur kerja yang dijelaskan dalam artikel tentang mengedit metadata di dokumen PDF yang dimuat

Aturan bisnis atau skema: apa arti validasi EN 16931?

HPDFValidateEInvoice, sebuah fungsi mandiri dalam unit HPDFEInvoiceValidator, menjalankan kedua lapisan validasi secara berurutan: pertama pemeriksaan struktur kontainer melalui ValidateFacturXInvoice, lalu aturan bisnis Schematron EN 16931 terhadap XML faktur yang diekstrak. HotPDF menyertakan lima berkas aturan Factur-X 1.09 resmi — MINIMUM, BASIC WL, BASIC, EN 16931, dan EXTENDED — di bawah Lib/resources/Schematron/, dan HPDFSchematronFileForProfile memilih berkas yang cocok dengan tingkat kepatuhan yang dideteksi, sehingga faktur EN 16931 secara otomatis diperiksa terhadap Factur-X_1.09_EN16931.sch. Fungsi ini mengembalikan nilai True hanya ketika kontainer valid dan tidak ada aturan bisnis yang dipicu

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;

Setiap entri dalam BusinessRules membawa pengenal aturan (rule identifier) yang diurai dari teks pernyataan — BR-45, BR-CO-17, dan skema penamaan EN 16931 selebihnya — ditambah XPath konteks aturan, ekspresi pengujian, dan tingkat keparahan (severity) yang mencerminkan atribut bendera Schematron. Hal itu membuat laporan dapat ditindaklanjui: alih-alih hanya lulus/gagal, Anda mendapatkan istilah bisnis yang tidak konsisten secara tepat, yang merupakan hal yang dibutuhkan tim dukungan Anda ketika pelanggan menanyakan mengapa faktur ditolak. Tim yang sudah menghasilkan laporan kepatuhan terstruktur untuk PDF arsip dapat melipat hasil ini ke dalam pipa yang sama yang dijelaskan dalam artikel tentang mengotomatiskan laporan preflight PDF

Di mana validator berhenti, secara jujur

Mesin Schematron dibangun di atas MSXML, yang mengevaluasi XPath 1.0. Berkas aturan EN 16931 mendeklarasikan queryBinding="xslt2" and membawa 424 pernyataan, yang sebagian besar hanya menggunakan fungsi XPath 1.0 seperti string-length dan substring-after — fungsi tersebut berjalan secara native. Sebagian kecil mengandalkan konstruksi XPath 2.0 seperti casting xs:decimal atau exists(); HotPDF memindai terlebih dahulu setiap ekspresi pengujian untuk token XPath 2.0 yang diketahui dan menandai aturan tersebut sebagai Skipped dengan tingkat keparahan info alih-alih mencoba evaluasi yang akan salah sasaran secara diam-diam. Rekaman ringkasan melaporkan jumlah yang dievaluasi dan dilewati secara terpisah, sehingga log Anda selalu menyatakan dengan tepat seberapa banyak aturan yang dijalankan. Pada Delphi 7, di mana pengikatan MSXML tidak tersedia, unit mengompilasi rintisan (stub) yang melaporkan mesin sebagai tidak tersedia tanpa mengganggu putusan kontainer; mesin lengkap memerlukan Delphi XE2 atau yang lebih baru

Batasan kedua bahkan lebih penting. Hasil hijau dari HPDFValidateEInvoice berarti struktur kontainer sesuai dan tidak ada aturan bisnis yang dievaluasi yang dipicu — ini bukan pengesahan kepatuhan pajak. Aturan perpanjangan nasional, persyaratan khusus penerima, dan kewajiban hukum seputar konten faktur berada di atas inti EN 16931 dan di luar pustaka mana pun. Perlakukan validator sebagai gerbang yang menjauhkan faktur yang rusak secara struktur dari kotak keluar Anda dan menandai faktur yang tidak konsisten di kotak masuk Anda, jalankan validator XPath 2.0 lengkap di pipa rilis Anda saat sertifikasi menuntutnya, dan biarkan penasihat pajak Anda yang menangani masalah kepatuhan. API penyematan, deteksi, dan validasi yang ditunjukkan di sini adalah bagian dari HotPDF Component standar untuk Delphi dan C++Builder, dengan sampel E-Invoice lengkap yang mencakup ketujuh profil ZUGFeRD dan Factur-X