Teknisk artikel

Factur-X- og ZUGFeRD-hybridfakturaer i Delphi

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

Anatomien af en Factur-X-hybridfaktura bygget med PDF Library for Delphi: ét PDF/A-3-dokument rummer den menneskelæsbare side og en indlejret factur-x.xml forbundet via AFRelationship Alternative under EN 16931-modellen
Én PDF/A-3-fil bærer fakturaen to gange som ligeværdige repræsentationer, og kun /AFRelationship = Alternative fremsætter den påstand på en måde, validatorer accepterer

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

Profilkatalogdiagram af PDF Library for Delphi, der router seks Factur-X-, ZUGFeRD- og XRechnung-e-fakturaprofiler gennem BuildInvoiceXMLText ind i den moderne UN/CEFACT Cross Industry Invoice-familie og den ældre ZUGFeRD 1.0 :1p0-familie
De seks profiler deler ét dispatch-punkt, men ZUGFeRD 1.0 COMFORT bryder ud i det ældre :1p0-skema og har brug for sin egen XML-builder
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

Byggepipeline-diagram til samling af en Factur-X- eller ZUGFeRD-hybridfaktura med PDF Library for Delphi: SetPDFAMode konfigurerer PDF/A-3-containeren, den synlige side tegnes, AddFacturXAssociatedFileFromString vedhæfter faktura-XML'en, og SaveToFile udgiver derefter arkivdokumentet
Samlingen kører i tre ordnede trin før lagring, med konformitetsniveauer, bytesikker UTF-8-håndtering og den generiske associated-file-API som sidebemærkninger
// 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å