En regelkonform elektronisk faktura er ikke en PDF med en XML-fil hæftet på siden. Det er ét PDF/A-3-dokument, som bærer fakturaen to gange: én gang som en side, et menneske læser, og én gang som en maskinlæsbar Cross Industry Invoice-XML, der er gemt inde i filen som en associated file. De to repræsentationer beskriver den samme faktura. Den dobbelte natur er hele pointen med de formatfamilier, som europæiske mandater nu kræver, Factur-X i Frankrig og Tyskland, ZUGFeRD på tværs af tysktalende markeder og XRechnung til tysk offentlig fakturering. Denne artikel gennemgår, hvordan PDF Library for Delphi samler en sådan hybridfaktura i Delphi, hvor standarderne efterlader plads til at gøre det forkert, og hvorfor én profil i kataloget har brug for en helt separat XML-builder
Hvad en hybridfaktura egentlig er
Den synlige side og den indlejrede XML tjener forskellige læsere. En medarbejder, der godkender en betaling, ser på den gengivne side. Et kreditorsystem indlæser XML'en, læser totaler og momsopdeling som strukturerede felter og bogfører posten, uden at et menneske taster noget. Det semantiske indhold af den XML styres af EN 16931, den europæiske standard, der definerer fakturadatamodellen: hvilke felter der findes, hvad de betyder, og hvilke der er obligatoriske. EN 16931 er en semantisk model, ikke et filformat. Factur-X, ZUGFeRD 2.x og XRechnung realiserer alle den model som et UN/CEFACT Cross Industry Invoice-dokument, den syntaks der bærer EN 16931-felterne over ledningen
For at dokumentet kan være både arkiverbart og selvbeskrivende, er containeren PDF/A-3, defineret af ISO 19005-3. PDF/A-3 er det konformitetsniveau, der tillader vilkårlige indlejrede filer, hvilket er præcis det, en faktura-XML skal være. PDF/A-2 forbyder indlejring af filer, der ikke selv er PDF/A, så en Factur-X-faktura kan ikke være PDF/A-2. Valget af PDF/A-3 er derfor ikke en præference, det er et krav, der følger direkte af ønsket om at indlejre ikke-PDF-data i et arkivdokument
Hvorfor relationen er Alternative
At indlejre bytes er den lette del. ISO 32000 §7.11.4 definerer embedded file stream, det objekt der rummer den rå XML og dens parametre. Den del, der gør filen til en gyldig associated file, er §14.13, som tilføjer begrebet associated file og nøglen /AFRelationship. Den nøgle angiver, hvordan de indlejrede data forholder sig til det indhold, de er knyttet til, og den værdi, Factur-X foreskriver, er Alternative
Valget betyder noget, fordi de andre værdier ville hævde noget usandt om dokumentet. Source ville betyde, at XML'en er det materiale, det synlige indhold blev genereret ud fra, en master som siden er afledt af. Supplement ville betyde, at XML'en tilføjer oplysninger ud over det, siden viser, et tillæg der ikke er indeholdt i gengivelsen. Ingen af delene er det, en Factur-X-faktura er. XML'en og siden er to ligeværdige udtryk for én faktura, der bærer det samme juridiske indhold i to former. Alternative er den værdi, der siger præcis det: en ligeværdig alternativ repræsentation af det synlige indhold. En validator, der læser en hvilken som helst anden relation på en Factur-X-fil, vil afvise den, og med rette, fordi relationen er en maskinlæsbar påstand om, hvad vedhæftningen er til
Profilkataloget
E-Invoice-eksemplet, der leveres med PDF Library for Delphi, driver den samme genereringssti på tværs af seks profiler, defineret som et array af records i InvoiceModel.pas. Hver profil bærer de værdier, writeren har brug for: et visningsnavn, den indlejrede fils navn, et konformitetsniveau, /AFRelationship, en version, en valgfri landekode og den GuidelineID-URN, som XML'en annoncerer inde i sin dokumentkontekst
De seks er Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED for Frankrig, XRechnung 3.0, ZUGFeRD 1.0 COMFORT og ZUGFeRD 2.0 BASIC. GuidelineID er det felt, der fortæller en modtager præcis, hvilken profil der skal forventes, og værdierne er specifikke. Factur-X EN16931 annoncerer urn:cen.eu:en16931:2017. XRechnung 3.0 annoncerer urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC annoncerer urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Den indlejrede fils navn er også en del af kontrakten. Factur-X-profiler indlejrer factur-x.xml, XRechnung indlejrer xrechnung.xml, og ZUGFeRD-profilerne indlejrer ZUGFeRD-invoice.xml eller zugferd-invoice.xml. En modtager scanner vedhæftningsnavnene for at finde fakturaen, så filnavnet er ikke kosmetisk
Én detalje i kataloget er værd at læse omhyggeligt. De fleste profiler bruger relationen Alternative, men XRechnung 3.0-posten i eksemplet bruger Source. De to formater står til ansvar over for forskellige validatorer og konventioner, og eksemplet sætter hver profils relation ud fra kataloget i stedet for at hardkode én enkelt værdi, hvilket er grunden til, at feltet findes pr. profil frem for som en konstant
ZUGFeRD 1.0-fælden
Det er fristende at antage, at hver profil er EN 16931 Cross Industry Invoice med mindre variationer i, hvor mange valgfrie felter du udfylder. Det gælder for fem af de seks. Det gælder ikke for ZUGFeRD 1.0 COMFORT, og grunden er strukturel snarere end kosmetisk
De moderne profiler udsender en UN/CEFACT Cross Industry Invoice med namespace-version :100, hvis rodelement er rsm:CrossIndustryInvoice. ZUGFeRD 1.0 er ældre end det skema. Det er CrossIndustryDocument fra 2014 med namespace-version :1p0, og dets rodelement er rsm:CrossIndustryDocument. Namespace-URN'erne er forskellige, rodelementet er forskelligt, og elementtræet er forskelligt hele vejen igennem: :1p0-skemaet grupperer data under ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery og ApplicableSupplyChainTradeSettlement, hvor :100 bruger ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery og ApplicableHeaderTradeSettlement. Navngivningen ligner nok til at vildlede og afviger nok til at bryde
Ordet COMFORT i profilnavnet beskriver, hvor rige dataene er, en profil i automatiseringskvalitet med fulde linjeposter, momsopdeling og betalingsbetingelser, ikke hvilket skema der bærer dem. Så du kan ikke tage et :100-dokument og ommærke det til ZUGFeRD 1.0. Eksemplet håndterer dette med et flag på hver profil-record og to separate builder-funktioner og vælger den rigtige, før der genereres nogen XML
function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
const Data: TInvoiceData): string;
begin
// XMLFamily = 1 betyder det ældre ZUGFeRD 1.0 :1p0-skema; enhver
// anden profil er den moderne UN/CEFACT :100 Cross Industry Invoice.
if AProfile.XMLFamily = 1 then
Result := BuildZUGFeRD1Text(AProfile, Data)
else
Result := BuildCII100Text(AProfile, Data);
end;
Opdelingen er ikke en implementeringsfinesse. At fodre en ZUGFeRD 1.0-modtager med et :100-træ giver et dokument, der dumper skemavalideringen allerede ved rodelementet, så de to familier skal bygges af kode, der ved, hvilken den skriver
Valg af PDF/A-3-niveau
PDF/A-3 har tre konformitetsniveauer, og PDF Library for Delphi vælger dem gennem SetPDFAMode. Mode 5 er PDF/A-3b, det niveau der garanterer pålidelig visuel reproduktion. Mode 6 er PDF/A-3a, som tilføjer niveau a's krav om mærket struktur og tilgængelighed. Mode 7 er PDF/A-3u, som kræver, at al tekst er mappet til Unicode. At aktivere tilstanden indlejrer også bibliotekets indbyggede sRGB-output intent, den farvekarakterisering, PDF/A kræver, så gengivet farve er defineret frem for enhedsafhængig
De fleste fakturaflows kører på 3b, hvilket er tilstrækkeligt til en trofast synlig side plus den indlejrede XML. Hvis du har brug for en eksplicit ICC-profil frem for den indbyggede, bytter LoadOutputIntentProfile den ind, efter tilstanden er sat. Eksemplet indlæser repositoriets sRGB-profil på den måde og falder tilbage til den indbyggede intent, når filen ikke kan nås, så output intent altid er til stede
PDF := TPDFlib.Create;
try
// Mode 5 = PDF/A-3b, 6 = PDF/A-3a, 7 = PDF/A-3u.
if PDF.SetPDFAMode(5) <> 1 then
raise Exception.Create('PDF/A-3 mode could not be enabled');
// Valgfrit: byt den indbyggede sRGB-intent ud med en eksplicit ICC-profil.
if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
{ fald tilbage til den indbyggede sRGB-intent, som SetPDFAMode indlejrede };
finally
// ... fortsæt med at bygge dokumentet
end;
Opbygning af hybridfakturaen
Med containeren konfigureret er resten tre trin i rækkefølge: sæt PDF/A-3-tilstanden, tegn den menneskelæsbare side, og vedhæft derefter XML'en som en associated file. Den synlige side er almindeligt indhold. Den ene begrænsning, der er værd at huske, er, at PDF/A forbyder de ikke-indlejrede Standard 14-skrifttyper, så siden skal indlejre en rigtig skrifttype frem for at referere til en indbygget
Vedhæftningen er ét enkelt kald. AddFacturXAssociatedFileFromString tager de rå UTF-8-XML-bytes plus profilmetadataene, skriver embedded file stream, registrerer den i katalogets /AF-array, som PDF/A-3 kræver, anvender /AFRelationship og genererer de XMP-e-fakturametadata, der identificerer dokumentet som Factur-X, ZUGFeRD eller XRechnung. Den kontrollerer også, at XML'ens guideline-id matcher det konformitetsniveau, du bad om, så et misforhold mellem den XML, du byggede, og den profil, du navngav, fanges i stedet for at blive leveret i stilhed
// 1. PDF/A-3-tilstand og output intent er allerede sat.
// 2. Tegn den synlige side (indlejrer en rigtig TrueType-skrifttype).
DrawInvoicePage(PDF, AProfile, Data);
// 3. Byg den profilkorrekte XML og vedhæft den som en
// associated file med /AFRelationship = Alternative.
InvoiceXML := BuildInvoiceXML(AProfile, Data); // AnsiString af UTF-8-bytes
FileID := PDF.AddFacturXAssociatedFileFromString(
InvoiceXML,
AProfile.ConformanceLevel, // f.eks. 'EN16931'
AProfile.FileName, // 'factur-x.xml'
AProfile.Description,
AProfile.Relationship, // 'Alternative'
AProfile.Version, // '1.0'
AProfile.CountryCode); // '' eller 'DE' eller 'FR'
if FileID <= 0 then
raise Exception.Create('Invoice XML could not be attached');
PDF.SaveToFile(TargetFile);
En finesse i datastien er kodningen. Den indlejrede XML deklarerer encoding="UTF-8", og metoden tager sine bytes som en AnsiString, så et sælger- eller købernavn med ikke-ASCII-tegn skal nå kaldet som rå UTF-8-oktetter. En simpel cast gennem systemets ANSI-tegntabel ville ødelægge de tegn og i stilhed producere en faktura, hvis XML ikke længere matcher sin egen deklaration. Eksemplet koder eksplicit til UTF-8, før det overdrager bytes, hvilket er den sikre måde at fodre enhver byteorienteret PDF-API fra en Unicode-string
Til vedhæftning af XML, der ikke er en anerkendt e-fakturaprofil, er AddPDFA3AssociatedFileFromString det generiske modstykke. Den tager et filnavn, en MIME-type, en beskrivelse, en relation og bytes og skriver en almindelig PDF/A-3 associated file uden fakturaspecifikke metadata eller guideline-kontroller. Brug den til supplerende data; brug Factur-X-metoden til fakturaer, så profilmetadataene og guideline-matchet skrives for dig
Når dokumentet er produceret, er de næste spørgsmål, om det består PDF/A- og tilgængelighedsvalidering, og om det kan signeres uden at bryde overholdelsen. Det er dækket i gennemgangen af PDF/A- og PDF/UA-preflight og compliance- og signeringsarbejdsbænken. Alt dette leveres som en del af PDF Library for Delphi Delphi PDF Library, sammen med de PDF/A-, mærknings- og dokumentegenskabs-API'er, som e-fakturastien bygger på