Technický článok

Validácia elektronických faktúr: veraPDF a Mustang v Delphi

Faktúra Factur-X alebo ZUGFeRD sú dva dokumenty pod jedným názvom súboru. Vonkajším dokumentom je kontajner PDF/A-3, ktorý musí archívna čítačka akceptovať ďalších desať rokov. Vnútorným dokumentom je XML faktúra, ktorú musí účtovný systém kupujúceho analyzovať podľa EN 16931. Chybou, ktorá posiela poškodené faktúry do produkcie, je viera, že správne vytvorenie prvého dokumentu znamená bezproblémové vytvorenie druhého. Nie je to tak. Súbor môže byť bezchybný PDF/A-3 a stále obsahovať XML, ktoré žiadny daňový úrad neakceptuje, a môže obsahovať učebnicové XML podľa EN 16931 vo vnútri kontajnera, ktorý neprejde archivačnou validáciou. Tieto dve vrstvy sa validujú dvoma rôznymi nástrojmi, ktoré o sebe navzájom nič nevedia, a skutočný proces musí uspokojiť oba

Dva validátory, dve rôzne otázky

veraPDF je referenčná implementácia pre PDF/A. Nasmerujte ho na faktúru a odpovie na jednu otázku: je toto vyhovujúci súbor PDF/A-3. Kontroluje veci, na ktorých záleží ISO 19005-3. Je vložené každé písmo. Existuje OutputIntent. Deklarujú metaúdaje XMP správnu časť a úroveň zhody. Pri e-faktúre kontroluje aj prepojenie pridruženého súboru, ktoré vyžaduje PDF/A-3, pretože XML ide spolu ako vložený súbor s /AFRelationship a záznamom v poli /AF v katalógu dokumentu. veraPDF nehovorí nič o tom, či celková suma faktúry sedí, pretože to nie je v jeho kompetencii

Mustang je open-source validátor od Mustangproject. Kladie ortogonálnu otázku: je vložené XML platnou faktúrou. Spúšťa XML proti schéme pre deklarovaný profil a potom aplikuje obchodné pravidlá EN 16931 a na nich navrstvené súbory pravidiel špecifických pre danú krajinu, medzi nimi aj CIUS XRechnung. Kontroluje, či je prítomný identifikátor DPH predávajúceho, keď to súčty vyžadujú, či sa sumy zliav a poplatkov zhodujú s celkovou sumou dokumentu, či sa URN profilu v XML zhoduje s tým, za čo sa súbor vydáva. Mustang sa nestará o to, či okolité PDF vkladá svoje písma, pretože to je úloha veraPDF

Žiadny z nástrojov nie je nadmnožinou toho druhého. veraPDF prepustí štrukturálne dokonalý kontajner okolo nezmyselného XML. Mustang prepustí dokonalé XML zabalené v kontajneri s chýbajúcim OutputIntent. Každý zachytí presne ten typ chyby, voči ktorému je ten druhý slepý, čo je celý dôvod, prečo seriózny testovací rámec spúšťa oba a považuje súbor za pripravený na odoslanie, len keď obaja súhlasia

Validačná matica

Na preukázanie toho, že knižnica produkuje súbory, ktoré prežijú obe brány, testovací rámec vytvára maticu. Šesť profilov faktúr pokrýva rozsah, s ktorým sa európsky proces stretáva v praxi: Factur-X EN 16931, Factur-X BASIC, variant Factur-X EXTENDED pre B2B vo Francúzsku, XRechnung 3.0, ZUGFeRD 1.0 COMFORT a ZUGFeRD 2.0 BASIC. Každý profil je generovaný voči dvom podúrovniam zhody PDF/A, 3b a 3u, pretože požiadavky úrovne B a úrovne U sa líšia v mapovaní Unicode a súbor, ktorý prejde jedným, môže zlyhať na druhom. Šesť profilov krát dve úrovne je dvanásť súborov, pričom každý z nich je vytvorený bez používateľského rozhrania (headless) pomocou rovnakej cesty kódu, s akou sa dodáva ukážka GUI, takže testované artefakty nie sú pre test manuálne vylepšované

Generátor zapíše všetkých dvanásť a skript každý z nich vloží do oboch validátorov. Pri prvom úplnom spustení veraPDF schválilo všetkých dvanásť. Prepojenie kontajnera bolo správne vo všetkých smeroch: pridružené súbory boli zaregistrované, zhoda s XMP bola deklarovaná, output intents boli na svojom mieste. Mustang schválil osem. Štyri faktúry boli štrukturálne platné súbory PDF/A-3 nesúce XML, ktoré validátor obchodných pravidiel odmietol, čo je presne ten rozdiel, na ktorého odhalenie slúži prístup dvoch nástrojov. Keby testovací rámec dôveroval len veraPDF, tieto štyri by vyzerali ako hotové

