Teknik Makale

Delphi'de Factur-X ve ZUGFeRD Hibrit Faturaları

Uyumlu bir elektronik fatura, yanına bir XML dosyası eklenmiş sıradan bir PDF değildir. Faturayı iki kez taşıyan tek bir PDF/A-3 belgesidir: bir kez bir insanın okuduğu sayfa olarak, bir kez de dosya içinde ilişkili dosya olarak saklanan, makine tarafından okunabilir bir Endüstriler Arası Fatura (Cross Industry Invoice) XML'i olarak. Bu iki temsil aynı faturayı tanımlar. Söz konusu çift doğa, Avrupa'nın artık zorunlu kıldığı format ailelerinin tüm amacıdır: Fransa ve Almanya'da Factur-X, Almanca konuşulan pazarlarda ZUGFeRD ve Alman kamu sektörü faturalandırması için XRechnung. Bu makale, PDF Library for Delphi'ın Delphi'de böyle bir hibrit faturayı nasıl oluşturduğunu, standartların nerede hataya yer bıraktığını ve katalogdaki bir profilin neden tamamen ayrı bir XML oluşturucuya ihtiyaç duyduğunu adım adım ele alır

Hibrit fatura gerçekte nedir

Görünür sayfa ve gömülü XML farklı okuyuculara hizmet eder. Bir ödemeyi onaylayan görevli işlenmiş sayfaya bakar. Bir borç hesapları sistemi ise XML'i alır, toplamları ve vergi dökümünü yapılandırılmış alanlar olarak okur ve hiçbir insan hiçbir şey girmeden kaydı defterine işler. Bu XML'in anlamsal içeriği, fatura veri modelini tanımlayan Avrupa standardı EN 16931 tarafından yönetilir: hangi alanların bulunduğu, ne anlama geldikleri ve hangilerinin zorunlu olduğu. EN 16931 anlamsal bir modeldir, bir dosya biçimi değildir. Factur-X, ZUGFeRD 2.x ve XRechnung'un hepsi bu modeli, EN 16931 alanlarını taşıyan sözdizimi olan bir UN/CEFACT Endüstriler Arası Fatura belgesi olarak hayata geçirir

Belgenin hem arşivlenebilir hem de kendini tanımlayıcı olabilmesi için kapsayıcı format, 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; bir fatura XML'inin tam olarak ihtiyaç duyduğu şey de budur. PDF/A-2 ise kendisi PDF/A olmayan dosyaların gömülmesini yasaklar, bu yüzden bir Factur-X faturası PDF/A-2 olamaz. Dolayısıyla PDF/A-3 seçimi bir tercih değil, arşiv niteliğindeki bir belgeye PDF olmayan veri gömme isteğinden doğrudan doğan bir zorunluluktur

İlişkinin neden Alternative olduğu

Baytları gömmek işin kolay kısmıdır. ISO 32000 §7.11.4, ham XML'i ve parametrelerini tutan nesne olan gömülü dosya akışını tanımlar. Dosyayı geçerli bir ilişkili dosya yapan kısım ise, ilişkili dosya kavramını ve /AFRelationship anahtarını ekleyen §14.13'tür. Bu anahtar, gömülü verinin eklendiği içerikle nasıl ilişkili olduğunu belirtir ve Factur-X'in zorunlu kıldığı değer Alternative'dir

Bu seçim önemlidir, çünkü diğer değerler belge hakkında yanlış bir iddiada bulunmuş olurdu. Source, XML'in görünür içeriğin üretildiği kaynak malzeme, yani sayfanın türetildiği bir ana öğe olduğu anlamına gelirdi. Supplement ise XML'in sayfanın gösterdiklerinin ötesinde bilgi eklediği, yani görüntülemede yer almayan bir ek olduğu anlamına gelirdi. Hiçbiri bir Factur-X faturasının gerçek doğasını yansıtmaz. XML ve sayfa, aynı yasal içeriği iki biçimde taşıyan bir faturanın iki eşdeğer ifadesidir. Alternative tam olarak bunu söyleyen değerdir: görünür içeriğin eşdeğer bir alternatif temsili. Factur-X dosyasında başka herhangi bir ilişki değeri okuyan bir doğrulayıcı bunu reddedecektir, ve haklı olarak da öyle yapar; çünkü bu ilişki, ekin ne amaçla var olduğuna dair makine tarafından okunabilir bir iddiadır

