Τεχνικό Άρθρο

Ηλεκτρονικά Τιμολόγια ZUGFeRD και Factur-X στο Delphi με το HotPDF

Το HotPDF δημιουργεί, ανιχνεύει και επικυρώνει ηλεκτρονικά τιμολόγια ZUGFeRD και Factur-X από το Delphi και το C++Builder. Η AddFacturXAssociatedFile ενσωματώνει το XML τιμολογίου EN 16931 σε έναν περιέκτη PDF/A-3, η DetectFacturXInvoice αναγνωρίζει ένα εισερχόμενο υβριδικό τιμολόγιο, και η HPDFValidateEInvoice εκτελεί τους επίσημους επιχειρηματικούς κανόνες EN 16931 Schematron έναντι του ενσωματωμένου XML. Αυτό το άρθρο περιγράφει και τα τρία βήματα και αναφέρεται ειλικρινά στα όρια της μηχανής επικύρωσης

Η πίεση πίσω από αυτό το σύνολο χαρακτηριστικών είναι κανονιστική και όχι τεχνική. Η Γερμανία και η Γαλλία εισάγουν σταδιακά την υποχρεωτική δομημένη ηλεκτρονική τιμολόγηση για εγχώριες B2B συναλλαγές, και οι δύο κανονισμοί συγκλίνουν στο ίδιο υβριδικό μοντέλο: το τιμολόγιο PDF που ήδη παράγει το ERP σας πρέπει να φέρει ένα μηχανικά αναγνώσιμο XML δίδυμο μέσα του. Μια Delphi εφαρμογή λογιστικής ή ERP που εκπέμπει απλά τιμολόγια PDF σήμερα έχει μια αυστηρή προθεσμία να αρχίσει να εκπέμπει Factur-X ή ZUGFeRD, και η διαφορά μεταξύ των δύο ονομάτων είναι μικρότερη από αυτή που υποδηλώνει το μάρκετινγκ — τα Factur-X και ZUGFeRD αποτελούν το ίδιο γαλλογερμανικό πρότυπο που δημοσιεύεται με δύο ετικέτες

Τι καθιστά ένα PDF έγκυρο τιμολόγιο Factur-X;

Ένα έγκυρο τιμολόγιο Factur-X είναι ένα αρχείο PDF/A-3 (ISO 19005-3) που ενσωματώνει ακριβώς ένα δομημένο XML τιμολογίου ως συσχετισμένο αρχείο, το συνδέει από τον κατάλογο εγγράφου και το δηλώνει στα μεταδεδομένα επέκτασης XMP. Τρία επίπεδα πρέπει να συμφωνούν. Πρώτον, ο περιέκτης πρέπει να συμμορφώνεται με το PDF/A-3, το αρχειακό προφίλ που — σε αντίθεση με τα PDF/A-1 και PDF/A-2 — επιτρέπει ενσωματωμένα αρχεία οποιουδήποτε τύπου. Δεύτερον, το XML τιμολογίου, με το όνομα factur-x.xml για τυπικά προφίλ ή xrechnung.xml για τη γερμανική παραλλαγή XRechnung, πρέπει να είναι καταχωρισμένο στον πίνακα /AF του καταλόγου και στο δέντρο /Names /EmbeddedFiles, με μια καταχώριση AFRelationship που δηλώνει τι είναι το συνημμένο: το Alternative σημαίνει ότι το XML είναι μια ισοδύναμη αναπαράσταση του ορατού τιμολογίου, η οποία είναι η σημασιολογία που απαιτούν τα περισσότερα προφίλ. Τρίτον, το σχήμα επέκτασης XMP του Factur-X πρέπει να ονομάζει το ενσωματωμένο αρχείο, το επίπεδο συμμόρφωσής του και τον τύπο εγγράφου, ώστε ένα σύστημα λήψης να μπορεί να ταξινομήσει το τιμολόγιο χωρίς καν να αναλύσει το XML

Γιατί το ZUGFeRD 2.5 δεν χρειάζεται νέο περιέκτη

