Vyhovujúca elektronická faktúra nie je PDF s pripojeným XML súborom na boku. Je to jediný dokument PDF/A-3, ktorý nesie faktúru dvakrát: raz ako stránku, ktorú číta človek, a raz ako strojom čitateľné XML Cross Industry Invoice uložené vo vnútri súboru ako pridružený súbor. Obe reprezentácie opisujú rovnakú faktúru. Táto duálna povaha je celou pointou skupín formátov, ktoré teraz vyžadujú európske mandáty: Factur-X vo Francúzsku a Nemecku, ZUGFeRD na po nemecky hovoriacich trhoch a XRechnung na fakturáciu vo verejnom sektore v Nemecku. Tento článok vás prevedie tým, ako PDFlibPas zostavuje takúto hybridnú faktúru v Delphi, kde štandardy ponechávajú priestor na chyby a prečo jeden profil v katalógu potrebuje úplne samostatný XML builder
Čo vlastne hybridná faktúra je
Viditeľná stránka a vložené XML slúžia rôznym čitateľom. Úradník schvaľujúci platbu sa pozerá na vygenerovanú stránku. Systém záväzkov načíta XML, prečíta súčty a rozpis daní ako štruktúrované polia a zaúčtuje záznam bez toho, aby niekto musel niečo zadávať. Sémantický obsah tohto XML sa riadi európskou normou EN 16931, ktorá definuje dátový model faktúry: aké polia existujú, čo znamenajú a ktoré sú povinné. EN 16931 je sémantický model, nie formát súboru. Factur-X, ZUGFeRD 2.x a XRechnung realizujú tento model ako dokument UN/CEFACT Cross Industry Invoice, čo je syntax, ktorá prenáša polia EN 16931 po sieti
Aby bol dokument archivovateľný a zároveň sebaopisný, kontajnerom je PDF/A-3, definovaný v ISO 19005-3. PDF/A-3 je úroveň zhody, ktorá povoľuje ľubovoľné vložené súbory, čo je presne to, čím musí XML faktúry byť. PDF/A-2 zakazuje vkladať súbory, ktoré samy nie sú PDF/A, takže faktúra Factur-X nemôže byť PDF/A-2. Voľba PDF/A-3 preto nie je preferenciou, ale požiadavkou, ktorá priamo vyplýva z túžby vložiť do archívneho dokumentu dáta, ktoré nie sú vo formáte PDF
Prečo je vzťah Alternative
Vloženie bajtov je tá ľahšia časť. ISO 32000 §7.11.4 definuje prúd vloženého súboru, objekt, ktorý drží surové XML a jeho parametre. Časť, ktorá robí zo súboru platný pridružený súbor, je §14.13, ktorý pridáva koncept pridruženého súboru a kľúč /AFRelationship. Tento kľúč uvádza, ako vložené údaje súvisia s obsahom, ku ktorému sú pripojené, a hodnota, ktorú Factur-X nariaďuje, je Alternative
Na voľbe záleží, pretože iné hodnoty by o dokumente tvrdili niečo nepravdivé. Source by znamenalo, že XML je materiál, z ktorého bol vygenerovaný viditeľný obsah, predloha, z ktorej stránka vychádza. Supplement by znamenalo, že XML pridáva informácie nad rámec toho, čo ukazuje stránka, niečo navyše, čo nie je obsiahnuté v zobrazení. Ani jedno nezodpovedá tomu, čím faktúra Factur-X je. XML a stránka sú dva ekvivalentné výrazy jednej faktúry, nesúce rovnaký právny obsah v dvoch formách. Alternative je hodnota, ktorá hovorí presne toto: ekvivalentná alternatívna reprezentácia viditeľného obsahu. Validátor, ktorý načíta akýkoľvek iný vzťah v súbore Factur-X, ho zamietne, a to oprávnene, pretože tento vzťah je strojom čitateľné tvrdenie o tom, na čo príloha slúži
Katalóg profilov
Ukážka e-faktúry dodávaná s PDFlibPas riadi rovnakú cestu generovania naprieč šiestimi profilmi, ktoré sú definované ako pole záznamov v InvoiceModel.pas. Každý profil nesie hodnoty, ktoré zapisovač potrebuje: zobrazovaný názov, názov vloženého súboru, úroveň zhody, /AFRelationship, verziu, voliteľný kód krajiny a URN GuidelineID, ktoré XML oznamuje vo vnútri svojho kontextu dokumentu
Týchto šesť je Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED pre Francúzsko, XRechnung 3.0, ZUGFeRD 1.0 COMFORT a ZUGFeRD 2.0 BASIC. GuidelineID je pole, ktoré príjemcovi presne povie, aký profil má očakávať a tieto hodnoty sú špecifické. Factur-X EN16931 oznamuje urn:cen.eu:en16931:2017. XRechnung 3.0 oznamuje urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC oznamuje urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Názov vloženého súboru je tiež súčasťou zmluvy. Profily Factur-X vkladajú factur-x.xml, XRechnung vkladá xrechnung.xml a profily ZUGFeRD vkladajú ZUGFeRD-invoice.xml alebo zugferd-invoice.xml. Prijímač prehľadáva názvy príloh, aby našiel faktúru, takže názov súboru nie je len kozmetický
Jeden detail v katalógu si zaslúži pozorné čítanie. Väčšina profilov používa vzťah Alternative, ale položka XRechnung 3.0 v ukážke používa Source. Tieto dva formáty podliehajú iným validátorom a konvenciám a ukážka nastavuje vzťah každého profilu z katalógu namiesto pevného kódovania jedinej hodnoty, čo je dôvod, prečo existuje pole pre každý profil namiesto konštanty
Pasca ZUGFeRD 1.0
Je lákavé predpokladať, že každý profil je faktúra Cross Industry Invoice podľa EN 16931 s menšími odchýlkami v tom, koľko voliteľných polí vyplníte. To platí pre päť zo šiestich. Neplatí to pre ZUGFeRD 1.0 COMFORT a dôvod je skôr štrukturálny než kozmetický
Moderné profily vysielajú UN/CEFACT Cross Industry Invoice s verziou menného priestoru :100, ktorej koreňovým prvkom je rsm:CrossIndustryInvoice. ZUGFeRD 1.0 predchádza tejto schéme. Ide o CrossIndustryDocument z roku 2014 s verziou menného priestoru :1p0 a jeho koreňovým prvkom je rsm:CrossIndustryDocument. URN menného priestoru sa líšia, koreňový prvok sa líši a strom prvkov sa líši v celom rozsahu: schéma :1p0 zoskupuje údaje pod ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery a ApplicableSupplyChainTradeSettlement, kdežto :100 používa ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery a ApplicableHeaderTradeSettlement. Pomenovanie je dostatočne podobné na to, aby zavádzalo, a dostatočne odlišné na to, aby veci pokazilo
Slovo COMFORT v názve profilu opisuje to, aké bohaté sú údaje, ide o profil na úrovni automatizácie s úplnými riadkovými položkami, rozpisom daní a platobnými podmienkami, a nie to, aká schéma ho nesie. Nemôžete teda vziať dokument :100 a len ho premenovať na ZUGFeRD 1.0. Ukážka to rieši príznakom v každom zázname profilu a dvoma samostatnými funkciami builderov, pričom vyberie tú správnu skôr, ako sa vygeneruje akékoľvek 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;
Toto rozdelenie nie je len implementačnou drobnosťou. Dodanie stromu :100 príjemcovi ZUGFeRD 1.0 vytvorí dokument, ktorý zlyhá pri validácii schémy na koreňovom prvku, takže tieto dve rodiny musia byť postavené kódom, ktorý vie, ktorú z nich zapisuje
Výber úrovne PDF/A-3
PDF/A-3 má tri úrovne zhody a PDFlibPas ich vyberá pomocou SetPDFAMode. Režim 5 je PDF/A-3b, úroveň, ktorá zaručuje spoľahlivú vizuálnu reprodukciu. Režim 6 je PDF/A-3a, ktorý pridáva štruktúru tagov a požiadavky na prístupnosť úrovne a. Režim 7 je PDF/A-3u, ktorý vyžaduje, aby bol všetok text zmapovaný do Unicode. Povolenie tohto režimu zároveň vkladá vstavaný výstupný zámer (output intent) sRGB knižnice, čo je charakterizácia farieb, ktorú PDF/A vyžaduje, aby bola vykreslená farba definovaná, a nie závislá od zariadenia
Väčšina tokov faktúr prebieha v 3b, čo postačuje na vernú viditeľnú stránku plus vložené XML. Ak potrebujete explicitný ICC profil namiesto vstavaného, LoadOutputIntentProfile ho po nastavení režimu vymení. Ukážka takto načíta profil sRGB z repozitára a ak súbor nie je dostupný, vráti sa k vstavanému zámeru, takže výstupný zámer je vždy prítomný
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;
Vytvorenie hybridnej faktúry
S nakonfigurovaným kontajnerom je zvyšok tvorený tromi krokmi v poradí: nastavenie režimu PDF/A-3, nakreslenie stránky čitateľnej pre človeka, potom pripojenie XML ako pridruženého súboru. Viditeľná stránka je obyčajný obsah. Jediné obmedzenie, ktoré si treba pamätať, je, že PDF/A zakazuje nevložené písma Standard 14, takže stránka musí vkladať skutočný typ písma namiesto odkazovania na nejaký vstavaný
Pripojenie je jediné volanie. AddFacturXAssociatedFileFromString berie surové bajty XML v UTF-8 plus metaúdaje profilu, zapisuje prúd vloženého súboru, registruje ho v poli katalógu /AF, ktoré vyžaduje PDF/A-3, aplikuje /AFRelationship a generuje metaúdaje e-faktúry XMP, ktoré identifikujú dokument ako Factur-X, ZUGFeRD alebo XRechnung. Skontroluje tiež, či sa ID usmernenia XML zhoduje s úrovňou zhody, ktorú ste požadovali, takže nesúlad medzi vytvoreným XML a pomenovaným profilom je zachytený a nie ticho odoslaný
// 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);
Jednou zo záludností v dátovej ceste je kódovanie. Vložené XML deklaruje encoding="UTF-8" a metóda prijíma jeho bajty ako AnsiString, takže meno predávajúceho alebo kupujúceho bez ASCII musí dosiahnuť volanie ako surové oktety UTF-8. Jednoduché pretypovanie prostredníctvom systémovej kódovej stránky ANSI by tieto znaky poškodilo a ticho vytvorilo faktúru, ktorej XML by sa už nezhodovalo s jej vlastnou deklaráciou. Ukážka pred odovzdaním bajtov explicitne kóduje do UTF-8, čo je bezpečný spôsob, ako vložiť do akéhokoľvek bajtovo orientovaného API na PDF z Unicode string
Na pripojenie XML, ktoré nie je rozpoznaným profilom e-faktúry, slúži ako všeobecný proťajšok AddPDFA3AssociatedFileFromString. Berie názov súboru, typ MIME, popis, vzťah a bajty a zapisuje obyčajný pridružený súbor PDF/A-3 bez akýchkoľvek metaúdajov špecifických pre faktúry alebo kontrol usmernení. Použite ho na doplňujúce údaje; metódu Factur-X použite pre faktúry, takže metaúdaje profilu a zhoda s usmernením sa zapíšu za vás
Keď je dokument vytvorený, ďalšou otázkou je, či prejde validáciou na PDF/A a prístupnosť, a či ho možno podpísať bez porušenia zhody. Týmto témam sa venujeme v prehľade preflightu PDF/A a PDF/UA v Delphi a v pracovnom stole pre zhodu a podpisovanie. Toto všetko sa dodáva ako súčasť PDFlibPas Delphi PDF Library, popri API pre PDF/A, tagovanie a vlastnosti dokumentov, na ktorých stavia cesta pre e-faktúry