PDF Library for Delphi ile oluşturulan bir Factur-X hibrit faturasının anatomisi: tek bir PDF/A-3 belgesi, insan tarafından okunabilir sayfayı ve EN 16931 modeli altında AFRelationship Alternative ile birleştirilmiş gömülü bir factur-x.xml dosyasını barındırır
Tek bir PDF/A-3 dosyası faturayı eşdeğer temsiller olarak iki kez taşır ve bu iddiayı doğrulayıcıların kabul edeceği biçimde yalnızca /AFRelationship = Alternative bildirir

Profil kataloğu

PDF Library for Delphi ile birlikte gelen E-Fatura örneği, InvoiceModel.pas dosyasında bir kayıt dizisi olarak tanımlanan altı profil üzerinden aynı oluşturma yolunu izler. Her profil, yazıcının ihtiyaç duyduğu değerleri taşır: bir görüntüleme adı, gömülü dosya adı, bir uygunluk düzeyi, /AFRelationship, bir sürüm, isteğe bağlı bir ülke kodu ve XML'in belge bağlamı içinde duyurduğu GuidelineID URN'i

Bu altı profil şunlardır: Factur-X EN16931, Factur-X BASIC, Fransa için Factur-X EXTENDED, XRechnung 3.0, ZUGFeRD 1.0 COMFORT ve ZUGFeRD 2.0 BASIC. GuidelineID, alıcıya tam olarak hangi profili beklemesi gerektiğini söyleyen alandır ve değerler son derece kesindir. Factur-X EN16931, urn:cen.eu:en16931:2017 değerini duyurur. XRechnung 3.0, urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0 değerini duyurur. ZUGFeRD 2.0 BASIC, urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic değerini duyurur. Gömülü dosya adı da sözleşmenin bir parçasıdır. Factur-X profilleri factur-x.xml dosyasını, XRechnung xrechnung.xml dosyasını, ZUGFeRD profilleri ise ZUGFeRD-invoice.xml veya zugferd-invoice.xml dosyasını gömer. Bir alıcı faturayı bulmak için ek adlarını tarar, dolayısıyla dosya adı kozmetik bir ayrıntı değildir

Katalogdaki bir ayrıntı dikkatle okunmayı hak eder. Profillerin çoğu Alternative ilişkisini kullanır, ancak örnekteki XRechnung 3.0 girdisi Source kullanır. İki biçim farklı doğrulayıcılara ve kurallara tabidir; örnek de her profilin ilişkisini tek bir sabit değer olarak koda gömmek yerine katalogdan ayarlar. Profil başına ayrı bir alanın, sabit bir değer yerine var olmasının nedeni de tam olarak budur

ZUGFeRD 1.0 tuzağı

Her profilin, yalnızca kaç isteğe bağlı alanı doldurduğunuz konusunda küçük farklar içeren bir EN 16931 Endüstriler Arası Fatura olduğunu varsaymak cazip gelebilir. Bu, altı profilden beşi için geçerlidir. ZUGFeRD 1.0 COMFORT için geçerli değildir ve bunun nedeni kozmetik değil yapısaldır

Modern profiller, kök öğesi rsm:CrossIndustryInvoice olan, :100 ad alanı sürümüne sahip bir UN/CEFACT Endüstriler Arası Fatura yayınlar. ZUGFeRD 1.0 ise bu şemadan öncesine aittir. Ad alanı sürümü :1p0 olan 2014 tarihli CrossIndustryDocument'tır ve kök öğesi rsm:CrossIndustryDocument'tır. Ad alanı URN'leri farklıdır, kök öğe farklıdır ve öğe ağacı baştan sona farklıdır: :1p0 şeması veriyi ApplicableSupplyChainTradeAgreement, ApplicableSupplyChainTradeDelivery ve ApplicableSupplyChainTradeSettlement altında gruplarken, :100 ApplicableHeaderTradeAgreement, ApplicableHeaderTradeDelivery ve ApplicableHeaderTradeSettlement'ı kullanır. Adlandırma, yanıltacak kadar benzer ve bozacak kadar farklıdır

