مقال تقني

فواتير Factur-X و ZUGFeRD الهجينة في Delphi

الفاتورة الإلكترونية المتوافقة ليست مجرد ملف PDF مع ملف XML مرفق بجانبه. إنها مستند PDF/A-3 واحد يحمل الفاتورة مرتين: مرة كصفحة يقرؤها الإنسان، ومرة بتنسيق Cross Industry Invoice XML قابل للقراءة آليًا ومخزن داخل الملف كملف مرتبط. هذه الطبيعة المزدوجة هي الهدف الأساسي لمجموعات التنسيقات التي تتطلبها الآن التفويضات الأوروبية، مثل Factur-X في فرنسا وألمانيا، و ZUGFeRD في الأسواق الناطقة بالألمانية، و XRechnung لفواتير القطاع العام الألماني. يوضح هذا المقال كيفية قيام PDFlibPas بتجميع مثل هذه الفاتورة الهجينة في Delphi، ومكامن الخطأ التي تتركها المعايير مفتوحة، ولماذا يحتاج أحد ملفات التعريف في الكتالوج إلى منشئ XML منفصل تمامًا

ما هي الفاتورة الهجينة بالفعل

تخدم الصفحة المرئية وملف XML المدمج قراءً مختلفين. ينظر الموظف الذي يوافق على الدفع إلى الصفحة المعروضة. بينما يستوعب نظام الحسابات الدائنة ملف XML، ويقرأ الإجماليات وتقسيم الضرائب كحقول مهيكلة، ويسجل الإدخال دون أن يكتب الإنسان أي شيء. يحكم المعيار الأوروبي EN 16931 المحتوى الدلالي لملف XML هذا، وهو المعيار الذي يحدد نموذج بيانات الفاتورة: الحقول الموجودة، وماذا تعني، وأيها إلزامي. EN 16931 هو نموذج دلالي، وليس تنسيق ملف. تدرك جميع تنسيقات Factur-X و ZUGFeRD 2.x و XRechnung هذا النموذج كمستند UN/CEFACT Cross Industry Invoice، وهو بناء الجملة الذي يحمل حقول EN 16931 عبر الشبكة

لكي يكون المستند قابلاً للأرشفة وواصفًا لنفسه، فإن الحاوية هي PDF/A-3، المُعرَّفة بواسطة ISO 19005-3. مستوى المطابقة PDF/A-3 هو الذي يسمح بوجود ملفات مدمجة عشوائية، وهو بالضبط ما يجب أن يكون عليه ملف XML للفاتورة. يمنع PDF/A-2 دمج الملفات التي لا تكون بدورها PDF/A، لذلك لا يمكن أن تكون فاتورة Factur-X بصيغة PDF/A-2. وبالتالي فإن اختيار PDF/A-3 ليس تفضيلًا، بل هو متطلب ينتج مباشرة عن الرغبة في دمج بيانات غير PDF في مستند أرشيفي

لماذا العلاقة هي البديل (Alternative)

تضمين البايتات هو الجزء السهل. يُعرِّف ISO 32000 §7.11.4 تدفق الملف المدمج، وهو الكائن الذي يحتوي على بيانات XML الخام ومعلماتها. الجزء الذي يجعل الملف ملفًا مرتبطًا صالحًا هو §14.13، والذي يضيف مفهوم الملف المرتبط ومفتاح /AFRelationship. يوضح هذا المفتاح كيف ترتبط البيانات المدمجة بالمحتوى المرفقة به، والقيمة التي تفرضها Factur-X هي Alternative (بديل)

الاختيار مهم لأن القيم الأخرى قد تؤكد شيئًا خاطئًا عن المستند. القيمة Source (مصدر) قد تعني أن ملف XML هو المادة التي تم إنشاء المحتوى المرئي منها، وهو الأصل الذي تُشتق منه الصفحة. والقيمة Supplement (ملحق) قد تعني أن ملف XML يضيف معلومات تتجاوز ما تظهره الصفحة، أي معلومات إضافية غير متضمنة في العرض. لا تمثل أي من هاتين الحالتين فاتورة Factur-X. فملف XML والصفحة تعبيران متكافئان لفاتورة واحدة، يحملان نفس المحتوى القانوني بشكلين. إن Alternative هي القيمة التي تقول ذلك بالضبط: تمثيل بديل ومكافئ للمحتوى المرئي. أي مدقق يقرأ علاقة أخرى في ملف Factur-X سيرفضه، وعن حق، لأن العلاقة هي ادعاء قابل للقراءة آليًا حول الغرض من المرفق

كتالوج ملفات التعريف

