Tehnički članak

Factur-X i ZUGFeRD hibridni računi u Delphiju

Sukladan elektronički račun nije PDF s pričvršćenom XML datotekom sa strane. To je jedan jedini PDF/A-3 dokument koji nosi račun dvaput: jednom kao stranicu koju čita čovjek, i jednom kao strojno čitljiv Cross Industry Invoice XML pohranjen unutar datoteke kao pridružena datoteka. Ta dva prikaza opisuju isti račun. Ta dvojna narav je sva poanta porodica formata koje europski propisi sada zahtijevaju: Factur-X u Francuskoj i Njemačkoj, ZUGFeRD na tržištima njemačkog govornog područja, i XRechnung za njemačko javno naručivanje. Ovaj članak prolazi kroz to kako PDF Library for Delphi sastavlja takav hibridni račun u Delphiju, gdje standardi ostavljaju prostora za pogrešku, i zašto jedan profil u katalogu treba potpuno zaseban XML graditelj

Što je hibridni račun zapravo

Vidljiva stranica i ugrađeni XML služe različitim čitateljima. Referent koji odobrava plaćanje gleda iscrtanu stranicu. Sustav za obveze prema dobavljačima učitava XML, čita ukupne iznose i porezni raspored kao strukturirana polja, te knjiži stavku bez da čovjek išta upisuje. Semantički sadržaj tog XML-a uređuje EN 16931, europska norma koja definira model podataka računa: koja polja postoje, što znače, i koja su obvezna. EN 16931 je semantički model, a ne format datoteke. Factur-X, ZUGFeRD 2.x i XRechnung svi realiziraju taj model kao UN/CEFACT Cross Industry Invoice dokument, sintaksu koja nosi EN 16931 polja na žici

Da bi dokument bio i arhivabilan i samoopisujući, spremnik je PDF/A-3, definiran normom ISO 19005-3. PDF/A-3 je razina sukladnosti koja dopušta proizvoljne ugrađene datoteke, a upravo to XML računa mora biti. PDF/A-2 zabranjuje ugrađivanje datoteka koje same nisu PDF/A, pa Factur-X račun ne može biti PDF/A-2. Izbor PDF/A-3 stoga nije stvar preferencije, nego zahtjev koji izravno proizlazi iz želje da se u arhivski dokument ugrade podaci koji nisu PDF

Zašto je odnos Alternative

Ugrađivanje bajtova lakši je dio. ISO 32000 §7.11.4 definira stream ugrađene datoteke, objekt koji sadrži sirovi XML i njegove parametre. Dio koji datoteku čini valjanom pridruženom datotekom je §14.13, koji dodaje pojam pridružene datoteke i ključ /AFRelationship. Taj ključ navodi kako se ugrađeni podaci odnose prema sadržaju uz koji su pridruženi, a vrijednost koju Factur-X propisuje je Alternative

Izbor je važan jer bi druge vrijednosti tvrdile nešto neistinito o dokumentu. Source bi značilo da je XML materijal iz kojeg je generiran vidljivi sadržaj, izvornik iz kojeg stranica proizlazi. Supplement bi značilo da XML dodaje informacije izvan onoga što stranica prikazuje, dodatak koji nije sadržan u iscrtavanju. Nijedno od toga nije ono što Factur-X račun jest. XML i stranica dva su ravnopravna izraza jednog računa, koji nose isti pravni sadržaj u dva oblika. Alternative je vrijednost koja upravo to kaže: ravnopravan alternativni prikaz vidljivog sadržaja. Validator koji na Factur-X datoteci pročita bilo koji drugi odnos odbacit će je, i to opravdano, jer je taj odnos strojno čitljiva tvrdnja o tome čemu privitak služi

Anatomija hibridnog Factur-X računa izgrađenog s PDF Library for Delphi: jedan PDF/A-3 dokument drži ljudima čitljivu stranicu i ugrađeni factur-x.xml povezane s AFRelationship Alternative prema EN 16931 modelu
Jedna PDF/A-3 datoteka nosi račun dvaput kao ekvivalentne reprezentacije, i samo /AFRelationship = Alternative izražava tu tvrdnju na način koji validatori prihvaćaju

Katalog profila

Primjer E-Invoice koji se isporučuje s PDFlibPasom vodi isti put generiranja kroz šest profila, definiranih kao niz zapisa u InvoiceModel.pas. Svaki profil nosi vrijednosti koje pisaču trebaju: prikazni naziv, naziv ugrađene datoteke, razinu sukladnosti, /AFRelationship, verziju, neobavezan kod države, i GuidelineID URN koji XML objavljuje unutar svog konteksta dokumenta

