Factur-XまたはZUGFeRDのインボイスは、1つのファイル名をまとった2つのドキュメントです。外側のドキュメントはPDF/A-3コンテナであり、アーカイブリーダーはこれを今後10年間受け入れる必要があります。内側のドキュメントはXMLインボイスであり、買い手の会計システムはこれをEN 16931に照らして解析する必要があります。壊れたインボイスを本番環境に出荷してしまう間違いは、最初のものを正しく作成すれば、2番目のものも自動的に正しくなると信じていることです。そうではありません。ファイルが欠陥のないPDF/A-3であっても、税務当局が受け入れないXMLを保持している可能性がありますし、アーカイブの検証に失敗するコンテナの内部に教科書通りのEN 16931 XMLを保持している可能性もあります。この2つのレイヤーは、互いについて何も知らない2つの異なるツールによって検証され、実際のパイプラインは両方を満たさなければなりません
2つのバリデーター、2つの異なる質問
veraPDFは、PDF/Aのリファレンス実装です。それをインボイスに向けると、1つの質問に答えます:これは準拠したPDF/A-3ファイルか? それはISO 19005-3が関心を持つ事柄をチェックします。すべてのフォントが埋め込まれているか。OutputIntentが存在するか。XMPメタデータは正しいパートと適合性レベルを宣言しているか。電子インボイスの場合、XMLは/AFRelationshipとドキュメントカタログの/AF配列内のエントリを持つ埋め込みファイルとして同乗するため、PDF/A-3が要求する関連ファイル(associated-file)の配管もチェックします。veraPDFは、インボイスの合計が合っているかどうかについては何も言いません。それはその権限外だからです
Mustangは、Mustangprojectのオープンソースバリデーターです。それは直交する質問をします:埋め込まれたXMLは有効なインボイスか? 宣言されたプロファイルのスキーマに対してXMLを実行し、次にEN 16931のビジネスルールと、その上に重ねられた国別のルールセット(XRechnungのCIUSもその1つ)を適用します。合計がそれを要求する場合に売り手のVAT識別子が存在するかどうか、値引きおよび追加料金の金額がドキュメントの合計と一致するかどうか、XML内のプロファイルURNがファイルが主張するものと一致するかどうかをチェックします。Mustangは、周囲のPDFがフォントを埋め込んでいるかどうかは気にしません。それはveraPDFの仕事だからです
どちらのツールも、もう一方のスーパーセットではありません。veraPDFは、無意味なXMLを囲む構造的に完璧なコンテナをパスさせます。Mustangは、OutputIntentが欠落しているコンテナにラップされた完璧なXMLをパスさせます。それぞれが、もう一方が盲目である欠陥のクラスを正確にキャッチします。これが、本格的な検証ハーネスが両方を実行し、両方が同意した場合にのみファイルを出荷可能として扱うことのすべての理由です
検証マトリックス
ライブラリが両方のゲートを生き残るファイルを生成することを証明するために、ハーネスはマトリックスを構築します。6つのインボイスプロファイルが、ヨーロッパのパイプラインが実際に満たす範囲をカバーします:Factur-X EN 16931、Factur-X BASIC、Factur-X EXTENDEDフランスB2Bバリアント、XRechnung 3.0、ZUGFeRD 1.0 COMFORT、およびZUGFeRD 2.0 BASICです。レベルBとレベルUの要件はUnicodeマッピングで分岐し、一方に合格したファイルがもう一方に不合格になる可能性があるため、各プロファイルは2つのPDF/Aサブ適合性レベル、3bと3uに対して生成されます。6つのプロファイル×2つのレベルで12個のファイルになり、それらすべてがGUIサンプルが出荷するのと同じコードパスによってヘッドレスで構築されるため、テスト対象のアーティファクトはテスト用に手動で調整されたものではありません
ジェネレーターは12個すべてを書き込み、スクリプトがそれぞれを両方のバリデーターにフィードします。最初のフル実行で、veraPDFは12個すべてに合格しました。関連ファイルが登録され、XMP適合性が宣言され、出力インテントが適切な場所にあるなど、コンテナの配管は全体にわたって正確でした。Mustangは8個に合格しました。4つのインボイスは、ビジネスルールバリデーターが拒否したXMLを保持する、構造的に有効なPDF/A-3ファイルでした。これこそまさに、2ツールアプローチが表面化させるために存在している分割です。もしハーネスがveraPDFだけを信頼していたら、その4つは完成したように見えたでしょう
ギャップを埋めた2つの修正
4つのMustangの失敗は2つの明確な原因から生じており、それぞれに対する修正は、あなた自身でこれらのプロファイルを生成する前に知っておく価値のある詳細です
1つ目は、Factur-X EXTENDEDフランスB2Bプロファイルでした。元のジェネレーターは内部ラベルを適合性レベルとして渡し、内部URNをガイドラインとして渡したため、Mustangは「無効な適合性値」エラーとそれに続く「サポートされていないプロファイルタイプ」エラーでファイルを拒否しました。理由は、XMPのfx:ConformanceLevelフィールドは独自のプロファイル命名のためのフリーテキストスロットではないからです。Factur-Xは、そのために正確に5つの標準値を定義しています:MINIMUM、BASIC WL、BASIC、EN 16931、およびEXTENDEDです。XMPメタデータに関する限り、フランス固有のB2Bインボイスは依然としてEXTENDEDプロファイルのドキュメントです。インボイスのフランス的な特徴は、6番目の適合性値を考案することによって表現されるのではありません。それは国コードFRと、EN 16931に準拠したCIUSを示すurn:cen.eu:en16931:2017#conformant#プレフィックスを保持しなければならないXML内部のガイドライン識別子によって表現されます。国コードとしてFRを使用し、正しいガイドラインURNを使用して標準のEXTENDED値を渡すことで、ファイルは準拠しました
ライブラリAPIでは、これは適合性、国、およびガイドラインが揃えられたAddFacturXAssociatedFileFromStringの呼び出しです。適合性レベルの引数は標準トークンを保持し、国コードの引数はFRを保持し、ガイドラインURNは渡されるXMLバイト内に存在します
var
FileID: Integer;
begin
PDF.SetPDFAMode(5); // PDF/A-3b
PDF.NewDocument;
// ... 人間が読めるインボイスページを描画する ...
// ExtendedXMLは以下の形式のEN 16931ガイドラインURNを保持する
// urn:cen.eu:en16931:2017#conformant#urn:factur-x.eu:1p0:extended
FileID := PDF.AddFacturXAssociatedFileFromString(
ExtendedXML,
'EXTENDED', // 内部ラベルではなく、標準のfx:ConformanceLevel
'factur-x.xml',
'Factur-X EXTENDED invoice',
'Alternative', // /AFRelationship
'1.0',
'FR'); // フランスB2Bは適合性によってではなく、国コードによってマークされる
if FileID = 0 then
raise Exception.Create('Factur-X attachment rejected');
PDF.SaveToFile('02_Factur-X-EXTENDED-FR_PDFA-3b.pdf');
end;
2つ目の原因はZUGFeRD 1.0 COMFORTプロファイルであり、これはメタデータとは何の関係もありませんでした。ZUGFeRD 1.0は:1p0 XSDに対して検証されますが、これは散文の要約が示唆するよりもカーディナリティ(濃度)について厳格です。XSDは、ヘッダー決済の合計ram:SpecifiedTradeSettlementMonetarySummationがram:ChargeTotalAmountとram:AllowanceTotalAmountをそれぞれ正確に1回ずつ含むことを要求しています。生成されたXMLは両方を省略していたため、Mustangは要素が正確に1回出現しなければならないと報告しました。スキーマがminOccursが1であると言っている場合、これらはオプションではありません。追加料金や値引きがない場合は0.00の値で、ram:LineTotalAmountの直後にXSDのシーケンス順序で両方を出力することで、スキーマは満たされました。ゼロは存在する要素であり、存在しない要素はスキーマ違反です。これら2つの修正を適用することで、マトリックスはveraPDFで12中12を維持しながら、Mustangでも12中12になりました
無効を有効に反転させるXRechnungフィールド
XRechnungは独自の注意に値します。なぜなら、そのドイツのCIUSはベースのEN 16931セットにはないビジネスルールを追加しており、それらは一見するとドキュメントに何も問題がないように見える方法で失敗するからです。そのうちの2つは電子アドレスに関するものです。BT-34は売り手の電子アドレスであり、BT-49は買い手の電子アドレスです。これらは、ドイツの公共部門ポータルがインボイスを配信および確認するために使用するルーティングエンドポイントです。ベースのEN 16931モデルはこれらをオプションとして扱います。XRechnungはそうしません。どちらか一方を省略すると、インボイスは整形式(well-formed)でスキーマも有効ですが、拒否されます
3つ目はルールBR-DE-6であり、これは売り手の連絡先電話番号が存在することを要求します。これは、データというよりはプレゼンテーションのように感じられるため、開発者が脱落させがちな種類のフィールドです。これが存在しないと、明らかに欠落しているものではなく、売り手の連絡先グループを指し示す検証の失敗を引き起こします。BT-34、BT-49、および売り手の電話番号を提供することが、XRechnungファイルをMustangの下で無効から有効に移行させるものであり、その3つはすべてXML内に存在するため、これによってveraPDFが見るものは何も変わりません
ライブラリの出力をバリデーターに接続する
ハーネスの背後にあるアーキテクチャの要点は、あらゆるビジネスシステムに一般化できます。PDFライブラリは、準拠したコンテナを書き込み、XMLを埋め込みます。それはEN 16931ビジネスルールの権威になろうとはしませんし、そうすべきでもありません。ライブラリ内のValidateFacturXInvoiceは、カタログの/AF配列、埋め込みファイルの名前ツリー、XMPのDocumentFileName、プロファイル、ガイドライン、および/AFRelationshipがすべて一致しているというコンテナの一貫性をチェックしますが、税金コードを検証したり金額を照合したりはしません。正しい分業は、ハーネスがXMLをMustangに渡すのとまったく同じように、ビジネスシステムがXMLを抽出し、それを専用のインボイスバリデーターに渡すことです
ファイルを読み戻すことで、実際に何が書き込まれたかがわかります。DetectFacturXInvoiceはインボイスが認識されたかどうかを報告し、GetFacturXInvoiceInfoはタグによってメタデータフィールドを読み取ります。タグ1は埋め込みファイル名、タグ2はXMPのDocumentFileName、タグ5は適合性レベル、タグ6はガイドライン識別子、タグ7は/AFRelationshipです。読み戻した適合性レベルが内部ラベルではなく標準トークンであることを確認することは、ファイルがビルドから出る前にEXTENDEDのミスを見つけるための最もコストのかからない方法です
function ExtractAndInspect(const PdfPath: string): AnsiString;
var
Profile, Guideline: WideString;
begin
Result := '';
PDF.LoadFromFile(PdfPath);
if PDF.DetectFacturXInvoice = 1 then
begin
Profile := PDF.GetFacturXInvoiceInfo(5); // fx:ConformanceLevel
Guideline := PDF.GetFacturXInvoiceInfo(6); // XML guideline ID
Writeln('Profile: ', Profile);
Writeln('Guideline: ', Guideline);
// 生のXMLを専用のEN 16931 / Mustangバリデーターに渡す。
Result := PDF.ExtractFacturXXMLToString;
end;
end;
ExtractFacturXXMLToStringは、生のXMLバイトをAnsiStringとして返し、ファイルに書き込んだり、バリデータープロセスにストリーミングしたりする準備が整います。テストハーネスにおいて、そのターゲットはMustangであり、コマンドラインのjarを通じて呼び出され、veraPDFは同じファイルに対する同じパスで実行されます。配線は小さいです。コンソールジェネレーターEInvoiceValidation.dprは、サンプルの共有インボイスモデルを使用して12のファイルを書き込み、スクリプトrun-validation.ps1は出力ディレクトリに対して両方のバリデーターを駆動し、合格と不合格のテーブルを出力します。ライブラリで生成し、外部のバリデーターで検証するという同じ2ステップの形は、インボイス生成へのすべての変更に対して継続的インテグレーション(CI)ジョブが実行すべきものです。なぜなら、ファイルが両方のレイヤーを満たしていることを知る唯一の方法は、両方のツールに尋ねることだからです
パイプラインが署名する前にコンテナを証明(certify)する必要もある場合、この作業のプリフライトの側面は、DelphiでのPDF/AおよびPDF/UAプリフライトのチュートリアルでカバーされており、より広範な「証明して署名する(certify-then-sign)」フローはコンプライアンスと署名のワークベンチで説明されています。どちらも、ここで使用されているPDF/A、関連ファイル、およびメタデータのAPIとともに、DelphiおよびC++Builder向けのDelphi PDF Libraryの一部として出荷されているのと同じ生成パスに基づいています