Tehnični članak

Hibridni računi Factur-X in ZUGFeRD v Delphiju

Skladen elektronski račun ni PDF z XML-datoteko, pripeto ob strani. Je enoten dokument PDF/A-3, ki vsebuje račun dvakrat: enkrat kot stran, ki jo bere človek, in enkrat kot strojno berljiv XML medpanožnega računa (Cross Industry Invoice), shranjen znotraj datoteke kot pridružena datoteka. Obe predstavitvi opisujeta isti račun. Ta dvojna narava je bistvo skupin formatov, ki jih zahtevajo evropske direktive - Factur-X v Franciji in Nemčiji, ZUGFeRD na nemško govorečih trgih ter XRechnung za zaračunavanje v nemškem javnem sektorju. Ta članek opisuje, kako PDF Library for Delphi sestavi takšen hibridni račun v Delphiju, kje standardi puščajo prostor za napake in zakaj eden od profilov v katalogu zahteva popolnoma ločen gradnik XML

Kaj hibridni račun dejansko je

Vidna stran in vgrajeni XML služita različnim bralcem. Uradnik, ki odobri plačilo, si ogleda prikazano stran. Sistem za obveznosti do dobaviteljev uvozi XML, prebere skupne zneske in davčno razčlenitev kot strukturirana polja ter vnos poknjiži brez ročnega tipkanja. Semantično vsebino tega XML ureja EN 16931, evropski standard, ki definira podatkovni model računa: katera polja obstajajo, kaj pomenijo in katera so obvezna. EN 16931 je semantični model in ne oblika datoteke. Factur-X, ZUGFeRD 2.x in XRechnung ta model uveljavljajo kot dokument Cross Industry Invoice UN/CEFACT - sintakso, ki prenaša polja EN 16931 po žici

Da je dokument hkrati arhivljiv in samoopisen, je vsebnik PDF/A-3, opredeljen z ISO 19005-3. PDF/A-3 je raven skladnosti, ki dovoljuje poljubne vgrajene datoteke - kar XML račun natanko potrebuje. PDF/A-2 prepoveduje vgrajevanje datotek, ki same niso PDF/A, zato račun Factur-X ne more biti PDF/A-2. Izbira PDF/A-3 torej ni prednost, temveč zahteva, ki izhaja neposredno iz želje po vgrajevanju podatkov, ki niso PDF, v arhivski dokument

Zakaj je razmerje Alternative

Vgrajevanje bajtov je enostaven del. ISO 32000 §7.11.4 definira tok vgrajene datoteke - objekt, ki vsebuje surovi XML in njegove parametre. Del, ki datoteko naredi veljavno pridruženo datoteko, je §14.13, ki uvaja pojem pridružene datoteke in ključ /AFRelationship. Ta ključ določa, kako so vgrajeni podatki povezani z vsebino, ki ji so priloženi, vrednost, ki jo zahteva Factur-X, pa je Alternative

Izbira je pomembna, ker bi druge vrednosti trdile kaj lažnega o dokumentu. Source bi pomenila, da je XML gradivo, iz katerega je bila ustvarjena vidna vsebina - izvirnik, iz katerega izhaja stran. Supplement bi pomenila, da XML dodaja informacije prek tega, kar stran prikazuje - dodatek, ki ni vsebovan v upodabljanju. Nobeno ne ustreza temu, kar je račun Factur-X. XML in stran sta dve enakovredni izrazitvi enega računa in nosita isto pravno vsebino v dveh oblikah. Alternative je vrednost, ki pove natanko to: enakovredna alternativna predstavitev vidne vsebine. Validator, ki na datoteki Factur-X prebere katero koli drugo razmerje, jo bo zavrnil - in upravičeno, ker je razmerje strojno berljiva izjava o tem, čemu je priponka namenjena

Anatomija hibridnega računa Factur-X, zgrajenega s PDF Library for Delphi: en dokument PDF/A-3 hrani človeku berljivo stran in vgrajeni factur-x.xml, povezana z AFRelationship Alternative pod modelom EN 16931
Ena datoteka PDF/A-3 nosi račun dvakrat kot enakovredni predstavitvi, le /AFRelationship = Alternative pa to trditev izrazi na način, ki ga preverjevalniki sprejmejo

Katalog profilov

Vzorec e-račun, ki se prinaša s PDF Library for Delphi, vodi isto pot generiranja prek šestih profilov, opredeljenih kot polje zapisov v InvoiceModel.pas. Vsak profil nosi vrednosti, ki jih pisec potrebuje: prikazno ime, ime vgrajene datoteke, raven skladnosti, /AFRelationship, različico, neobvezno kodo države in URN GuidelineID, ki ga XML objavi znotraj konteksta svojega dokumenta

