Teknik Makale

Delphi'de Factur-X ve ZUGFeRD Hibrit Faturaları

Uyumlu (compliant) bir elektronik (electronic) fatura (invoice), yan tarafına (on the side) zımbalanmış (stapled) bir XML dosyası olan bir PDF değildir. Faturayı (invoice) iki (twice) kez (twice) taşıyan (carries) tek (single) bir PDF/A-3 belgesidir (document): bir (once) kez (once) bir insanın (human) okuduğu bir sayfa (page) olarak ve bir kez (once) de dosyanın (file) içinde ilişkili (associated) bir dosya (file) olarak depolanan makine (machine) tarafından okunabilir (machine-readable) bir Endüstriler Arası (Cross Industry) Fatura (Invoice) XML'i olarak. İki (two) temsil (representations) aynı (same) faturayı tanımlar. Bu ikili (dual) doğa (nature), Fransa ve Almanya'da Factur-X, Almanca konuşulan (German-speaking) pazarlarda (markets) ZUGFeRD ve Alman kamu sektörü (public-sector) faturalandırması (billing) için XRechnung gibi Avrupa zorunluluklarının (mandates) artık (now) gerektirdiği (require) format ailelerinin (families) tüm (whole) amacıdır (point). Bu (This) makale, PDFlibPas'ın Delphi'de böyle bir hibrit faturayı nasıl birleştirdiğini, standartların yanlış yapmaya nerede yer bıraktığını ve katalogdaki bir profilin neden tamamen ayrı bir XML oluşturucuya ihtiyaç duyduğunu adım adım açıklamaktadır

Hibrit faturanın gerçekte ne olduğu (What a hybrid invoice actually is)

Görünür sayfa ve gömülü XML farklı (different) okuyuculara hizmet (serve) eder. Bir ödemeyi onaylayan bir memur işlenmiş (rendered) sayfaya bakar (looks at). Bir borç hesapları sistemi XML'i alır (ingests), toplamları ve vergi dökümünü yapılandırılmış alanlar olarak okur ve hiçbir insan hiçbir şeyi tuşlamadan (without a human keying anything) girişi kaydeder (books). Bu XML'in anlamsal (semantic) içeriği (content), fatura veri modelini (data model) tanımlayan (defines) Avrupa standardı olan EN 16931 tarafından yönetilir (is governed by): hangi (which) alanların var olduğu, ne anlama (mean) geldikleri ve hangilerinin zorunlu (mandatory) olduğu. EN 16931 anlamsal bir modeldir, bir dosya formatı değildir (not a file format). Factur-X, ZUGFeRD 2.x ve XRechnung'un tümü bu modeli, kablodaki (on the wire) EN 16931 alanlarını taşıyan sözdizimi (syntax) olan (which is) bir UN/CEFACT Endüstriler Arası Fatura (Cross Industry Invoice) belgesi olarak gerçekleştirir (realize)

Belgenin (document) hem (both) arşivlenebilir (archivable) hem de kendi (self) kendini (self) tanımlayıcı (self-describing) olması (to be) için kapsayıcı, ISO 19005-3 tarafından tanımlanan PDF/A-3'tür. PDF/A-3, keyfi gömülü dosyalara izin veren uygunluk düzeyidir, ki bir fatura XML'inin tam olarak (exactly) olması (needs to be) gereken (what) şey de budur (is). PDF/A-2, kendileri (themselves) PDF/A olmayan dosyaların gömülmesini (embedding) yasaklar (forbids), bu nedenle bir Factur-X faturası PDF/A-2 olamaz. Bu nedenle PDF/A-3 seçimi bir tercih değildir, arşiv (archival) niteliğinde bir belgeye (document) PDF olmayan verileri gömmeyi (embed) istemekten doğrudan kaynaklanan bir gerekliliktir

İlişkinin (relationship) neden (Why) Alternatif (Alternative) olduğu

Baytları (bytes) gömmek (Embedding) işin (part) kolay (easy) kısmıdır. ISO 32000 §7.11.4, ham (raw) XML'i ve parametrelerini (parameters) tutan nesne (object) olan gömülü (embedded) dosya akışını tanımlar. Dosyayı (file) geçerli (valid) bir ilişkili (associated) dosya (file) yapan bölüm, ilişkili (associated) bir dosya kavramını ve /AFRelationship anahtarını (key) ekleyen (adds) §14.13'tür. Bu (That) anahtar (key), gömülü verilerin eklendiği içerikle nasıl ilişkili (relates to) olduğunu belirtir (states) ve Factur-X'in zorunlu kıldığı (mandates) değer (value) Alternative'dir

