Technischer Artikel

ZUGFeRD und Factur-X E-Rechnungen in Delphi mit HotPDF

HotPDF erzeugt, erkennt und validiert elektronische ZUGFeRD- und Factur-X-Rechnungen in Delphi und C++Builder. AddFacturXAssociatedFile bettet die XML-Rechnungsdaten nach EN 16931 in einen PDF/A-3-Container ein, DetectFacturXInvoice erkennt eine eingehende hybride Rechnung und HPDFValidateEInvoice führt die offiziellen EN 16931-Schematron-Geschäftsregeln für die eingebettete XML-Datei aus. Dieser Artikel führt Sie durch alle drei Schritte und zeigt ehrlich auf, wo die Validierungs-Engine an ihre Grenzen stößt

Der Druck hinter diesem Funktionsumfang ist regulatorischer und nicht technischer Natur. Deutschland und Frankreich führen schrittweise die obligatorische strukturierte E-Rechnungsstellung für inländische B2B-Transaktionen ein, und beide Mandate laufen auf dasselbe hybride Modell hinaus: Die PDF-Rechnung, die Ihr ERP bereits erzeugt, muss einen maschinenlesbaren XML-Zwilling enthalten. Eine Delphi-Buchhaltungs- oder ERP-Anwendung, die heute einfache PDF-Rechnungen ausgibt, hat eine strikte Frist, um mit der Ausgabe von Factur-X oder ZUGFeRD zu beginnen, und der Unterschied zwischen den beiden Namen ist kleiner, als das Marketing vermuten lässt — Factur-X und ZUGFeRD sind derselbe deutsch-französische Standard, der unter zwei Bezeichnungen veröffentlicht wird

Was macht ein PDF zu einer gültigen Factur-X-Rechnung?

Eine gültige Factur-X-Rechnung ist eine PDF/A-3-Datei (ISO 19005-3), die genau eine strukturierte Rechnungs-XML als zugehörige Datei einbettet, diese aus dem Dokumentenkatalog verknüpft und in den XMP-Erweiterungsmetadaten deklariert. Drei Ebenen müssen übereinstimmen. Erstens muss der Container PDF/A-3 entsprechen, dem Archivierungsprofil, das — im Gegensatz zu PDF/A-1 und PDF/A-2 — eingebettete Dateien beliebigen Typs zulässt. Zweitens muss die Rechnungs-XML-Datei, die bei Standardprofilen factur-x.xml oder bei der deutschen XRechnung-Variante xrechnung.xml heißt, im /AF-Array des Katalogs und im /Names /EmbeddedFiles-Baum registriert sein, mit einem AFRelationship-Eintrag, der angibt, worum es sich bei dem Anhang handelt: Alternative bedeutet, dass die XML-Datei eine gleichwertige Darstellung der sichtbaren Rechnung ist, was der Semantik entspricht, die die meisten Profile erfordern. Drittens muss das Factur-X XMP-Erweiterungsschema den Namen der eingebetteten Datei, ihre Konformitätsstufe und den Dokumenttyp angeben, damit ein empfangendes System die Rechnung klassifizieren kann, ohne die XML-Datei überhaupt analysieren zu müssen

Die XML-Nutzdaten selbst folgen dem semantischen Modell der EN 16931, der europäischen Norm, die die Kernrechnung definiert: Käufer, Verkäufer, Rechnungszeilen, Steueraufschlüsselung, Zahlungsbetrag, jeweils mit einer Business-Term-Nummer (BT-1 für die Rechnungsnummer, BT-5 für die Währung usw.). Die Validierung nach EN 16931 erfolgt daher auf zwei verschiedenen Ebenen. Die XML-Schema-Validierung prüft, ob das Dokument ein wohlgeformtes CII mit den richtigen Elementnamen ist. Die Schematron-Geschäftsregeln prüfen, ob der Inhalt kohärent is — dass BR-CO-15 gilt und die Gesamtsumme der Rechnung tatsächlich der Summe der Nettozeilenbeträge plus Umsatzsteuer entspricht und dass obligatorische Begriffe für das deklarierte Profil vorhanden sind. Eine Datei kann die Schema-Validierung bestehen und dennoch eine ungültige Rechnung sein, weshalb die Geschäftsregelebene existiert

Warum ZUGFeRD 2.5 keinen neuen Container benötigt