Το ZUGFeRD 2.5 δεν αλλάζει τίποτα σε επίπεδο περιέκτη PDF: τα URN οδηγιών, το σχήμα XMP και το διακριτικό έκδοσης (version token) είναι πανομοιότυπα με το ZUGFeRD 2.3 / Factur-X 1.0. Αυτό εκπλήσσει τους περισσότερους υπεύθυνους υλοποίησης, οπότε αξίζει να παραθέσουμε την πηγή. Η προδιαγραφή Factur-X 1.09 δηλώνει ότι ο αριθμός έκδοσης στο URI του σχήματος επέκτασης PDF/A δεν σχετίζεται με τον αριθμό έκδοσης της προδιαγραφής δεδομένων XML, και ότι η έκδοση των στιγμιότυπων τιμολογίων Factur-X παραμένει 1.0 — τόσο στο XMP fx:Version όσο και στο BT-24 του factur-x.xml. Το επίσημο σώμα δειγμάτων (sample corpus) του ZUGFeRD 2.5 το επιβεβαιώνει: κάθε δείγμα ενσωματώνει το factur-x.xml κάτω από ένα URN οδηγιών urn:factur-x.eu:1p0 με fx:Version 1.0. Αυτό που πραγματικά προσθέτει το 2.5 ζει μέσα στο μοντέλο δεδομένων XML — νέα στοιχεία όπως γραμμές υπο-τιμολογίων και αναγνωριστικά παρτίδας — το οποίο είναι το επιχειρηματικό επίπεδο που παράγει η εφαρμογή σας, όχι το επίπεδο περιέκτη που διαχειρίζεται η βιβλιοθήκη PDF

Το HotPDF κωδικοποιεί αυτό το εύρημα απευθείας. Η AddFacturXAssociatedFile δέχεται ένα όρισμα Version και το κανονικοποιεί μέσω της HPDFFacturXNormalizeVersion πριν γράψει το XMP, οπότε ένας καλών που περνά το ψευδώνυμο 2p5 εξακολουθεί να εκπέμπει το συμβατό με τις προδιαγραφές διακριτικό, αντί να εφεύρει μια έκδοση XMP 2p5 που κανένας επικυρωτής δεν θα αποδεχόταν. Οι βοηθοί ανίχνευσης αντιμετωπίζουν το 2p5 ως ισοδύναμο κατά την ανάγνωση, οπότε οι αμφίδρομες διαδρομές παραμένουν συμμετρικές. Εάν μια βιβλιοθήκη ή ένα εργαλείο σάς λέει ότι γράφει έναν ειδικό περιέκτη ZUGFeRD 2.5, η ίδια η προδιαγραφή αναφέρει ότι δεν υπάρχει τέτοιο πράγμα

Ενσωμάτωση του XML τιμολογίου από το Delphi

Η THotPDF.AddFacturXAssociatedFile μετατρέπει μια κανονική εκτέλεση παραγωγής PDF/A-3 σε τιμολόγιο Factur-X με μία κλήση: ενσωματώνει τα byte του XML ως συσχετισμένο αρχείο, καταχωρεί το factur-x.xml στον πίνακα /AF του καταλόγου και στο δέντρο /Names /EmbeddedFiles, και εκπέμπει τα αντίστοιχα μεταδεδομένα επέκτασης Factur-X XMP. Σχεδιάζετε την ανθρωποκεντρική σελίδα τιμολογίου με το κανονικό API καμβά του HotPDF· η εργασία του περιέκτη είναι μια απλή γραμμή μεταξύ των BeginDoc και 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;

Οι προεπιλογές ακολουθούν την προδιαγραφή: όνομα αρχείου factur-x.xml, σχέση Alternative, έκδοση 1.0. Η AddUBLSupplementaryFile υλοποιεί την ενότητα 6.4 της προδιαγραφής Factur-X 1.09, η οποία επιτρέπει σε μια UBL αναπαράσταση του ίδιου τιμολογίου να συνοδεύει ως δεύτερο συνημμένο με το όνομα factur-xubl.xml με τύπο MIME application/xml και σχέση Alternative. Η σειρά έχει σημασία εδώ, και το HotPDF επιβάλλει τον περιορισμό που υπονοεί η προδιαγραφή: το αρχείο UBL είναι μια συμπληρωματική προβολή, ποτέ το κύριο τιμολόγιο, επομένως πρέπει να προστεθεί αφού η AddFacturXAssociatedFile καταχωρίσει το CII master. Σημειώστε ότι η σχεδιασμένη σελίδα και το ενσωματωμένο XML πρέπει να αναφέρουν το ίδιο τιμολόγιο — ένα PDF που εμφανίζει ένα σύνολο και κωδικοποιεί ένα άλλο είναι ακριβώς η περίπτωση αποτυχίας που η υβριδική τιμολόγηση έχει σχεδιαστεί να αποτρέπει. Η πλευρά PDF/A-3 αυτής της ροής εργασίας, συμπεριλαμβανομένων των OutputIntent και ενσωμάτωσης γραμματοσειρών, καλύπτεται σε μεγαλύτερο βάθος στο συνοδευτικό άρθρο για την επικύρωση PDF/A, PDF/X και PDF/UA στο Delphi

