Техническа статия

Електронни фактури ZUGFeRD и Factur-X в Delphi с HotPDF

HotPDF генерира, открива и валидира електронни фактури по стандартите ZUGFeRD и Factur-X от Delphi и C++Builder. Методът AddFacturXAssociatedFile вгражда XML файла на фактурата съгласно EN 16931 в PDF/A-3 контейнер, DetectFacturXInvoice разпознава входяща хибридна фактура, а HPDFValidateEInvoice изпълнява официалните бизнес правила на Schematron по стандарта EN 16931 срещу вградения XML. Тази статия разглежда и трите стъпки и очертава границите, до които спира двигателят за валидиране

Натиснят зад този набор от функции е регулаторен, а не технически. Германия и Франция поетапно въвеждат задължително структурирано електронно фактуриране за вътрешни B2B трансакции, като и двата мандата се сближават към един и същ хибриден модел: PDF фактурата, която вашата ERP система вече произвежда, трябва да носи вграден машинно четим XML близнак в себе си. Счетоводно или ERP приложение на Delphi, което днес издава обикновени 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 е еквивалентно представяне на видимата фактура, което е семантиката, изисквана от повечето профили. Трето, схемата на разширението Factur-X XMP трябва да посочва името на вградения файл, неговото ниво на съответствие и типа на документа, така че получаващата система да може да класифицира фактурата, без изобщо да анализира самия XML

Самият XML полезен товар следва семантичния модел по EN 16931 – европейския стандарт, който дефинира ядрото на фактурата: купувач, продавач, редове от фактурата, разбивка на данъците, дължима сума, всеки с номер на бизнес термин (например BT-1 за номер на фактура, BT-5 за валута и т.н.). Следователно валидирането по EN 16931 се случва на два отделни слоя. Валидирането на XML схемата проверява дали документът е правилно оформен CII (Cross Industry Invoice) с правилните имена на елементите. Бизнес правилата на Schematron проверяват дали съдържанието е съгласувано – дали е изпълнено правилото BR-CO-15 и общата сума на фактурата наистина е равна на сумата от нетните стойности на редовете плюс ДДС, и дали задължителните термини присъстват за декларирания профил. Даден файл може да премине успешно валидирането на схемата и въпреки това да бъде невалидна фактура, поради което съществува слоят за бизнес правила

Защо ZUGFeRD 2.5 не се нуждае от нов контейнер

ZUGFeRD 2.5 не променя нищо на ниво PDF контейнер: насочващите URN адреси, схемата XMP и версията са идентични с тези в 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. Официалният корпус от примери за 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 фактура с едно извикване: той вгражда XML байтовете като асоцииран файл, регистрира factur-x.xml в масива на каталога /AF и дървото /Names /EmbeddedFiles и записва съответните XMP метаданни за Factur-X разширението. Вие изчертавате визуалната фактура с обичайното canvas 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 оригинала. Имайте предвид, че изчертаната страница и вграденият XML трябва да описват една и съща фактура – PDF, който показва една сума и кодира друга, е точно дефектът, който хибридното фактуриране цели да предотврати. PDF/A-3 страната на този процес (включително OutputIntent и вграждането на шрифтове) е разгледана по-подробно в придружаващата статия за валидиране на PDF/A, PDF/X и PDF/UA в Delphi

Как се разпознава Factur-X фактура в рамките на входящ PDF?

Методът THotPDF.DetectFacturXInvoice отговаря на изискванията за получаване: заредете произволен PDF файл и разберете с едно извикване дали той е хибридна фактура, какъв профил декларира и какви прикачени файлове носи. Функцията връща запис THPDFFacturXInvoiceInfo, съдържащ името на вградения XML файл, името и типа на документа съгласно XMP, версията, нивото на съответствие, водещия 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, а след това бизнес правилата на Schematron по стандарта EN 16931 срещу извлечения 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. Това прави отчета полезен: вместо обикновено преминал/неуспешен получавате точния бизнес термин, който е несъвместим, което е необходимо на вашия екип по поддръжка, когато клиент попита защо дадена фактура е отхвърлена. Екипите, които вече генерират структурирани отчети за съответствие за архивни PDF файлове, могат да добавят тези резултати в същия процес, описан в статията за автоматизиране на отчети за проверка на PDF

Къде спира валидаторът, честно казано

Двигателят на Schematron е изграден върху библиотеката MSXML, която оценява XPath 1.0. Файлът с правила за EN 16931 декларира queryBinding="xslt2" и съдържа 424 твърдения, повечето от които използват само 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 валидатор във вашия процес на внедряване, когато сертификацията го изисква, и оставете данъчните консултанти да решават въпросите по съответствието. Приложните програмни интерфейсове (API) за вграждане, откриване и валидиране, показани тук, са част от стандартния компонент HotPDF Component за Delphi и C++Builder, придружени от пълен пример за електронна фактура, покриващ всичките седем ZUGFeRD и Factur-X профили