Techninis straipsnis

ZUGFeRD ir Factur-X elektroninės sąskaitos faktūros Delphi aplinkoje su HotPDF

„HotPDF“ generuoja, aptinka ir patikrina ZUGFeRD bei Factur-X elektronines sąskaitas faktūras Delphi ir C++Builder aplinkose. „AddFacturXAssociatedFile“ įterpia sąskaitos XML pagal EN 16931 į PDF/A-3 konteinerį, „DetectFacturXInvoice“ atpažįsta gaunamą hibridinę sąskaitą faktūrą, o „HPDFValidateEInvoice“ paleidžia oficialias EN 16931 „Schematron“ verslo taisykles prieš įterptąjį XML. Šiame straipsnyje apžvelgiami visi trys žingsniai ir atvirai kalbama apie tai, kur patikros variklis sustoja

Šį funkcijų rinkinį paskatino teisiniai, o ne techniniai reikalavimai. Vokietija ir Prancūzija palaipsniui įveda privalomą struktūrizuotą el. sąskaitų faktūrų išrašymą vietinėms B2B operacijoms, ir abu įsipareigojimai susilieja į tą patį hibridinį modelį: PDF sąskaita faktūra, kurią jau dabar kuria jūsų ERP programa, savo viduje privalo turėti mašininiu būdu nuskaitomą XML kopiją. Delphi apskaitos arba ERP programoms, kurios šiandien kuria tik paprastas PDF sąskaitas faktūras, artėja terminas pradėti kurti „Factur-X“ arba ZUGFeRD, o skirtumas tarp šių dviejų pavadinimų yra mažesnis, nei teigia rinkodara — „Factur-X“ ir ZUGFeRD yra tas pats prancūzų-vokiečių standartas, išleistas po dviem etiketėmis

Kas daro PDF failą galiojančia Factur-X sąskaita faktūra?

Galiojanti „Factur-X“ sąskaita faktūra yra PDF/A-3 failas (ISO 19005-3), kuriame kaip susijęs failas įterptas lygiai vienas struktūrizuotas sąskaitos XML, susietas iš dokumento katalogo ir deklaruotas XMP metaduomenyse. Turi sutapti trys sluoksniai. Pirma, konteineris turi atitikti PDF/A-3 archyvavimo profilį, kuris — kitaip nei PDF/A-1 ir PDF/A-2 — leidžia bet kokio tipo įterptus failus. Antra, sąskaitos XML, pavadintas „factur-x.xml“ standartiniams profiliams arba „xrechnung.xml“ vokiškam „XRechnung“ variantui, turi būti užregistruotas katalogo „/AF“ masyve ir „/Names /EmbeddedFiles“ medyje su „AFRelationship“ įrašu, kuris nurodo, kas yra priedas: „Alternative“ reiškia, kad XML yra lygiavertis matomos sąskaitos faktūros vaizdas, ko reikalauja dauguma profilių. Trečia, „Factur-X“ XMP išplėtimo schema turi nurodyti įterpto failo pavadinimą, jo atitikties lygį ir dokumento tipą, kad gaunanti sistema galėtų klasifikuoti sąskaitą faktūrą išvis neanalizuodama XML

Kodėl ZUGFeRD 2.5 nereikia naujo konteinerio

ZUGFeRD 2.5 nieko nekeičia PDF konteinerio lygiu: gairių URN, XMP schema ir versijos atpažinimo raktas (token) yra identiški ZUGFeRD 2.3 / Factur-X 1.0. Tai stebina daugelį programuotojų, nes pavyzdžiui prancūzų-vokiečių oficialiose ataskaitose buvo kalbama apie ZUGFeRD 2.5. Tačiau „Factur-X 1.09“ specifikacija nurodo, kad versijos numeris PDF/A išplėtimo schemos URI nėra susijęs su XML duomenų specifikacijos versijos numeriu ir kad „Factur-X“ sąskaitų faktūrų egzempliorių versija išlieka 1.0 — tiek XMP „fx:Version“ lauke, tiek pačio „factur-x.xml“ elemente BT-24. Oficialus „ZUGFeRD 2.5“ pavyzdžių rinkinys tai patvirtina: kiekvienas pavyzdys įterpia „factur-x.xml“ su gairės URN „urn:factur-x.eu:1p0“ ir „fx:Version“ reikšme „1.0“. Tai, ką ZUGFeRD 2.5 iš tikrųjų prideda, gyvena XML duomenų modelyje — nauji elementai, tokie kaip sąskaitos faktūros sub-eilutės ir partijų identifikatoriai — kas yra jūsų programos kuriamas verslo sluoksnis, o ne PDF bibliotekos valdomas konteinerio sluoksnis