Seçim önemlidir çünkü (because) diğer (other) değerler (values) belge hakkında yanlış (false) bir iddiada (assert) bulunacaktır (would assert). Source, XML'in görünür (visible) içeriğin üretildiği (generated) malzeme, yani sayfanın (page) türetildiği (derives from) bir ana öğe (master) olduğu anlamına gelir (would mean). Supplement, XML'in sayfanın gösterdiklerinin ötesinde bilgi (information) eklediği (adds), yani (an) işlemede (rendering) yer (contained) almayan (not) bir ekstra olduğu anlamına gelir. İkisi de (Neither is) bir Factur-X faturasının (invoice) ne (what) olduğu (is) değildir. XML ve sayfa (page), aynı (same) yasal (legal) içeriği (content) iki formda taşıyan bir faturanın iki eşdeğer (equivalent) ifadesidir (expressions). Alternative tam (exactly) olarak bunu söyleyen değerdir: görünür (visible) içeriğin (content) eşdeğer (equivalent) bir alternatif temsili (representation). Factur-X dosyasındaki başka herhangi bir (any other) ilişkiyi (relationship) okuyan bir doğrulayıcı (validator) onu (it) reddedecektir (will reject) ve (and) haklı (rightly) olarak da öyledir, çünkü ilişki (relationship), ekin (attachment) ne için (what) olduğuna (is for) dair makine (machine) tarafından okunabilir (machine-readable) bir iddiadır (claim)

Profil (profile) kataloğu (catalog)

PDFlibPas ile birlikte (with) gelen E-Fatura (E-Invoice) örneği (sample), InvoiceModel.pas dosyasında (in) bir kayıt (records) dizisi (array) olarak tanımlanan altı (six) profil (profiles) boyunca (across) aynı oluşturma yolunu yönlendirir. Her (Each) profil (profile), yazarın (writer) ihtiyaç duyduğu (needs) değerleri (values) taşır (carries): bir görüntüleme adı (display name), gömülü (embedded) dosya adı (file name), uygunluk (conformance) düzeyi (level), /AFRelationship, bir sürüm (version), isteğe bağlı bir ülke (country) kodu ve (and) XML'in belge (document) bağlamı (context) içinde (inside) duyurduğu GuidelineID URN

Bu (The) altı (six) profil şunlardır: Factur-X EN16931, Factur-X BASIC, Fransa (France) için Factur-X EXTENDED, XRechnung 3.0, ZUGFeRD 1.0 COMFORT ve ZUGFeRD 2.0 BASIC. GuidelineID, alıcıya (receiver) tam (precisely) olarak hangi (which) profili beklemesi (to expect) gerektiğini (tells) söyleyen (tells) alandır (field) ve değerler belirsizliğe yer bırakmaz (are specific). Factur-X EN16931 urn:cen.eu:en16931:2017 olduğunu duyurur. XRechnung 3.0 urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0 olduğunu duyurur. ZUGFeRD 2.0 BASIC urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic olduğunu duyurur. Gömülü (embedded) dosya adı da (also) sözleşmenin (contract) bir parçasıdır (part). Factur-X profilleri factur-x.xml dosyasını (embed), XRechnung xrechnung.xml dosyasını (embeds) ve ZUGFeRD profilleri (profiles) ZUGFeRD-invoice.xml veya zugferd-invoice.xml dosyasını (embed) gömer. Bir alıcı (receiver) faturayı bulmak için (to find) eklenti adlarını tarar, bu nedenle (so) dosya adı (file name) kozmetik değildir

Katalogdaki (catalog) bir detay (detail) dikkatle okumaya değerdir. Profillerin (profiles) çoğu (Most) Alternative ilişkisini (relationship) kullanır (use), ancak (but) örnekteki XRechnung 3.0 girişi Source'u kullanır (uses). İki (Two) format (formats) farklı (different) doğrulayıcılara ve (and) kurallara (conventions) yanıt (answer) verir ve örnek, her profilin ilişkisini sabit (hard-coding) tek bir değer (value) olarak kodlamak yerine (rather than) katalogdan (from the catalog) ayarlar, bu da profil (per-profile) başına alanın (field) bir sabit (constant) yerine (rather than) neden var (exists) olduğunun (why) nedenidir

ZUGFeRD 1.0 tuzağı (trap)

