Teknisk artikel

ZUGFeRD- og Factur-X-e-fakturaer i Delphi med HotPDF

HotPDF genererer, detekterer og validerer elektroniske ZUGFeRD- og Factur-X-fakturaer fra Delphi og C++Builder. AddFacturXAssociatedFile indlejrer EN 16931-faktura-XML'en i en PDF/A-3-container, DetectFacturXInvoice genkender en indkommende hybridfaktura, og HPDFValidateEInvoice kører de officielle EN 16931 Schematron-forretningsregler mod den indlejrede XML. Denne artikel gennemgår alle tre trin og er ærlig om, hvor valideringsmotoren stopper

Presset bag dette funktionssæt er lovgivningsmæssigt, ikke teknisk. Tyskland og Frankrig indfaser obligatorisk struktureret e-fakturering for indenlandske B2B-transaktioner, og begge mandater konvergerer om den samme hybridmodel: Den PDF-faktura, dit ERP allerede producerer, skal indeholde en maskinlæsbar XML-tvilling. En Delphi-regnskabs- eller ERP-applikation, der udsender almindelige PDF-fakturaer i dag, har en hård deadline for at begynde at udsende Factur-X eller ZUGFeRD, og forskellen mellem de to navne er mindre, end markedsføringen antyder — Factur-X og ZUGFeRD er den samme fransk-tyske standard udgivet under to mærkater

Hvad gør en PDF til en gyldig Factur-X-faktura?

En gyldig Factur-X-faktura er en PDF/A-3-fil (ISO 19005-3), der indlejrer præcis én struktureret faktura-XML som en tilknyttet fil, linker til den fra dokumentkataloget og erklærer den i XMP-udvidelsesmetadata. Tre lag skal stemme overens. For det første skal containeren overholde PDF/A-3, arkiveringsprofilen, som — i modsætning til PDF/A-1 og PDF/A-2 — tillader indlejrede filer af enhver type. For det andet skal faktura-XML'en, navngivet factur-x.xml for standardprofiler eller xrechnung.xml for den tyske XRechnung-variant, være registreret i katalogets /AF-array og i /Names /EmbeddedFiles-træet med en AFRelationship-post, der angiver, hvad vedhæftningen er: Alternative betyder, at XML'en er en tilsvarende repræsentation af den synlige faktura, hvilket er den semantik, de fleste profiler kræver. For det tredje skal Factur-X XMP-udvidelsesskemaet navngive den indlejrede fil, dens overensstemmelsesniveau og dokumenttypen, så et modtagende system kan klassificere fakturaen uden overhovedet at analysere XML'en

Selve XML-indholdet følger den semantiske EN 16931-model, den europæiske norm, der definerer kernefakturaen: Køber, sælger, linjeposter, momsspecifikation, beløb til betaling, hver med et forretningsbegrebsnummer (BT-1 for fakturanummer, BT-5 for valuta osv.). EN 16931-validering sker derfor på to forskellige lag. XML-skemavalidering kontrollerer, at dokumentet is velformet CII med de rigtige elementnavne. Schematron-forretningsreglerne kontrollerer, at indholdet er sammenhængende — at BR-CO-15 gælder, og at fakturatotalen faktisk svarer til summen af linjenettobeløb plus moms, og at obligatoriske begreber er til stede for den erklærede profil. En fil kan bestå XML-skemavalidering og stadig være en ugyldig faktura, hvilket er grunden til, at forretningsregellaget eksisterer

Hvorfor ZUGFeRD 2.5 ikke har brug for en ny container