„HotPDF“ tiesiogiai atspindi šią išvadą. „AddFacturXAssociatedFile“ priima parametrą „Version“ ir normalizuoja jį per „HPDFFacturXNormalizeVersion“ prieš įrašydama XMP, zodžiu, iškvietėjas, perduodamas „2p5“ pseudonimą (alias), vis tiek įrašo specifikaciją atitinkantį atpažinimo raktą, užuot sugalvojęs „2p5“ XMP versiją, kurios nepriimtų joks tikrinimo įrankis. Aptikimo funkcijos skaitant laiko „2p5“ lygiaverčiu variantu, todėl veiksmai išlieka simetriški. Jei kokia nors biblioteka ar įrankis teigia, kad rašo specialų ZUGFeRD 2.5 konteinerį, pati specifikacija sako, kad tokio dalyko nėra

Sąskaitos XML įterpimas iš Delphi

„THotPDF.AddFacturXAssociatedFile“ vienu iškvietimu paverčia įprastą PDF/A-3 generavimo procesą į „Factur-X“ sąskaitą faktūrą: ši funkcija įterpia XML baitus kaip susijusį failą, užregistruoja „factur-x.xml“ katalogo „/AF“ masyve ir „/Names /EmbeddedFiles“ medyje bei įrašo atitinkamus „Factur-X“ XMP išplėtimo metaduomenis. Žmogui skaitomą sąskaitos faktūros puslapį nupiešiate naudodami įprastą „HotPDF“ drobės (canvas) API, o visas darbas su konteineriu atliekamas viena eilute tarp „BeginDoc“ ir „EndDoc“

var
  Pdf: THotPDF;
  InvoiceXML, UBLXML: TBytes;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'invoice-2026-0042.pdf';
    Pdf.PDFACompliance := '3B';            // PDF/A-3b container
    Pdf.BeginDoc;
    // embed the EN 16931 CII invoice XML as factur-x.xml
    InvoiceXML := BuildInvoiceXML;         // produced by your business layer
    Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
    // optional: attach a UBL view as a supplementary representation
    UBLXML := BuildUBLXML;
    Pdf.AddUBLSupplementaryFile(UBLXML);   // factur-xubl.xml, Alternative
    // ... draw the visible invoice page here ...
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Numatytieji nustatymai atitinka specifikaciją: failo pavadinimas „factur-x.xml“, santykis „Alternative“, versija „1.0“. „AddUBLSupplementaryFile“ realizuoja „Factur-X 1.09“ specifikacijos 6.4 skyrių, kuris leidžia to paties sąskaitos dokumento UBL variantui būti pridėtam kaip antram priedui pavadinimu „factur-xubl.xml“ su „application/xml“ MIME tipu ir „Alternative“ santykiu. Tvarka čia yra svarbi, ir „HotPDF“ užtikrina specifikacijoje numatytą ribojimą: UBL failas yra tik papildomas vaizdas, o ne pagrindinė sąskaita faktūra, todėl jis turi būti pridėtas tik po to, kai „AddFacturXAssociatedFile“ užregistravo CII pagrindinį dokumentą. Atminkite, kad nupieštas puslapis ir įterptas XML privalo rodyti tą pačią sąskaitą faktūrą — PDF, rodantis vieną sumą puslapyje ir koduojantis kitą XML faile, yra būtent tas klaidų scenarijus, kurio siekiama išvengti hibridinėmis sąskaitomis. Šios darbo eigos PDF/A-3 dalis, įskaitant „OutputIntent“ ir šriftų įterpimą, išsamiau aprašyta straipsnyje apie PDF/A, PDF/X ir PDF/UA tikrinimą Delphi aplinkoje