Her (every) profilin (profile), ne kadar (how many) isteğe bağlı (optional) alanı doldurduğunuz (you populate) konusunda (in) küçük (minor) farklılıklar (variations) içeren EN 16931 Endüstriler Arası (Cross Industry) Fatura (Invoice) olduğunu (is) varsaymak caziptir (tempting). Bu (That) altı profilden beşi için geçerlidir (holds). ZUGFeRD 1.0 COMFORT için (for) geçerli (hold) değildir ve (and) nedeni (reason) kozmetik olmaktan ziyade yapısaldır (structural)

Modern profiller (profiles), kök (root) öğesi (element) rsm:CrossIndustryInvoice olan ad (namespace) alanı sürüm (version) :100 ile (with) bir UN/CEFACT Endüstriler Arası (Cross Industry) Fatura (Invoice) yayınlar (emit). ZUGFeRD 1.0 bu şemadan (schema) daha eskidir (predates). İsim (namespace) alanı sürümü (version) :1p0 olan 2014 CrossIndustryDocument'tır ve kök (root) öğesi (element) rsm:CrossIndustryDocument'tır. Ad alanı URN'leri farklıdır, kök (root) öğesi (element) farklıdır ve (and) öğe (element) ağacı (tree) baştan (throughout) sona farklıdır: :1p0 şeması verileri ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery ve ApplicableSupplyChainTradeSettlement altında (under) gruplandırırken, :100 ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery ve ApplicableHeaderTradeSettlement'ı kullanır. Adlandırma (naming) yanıltacak kadar (enough to mislead) benzer (similar) ve kıracak kadar (enough to break) farklıdır

Profil adındaki COMFORT kelimesi (word), verilerin (data) hangi (which) şemanın taşıdığını (carries it) değil, tam (full) kalem öğeleri (line items), vergi (tax) dökümü (breakdown) ve ödeme koşulları (terms) ile (with) otomasyon sınıfı (automation-grade) bir profil (profile) olup (is) ne kadar (how) zengin (rich) olduğunu açıklar. Bu nedenle, bir :100 belgesini alıp ZUGFeRD 1.0 için yeniden etiketleyemezsiniz. Örnek, bunu (this) her (each) profil (profile) kaydında (record) bir işaret (flag) ve iki (two) ayrı (separate) oluşturucu (builder) işleviyle (functions) işler, herhangi bir (any) XML oluşturulmadan (is generated) önce doğru (right) olanı seçer (selecting)

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;

Bölünme (split), uygulamanın hoş bir inceliği değildir. Bir :100 ağacını ZUGFeRD 1.0 alıcısına beslemek, şema (schema) doğrulamasında kök (root) öğesinde başarısız olan bir belge (document) üretir, bu nedenle (so) iki (two) ailenin, hangisini (which one) yazdığını bilen kod (code) tarafından oluşturulması (built) gerekir (have to be)

PDF/A-3 düzeyinin (level) seçilmesi (Selecting)

PDF/A-3'ün üç (three) uygunluk (conformance) düzeyi (levels) vardır ve (and) PDFlibPas bunları (them) SetPDFAMode aracılığıyla (through) seçer. Mod 5, güvenilir görsel reprodüksiyonu garanti eden seviye olan PDF/A-3b'dir. Mod 6, a seviyesinin etiketli (tagged-structure) yapı ve erişilebilirlik gereksinimlerini (requirements) ekleyen PDF/A-3a'dır. Mod 7, tüm (all) metinlerin (text) Unicode'a eşlenmesini gerektiren (requires) PDF/A-3u'dur. Modun etkinleştirilmesi (Enabling), işlenmiş (rendered) rengin (color) cihaza bağımlı (device-dependent) olmaktan ziyade tanımlı olması için (so that) PDF/A'nın talep ettiği (demands) renk karakterizasyonu (characterization) olan kitaplığın yerleşik (built-in) sRGB çıktı amacını (output intent) da (also) gömer

Çoğu (Most) fatura akışı (flows), gömülü XML ile (plus) birlikte aslına uygun (faithful) görünür (visible) bir sayfa için yeterli (sufficient) olan 3b'de çalışır (run at). Yerleşik (built-in) olan yerine açık bir ICC profiline ihtiyacınız (you need) varsa, mod (mode) ayarlandıktan (is set) sonra (after) LoadOutputIntentProfile bunu (it) değiştirir (swaps it in). Örnek, havuz (repository) sRGB profilini bu (this) şekilde (way) yükler ve (and) dosyaya (file) ulaşılamadığında yerleşik (built-in) amaca (intent) geri (back) döner (falls), bu nedenle çıktı (output) amacı (intent) her (always) zaman (always) mevcuttur

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;

