Articol tehnic

Facturi electronice ZUGFeRD și Factur-X în Delphi cu HotPDF

HotPDF generează, detectează și validează facturi electronice ZUGFeRD și Factur-X din Delphi și C++Builder. AddFacturXAssociatedFile încorporează fișierul XML al facturii EN 16931 într-un container PDF/A-3, DetectFacturXInvoice recunoaște o factură hibridă de intrare, iar HPDFValidateEInvoice rulează regulile de afaceri oficiale Schematron EN 16931 pe baza XML-ului încorporat. Acest articol parcurge toți cei trei pași și expune clar limitele motorului de validare

Presiunea din spatele acestui set de caracteristici este de natură reglementară, nu tehnică. Germania și Franța introduc treptat facturarea electronică structurată obligatorie pentru tranzacțiile interne B2B, iar ambele mandate converg pe același model hibrid: factura PDF pe care ERP-ul dvs. o produce deja trebuie să conțină o copie XML lizibilă de către computer în interiorul ei. O aplicație de contabilitate sau ERP dezvoltată în Delphi care emite facturi simple PDF astăzi are un termen limită strict pentru a începe să emită Factur-X sau ZUGFeRD, iar diferența dintre cele două denumiri este mai mică decât sugerează marketingul — Factur-X și ZUGFeRD reprezintă același standard franco-german publicat sub două denumiri

Ce face ca un PDF să fie o factură Factur-X validă?

O factură Factur-X validă este un fișier PDF/A-3 (ISO 19005-3) care încorporează exact un fișier XML de factură structurat ca fișier asociat, îl înregistrează din catalogul de documente și îl declară în metadatele de extensie XMP. Trei straturi trebuie să fie în concordanță. În primul rând, containerul trebuie să fie conform cu PDF/A-3, profilul de arhivare care — spre deosebire de PDF/A-1 și PDF/A-2 — permite fișiere încorporate de orice tip. În al doilea rând, XML-ul facturii, numit factur-x.xml pentru profilele standard sau xrechnung.xml pentru varianta germană XRechnung, trebuie înregistrat în tabloul catalogului /AF și în arborele /Names /EmbeddedFiles, cu o intrare AFRelationship care precizează ce reprezintă atașamentul: Alternative înseamnă că XML-ul este o reprezentare echivalentă a facturii vizibile, care este semantica cerută de majoritatea profilelor. În al doilea rând, schema de extensie XMP Factur-X trebuie să numească fișierul încorporat, nivelul său de conformitate și tipul de document, astfel încât sistemul receptor să poată clasifica factura fără a analiza deloc fișierul XML

Sarcina utilă XML în sine urmează modelul semantic EN 16931, Norma Europeană care definește factura de bază: cumpărătorul, vânzătorul, articolele din factură, defalcarea taxelor, suma de plată, fiecare având un număr de termen de afaceri (BT-1 pentru numărul facturii, BT-5 pentru monedă și așa mai departe). Validarea EN 16931 are loc pe două straturi distincte. Validarea schemei XML verifică dacă documentul este un CII corect format cu numele corect al elementelor. Regulile de afaceri Schematron verifică dacă conținutul este coerent — că BR-CO-15 se respectă și totalul facturii este egal cu suma valorilor nete ale liniilor plus TVA, iar termenii obligatorii sunt prezenți pentru profilul declarat. Un fișier poate trece de validarea schemei și totuși să fie o factură nevalidă, motiv pentru care există stratul regulilor de afaceri

De ce ZUGFeRD 2.5 nu are nevoie de un container nou

ZUGFeRD 2.5 nu modifică nimic la nivelul containerului PDF: URN-urile directoare, schema XMP și tokenul de versiune sunt identice cu ZUGFeRD 2.3 / Factur-X 1.0. Acest lucru îi surprinde pe majoritatea implementatorilor, așa că merită citată sursa. Specificația Factur-X 1.09 precizează că numărul versiunii din URI-ul schemei de extensie PDF/A nu are legătură cu numărul versiunii specificației de date XML și că versiunea instanțelor de factură Factur-X rămâne 1.0 — atât în XMP fx:Version, cât și în BT-24 din factur-x.xml. Corpusul oficial de mostre ZUGFeRD 2.5 confirmă acest lucru: fiecare mostră încorporează factur-x.xml sub un URN director urn:factur-x.eu:1p0 cu fx:Version fiind 1.0. Ceea ce adaugă efectiv versiunea 2.5 se află în interiorul modelului de date XML — elemente noi precum linii de sub-factură și identificatori de lot — adică stratul de afaceri pe care îl generează aplicația dvs., nu stratul de container pe care îl gestionează biblioteca PDF