Kaip aptikti Factur-X sąskaitą faktūrą gaunamame PDF faile?

„THotPDF.DetectFacturXInvoice“ atlieka gavimo pusės darbą: įkelkite bet kurį PDF failą ir vienu iškvietimu sužinokite, ar tai hibridinė sąskaita faktūra, kokį profilį ji deklaruoja ir kokius priedus turi. Funkcija grąžina „THPDFFacturXInvoiceInfo“ įrašą su įterpto XML failo pavadinimu, XMP deklaruotu dokumento pavadinimu bei tipu, versijos raktu, atitikties lygiu, gairės URN ir „AFRelationship“ reikšme. Dar du laukai nurodo papildomus vaizdus: „UBLFileName“ užpildomas, kai yra „factur-xubl.xml“ priedas, o „EDIFACTFileName“ — kai pridedamas EDIFACT vaizdas. Nė vienas iš jų nedaro įtakos aptikimo rezultatui, nes papildomas vaizdas negali egzistuoti be pirminės CII sąskaitos faktūros

var
  Reader: THotPDF;
  Info: THPDFFacturXInvoiceInfo;
begin
  Reader := THotPDF.Create(nil);
  try
    if Reader.LoadFromFile('incoming-invoice.pdf') <= 0 then
      raise Exception.Create('No pages loaded.');
    if Reader.DetectFacturXInvoice(Info) then
    begin
      Writeln('Invoice XML:    ', string(Info.FileName));
      Writeln('Conformance:    ', string(Info.ConformanceLevel));
      Writeln('Guideline URN:  ', string(Info.GuidelineID));
      Writeln('Relationship:   ', string(Info.AFRelationship));
      if Info.UBLFileName <> '' then
        Writeln('UBL view:       ', string(Info.UBLFileName));
    end
    else
      Writeln('Not a Factur-X / ZUGFeRD hybrid invoice.');
  finally
    Reader.Free;
  end;
end;

Aptikimas nuskaito tuos pačius signalus, kuriuos skaito standartinis tikrinimo įrankis — XMP išplėtimo schemą ir susijusių failų registrą, o ne tiesiog spėja pagal failų pavadinimus. Tas pats įkelto dokumento objektas taip pat leidžia pasiekti XMP ir „Info“ žodyno metaduomenis peržiūrai ar taisymui, kas aprašyta straipsnyje apie metaduomenų redagavimą įkeltuose PDF dokumentuose

Verslo taisyklės ar schema: ką reiškia EN 16931 tikrinimas?

„HPDFValidateEInvoice“ — atskira funkcija „HPDFEInvoiceValidator“ modulyje — paeiliui paleidžia abu tikrinimo sluoksnius: pirmiausia konteinerio struktūros patikrą per „ValidateFacturXInvoice“, o tada EN 16931 „Schematron“ verslo taisykles įterptam XML. „HotPDF“ pateikia penkis oficialius „Factur-X 1.09“ taisyklių failus („MINIMUM“, „BASIC WL“, „BASIC“, „EN 16931“ ir „EXTENDED“) aplanke „Lib/resources/Schematron/“, o „HPDFSchematronFileForProfile“ parenka failą pagal aptiktą atitikties lygį, todėl EN 16931 sąskaita faktūra yra automatiškai tikrinama pagal „Factur-X_1.09_EN16931.sch“. Funkcija grąžina „True“ tik tada, kai konteineris yra teisingas ir nė viena verslo taisyklė nebuvo pažeista

uses HPDFEInvoiceValidator;

var
  Reader: THotPDF;
  Report: THPDFEInvoiceValidationReport;
  I: Integer;