Šest profilov je Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED za Francijo, XRechnung 3.0, ZUGFeRD 1.0 COMFORT in ZUGFeRD 2.0 BASIC. GuidelineID je polje, ki prejemniku natančno pove, kateri profil pričakovati, vrednosti pa so specifične. Factur-X EN16931 objavi urn:cen.eu:en16931:2017. XRechnung 3.0 objavi urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC objavi urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Ime vgrajene datoteke je prav tako del pogodbe. Profili Factur-X vgradijo factur-x.xml, XRechnung vgradi xrechnung.xml, profili ZUGFeRD pa ZUGFeRD-invoice.xml ali zugferd-invoice.xml. Prejemnik pregleda imena priponk, da najde račun, zato ime datoteke ni kozmetično

Ena podrobnost v katalogu si zasluži pozorno branje. Večina profilov uporablja razmerje Alternative, toda vnos XRechnung 3.0 v vzorcu uporablja Source. Oba formata odgovarjata različnim validatorjem in konvencijam, vzorec pa nastavi razmerje vsakega profila iz kataloga in ne trdo kodira ene same vrednosti - zato polje za posamezni profil obstaja namesto konstante

Past ZUGFeRD 1.0

Skušnjava je predpostaviti, da je vsak profil medpanožni račun EN 16931 z manjšimi različicami v tem, koliko izbirnih polj zapolnite. To velja za pet od šestih. Za ZUGFeRD 1.0 COMFORT to ne velja, razlog pa je strukturen in ne kozmetičen

Moderni profili oddajajo medpanožni račun UN/CEFACT z različico imenskega prostora :100, katerega korenski element je rsm:CrossIndustryInvoice. ZUGFeRD 1.0 je starejši od te sheme. Gre za CrossIndustryDocument iz leta 2014 z različico imenskega prostora :1p0, katerega korenski element je rsm:CrossIndustryDocument. URI-ji imenskih prostorov se razlikujejo, korenski element se razlikuje in drevo elementov se razlikuje v celoti: shema :1p0 grupira podatke pod ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery in ApplicableSupplyChainTradeSettlement, medtem ko :100 uporablja ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery in ApplicableHeaderTradeSettlement. Poimenovanje je dovolj podobno, da zavaja, in dovolj različno, da povzroči napake

Beseda COMFORT v imenu profila opisuje bogatost podatkov - profil stopnje avtomatizacije s polnimi postavkami, davčno razčlenitvijo in pogoji plačila - ne pa katero shemo jih prenaša. Zato ne morete vzeti dokumenta :100 in ga preoznačiti za ZUGFeRD 1.0. Vzorec to obravnava z zastavico na vsakem zapisu profila in dvema ločenima funkcijama graditelja, ki izbereta pravo, preden se generira kateri koli XML

function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
  const Data: TInvoiceData): string;
begin
  // XMLFamily = 1 pomeni starejšo shemo ZUGFeRD 1.0 :1p0; vsi
  // drugi profili so sodobni UN/CEFACT :100 Cross Industry Invoice.
  if AProfile.XMLFamily = 1 then
    Result := BuildZUGFeRD1Text(AProfile, Data)
  else
    Result := BuildCII100Text(AProfile, Data);
end;

Ločitev ni implementacijska lepota. Podajanje drevesa :100 sprejemniku ZUGFeRD 1.0 ustvari dokument, ki ne prestane validacije sheme pri korenskem elementu, zato morata obe skupini biti zgrajeni s kodo, ki ve, katero piše

Izbira ravni PDF/A-3

PDF/A-3 ima tri ravni skladnosti, PDF Library for Delphi pa jih izbira prek SetPDFAMode. Način 5 je PDF/A-3b, raven, ki zagotavlja zanesljivo vizualno reprodukcijo. Način 6 je PDF/A-3a, ki dodaja zahteve označene strukture in dostopnosti ravni a. Način 7 je PDF/A-3u, ki zahteva, da je vse besedilo preslikano na Unicode. Aktiviranje načina prav tako vgradi vgrajeni izhodni namen sRGB knjižnice - barvno karakterizacijo, ki jo PDF/A zahteva, da je upodobljena barva opredeljena in ne odvisna od naprave

Večina računskih tokov deluje pri ravni 3b, ki zadostuje za zvesto vidno stran skupaj z vgrajenim XML. Če potrebujete izrecni profil ICC namesto vgrajenega, LoadOutputIntentProfile zamenja po nastavitvi načina. Vzorec naloži profil sRGB iz repozitorija na ta način in se vrne na vgrajeni izhodni namen, kadar datoteka ni dosegljiva, zato je izhodni namen vedno prisoten

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');

  // Izbirno: zamenjaj vgrajeni namen sRGB z izrecnim profilom ICC.
  if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
    { uporabi vgrajeni namen sRGB, ki ga je vdelal SetPDFAMode };
finally
  // ... nadaljuj z gradnjo dokumenta
end;

