Reikalavimus atitinkanti elektroninė sąskaita faktūra nėra PDF failas, prie kurio šono prisegtas XML failas. Tai yra vienas PDF/A-3 dokumentas, kuris neša sąskaitą faktūrą du kartus: vieną kartą kaip puslapį, kurį skaito žmogus, ir antrą kartą kaip mašininiu būdu nuskaitomą „Cross Industry Invoice“ XML, saugomą failo viduje kaip susietą failą (associated file). Dvi reprezentacijos apibūdina tą pačią sąskaitą faktūrą. Ši dviguba prigimtis yra pagrindinė formatų šeimų, kurių dabar reikalauja Europos mandatai, esmė: Factur-X Prancūzijoje ir Vokietijoje, ZUGFeRD visose vokiškai kalbančiose rinkose ir XRechnung Vokietijos viešojo sektoriaus atsiskaitymams. Šiame straipsnyje apžvelgiama, kaip PDFlibPas surenka tokią hibridinę sąskaitą faktūrą Delphi aplinkoje, kur standartai palieka vietos klaidoms, ir kodėl vienam kataloge esančiam profiliui reikia visiškai atskiro XML kūrėjo (builder)
Kas iš tikrųjų yra hibridinė sąskaita faktūra
Matomas puslapis ir įterptas XML tarnauja skirtingiems skaitytojams. Tarnautojas, tvirtinantis mokėjimą, žiūri į atvaizduotą (rendered) puslapį. Mokėtinų sumų sistema (accounts-payable system) sugeria (ingests) XML, nuskaito sumas ir mokesčių išklotinę kaip struktūrizuotus laukus ir užregistruoja įrašą žmogui nieko neįvedant. Šio XML semantinį turinį reglamentuoja EN 16931 – Europos standartas, apibrėžiantis sąskaitos faktūros duomenų modelį: kokie laukai egzistuoja, ką jie reiškia ir kurie iš jų yra privalomi. EN 16931 yra semantinis modelis, o ne failo formatas. Factur-X, ZUGFeRD 2.x ir XRechnung visi realizuoja šį modelį kaip UN/CEFACT „Cross Industry Invoice“ dokumentą – sintaksę, kuri perduoda EN 16931 laukus tinklu (on the wire)
Kad dokumentas būtų archyvuojamas (archivable) ir save aprašantis (self-describing), jo konteineris yra PDF/A-3, kurį apibrėžia ISO 19005-3. PDF/A-3 yra atitikties lygis (conformance level), leidžiantis įterpti savavališkus failus, o būtent tuo ir turi būti sąskaitos faktūros XML. PDF/A-2 draudžia įterpti failus, kurie patys nėra PDF/A, todėl Factur-X sąskaita faktūra negali būti PDF/A-2. Todėl PDF/A-3 pasirinkimas nėra pirmenybės klausimas – tai reikalavimas, tiesiogiai išplaukiantis iš noro įterpti ne PDF duomenis į archyvinį dokumentą
Kodėl ryšys (relationship) yra Alternative
Baitų įterpimas yra lengvoji dalis. ISO 32000 §7.11.4 apibrėžia įterpto failo srautą (embedded file stream) – objektą, kuriame saugomas neapdorotas XML ir jo parametrai. Dalis, kuri paverčia failą galiojančiu susietu failu, yra §14.13, kuri prideda susieto failo (associated file) sąvoką ir /AFRelationship raktą. Šis raktas nurodo, kaip įterpti duomenys susiję su turiniu, prie kurio jie prijungti, o vertė, kurios reikalauja Factur-X, yra Alternative
Pasirinkimas yra svarbus, nes kitos reikšmės patvirtintų ką nors klaidingo apie dokumentą. Source reikštų, kad XML yra medžiaga, iš kurios buvo sugeneruotas matomas turinys – originalas (master), iš kurio kildinamas puslapis. Supplement reikštų, kad XML prideda informacijos, kurios nėra rodomame puslapyje, – papildymą, kurio nėra atvaizdavime. Nė vienas iš šių teiginių neatitinka to, kas yra Factur-X sąskaita faktūra. XML ir puslapis yra dvi lygiavertės vienos sąskaitos faktūros išraiškos, nešančios tą patį teisinį turinį dviem formomis. Alternative yra reikšmė, sakanti būtent tai: lygiavertė alternatyvi matomo turinio reprezentacija. Validatorius, nuskaitęs bet kokį kitą ryšį Factur-X faile, jį atmes, ir teisingai, nes ryšys (relationship) yra mašininiu būdu nuskaitomas teiginys apie tai, kam skirtas priedas (attachment)
Profilių katalogas
„E-Invoice“ pavyzdys, pateikiamas kartu su PDFlibPas, naudoja tą patį generavimo kelią šešiems profiliams, apibrėžtiems kaip įrašų (records) masyvas faile InvoiceModel.pas. Kiekvienas profilis turi reikšmes, kurių reikia rašytojui (writer): rodomą pavadinimą (display name), įterpto failo pavadinimą, atitikties lygį, /AFRelationship, versiją, neprivalomą šalies kodą ir GuidelineID URN, kurį XML paskelbia savo dokumento kontekste
Šie šeši yra: Factur-X EN16931, Factur-X BASIC, Factur-X EXTENDED Prancūzijai, XRechnung 3.0, ZUGFeRD 1.0 COMFORT ir ZUGFeRD 2.0 BASIC. GuidelineID yra laukas, nurodantis gavėjui, kokio tiksliai profilio tikėtis, o reikšmės yra specifinės. Factur-X EN16931 skelbia urn:cen.eu:en16931:2017. XRechnung 3.0 skelbia urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. ZUGFeRD 2.0 BASIC skelbia urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. Įterpto failo pavadinimas taip pat yra sutarties (contract) dalis. Factur-X profiliai įterpia factur-x.xml, XRechnung įterpia xrechnung.xml, o ZUGFeRD profiliai įterpia ZUGFeRD-invoice.xml arba zugferd-invoice.xml. Gavėjas nuskaito priedų (attachments) pavadinimus, kad rastų sąskaitą faktūrą, todėl failo pavadinimas nėra tik kosmetinis
Vieną detalę kataloge verta atidžiai perskaityti. Dauguma profilių naudoja Alternative ryšį (relationship), tačiau XRechnung 3.0 įrašas pavyzdyje nausoja Source. Šie du formatai atsako skirtingiems validatoriams ir konvencijoms, o pavyzdys nustato kiekvieno profilio ryšį iš katalogo, užuot „kietai“ užkodavęs (hard-coding) vieną reikšmę – būtent todėl ir egzistuoja atskiras profilio laukas, o ne konstanta
ZUGFeRD 1.0 spąstai (trap)
Kyla pagunda daryti prielaidą, kad kiekvienas profilis yra EN 16931 „Cross Industry Invoice“ su nedideliais skirtumais, kiek neprivalomų laukų jūs užpildote. Tai galioja penkiems iš šešių. Tai negalioja ZUGFeRD 1.0 COMFORT profiliui, ir to priežastis yra labiau struktūrinė nei kosmetinė
Šiuolaikiniai profiliai išveda UN/CEFACT „Cross Industry Invoice“ su vardų srities (namespace) versija :100, kurio šakninis elementas (root element) yra rsm:CrossIndustryInvoice. ZUGFeRD 1.0 atsirado anksčiau už šią schemą. Tai yra 2014 m. „CrossIndustryDocument“ su vardų srities versija :1p0, o jos šakninis elementas yra rsm:CrossIndustryDocument. Vardų sričių URN skiriasi, šakninis elementas skiriasi ir elementų medis skiriasi visoje struktūroje: :1p0 schema grupuoja duomenis po ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery ir ApplicableSupplyChainTradeSettlement, kai tuo tarpu :100 naudoja ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery ir ApplicableHeaderTradeSettlement. Pavadinimai yra pakankamai panašūs, kad klaidintų, ir pakankamai skirtingi, kad viską sugadintų (break)
Žodis COMFORT profilio pavadinime apibūdina, kokie turtingi yra duomenys – automatizavimo lygio profilis su visomis eilutėmis (line items), mokesčių išklotine ir mokėjimo sąlygomis (payment terms), o ne tai, kuri schema juos neša. Todėl negalite paimti :100 dokumento ir tiesiog pakeisti jo etiketės į ZUGFeRD 1.0. Pavyzdys tai išsprendžia naudodamas žymą (flag) kiekviename profilio įraše ir dvi atskiras kūrimo funkcijas (builder functions), pasirenkant teisingą prieš sugeneruojant bet kokį XML
function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
const Data: TInvoiceData): string;
begin
// XMLFamily = 1 means the legacy ZUGFeRD 1.0 :1p0 schema; every
// other profile is the modern UN/CEFACT :100 Cross Industry Invoice.
if AProfile.XMLFamily = 1 then
Result := BuildZUGFeRD1Text(AProfile, Data)
else
Result := BuildCII100Text(AProfile, Data);
end;
Šis atskyrimas nėra tik realizacijos grožis (implementation nicety). Pateikus :100 medį ZUGFeRD 1.0 gavėjui, sukuriamas dokumentas, kurio schemos validavimas žlunga (fails) ties šakniniu elementu, todėl abi šeimos turi būti kuriamos kodo, kuris žino, kurią iš jų jis rašo
PDF/A-3 lygio pasirinkimas
PDF/A-3 turi tris atitikties lygius (conformance levels), ir PDFlibPas pasirenka juos per SetPDFAMode. 5 režimas yra PDF/A-3b – lygis, kuris garantuoja patikimą vizualų atkūrimą. 6 režimas yra PDF/A-3a, kuris prideda lygio „a“ žymėtos struktūros (tagged-structure) ir prieinamumo (accessibility) reikalavimus. 7 režimas yra PDF/A-3u, kuris reikalauja, kad visas tekstas būtų atvaizduotas į Unicode. Režimo įjungimas taip pat įterpia bibliotekoje integruotą sRGB output intent (išvesties ketinimą) – spalvų charakteristiką, kurios reikalauja PDF/A, kad atvaizduojama spalva būtų apibrėžta, o ne priklausoma nuo įrenginio (device-dependent)
Dauguma sąskaitų faktūrų srautų veikia 3b lygiu, kurio pakanka ištikimam matomam puslapiui plius įterptam XML. Jei jums reikia aiškaus (explicit) ICC profilio, o ne integruoto, LoadOutputIntentProfile jį pakeičia po to, kai nustatomas režimas. Pavyzdys tokiu būdu įkelia saugyklos (repository) sRGB profilį ir grįžta prie integruoto išvesties ketinimo (built-in intent), kai failas nepasiekiamas, todėl output intent visada yra
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');
// Optional: swap the built-in sRGB intent for an explicit ICC profile.
if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
{ fall back to the built-in sRGB intent that SetPDFAMode embedded };
finally
// ... continue building the document
end;
Hibridinės sąskaitos faktūros kūrimas
Sukonfigūravus konteinerį, likę dalykai susiveda į tris žingsnius eilės tvarka: nustatyti PDF/A-3 režimą, nupiešti žmogui skaitomą puslapį, tada pridėti XML kaip susietą failą (associated file). Matomas puslapis yra paprastas turinys. Vienintelis apribojimas, kurį verta prisiminti, yra tas, kad PDF/A draudžia neįterptus standartinius 14 šriftų (Standard 14 fonts), todėl puslapyje turi būti įterptas tikras šriftas (real font face), o ne nuoroda į integruotą
Priedas (attachment) atliekamas vienu iškvietimu. AddFacturXAssociatedFileFromString paima neapdorotus UTF-8 XML baitus ir profilio metaduomenis, įrašo įterpto failo srautą, užregistruoja jį katalogo /AF masyve, kurio reikalauja PDF/A-3, pritaiko /AFRelationship ir sugeneruoja XMP elektroninės sąskaitos faktūros metaduomenis, kurie identifikuoja dokumentą kaip Factur-X, ZUGFeRD arba XRechnung. Ji taip pat patikrina, ar XML gairių (guideline) ID atitinka atitikties lygį, kurio prašėte, todėl neatitikimas tarp jūsų sukurto XML ir nurodyto profilio yra pagaunamas, užuot tyliai išsiųstas
// 1. PDF/A-3 mode and output intent are already set.
// 2. Draw the visible page (embeds a real TrueType font).
DrawInvoicePage(PDF, AProfile, Data);
// 3. Build the profile-correct XML and attach it as an
// associated file with /AFRelationship = Alternative.
InvoiceXML := BuildInvoiceXML(AProfile, Data); // AnsiString of UTF-8 bytes
FileID := PDF.AddFacturXAssociatedFileFromString(
InvoiceXML,
AProfile.ConformanceLevel, // e.g. 'EN16931'
AProfile.FileName, // 'factur-x.xml'
AProfile.Description,
AProfile.Relationship, // 'Alternative'
AProfile.Version, // '1.0'
AProfile.CountryCode); // '' or 'DE' or 'FR'
if FileID <= 0 then
raise Exception.Create('Invoice XML could not be attached');
PDF.SaveToFile(TargetFile);
Viena subtilybė duomenų kelyje (data path) yra koduotė (encoding). Įterptas XML deklaruoja encoding="UTF-8", o metodas priima jo baitus kaip AnsiString, todėl pardavėjo arba pirkėjo vardas, turintis ne ASCII simbolių, turi pasiekti iškvietimą kaip neapdoroti (raw) UTF-8 oktetai (octets). Paprastas duomenų tipo konvertavimas (cast) per sistemos ANSI kodų puslapį (code page) sugadintų tuos simbolius ir tyliai sukurtų sąskaitą faktūrą, kurios XML nebeatitinka savo paties deklaracijos. Pavyzdys aiškiai (explicitly) užkoduoja į UTF-8 prieš perduodamas baitus, o tai yra saugus būdas pamaitinti bet kokią į baitus orientuotą PDF API iš Unicode string (eilutės)
XML, kuris nėra pripažintas elektroninės sąskaitos faktūros profilis, įterpimui skirtas bendresnis analogas (generic counterpart) yra AddPDFA3AssociatedFileFromString. Ji priima failo pavadinimą, MIME tipą, aprašymą, ryšį (relationship) ir baitus, ir įrašo paprastą PDF/A-3 susietą failą be jokių su sąskaitomis faktūromis susijusių metaduomenų ar gairių (guideline) patikrų. Naudokite jį papildomiems duomenims; sąskaitoms faktūroms naudokite Factur-X metodą, kad profilio metaduomenys ir gairių atitiktis būtų įrašyti už jus
Sukūrus dokumentą, kiti klausimai yra tie, ar jis praeina PDF/A ir prieinamumo validavimą (accessibility validation), ir ar jį galima pasirašyti nesugriaunant atitikties (compliance). Šie dalykai aprašyti PDF/A ir PDF/UA preflight Delphi aplinkoje peržiūroje bei atitikties ir pasirašymo darbo aplinkoje (workbench). Visa tai pateikiama kaip PDFlibPas Delphi PDF Library dalis, kartu su PDF/A, žymėjimo (tagging) ir dokumento savybių (document-property) API, kuriais remiasi elektroninių sąskaitų faktūrų kelias