Faktur elektronik yang patuh standar (compliant) bukanlah sebuah PDF yang sekadar ditempeli file XML di sisinya. Dokumen ini merupakan satu dokumen PDF/A-3 utuh yang membawa faktur tersebut dalam dua wujud: satu sebagai halaman yang dibaca oleh manusia, dan satu lagi berupa XML Cross Industry Invoice yang bisa dibaca oleh mesin dan tersimpan di dalam file sebagai sebuah berkas terhubung (associated file). Kedua representasi tersebut mendeskripsikan faktur yang sama persis. Sifat ganda ini adalah esensi utama dari rumpun format yang kini diwajibkan oleh mandat perundang-undangan Eropa, seperti Factur-X di Prancis dan Jerman, ZUGFeRD di seluruh pasar berbahasa Jerman, dan XRechnung untuk penagihan sektor publik di Jerman. Artikel ini mengupas langkah demi langkah bagaimana PDFlibPas merangkai faktur hibrida semacam itu di Delphi, celah di mana standar yang ada menyisakan ruang terjadinya kesalahan, dan mengapa satu profil spesifik di dalam katalog membutuhkan pembangun (builder) XML yang sepenuhnya terpisah
Apa sebenarnya faktur hibrida itu
Halaman yang tampak visual dan XML yang tertanam melayani pembaca yang berbeda. Seorang staf administrasi yang menyetujui pembayaran akan melihat halaman yang di-render. Sementara itu, sistem utang usaha (accounts-payable) akan menelan XML tersebut, membaca totalan serta rincian pajaknya sebagai bidang terstruktur (structured fields), lalu membukukan entri tersebut tanpa ada campur tangan manusia untuk mengetik apapun. Konten semantik dari XML tersebut diatur oleh EN 16931, standar Eropa yang mendefinisikan model data faktur: bidang-bidang apa saja yang harus ada, apa maknanya, dan mana yang sifatnya wajib. EN 16931 adalah sebuah model semantik, bukan format file. Factur-X, ZUGFeRD 2.x, dan XRechnung secara keseluruhan mewujudkan model tersebut dalam bentuk dokumen UN/CEFACT Cross Industry Invoice, sintaks yang membawa rincian bidang-bidang EN 16931 saat dipertukarkan (on the wire)
Agar dokumen dapat diarsipkan (archivable) sekaligus mampu mendeskripsikan dirinya sendiri (self-describing), format kontainer yang dipakai adalah PDF/A-3, yang didefinisikan oleh ISO 19005-3. PDF/A-3 merupakan tingkat kepatuhan yang mengizinkan penyematan file-file bebas sembarang, yang mana ini persis menjadi kebutuhan dari sebuah XML faktur. PDF/A-2 melarang keras penyematan file yang wujud aslinya bukanlah PDF/A, sehingga faktur Factur-X mustahil menggunakan format PDF/A-2. Oleh karena itu, pemilihan PDF/A-3 bukanlah masalah preferensi, melainkan sebuah persyaratan mutlak yang muncul langsung dari kebutuhan untuk menanamkan data non-PDF ke dalam sebuah dokumen arsip
Mengapa nilai relasinya harus Alternative
Menyematkan byte data adalah bagian yang paling mudah. ISO 32000 §7.11.4 mendefinisikan aliran file tertanam (embedded file stream), yakni objek yang menampung XML mentah beserta berbagai parameternya. Bagian yang membuat file tersebut menjadi associated file yang valid adalah §14.13, yang menambahkan konsep associated file serta kunci /AFRelationship. Kunci tersebut menyatakan bagaimana kaitan data yang tertanam dengan konten yang dilampirinya, dan nilai yang dimandatkan oleh Factur-X adalah Alternative
Pilihan ini sangat krusial sebab nilai lain akan menegaskan sesuatu yang keliru mengenai dokumen tersebut. Source akan berarti bahwa XML tersebut adalah materi asal dari mana konten visualnya dihasilkan, sebuah master rujukan tempat halaman itu diturunkan. Supplement akan bermakna XML tersebut menambahkan informasi melebihi apa yang ditampilkan di halaman, sebuah porsi ekstra yang tidak ada di wujud rendering-nya. Keduanya sama sekali tidak merepresentasikan apa itu faktur Factur-X yang sebenarnya. XML maupun halaman visualnya merupakan dua ekspresi yang setara dari satu faktur tunggal, memuat konten legal yang identik dalam dua bentuk rupa. Alternative adalah nilai yang mengatakan hal tersebut secara presisi: representasi alternatif yang setara dari konten yang terlihat. Validator yang mendapati relasi selain itu di sebuah file Factur-X akan menolaknya, dan hal itu bisa dibenarkan, karena relasi tersebut merupakan sebuah klaim terbaca mesin mengenai untuk apa lampiran tersebut ditujukan
Katalog profil
Sampel E-Invoice yang disertakan bersama PDFlibPas menggerakkan alur pembuatan yang sama untuk enam profil berbeda, yang didefinisikan sebagai array of records di dalam InvoiceModel.pas. Masing-masing profil memuat nilai-nilai yang dibutuhkan oleh kelas penulis (writer): nama tampilan, nama file yang tertanam, tingkat kepatuhan (conformance level), /AFRelationship, versi, kode negara opsional, serta URN GuidelineID yang dikumandangkan oleh XML di dalam konteks dokumennya
Keenam profil tersebut meliputi Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED untuk Prancis, XRechnung 3.0, ZUGFeRD 1.0 COMFORT, dan ZUGFeRD 2.0 BASIC. GuidelineID merupakan kolom yang menginformasikan kepada pihak penerima secara presisi profil apa yang bakal mereka temui, dan nilainya sangat spesifik. Factur-X EN16931 menggunakan urn:cen.eu:en16931:2017. XRechnung 3.0 mendeklarasikan urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC menyatakan urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Nama file tertanam pun merupakan bagian dari ikatan kontrak spesifikasi ini. Profil-profil Factur-X menyematkan factur-x.xml, XRechnung menyematkan xrechnung.xml, dan profil ZUGFeRD menyematkan ZUGFeRD-invoice.xml ataupun zugferd-invoice.xml. Pihak penerima akan memindai nama lampiran untuk menemukan faktur tersebut, sehingga penamaan file bukanlah perkara sekadar hiasan semata
Satu rincian di katalog ini patut untuk dibaca secara cermat. Mayoritas profil memakai relasi Alternative, namun entri XRechnung 3.0 dalam sampel program ini memakai Source. Kedua format tersebut harus menjawab aturan validator dan konvensi yang berbeda, dan aplikasi sampel ini menetapkan relasi setiap profil langsung dari rincian katalog ketimbang menggunakan nilai tunggal yang di-hardcode, dan itulah alasannya mengapa bidang (field) per profil ini hadir alih-alih menggunakan konstanta mati
Jebakan ZUGFeRD 1.0
Sangat menggoda untuk berasumsi bahwa setiap profil merupakan wujud UN/CEFACT EN 16931 Cross Industry Invoice yang hanya berselisih seputar berapa banyak kolom opsional yang Anda isi nilainya. Asumsi tersebut mungkin valid untuk lima dari enam profil yang ada. Namun asumsi tersebut tidak berlaku pada ZUGFeRD 1.0 COMFORT, dan alasannya terletak pada masalah struktural alih-alih masalah presentasi kosmetik (cosmetic)
Profil-profil modern akan memancarkan sebuah UN/CEFACT Cross Industry Invoice dengan rujukan versi namespace :100, yang memiliki elemen root (root element) rsm:CrossIndustryInvoice. ZUGFeRD 1.0 terlahir jauh sebelum skema tersebut hadir. Ia mengadopsi kerangka CrossIndustryDocument versi tahun 2014 dengan namespace versi :1p0, lalu menempatkan rsm:CrossIndustryDocument sebagai elemen root. Namespace URN-nya pun berlainan, elemen root berbeda, dan susunan elemen strukturnya juga berbeda secara menyuruh: skema :1p0 mengelompokkan data di bawah ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery, dan ApplicableSupplyChainTradeSettlement, di mana skema :100 memanfaatkan penggunaan elemen bertajuk ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery, serta ApplicableHeaderTradeSettlement. Penamaannya memiliki kemiripan untuk mengecoh, namun wujud perbedaannya terbilang fatal untuk memicu kegagalan
Kata COMFORT pada nama profil mendeskripsikan seberapa kaya bentangan data di dalamnya, sebuah level profil bernilai otomatisasi penuh (automation-grade) yang dibekali perincian item baris lengkap, rincian potongan pajak, dan syarat pembayaran, dan kata itu tidak menunjukkan skema mana yang memuatnya. Sehingga Anda sama sekali tidak bisa sekadar mencaplok dokumen :100 dan menempelkan label ulang (relabel) agar terbaca ZUGFeRD 1.0. Program sampel PDFlibPas menangani problem ini melalui penanaman sebuah parameter indikator (flag) di setiap data profil beserta hadirnya dua fungsi perakit yang terpisah, agar program dapat menjatuhkan pilihan pada fungsi yang paling tepat sebelum file XML mulai dirangkai
function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
const Data: TInvoiceData): string;
begin
// XMLFamily = 1 means the legacy ZUGFeRD 1.0 :1p0 schema; every
// other profile is the modern UN/CEFACT :100 Cross Industry Invoice.
if AProfile.XMLFamily = 1 then
Result := BuildZUGFeRD1Text(AProfile, Data)
else
Result := BuildCII100Text(AProfile, Data);
end;
Pemisahan ini bukan sekadar pemanis implementasi (implementation nicety). Memasok sebuah susunan data jenis :100 menuju perangkat penerima ZUGFeRD 1.0 bakal melahirkan dokumen yang ditolak seketika pada tahap validasi skema di tingkat root element, alhasil kedua kerangka rupa faktur ini wajib dibangun secara langsung memakai struktur instruksi kode spesifik yang tahu persis rupa apa yang kelak ditulisnya
Memilih spesifikasi level PDF/A-3
Standar PDF/A-3 mengatur hadirnya tiga tingkat kepatuhan (conformance level), dan PDFlibPas memilihnya lewat SetPDFAMode. Mode 5 adalah PDF/A-3b, tingkatan yang menggaransi presisi visual rupa dokumen. Mode 6 adalah PDF/A-3a, yang turut membubuhkan spesifikasi tag navigasi dokumen terstruktur (tagged-structure) beserta persyaratan aksesibilitas (accessibility) dari level a. Mode 7 merupakan rupa kelas PDF/A-3u, format yang mewajibkan bahwa semua teks harus dipetakan ke Unicode. Mengaktifkan mode tersebut juga akan menanamkan profil karakteristik warna (sRGB output intent) bawaan perpustakaan tersebut ke dalamnya, sebuah spesifikasi yang PDF/A wajibkan agar rupa warna yang di-render punya parameter kalibrasi mutlak yang terdefinisi ketimbang berstatus membebaskan spesifikasi instrumen tampilan (device-dependent)
Sebagian besar alur proses faktur beroperasi di PDF/A-3b, yang mana sudah sangat memadai guna memproduksi wujud halaman visual yang akurat sekaligus menempelkan XML di dalamnya. Apabila Anda membutuhkan sebuah profil ICC secara spesifik alih-alih memanfaatkan profil bawaan, fungsi LoadOutputIntentProfile akan memasukkannya sesudah modenya diset. Sampel tersebut akan memuat profil sRGB yang tersedia di repositori lewat metode ini, lalu kelak bakal bersandar kembali (fall back) pada intent bawaannya bilamana file yang dituju ternyata tak dapat diakses, dengan demikian kehadiran output intent tersebut akan selalu terjaga di sepanjang proses produksi
PDF := TPDFlib.Create;
try
// Mode 5 = PDF/A-3b, 6 = PDF/A-3a, 7 = PDF/A-3u.
if PDF.SetPDFAMode(5) <> 1 then
raise Exception.Create('PDF/A-3 mode could not be enabled');
// Opsional: ganti intent sRGB bawaan dengan sebuah profil ICC eksplisit.
if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
{ fall back ke intent sRGB bawaan yang telah ditanam oleh SetPDFAMode };
finally
// ... lanjut menyusun dokumen
end;
Membangun faktur hibrida
Dengan beresnya tahap konfigurasi atas kerangka kontainernya, rute kelanjutannya tinggal menuntaskan tiga langkah ini secara berurutan: menetapkan mode PDF/A-3, melukis penampang halamannya, dan menyematkan XML sebagai associated file. Halaman visualnya murni sekadar konten biasa. Satu prasyarat penting yang layak mendapat penekanan adalah bahwa PDF/A sangat melarang keberadaan 14 jenis font standar yang belum di-embed (non-embedded Standard 14 fonts), sehingga seluruh muatan halaman mutlak harus menanamkan wujud font aslinya (embed a real font face) alih-alih cuma menyodorkan referensi font bawaan
Upaya pelampirannya (attachment) murni hanya memakan satu panggilan fungsi. AddFacturXAssociatedFileFromString mengambil pasokan byte UTF-8 XML mentahnya beserta parameter profil metadata, menuliskan aliran berkas tertanam (embedded file stream), lantas meregistrasikannya ke dalam array /AF di Katalog yang diwajibkan oleh PDF/A-3, mengaplikasikan /AFRelationship, lalu menerbitkan informasi metadata e-invoice XMP yang mengidentifikasi dokumen tersebut sebagai format Factur-X, ZUGFeRD, atau XRechnung. Ia juga memverifikasi apakah guideline ID pada XML sesuai dengan tingkat ketaatan (conformance level) yang Anda minta, sehingga ketidakcocokan antara XML yang Anda susun dan profil targetnya bakal tertangkap urung dikirim dengan diam-diam
// 1. Mode PDF/A-3 dan intent output sudah terpasang.
// 2. Lukis tampilan halamannya (disertai embed font TrueType secara penuh).
DrawInvoicePage(PDF, AProfile, Data);
// 3. Bangun XML yang sudah pas dengan profilnya, lantas sisipkan
// dia sebagai associated file memakai /AFRelationship = Alternative.
InvoiceXML := BuildInvoiceXML(AProfile, Data); // AnsiString wujud dari UTF-8 bytes
FileID := PDF.AddFacturXAssociatedFileFromString(
InvoiceXML,
AProfile.ConformanceLevel, // contoh 'EN16931'
AProfile.FileName, // 'factur-x.xml'
AProfile.Description,
AProfile.Relationship, // 'Alternative'
AProfile.Version, // '1.0'
AProfile.CountryCode); // '' atau 'DE' atau 'FR'
if FileID <= 0 then
raise Exception.Create('Invoice XML could not be attached');
PDF.SaveToFile(TargetFile);
Satu kejelian di dalam rute pengelolaan datanya terletak di perkara encoding. Berkas XML yang tertanam itu mendeklarasikan encoding="UTF-8", dan karena fungsi tersebut mengambil parameter muatan byte dalam bentuk AnsiString, maka penyebutan nama penjual (seller) maupun pembeli (buyer) yang memiliki karakter rupa non-ASCII wajib mendarat mendatangi panggilannya murni sebagai kumpulan oktet UTF-8 (raw UTF-8 octets). Tindakan cast yang melulu menumpang lewat rute pengaturan kode standar milik sistem ANSI niscaya bakal menghancurkan karakter-karakternya sekaligus meloloskan secara mulus keberadaan invoice di mana muatan XML-nya lantas menyimpang dari deklarasi kepunyaannya sendiri. Modul aplikasi contoh itu secara eksplisit mengelola komando encoding ke arah susunan karakter UTF-8 terlebih dahulu saat sebelum mendistribusikannya ke dalam deretan nilai parameter fungsi, dan itu mutlak merupakan jurus penanganan yang luar biasa jitu serta paling aman manakala menghadapi pemrosesan interaksi tipe wujud komponen string menuju API rupa objek berorientasi susunan byte parameter PDF (byte-oriented PDF API)
Guna melampirkan komponen XML yang identitas e-invoice miliknya tak sepenuhnya dikenali pada spesifikasi, AddPDFA3AssociatedFileFromString bertindak selaku padanan fungsi wujud rupa generiknya (generic counterpart). Ia bakal mengumpulkan wujud komponen isian dari file name, MIME type, description, relationship, serta sekumpulan data bytes, lalu lantas merangkai sebuah susunan associated file berkelas wujud PDF/A-3 polos sepenuhnya tanpa dibekali rincian metadata khusus yang merujuk pada faktur ataupun proses pengecekan guideline apapun. Gunakan ia bagi menampung tumpangan muatan suplemen data; pakailah rupa metode Factur-X bagi urusan menyangkut tagihan faktur, supaya profil metadata serta perihal kecocokan guideline dapat dikelola dengan tepat
Manakala berkas dokumen ini selesai terangkai, persoalan berikutnya berpusat di soal apakah wujud struktur komponennya mampu menjawab standar uji kelayakan PDF/A maupun pemantauan porsi aksesibilitas, dan apakah itu bisa diteken melalui skema bubuhan signature tanpa membatalkan level kepatuhan (compliance) yang sudah diraihnya. Pembahasan menyangkut dua isu itu tuntas didalami di artikel bimbingan uji pra-kelayakan (preflight) PDF/A serta PDF/UA beserta stasiun pusat kepatuhan dan manajemen penandatanganan (compliance and signing workbench). Serangkaian elemen fitur itu disertakan laksana fasilitas bawaan kepunyaan instrumen Delphi PDF Library rupa produk dari PDFlibPas, sejajar berdampingan mengawal API elemen tag struktur, PDF/A, lantas juga profil isian properti manajemen dokumen (document-property APIs) di mana seluruh lintasan dari perakitan e-invoice faktur ini lantas melabuhkan operasinya