ZUGFeRD 2.5 ændrer intet på PDF-containerniveau: Retningslinjernes URN'er, XMP-skemaet og versionstokenet er identiske med ZUGFeRD 2.3 / Factur-X 1.0. Dette overrasker de fleste implementører, så det er værd at citere kilden. Factur-X 1.09-specifikationen slår fast, at versionsnummeret i URI'en for PDF/A-udvidelsesskemaet ikke er relateret til versionsnummeret på XML-dataspecifikationen, og at versioneringen af Factur-X-faktura-instanser forbliver 1.0 — både i XMP fx:Version og i BT-24 i factur-x.xml. Det officielle ZUGFeRD 2.5-eksempelsamling bekræfter det: Hvert eksempel indlejrer factur-x.xml under en urn:factur-x.eu:1p0-retningslinje-URN med en fx:Version1.0. Hvad 2.5 rent faktisk tilføjer, lever inde i XML-datamodellen — nye elementer som f.eks. underfakturalinjer og batch-identifikatorer — hvilket er det forretningslag, din applikation genererer, ikke det containerlag, som PDF-biblioteket administrerer

HotPDF koder denne konstatering direkte. AddFacturXAssociatedFile accepterer et Version-argument og normaliserer det via HPDFFacturXNormalizeVersion før skrivning of XMP, so en kalder, der sender 2p5-aliaset, stadig udsender det specifikationskonforme token i stedet for at opfinde en 2p5 XMP-version, som ingen validator ville acceptere. Detektionshjælperne behandler 2p5 som ækvivalent ved læsning, så dataene forbliver symmetriske. Hvis et bibliotek eller værktøj fortæller dig, at det skriver en speciel ZUGFeRD 2.5-container, siger specifikationen selv, at der ikke findes noget sådant

Indlejring af faktura-XML fra Delphi

THotPDF.AddFacturXAssociatedFile forvandler en almindelig PDF/A-3-genereringskørsel til en Factur-X-faktura med et enkelt kald: Den indlejrer XML-bytes som en tilknyttet fil, registrerer factur-x.xml i katalogets /AF-array og /Names /EmbeddedFiles-træet og udsender de matchende Factur-X XMP-udvidelsesmetadata. Du tegner den menneskeligt læsbare fakturaside med the normale HotPDF-canvas-API; containerarbejdet er en enkelt linje mellem BeginDoc og 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;

Standarderne følger specifikationen: Filnavn factur-x.xml, relation Alternative, version 1.0. AddUBLSupplementaryFile implementerer afsnit 6.4 i Factur-X 1.09-specifikationen, som tillader en UBL-repræsentation af den samme faktura at køre med som en anden vedhæftet fil navngivet factur-xubl.xml med MIME-typen application/xml og relationen Alternative. Rækkefølgen har betydning her, og HotPDF håndhæver den begrænsning, som specifikationen indebærer: UBL-filen er en supplerende visning, aldrig den primære faktura, så den skal tilføjes, efter at AddFacturXAssociatedFile har registreret CII-masteren. Bemærk, at den tegnede side og den indlejrede XML skal angive den samme faktura — en PDF, der viser én total og koder en anden, er præcis den fejltilstand, som hybrid fakturering er designet til at forhindre. PDF/A-3-siden af dette workflow, inklusive OutputIntent og skrifttypeindlejring, er dækket mere i dybden i den tilhørende artikel om PDF/A-, PDF/X- og PDF/UA-validering i Delphi

Hvordan detekterer du en Factur-X-faktura i en indkommende PDF?

THotPDF.DetectFacturXInvoice besvarer den modtagende side af mandatet: Indlæs en hvilken som helst PDF, og få med et enkelt kald at vide, om det er en hybridfaktura, hvilken profil den erklærer, og hvilke vedhæftede filer den bærer. Funktionen returnerer en THPDFFacturXInvoiceInfo-post med det indlejrede XML-filnavn, det XMP-erklærede dokumentfilnavn og -type, versionstokenet, overensstemmelsesniveauet, retningslinje-URN'en og AFRelationship-værdien. Yderligere to felter rapporterer supplerende visninger: UBLFileName udfyldes, når en factur-xubl.xml-vedhæftning er til stede, og EDIFACTFileName, når en EDIFACT-visning kører med — ingen af dem påvirker detektionsresultatet, da en supplerende repræsentation ikke kan eksistere uden den primære CII-faktura

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;

