Tehnični članek

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 PDFlibPas 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

Katalog profilov

Vzorec e-račun, ki se prinaša s PDFlibPas, 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 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;

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, PDFlibPas 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
  // 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');

  // Optional: swap the built-in sRGB intent for an explicit ICC profile.
  if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
    { fall back to the built-in sRGB intent that SetPDFAMode embedded };
finally
  // ... continue building the document
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. PDF/A-3 mode and output intent are already set.
// 2. Draw the visible page (embeds a real TrueType font).
DrawInvoicePage(PDF, AProfile, Data);

// 3. Build the profile-correct XML and attach it as an
//    associated file with /AFRelationship = Alternative.
InvoiceXML := BuildInvoiceXML(AProfile, Data);   // AnsiString of UTF-8 bytes
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);

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

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 PDFlibPas Delphi PDF, skupaj z API-ji PDF/A, označevanja in lastnosti dokumenta, na katerih temelji pot e-računa