HotPDF codifică această constatare în mod direct. AddFacturXAssociatedFile acceptă un argument Version și îl normalizează prin HPDFFacturXNormalizeVersion înainte de a scrie XMP, astfel încât un apelant care transmite aliasul 2p5 emite în continuare tokenul conform cu specificația în loc să inventeze o versiune XMP 2p5 pe care niciun validator nu ar accepta-o. Ajutoarele de detecție tratează 2p5 ca echivalent la citire, astfel încât fluxul bidirecțional rămâne simetric. Dacă o bibliotecă sau un instrument vă spune că scrie un container special ZUGFeRD 2.5, specificația în sine precizează că nu există așa ceva

Încorporarea fișierului XML al facturii din Delphi

THotPDF.AddFacturXAssociatedFile transformă o generare obișnuită PDF/A-3 într-o factură Factur-X cu un singur apel: încorporează octeții XML ca fișier asociat, înregistrează factur-x.xml în tabloul catalogului /AF și în arborele /Names /EmbeddedFiles și emite metadatele potrivite de extensie XMP Factur-X. Desenați pagina facturii lizibilă pentru oameni cu API-ul de canvas HotPDF; configurarea containerului este o singură linie de cod între BeginDoc și 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;
    // încorporează XML-ul facturii CII conform cu EN 16931 ca factur-x.xml
    InvoiceXML := BuildInvoiceXML;         // generat de stratul dvs. de afaceri
    Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
    // opțional: atașați o vizualizare UBL ca reprezentare suplimentară
    UBLXML := BuildUBLXML;
    Pdf.AddUBLSupplementaryFile(UBLXML);   // factur-xubl.xml, Alternative
    // ... desenați pagina vizibilă a facturii aici ...
    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Valorile implicite urmează specificația: numele fișierului factur-x.xml, relația Alternative, versiunea 1.0. AddUBLSupplementaryFile implementează secțiunea 6.4 a specificației Factur-X 1.09, care permite ca o reprezentare UBL a aceleiași facturi să fie adăugată ca al doilea atașament numit factur-xubl.xml cu tipul MIME application/xml și relația Alternative. Ordinea contează aici, iar HotPDF impune constrângerea pe care o implică specificația: fișierul UBL este o vizualizare suplimentară, niciodată factura principală, prin urmare trebuie adăugat după ce AddFacturXAssociatedFile a înregistrat documentul master CII. Rețineți că pagina desenată și XML-ul încorporat trebuie să declare aceeași factură — un PDF care afișează un total și codifică un altul este exact modul de eroare pe care facturarea hibridă este concepută să îl prevină. Partea de PDF/A-3 a acestui flux de lucru, inclusiv OutputIntent și încorporarea fonturilor, este acoperită în detaliu în articolul însoțitor despre validarea PDF/A, PDF/X și PDF/UA în Delphi

Cum se detectează o factură Factur-X într-un PDF primit?

THotPDF.DetectFacturXInvoice răspunde părții de primire a mandatului: încărcați orice PDF și aflați dintr-un singur apel dacă este o factură hibridă, ce profil declară și ce atașamente conține. Funcția returnează o înregistrare THPDFFacturXInvoiceInfo cu numele fișierului XML încorporat, numele și tipul fișierului de document declarat în XMP, tokenul de versiune, nivelul de conformitate, URN-ul ghid și valoarea AFRelationship. Încă două câmpuri raportează vizualizările suplimentare: UBLFileName este completat când este prezent atașamentul factur-xubl.xml, iar EDIFACTFileName când este prezentă o vizualizare EDIFACT — niciuna dintre acestea nu afectează rezultatul detecției, deoarece o reprezentare suplimentară nu poate exista fără factura CII principală

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('XML factură:    ', string(Info.FileName));
      Writeln('Conformitate:    ', string(Info.ConformanceLevel));
      Writeln('URN ghid:  ', string(Info.GuidelineID));
      Writeln('Relație:   ', string(Info.AFRelationship));
      if Info.UBLFileName <> '' then
        Writeln('Vizualizare UBL:       ', string(Info.UBLFileName));
    end
    else
      Writeln('Nu este o factură hibridă Factur-X / ZUGFeRD.');
  finally
    Reader.Free;
  end;
end;

