En Factur-X eller ZUGFeRD-faktura er to dokumenter iført ét filnavn. Det ydre dokument er en PDF/A-3-container, som en arkivlæser skal acceptere de næste ti år. Det indre dokument er en XML-faktura, som en købers regnskabssystem skal parse i forhold til EN 16931. Den fejltagelse, der sender ødelagte fakturaer i produktion, er at tro, at hvis man får den første rigtig, får man den anden gratis. Det gør man ikke. En fil kan være en fejlfri PDF/A-3 og stadig bære XML, som ingen skattemyndighed vil acceptere, og den kan bære lærebogs-EN 16931-XML inde i en container, der dumper arkivvalidering. De to lag valideres af to forskellige værktøjer, der intet ved om hinanden, og en rigtig pipeline skal tilfredsstille begge
To validatorer, to forskellige spørgsmål
veraPDF er referenceimplementeringen for PDF/A. Peg det mod en faktura, og det besvarer ét spørgsmål: er dette en overensstemmende PDF/A-3-fil. Den tjekker de ting, ISO 19005-3 bekymrer sig om. Er enhver skrifttype indlejret. Er der en OutputIntent. Deklarerer XMP-metadataene den rigtige del og overensstemmelsesniveau. For en e-faktura tjekker den også associated-file-mekanikken, som PDF/A-3 kræver, fordi XML'en kører med som en indlejret fil med et /AFRelationship og en post i dokumentkatalogets /AF-array. veraPDF siger intet om, hvorvidt fakturatotalen går op, fordi det ikke er inden for dens ansvarsområde
Mustang er open-source-validatoren fra Mustangproject. Den stiller det ortogonale spørgsmål: er den indlejrede XML en gyldig faktura. Den kører XML'en mod skemaet for den deklarerede profil og anvender derefter EN 16931-forretningsreglerne samt de landespecifikke regelsæt, der er lagt ovenpå, XRechnungs CIUS iblandt dem. Den tjekker, at et sælger-momsnummer er til stede, når totalerne kræver et, at rabat- og gebyrbeløb stemmer overens med dokumenttotalen, at profil-URN'en i XML'en matcher, hvad filen hævder at være. Mustang er ligeglad med, om den omgivende PDF indlejrer sine skrifttyper, for det er veraPDFs job
Intet af værktøjerne er en overmængde af det andet. veraPDF godkender en strukturelt perfekt container omkring nonsens-XML. Mustang godkender perfekt XML indpakket i en container med en manglende OutputIntent. Hver fanger præcis den klasse af fejl, den anden er blind over for, hvilket er hele grunden til, at en seriøs valideringsharness kører begge og behandler en fil som klar til forsendelse, kun når begge er enige
Valideringsmatricen
For at bevise, at biblioteket producerer filer, der overlever begge porte, bygger harness'en en matrix. Seks fakturaprofiler dækker det spænd, en europæisk pipeline møder i praksis: Factur-X EN 16931, Factur-X BASIC, Factur-X EXTENDED France B2B-varianten, XRechnung 3.0, ZUGFeRD 1.0 COMFORT og ZUGFeRD 2.0 BASIC. Hver profil genereres mod to PDF/A-underoverensstemmelsesniveauer, 3b og 3u, fordi B-niveauets og U-niveauets krav afviger på Unicode-mapping, og en fil, der består den ene, kan dumpe den anden. Seks profiler gange to niveauer er tolv filer, hver og en bygget headless af den samme kodesti, som GUI-eksemplet leverer, så artefakterne under test er ikke håndtunet til testen
Generatoren skriver alle tolv, og et script fodrer hver enkelt til begge validatorer. På den første fulde kørsel godkendte veraPDF alle tolv. Containermekanikken var korrekt over hele linjen: tilknyttede filer registreret, XMP-overensstemmelse deklareret, output-intents på plads. Mustang godkendte otte. Fire fakturaer var strukturelt gyldige PDF/A-3-filer, der bar XML, som forretningsregel-validatoren afviste, hvilket er præcis den opdeling, to-værktøjs-tilgangen eksisterer for at få frem i lyset. Havde harness'en stolet på veraPDF alene, ville de fire have set færdige ud
De to rettelser der lukkede hullet
De fire Mustang-fejl stammede fra to distinkte årsager, og rettelsen for hver er en detalje, det er værd at kende, før du genererer disse profiler selv
Den første var Factur-X EXTENDED France B2B-profilen. Den originale generator videregav en intern etiket som overensstemmelsesniveauet og en intern URN som retningslinjen, og Mustang afviste filen med en ugyldig-overensstemmelsesværdi-fejl efterfulgt af en ikke-understøttet-profiltype-fejl. Årsagen er, at XMP-feltet fx:ConformanceLevel ikke er en fritekstplads til din egen profilnavngivning. Factur-X definerer præcis fem standardværdier for det: MINIMUM, BASIC WL, BASIC, EN 16931 og EXTENDED. En Frankrig-specifik B2B-faktura er stadig et EXTENDED-profil-dokument, for så vidt angår XMP-metadataene. Fakturaens franske karakter udtrykkes ikke ved at opfinde en sjette overensstemmelsesværdi. Den udtrykkes ved landekoden, FR, og ved retningslinje-identifikatoren inde i XML'en, som skal bære urn:cen.eu:en16931:2017#conformant#-præfikset, der markerer en CIUS konform med EN 16931. At videregive den standard EXTENDED-værdi med FR som landekode og den korrekte retningslinje-URN gjorde filen konform
I biblioteks-API'en er det et kald til AddFacturXAssociatedFileFromString med overensstemmelse, land og retningslinje afstemt. Overensstemmelsesniveau-argumentet bærer standardtokenet, landekode-argumentet bærer FR, og retningslinje-URN'en bor i de XML-bytes, du sender ind
Den anden årsag var ZUGFeRD 1.0 COMFORT-profilen, og den havde intet at gøre med metadata. ZUGFeRD 1.0 valideres mod :1p0-XSD'en, som er strengere omkring kardinalitet, end prosasammenfatningerne antyder. XSD'en kræver, at headerafregningssummen, ram:SpecifiedTradeSettlementMonetarySummation, indeholder ram:ChargeTotalAmount og ram:AllowanceTotalAmount hver præcis én gang. Den genererede XML udelod begge, så Mustang rapporterede, at elementerne skal forekomme præcis én gang. Disse er ikke valgfrie, når skemaet siger minOccurs er en. At udsende begge i XSD-sekvensrækkefølge, umiddelbart efter ram:LineTotalAmount, med en værdi på 0.00, når der ikke er nogen gebyrer eller rabatter, tilfredsstillede skemaet. Et nul er et tilstedeværende element; et fraværende element er et brud på skemaet. Med disse to rettelser på plads gik matricen til tolv ud af tolv på Mustang, mens den forblev tolv ud af tolv på veraPDF
XRechnung-felterne der vender ugyldig til gyldig
XRechnung fortjener sin egen bemærkning, fordi dens tyske CIUS tilføjer forretningsregler, der er fraværende fra det grundlæggende EN 16931-sæt, og de fejler på måder, der ser ud som om intet er galt med dokumentet ved et øjekast. To af dem angår elektroniske adresser. BT-34 er sælgers elektroniske adresse, og BT-49 er købers elektroniske adresse, de routing-endepunkter en tysk offentlig sektor-portal bruger til at levere og anerkende fakturaen. Den grundlæggende EN 16931-model behandler dem som valgfrie. Det gør XRechnung ikke. Udelad en af dem, og fakturaen er velformet, skema-gyldig og afvist
Den tredje er regel BR-DE-6, som kræver, at sælgers kontakttelefonnummer er til stede. Det er den slags felt, en udvikler dropper, fordi det føles som præsentation snarere end data, og dets fravær producerer en valideringsfejl, der peger på sælgerkontaktgruppen snarere end på noget åbenlyst manglende. At levere BT-34, BT-49 og sælgers telefonnummer er, hvad der flytter en XRechnung-fil fra ugyldig til gyldig under Mustang, og intet af det ændrer noget veraPDF ser, fordi alle tre bor i XML'en
Forbindelse af biblioteksoutput til en validator
Det arkitektoniske punkt bag harness'en generaliserer til ethvert forretningssystem. PDF-biblioteket skriver en overensstemmende container og indlejrer XML'en. Det forsøger ikke, og bør ikke forsøge, at være EN 16931-forretningsregel-autoriteten. ValidateFacturXInvoice i biblioteket tjekker containerkonsistens, at katalogets /AF-array, de indlejrede filers navnetræ, XMP's DocumentFileName, profilen, retningslinjen og /AFRelationship alle er enige, men den validerer ikke momskoder eller afstemmer beløb. Den rigtige arbejdsdeling er for forretningssystemet at udtrække XML'en og overrække den til en dedikeret fakturavalidator, præcis ligesom harness'en overrækker den til Mustang
At læse filen tilbage fortæller dig, hvad der rent faktisk blev skrevet. DetectFacturXInvoice rapporterer, om en faktura blev genkendt, og GetFacturXInvoiceInfo læser metadatafelterne efter tag: tag 1 er det indlejrede filnavn, tag 2 XMP's DocumentFileName, tag 5 overensstemmelsesniveauet, tag 6 retningslinje-identifikatoren, og tag 7 /AFRelationship. At bekræfte, at det overensstemmelsesniveau, du læser tilbage, er standardtokenet og ikke en intern etiket, er den billigste måde at fange EXTENDED-fejlen på, før en fil forlader dit build
ExtractFacturXXMLToString returnerer de rå XML-bytes som en AnsiString, klar til at skrive til en fil eller streame ind i en validatorproces. I testharness'en er det mål Mustang, påkaldt gennem dens command-line jar, med veraPDF kørt i det samme pas over den samme fil. Opkoblingen er lille: en konsolgenerator, EInvoiceValidation.dpr, skriver de tolv filer ved hjælp af den delte fakturamodel fra eksemplet, og et script, run-validation.ps1, driver begge validatorer over output-mappen og udskriver en bestået- og dumpet-tabel. Den samme to-trins-form, generer med biblioteket og verificer med eksterne validatorer, er hvad et continuous-integration-job bør køre ved hver ændring til fakturagenerering, fordi den eneste måde at vide, om en fil tilfredsstiller begge lag, er at spørge begge værktøjer
Hvis din pipeline også skal certificere containeren før signering, er preflight-siden af dette arbejde dækket i vores gennemgang af PDF/A- og PDF/UA-preflight i Delphi, og det bredere certificer-så-signer-flow er beskrevet i overensstemmelses- og signerings-arbejdsbænken. Begge bygger på den samme genereringssti, der leveres som del af Delphi PDF Library til Delphi og C++Builder, ved siden af de PDF/A-, associated-file- og metadata-API'er, der bruges her