مقاله فنی

فاکتورهای هیبریدی Factur-X و ZUGFeRD در Delphi

یک فاکتور الکترونیکی منطبق یک PDF با یک فایل XML منگنه شده در کنار آن نیست. این یک سند منفرد PDF/A-3 است که فاکتور را دو بار حمل می‌کند: یک بار به عنوان صفحه‌ای که یک انسان آن را می‌خواند، و یک بار به عنوان XML Cross Industry Invoice قابل خواندن توسط ماشین که در داخل فایل به عنوان یک فایل مرتبط ذخیره شده است. این دو نمایش یک فاکتور یکسان را توصیف می‌کنند. این ماهیت دوگانه کل هدف خانواده‌های فرمتی است که دستورات اروپایی اکنون به آنها نیاز دارند، Factur-X در فرانسه و آلمان، ZUGFeRD در سراسر بازارهای آلمانی‌زبان، و XRechnung برای صدور صورتحساب بخش عمومی آلمان. این مقاله به نحوه مونتاژ چنین فاکتور هیبریدی در Delphi توسط PDFlibPas می‌پردازد، جایی که استانداردها فضایی را برای اشتباه کردن باقی می‌گذارند، و چرا یک نمایه در کاتالوگ به یک سازنده XML کاملاً مجزا نیاز دارد

یک فاکتور هیبریدی در واقع چیست

صفحه قابل مشاهده و XML تعبیه شده به خوانندگان متفاوتی خدمت می‌کنند. کارمندی که پرداخت را تأیید می‌کند به صفحه رندر شده نگاه می‌کند. یک سیستم حساب‌های پرداختنی XML را دریافت می‌کند، مجموع‌ها و تفکیک مالیات را به عنوان فیلدهای ساختاریافته می‌خواند، و بدون اینکه یک انسان چیزی را کلید بزند، ورودی را رزرو می‌کند. محتوای معنایی آن XML توسط EN 16931 اداره می‌شود، استاندارد اروپایی که مدل داده فاکتور را تعریف می‌کند: کدام فیلدها وجود دارند، چه معنایی دارند، و کدام یک اجباری هستند. 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

وسوسه انگیز است که فرض کنیم هر نمایه فاکتور Cross Industry Invoice EN 16931 با تغییرات جزئی در تعداد فیلدهای اختیاری است که شما پر می‌کنید. این برای پنج مورد از شش مورد صادق است. اما برای ZUGFeRD 1.0 COMFORT صادق نیست و دلیل آن ساختاری است تا تزئینی

نمایه‌های مدرن یک فاکتور Cross Industry Invoice UN/CEFACT با فضای نام نسخه :100 ساطع می‌کنند، که عنصر ریشه آن rsm:CrossIndustryInvoice است. ZUGFeRD 1.0 قبل از آن طرحواره است. این CrossIndustryDocument 2014 با فضای نام نسخه :1p0 است، و عنصر ریشه آن rsm:CrossIndustryDocument است. URNهای فضای نام متفاوت هستند، عنصر ریشه متفاوت است، و درخت عناصر در سراسر سیستم متفاوت است: طرحواره :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 است که الزامات ساختار-تگ‌دار و دسترسی‌پذیری سطح a را اضافه می‌کند. حالت 7 مربوط به PDF/A-3u است، که ایجاب می‌کند تمام متن به یونیکد نگاشت شود. فعال کردن حالت همچنین intent خروجی 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 به اضافه اَبَرداده نمایه را دریافت می‌کند، جریان فایل تعبیه شده را می‌نویسد، آن را در آرایه Catalog /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 به فراخوانی برسد. یک تبدیل ساده (cast) از طریق صفحه کد ANSI سیستم، آن کاراکترها را خراب کرده و بی‌صدا فاکتوری تولید می‌کند که XML آن دیگر با اعلان خودش مطابقت ندارد. نمونه قبل از تحویل بایت‌ها به طور صریح در UTF-8 رمزگذاری می‌کند، که روش امن برای تغذیه هر API بایت‌گرای PDF از یک string یونیکد است

برای پیوست کردن XML که یک نمایه شناخته شده فاکتور الکترونیکی نیست، AddPDFA3AssociatedFileFromString همتای عمومی است. این تابع نام فایل، نوع MIME، توضیحات، رابطه و بایت‌ها را دریافت می‌کند، و یک فایل مرتبط PDF/A-3 ساده را بدون اَبَرداده فاکتور خاص یا بررسی‌های دستورالعمل می‌نویسد. از آن برای داده‌های تکمیلی استفاده کنید؛ از روش Factur-X برای فاکتورها استفاده کنید، به طوری که اَبَرداده نمایه و تطابق دستورالعمل برای شما نوشته شود

پس از تولید سند، سوالات بعدی این است که آیا از اعتبارسنجی PDF/A و دسترسی‌پذیری عبور می‌کند و آیا می‌توان آن را بدون نقض انطباق امضا کرد. این موارد در گذر از پیش‌پرواز PDF/A و PDF/UA و میز کار انطباق و امضا پوشش داده شده است. همه اینها به عنوان بخشی از کتابخانه PDF Delphi، در کنار APIهای PDF/A، تگ‌گذاری و ویژگی‌های سند که مسیر فاکتور الکترونیکی روی آن استوار است، عرضه می‌شود