Dve opravy, ktoré uzavreli medzeru

Štyri zlyhania v Mustangu pochádzali z dvoch rôznych príčin a oprava každej z nich je detail, ktorý by ste mali poznať predtým, ako sami vygenerujete tieto profily

Prvým bol profil Factur-X EXTENDED France B2B. Pôvodný generátor odovzdal interný štítok ako úroveň zhody a interné URN ako usmernenie a Mustang odmietol súbor s chybou o neplatnej hodnote zhody nasledovanou chybou o nepodporovanom type profilu. Dôvodom je, že pole XMP fx:ConformanceLevel nie je slot s voľným textom pre vaše vlastné pomenovanie profilu. Factur-X preň definuje presne päť štandardných hodnôt: MINIMUM, BASIC WL, BASIC, EN 16931 a EXTENDED. B2B faktúra špecifická pre Francúzsko je stále dokumentom s profilom EXTENDED, pokiaľ ide o metaúdaje XMP. Francúzsky charakter faktúry nie je vyjadrený vymyslením šiestej hodnoty zhody. Vyjadruje sa kódom krajiny FR a identifikátorom usmernenia vo vnútri XML, ktorý musí obsahovať predponu urn:cen.eu:en16931:2017#conformant#, ktorá označuje CIUS vyhovujúci EN 16931. Odovzdanie štandardnej hodnoty EXTENDED s kódom krajiny FR a správnym URN usmernenia urobilo súbor vyhovujúcim

V API knižnice je to volanie AddFacturXAssociatedFileFromString, kde sú zhoda, krajina a usmernenie zosúladené. Argument pre úroveň zhody nesie štandardný token, argument pre kód krajiny nesie FR a URN usmernenia sa nachádza v bajtoch XML, ktoré odovzdáte

var
  FileID: Integer;
begin
  PDF.SetPDFAMode(5);            // PDF/A-3b
  PDF.NewDocument;
  // ... draw the human-readable invoice page ...
  // ExtendedXML carries an EN 16931 guideline URN of the form
  //   urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended
  FileID := PDF.AddFacturXAssociatedFileFromString(
    ExtendedXML,
    'EXTENDED',          // standard fx:ConformanceLevel, not an internal label
    'factur-x.xml',
    'Factur-X EXTENDED invoice',
    'Alternative',       // /AFRelationship
    '1.0',
    'FR');               // France B2B marked by country code, not by conformance
  if FileID = 0 then
    raise Exception.Create('Factur-X attachment rejected');
  PDF.SaveToFile('02_Factur-X-EXTENDED-FR_PDFA-3b.pdf');
end;

Druhou príčinou bol profil ZUGFeRD 1.0 COMFORT a nemalo to nič spoločné s metaúdajmi. ZUGFeRD 1.0 je validovaný proti XSD :1p0, ktoré je prísnejšie z hľadiska kardinality, než naznačujú zhrnutia v texte. XSD vyžaduje, aby sumarizácia vyrovnania v hlavičke, ram:SpecifiedTradeSettlementMonetarySummation, obsahovala ram:ChargeTotalAmount a ram:AllowanceTotalAmount, a to každé presne raz. Vygenerované XML obe vynechalo, takže Mustang nahlásil, že tieto prvky sa musia vyskytnúť presne raz. Tieto nie sú voliteľné, keď schéma hovorí, že minOccurs je jedna. Ich vygenerovanie v poradí sekvencie XSD, bezprostredne po ram:LineTotalAmount, s hodnotou 0.00, keď neexistujú žiadne poplatky alebo zľavy, schému uspokojilo. Nula znamená prítomný prvok; chýbajúci prvok je porušením schémy. S týmito dvoma nasadenými opravami dosiahla matica dvanásť z dvanástich v Mustangu, pričom zostala dvanásť z dvanástich vo veraPDF

Polia XRechnung, ktoré preklopia z neplatného na platné

XRechnung si zaslúži osobitnú zmienku, pretože jeho nemecký CIUS pridáva obchodné pravidlá, ktoré v základnej sade EN 16931 chýbajú, a zlyhávajú spôsobmi, ktoré na prvý pohľad vyzerajú, že s dokumentom nie je nič v neporiadku. Dve z nich sa týkajú elektronických adries. BT-34 je elektronická adresa predávajúceho a BT-49 je elektronická adresa kupujúceho, čo sú smerovacie koncové body, ktoré nemecký portál verejného sektora používa na doručenie a potvrdenie faktúry. Základný model EN 16931 ich považuje za voliteľné. XRechnung nie. Vynechajte ktorúkoľvek a faktúra je síce správne naformátovaná, platná podľa schémy, ale odmietnutá