يقود نموذج E-Invoice الذي يأتي مع PDFlibPas نفس مسار الإنشاء عبر ستة ملفات تعريف، مُعرَّفة كمصفوفة من السجلات في InvoiceModel.pas. يحمل كل ملف تعريف القيم التي يحتاجها الكاتب: اسم العرض، واسم الملف المدمج، ومستوى المطابقة، و /AFRelationship، والإصدار، ورمز الدولة الاختياري، و URN لـ GuidelineID الذي يعلنه ملف XML داخل سياق المستند الخاص به

الستة هم: Factur-X EN16931 و Factur-X BASIC و Factur-X EXTENDED لفرنسا و XRechnung 3.0 و ZUGFeRD 1.0 COMFORT و ZUGFeRD 2.0 BASIC. حقل GuidelineID هو الحقل الذي يخبر المستلم بالضبط بأي ملف تعريف يجب توقعه، والقيم محددة. يعلن Factur-X EN16931 عن urn:cen.eu:en16931:2017. يعلن XRechnung 3.0 عن urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0. يعلن ZUGFeRD 2.0 BASIC عن urn:cen.eu:en16931:2017#compliant#urn:zugferd.de:2p0:basic. يعد اسم الملف المدمج جزءًا من العقد أيضًا. تدمج ملفات تعريف Factur-X الملف factur-x.xml، وتدمج XRechnung الملف xrechnung.xml، وتدمج ملفات تعريف ZUGFeRD إما ZUGFeRD-invoice.xml أو zugferd-invoice.xml. يقوم المستلم بمسح أسماء المرفقات للعثور على الفاتورة، لذا فإن اسم الملف ليس تجميليًا

هناك تفصيل واحد في الكتالوج يستحق القراءة بعناية. تستخدم معظم ملفات التعريف العلاقة Alternative، ولكن إدخال XRechnung 3.0 في النموذج يستخدم Source. يستجيب التنسيقان لمدققين واتفاقيات مختلفة، ويقوم النموذج بتعيين علاقة كل ملف تعريف من الكتالوج بدلاً من كتابة قيمة واحدة ثابتة في الكود، وهذا هو سبب وجود حقل لكل ملف تعريف بدلاً من ثابت

فخ ZUGFeRD 1.0

من المغري افتراض أن كل ملف تعريف هو فاتورة EN 16931 Cross Industry Invoice مع اختلافات طفيفة في عدد الحقول الاختيارية التي تملؤها. هذا ينطبق على خمسة من الستة. لكنه لا ينطبق على ZUGFeRD 1.0 COMFORT، والسبب هيكلي وليس تجميليًا

تُصدر ملفات التعريف الحديثة فاتورة UN/CEFACT Cross Industry Invoice مع إصدار مساحة الأسماء :100، وعنصرها الجذري هو rsm:CrossIndustryInvoice. يسبق ZUGFeRD 1.0 هذا المخطط. فهو CrossIndustryDocument لعام 2014 مع مساحة الأسماء :1p0، وعنصرها الجذري هو rsm:CrossIndustryDocument. تختلف معرفات موارد مساحة الأسماء (URNs)، ويختلف العنصر الجذري، وتختلف شجرة العناصر بالكامل: يقوم مخطط :1p0 بتجميع البيانات تحت ApplicableSupplyChainTradeAgreement و ApplicableSupplyChainTradeDelivery و ApplicableSupplyChainTradeSettlement، بينما يستخدم المخطط :100 ApplicableHeaderTradeAgreement و ApplicableHeaderTradeDelivery و ApplicableHeaderTradeSettlement. التسمية متشابهة بما يكفي للتضليل ومختلفة بما يكفي للكسر

تصف كلمة COMFORT في اسم ملف التعريف مدى ثراء البيانات، وهو ملف تعريف من مستوى الأتمتة مع عناصر تفصيلية كاملة وتقسيم الضرائب وشروط الدفع، وليس المخطط الذي يحملها. لذا لا يمكنك أخذ مستند :100 وإعادة تسميته لـ ZUGFeRD 1.0. يعالج النموذج هذا الأمر بوجود علامة في كل سجل لملف التعريف ودالتين منفصلتين للبناء، مع اختيار الدالة الصحيحة قبل إنشاء أي 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;

الانقسام ليس مجرد تفصيل تنفيذي لطيف. إذ أن إطعام شجرة :100 لمستلم ZUGFeRD 1.0 ينتج عنه مستند يفشل في التحقق من صحة المخطط عند العنصر الجذري، لذا يجب بناء العائلتين بواسطة كود يعرف أي واحدة يكتب

تحديد مستوى PDF/A-3