Profil adındaki COMFORT kelimesi, verinin hangi şema tarafından taşındığını değil, tam kalem öğeleri, vergi dökümü ve ödeme koşullarıyla birlikte otomasyon sınıfında ne kadar zengin bir profil olduğunu tanımlar. Bu yüzden bir :100 belgesini alıp ZUGFeRD 1.0 için yeniden etiketleyemezsiniz. Örnek, bunu her profil kaydındaki bir bayrak ve iki ayrı oluşturucu işleviyle çözer; herhangi bir XML üretilmeden önce doğru olanı seçer

function BuildInvoiceXMLText(const AProfile: TeInvoiceProfile;
  const Data: TInvoiceData): string;
begin
  // XMLFamily = 1, eski ZUGFeRD 1.0 :1p0 şeması anlamına gelir; diğer
  // tüm profiller modern UN/CEFACT :100 Endüstriler Arası Fatura'dır.
  if AProfile.XMLFamily = 1 then
    Result := BuildZUGFeRD1Text(AProfile, Data)
  else
    Result := BuildCII100Text(AProfile, Data);
end;

Bu ayrım, uygulamanın hoş bir inceliği değildir. Bir :100 ağacını ZUGFeRD 1.0 alıcısına beslemek, şema doğrulamasında kök öğede başarısız olan bir belge üretir; bu yüzden iki ailenin, hangisini yazdığını bilen kod tarafından oluşturulması gerekir

PDF/A-3 düzeyinin seçilmesi

PDF/A-3'ün üç uygunluk düzeyi vardır ve PDF Library for Delphi bunları SetPDFAMode aracılığıyla seçer. Mod 5, güvenilir görsel yeniden üretimi garanti eden düzey olan PDF/A-3b'dir. Mod 6, a düzeyinin etiketli yapı ve erişilebilirlik gereksinimlerini ekleyen PDF/A-3a'dır. Mod 7 ise tüm metnin Unicode'a eşlenmesini zorunlu kılan PDF/A-3u'dur. Modu etkinleştirmek, işlenmiş rengin cihaza bağımlı olmak yerine tanımlı olması için PDF/A'nın talep ettiği renk karakterizasyonu olan kitaplığın yerleşik sRGB çıktı amacını da gömer

Çoğu fatura akışı, gömülü XML ile birlikte aslına uygun görünür bir sayfa için yeterli olan 3b düzeyinde çalışır. Yerleşik olan yerine açık bir ICC profiline ihtiyacınız varsa, mod ayarlandıktan sonra LoadOutputIntentProfile bunu değiştirir. Örnek, depo içindeki sRGB profilini bu şekilde yükler ve dosyaya ulaşılamadığında yerleşik amaca geri döner; böylece çıktı amacı her zaman mevcuttur

