HotPDF ทำหน้าที่สร้าง, ตรวจจับ และตรวจสอบความถูกต้องของใบแจ้งหนี้อิเล็กทรอนิกส์ ZUGFeRD และ Factur-X จาก Delphi และ C++Builder ฟังก์ชัน AddFacturXAssociatedFile จะฝังไฟล์ XML ใบแจ้งหนี้ตามมาตรฐาน EN 16931 ลงในคอนเทนเนอร์ PDF/A-3, ฟังก์ชัน DetectFacturXInvoice จะตรวจจับใบแจ้งหนี้แบบไฮบริดที่รับเข้ามา และฟังก์ชัน HPDFValidateEInvoice จะรันกฎทางธุรกิจ EN 16931 Schematron อย่างเป็นทางการกับไฟล์ XML ที่ฝังอยู่ บทความนี้จะนำเสนอทั้ง 3 ขั้นตอนและชี้แจงอย่างตรงไปตรงมาเกี่ยวกับจุดที่เอนจินตรวจสอบความถูกต้องหยุดทำงาน
แรงผลักดันเบื้องหลังชุดฟีเจอร์นี้เป็นเรื่องของกฎระเบียบข้อบังคับ ไม่ใช่เรื่องของเทคโนโลยี ประเทศเยอรมนีและฝรั่งเศสกำลังเริ่มบังคับใช้ใบแจ้งหนี้อิเล็กทรอนิกส์แบบมีโครงสร้างสำหรับการทำธุรกรรม B2B ภายในประเทศ และข้อบังคับของทั้งสองประเทศก็มาบรรจบกันที่โมเดลไฮบริดแบบเดียวกัน: นั่นคือใบแจ้งหนี้ PDF ที่ระบบ ERP ของคุณสร้างขึ้นอยู่แล้ว จะต้องบรรจุไฟล์ XML แบบที่เครื่องสามารถอ่านได้ควบคู่ไปด้วย แอปพลิเคชันบัญชีหรือระบบ ERP บน Delphi ที่ปัจจุบันส่งออกเฉพาะใบแจ้งหนี้ PDF ธรรมดานั้น มีกำหนดเวลาที่เข้มงวดในการเริ่มส่งออกไฟล์ Factur-X หรือ ZUGFeRD และความแตกต่างระหว่างสองชื่อนี้ก็น้อยกว่าที่การตลาดโฆษณาไว้ — ZUGFeRD และ Factur-X คือมาตรฐานร่วมระหว่างฝรั่งเศสและเยอรมนีตัวเดียวกันที่เผยแพร่ภายใต้ชื่อสองชื่อ
อะไรที่ทำให้ PDF กลายเป็นใบแจ้งหนี้ Factur-X ที่ถูกต้อง?
ใบแจ้งหนี้ Factur-X ที่ถูกต้องคือไฟล์ PDF/A-3 (ISO 19005-3) ที่ฝังไฟล์ XML ใบแจ้งหนี้แบบมีโครงสร้างไว้เป็นไฟล์ร่วม (associated file) เพียงไฟล์เดียว เชื่อมโยงมันจากแคตตาล็อกเอกสาร และประกาศไว้ในข้อมูลเมตาส่วนขยาย XMP ซึ่งข้อมูลทั้ง 3 เลเยอร์จะต้องสอดคล้องกัน ประการแรก ตัวคอนเทนเนอร์ต้องสอดคล้องกับมาตรฐาน PDF/A-3 ซึ่งเป็นโปรไฟล์สำหรับการเก็บถาวรที่อนุญาตให้ฝังไฟล์ประเภทใดก็ได้ ต่างจาก PDF/A-1 และ PDF/A-2 ประการที่สอง ไฟล์ XML ใบแจ้งหนี้ที่ชื่อ factur-x.xml สำหรับโปรไฟล์มาตรฐาน หรือชื่อ xrechnung.xml สำหรับเวอร์ชัน XRechnung ของเยอรมนี จะต้องลงทะเบียนในอาร์เรย์ /AF ของแคตตาล็อก และทรี /Names /EmbeddedFiles พร้อมมีข้อมูล AFRelationship ระบุว่าไฟล์แนบคืออะไร: ซึ่งคำว่า Alternative หมายถึงไฟล์ XML เป็นตัวแทนที่เทียบเท่าของใบแจ้งหนี้ที่มองเห็นได้ ซึ่งเป็นความหมายที่โปรไฟล์ส่วนใหญ่กำหนด ประการที่สาม Factur-X XMP extension schema ต้องระบุชื่อไฟล์ที่ฝัง, ระดับความสอดคล้อง (conformance level) และประเภทเอกสาร เพื่อให้ระบบรับข้อมูลสามารถจัดประเภทใบแจ้งหนี้ได้โดยไม่ต้องแกะข้อมูล XML เลย
ตัวข้อมูล XML เองจะเป็นไปตามโมเดลความหมาย EN 16931 ซึ่งเป็นมาตรฐานยุโรปที่กำหนดแกนหลักของใบแจ้งหนี้: ผู้ซื้อ, ผู้ขาย, รายการแถวข้อมูล, รายละเอียดภาษี, ยอดเงินที่ต้องชำระ โดยแต่ละส่วนจะมีหมายเลขคำศัพท์ทางธุรกิจ (เช่น BT-1 สำหรับเลขใบแจ้งหนี้, BT-5 สำหรับสกุลเงิน เป็นต้น) ดังนั้นการตรวจสอบมาตรฐาน EN 16931 จึงเกิดขึ้นบนสองเลเยอร์ที่ต่างกัน การตรวจสอบโครงสร้าง XML (XML schema validation) จะตรวจว่าเอกสารเป็นโครงสร้าง CII ที่ถูกต้องพร้อมมีชื่อองค์ประกอบที่ถูกต้อง ส่วนกฎทางธุรกิจ Schematron จะตรวจว่าเนื้อหามีความสอดคล้องกัน — เช่น กฎ BR-CO-15 ระบุว่ายอดรวมใบแจ้งหนี้ต้องเท่ากับผลรวมของยอดสุทธิของแต่ละบรรทัดบวกกับภาษีมูลค่าเพิ่มจริง และคำศัพท์ทางธุรกิจที่จำเป็นต้องมีอยู่ครบถ้วนสำหรับโปรไฟล์ที่ประกาศ ไฟล์เอกสารอาจผ่านการตรวจสอบโครงสร้าง XML แต่ก็ยังเป็นใบแจ้งหนี้ที่ไม่ถูกต้องได้ ซึ่งนั่นคือสาเหตุที่มีเลเยอร์กฎทางธุรกิจขึ้นมา
ทำไม ZUGFeRD 2.5 จึงไม่ต้องใช้คอนเทนเนอร์ใหม่
ZUGFeRD 2.5 ไม่มีการเปลี่ยนแปลงใดๆ ในระดับคอนเทนเนอร์ PDF: ทั้ง URN แนวทางปฏิบัติ, โครงสร้าง XMP และโทเค็นเวอร์ชันจะเหมือนกันกับ ZUGFeRD 2.3 / Factur-X 1.0 ทุกประการ เรื่องนี้มักจะสร้างความประหลาดใจให้ผู้พัฒนาส่วนใหญ่ ดังนั้นจึงจำเป็นต้องอ้างอิงข้อมูลจากแหล่งข้อมูลอย่างเป็นทางการ ข้อกำหนดของ Factur-X 1.09 ระบุว่าเลขเวอร์ชันใน URI ของ PDF/A extension schema จะไม่เกี่ยวข้องกับเลขเวอร์ชันของข้อกำหนดข้อมูล XML และเลขเวอร์ชันของอินสแตนซ์ใบแจ้งหนี้ Factur-X จะยังคงเป็น 1.0 เสมอ — ทั้งใน fx:Version ของ XMP และใน BT-24 of factur-x.xml คลังตัวอย่าง ZUGFeRD 2.5 อย่างเป็นทางการยืนยันข้อนี้: ตัวอย่างทุกตัวจะฝัง factur-x.xml ภายใต้แนวทาง URN urn:factur-x.eu:1p0 ร่วมกับค่า fx:Version เป็น 1.0 สิ่งที่รุ่น 2.5 เพิ่มเข้ามาจริงๆ นั้นจะอยู่ภายในโมเดลข้อมูล XML — เช่น องค์ประกอบใหม่ๆ อย่างบรรทัดใบแจ้งหนี้ย่อยและตัวระบุชุดข้อมูล — ซึ่งเป็นเลเยอร์ทางธุรกิจที่แอปพลิเคชันของคุณสร้างขึ้น ไม่ใช่เลเยอร์คอนเทนเนอร์ที่ไลบรารี PDF จัดการ
HotPDF นำผลลัพธ์นี้มาใช้โดยตรง ฟังก์ชัน AddFacturXAssociatedFile จะรับพารามิเตอร์ Version และปรับรูปแบบผ่าน HPDFFacturXNormalizeVersion ก่อนเขียน XMP ดังนั้นผู้เรียกใช้งานที่ส่งชื่อแฝง 2p5 ก็จะยังคงสร้างโทเค็นที่สอดคล้องกับข้อกำหนดออกไป แทนที่จะสร้างเวอร์ชัน XMP 2p5 ขึ้นมาเองซึ่งจะไม่มีโปรแกรมตรวจสอบใดๆ ยอมรับ ส่วนระบบช่วยตรวจจับจะถือว่า 2p5 มีค่าเทียบเท่าเมื่อทำการอ่าน เพื่อให้ระบบไปกลับยังคงสมมาตรกัน หากไลบรารีหรือเครื่องมือใดบอกคุณว่ามันเขียนคอนเทนเนอร์ ZUGFeRD 2.5 แบบพิเศษ ตัวข้อกำหนดของมาตรฐานเองยืนยันว่าไม่มีสิ่งนั้นอยู่จริง
การฝัง XML ใบแจ้งหนี้จาก Delphi
var
Pdf: THotPDF;
InvoiceXML, UBLXML: TBytes;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-2026-0042.pdf';
Pdf.PDFACompliance := '3B'; // PDF/A-3b container
Pdf.BeginDoc;
// embed the EN 16931 CII invoice XML as factur-x.xml
InvoiceXML := BuildInvoiceXML; // produced by your business layer
Pdf.AddFacturXAssociatedFile(InvoiceXML, 'EN 16931');
// optional: attach a UBL view as a supplementary representation
UBLXML := BuildUBLXML;
Pdf.AddUBLSupplementaryFile(UBLXML); // factur-xubl.xml, Alternative
// ... draw the visible invoice page here ...
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
ค่าเริ่มต้นจะเป็นไปตามข้อกำหนด: ชื่อไฟล์คือ factur-x.xml, ความสัมพันธ์ Alternative, เวอร์ชัน 1.0 ฟังก์ชัน AddUBLSupplementaryFile จะทำงานตามส่วนที่ 6.4 ของข้อกำหนด Factur-X 1.09 ซึ่งอนุญาตให้นำการแสดงรูปแบบ UBL ของใบแจ้งหนี้เดียวกันแนบไปด้วยเป็นไฟล์แนบที่สองชื่อ factur-xubl.xml พร้อม MIME type application/xml และระบุความสัมพันธ์ Alternative ลำดับขั้นตอนมีความสำคัญที่นี่ และ HotPDF จะบังคับใช้เงื่อนไขตามที่ข้อกำหนดระบุไว้: นั่นคือไฟล์ UBL จะเป็นมุมมองเสริมเท่านั้น ไม่มีทางเป็นใบแจ้งหนี้หลัก ดังนั้นมันต้องถูกเพิ่มหลังจากฟังก์ชัน AddFacturXAssociatedFile ได้ลงทะเบียนตัวหลัก CII เรียบร้อยแล้ว ข้อควรทราบคือ หน้าเอกสารที่วาดขึ้นและไฟล์ XML ที่ฝังอยู่จะต้องระบุข้อมูลใบแจ้งหนี้ตัวเดียวกัน — ไฟล์ PDF ที่แสดงยอดรวมยอดหนึ่งแต่เข้ารหัสไว้อีกยอดหนึ่งนั้นคือโหมดความล้มเหลวที่ระบบใบแจ้งหนี้แบบไฮบริดพยายามป้องกัน ฝั่ง PDF/A-3 ของกระบวนการนี้ ซึ่งรวมถึง OutputIntent และการฝังฟอนต์ ได้รับการครอบคลุมรายละเอียดไว้แล้วในบทความคู่กันเกี่ยวกับการตรวจสอบความถูกต้องของ PDF/A, PDF/X และ PDF/UA ใน Delphi
จะตรวจจับใบแจ้งหนี้ Factur-X ใน PDF ที่รับเข้ามาได้อย่างไร?
var
Reader: THotPDF;
Info: THPDFFacturXInvoiceInfo;
begin
Reader := THotPDF.Create(nil);
try
if Reader.LoadFromFile('incoming-invoice.pdf') <= 0 then
raise Exception.Create('No pages loaded.');
if Reader.DetectFacturXInvoice(Info) then
begin
Writeln('Invoice XML: ', string(Info.FileName));
Writeln('Conformance: ', string(Info.ConformanceLevel));
Writeln('Guideline URN: ', string(Info.GuidelineID));
Writeln('Relationship: ', string(Info.AFRelationship));
if Info.UBLFileName <> '' then
Writeln('UBL view: ', string(Info.UBLFileName));
end
else
Writeln('Not a Factur-X / ZUGFeRD hybrid invoice.');
finally
Reader.Free;
end;
end;
การตรวจจับจะอ่านสัญญาณเดียวกับที่โปรแกรมตรวจสอบมาตรฐานทั่วไปอ่าน — นั่นคือ XMP extension schema และทะเบียนไฟล์ร่วม (associated-file registry) — แทนที่จะเป็นการคาดเดาจากชื่อไฟล์เพียงอย่างเดียว วัตถุเอกสารที่โหลดขึ้นมาตัวเดียวกันนี้จะเปิดเผยข้อมูลเมตาของ XMP และพจนานุกรมข้อมูลสำหรับการตรวจสอบหรือแก้ไข ซึ่งเป็นกระบวนการทำงานที่อธิบายไว้ในบทความเกี่ยวกับการแก้ไขข้อมูลเมตาในเอกสาร PDF ที่โหลดขึ้นมา
กฎทางธุรกิจหรือโครงสร้าง: การตรวจสอบมาตรฐาน EN 16931 หมายความว่าอย่างไร?
ฟังก์ชัน HPDFValidateEInvoice ซึ่งเป็นฟังก์ชันอิสระในยูนิต HPDFEInvoiceValidator จะรันเลเยอร์ตรวจสอบทั้งสองตามลำดับ: ขั้นแรกคือการตรวจสอบโครงสร้างคอนเทนเนอร์ผ่านทาง ValidateFacturXInvoice จากนั้นจึงตรวจสอบกฎทางธุรกิจ EN 16931 Schematron กับไฟล์ XML ใบแจ้งหนี้ที่แยกออกมา HotPDF จะจัดส่งไฟล์กฎ Factur-X 1.09 อย่างเป็นทางการทั้ง 5 ไฟล์ — MINIMUM, BASIC WL, BASIC, EN 16931 และ EXTENDED — ไว้ที่ตำแหน่ง Lib/resources/Schematron/ และฟังก์ชัน HPDFSchematronFileForProfile จะเลือกไฟล์ที่ตรงกับระดับความสอดคล้องที่ตรวจพบ ดังนั้นใบแจ้งหนี้ตามมาตรฐาน EN 16931 จะถูกตรวจสอบเทียบกับไฟล์ Factur-X_1.09_EN16931.sch โดยอัตโนมัติ ฟังก์ชันจะส่งกลับค่า True ก็ต่อเมื่อโครงสร้างคอนเทนเนอร์ถูกต้องและไม่มีกฎทางธุรกิจใดๆ ถูกละเมิด
uses HPDFEInvoiceValidator;
var
Reader: THotPDF;
Report: THPDFEInvoiceValidationReport;
I: Integer;
begin
Reader := THotPDF.Create(nil);
try
Reader.LoadFromFile('incoming-invoice.pdf');
if HPDFValidateEInvoice(Reader, SchematronDir, Report) then
Writeln('Container and business rules: OK')
else
Writeln('Validation found issues.');
if Report.BusinessRulesEvaluated then
begin
Writeln(Report.BusinessSummary.Evaluated, ' rules evaluated, ',
Report.BusinessSummary.Skipped, ' skipped (XPath 2.0)');
for I := 0 to High(Report.BusinessRules) do
if (not Report.BusinessRules[I].Skipped) and
(Report.BusinessRules[I].Severity = stsError) then
Writeln(string(Report.BusinessRules[I].RuleID), ': ',
string(Report.BusinessRules[I].Message));
end;
finally
Reader.Free;
end;
end;
แต่ละรายการใน BusinessRules จะระบุตัวระบุกฎที่แยกออกมาจากข้อความยืนยัน — เช่น BR-45, BR-CO-17 และระบบการตั้งชื่ออื่นๆ ตามมาตรฐาน EN 16931 — ควบคู่ไปกับ XPath บริบทของกฎ, นิพจน์การทดสอบ และระดับความรุนแรงที่สะท้อนถึงแอตทริบิวต์แฟล็กของ Schematron สิ่งนี้ทำให้รายงานมีความเป็นรูปธรรมที่สามารถนำไปปฏิบัติการแก้ไขได้: แทนที่จะบอกแค่ผ่านหรือไม่ผ่าน คุณจะได้รับข้อมูลคำศัพท์ทางธุรกิจที่ขัดแย้งกันอย่างแน่ชัด ซึ่งเป็นข้อมูลที่ทีมสนับสนุนของคุณต้องการเมื่อลูกค้าสอบถามว่าเหตุใดใบแจ้งหนี้จึงถูกปฏิเสธ ทีมงานที่สร้างรายงานความสอดคล้องแบบมีโครงสร้างสำหรับเอกสาร PDF จัดเก็บอยู่แล้ว สามารถนำผลลัพธ์เหล่านี้ไปรวมเข้าในกระบวนการทำงานเดียวกันที่อธิบายไว้ในบทความเกี่ยวกับการทำระบบรายงานการตรวจสอบความพร้อมก่อนส่งพิมพ์ PDF (preflight reports) แบบอัตโนมัติ
ขอบเขตที่ตัวตรวจสอบความถูกต้องหยุดทำงานตามความเป็นจริง
เอนจิน Schematron ถูกสร้างขึ้นบน MSXML, ซึ่งจะประมวลผลคำสั่ง XPath 1.0 ไฟล์กฎ EN 16931 ประกาศการทำงาน queryBinding="xslt2" และบรรจุข้อความยืนยัน 424 รายการ ซึ่งส่วนใหญ่ใช้เฉพาะฟังก์ชัน XPath 1.0 เช่น string-length และ substring-after — ซึ่งสามารถรันแบบเนทีฟได้ ส่วนน้อยจะพึ่งพาโครงสร้างของ XPath 2.0 เช่น การแปลงค่า xs:decimal หรือฟังก์ชัน exists(); HotPDF จะสแกนนิพจน์การทดสอบล่วงหน้าเพื่อหาโทเค็นของ XPath 2.0 ที่รู้จัก และทำเครื่องหมายกฎเหล่านั้นว่า Skipped พร้อมระบุระดับความรุนแรงเป็นระดับข้อมูล (info) แทนที่จะพยายามประมวลผลซึ่งอาจทำให้เกิดการตีความผิดพลาดอย่างเงียบเชียบ รายงานสรุปจะแสดงจำนวนการประมวลผลและการข้ามแยกกัน เพื่อให้บันทึกของคุณระบุถึงจำนวนกฎที่ได้ทำการตรวจสอบอย่างชัดเจน บน Delphi 7 ที่ไม่มีการผูกเชื่อมต่อ MSXML ตัวยูนิตจะคอมไพล์โค้ดจำลองที่รายงานว่าเอนจินไม่พร้อมใช้งานโดยไม่รบกวนผลลัพธ์ของคอนเทนเนอร์; เอนจินแบบเต็มรูปแบบต้องใช้ Delphi XE2 ขึ้นไป
ข้อจำกัดประการที่สองมีความสำคัญยิ่งกว่า ผลลัพธ์ผ่าน (สีเขียว) จาก HPDFValidateEInvoice หมายถึงโครงสร้างคอนเทนเนอร์มีความถูกต้องและไม่มีการละเมิดกฎทางธุรกิจที่ได้รับการประมวลผล — ซึ่งไม่ใช่การรับรองความสอดคล้องทางกฎหมายภาษี กฎส่วนขยายของประเทศ, ข้อกำหนดเฉพาะของผู้รับ และภาระผูกพันทางกฎหมายเกี่ยวกับเนื้อหาใบแจ้งหนี้ล้วนอยู่ในระดับที่สูงกว่าแกนหลัก EN 16931 และอยู่นอกเหนือขอบเขตของไลบรารีใดๆ ให้ถือว่าระบบตรวจสอบความถูกต้องทำหน้าที่เป็นประตูกรองที่คัดกรองใบแจ้งหนี้ที่มีโครงสร้างชำรุดเสียหายออกจากกล่องข้อความขาออกของคุณ และทำเครื่องหมายเอกสารที่ขัดแย้งในกล่องข้อความขาเข้าของคุณ ให้รันระบบตรวจสอบ XPath 2.0 เต็มรูปแบบในกระบวนการทำงานของคุณเมื่อจำเป็นต้องได้รับการรับรอง และปล่อยให้ที่ปรึกษาด้านภาษีของคุณดูแลเรื่องความสอดคล้องทางกฎหมายภาษี APIs การฝัง, การตรวจจับ และการตรวจสอบความถูกต้องที่แสดงไว้ที่นี่เป็นส่วนหนึ่งของ HotPDF Component สำหรับ Delphi และ C++Builder พร้อมคลังตัวอย่าง E-Invoice ที่สมบูรณ์ซึ่งครอบคลุมโปรไฟล์ ZUGFeRD และ Factur-X ทั้ง 7 โปรไฟล์