O factură Factur-X sau ZUGFeRD reprezintă două documente sub un singur nume de fișier. Documentul exterior este un container PDF/A-3 pe care un cititor de arhivare trebuie să îl accepte pentru următorii zece ani. Documentul interior este o factură XML pe care sistemul contabil al cumpărătorului trebuie să o analizeze în raport cu EN 16931. Greșeala care trimite facturi defecte în producție este credința că dacă obții primul element corect, îl obții automat și pe al doilea. Lucrurile nu stau așa. Un fișier poate fi un PDF/A-3 impecabil și totuși poate conține XML pe care nicio autoritate fiscală nu îl va accepta, și poate conține un XML EN 16931 de manual în interiorul unui container care eșuează validarea de arhivare. Cele două straturi sunt validate de două instrumente diferite care nu știu nimic unul despre celălalt, iar un pipeline real trebuie să le satisfacă pe ambele
Două validatoare, două întrebări diferite
veraPDF este implementarea de referință pentru PDF/A. Îndreptați-l spre o factură și va răspunde la o singură întrebare: este acesta un fișier PDF/A-3 conform. Verifică lucrurile de care se preocupă ISO 19005-3. Este fiecare font încorporat. Există un OutputIntent. Declară metadatele XMP partea corectă și nivelul de conformitate. Pentru o factură electronică verifică și instalațiile de fișiere asociate pe care le impune PDF/A-3, deoarece XML-ul este transportat ca fișier încorporat cu un /AFRelationship și o intrare în matricea /AF a catalogului documentului. veraPDF nu spune nimic despre dacă totalul facturii este corect, deoarece aceasta nu ține de competența sa
Mustang este validatorul open-source de la Mustangproject. Pune întrebarea ortogonală: este XML-ul încorporat o factură validă. Rulează XML-ul față de schema pentru profilul declarat și aplică apoi regulile de afaceri EN 16931 și seturile de reguli specifice fiecărei țări aplicate peste acestea, printre care CIUS al XRechnung. Verifică dacă identificatorul TVA al vânzătorului este prezent atunci când totalurile o cer, dacă sumele de reduceri și taxe se reconciliază cu totalul documentului, dacă URN-ul profilului din XML corespunde cu ceea ce pretinde fișierul că este. Mustang nu se preocupă dacă PDF-ul din jur are fonturile încorporate, deoarece aceasta este treaba lui veraPDF
Niciun instrument nu este un supraset al celuilalt. veraPDF trece un container perfect structural în jurul unui XML lipsit de sens. Mustang trece un XML perfect ambalat într-un container cu un OutputIntent lipsă. Fiecare prinde exact clasa de defect față de care celălalt este orb, și acesta este motivul pentru care un harness serios de validare le rulează pe ambele și tratează un fișier ca livrabil numai când ambele sunt de acord
Matricea de validare
Pentru a dovedi că biblioteca produce fișiere care supraviețuiesc ambelor porți, harness-ul construiește o matrice. Șase profiluri de factură acoperă gama pe care un pipeline european o întâlnește în practică: Factur-X EN 16931, Factur-X BASIC, varianta Factur-X EXTENDED France B2B, XRechnung 3.0, ZUGFeRD 1.0 COMFORT și ZUGFeRD 2.0 BASIC. Fiecare profil este generat față de două niveluri de sub-conformitate PDF/A, 3b și 3u, deoarece cerințele de nivel B și nivel U diferă în ceea ce privește maparea Unicode, iar un fișier care trece unul poate eșua la celălalt. Șase profiluri înmulțite cu două niveluri înseamnă douăsprezece fișiere, fiecare construit headless de același drum de cod pe care îl expediază eșantionul GUI, astfel încât artefactele testate nu sunt ajustate manual pentru test
Generatorul scrie toate cele douăsprezece și un script alimentează fiecare la ambii validatori. La prima rulare completă, veraPDF a trecut toate douăsprezece. Instalațiile containerului au fost corecte peste tot: fișierele asociate înregistrate, conformitatea XMP declarată, intențiile de ieșire în vigoare. Mustang a trecut opt. Patru facturi erau fișiere PDF/A-3 valide structural care conțineau XML pe care validatorul de reguli de afaceri l-a respins, ceea ce este exact separarea pe care abordarea cu două instrumente există pentru a o evidenția. Dacă harness-ul ar fi avut încredere numai în veraPDF, cele patru ar fi arătat finalizate
Cele două remedieri care au închis decalajul
Cele patru eșecuri Mustang au provenit din două cauze distincte, iar remedierea fiecăreia este un detaliu care merită cunoscut înainte de a genera aceste profiluri
Prima a fost profilul Factur-X EXTENDED France B2B. Generatorul original a transmis o etichetă internă ca nivel de conformitate și un URN intern ca ghid, iar Mustang a respins fișierul cu o eroare de valoare-de-conformitate-invalidă urmată de o eroare de tip-de-profil-neacceptat. Motivul este că câmpul XMP fx:ConformanceLevel nu este un spațiu text liber pentru propria numire de profil. Factur-X definește exact cinci valori standard pentru acesta: MINIMUM, BASIC WL, BASIC, EN 16931 și EXTENDED. O factură B2B specifică Franței este în continuare un document de profil EXTENDED în ceea ce privește metadatele XMP. Caracterul francez al facturii nu se exprimă prin inventarea unui al șaselea nivel de conformitate. Se exprimă prin codul de țară, FR, și prin identificatorul ghidului din interiorul XML-ului, care trebuie să poarte prefixul urn:cen.eu:en16931:2017#conformant# ce marchează un CIUS conform cu EN 16931. Transmiterea valorii standard EXTENDED cu FR ca cod de țară și URN-ul corect al ghidului a făcut fișierul conform
În API-ul bibliotecii aceasta este un apel la AddFacturXAssociatedFileFromString cu conformitatea, țara și ghidul aliniate. Argumentul nivelului de conformitate poartă tokenul standard, argumentul codului de țară poartă FR, iar URN-ul ghidului trăiește în octeții XML pe care îi transmiteți
var
FileID: Integer;
begin
PDF.SetPDFAMode(5); // PDF/A-3b
PDF.NewDocument;
// ... draw the human-readable invoice page ...
// ExtendedXML carries an EN 16931 guideline URN of the form
// urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended
FileID := PDF.AddFacturXAssociatedFileFromString(
ExtendedXML,
'EXTENDED', // standard fx:ConformanceLevel, not an internal label
'factur-x.xml',
'Factur-X EXTENDED invoice',
'Alternative', // /AFRelationship
'1.0',
'FR'); // France B2B marked by country code, not by conformance
if FileID = 0 then
raise Exception.Create('Factur-X attachment rejected');
PDF.SaveToFile('02_Factur-X-EXTENDED-FR_PDFA-3b.pdf');
end;
A doua cauză a fost profilul ZUGFeRD 1.0 COMFORT și nu a avut nicio legătură cu metadatele. ZUGFeRD 1.0 este validat față de XSD-ul :1p0, care este mai strict în ceea ce privește cardinalitatea decât sugerează rezumatele în proză. XSD-ul cere că suma de decontare a antetului, ram:SpecifiedTradeSettlementMonetarySummation, să conțină ram:ChargeTotalAmount și ram:AllowanceTotalAmount fiecare exact o dată. XML-ul generat a omis ambele, astfel Mustang a raportat că elementele trebuie să apară exact o dată. Acestea nu sunt opționale atunci când schema spune că minOccurs este unu. Emiterea ambelor în ordinea secvenței XSD, imediat după ram:LineTotalAmount, cu o valoare de 0.00 când nu există taxe sau reduceri, a satisfăcut schema. Un zero este un element prezent; un element absent este o încălcare a schemei. Cu acele două remedieri în vigoare, matricea a ajuns la doisprezece din doisprezece la Mustang, rămânând totodată la doisprezece din doisprezece la veraPDF
Câmpurile XRechnung care schimbă invalid în valid
XRechnung merită o notă proprie deoarece CIUS-ul său german adaugă reguli de afaceri absente din setul de bază EN 16931, și acestea eșuează în moduri care la prima vedere par că nu este nimic greșit cu documentul. Două dintre ele privesc adresele electronice. BT-34 este adresa electronică a vânzătorului și BT-49 este adresa electronică a cumpărătorului, punctele de rutare pe care un portal al sectorului public german le folosește pentru a livra și confirma factura. Modelul de bază EN 16931 le tratează ca opționale. XRechnung nu le tratează astfel. Omiteți oricare și factura este bine formată, validă din punct de vedere al schemei și respinsă
Al treilea este regula BR-DE-6, care cere ca numărul de telefon de contact al vânzătorului să fie prezent. Este tipul de câmp pe care un dezvoltator îl omite deoarece pare mai degrabă prezentare decât date, iar absența sa produce un eșec de validare care indică spre grupul de contact al vânzătorului mai degrabă decât spre ceva evident lipsă. Furnizarea BT-34, BT-49 și a numărului de telefon al vânzătorului este ceea ce mută un fișier XRechnung de la invalid la valid sub Mustang, și nimic din acestea nu schimbă ce vede veraPDF, deoarece toate trei trăiesc în XML
Conectarea ieșirii bibliotecii la un validator
Punctul arhitectural din spatele harness-ului se generalizează la orice sistem de afaceri. Biblioteca PDF scrie un container conform și încorporează XML-ul. Nu încearcă și nu ar trebui să încerce să fie autoritatea regulilor de afaceri EN 16931. ValidateFacturXInvoice din bibliotecă verifică consistența containerului, că matricea /AF a catalogului, arborele de nume al fișierelor încorporate, DocumentFileName XMP, profilul, ghidul și /AFRelationship sunt toate de acord, dar nu validează coduri fiscale sau reconciliază sume. Diviziunea corectă a muncii este ca sistemul de afaceri să extragă XML-ul și să îl transmită unui validator de facturi dedicat, exact cum harness-ul îl transmite lui Mustang
Citirea fișierului înapoi vă spune ce a fost scris efectiv. DetectFacturXInvoice raportează dacă o factură a fost recunoscută, iar GetFacturXInvoiceInfo citește câmpurile de metadate după etichetă: eticheta 1 este numele fișierului încorporat, eticheta 2 este DocumentFileName XMP, eticheta 5 este nivelul de conformitate, eticheta 6 este identificatorul ghidului și eticheta 7 este /AFRelationship. Confirmarea că nivelul de conformitate citit înapoi este tokenul standard și nu o etichetă internă este cel mai ieftin mod de a prinde greșeala EXTENDED înainte ca un fișier să părăsească build-ul
function ExtractAndInspect(const PdfPath: string): AnsiString;
var
Profile, Guideline: WideString;
begin
Result := '';
PDF.LoadFromFile(PdfPath);
if PDF.DetectFacturXInvoice = 1 then
begin
Profile := PDF.GetFacturXInvoiceInfo(5); // fx:ConformanceLevel
Guideline := PDF.GetFacturXInvoiceInfo(6); // XML guideline ID
Writeln('Profile: ', Profile);
Writeln('Guideline: ', Guideline);
// Hand the raw XML to a dedicated EN 16931 / Mustang validator.
Result := PDF.ExtractFacturXXMLToString;
end;
end;
ExtractFacturXXMLToString returnează octeții XML brut ca AnsiString, gata pentru a fi scris într-un fișier sau transmis în flux unui proces validator. În harness-ul de test, acea țintă este Mustang, invocat prin jar-ul său de linie de comandă, cu veraPDF rulat în aceeași trecere peste același fișier. Cablajul este mic: un generator de consolă, EInvoiceValidation.dpr, scrie cele douăsprezece fișiere folosind modelul de factură partajat din eșantion, iar un script, run-validation.ps1, conduce ambii validatori peste directorul de ieșire și afișează un tabel de trecere și eșec. Aceeași formă în doi pași, generare cu biblioteca și verificare cu validatori externi, este ceea ce un job de integrare continuă ar trebui să ruleze la fiecare modificare a generării de facturi, deoarece singurul mod de a ști că un fișier satisface ambele straturi este să întrebați ambele instrumente
Dacă pipeline-ul dvs. trebuie să certifice și containerul înainte de semnare, partea de preflight a acestei lucrări este acoperită în ghidul nostru privind preflight-ul PDF/A și PDF/UA în Delphi, iar fluxul mai larg de certificare-apoi-semnare este descris în workbench-ul de conformitate și semnare. Ambele se bazează pe același drum de generare care este livrat ca parte a Delphi PDF Library pentru Delphi și C++Builder, alături de API-urile PDF/A, fișiere asociate și metadate utilizate aici