PDF := TPDFlib.Create;
try
  // Mod 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');

  // İsteğe bağlı: yerleşik sRGB amacını açık bir ICC profiliyle değiştir.
  if PDF.LoadOutputIntentProfile(ICCFile, 'DeviceRGB') <> 1 then
    { SetPDFAMode'un gömdüğü yerleşik sRGB amacına geri dön };
finally
  // ... belgeyi oluşturmaya devam et
end;

Hibrit faturanın oluşturulması

Kapsayıcı yapılandırıldıktan sonra geri kalanı sırasıyla üç adımdır: PDF/A-3 modunu ayarlayın, insan tarafından okunabilir sayfayı çizin, ardından XML'i ilişkili bir dosya olarak ekleyin. Görünür sayfa sıradan bir içeriktir. Akılda tutulması gereken tek kısıtlama, PDF/A'nın gömülü olmayan Standart 14 yazı tiplerini yasaklamasıdır; bu yüzden sayfa, yerleşik bir yazı tipine başvurmak yerine gerçek bir yazı tipi gömmelidir

Ekleme işlemi tek bir çağrıdır. AddFacturXAssociatedFileFromString, ham UTF-8 XML baytlarını ve profil meta verilerini alır, gömülü dosya akışını yazar, PDF/A-3'ün gerektirdiği Katalog /AF dizisine kaydeder, /AFRelationship değerini uygular ve belgeyi Factur-X, ZUGFeRD veya XRechnung olarak tanımlayan XMP e-fatura meta verilerini üretir. Ayrıca XML'in kılavuz kimliğinin, talep ettiğiniz uygunluk düzeyiyle eşleşip eşleşmediğini de kontrol eder; böylece derlediğiniz XML ile adlandırdığınız profil arasındaki bir uyumsuzluk sessizce gönderilmek yerine yakalanır

// 1. PDF/A-3 modu ve çıktı amacı zaten ayarlandı.
// 2. Görünür sayfayı çiz (gerçek bir TrueType yazı tipi gömer).
DrawInvoicePage(PDF, AProfile, Data);

// 3. Profile uygun XML'i oluştur ve onu /AFRelationship = Alternative
//    ile ilişkili bir dosya olarak ekle.
InvoiceXML := BuildInvoiceXML(AProfile, Data);   // UTF-8 baytlarından oluşan AnsiString
FileID := PDF.AddFacturXAssociatedFileFromString(
  InvoiceXML,
  AProfile.ConformanceLevel,   // örn. 'EN16931'
  AProfile.FileName,           // 'factur-x.xml'
  AProfile.Description,
  AProfile.Relationship,       // 'Alternative'
  AProfile.Version,            // '1.0'
  AProfile.CountryCode);       // '' veya 'DE' veya 'FR'
if FileID <= 0 then
  raise Exception.Create('Invoice XML could not be attached');

PDF.SaveToFile(TargetFile);

Veri yolundaki bir incelik, kodlamadır. Gömülü XML encoding="UTF-8" değerini bildirir ve yöntem baytları bir AnsiString olarak alır; dolayısıyla ASCII dışı bir satıcı veya alıcı adı, çağrıya ham UTF-8 sekizlileri olarak ulaşmalıdır. Sistem ANSI kod sayfası üzerinden yapılacak düz bir dönüştürme bu karakterleri bozar ve sessizce, XML'i artık kendi beyanıyla eşleşmeyen bir fatura üretirdi. Örnek, baytları teslim etmeden önce açıkça UTF-8'e kodlar; bir Unicode string'ten bayt yönelimli herhangi bir PDF API'sini beslemenin güvenli yolu da budur

PDF Library for Delphi profil kataloğu şeması: altı Factur-X, ZUGFeRD ve XRechnung e-fatura profilini BuildInvoiceXMLText üzerinden modern UN/CEFACT Cross Industry Invoice ailesine ve eski ZUGFeRD 1.0 :1p0 ailesine yönlendirir
Altı profil tek bir dağıtım noktasını paylaşır; ancak ZUGFeRD 1.0 COMFORT, eski :1p0 şemasına ayrılır ve kendi XML oluşturucusunu gerektirir

Tanınan bir e-fatura profili olmayan XML'i eklemek için AddPDFA3AssociatedFileFromString genel karşılıktır. Bir dosya adı, MIME türü, açıklama, ilişki ve bayt alır; faturaya özgü herhangi bir meta veri veya kılavuz denetimi olmadan düz bir PDF/A-3 ilişkili dosyası yazar. Bunu ek veriler için kullanın; faturalar için ise Factur-X yöntemini kullanın, böylece profil meta verileri ve kılavuz eşleşmesi sizin için yazılmış olur

Belge üretildikten sonra, sıradaki sorular PDF/A ve erişilebilirlik doğrulamasından geçip geçmediği ve uygunluğu bozmadan imzalanıp imzalanamayacağıdır. Bunlar, Delphi'deki PDF/A ve PDF/UA uçuş öncesi incelemesinde ve uyumluluk ve imzalama çalışma masasında ele alınmıştır. Tüm bunlar, e-fatura yolunun üzerine kurulduğu PDF/A, etiketleme ve belge-özellik API'leriyle birlikte, PDF Library for Delphi Delphi PDF Kütüphanesinin bir parçası olarak sunulur

PDF Library for Delphi ile bir Factur-X veya ZUGFeRD hibrit faturası oluşturma hattı şeması: SetPDFAMode PDF/A-3 kapsayıcısını yapılandırır, görünür sayfa çizilir, AddFacturXAssociatedFileFromString fatura XML'ini ekler ve SaveToFile arşiv belgesini yayımlar
Montaj, kaydetmeden önce üç sıralı adımda yürür; uygunluk düzeyleri, bayt güvenli UTF-8 işleme ve genel ilişkili-dosya API'si yan not olarak kalır