Tih šest su Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED za Francusku, XRechnung 3.0, ZUGFeRD 1.0 COMFORT i ZUGFeRD 2.0 BASIC. GuidelineID je polje koje primatelju precizno govori koji profil očekivati, a vrijednosti su specifične. Factur-X EN16931 objavljuje urn:cen.eu:en16931:2017. XRechnung 3.0 objavljuje urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC objavljuje urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Naziv ugrađene datoteke također je dio ugovora. Factur-X profili ugrađuju factur-x.xml, XRechnung ugrađuje xrechnung.xml, a ZUGFeRD profili ugrađuju ZUGFeRD-invoice.xml ili zugferd-invoice.xml. Primatelj pretražuje nazive privitaka kako bi pronašao račun, pa naziv datoteke nije kozmetički detalj

Jedan detalj u katalogu vrijedi pažljivo pročitati. Većina profila koristi odnos Alternative, ali unos za XRechnung 3.0 u primjeru koristi Source. Ta dva formata odgovaraju različitim validatorima i konvencijama, a primjer postavlja odnos svakog profila iz kataloga umjesto da tvrdo kodira jednu vrijednost, zbog čega polje po profilu postoji umjesto konstante

Zamka ZUGFeRD 1.0

Primamljivo je pretpostaviti da je svaki profil EN 16931 Cross Industry Invoice s manjim varijacijama u broju neobaveznih polja koja popunjavate. To vrijedi za pet od šest. Ne vrijedi za ZUGFeRD 1.0 COMFORT, a razlog je strukturne, a ne kozmetičke naravi

Moderni profili emitiraju UN/CEFACT Cross Industry Invoice s verzijom imenskog prostora :100, čiji je korijenski element rsm:CrossIndustryInvoice. ZUGFeRD 1.0 prethodi toj shemi. To je CrossIndustryDocument iz 2014. godine s verzijom imenskog prostora :1p0, a njegov korijenski element je rsm:CrossIndustryDocument. URN-ovi imenskog prostora se razlikuju, korijenski element se razlikuje, a stablo elemenata razlikuje se posvuda: shema :1p0 grupira podatke pod ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery i ApplicableSupplyChainTradeSettlement, dok :100 koristi ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery i ApplicableHeaderTradeSettlement. Imenovanje je dovoljno slično da zavede, a dovoljno različito da se slomi

Riječ COMFORT u nazivu profila opisuje koliko su podaci bogati, profil razine automatizacije s potpunim stavkama, poreznim rasporedom i uvjetima plaćanja, a ne koju shemu nosi. Zato ne možete uzeti :100 dokument i samo ga preimenovati u ZUGFeRD 1.0. Primjer to rješava zastavicom na svakom zapisu profila i dvjema odvojenim graditeljskim funkcijama, birajući ispravnu prije nego što se generira ikakav XML

function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
  const Data: TInvoiceData): string;
begin
  // XMLFamily = 1 znači zastarjelu ZUGFeRD 1.0 :1p0 shemu; svaki
  // drugi profil je moderni UN/CEFACT :100 Cross Industry Invoice.
  if AProfile.XMLFamily = 1 then
    Result := BuildZUGFeRD1Text(AProfile, Data)
  else
    Result := BuildCII100Text(AProfile, Data);
end;

Ta podjela nije samo implementacijska finoća. Predavanje :100 stabla ZUGFeRD 1.0 primatelju proizvodi dokument koji ne prolazi validaciju sheme već na korijenskom elementu, pa obje porodice mora graditi kod koji zna koju od njih piše

Odabir razine PDF/A-3

PDF/A-3 ima tri razine sukladnosti, a PDF Library for Delphi ih odabire kroz SetPDFAMode. Način 5 je PDF/A-3b, razina koja jamči pouzdanu vizualnu reprodukciju. Način 6 je PDF/A-3a, koji dodaje strukturu s oznakama i zahtjeve pristupačnosti razine a. Način 7 je PDF/A-3u, koji zahtijeva da se sav tekst mapira u Unicode. Uključivanje načina također ugrađuje knjižničnu ugrađenu sRGB izlaznu namjeru, karakterizaciju boje koju PDF/A zahtijeva kako bi iscrtana boja bila definirana, a ne ovisna o uređaju

Većina tokova računa radi na 3b, što je dovoljno za vjernu vidljivu stranicu uz ugrađeni XML. Ako trebate izričit ICC profil umjesto ugrađenog, LoadOutputIntentProfile ga zamjenjuje nakon što je način postavljen. Primjer na taj način učitava repozitorijski sRGB profil i vraća se na ugrađenu namjeru kada datoteka nije dostupna, pa je izlazna namjera uvijek prisutna

PDF := TPDFlib.Create;
try
  // Način 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');

  // Neobavezno: zamijeni ugrađenu sRGB namjeru izričitim ICC profilom.
  if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
    { vrati se na ugrađenu sRGB namjeru koju je ugradio SetPDFAMode };
finally
  // ... nastavi graditi dokument
end;