Πώς ανιχνεύετε ένα τιμολόγιο Factur-X σε ένα εισερχόμενο PDF;

Η THotPDF.DetectFacturXInvoice απαντά στην πλευρά λήψης της εντολής: φορτώστε οποιοδήποτε PDF και μάθετε με μία κλήση εάν πρόκειται για υβριδικό τιμολόγιο, ποιο προφίλ δηλώνει και ποια συνημμένα φέρει. Η συνάρτηση επιστρέφει μια εγγραφή THPDFFacturXInvoiceInfo με το όνομα του ενσωματωμένου αρχείου XML, το όνομα και τον τύπο του εγγράφου που δηλώνεται στο XMP, το διακριτικό έκδοσης (version token), το επίπεδο συμμόρφωσης, το URN οδηγιών και την τιμή AFRelationship. Δύο επιπλέον πεδία αναφέρουν συμπληρωματικές προβολές: το UBLFileName συμπληρώνεται όταν υπάρχει συνημμένο factur-xubl.xml, και το EDIFACTFileName όταν συνοδεύει μια προβολή EDIFACT — κανένα από τα δύο δεν επηρεάζει την απόφαση ανίχνευσης, καθώς μια συμπληρωματική αναπαράσταση δεν μπορεί να υπάρξει χωρίς το κύριο τιμολόγιο CII

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;

Η ανίχνευση διαβάζει τα ίδια σήματα που διαβάζει ένας συμβατός επικυρωτής — το σχήμα επέκτασης XMP και το μητρώο συσχετισμένων αρχείων — αντί να μαντεύει μόνο από τα ονόματα των αρχείων. Το ίδιο αντικείμενο φορτωμένου εγγράφου εκθέτει επίσης τα μεταδεδομένα του XMP και του λεξικού Info για επιθεώρηση ή διόρθωση, μια ροή εργασίας που περιγράφεται στο άρθρο για την επεξεργασία μεταδεδομένων σε φορτωμένα έγγραφα PDF

Επιχειρηματικοί κανόνες ή σχήμα: τι σημαίνει η επικύρωση EN 16931;

Η HPDFValidateEInvoice, μια αυτόνομη συνάρτηση στη μονάδα HPDFEInvoiceValidator, εκτελεί και τα δύο επίπεδα επικύρωσης διαδοχικά: πρώτα τον έλεγχο δομής του περιέκτη μέσω της ValidateFacturXInvoice, και στη συνέχεια τους επιχειρηματικούς κανόνες EN 16931 Schematron έναντι του εξαχθέντος XML τιμολογίου. Το HotPDF αποστέλλει τα πέντε επίσημα αρχεία κανόνων Factur-X 1.09 — MINIMUM, BASIC WL, BASIC, EN 16931 και EXTENDED — κάτω από το Lib/resources/Schematron/, και η HPDFSchematronFileForProfile επιλέγει το αρχείο που ταιριάζει με το ανιχνευμένο επίπεδο συμμόρφωσης, οπότε ένα τιμολόγιο EN 16931 ελέγχεται αυτόματα έναντι του Factur-X_1.09_EN16931.sch. Η συνάρτηση επιστρέφει True μόνο όταν ο περιέκτης είναι έγκυρος και δεν ενεργοποιήθηκε κανένας επιχειρηματικός κανόνας

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;