begin
  Reader := THotPDF.Create(nil);
  try
    Reader.LoadFromFile('incoming-invoice.pdf');
    if HPDFValidateEInvoice(Reader, SchematronDir, Report) then
      Writeln('Container and business rules: OK')
    else
      Writeln('Validation found issues.');
    if Report.BusinessRulesEvaluated then
    begin
      Writeln(Report.BusinessSummary.Evaluated, ' rules evaluated, ',
        Report.BusinessSummary.Skipped, ' skipped (XPath 2.0)');
      for I := 0 to High(Report.BusinessRules) do
        if (not Report.BusinessRules[I].Skipped) and
           (Report.BusinessRules[I].Severity = stsError) then
          Writeln(string(Report.BusinessRules[I].RuleID), ': ',
            string(Report.BusinessRules[I].Message));
    end;
  finally
    Reader.Free;
  end;
end;

Kiekvienas „BusinessRules“ įrašas perneša taisyklės identifikatorių, išgautą iš teiginio teksto — „BR-45“, „BR-CO-17“ ir kitus pagal EN 16931 sistemą — taip pat taisyklės konteksto XPath kelią, testo išraišką ir svarbumo lygį, atitinkantį „Schematron“ požymį. Tai daro ataskaitą naudingą veiksmams: vietoj paprasto „praėjo/nepraėjo“ rezultato gaunate konkretų verslo terminą, kuris yra prieštaringas, o to reikia palaikymo komandai, kai klientas klausia, kodėl sąskaita faktūra buvo atmesta. Komandos, kurios jau generuoja struktūrizuotas atitikties ataskaitas archyviniams PDF failams, gali įtraukti šiuos rezultatus į tą patį procesą, aprašytą straipsnyje apie PDF išankstinio tikrinimo ataskaitų automatizavimą

Kur patikros variklis sustoja, atvirai

„Schematron“ variklis yra pagrįstas MSXML, kuris atlieka XPath 1.0 skaičiavimus. EN 16931 taisyklių failas deklaruoja „queryBinding="xslt2"“ ir turi 424 teiginius, kurių dauguma naudoja tik XPath 1.0 funkcijas, tokias kaip „string-length“ ir „substring-after“ — jos vykdomos tiesiogiai. Mažuma taisyklių remiasi XPath 2.0 konstrukcijomis, tokiomis kaip „xs:decimal“ tipų keitimas arba „exists()“; „HotPDF“ iš anksto nuskaito kiekvieno testo išraišką, ieškodama žinomų XPath 2.0 raktų, ir pažymi šias taisykles kaip „Skipped“ (praleista) su „info“ svarbumu, užuot bandžiusi jas vykdyti ir sukėlusi klaidą. Suvestinės įrašas pateikia įvertintų ir praleistų taisyklių skaičių atskirai, zodžiu, jūsų žurnale visada tiksliai nurodoma, kiek taisyklių buvo pritaikyta. Delphi 7 aplinkoje, kur MSXML sąsajos nepasiekiamos, modulis sukompiliuoja laikiną užpildą (stub), kuris praneša, kad variklis neprieinamas, nepažeisdamas paties konteinerio vertinimo; pilnam varikliui reikalinga Delphi XE2 ar vėlesnė versija

Antroji riba yra dar svarbesnė. Sėkmingas „HPDFValidateEInvoice“ rezultatas reiškia, kad konteinerio struktūra yra teisinga ir nė viena įvertinta verslo taisyklė nebuvo pažeista — tai nėra mokesčių atitikties patvirtinimas. Nacionalinės išplėtimo taisyklės, specifiniai gavėjo reikalavimai ir teisiniai įsipareigojimai dėl sąskaitų faktūrų turinio yra aukščiau EN 16931 standarto ir už bet kokios bibliotekos ribų. Laikykite tikrinimo įrankį vartais, kurie neleidžia struktūriškai sugadintoms sąskaitoms išeiti iš jūsų programos ir pažymi neteisingas gaunamas sąskaitas, o kai sertifikavimas reikalauja — naudokite pilną XPath 2.0 tikrinimo įrankį ir leiskite mokesčių konsultantams atsakyti į teisinius klausimus. Įterpimo, aptikimo ir tikrinimo API yra dalis „HotPDF Component“ komponente, skirtame Delphi ir C++Builder, su pilnu el. sąskaitų faktūrų pavyzdžiu, apimančiu visus septynius „ZUGFeRD“ ir „Factur-X“ profilius