ZUGFeRD 2.5 ändert auf der PDF-Containerebene nichts: Die Richtlinien-URNs, das XMP-Schema und das Versionstoken sind identisch mit ZUGFeRD 2.3 / Factur-X 1.0. Dies überrascht die meisten Entwickler, weshalb es sich lohnt, die Quelle zu zitieren. Die Factur-X 1.09-Spezifikation besagt, dass die Versionsnummer in der URI des PDF/A-Erweiterungsschemas nicht mit der Versionsnummer der XML-Datenspezifikation zusammenhängt und dass die Versionierung von Factur-X-Rechnungsinstanzen bei 1.0 bleibt — sowohl in XMP fx:Version als auch in BT-24 von factur-x.xml. Der offizielle ZUGFeRD 2.5-Beispielkorpus bestätigt dies: Jedes Beispiel bettet factur-x.xml unter einer Richtlinien-URN urn:factur-x.eu:1p0 mit der fx:Version 1.0 ein. Was 2.5 tatsächlich hinzufügt, befindet sich innerhalb des XML-Datenmodells — neue Elemente wie Unterrechnungszeilen und Chargenkennungen —, was die von Ihrer Anwendung generierte Geschäftsebene ist, nicht die von der PDF-Bibliothek verwaltete Containerebene

Einbetten der Rechnungs-XML in Delphi

THotPDF.AddFacturXAssociatedFile verwandelt einen regulären PDF/A-3-Erzeugungslauf mit einem einzigen Aufruf in eine Factur-X-Rechnung: Es bettet die XML-Bytes als zugehörige Datei ein, registriert factur-x.xml im /AF-Array des Katalogs sowie im /Names /EmbeddedFiles-Baum und gibt die passenden Factur-X XMP-Erweiterungsmetadaten aus. Sie zeichnen die visuelle Rechnungsseite mit der normalen HotPDF-Canvas-API; die Container-Arbeit ist eine einzige Zeile zwischen BeginDoc und 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;

Die Standardeinstellungen folgen der Spezifikation: Dateiname factur-x.xml, Beziehung Alternative, Version 1.0. AddUBLSupplementaryFile implementiert Abschnitt 6.4 der Factur-X 1.09-Spezifikation, der es erlaubt, eine UBL-Darstellung derselben Rechnung als zweiten Anhang namens factur-xubl.xml mit dem MIME-Typ application/xml und der Beziehung Alternative mitzuführen. Die Reihenfolge ist hierbei wichtig, und HotPDF erzwingt die von der Spezifikation implizierte Einschränkung: Die UBL-Datei ist eine ergänzende Ansicht, niemals die Hauptrechnung, und muss daher hinzugefügt werden, nachdem AddFacturXAssociatedFile den CII-Master registriert hat. Beachten Sie, dass die gezeichnete Seite und die eingebettete XML-Datei dieselbe Rechnung ausweisen müssen — ein PDF, das eine Summe anzeigt und eine andere codiert, ist genau der Fehlerfall, den die hybride Rechnungsstellung verhindern soll. Die PDF/A-3-Seite dieses Workflows, einschließlich OutputIntent und Schrifteinbettung, wird im Begleitartikel zur PDF/A-, PDF/X- und PDF/UA-Validierung in Delphi ausführlicher behandelt

Wie erkennt man eine Factur-X-Rechnung in einer eingehenden PDF-Datei?

THotPDF.DetectFacturXInvoice deckt die Empfangsseite des Mandats ab: Laden Sie ein beliebiges PDF und erfahren Sie mit einem einzigen Aufruf, ob es sich um eine hybride Rechnung handelt, welches Profil sie deklariert und welche Anhänge sie enthält. Die Funktion gibt einen THPDFFacturXInvoiceInfo-Datensatz mit dem Namen der eingebetteten XML-Datei, dem in XMP deklarierten Namen und Typ der Dokumentdatei, dem Versionstoken, der Konformitätsstufe, der Richtlinien-URN und dem AFRelationship-Wert zurück. Zwei weitere Felder melden ergänzende Ansichten: UBLFileName wird ausgefüllt, wenn ein factur-xubl.xml-Anhang vorhanden ist, und EDIFACTFileName, wenn eine EDIFACT-Ansicht mitgeführt wird — beides hat keinen Einfluss auf das Erkennungsergebnis, da eine ergänzende Darstellung nicht ohne die primäre CII-Rechnung existieren kann

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;

Die Erkennung liest dieselben Signale, die auch ein konformer Validator liest — das XMP-Erweiterungsschema und das Register der zugehörigen Dateien —, anstatt nur anhand von Dateinamen zu raten. Dasselbe geladene Dokumentenobjekt macht auch die XMP- und Info-Dictionary-Metadaten zur Überprüfung oder Korrektur zugänglich, ein Workflow, der im Artikel über das Bearbeiten von Metadaten in geladenen PDF-Dokumenten beschrieben wird