يحتوي PDF/A-3 على ثلاثة مستويات للمطابقة، ويقوم PDFlibPas بتحديدها من خلال SetPDFAMode. الوضع 5 هو PDF/A-3b، وهو المستوى الذي يضمن استنساخًا مرئيًا موثوقًا. الوضع 6 هو PDF/A-3a، والذي يضيف متطلبات الهيكل ذي العلامات (tagged-structure) وإمكانية الوصول للمستوى a. الوضع 7 هو PDF/A-3u، والذي يتطلب تعيين جميع النصوص إلى Unicode. يؤدي تمكين الوضع أيضًا إلى دمج القصد الإخراجي sRGB المدمج في المكتبة، وهو توصيف الألوان الذي يطلبه PDF/A بحيث يكون اللون المُصيَّر مُعرَّفًا بدلاً من الاعتماد على الجهاز

تعمل معظم تدفقات الفواتير على 3b، وهو كافٍ للحصول على صفحة مرئية وفية بالإضافة إلى ملف XML المدمج. إذا كنت بحاجة إلى ملف تعريف ICC صريح بدلاً من الملف المدمج، فيمكن لـ LoadOutputIntentProfile تبديله بعد تعيين الوضع. يحمل النموذج ملف تعريف sRGB للمستودع بهذه الطريقة ويتراجع إلى القصد المدمج عندما يتعذر الوصول إلى الملف، لذلك يكون القصد الإخراجي موجودًا دائمًا

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;

بناء الفاتورة الهجينة

بعد تكوين الحاوية، يصبح الباقي ثلاث خطوات بالترتيب: تعيين وضع PDF/A-3، رسم الصفحة المقروءة للإنسان، ثم إرفاق XML كملف مرتبط. الصفحة المرئية هي محتوى عادي. القيد الوحيد الذي يستحق التذكر هو أن PDF/A يمنع الخطوط القياسية الـ 14 غير المدمجة، لذلك يجب أن تدمج الصفحة وجه خط حقيقي بدلاً من الرجوع إلى خط مدمج

الإرفاق عبارة عن استدعاء واحد. تأخذ الدالة AddFacturXAssociatedFileFromString بايتات XML UTF-8 الخام بالإضافة إلى بيانات ملف التعريف الوصفية، وتكتب تدفق الملف المدمج، وتسجله في مصفوفة الكتالوج /AF التي يتطلبها PDF/A-3، وتطبق /AFRelationship، وتنشئ بيانات الفاتورة الإلكترونية الوصفية XMP التي تحدد المستند كـ Factur-X أو ZUGFeRD أو XRechnung. وتتحقق أيضًا من أن معرّف إرشادات XML يطابق مستوى المطابقة الذي طلبته، بحيث يتم اكتشاف عدم التطابق بين ملف XML الذي بنيته وملف التعريف الذي قمت بتسميته بدلاً من شحنه بصمت

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

هناك دقة واحدة في مسار البيانات وهي الترميز. يعلن ملف XML المدمج encoding="UTF-8"، وتأخذ الطريقة بايتاتها كـ AnsiString، لذا يجب أن يصل اسم البائع أو المشتري غير التابع لـ ASCII إلى الاستدعاء كثمانيات UTF-8 خام. سيؤدي التحويل الصريح عبر صفحة أكواد ANSI الخاصة بالنظام إلى إتلاف تلك الأحرف وإنتاج فاتورة بهدوء لم يعد ملف XML الخاص بها يطابق إعلانه الخاص. يقوم النموذج بالترميز إلى UTF-8 بشكل صريح قبل تسليم البايتات، وهي الطريقة الآمنة لتغذية أي واجهة برمجة تطبيقات (API) لـ PDF موجهة للبايتات من string في Unicode

بالنسبة لربط XML الذي ليس ملف تعريف فاتورة إلكترونية معترف به، فإن الدالة AddPDFA3AssociatedFileFromString هي النظير العام. إنها تأخذ اسم ملف ونوع MIME ووصفًا وعلاقة وبايتات، وتكتب ملف PDF/A-3 مرتبطًا عاديًا دون أي بيانات وصفية خاصة بالفاتورة أو عمليات تحقق من الإرشادات. استخدمها للبيانات التكميلية؛ واستخدم طريقة Factur-X للفواتير، بحيث يتم كتابة البيانات الوصفية لملف التعريف وتطابق الإرشادات من أجلك

بمجرد إنتاج المستند، فإن الأسئلة التالية هي ما إذا كان سيجتاز عمليات التحقق من صحة PDF/A وإمكانية الوصول، وما إذا كان يمكن توقيعه دون كسر المطابقة. تمت تغطية ذلك في جولة ما قبل الطباعة (preflight) لـ PDF/A و PDF/UA و طاولة عمل المطابقة والتوقيع. يتم شحن كل هذا كجزء من PDFlibPas Delphi PDF Library، جنبًا إلى جنب مع واجهات برمجة التطبيقات لـ PDF/A والعلامات وخصائص المستند التي يبني عليها مسار الفاتورة الإلكترونية