Izgradnja hibridnog računa

Kad je spremnik konfiguriran, ostatak su tri koraka redom: postavite način PDF/A-3, iscrtajte stranicu čitljivu čovjeku, zatim pridružite XML kao pridruženu datoteku. Vidljiva stranica je obični sadržaj. Jedino ograničenje vrijedno pamćenja je da PDF/A zabranjuje neugrađene Standard 14 fontove, pa stranica mora ugraditi pravi font umjesto da upućuje na ugrađeni

Pridruživanje je jedan poziv. AddFacturXAssociatedFileFromString uzima sirove UTF-8 XML bajtove i metapodatke profila, piše stream ugrađene datoteke, registrira ga u nizu /AF Kataloga koji PDF/A-3 zahtijeva, primjenjuje /AFRelationship, i generira XMP e-invoice metapodatke koji dokument identificiraju kao Factur-X, ZUGFeRD ili XRechnung. Također provjerava odgovara li guideline ID iz XML-a razini sukladnosti koju ste zatražili, pa se neslaganje između izgrađenog XML-a i imenovanog profila uhvati, umjesto da tiho ode dalje

// 1. Način rada PDF/A-3 i namjera izlaza već su postavljeni
// 2. Iscrtaj vidljivu stranicu (ugrađuje pravi TrueType font)
DrawInvoicePage(PDF, AProfile, Data);

// 3. Izradi XML usklađen s profilom i priloži ga kao
//    povezanu datoteku s /AFRelationship = Alternative
InvoiceXML := BuildInvoiceXML(AProfile, Data);   // AnsiString UTF-8 bajtova
FileID := PDF.AddFacturXAssociatedFileFromString(
  InvoiceXML,
  AProfile.ConformanceLevel,   // e.g. 'EN16931'
  AProfile.FileName,           // 'factur-x.xml'
  AProfile.Description,
  AProfile.Relationship,       // 'Alternative'
  AProfile.Version,            // '1.0'
  AProfile.CountryCode);       // '' or 'DE' or 'FR'
if FileID <= 0 then
  raise Exception.Create('Invoice XML could not be attached');

PDF.SaveToFile(TargetFile);

Jedna suptilnost na putu podataka je kodiranje. Ugrađeni XML deklarira encoding="UTF-8", a metoda njegove bajtove uzima kao AnsiString, pa ne-ASCII naziv prodavatelja ili kupca do poziva mora stići kao sirovi UTF-8 oktet. Obično pretvaranje kroz sistemsku ANSI kodnu stranicu iskvarilo bi te znakove i tiho proizvelo račun čiji se XML više ne slaže s vlastitom deklaracijom. Primjer eksplicitno kodira u UTF-8 prije predaje bajtova, što je siguran način hranjenja bilo kojeg bajtno orijentiranog PDF API-ja Unicode nizom string

Dijagram kataloga profila PDF Library for Delphi koji usmjerava šest profila e-računa Factur-X, ZUGFeRD i XRechnung kroz BuildInvoiceXMLText u suvremenu obitelj UN/CEFACT Cross Industry Invoice i stariju obitelj ZUGFeRD 1.0 :1p0
Šest profila dijeli jednu točku upravljanja, ali se ZUGFeRD 1.0 COMFORT odvaja u naslijeđenu :1p0 shemu i treba vlastiti XML builder

Za pridruživanje XML-a koji nije prepoznati profil e-računa, AddPDFA3AssociatedFileFromString je generička istoznačnica. Uzima naziv datoteke, MIME tip, opis, odnos i bajtove, te piše običnu PDF/A-3 pridruženu datoteku bez ikakvih metapodataka specifičnih za račun ili provjera smjernica. Koristite je za dodatne podatke; za račune koristite Factur-X metodu, kako bi metapodaci profila i podudaranje smjernice bili napisani umjesto vas

Kad je dokument proizveden, sljedeća pitanja su prolazi li PDF/A i validaciju pristupačnosti, te može li se potpisati bez narušavanja sukladnosti. To je obrađeno u prolasku kroz PDF/A i PDF/UA preflight i radnoj stanici za sukladnost i potpisivanje. Sve se ovo isporučuje kao dio PDF Library for Delphi Delphi PDF Library, uz PDF/A, označavanje i API-je svojstava dokumenta na kojima se temelji put e-računa

Dijagram tijeka izgradnje za sastavljanje hibridnog Factur-X ili ZUGFeRD računa s PDF Library for Delphi: SetPDFAMode podešava PDF/A-3 spremnik, crta se vidljiva stranica, AddFacturXAssociatedFileFromString prilaže XML računa, a zatim SaveToFile objavljuje arhivski dokument
Sastavljanje ide kroz tri poredana koraka prije spremanja, uz razine sukladnosti, byte-safe rukovanje UTF-8 i generički associated-file API kao sporedne bilješke