Κάθε καταχώριση στο BusinessRules φέρει το αναγνωριστικό κανόνα που αναλύθηκε από το κείμενο του ισχυρισμού — BR-45, BR-CO-17 και το υπόλοιπο σχήμα ονοματοδοσίας EN 16931 — συν το πλαίσιο XPath του κανόνα, την έκφραση δοκιμής και μια σοβαρότητα (severity) που αντικατοπτρίζει το χαρακτηριστικό σημαίας Schematron. Αυτό καθιστά την αναφορά αξιοποιήσιμη: αντί για ένα απλό pass/fail λαμβάνετε τον ακριβή επιχειρηματικό όρο που είναι ασυνεπής, κάτι που χρειάζεται η ομάδα υποστήριξής σας όταν ένας πελάτης ρωτά γιατί απορρίφθηκε ένα τιμολόγιο. Οι ομάδες που ήδη παράγουν δομημένες αναφορές συμμόρφωσης για αρχειακά PDF μπορούν να ενσωματώσουν αυτά τα αποτελέσματα στην ίδια ροή εργασίας που περιγράφεται στο άρθρο για την αυτοματοποίηση των αναφορών PDF preflight

Πού σταματά η επικύρωση, ειλικρινά

Η μηχανή Schematron είναι χτισμένη στο MSXML, το οποίο αξιολογεί το XPath 1.0. Το αρχείο κανόνων EN 16931 δηλώνει queryBinding="xslt2" και φέρει 424 ισχυρισμούς (assertions), οι περισσότεροι από τους οποίους χρησιμοποιούν μόνο συναρτήσεις XPath 1.0 όπως string-length και substring-after — αυτές εκτελούνται εγγενώς. Μια μειοψηφία βασίζεται σε δομές XPath 2.0 όπως μετατροπές xs:decimal ή exists()· το HotPDF προ-σαρώνει κάθε έκφραση δοκιμής για γνωστά διακριτικά XPath 2.0 και επισημαίνει αυτούς τους κανόνες ως Skipped με σοβαρότητα πληροφορίας (info), αντί να επιχειρήσει μια αξιολόγηση που θα αποτύγχανε σιωπηλά. Η συνοπτική εγγραφή αναφέρει χωριστά τους μετρητές αξιολόγησης και παράκαμψης, οπότε το αρχείο καταγραφής σας δηλώνει πάντα ακριβώς πόσο μέρος του συνόλου των κανόνων εκτελέστηκε. Στο Delphi 7, όπου οι δεσμεύσεις MSXML δεν είναι διαθέσιμες, η μονάδα μεταγλωττίζει ένα stub που αναφέρει τη μηχανή ως μη διαθέσιμη χωρίς να διαταράσσει την απόφαση του περιέκτη· η πλήρης μηχανή απαιτεί Delphi XE2 ή νεότερη έκδοση

Το δεύτερο όριο έχει ακόμη μεγαλύτερη σημασία. Ένα πράσινο αποτέλεσμα από την HPDFValidateEInvoice σημαίνει ότι η δομή του περιέκτη συμμορφώνεται και κανένας αξιολογημένος επιχειρηματικός κανόνας δεν ενεργοποιήθηκε — δεν αποτελεί έγκριση φορολογικής συμμόρφωσης. Οι εθνικοί κανόνες επέκτασης, οι απαιτήσεις ειδικές για τον παραλήπτη και οι νομικές υποχρεώσεις σχετικά με το περιεχόμενο του τιμολογίου βρίσκονται πάνω από τον πυρήνα EN 16931 και εκτός οποιασδήποτε βιβλιοθήκης. Αντιμετωπίστε τον επικυρωτή ως την πύλη που κρατά τα δομικά σπασμένα τιμολόγια έξω από τα εξερχόμενά σας και επισημαίνει τα ασυνεπή στα εισερχόμενά σας, εκτελέστε έναν πλήρη επικυρωτή XPath 2.0 στη ροή παραγωγής σας (release pipeline) όταν η πιστοποίηση το απαιτεί, και αφήστε τους φορολογικούς σας συμβούλους να αναλάβουν το ζήτημα της συμμόρφωσης. Τα API ενσωμάτωσης, ανίχνευσης και επικύρωσης που εμφανίζονται εδώ αποτελούν μέρος του τυπικού HotPDF Component για Delphi και C++Builder, με ένα πλήρες δείγμα E-Invoice που καλύπτει και τα επτά προφίλ ZUGFeRD και Factur-X