Geschäftsregeln oder Schema: Was bedeutet die EN 16931-Validierung?

HPDFValidateEInvoice, eine eigenständige Funktion in der Unit HPDFEInvoiceValidator, führt beide Validierungsebenen nacheinander aus: zuerst die Container-Strukturprüfung über ValidateFacturXInvoice, dann die EN 16931-Schematron-Geschäftsregeln für die extrahierte Rechnungs-XML. HotPDF liefert die fünf offiziellen Factur-X 1.09-Regeldateien — MINIMUM, BASIC WL, BASIC, EN 16931, und EXTENDED — unter Lib/resources/Schematron/ aus, und HPDFSchematronFileForProfile wählt die Datei aus, die der erkannten Konformitätsstufe entspricht, sodass eine EN 16931-Rechnung automatisch mit Factur-X_1.09_EN16931.sch geprüft wird. Die Funktion gibt nur dann True zurück, wenn der Container gültig ist und keine Geschäftsregel verletzt wurde

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;

Jeder Eintrag in BusinessRules enthält die aus dem Assertionstext analysierte Regelkennung — BR-45, BR-CO-17 und den Rest des EN 16931-Namensschemas — sowie den XPath des Regelkontexts, den Testausdruck und einen Schweregrad, der das Schematron-Flag-Attribut widerspiegelt. Das macht den Bericht handhabbar: Anstelle eines bloßen Bestehens/Nichtbestehens erhalten Sie genau den Begriff, der inkonsistent ist. Dies benötigt Ihr Support-Team, wenn ein Kunde fragt, warum eine Rechnung abgelehnt wurde. Teams, die bereits strukturierte Konformitätsberichte für Archiv-PDFs erstellen, können diese Ergebnisse in dieselbe Pipeline einbinden, die im Artikel über die Automatisierung von PDF-Preflight-Berichten beschrieben wird

Wo der Validator aufhört, ganz ehrlich

Die Schematron-Engine basiert auf MSXML, das XPath 1.0 auswertet. Die EN 16931-Regeldatei deklariert queryBinding="xslt2" und enthält 424 Assertionen, von denen die meisten nur XPath 1.0-Funktionen wie string-length und substring-after verwenden — diese werden nativ ausgeführt. Eine Minderheit stützt sich auf XPath 2.0-Konstrukte wie xs:decimal-Typkonvertierungen oder exists(); HotPDF scannt jeden Testausdruck im Voraus auf bekannte XPath 2.0-Token und markiert diese Regeln als Skipped mit dem Schweregrad Info, anstatt eine Auswertung zu versuchen, die stillschweigend fehlschlagen würde. Der Zusammenfassungsdatensatz meldet die Anzahl der ausgewerteten und übersprungenen Regeln separat, sodass Ihr Protokoll immer genau angibt, wie viel des Regelsatzes ausgeführt wurde. Unter Delphi 7, wo MSXML-Bindungen nicht verfügbar sind, kompiliert die Unit einen Stub, der die Engine als nicht verfügbar meldet, ohne das Container-Urteil zu beeinträchtigen; die vollständige Engine erfordert Delphi XE2 oder neuer

Die zweite Grenze ist noch wichtiger. Ein grünes Ergebnis von HPDFValidateEInvoice bedeutet, dass die Containerstruktur konform ist und keine ausgewertete Geschäftsregel verletzt wurde — es handelt sich nicht um eine steuerrechtliche Freigabe. Nationale Erweiterungsregeln, empfängerspezifische Anforderungen und die gesetzlichen Verpflichtungen bezüglich des Rechnungsinhalts liegen über dem EN 16931-Kern und außerhalb jeder Bibliothek. Betrachten Sie den Validator als das Tor, das strukturell fehlerhafte Rechnungen aus Ihrem Postausgang fernhält und inkonsistente in Ihrem Posteingang markiert. Führen Sie einen vollständigen XPath 2.0-Validator in Ihrer Release-Pipeline aus, wenn eine Zertifizierung dies erfordert, und überlassen Sie die Compliance-Frage Ihren Steuerberatern. Die hier gezeigten APIs zum Einbetten, Erkennen und Validieren sind Teil der standardmäßigen HotPDF-Komponente für Delphi und C++Builder, mit einem vollständigen E-Rechnungs-Beispiel, das alle sieben ZUGFeRD- und Factur-X-Profile abdeckt