Factur-X- tai ZUGFeRD-lasku on kaksi asiakirjaa yhdellä tiedostonimellä. Ulompi asiakirja on PDF/A-3-säiliö, joka arkistolukijan on hyväksyttävä seuraavat kymmenen vuotta. Sisempi asiakirja on XML-lasku, joka ostajan kirjanpitojärjestelmän on jäsennettävä standardin EN 16931 mukaisesti. Virhe, joka vie rikkinäisiä laskuja tuotantoon, on uskoa, että ensimmäisen osan onnistuminen tuo toisen osan ilmaiseksi. Näin ei ole. Tiedosto voi olla virheetön PDF/A-3 ja silti sisältää XML-tiedostoja, joita mikään veroviranomainen ei hyväksy, ja se voi sisältää oppikirjamaista EN 16931 XML -tiedostoa säiliössä, joka epäonnistuu arkistointivalidoinnissa. Nämä kaksi tasoa validoidaan kahdella eri työkalulla, jotka eivät tiedä toisistaan mitään, ja todellisen putken on tyydytettävä molemmat
Kaksi validaattoria, kaksi eri kysymystä
veraPDF on PDF/A:n referenssitoteutus. Osoita se laskuun ja se vastaa yhteen kysymykseen: onko tämä yhteensopiva PDF/A-3-tiedosto. Se tarkistaa asiat, joista ISO 19005-3 välittää. Onko jokainen fontti upotettu. Onko OutputIntent olemassa. Ilmoittaako XMP-metatieto oikean osan ja vaatimustenmukaisuustason. Verkkolaskun osalta se tarkistaa myös PDF/A-3:n vaatiman associated-file-putkiston, koska XML kulkee mukana upotettuna tiedostona /AFRelationship-ominaisuudella ja merkinnällä asiakirjan luettelon /AF-taulukossa. veraPDF ei sano mitään siitä, täsmääkö laskun loppusumma, koska se ei kuulu sen tehtäviin
Mustang on Mustang-projektin avoimen lähdekoodin validaattori. Se kysyy ortogonaalista kysymystä: onko upotettu XML voimassa oleva lasku. Se ajaa XML:ää ilmoitetun profiilin skeemaa vastaan ja soveltaa sitten EN 16931 -liiketoimintasääntöjä ja niiden päälle kerrostettuja maakohtaisia sääntöjoukkoja, mukaan lukien XRechnungin CIUS. Se tarkistaa, että myyjän alv-tunniste on läsnä, kun loppusummat sitä vaativat, että alennus- ja veloitussummat täsmäävät asiakirjan kokonaissummaan, että XML-tiedoston profiilin URN vastaa sitä, mitä tiedosto väittää olevansa. Mustang ei välitä, onko ympäröivä PDF upottanut fonttinsa, koska se on veraPDF:n työ
Kumpikaan työkalu ei ole toisen superjoukko. veraPDF läpäisee rakenteellisesti täydellisen säilön hölynpöly-XML:n ympärillä. Mustang läpäisee täydellisen XML:n, joka on kääritty säilöön, josta puuttuu OutputIntent. Kumpikin havaitsee tarkalleen sen vikaluokan, jolle toinen on sokea, mikä on koko syy siihen, miksi vakava validointivaljaat käyttää molempia ja käsittelee tiedostoa lähetettävänä vasta, kun molemmat ovat yhtä mieltä
Validointimatriisi
Todistaakseen, että kirjasto tuottaa tiedostoja, jotka selviytyvät molemmista porteista, valjaat rakentaa matriisin. Kuusi laskuprofiilia kattaa sen laajuuden, jonka eurooppalainen putki kohtaa käytännössä: Factur-X EN 16931, Factur-X BASIC, Factur-X EXTENDED France B2B -muunnelma, XRechnung 3.0, ZUGFeRD 1.0 COMFORT ja ZUGFeRD 2.0 BASIC. Jokainen profiili luodaan kahta PDF/A-alivaatimustenmukaisuustasoa, 3b ja 3u, vastaan, koska tasojen B ja U vaatimukset eroavat Unicoden mäppäyksessä ja tiedosto, joka läpäisee yhden, voi epäonnistua toisessa. Kuusi profiilia kertaa kaksi tasoa on kaksitoista tiedostoa, jokainen niistä on rakennettu headless-tilassa samalla koodipolulla, jonka GUI-esimerkki toimittaa, joten testattavia artefakteja ei ole hienosäädetty testiä varten käsin
Generaattori kirjoittaa kaikki kaksitoista ja skripti syöttää jokaisen molemmille validaattoreille. Ensimmäisellä täydellä ajokerralla veraPDF läpäisi kaikki kaksitoista. Säilön putkisto oli oikein kaikkialla: associated files rekisteröity, XMP-vaatimustenmukaisuus ilmoitettu, OutputIntent paikallaan. Mustang läpäisi kahdeksan. Neljä laskua olivat rakenteellisesti päteviä PDF/A-3-tiedostoja, jotka kantoivat XML:ää, jonka liiketoimintasääntöjen validaattori hylkäsi, mikä on juuri se jakautuminen, jota kahden työkalun lähestymistapa pyrkii tuomaan pintaan. Jos valjaat olisivat luottaneet vain veraPDF:ään, nuo neljä olisivat näyttäneet valmiilta
Kaksi korjausta, jotka sulkivat aukon
Neljä Mustang-epäonnistumista johtuivat kahdesta eri syystä, ja kummankin korjaus on yksityiskohta, joka kannattaa tietää ennen kuin luot näitä profiileja itse
Ensimmäinen oli Factur-X EXTENDED France B2B -profiili. Alkuperäinen generaattori välitti sisäisen etiketin vaatimustenmukaisuustasona ja sisäisen URN:n ohjeistuksena, ja Mustang hylkäsi tiedoston invalid-conformance-value-virheellä, jota seurasi unsupported-profile-type-virhe. Syynä on se, että XMP fx:ConformanceLevel -kenttä ei ole vapaatekstipaikka omalle profiilisi nimeämiselle. Factur-X määrittelee sille tasan viisi vakioarvoa: MINIMUM, BASIC WL, BASIC, EN 16931 ja EXTENDED. Ranskan sisäinen B2B-lasku on edelleen EXTENDED-profiilin asiakirja siltä osin kuin XMP-metatiedot ovat kyseessä. Laskun ranskalaista luonnetta ei ilmaista keksimällä kuudes vaatimustenmukaisuusarvo. Se ilmaistaan maakoodilla FR, ja XML:n sisällä olevalla ohjeistuksen tunnisteella, jonka on kuljetettava urn:cen.eu:en16931:2017#conformant#-etuliitettä, joka merkitsee EN 16931 -standardin mukaista CIUS:ia. Vakio EXTENDED -arvon välittäminen siten, että FR on maakoodi ja oikea ohjeistuksen URN teki tiedostosta yhteensopivan
Kirjaston sovellusliittymässä tämä on kutsu kohteeseen AddFacturXAssociatedFileFromString, jossa vaatimustenmukaisuus, maa ja ohjeistus on kohdistettu. Vaatimustenmukaisuustaso -argumentti kuljettaa vakiotunnistetta, maakoodi-argumentti kuljettaa FR, ja ohjeistuksen URN on niissä XML-tavuissa, jotka välität sisään
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;
Toinen syy oli ZUGFeRD 1.0 COMFORT -profiili, ja sillä ei ollut mitään tekemistä metatietojen kanssa. ZUGFeRD 1.0 validoidaan :1p0 XSD:tä vastaan, joka on kardinaliteetin suhteen tiukempi kuin proosayhteenvedot antavat ymmärtää. XSD edellyttää, että ylätunnisteen selvityksen yhteenveto, ram:SpecifiedTradeSettlementMonetarySummation, sisältää ram:ChargeTotalAmount ja ram:AllowanceTotalAmount kummatkin tasan kerran. Luotu XML jätti molemmat pois, joten Mustang ilmoitti, että elementtien on esiinnyttävä tasan kerran. Nämä eivät ole valinnaisia, kun skeema sanoo minOccurs on yksi. Molempien lähettäminen XSD-sekvenssijärjestyksessä, välittömästi kohteen ram:LineTotalAmount jälkeen, arvolla 0.00, kun maksuja tai vähennyksiä ei ole, tyydytti skeeman. Nolla on läsnä oleva elementti; puuttuva elementti on skeeman vastainen. Kun nämä kaksi korjausta oli tehty, matriisi nousi Mustangissa kahteentoista kahdestatoista ja pysyi kahdessatoista kahdestatoista veraPDF:ssä
XRechnung-kentät, jotka muuttavat virheellisen kelvolliseksi
XRechnung ansaitsee oman huomautuksensa, koska sen saksalainen CIUS lisää liiketoimintasääntöjä, jotka puuttuvat perus EN 16931 -sarjasta, ja ne epäonnistuvat tavoilla, jotka näyttävät siltä, että asiakirjassa ei ole mitään vikaa ensi silmäyksellä. Kaksi niistä koskee sähköpostiosoitteita. BT-34 on myyjän sähköinen osoite ja BT-49 on ostajan sähköinen osoite, reitityspäätepisteet, joita Saksan julkisen sektorin portaali käyttää laskun toimittamiseen ja vahvistamiseen. Perus EN 16931 -malli käsittelee niitä valinnaisina. XRechnung ei. Jätä jompikumpi pois ja lasku on hyvin muotoiltu, skeeman mukainen ja hylätty
Kolmas on sääntö BR-DE-6, joka edellyttää myyjän yhteyspuhelinnumeron olevan läsnä. Se on eräänlainen kenttä, jonka kehittäjä pudottaa, koska se tuntuu enemmän esitystavalta kuin tiedolta, ja sen puuttuminen tuottaa vahvistusvirheen, joka osoittaa myyjän yhteysryhmään eikä mihinkään selvästi puuttuvaan. BT-34:n, BT-49:n ja myyjän puhelinnumeron toimittaminen siirtää XRechnung-tiedoston virheellisestä kelvolliseksi Mustangin alla, eikä mikään siitä muuta mitään, mitä veraPDF näkee, koska kaikki kolme elävät XML:ssä
Kirjaston lähdön johdotus validaattoriin
Valjaiden takana oleva arkkitehtoninen pointti voidaan yleistää mihin tahansa liiketoimintajärjestelmään. PDF-kirjasto kirjoittaa vaatimustenmukaisen säilön ja upottaa XML:n. Sen ei pidä, eikä sen tulisi yrittää olla EN 16931 -liiketoimintasääntöjen auktoriteetti. ValidateFacturXInvoice kirjastossa tarkistaa säiliön johdonmukaisuuden, että luettelo /AF array, upotettujen tiedostojen name tree, XMP DocumentFileName, profiili, ohjeistus ja /AFRelationship ovat kaikki samaa mieltä, mutta se ei validoi verokoodeja tai täsmäytä summia. Oikea työnjako on se, että liiketoimintajärjestelmä poimii XML:n ja luovuttaa sen erilliselle laskuvalidaattorille, täsmälleen samalla tavalla kuin valjaat luovuttavat sen Mustangille
Tiedoston lukeminen takaisin kertoo, mitä todella kirjoitettiin. DetectFacturXInvoice raportoi, tunnistettiinko lasku, ja GetFacturXInvoiceInfo lukee metatietokentät tunnisteen perusteella: tunniste 1 on upotetun tiedoston nimi, tunniste 2 on XMP DocumentFileName, tunniste 5 on vaatimustenmukaisuustaso, tunniste 6 on ohjeistuksen tunniste ja tunniste 7 on /AFRelationship. Vahvistamalla, että takaisin lukemasi vaatimustenmukaisuustaso on vakiotunniste eikä sisäinen tunniste, on halvin tapa saada kiinni EXTENDED-virheestä ennen kuin tiedosto lähtee koontiversiostasi
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 palauttaa raa'at XML-tavut muodossa AnsiString, valmiina kirjoitettavaksi tiedostoon tai suoratoistettavaksi validaattoriprosessiin. Testivaljaissa tämä kohde on Mustang, jota kutsutaan sen komentorivin jarin kautta, ja veraPDF suoritetaan samassa vaiheessa saman tiedoston yli. Johdotus on pieni: konsoligeneraattori, EInvoiceValidation.dpr, kirjoittaa kaksitoista tiedostoa käyttämällä jaettua laskumallia näytteestä, ja komentosarja, run-validation.ps1, ohjaa molemmat validaattorit tulostushakemiston yli ja tulostaa hyväksyttyjen ja hylättyjen taulukon. Sama kaksivaiheinen muoto, luo kirjastolla ja varmista ulkoisilla validaattoreilla, on se, mitä jatkuvan integraation tehtävän tulisi suorittaa jokaisessa laskun luonnin muutoksessa, koska ainoa tapa tietää, että tiedosto täyttää molemmat kerrokset, on kysyä molemmilta työkaluilta
Jos putkesi on myös sertifioitava säilö ennen allekirjoittamista, tämän työn preflight-puoli on käsitelty pdflibpas-pdfa-pdfua-preflight-delphi.html-artikkelissamme PDF/A- ja PDF/UA-preflightista Delphissä, ja laajempi certify-then-sign -kulku on kuvattu pdflibpas-compliance-signing-workbench.html-artikkelissa. Molemmat rakentuvat samalle luontipolulle, joka toimitetaan osana Delphi PDF Library -kirjastoa Delphille ja C++Builderille, yhdessä tässä käytettyjen PDF/A-, associated-file- ja metatieto-API:iden kanssa