Detektering læser de samme signaler, som en overensstemmende validator læser — XMP-udvidelsesskemaet og registeret over tilknyttede filer — i stedet for blot at gætte ud fra filnavne. Det samme indlæste dokumentobjekt eksponerer også XMP- og Info-ordbogsmetadata for inspektion eller korrektion, et workflow beskrevet i artiklen om redigering af metadata i indlæste PDF-dokumenter

Forretningsregler eller skema: Hvad betyder EN 16931-validering?

HPDFValidateEInvoice, en selvstændig funktion i HPDFEInvoiceValidator-enheden, kører begge valideringslag i rækkefølge: Først kontrollen af containerstruktur via ValidateFacturXInvoice, derefter EN 16931 Schematron-forretningsreglerne mod den udtrukne faktura-XML. HotPDF leverer de fem officielle Factur-X 1.09-regelfiler — MINIMUM, BASIC WL, BASIC, EN 16931 og EXTENDED — under Lib/resources/Schematron/, og HPDFSchematronFileForProfile vælger den fil, der matcher det detekterede overensstemmelsesniveau, så en EN 16931-faktura automatisk kontrolleres mod Factur-X_1.09_EN16931.sch. Funktionen returnerer kun True, når containeren er gyldig, og ingen forretningsregel blev udløst

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;

Hver post i BusinessRules bærer regelidentifikatoren parset fra påstandsteksten — BR-45, BR-CO-17 og resten af EN 16931-navngivningsskemaet — plus regelkontekstens XPath, testudtrykket og en alvorlighedsgrad, der spejler Schematrons flag-attribut. Det gør rapporten handlingsorienteret: I stedet for et rent bestået/ikke-bestået får du det præcise forretningsbegreb, der er inkonsistent, hvilket er det, dit supportteam har brug for, når en kunde spørger, hvorfor en faktura blev afvist. Teams, der i forvejen genererer strukturerede overensstemmelsesrapporter for arkiv-PDF'er, kan indpasse disse resultater i den samme pipeline, der er beskrevet i artiklen om automatisering af PDF-preflightrapporter

Hvor valideringsmotoren stopper, ærligt

Schematron-motoren er bygget på MSXML, som evaluerer XPath 1.0. EN 16931-regelfilen erklærer queryBinding="xslt2" og bærer 424 påstande, hvoraf de fleste kun bruger XPath 1.0-funktioner som f.eks. string-length og substring-after — disse kører indfødt. Et mindretal er afhængige af XPath 2.0-konstruktioner som f.eks. xs:decimal-casts eller exists(); HotPDF scanner på forhånd hvert testudtryk for kendte XPath 2.0-tokens og markerer disse regler som Skipped med alvorlighedsgraden info frem for at forsøge en evaluering, der lydløst ville slå fejl. Opsummeringsposten rapporterer antallet af evaluerede og oversprungne regler separat, så din log altid angiver præcis, hvor meget af regelsættet der blev eksekveret. På Delphi 7, hvor MSXML-bindinger ikke er tilgængelige, enheden kompilerer en stub, der rapporterer motoren som utilgængelig uden at forstyrre containerresultatet; den fulde motor kræver Delphi XE2 eller nyere

Den anden grænse betyder endnu mere. Et grønt resultat fra HPDFValidateEInvoice betyder, at containerstrukturen overholder reglerne, og at ingen af de evaluerede forretningsregler blev udløst — det er ikke en godkendelse af skattemæssig overholdelse. Nationale udvidelsesregler, modtagerspecifikke krav og de juridiske forpligtelser omkring fakturaindhold ligger over EN 16931-kernen og uden for ethvert bibliotek. Behandl validatoren som den port, der houdt strukturelt defekte fakturaer ude af din udbakke og flager inkonsistente fakturaer i din indbakke, kør en fuld XPath 2.0-validator i din udgivelsespipeline, når certificering kræver det, og lad dine skatterådgivere om compliance-spørgsmålet. De API'er til indlejring, detektering og validering, der vises her, er en del af standarden HotPDF Component til Delphi og C++Builder, med et komplet E-Invoice-eksempel, der dækker alle syv ZUGFeRD- og Factur-X-profiler