Hibrit (hybrid) faturanın (invoice) oluşturulması (Building)

Kapsayıcı yapılandırıldıktan sonra (With), gerisi sırayla (in order) üç (three) adımdır: PDF/A-3 modunu (mode) ayarlayın, insan (human) tarafından okunabilir (human-readable) sayfayı (page) çizin, ardından (then) XML'i ilişkili bir dosya (file) olarak ekleyin. Görünür (visible) sayfa sıradan (ordinary) içeriktir. Hatırlanmaya değer (worth) tek kısıtlama (constraint), PDF/A'nın gömülü olmayan Standart 14 yazı (fonts) tiplerini yasaklamasıdır (forbids), bu nedenle (so) sayfa (page), yerleşik bir (built-in one) yazı tipine (font face) atıfta (reference) bulunmak yerine gerçek (real) bir yazı tipi gömmelidir

Eklenti tek bir çağrıdır. AddFacturXAssociatedFileFromString ham (raw) UTF-8 XML baytlarını ve (plus) profil meta verilerini (metadata) alır, gömülü (embedded) dosya akışını yazar, PDF/A-3'ün gerektirdiği Katalog /AF dizisine (array) kaydeder (registers), /AFRelationship değerini (the) uygular (applies) ve belgeyi Factur-X, ZUGFeRD veya XRechnung olarak tanımlayan XMP e-fatura meta verilerini oluşturur (generates). Ayrıca XML'in kılavuz kimliğinin (ID) istediğiniz (you asked for) uygunluk (conformance) düzeyiyle (level) eşleşip (matches) eşleşmediğini kontrol eder (checks), böylece (so) derlediğiniz XML ile (between) adlandırdığınız (you named) profil (profile) arasındaki bir uyuşmazlık (mismatch) sessizce (silently) sevk edilmek (shipped) yerine (rather than) yakalanır

// 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);

Veri (data) yolundaki (path) bir (One) incelik (subtlety), kodlamadır (encoding). Gömülü (embedded) XML encoding="UTF-8" değerini (the) bildirir ve yöntem (method) baytlarını (bytes) bir AnsiString olarak alır (takes), dolayısıyla ASCII dışı bir satıcı veya alıcı (buyer) adı (name), çağrıya ham UTF-8 sekizlileri olarak ulaşmalıdır. Sistem ANSI kod sayfası aracılığıyla yapılacak düz bir (plain) dönüştürme (cast) bu karakterleri bozar (would corrupt) ve sessizce XML'i artık kendi beyanıyla eşleşmeyen bir fatura üretirdi. Örnek, baytları (bytes) teslim (handing) etmeden önce açıkça UTF-8'e kodlar (encodes), ki Unicode bir dizeden bayt (byte-oriented) yönelimli (oriented) herhangi bir PDF API'sini beslemenin (feed) güvenli yolu (way) budur

Tanınan bir e-fatura (e-invoice) profili (profile) olmayan XML'i eklemek (attaching) için, AddPDFA3AssociatedFileFromString genel karşılıktır. Bir dosya (file) adı, MIME türü, açıklama, ilişki ve bayt (bytes) alır ve faturaya özgü (invoice-specific) herhangi bir (any) meta veri (metadata) veya (or) kılavuz (guideline) denetimi olmadan düz bir PDF/A-3 ilişkili dosyası (file) yazar. Bunu ek (supplementary) veriler (data) için kullanın; faturalar için Factur-X yöntemini (method) kullanın, böylece profil (profile) meta verileri (metadata) ve (and) kılavuz (guideline) eşleşmesi (match) sizin için (for you) yazılır (are written)

Belge (document) üretildikten (is produced) sonra, bir sonraki (next) sorular PDF/A ve erişilebilirlik (accessibility) doğrulamasından geçip geçmediği (whether it passes) ve uygunluğu (compliance) bozmadan (without breaking) imzalanıp imzalanamayacağıdır (can be signed). Bunlar (Those), Delphi'deki PDF/A ve PDF/UA uçuş öncesi (preflight) incelemesinde ve uyumluluk (compliance) ve imzalama (signing) çalışma masasında (workbench) ele alınmıştır (are covered in). Tüm (All) bunlar (this), burada (here) kullanılan (used) PDF/A, etiketleme (tagging) ve (and) belge-özellik (document-property) API'leriyle (APIs) birlikte, Delphi ve C++Builder için PDFlibPas Delphi PDF Kütüphanesinin (Library) bir parçası olarak gönderilir