Gradnja hibridnega računa

Ko je vsebnik konfiguriran, sledijo trije koraki po vrsti: nastavi način PDF/A-3, nariši človeško berljivo stran, nato priloži XML kot pridruženo datoteko. Vidna stran je navadna vsebina. Ena omejitev, ki si jo je vredno zapomniti, je, da PDF/A prepoveduje nevgrajene standardne pisave iz nabora 14, zato mora stran vgraditi pravo pisavo namesto sklicevanja na vgrajeno

Priponka je en sam klic. AddFacturXAssociatedFileFromString sprejme surove bajte XML v kodiranju UTF-8 skupaj z metapodatki profila, zapiše tok vgrajene datoteke, ga registrira v polju /AF kataloga, ki ga zahteva PDF/A-3, uveljavi /AFRelationship in generira XMP metapodatke e-računa, ki dokument identificirajo kot Factur-X, ZUGFeRD ali XRechnung. Prav tako preveri, da se GuidelineID XML ujema z ravnjo skladnosti, ki ste jo zahtevali, tako da neskladje med XML, ki ste ga zgradili, in profilom, ki ste ga navedli, ni tiho odposlano

// 1. Način PDF/A-3 in izhodni namen sta že nastavljena.
// 2. Nariši vidno stran (vgradi pravo pisavo TrueType).
DrawInvoicePage(PDF, AProfile, Data);

// 3. Zgradi XML, ustrezen profilu, in ga priloži kot
//    pridruženo datoteko z /AFRelationship = Alternative.
InvoiceXML := BuildInvoiceXML(AProfile, Data);   // AnsiString bajtov UTF-8
FileID := PDF.AddFacturXAssociatedFileFromString(
  InvoiceXML,
  AProfile.ConformanceLevel,   // npr. 'EN16931'
  AProfile.FileName,           // 'factur-x.xml'
  AProfile.Description,
  AProfile.Relationship,       // 'Alternative'
  AProfile.Version,            // '1.0'
  AProfile.CountryCode);       // '' ali 'DE' ali 'FR'
if FileID <= 0 then
  raise Exception.Create('Invoice XML could not be attached');

PDF.SaveToFile(TargetFile);

Ena subtilnost v podatkovni poti je kodiranje. Vgrajeni XML razglaša encoding="UTF-8", metoda pa sprejema bajte kot AnsiString, zato mora nestandardno ime prodajalca ali kupca doseči klic kot surovi okteti UTF-8. Preprosta pretvorba prek sistemske kodne strani ANSI bi poškodovala te znake in tiho ustvarila račun, katerega XML se ne ujema z lastno razglasitvijo. Vzorec kodira v UTF-8 izrecno, preden preda bajte, kar je varen način podajanja kateremu koli PDF API-ju, ki temelji na bajtih, iz Unicodovega tipa string

Diagram kataloga profilov PDF Library for Delphi, ki šest profilov e-računov Factur-X, ZUGFeRD in XRechnung usmerja skozi BuildInvoiceXMLText v sodobno družino UN/CEFACT Cross Industry Invoice in staro družino ZUGFeRD 1.0 :1p0
Šest profilov si deli eno točko usmerjanja, a ZUGFeRD 1.0 COMFORT odstopi v zastarelo shemo :1p0 in potrebuje svoj graditelj XML

Za prilaganje XML, ki ni prepoznan profil e-računa, je AddPDFA3AssociatedFileFromString splošni ustreznik. Sprejme ime datoteke, vrsto MIME, opis, razmerje in bajte ter zapiše navadno pridruženo datoteko PDF/A-3 brez metapodatkov za račun ali preverjanj smernic. Uporabite ga za dopolnilne podatke; za račune pa metodo Factur-X, da se za vas zapišejo metapodatki profila in izvede preverjanje ujemanja smernic

Ko je dokument ustvarjen, so naslednja vprašanja, ali prestane validacijo PDF/A in dostopnosti ter ali ga je mogoče podpisati brez kršitve skladnosti. To je obravnavano v vodičtu za predkontroliranje PDF/A in PDF/UA in delavnici za skladnost in podpisovanje. Vse to se prinaša kot del knjižnice PDF Library for Delphi Delphi PDF, skupaj z API-ji PDF/A, označevanja in lastnosti dokumenta, na katerih temelji pot e-računa

Diagram gradbenega cevovoda za sestavo hibridnega računa Factur-X ali ZUGFeRD s PDF Library for Delphi: SetPDFAMode nastavi vsebnik PDF/A-3, nariše se vidna stran, AddFacturXAssociatedFileFromString pripne XML računa, nato SaveToFile objavi arhivski dokument
Sestavljanje poteka v treh urejenih korakih pred shranjevanjem, ravni skladnosti, bajtno varno ravnanje z UTF-8 in splošni API za povezane datoteke pa so stranske opombe