Treťou je pravidlo BR-DE-6, ktoré vyžaduje, aby bolo prítomné kontaktné telefónne číslo predávajúceho. Je to druh poľa, ktoré vývojár vynechá, pretože to pôsobí skôr ako prezentácia než dáta, a jeho neprítomnosť spôsobí zlyhanie validácie, ktoré ukazuje na skupinu kontaktov predávajúceho, a nie na niečo zjavne chýbajúce. Doplnenie BT-34, BT-49 a telefónneho čísla predávajúceho je to, čo presunie súbor XRechnung z neplatného na platný pod Mustangom, a nič z toho nemení to, čo vidí veraPDF, pretože všetky tri sa nachádzajú v XML

Prepojenie výstupu knižnice s validátorom

Architektonický bod tohto testovacieho rámca sa dá zovšeobecniť na akýkoľvek obchodný systém. Knižnica PDF zapíše vyhovujúci kontajner a vloží XML. Nepokúša sa a ani by sa nemala pokúšať byť autoritou pre obchodné pravidlá EN 16931. ValidateFacturXInvoice v knižnici kontroluje konzistenciu kontajnera, to, že pole /AF katalógu, strom mien vložených súborov, XMP DocumentFileName, profil, usmernenie a /AFRelationship spolu súhlasia, ale nevaliduje daňové kódy ani nezlaďuje sumy. Správne rozdelenie práce je také, že obchodný systém extrahuje XML a odovzdá ho špecializovanému validátoru faktúr, presne tak, ako ho testovací rámec odovzdáva Mustangu

Spätné načítanie súboru vám povie, čo bolo v skutočnosti zapísané. DetectFacturXInvoice nahlási, či bola faktúra rozpoznaná a GetFacturXInvoiceInfo načíta polia metaúdajov podľa značky: značka 1 je názov vloženého súboru, značka 2 je XMP DocumentFileName, značka 5 je úroveň zhody, značka 6 je identifikátor usmernenia a značka 7 je /AFRelationship. Potvrdenie, že úroveň zhody, ktorú načítate späť, je štandardný token a nie interný štítok, je najlacnejší spôsob, ako zachytiť chybu EXTENDED predtým, ako súbor opustí vaše zostavenie

function ExtractAndInspect(const PdfPath: string): AnsiString;
var
  Profile, Guideline: WideString;
begin
  Result := '';
  PDF.LoadFromFile(PdfPath);
  if PDF.DetectFacturXInvoice = 1 then
  begin
    Profile   := PDF.GetFacturXInvoiceInfo(5);  // fx:ConformanceLevel
    Guideline := PDF.GetFacturXInvoiceInfo(6);  // XML guideline ID
    Writeln('Profile:   ', Profile);
    Writeln('Guideline: ', Guideline);
    // Hand the raw XML to a dedicated EN 16931 / Mustang validator.
    Result := PDF.ExtractFacturXXMLToString;
  end;
end;

ExtractFacturXXMLToString vracia surové bajty XML ako AnsiString, pripravené na zápis do súboru alebo streamovanie do validačného procesu. V testovacom rámci je týmto cieľom Mustang, vyvolaný cez jeho jar pre príkazový riadok, s veraPDF spusteným v rovnakom prechode nad rovnakým súborom. Prepojenie je malé: konzolový generátor EInvoiceValidation.dpr zapisuje dvanásť súborov pomocou zdieľaného modelu faktúry z ukážky a skript run-validation.ps1 riadi oba validátory v cieľovom adresári a vypíše tabuľku úspechov a zlyhaní. Rovnaký dvojkrokový tvar, generovanie pomocou knižnice a overovanie pomocou externých validátorov, by mala spúšťať úloha kontinuálnej integrácie pri každej zmene generovania faktúr, pretože jediným spôsobom, ako zistiť, či súbor spĺňa obe vrstvy, je spýtať sa oboch nástrojov

Ak musí váš proces tiež certifikovať kontajner pred podpisom, preflight stránka tejto práce je pokrytá v našom prehľade preflightu PDF/A a PDF/UA v Delphi a širší postup certifikácie a následného podpisu je opísaný v pracovnom stole pre zhodu a podpisovanie. Obe stavajú na rovnakej ceste generovania, ktorá sa dodáva ako súčasť Delphi PDF Library pre Delphi a C++Builder, popri API pre PDF/A, pridružené súbory a metaúdaje, ktoré sú tu použité