Detecția citește aceleși semnale pe care le citește un validator conform — schema de extensie XMP și registrul de fișiere asociate — în loc să ghicească doar pe baza numelor de fișiere. Același obiect document încărcat expune, de asemenea, metadatele XMP și dicționarul Info pentru inspecție sau corectare, un flux de lucru descris în articolul despre editarea metadatelor în documente PDF încărcate

Reguli de afaceri sau schemă: ce înseamnă validarea EN 16931?

HPDFValidateEInvoice, o funcție autonomă din unitatea HPDFEInvoiceValidator, rulează ambele straturi de validare în secvență: mai întâi verificarea structurii containerului prin ValidateFacturXInvoice, apoi regulile de afaceri Schematron EN 16931 pe baza XML-ului de factură extras. HotPDF livrează cele cinci fișiere oficiale de reguli Factur-X 1.09 — MINIMUM, BASIC WL, BASIC, EN 16931 și EXTENDED — sub folderul Lib/resources/Schematron/, iar HPDFSchematronFileForProfile alege fișierul potrivit cu nivelul de conformitate detectat, astfel încât o factură EN 16931 este verificată automat în raport cu Factur-X_1.09_EN16931.sch. Funcția returnează True numai când containerul este valid și nicio regulă de afaceri nu a eșuat

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 și reguli de afaceri: OK')
    else
      Writeln('Validarea a găsit probleme.');
    if Report.BusinessRulesEvaluated then
    begin
      Writeln(Report.BusinessSummary.Evaluated, ' reguli evaluate, ',
        Report.BusinessSummary.Skipped, ' omiși (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;

Fiecare intrare în BusinessRules conține identificatorul regulii extras din textul aserțiunii — BR-45, BR-CO-17 și restul schemei de denumire EN 16931 — plus calea XPath a contextului regulii, expresia de test și o severitate care oglindește atributul Schematron flag. Acest lucru face ca raportul să fie utilizabil: în loc de un simplu succes/eșec, obțineți termenul exact de afaceri care este inconsistent, adică ceea ce are nevoie echipa dvs. de asistență atunci când un client întreabă de ce o factură a fost respinsă. Echipele care generează deja rapoarte de conformitate structurate pentru fișiere PDF de arhivare pot integra aceste rezultate în aceeași conductă descrisă în articolul despre automatizarea rapoartelor preflight PDF

Unde se oprește motorul de validare, în mod onest

Motorul Schematron este construit pe MSXML, care evaluează XPath 1.0. Fișierul de reguli EN 16931 declară queryBinding="xslt2" și conține 424 de aserțiuni, dintre care majoritatea utilizează numai funcții XPath 1.0 precum string-length și substring-after — acestea rulează nativ. O minoritate se bazează pe structuri XPath 2.0, cum ar fi conversiile xs:decimal sau exists(); HotPDF pre-scanează fiecare expresie de test pentru tokeni XPath 2.0 cunoscuți și marchează acele reguli ca Skipped cu nivel de severitate info, în loc să încerce o evaluare care ar da greș în mod silențios. Înregistrarea de rezumat raportează separat numărul de reguli evaluate și cel de reguli omise, astfel încât jurnalul dvs. declară întotdeauna cu exactitate cât de mult din setul de reguli a fost testat. Pe Delphi 7, unde legăturile MSXML nu sunt disponibile, unitatea compilează un ciot (stub) care raportează motorul ca fiind indisponibil, fără a perturba decizia privind containerul; motorul complet de validare necesită Delphi XE2 sau o versiune mai nouă

A doua limitare contează și mai mult. Un rezultat pe verde de la HPDFValidateEInvoice înseamnă că structura containerului este conformă și nicio regulă de afaceri evaluată nu a eșuat — nu este un aviz de conformitate fiscală. Regulile naționale de extindere, cerințele specifice destinatarului și obligațiile legale referitoare la conținutul facturii se află deasupra nucleului EN 16931 și în afara oricărei biblioteci. Tratați validatorul ca pe o barieră care ține facturile defecte din punct de vedere structural în afara casetei de trimitere (outbox) și semnalează facturile inconsistente în cea de primire (inbox), rulați un validator complet XPath 2.0 în fluxul de producție când practicarea o cere și lăsați consilierii fiscali să își asume responsabilitatea pentru conformitate. API-urile de încorporare, detecție și validare prezentate aici fac parte din versiunea standard a HotPDF Component pentru Delphi și C++Builder, cu un exemplu complet de factură electronică ce acoperă toate cele șapte profiluri ZUGFeRD și Factur-X