PDFlibPas, losLab Delphi PDF Library, kontroluje obchodní pravidla EN 16931, XRechnung 3.0.2 a Peppol BIS Billing 3.0 nativně v Pascalu. TPDFlib.ValidateInvoiceBusinessRulesBuiltIn načte XML vložené do PDF faktury Factur-X nebo ZUGFeRD a vrátí JSON report s oficiálními ID pravidel, bez Java runtime, bez Saxonu a bez Schematron toolchainu. Report sám sebe označuje za podmnožinu a tenhle popisek je nejdůležitější pole v něm
Tlak kolem tohle tématu je hlavně německý. Od ledna 2025 musí každý tuzemský podnik v Německu umět přijímat strukturované B2B e-faktury a povinnost je vystavovat přichází postupně. Příjmová strana účetní nebo ERP aplikace v Delphi najednou musí zodpovědět otázku, kterou si předtím nikdy nekladla: je XML uvnitř tohohle PDF opravdu platná faktura EN 16931? Oficiální odpověď přichází ze Schematron artefaktů zkompilovaných do XSLT 2.0 nebo z KoSIT validátoru pro XRechnung a obojí běží na JVM. Distribuovat a opravovat JRE vedle každé desktopové instalace jen kvůli odmítnutí špatné faktury je náklad, který spousta týmů radši nést nechce
Co vestavěný validátor vlastně kontroluje?
Nativní validátor mapuje XML faktury na obchodní pojmy EN 16931 a provádí zdokumentovanou podmnožinu pravidel z validačních artefaktů EN 16931 1.3.16, přičemž každý nález hlásí pod oficiálním ID pravidla. Podporované jsou obě syntaxe: UN/CEFACT CII D16B, kterou vkládají Factur-X i ZUGFeRD, a OASIS UBL 2.1 Invoice a CreditNote. Pokryté oblasti jsou:
- Povinné údaje: identifikátor specifikace, číslo faktury, datum vystavení, kód typu, měna, jména prodávajícího a kupujícího, adresy, součty dokumentu, rozpad DPH a řádky (BR-01 až BR-16, BR-21 až BR-28, BR-CO-04, BR-CO-18, BR-CO-26, BR-45 až BR-48)
- Výpočty: součty řádků, slev, poplatků, daně a k úhradě, plus základ a výše daně každé kategorie DPH (BR-CO-10 až BR-CO-17, BR-xx-08, BR-xx-09)
- Pravidla kategorií DPH pro S, Z, E, AE, K, G, O, L a M: sazby řádků, důvody osvobození a DPH identifikátory, které každá kategorie vyžaduje
- Všechny číselníky BR-CL-01 až BR-CL-26, včetně více než 2100 kódů jednotek UN/ECE Rec 20/21 za BR-CL-23, a pravidla dvou desetinných míst BR-DEC-01, -05, -09 až -20 a -23
- Pravidla profilů: XRechnung 3.0.2 (BR-DE-1 až BR-DE-31, BR-DEX-01 až BR-DEX-14) na CII a UBL a core, common, code list a national pravidla Peppol BIS Billing 3.0.20 na UBL
Rozsah se hlásí, ne naznačuje. Bez souboru XSD potvrdí schema fáze jen dobrou formu a kořenový prvek. Každá fáze vypíše pravidla mimo podmnožinu do pole notCovered a GetBuiltInInvoiceRuleCatalog vrátí celou mapu jako JSON, takže vaše UI může uživateli ukázat přesně, co pokrývá zelený výsledek
Jedna hranice jde snadno přehlédnout. Dokumenty Factur-X MINIMUM a BASIC WL nejsou faktury EN 16931, takže validátor na ně pravidla EN 16931 neaplikuje. Místo toho přidá informativní nález BuiltIn.Coverage.Profile, který to říká, což je čestná odpověď a zároveň nápověda: podle německých pravidel ty dva profily stejně nepočítají jako e-faktury
Validace vloženého XML hybridního PDF
ValidateInvoiceBusinessRulesBuiltIn bere jeden argument, název vloženého souboru, a prázdný řetězec je běžná volba. PDFlibPas pak vybere přílohu, kterou identifikuje detekce Factur-X; u PDF bez metadat Factur-X se vrátí k jediné příloze .xml. Několik kandidátů .xml nebo dvě přílohy se stejným názvem vyústí v chybu Invoice.AttachmentAmbiguous místo hádání, dekódování je omezené na 64 MB a chybějící faktura nastaví LastErrorCode na 401
uses
System.SysUtils, System.JSON, PDFlibrary;
procedure CheckInboundInvoice(const FileName: string);
var
PDF: TPDFlib;
Report: TJSONObject;
begin
PDF := TPDFlib.Create;
try
if PDF.LoadFromFile(FileName, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s (error %d)',
[FileName, PDF.LastErrorCode]);
// Prázdný název: validuj zjištěnou přílohu Factur-X / ZUGFeRD
Report := TJSONObject.ParseJSONValue(
PDF.ValidateInvoiceBusinessRulesBuiltIn('')) as TJSONObject;
try
Writeln('profile: ', Report.GetValue<string>('profile'));
Writeln('valid: ', Report.GetValue<Boolean>('valid'));
Writeln('coverage: ', Report.GetValue<string>('coverage')); // "podmnožina"
Writeln('full: ', Report.GetValue<Boolean>('fullConformance')); // tady nikdy True
finally
Report.Free;
end;
finally
PDF.Free;
end;
end;
Vrchní úroveň reportu nese standard, version, profile, complete, valid, fullConformance, coverage, fáze schema a schematron a objekt package, který je ve vestavěném režimu null. Každá fáze hlásí status, engine, počet provedených pravidel, své pokrytí a pole issues. Každý nález má ruleID, fieldPath, severity, category, line a message
Čtení nálezů podle kategorie místo podle zprávy
Pole category dělá report použitelným v reálné schránce, protože řekne, kdo má problém opravit. Hodnoty jsou syntax, schema, cardinality, calculation, vat, codelist, decimal, profile, coverage, processing a integrity. Nález calculation většinou ukazuje na zaokrouhlovací kód odesílatele, nález codelist na číselníky v master datech a nález profile na pole XRechnung, třeba kupujícího referenci
procedure ListIssues(Report: TJSONObject);
var
Item: TJSONValue;
Issue: TJSONObject;
begin
for Item in Report.GetValue<TJSONArray>('schematron.issues') do
begin
Issue := Item as TJSONObject;
if Issue.GetValue<string>('severity') = 'info' then
Continue; // poznámky pokrytí, třeba BuiltIn.Coverage.Profile
Writeln(Format('[%s] %s at %s: %s', [
Issue.GetValue<string>('category'),
Issue.GetValue<string>('ruleID'),
Issue.GetValue<string>('fieldPath'),
Issue.GetValue<string>('message')]));
end;
end;
Stejnou smyčku spusťte i nad schema.issues, protože tam dopadají chyby formy a kořenového prvku a fáze obchodních pravidel nikdy nevidí dokument, který neuměla rozparsovat
Proč stejná faktura projde v CII a selže v UBL?
Oficiální artefakty neaplikují v obou syntaxích stejnou číselnou toleranci a PDFlibPas bere každé pravidlo z XSLT své syntaxe, nesnaží se je sjednotit. BR-CO-17 je nejzřetelnější případ. Kontroluje, že daňová částka kategorie DPH se rovná základu krát sazba zaokrouhlenému na dvě desetinná místa, a obě syntaxe dovolují rozdíl jedné měnové jednotky. Artefakt CII to zapíše jako <= a >=; artefakt UBL používá abs s ostrým <
Vezměte základ 100,00 s devatenácti procenty, takže očekávaná daň je 19,00, a fakturu, která hlásí 20,00. V CII dá 20,00 minus 1 rovných 19,00, což podmínku <= splňuje, a BR-CO-17 projde. V UBL je tentýž srovnávací test ostrý, 19,00 není méně než 19,00 a BR-CO-17 selže. Nikdo takovou fakturu neposílá schválně, ale generátory s chybou v zaokrouhlování sedí přesně na takových hranicích a test suite pokrývající jen jednu syntaxi si jich nevšimne
Rozdíly jdou dál než BR-CO-17. BR-xx-08 vyžaduje přesnou rovnost po zaokrouhlení na dvě desetinná místa v CII, zatímco UBL dovoluje ±1 a spustí se jen tehdy, když kategorie skutečně má řádky, slevy nebo poplatky. BR-O-08 je přesné v obou. Artefakt CII definuje BR-AF-09 a BR-AG-09 jako true(), takže tam nikdy neselhávají, zatímco UBL je kontroluje s ±1. A když rozpad DPH v CII nemá vůbec žádnou sazbu, pravidla kategorií mimo O procházejí prázdně, protože XPath every $rate nad prázdnou sekvencí je true. Nativní kód je musí přeskočit stejným způsobem, jinak hlásí chyby na oficiálních vzorových souborech
Druhá past se týká ID pravidel. Prefix kategorie pravidla není kód kategorie DPH: K mapuje na BR-IC, L na BR-AF a M na BR-AG. Odvozování ID z kódu kategorie vyrábí BR-K-09 a BR-K-10, ID, která v EN 16931 neexistují. Pokud vaše aplikace mapuje ID pravidel na nápovědní texty, berte ID z reportu a nikdy si je nesestavujte z kódu daňové kategorie
Jak se zapínají pravidla XRechnung a Peppol?
Vestavěný validátor vybírá pravidla profilů z identifikátoru specifikace (BT-24). Identifikátor obsahující xrechnung spustí CIUS pravidla XRechnung 3.0.2 na CII nebo UBL; rozšiřující identifikátor urn:xeinkauf.de:kosit:extension:xrechnung_3.0 navíc zapne pravidla BR-DEX. Kontroly částek podřízených řádků faktury a DPH (BR-DEX-02, -03 a -09 až -14) běží jen na UBL; na CII hlásí BR-DEX-15, že podřízené řádky faktury nejsou k dispozici. Identifikátor obsahující peppol spustí pravidla Peppol BIS Billing 3.0.20, která Peppol definuje jen pro UBL. Dokument CII hlásící Peppol customizaci dostane informativní poznámku BuiltIn.Coverage.Profile, že se ty pravidla nekontrolovaly, místo tichého prošla. Pro jiné CIUS profily použijte oficiální artefakty
Jeden UBL detail stojí za znalost: BR-06 a BR-07 přijímají jako jméno prodávajícího nebo kupujícího jen PartyLegalEntity/RegistrationName. Fallback na PartyName/Name vypadá shovívavě, ale skrývá skutečné selhání BR-06, a proto je kontrola přísná
Kdy stále potřebujete oficiální Schematron?
Oficiální artefakty, Schematron EN 16931 přes Saxon nebo KoSIT validátor pro XRechnung použijte vždy, když výsledek nese právní nebo smluvní váhu: odmítání faktury od dodavatele, certifikace vlastního generátoru nebo urovnávání sporu. Vestavěný report nikdy nenastaví fullConformance na true. Tenhle příznak vyžaduje ověřený pravidlový balíček, prošlé obě fáze, obě fáze prováděné enginem, který vykonává pravidla balíčku samotného, a nic zbývajícího v notCovered
ValidateInvoiceBusinessRulesWithPackage jede touto cestou. Pravidlový balíček je adresář s manifestem rulepackage.xml, který zapisuje CreateInvoiceRulePackage, jmenuje standard, verzi, schema a Schematron stylesheet a zaznamenává SHA-256 každého souboru. PDFlibPas kontroluje každý digest při načtení balíčku a znovu těsně před tím, než soubor čte engine, čímž zúží okno pro prohození souboru po ověření. Cesty nesmí opustit adresář balíčku a nativní XSD loader odmítá importy, které manifest neuvádí. Při nakonfigurovaném xmllint čte importované schema přímo xmllint, takže je chrání jen kontrola při načítání
const
// SHA-256 rulepackage.xml, který jste zrevidovali a schválili
ApprovedManifestSHA256 = '<64 hex digits from your release review>';
var
Report, Pkg: TJSONObject;
begin
// PDF: TPDFlib s už načtenou fakturou, jako v prvním příkladu
Report := TJSONObject.ParseJSONValue(
PDF.ValidateInvoiceBusinessRulesWithPackage('',
'C:\Rules\EN16931-CII-1.3.16', ApprovedManifestSHA256,
'', // prázdné: nativní fáze XSD
'C:\Java\bin\java.exe', 'C:\Saxon\saxon-he.jar',
0)) as TJSONObject; // 0: 30000 ms na nástroj
try
if not (Report.FindValue('package') is TJSONObject) then
raise Exception.Create('No rule package in the report');
Pkg := Report.GetValue<TJSONObject>('package');
if not (Pkg.GetValue<Boolean>('pinned') and Pkg.GetValue<Boolean>('verified')) then
raise Exception.Create('Rule package failed its integrity check');
if Report.GetValue<Boolean>('fullConformance') then
Writeln('Conforms to the complete rule package');
finally
Report.Free;
end;
end;
Připnutý digest není volitelná ozdoba. Digesty souborů dokazují, že soubory odpovídají manifestu, ale nic uvnitř balíčku nedokazuje, že manifest sám je pravý: útočník, který umí přepsat pravidla, umí přepsat spolu s nimi i manifest. Tu mezeru uzavírá jen digest připnutý volajícím, v kódu nebo v podepsané konfiguraci, a report drží pinned a verified odděleně, abyste poznali, kterou záruku skutečně máte
Když předáte prázdné JavaExecutable, schema fáze stále validuje nativně proti XSD balíčku, od v3.672.0 včetně facets a identity constraints, ale obchodní pravidla se vrátí k vestavěné podmnožině a fullConformance zůstává false. Schema konstrukce, které nativní loader nepodporuje, jako substitution groups, způsobí selhání načtení s XSD.Unsupported místo přeskočení
Kam patří veraPDF a Mustang
Žádná z těchhle funkcí se nepodívá na PDF kontejner. Soulad PDF/A-3 podle ISO 19005-3, XMP extension schema Factur-X a vazba připojeného souboru jsou samostatné otázky a veraPDF a Mustang zůstávají referenčními nástroji, jak popisuje validace e-faktur pomocí veraPDF a Mustang. Produkční strana, od připojení XML po deklaraci profilu, je pokryta v stavbě hybridních faktur Factur-X a ZUGFeRD v Delphi a extension schematech PDF/A-3 pro metadata Factur-X
Rozumné rozdělení provádí vestavěnou kontrolu in-process na každé příchozí faktuře, kde stojí milisekundy: UBL faktura zhruba 1 MB s base64 přílohami se validuje za 15 ms, se XSD UBL 2.1 za 79 ms. Běh zabaleného Saxonu patří do CI a do cesty odmítnutí a kontejner pokrývá veraPDF. Ať už je vestavěný výsledek jakýkoliv, popisuje pokrytou podmnožinu, ne standard — přesně na tohle je tu coverage pole, aby vás na to připomínalo
Funkce validace faktur, katalog pravidel a nástroje pravidlových balíčků dodává Delphi PDF Library pro Delphi a C++Builder, spolu s API pro Factur-X, PDF/A-3 a vložené soubory použitými výše