C#での請求書処理:.NETでPDF請求書を生成、抽出、自動化する

This article was translated from English: Does it need improvement?
Translated
View the article in English

請求書処理 in C# .HTMLテンプレートからプロフェッショナルなPDF請求書を生成し、ZUGFeRDFactur-X電子請求書標準に準拠し、テキスト解析とAI処理を使用して受信した請求書から構造化データを抽出し、QuickBooks、Xero、SAPのような会計システムと統合するバッチ自動化パイプラインを構築します。

TL;DR:クイックスタートガイド

このチュートリアルでは、C# .NETによるPDF請求書の生成、抽出、自動化について、電子請求書のコンプライアンス、AIを利用した解析、会計システムとの統合を含めて説明します。

  • 対象者: .NET開発者で、請求書発行モジュール、買掛金の自動化、または電子請求書のコンプライアンスを構築している人。
  • 構築するもの 行項目と税金計算を含むHTMLテンプレートによる請求書生成、支払いリンク用のQRコード、ZUGFeRD/Factur-X準拠のPDF/A-3出力、正規表現によるテキスト抽出、AIによる請求書解析、会計システム統合によるバッチ処理。
  • Where it runs: .NET 10、.NET 8 LTS、.NET Framework 4.6.2+、.NET Standard 2.0。外部サービスへの依存はありません。
  • このアプローチを使用する場合: 請求書PDFの生成、EUの電子請求書発行義務に対応する必要がある場合、または買掛金のためにベンダーの請求書からデータを抽出する必要がある場合。
  • 技術的に重要な理由: IronPDFはピクセルパーフェクトな精度でHTMLをPDFにレンダリングし、埋め込まれたXMLのためにPDF/A-3をサポートし、非構造化請求書を構造化データに変換するために正規表現またはAIと対になるテキスト抽出APIを提供します。

わずか数行のコードで最初のPDF請求書を生成します:

Nuget Icon今すぐ NuGet で PDF を作成してみましょう:

  1. NuGet パッケージ マネージャーを使用して IronPDF をインストールします

    PM > Install-Package IronPdf

  2. このコード スニペットをコピーして実行します。

    var renderer = new IronPdf.ChromePdfRenderer();
    var pdf = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Total: $500.00</p>");
    pdf.SaveAs("invoice.pdf");
  3. 実際の環境でテストするためにデプロイする

    今すぐ無料トライアルでプロジェクトに IronPDF を使い始めましょう
    arrow pointer

IronPDFを購入または30日間のトライアルにサインアップした後、アプリケーションの最初にライセンスキーを追加してください。

IronPdf.License.LicenseKey = "KEY";
IronPdf.License.LicenseKey = "KEY";
$vbLabelText   $csharpLabel

今日あなたのプロジェクトでIronPDFを無料トライアルで使用開始。

最初のステップ:
green arrow pointer
NuGet 購入の準備ができていませんか?

PM >  Install-Package IronPdf

IronPDFNuGet でチェックしてください。1000万回以上のダウンロードで、C#によるPDF開発を変革しています。 DLL または Windowsインストーラー をダウンロードすることもできます。

目次

NuGet 購入の準備ができていませんか?

PM >  Install-Package IronPdf

IronPDFNuGet でチェックしてください。1000万回以上のダウンロードで、C#によるPDF開発を変革しています。 DLL または Windowsインストーラー をダウンロードすることもできます。

インボイスのライフサイクルとは何ですか?

コードに飛び込む前に、請求書が最新のビジネスシステムでどのように処理されるかを理解することが役立ちます。 請求書のライフサイクルは、生成、配布、受領、データ抽出、会計統合の5つの段階から構成されます。

請求書の作成プロセス ある企業が、明細項目、価格、税金計算、支払い条件、ブランディングを含む請求書を作成します。 請求書はプロフェッショナルな外観で、すべての法的要件を満たす必要があります。 次に、請求書を電子メール、顧客ポータル、または従来の郵便で顧客に送信する配信です。 顧客がこの文書を受け取ると、買掛金チームはその文書を取り込み、処理の準備を整えます。 データ抽出では、請求書からベンダーの詳細、明細、合計、支払期日などの主要な情報を抽出し、発注書と照合します。 最後に、会計統合は、支払いや記録保持のために、このデータをQuickBooks、Xero、またはSAPのような財務システムに移動します。

なぜPDFは、これほど長い年月を経てもなお、最も広く使われているフォーマットなのでしょうか? そのためには、さまざまな利点を組み合わせる必要があります。 PDFは、使用するデバイスやオペレーティングシステムに関係なく、請求書の書式を一定に保ちます。 Windows、Mac、携帯電話のいずれで請求書を開いても、デザインどおりに表示されます。 また、PDFは誤って変更することが難しいため、WordやExcelのような形式よりも文書の完全性を守ることができます。 信頼性のためにデジタル署名を追加し、セキュリティのために暗号化を使用することができます。 最も重要なことは、PDFはあらゆるビジネスシステムが認識し、サポートする普遍的な標準になっているということです。

もちろん、課題もあります。 PDFは、コンピュータが処理するためではなく、人が読むために簡単に作られています。 PDFは、情報を構造化データとして保存する代わりに、テキスト、線、図形、画像をページ上のどこに表示されるかに基づいて保存します。 そのためIronPdfのようなツールはとても役に立ちます。


C#でプロフェッショナルなPDF請求書を作成する方法

プログラムで請求書を作成するには、顧客情報、行項目、計算などの構造化データを洗練されたPDF文書に変換する必要があります。 IronPdfはHTMLとCSSを活用することで、ほとんどの開発者がすでによく知っている技術でありながら、驚くほど簡単に翻訳することができます。

このチュートリアルでは、実世界で遭遇する可能性の高いシナリオを説明します。 以下のこちらからプロジェクトをダウンロードすることもできます。

請求書HTMLテンプレートの作成方法

IronPdfによる請求書作成の基本はHTMLです。 低レベルのPDF描画コマンドと格闘するのではなく、標準的なHTMLとCSSを使用して請求書をデザインし、IronPDFのChromeベースのレンダリングエンジンがピクセルパーフェクトなPDFに変換します。

以下は、このアプローチを示す基本的な請求書テンプレートです:

:path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/basic-invoice-template.cs
using IronPdf;

// Define the HTML template for a basic invoice
// Uses inline CSS for styling headers, tables, and totals
string invoiceHtml = @"
E html>


le>
body { font-family: Arial, sans-serif; padding: 40px; }
.header { text-align: right; margin-bottom: 40px; }
.company-name { font-size: 24px; font-weight: bold; color: #333; }
.invoice-title { font-size: 32px; margin: 20px 0; }
.bill-to { margin: 20px 0; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th { background-color: #2A95D5; color: white; padding: 10px; text-align: left; }
td { padding: 10px; border-bottom: 1px solid #ddd; }
.total { text-align: right; font-size: 20px; font-weight: bold; margin-top: 20px; }
yle>


 class='header'>
<div class='company-name'>Your Company Name</div>
<div>123 Business Street</div>
<div>City, State 12345</div>
v>

 class='invoice-title'>INVOICE</div>

 class='bill-to'>
<strong>Bill To:</strong><br>
Customer Name<br>
456 Customer Avenue<br>
City, State 67890
v>

le>
<tr>
    <th>Description</th>
    <th>Quantity</th>
    <th>Price</th>
    <th>Total</th>
</tr>
<tr>
    <td>Web Development Services</td>
    <td>10 hours</td>
    <td>$100.00</td>
    <td>$1,000.00</td>
</tr>
<tr>
    <td>Consulting</td>
    <td>5 hours</td>
    <td>$150.00</td>
    <td>$750.00</td>
</tr>
ble>

 class='total'>Total: $1,750.00</div>

;

// Initialize the Chrome-based PDF renderer
var renderer = new ChromePdfRenderer();

// Convert the HTML string to a PDF document
var pdf = renderer.RenderHtmlAsPdf(invoiceHtml);

// Save the generated PDF to disk
pdf.SaveAs("basic-invoice.pdf");
$vbLabelText   $csharpLabel

サンプル出力

このアプローチは、非常に柔軟性があります。 フレックスボックス、グリッドレイアウト、カスタムフォントなどの最新機能を含め、Chromeで動作するCSSはすべてPDFで動作します。 URLやローカルのファイルパスを参照することで、外部のスタイルシートや画像を使用することもできます。

動的な行項目を追加して合計を計算する方法

実際の請求書には、静的なコンテンツはほとんどありません。 データベースから行項目を入力し、小計を計算し、税率を適用し、通貨値をフォーマットする必要があります。 次の例では、動的な請求書生成のためのプロダクション・レディのパターンを示します:

using IronPdf;
using System;
using System.Collections.Generic;
using System.Linq;

// Represents a single line item on an invoice
public class InvoiceLineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }

    // Auto-calculates line total from quantity and unit price
    public decimal Total => Quantity * UnitPrice;
}

// Represents a complete invoice with customer details and line items
public class Invoice
{
    public string InvoiceNumber { get; set; }
    public DateTime InvoiceDate { get; set; }
    public string CustomerName { get; set; }
    public string CustomerAddress { get; set; }
    public List<InvoiceLineItem> LineItems { get; set; }

    // Computed properties for invoice totals
    public decimal Subtotal => LineItems.Sum(item => item.Total);
    public decimal TaxRate { get; set; } = 0.08m;  // Default 8% tax rate
    public decimal Tax => Subtotal * TaxRate;
    public decimal Total => Subtotal + Tax;
}

// Generates PDF invoices from Invoice objects using HTML templates
public class InvoiceGenerator
{
    public PdfDocument GenerateInvoice(Invoice invoice)
    {
        // Build HTML table rows dynamically from line items
        string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@"
            <tr>
                <td>{item.Description}</td>
                <td>{item.Quantity}</td>
                <td>${item.UnitPrice:F2}</td>
                <td>${item.Total:F2}</td>
            </tr>
        "));

        // Build the complete HTML invoice using string interpolation
        // All invoice data is injected into the template dynamically
        string invoiceHtml = $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: Arial, sans-serif; padding: 40px; }}
        .header {{ text-align: right; margin-bottom: 40px; }}
        .company-name {{ font-size: 24px; font-weight: bold; color: #333; }}
        .invoice-details {{ margin: 20px 0; }}
        table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
        th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }}
        td {{ padding: 10px; border-bottom: 1px solid #ddd; }}
        .totals {{ text-align: right; margin-top: 20px; }}
        .totals div {{ margin: 5px 0; }}
        .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }}
    </style>
</head>
<body>
    <div class='header'>
        <div class='company-name'>Your Company Name</div>
    </div>

    <h1>INVOICE</h1>

    <div class='invoice-details'>
        <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br>
        <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br>
        <strong>Bill To:</strong> {invoice.CustomerName}<br>
        {invoice.CustomerAddress}
    </div>

    <table>
        <tr>
            <th>Description</th>
            <th>Quantity</th>
            <th>Unit Price</th>
            <th>Total</th>
        </tr>
        {lineItemsHtml}
    </table>

    <div class='totals'>
        <div>Subtotal: ${invoice.Subtotal:F2}</div>
        <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div>
        <div class='grand-total'>Total: ${invoice.Total:F2}</div>
    </div>
</body>
</html>";

        // Render HTML to PDF and return the document
        var renderer = new ChromePdfRenderer();
        return renderer.RenderHtmlAsPdf(invoiceHtml);
    }
}
using IronPdf;
using System;
using System.Collections.Generic;
using System.Linq;

// Represents a single line item on an invoice
public class InvoiceLineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }

    // Auto-calculates line total from quantity and unit price
    public decimal Total => Quantity * UnitPrice;
}

// Represents a complete invoice with customer details and line items
public class Invoice
{
    public string InvoiceNumber { get; set; }
    public DateTime InvoiceDate { get; set; }
    public string CustomerName { get; set; }
    public string CustomerAddress { get; set; }
    public List<InvoiceLineItem> LineItems { get; set; }

    // Computed properties for invoice totals
    public decimal Subtotal => LineItems.Sum(item => item.Total);
    public decimal TaxRate { get; set; } = 0.08m;  // Default 8% tax rate
    public decimal Tax => Subtotal * TaxRate;
    public decimal Total => Subtotal + Tax;
}

// Generates PDF invoices from Invoice objects using HTML templates
public class InvoiceGenerator
{
    public PdfDocument GenerateInvoice(Invoice invoice)
    {
        // Build HTML table rows dynamically from line items
        string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@"
            <tr>
                <td>{item.Description}</td>
                <td>{item.Quantity}</td>
                <td>${item.UnitPrice:F2}</td>
                <td>${item.Total:F2}</td>
            </tr>
        "));

        // Build the complete HTML invoice using string interpolation
        // All invoice data is injected into the template dynamically
        string invoiceHtml = $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: Arial, sans-serif; padding: 40px; }}
        .header {{ text-align: right; margin-bottom: 40px; }}
        .company-name {{ font-size: 24px; font-weight: bold; color: #333; }}
        .invoice-details {{ margin: 20px 0; }}
        table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }}
        th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }}
        td {{ padding: 10px; border-bottom: 1px solid #ddd; }}
        .totals {{ text-align: right; margin-top: 20px; }}
        .totals div {{ margin: 5px 0; }}
        .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }}
    </style>
</head>
<body>
    <div class='header'>
        <div class='company-name'>Your Company Name</div>
    </div>

    <h1>INVOICE</h1>

    <div class='invoice-details'>
        <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br>
        <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br>
        <strong>Bill To:</strong> {invoice.CustomerName}<br>
        {invoice.CustomerAddress}
    </div>

    <table>
        <tr>
            <th>Description</th>
            <th>Quantity</th>
            <th>Unit Price</th>
            <th>Total</th>
        </tr>
        {lineItemsHtml}
    </table>

    <div class='totals'>
        <div>Subtotal: ${invoice.Subtotal:F2}</div>
        <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div>
        <div class='grand-total'>Total: ${invoice.Total:F2}</div>
    </div>
</body>
</html>";

        // Render HTML to PDF and return the document
        var renderer = new ChromePdfRenderer();
        return renderer.RenderHtmlAsPdf(invoiceHtml);
    }
}
$vbLabelText   $csharpLabel

サンプル出力

Invoiceクラスは、小計、税金、合計の計算プロパティを持つすべての請求書データをカプセル化します。 ジェネレーターは、文字列補間を使用してこのデータをHTMLに変換し、PDFにレンダリングします。 このように関係性を分離することで、コードの保守性とテストが可能になります。

請求書に会社のブランド名と透かしを追加する方法

プロフェッショナルな請求書には、ロゴや、場合によっては支払い状況を示す透かしなどのブランディング要素が必要です。 IronPDFはHTML内の埋め込み画像とレンダリング後のプログラムによる透かしの両方をサポートしています。

:path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/branding-watermarks.cs
using IronPdf;
using IronPdf;

var renderer = new ChromePdfRenderer();

// Invoice HTML template with company logo embedded via URL
// Logo can also be Base64-encoded or a local file path
string htmlWithLogo = @"
E html>


le>
body { font-family: Arial, sans-serif; padding: 40px; }
.logo { width: 200px; margin-bottom: 20px; }
yle>


 style='text-align: center;'>
<img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' />
v>
INVOICE</h1>
strong>Invoice Number:</strong> INV-2024-001</p>
strong>Total:</strong> $1,250.00</p>

;

// Render the HTML to PDF
var pdf = renderer.RenderHtmlAsPdf(htmlWithLogo);

// Apply a diagonal "UNPAID" watermark to mark invoice status
// 30% opacity keeps the content readable while the watermark is visible
pdf.ApplyWatermark("<h1 style='color: red;'>UNPAID</h1>",
    opacity: 30,
    rotation: 45,
    verticalAlignment: IronPdf.Editing.VerticalAlignment.Middle);

pdf.SaveAs("invoice-with-watermark.pdf");
using IronPdf;
$vbLabelText   $csharpLabel

サンプル出力

ApplyWatermarkメソッドはHTMLコンテンツを受け付け、透かしの外観を完全に制御できます。 不透明度、回転、位置を調整して、必要な外観を正確に実現できます。 これは、文書全体を再生成することなく、請求書に"PAID"、"DRAFT"、"CANCELLED"などのマークを付ける場合に特に役立ちます。

支払いリンクにQRコードを埋め込む方法

最近の請求書にはQRコードが記載されていることが多く、顧客がスキャンすることで素早く支払いを済ませることができます。 IronPDFはPDF生成に重点を置いていますが、バーコード作成のためにIronQRとシームレスに動作します:

:path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/qr-code-payment.cs
using IronPdf;
using IronQr;
using IronSoftware.Drawing;

string invoiceNumber = "INV-2026-002";
decimal amount = 1500.00m;

// Create a payment URL with invoice details as query parameters
string paymentUrl = $"https://yourcompany.com/pay?invoice={invoiceNumber}&amount={amount}";

// Generate QR code from the payment URL using IronQR
QrCode qrCode = QrWriter.Write(paymentUrl);
AnyBitmap qrImage = qrCode.Save();
qrImage.SaveAs("payment-qr.png", AnyBitmap.ImageFormat.Png);

// Build invoice HTML with the QR code image embedded
// Customers can scan the QR to pay directly from their phone
string invoiceHtml = $@"
E html>


le>
body {{ font-family: Arial, sans-serif; padding: 40px; }}
.payment-section {{ margin-top: 40px; text-align: center;
                   border-top: 2px solid #eee; padding-top: 20px; }}
.qr-code {{ width: 150px; height: 150px; }}
yle>


INVOICE {invoiceNumber}</h1>
strong>Amount Due:</strong> ${amount:F2}</p>

 class='payment-section'>
<p><strong>Scan to Pay Instantly:</strong></p>
<img src='payment-qr.png' alt='Payment QR Code' class='qr-code' />
<p style='font-size: 12px; color: #666;'>
    Or visit: {paymentUrl}
</p>
v>

;

// Convert HTML to PDF and save
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(invoiceHtml);
pdf.SaveAs($"invoice-{invoiceNumber}.pdf");
$vbLabelText   $csharpLabel

サンプル出力

QRコードは支払いページに直接リンクするため、顧客の摩擦を減らし、キャッシュフローを加速します。 このパターンは、URLベースの支払い開始をサポートするすべての支払いプロバイダで動作します。


C&numでZUGFeRDとFactur-Xの電子請求書発行基準に準拠する方法

ヨーロッパでは、電子請求書の発行が急速に義務化されつつあります。 ドイツはZUGFeRDでリードし、フランスはFactur-Xで続きました。 これらの標準は、機械可読XMLデータをPDF請求書に埋め込み、人間が読める文書を維持しながら自動処理を可能にします。 これらの標準を理解し、実装することは、欧州市場で事業を展開する企業にとってますます不可欠になっています。

ZUGFeRDとは何ですか、そしてどのように機能しますか?

ZUGFeRD(Zentraler User Guide des Forums elektronische Rechnung Deutschland)は、PDF/A-3準拠の文書に請求書データをXMLファイルの添付ファイルとして埋め込むドイツの電子請求書規格です。 XMLが埋め込まれているため、OCRや解析なしで自動データ抽出が可能です。

この規格は、3つの適合レベルを定義しており、それぞれが徐々に構造化されたデータを提供する:

  • Basic:単純な自動処理に適したコア請求書データを含みます。
  • 快適さ:完全に自動化された請求書処理を可能にする詳細な情報を追加します。
  • 拡張: 業界を超えた複雑なビジネスシナリオのための包括的なデータを含みます。

XMLはUN/CEFACT Cross-Industry Invoice (CII)スキーマに準拠しており、これは欧州の電子請求書標準化の基礎となっている。

Factur-Xとは何ですか、ZUGFeRDとの違いは何ですか?

Factur-Xは、同じ規格のフランス語版です。 ZUGFeRD 2.0とFactur-Xは技術的に同一です。 これらのツールは、同じXMLスキーマと、欧州規格EN 16931に基づく適合性プロファイルを共有しています。ZUGFeRD仕様で作成された請求書はFactur-Xでも有効であり、その逆も同様です。

PDF/A-3請求書にXMLデータを埋め込む方法

IronPdfは準拠した電子請求書を作成するために必要な添付機能を提供します。 このプロセスでは、請求書PDFを生成し、CIIスキーマに従ってXMLデータを作成し、正しい命名規則でXMLを添付ファイルとして埋め込みます:

using System;
using System.Xml.Linq;

// Generates ZUGFeRD-compliant invoices by embedding structured XML data
// ZUGFeRD allows automated processing while keeping a human-readable PDF
public class ZUGFeRDInvoiceGenerator
{
    public void GenerateZUGFeRDInvoice(Invoice invoice)
    {
        // First, create the visual PDF that humans will read
        var renderer = new ChromePdfRenderer();
        string invoiceHtml = BuildInvoiceHtml(invoice);
        var pdf = renderer.RenderHtmlAsPdf(invoiceHtml);

        // Define the UN/CEFACT namespaces required by the ZUGFeRD standard
        // These are mandatory for compliance with European e-invoicing regulations
        XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100";
        XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100";
        XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100";

        // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema
        var zugferdXml = new XDocument(
            new XDeclaration("1.0", "UTF-8", null),
            new XElement(rsm + "CrossIndustryInvoice",
                new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName),

                // Document context identifies which e-invoicing guideline is being followed
                new XElement(rsm + "ExchangedDocumentContext",
                    new XElement(ram + "GuidelineSpecifiedDocumentContextParameter",
                        new XElement(ram + "ID", "urn:cen.eu:en16931:2017")
                    )
                ),

                // Core document identification: invoice number, type, and date
                new XElement(rsm + "ExchangedDocument",
                    new XElement(ram + "ID", invoice.InvoiceNumber),
                    new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT
                    new XElement(ram + "IssueDateTime",
                        new XElement(udt + "DateTimeString",
                            new XAttribute("format", "102"),
                            invoice.InvoiceDate.ToString("yyyyMMdd")
                        )
                    )
                ),

                // A complete implementation would include additional sections:
                // - Seller information (ram:SellerTradeParty)
                // - Buyer information (ram:BuyerTradeParty)
                // - Line items (ram:IncludedSupplyChainTradeLineItem)
                // - Payment terms (ram:SpecifiedTradePaymentTerms)
                // - Tax summaries (ram:ApplicableTradeTax)

                // Financial summary with all monetary totals
                new XElement(rsm + "SupplyChainTradeTransaction",
                    new XElement(ram + "ApplicableHeaderTradeSettlement",
                        new XElement(ram + "InvoiceCurrencyCode", "EUR"),
                        new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation",
                            new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal),
                            new XElement(ram + "TaxTotalAmount",
                                new XAttribute("currencyID", "EUR"),
                                invoice.Tax),
                            new XElement(ram + "GrandTotalAmount", invoice.Total),
                            new XElement(ram + "DuePayableAmount", invoice.Total)
                        )
                    )
                )
            )
        );

        // Save the XML to a temp file for embedding
        string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml";
        zugferdXml.Save(xmlPath);

        // Attach the XML to the PDF - filename must follow ZUGFeRD conventions
        pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data");

        // Final PDF contains both visual invoice and machine-readable XML
        pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf");
    }

    // Generates simple HTML for the visual portion of the invoice
    private string BuildInvoiceHtml(Invoice invoice)
    {
        return $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: Arial, sans-serif; padding: 40px; }}
        h1 {{ color: #333; }}
        .zugferd-notice {{ 
            margin-top: 30px; padding: 10px; 
            background: #f0f0f0; font-size: 11px; 
        }}
    </style>
</head>
<body>
    <h1>RECHNUNG / INVOICE</h1>
    <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p>
    <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p>
    <p><strong>Betrag:</strong> €{invoice.Total:F2}</p>

    <div class='zugferd-notice'>
        This invoice contains embedded ZUGFeRD data for automated processing.
    </div>
</body>
</html>";
    }
}
using System;
using System.Xml.Linq;

// Generates ZUGFeRD-compliant invoices by embedding structured XML data
// ZUGFeRD allows automated processing while keeping a human-readable PDF
public class ZUGFeRDInvoiceGenerator
{
    public void GenerateZUGFeRDInvoice(Invoice invoice)
    {
        // First, create the visual PDF that humans will read
        var renderer = new ChromePdfRenderer();
        string invoiceHtml = BuildInvoiceHtml(invoice);
        var pdf = renderer.RenderHtmlAsPdf(invoiceHtml);

        // Define the UN/CEFACT namespaces required by the ZUGFeRD standard
        // These are mandatory for compliance with European e-invoicing regulations
        XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100";
        XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100";
        XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100";

        // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema
        var zugferdXml = new XDocument(
            new XDeclaration("1.0", "UTF-8", null),
            new XElement(rsm + "CrossIndustryInvoice",
                new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName),

                // Document context identifies which e-invoicing guideline is being followed
                new XElement(rsm + "ExchangedDocumentContext",
                    new XElement(ram + "GuidelineSpecifiedDocumentContextParameter",
                        new XElement(ram + "ID", "urn:cen.eu:en16931:2017")
                    )
                ),

                // Core document identification: invoice number, type, and date
                new XElement(rsm + "ExchangedDocument",
                    new XElement(ram + "ID", invoice.InvoiceNumber),
                    new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT
                    new XElement(ram + "IssueDateTime",
                        new XElement(udt + "DateTimeString",
                            new XAttribute("format", "102"),
                            invoice.InvoiceDate.ToString("yyyyMMdd")
                        )
                    )
                ),

                // A complete implementation would include additional sections:
                // - Seller information (ram:SellerTradeParty)
                // - Buyer information (ram:BuyerTradeParty)
                // - Line items (ram:IncludedSupplyChainTradeLineItem)
                // - Payment terms (ram:SpecifiedTradePaymentTerms)
                // - Tax summaries (ram:ApplicableTradeTax)

                // Financial summary with all monetary totals
                new XElement(rsm + "SupplyChainTradeTransaction",
                    new XElement(ram + "ApplicableHeaderTradeSettlement",
                        new XElement(ram + "InvoiceCurrencyCode", "EUR"),
                        new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation",
                            new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal),
                            new XElement(ram + "TaxTotalAmount",
                                new XAttribute("currencyID", "EUR"),
                                invoice.Tax),
                            new XElement(ram + "GrandTotalAmount", invoice.Total),
                            new XElement(ram + "DuePayableAmount", invoice.Total)
                        )
                    )
                )
            )
        );

        // Save the XML to a temp file for embedding
        string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml";
        zugferdXml.Save(xmlPath);

        // Attach the XML to the PDF - filename must follow ZUGFeRD conventions
        pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data");

        // Final PDF contains both visual invoice and machine-readable XML
        pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf");
    }

    // Generates simple HTML for the visual portion of the invoice
    private string BuildInvoiceHtml(Invoice invoice)
    {
        return $@"
<!DOCTYPE html>
<html>
<head>
    <style>
        body {{ font-family: Arial, sans-serif; padding: 40px; }}
        h1 {{ color: #333; }}
        .zugferd-notice {{ 
            margin-top: 30px; padding: 10px; 
            background: #f0f0f0; font-size: 11px; 
        }}
    </style>
</head>
<body>
    <h1>RECHNUNG / INVOICE</h1>
    <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p>
    <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p>
    <p><strong>Betrag:</strong> €{invoice.Total:F2}</p>

    <div class='zugferd-notice'>
        This invoice contains embedded ZUGFeRD data for automated processing.
    </div>
</body>
</html>";
    }
}
$vbLabelText   $csharpLabel

サンプル出力

コンプライアンス上の重要な点は、正しいXML名前空間を使用すること、CIIスキーマ構造に従うこと、適切なファイル名でXMLを埋め込むことです。 TypeCode"380"は、この文書がUN/CEFACT標準内の商業送り状であることを明確に示す。

EU指令に対応した請求書の作成方法

欧州連合(EU)では、加盟国間での電子請求書発行の義務化が進んでいます。 イタリアではすでにB2B取引に義務付けられており、フランスでは2026年まで段階的に義務化、ドイツでは2025年からB2B電子請求書の義務化を発表している。ZUGFeRD/Factur-Xサポートを構築することで、これらの規制要件にシステムを備えることができます。

さまざまな標準をターゲットにできる、コンプライアンスを意識した請求書ジェネレーターのパターンを紹介します:

using IronPdf;
using System;

// Enum representing supported European e-invoicing standards
public enum InvoiceStandard
{
    None,
    ZUGFeRD,    // German standard - uses CII XML format
    FacturX,    // French standard - technically identical to ZUGFeRD 2.0
    Peppol      // Pan-European standard - uses UBL XML format
}

// Factory class that generates invoices compliant with different e-invoicing standards
// Allows switching between standards without changing core invoice generation logic
public class CompliantInvoiceGenerator
{
    public PdfDocument GenerateCompliantInvoice(Invoice invoice, InvoiceStandard standard)
    {
        // Generate the base PDF from HTML
        var renderer = new ChromePdfRenderer();
        string html = BuildInvoiceHtml(invoice);
        var pdf = renderer.RenderHtmlAsPdf(html);

        // Attach the appropriate XML format based on target market/regulation
        switch (standard)
        {
            case InvoiceStandard.ZUGFeRD:
            case InvoiceStandard.FacturX:
                // Both use Cross-Industry Invoice format, just different filenames
                EmbedCIIXmlData(pdf, invoice, standard);
                break;
            case InvoiceStandard.Peppol:
                // Peppol uses Universal Business Language format
                EmbedUBLXmlData(pdf, invoice);
                break;
        }

        return pdf;
    }

    // Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X)
    private void EmbedCIIXmlData(PdfDocument pdf, Invoice invoice, InvoiceStandard standard)
    {
        string xml = GenerateCIIXml(invoice);

        // Filename convention differs between German and French standards
        string filename = standard == InvoiceStandard.ZUGFeRD
            ? "zugferd-invoice.xml"
            : "factur-x.xml";

        System.IO.File.WriteAllText("temp-invoice.xml", xml);
        pdf.Attachments.AddFile("temp-invoice.xml", filename, $"{standard} Invoice Data");
    }

    // Creates and embeds UBL-format XML for Peppol network compliance
    private void EmbedUBLXmlData(PdfDocument pdf, Invoice invoice)
    {
        // UBL (Universal Business Language) is the Peppol standard format
        string xml = $@"<?xml version='1.0' encoding='UTF-8'?>
<Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'>
    <ID>{invoice.InvoiceNumber}</ID>
    <IssueDate>{invoice.InvoiceDate:yyyy-MM-dd}</IssueDate>
    <DocumentCurrencyCode>EUR</DocumentCurrencyCode>
    <LegalMonetaryTotal>
        <PayableAmount currencyID='EUR'>{invoice.Total}</PayableAmount>
    </LegalMonetaryTotal>
</Invoice>";

        System.IO.File.WriteAllText("peppol-invoice.xml", xml);
        pdf.Attachments.AddFile("peppol-invoice.xml", "invoice.xml", "Peppol UBL Invoice");
    }

    // Generates minimal CII XML structure for demonstration
    private string GenerateCIIXml(Invoice invoice)
    {
        return $@"<?xml version='1.0' encoding='UTF-8'?>
<rsm:CrossIndustryInvoice
    xmlns:rsm='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'
    xmlns:ram='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'>
    <rsm:ExchangedDocument>
        <ram:ID>{invoice.InvoiceNumber}</ram:ID>
        <ram:TypeCode>380</ram:TypeCode>
    </rsm:ExchangedDocument>
</rsm:CrossIndustryInvoice>";
    }

    private string BuildInvoiceHtml(Invoice invoice)
    {
        return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>";
    }
}
using IronPdf;
using System;

// Enum representing supported European e-invoicing standards
public enum InvoiceStandard
{
    None,
    ZUGFeRD,    // German standard - uses CII XML format
    FacturX,    // French standard - technically identical to ZUGFeRD 2.0
    Peppol      // Pan-European standard - uses UBL XML format
}

// Factory class that generates invoices compliant with different e-invoicing standards
// Allows switching between standards without changing core invoice generation logic
public class CompliantInvoiceGenerator
{
    public PdfDocument GenerateCompliantInvoice(Invoice invoice, InvoiceStandard standard)
    {
        // Generate the base PDF from HTML
        var renderer = new ChromePdfRenderer();
        string html = BuildInvoiceHtml(invoice);
        var pdf = renderer.RenderHtmlAsPdf(html);

        // Attach the appropriate XML format based on target market/regulation
        switch (standard)
        {
            case InvoiceStandard.ZUGFeRD:
            case InvoiceStandard.FacturX:
                // Both use Cross-Industry Invoice format, just different filenames
                EmbedCIIXmlData(pdf, invoice, standard);
                break;
            case InvoiceStandard.Peppol:
                // Peppol uses Universal Business Language format
                EmbedUBLXmlData(pdf, invoice);
                break;
        }

        return pdf;
    }

    // Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X)
    private void EmbedCIIXmlData(PdfDocument pdf, Invoice invoice, InvoiceStandard standard)
    {
        string xml = GenerateCIIXml(invoice);

        // Filename convention differs between German and French standards
        string filename = standard == InvoiceStandard.ZUGFeRD
            ? "zugferd-invoice.xml"
            : "factur-x.xml";

        System.IO.File.WriteAllText("temp-invoice.xml", xml);
        pdf.Attachments.AddFile("temp-invoice.xml", filename, $"{standard} Invoice Data");
    }

    // Creates and embeds UBL-format XML for Peppol network compliance
    private void EmbedUBLXmlData(PdfDocument pdf, Invoice invoice)
    {
        // UBL (Universal Business Language) is the Peppol standard format
        string xml = $@"<?xml version='1.0' encoding='UTF-8'?>
<Invoice xmlns='urn:oasis:names:specification:ubl:schema:xsd:Invoice-2'>
    <ID>{invoice.InvoiceNumber}</ID>
    <IssueDate>{invoice.InvoiceDate:yyyy-MM-dd}</IssueDate>
    <DocumentCurrencyCode>EUR</DocumentCurrencyCode>
    <LegalMonetaryTotal>
        <PayableAmount currencyID='EUR'>{invoice.Total}</PayableAmount>
    </LegalMonetaryTotal>
</Invoice>";

        System.IO.File.WriteAllText("peppol-invoice.xml", xml);
        pdf.Attachments.AddFile("peppol-invoice.xml", "invoice.xml", "Peppol UBL Invoice");
    }

    // Generates minimal CII XML structure for demonstration
    private string GenerateCIIXml(Invoice invoice)
    {
        return $@"<?xml version='1.0' encoding='UTF-8'?>
<rsm:CrossIndustryInvoice
    xmlns:rsm='urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100'
    xmlns:ram='urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100'>
    <rsm:ExchangedDocument>
        <ram:ID>{invoice.InvoiceNumber}</ram:ID>
        <ram:TypeCode>380</ram:TypeCode>
    </rsm:ExchangedDocument>
</rsm:CrossIndustryInvoice>";
    }

    private string BuildInvoiceHtml(Invoice invoice)
    {
        return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>";
    }
}
$vbLabelText   $csharpLabel

このアーキテクチャにより、請求書作成ロジックのコアを再構築することなく、新しい標準が出現したときに追加することができます。 列挙ベースのアプローチにより、どのコンプライアンス・モードを使用するかをユーザーや構成に簡単に決定させることができます。


C&numでPDFの請求書からデータを抽出する方法

請求書の作成は、方程式の半分でしかありません。 また、ほとんどの企業はベンダーから請求書を受け取り、処理のためにデータを抽出する必要があります。 IronPdfは請求書データキャプチャの基盤となる強力なテキスト抽出機能を提供します。

PDF請求書からテキストを抽出する方法

最も基本的な抽出操作は、PDFからすべてのテキストコンテンツを取得します。 IronPDFのExtractAllTextメソッドはPDFの複雑なテキストエンコーディングと位置決めを処理します:

using IronPdf;
using System;

// Extracts raw text content from PDF invoices for further processing
public class InvoiceTextExtractor
{
    // Extracts all text from a PDF in one operation
    // Best for single-page invoices or when you need the complete content
    public string ExtractInvoiceText(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);

        // IronPDF handles the complexity of PDF text encoding and positioning
        string allText = pdf.ExtractAllText();
        Console.WriteLine("Full invoice text:");
        Console.WriteLine(allText);

        return allText;
    }

    // Extracts text page by page - useful for multi-page invoices
    // Allows you to process header info separately from line items
    public void ExtractTextByPage(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);

        // Iterate through each page (0-indexed)
        for (int i = 0; i < pdf.PageCount; i++)
        {
            string pageText = pdf.ExtractTextFromPage(i);
            Console.WriteLine($"\n--- Page {i + 1} ---");
            Console.WriteLine(pageText);
        }
    }
}
using IronPdf;
using System;

// Extracts raw text content from PDF invoices for further processing
public class InvoiceTextExtractor
{
    // Extracts all text from a PDF in one operation
    // Best for single-page invoices or when you need the complete content
    public string ExtractInvoiceText(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);

        // IronPDF handles the complexity of PDF text encoding and positioning
        string allText = pdf.ExtractAllText();
        Console.WriteLine("Full invoice text:");
        Console.WriteLine(allText);

        return allText;
    }

    // Extracts text page by page - useful for multi-page invoices
    // Allows you to process header info separately from line items
    public void ExtractTextByPage(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);

        // Iterate through each page (0-indexed)
        for (int i = 0; i < pdf.PageCount; i++)
        {
            string pageText = pdf.ExtractTextFromPage(i);
            Console.WriteLine($"\n--- Page {i + 1} ---");
            Console.WriteLine(pageText);
        }
    }
}
$vbLabelText   $csharpLabel

ページごとの抽出は、ヘッダー情報は最初のページにしか表示されないが、複数ページにまたがる行項目を見つけるなど、特定のセクションを見つける必要がある複数ページの請求書に特に便利です。

行項目のテーブル データを抽出する方法

請求書の項目は通常、表形式で表示されます。 PDFにはネイティブな表構造がありませんが、テキストを抽出して解析し、表データを再構築することができます:

using IronPdf;
using System;
using System.Collections.Generic;

// Data model for a single invoice line item
public class InvoiceLineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Total { get; set; }
}

// Extracts tabular line item data from PDF invoices
// Note: PDFs don't have native table structure, so this uses text parsing
public class InvoiceTableExtractor
{
    public List<InvoiceLineItem> ExtractLineItems(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        var lineItems = new List<InvoiceLineItem>();
        string[] lines = text.Split('\n');

        foreach (string line in lines)
        {
            // Currency symbols indicate potential line items with amounts
            if (line.Contains("$") || line.Contains("€"))
            {
                Console.WriteLine($"Potential line item: {line.Trim()}");

                // Split on whitespace to separate columns
                // Actual parsing logic depends on your invoice format
                string[] parts = line.Split(new[] { '\t', ' ' },
                    StringSplitOptions.RemoveEmptyEntries);

                // Try to find numeric values that could be amounts
                foreach (string part in parts)
                {
                    string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", "");
                    if (decimal.TryParse(cleaned, out decimal amount))
                    {
                        Console.WriteLine($"  Found amount: {amount:C}");
                    }
                }
            }
        }

        return lineItems;
    }
}
using IronPdf;
using System;
using System.Collections.Generic;

// Data model for a single invoice line item
public class InvoiceLineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Total { get; set; }
}

// Extracts tabular line item data from PDF invoices
// Note: PDFs don't have native table structure, so this uses text parsing
public class InvoiceTableExtractor
{
    public List<InvoiceLineItem> ExtractLineItems(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        var lineItems = new List<InvoiceLineItem>();
        string[] lines = text.Split('\n');

        foreach (string line in lines)
        {
            // Currency symbols indicate potential line items with amounts
            if (line.Contains("$") || line.Contains("€"))
            {
                Console.WriteLine($"Potential line item: {line.Trim()}");

                // Split on whitespace to separate columns
                // Actual parsing logic depends on your invoice format
                string[] parts = line.Split(new[] { '\t', ' ' },
                    StringSplitOptions.RemoveEmptyEntries);

                // Try to find numeric values that could be amounts
                foreach (string part in parts)
                {
                    string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", "");
                    if (decimal.TryParse(cleaned, out decimal amount))
                    {
                        Console.WriteLine($"  Found amount: {amount:C}");
                    }
                }
            }
        }

        return lineItems;
    }
}
$vbLabelText   $csharpLabel

解析ロジックは、請求書のフォーマットによって異なります。 既知のベンダーからの一貫したレイアウトの請求書については、形式固有のパーサーを構築することができます。 多様なフォーマットについては、この記事で後述するAIを活用した抽出をご検討ください。

請求書番号、日付、合計のパターンマッチングの使用方法

正規表現は、請求書のテキストから特定のデータポイントを抽出するのに非常に便利です。 請求書番号、日付、合計などの主要なフィールドは、多くの場合、認識可能なパターンに従っています:

using IronPdf;
using System;
using System.Text.RegularExpressions;

// Data model for extracted invoice information
public class InvoiceData
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate { get; set; }
    public decimal TotalAmount { get; set; }
    public string VendorName { get; set; }
}

// Extracts key invoice fields using regex pattern matching
// Multiple patterns handle variations across different vendors
public class InvoiceParser
{
    public InvoiceData ParseInvoice(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        var invoiceData = new InvoiceData();

        // Try multiple patterns to find invoice number
        // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer"
        string[] invoiceNumberPatterns = new[]
        {
            @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)",
            @"INV[-\s]?(\d+)",
            @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)",
            @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)"
        };

        foreach (string pattern in invoiceNumberPatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                invoiceData.InvoiceNumber = match.Groups[1].Value;
                Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}");
                break;
            }
        }

        // Date patterns for US, European, and written formats
        string[] datePatterns = new[]
        {
            @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})",
            @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})",
            @"(\d{1,2}\.\d{1,2}\.\d{4})",  // European: DD.MM.YYYY
            @"(\w+\s+\d{1,2},?\s+\d{4})"   // Written: January 15, 2024
        };

        foreach (string pattern in datePatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                invoiceData.InvoiceDate = match.Groups[1].Value;
                Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}");
                break;
            }
        }

        // Look for total amount with various labels
        string[] totalPatterns = new[]
        {
            @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})"
        };

        foreach (string pattern in totalPatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                // Remove commas before parsing
                string amountStr = match.Groups[1].Value.Replace(",", "");
                if (decimal.TryParse(amountStr, out decimal amount))
                {
                    invoiceData.TotalAmount = amount;
                    Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}");
                    break;
                }
            }
        }

        return invoiceData;
    }
}
using IronPdf;
using System;
using System.Text.RegularExpressions;

// Data model for extracted invoice information
public class InvoiceData
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate { get; set; }
    public decimal TotalAmount { get; set; }
    public string VendorName { get; set; }
}

// Extracts key invoice fields using regex pattern matching
// Multiple patterns handle variations across different vendors
public class InvoiceParser
{
    public InvoiceData ParseInvoice(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        var invoiceData = new InvoiceData();

        // Try multiple patterns to find invoice number
        // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer"
        string[] invoiceNumberPatterns = new[]
        {
            @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)",
            @"INV[-\s]?(\d+)",
            @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)",
            @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)"
        };

        foreach (string pattern in invoiceNumberPatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                invoiceData.InvoiceNumber = match.Groups[1].Value;
                Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}");
                break;
            }
        }

        // Date patterns for US, European, and written formats
        string[] datePatterns = new[]
        {
            @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})",
            @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})",
            @"(\d{1,2}\.\d{1,2}\.\d{4})",  // European: DD.MM.YYYY
            @"(\w+\s+\d{1,2},?\s+\d{4})"   // Written: January 15, 2024
        };

        foreach (string pattern in datePatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                invoiceData.InvoiceDate = match.Groups[1].Value;
                Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}");
                break;
            }
        }

        // Look for total amount with various labels
        string[] totalPatterns = new[]
        {
            @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})",
            @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})"
        };

        foreach (string pattern in totalPatterns)
        {
            var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase);
            if (match.Success)
            {
                // Remove commas before parsing
                string amountStr = match.Groups[1].Value.Replace(",", "");
                if (decimal.TryParse(amountStr, out decimal amount))
                {
                    invoiceData.TotalAmount = amount;
                    Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}");
                    break;
                }
            }
        }

        return invoiceData;
    }
}
$vbLabelText   $csharpLabel

このパターンベースのアプローチは、予測可能な形式の請求書に効果的です。 複数のパターンバリエーションにより、"Invoice #" や "Invoice Number:" のような、ベンダー間での一般的な書式の違いを処理します。

スキャンまたは画像ベースの請求書についてはどうですか?

上に示したテキスト抽出方法は、埋め込みテキストを含むPDFで機能します。 しかし、スキャンした文書や画像ベースのPDFには、抽出可能なテキストがありません。 基本的には請求書の写真です。

BRACKET-i-OPEN--スキャンした請求書を処理するには、OCR(光学式文字認識)機能が必要です。 Iron Suiteの一部であるIronOCRは、これらのシナリオのためにIronPDFとシームレスに統合されています。 スキャンした文書や画像からテキストを抽出する方法については、https://ironsoftware.com/csharp/ocr/ をご覧ください。


.NETで請求書を処理するためにAIを使用する方法

従来のパターンマッチングは、標準化された請求書には有効ですが、現実の買掛金管理部門では、数え切れないほどの形式の文書を受け取っています。 そこで、AIによる抽出が威力を発揮します。 大規模な言語モデルは、請求書のセマンティクスを理解し、見慣れないレイアウトからでも構造化データを抽出することができます。

請求書の解析にAIを統合する方法

AIによる請求書処理のパターンは、IronPdfのテキスト抽出とLLM APIコールを組み合わせたものです。 OpenAI互換のAPIで動作する汎用的な実装です:

using IronPdf;
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// Data model for extracted invoice information
public class InvoiceData
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate { get; set; }
    public string VendorName { get; set; }
    public decimal TotalAmount { get; set; }
}

// Leverages AI/LLM APIs to extract structured data from any invoice format
// Works with OpenAI or any compatible API endpoint
public class AIInvoiceParser
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly string _apiUrl;

    public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions")
    {
        _apiKey = apiKey;
        _apiUrl = apiUrl;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
    }

    public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath)
    {
        // First extract raw text from the PDF using IronPDF
        var pdf = PdfDocument.FromFile(pdfPath);
        string invoiceText = pdf.ExtractAllText();

        // Construct a prompt that instructs the AI to return structured JSON
        // Being explicit about the format reduces parsing errors
        string prompt = $@"Extract the following information from this invoice text.
Return ONLY valid JSON with no additional text or markdown formatting.

Required fields:
- InvoiceNumber: The invoice or document number
- InvoiceDate: The invoice date in YYYY-MM-DD format
- VendorName: The company or person who sent the invoice
- TotalAmount: The total amount due as a number (no currency symbols)

Invoice text:
{invoiceText}

JSON response:";

        // Build the API request with a system prompt for context
        var requestBody = new
        {
            model = "gpt-4",
            messages = new[]
            {
                new {
                    role = "system",
                    content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only."
                },
                new { role = "user", content = prompt }
            },
            temperature = 0.1  // Low temperature ensures consistent, deterministic results
        };

        var json = JsonSerializer.Serialize(requestBody);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync(_apiUrl, content);
        var responseJson = await response.Content.ReadAsStringAsync();

        // Navigate the API response structure to get the extracted content
        using var doc = JsonDocument.Parse(responseJson);
        var messageContent = doc.RootElement
            .GetProperty("choices")[0]
            .GetProperty("message")
            .GetProperty("content")
            .GetString();

        Console.WriteLine("AI Extracted Data:");
        Console.WriteLine(messageContent);

        // Deserialize the AI's JSON response into our data class
        var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent,
            new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        return invoiceData;
    }
}
using IronPdf;
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// Data model for extracted invoice information
public class InvoiceData
{
    public string InvoiceNumber { get; set; }
    public string InvoiceDate { get; set; }
    public string VendorName { get; set; }
    public decimal TotalAmount { get; set; }
}

// Leverages AI/LLM APIs to extract structured data from any invoice format
// Works with OpenAI or any compatible API endpoint
public class AIInvoiceParser
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly string _apiUrl;

    public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions")
    {
        _apiKey = apiKey;
        _apiUrl = apiUrl;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
    }

    public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath)
    {
        // First extract raw text from the PDF using IronPDF
        var pdf = PdfDocument.FromFile(pdfPath);
        string invoiceText = pdf.ExtractAllText();

        // Construct a prompt that instructs the AI to return structured JSON
        // Being explicit about the format reduces parsing errors
        string prompt = $@"Extract the following information from this invoice text.
Return ONLY valid JSON with no additional text or markdown formatting.

Required fields:
- InvoiceNumber: The invoice or document number
- InvoiceDate: The invoice date in YYYY-MM-DD format
- VendorName: The company or person who sent the invoice
- TotalAmount: The total amount due as a number (no currency symbols)

Invoice text:
{invoiceText}

JSON response:";

        // Build the API request with a system prompt for context
        var requestBody = new
        {
            model = "gpt-4",
            messages = new[]
            {
                new {
                    role = "system",
                    content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only."
                },
                new { role = "user", content = prompt }
            },
            temperature = 0.1  // Low temperature ensures consistent, deterministic results
        };

        var json = JsonSerializer.Serialize(requestBody);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync(_apiUrl, content);
        var responseJson = await response.Content.ReadAsStringAsync();

        // Navigate the API response structure to get the extracted content
        using var doc = JsonDocument.Parse(responseJson);
        var messageContent = doc.RootElement
            .GetProperty("choices")[0]
            .GetProperty("message")
            .GetProperty("content")
            .GetString();

        Console.WriteLine("AI Extracted Data:");
        Console.WriteLine(messageContent);

        // Deserialize the AI's JSON response into our data class
        var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent,
            new JsonSerializerOptions { PropertyNameCaseInsensitive = true });

        return invoiceData;
    }
}
$vbLabelText   $csharpLabel

低温設定(0.1)は、決定論的な出力を促します。これは、同じ入力に対して一貫した結果を求めるデータ抽出タスクにとって重要です。

請求書から構造化 JSON データを抽出する方法

行項目、ベンダー詳細、顧客情報を含むより複雑な請求書については、よりリッチなJSON構造を要求することができます:

using IronPdf;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;

// Comprehensive invoice data model with all details
public class DetailedInvoiceData
{
    public string InvoiceNumber { get; set; }
    public DateTime InvoiceDate { get; set; }
    public DateTime DueDate { get; set; }
    public VendorInfo Vendor { get; set; }
    public CustomerInfo Customer { get; set; }
    public List<LineItem> LineItems { get; set; }
    public decimal Subtotal { get; set; }
    public decimal Tax { get; set; }
    public decimal Total { get; set; }
}

public class VendorInfo
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string TaxId { get; set; }
}

public class CustomerInfo
{
    public string Name { get; set; }
    public string Address { get; set; }
}

public class LineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Total { get; set; }
}

// Extracts comprehensive invoice data including line items and party details
public class StructuredInvoiceExtractor
{
    private readonly AIInvoiceParser _aiParser;

    public StructuredInvoiceExtractor(string apiKey)
    {
        _aiParser = new AIInvoiceParser(apiKey);
    }

    public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        // Define the exact JSON structure we want the AI to return
        // This schema guides the AI to extract all relevant fields
        string jsonSchema = @"{
  ""InvoiceNumber"": ""string"",
  ""InvoiceDate"": ""YYYY-MM-DD"",
  ""DueDate"": ""YYYY-MM-DD"",
  ""Vendor"": {
    ""Name"": ""string"",
    ""Address"": ""string"",
    ""TaxId"": ""string or null""
  },
  ""Customer"": {
    ""Name"": ""string"",
    ""Address"": ""string""
  },
  ""LineItems"": [
    {
      ""Description"": ""string"",
      ""Quantity"": 0.0,
      ""UnitPrice"": 0.00,
      ""Total"": 0.00
    }
  ],
  ""Subtotal"": 0.00,
  ""Tax"": 0.00,
  ""Total"": 0.00
}";

        // Prompt includes both the schema and the extracted text
        string prompt = $@"Extract all invoice data and return it in this exact JSON structure:
{jsonSchema}

Invoice text:
{text}

Return only valid JSON, no markdown formatting or additional text.";

        // Call AI API and parse response (implementation as shown above)
        // Return deserialized DetailedInvoiceData

        return new DetailedInvoiceData(); // Placeholder
    }
}
using IronPdf;
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;

// Comprehensive invoice data model with all details
public class DetailedInvoiceData
{
    public string InvoiceNumber { get; set; }
    public DateTime InvoiceDate { get; set; }
    public DateTime DueDate { get; set; }
    public VendorInfo Vendor { get; set; }
    public CustomerInfo Customer { get; set; }
    public List<LineItem> LineItems { get; set; }
    public decimal Subtotal { get; set; }
    public decimal Tax { get; set; }
    public decimal Total { get; set; }
}

public class VendorInfo
{
    public string Name { get; set; }
    public string Address { get; set; }
    public string TaxId { get; set; }
}

public class CustomerInfo
{
    public string Name { get; set; }
    public string Address { get; set; }
}

public class LineItem
{
    public string Description { get; set; }
    public decimal Quantity { get; set; }
    public decimal UnitPrice { get; set; }
    public decimal Total { get; set; }
}

// Extracts comprehensive invoice data including line items and party details
public class StructuredInvoiceExtractor
{
    private readonly AIInvoiceParser _aiParser;

    public StructuredInvoiceExtractor(string apiKey)
    {
        _aiParser = new AIInvoiceParser(apiKey);
    }

    public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        // Define the exact JSON structure we want the AI to return
        // This schema guides the AI to extract all relevant fields
        string jsonSchema = @"{
  ""InvoiceNumber"": ""string"",
  ""InvoiceDate"": ""YYYY-MM-DD"",
  ""DueDate"": ""YYYY-MM-DD"",
  ""Vendor"": {
    ""Name"": ""string"",
    ""Address"": ""string"",
    ""TaxId"": ""string or null""
  },
  ""Customer"": {
    ""Name"": ""string"",
    ""Address"": ""string""
  },
  ""LineItems"": [
    {
      ""Description"": ""string"",
      ""Quantity"": 0.0,
      ""UnitPrice"": 0.00,
      ""Total"": 0.00
    }
  ],
  ""Subtotal"": 0.00,
  ""Tax"": 0.00,
  ""Total"": 0.00
}";

        // Prompt includes both the schema and the extracted text
        string prompt = $@"Extract all invoice data and return it in this exact JSON structure:
{jsonSchema}

Invoice text:
{text}

Return only valid JSON, no markdown formatting or additional text.";

        // Call AI API and parse response (implementation as shown above)
        // Return deserialized DetailedInvoiceData

        return new DetailedInvoiceData(); // Placeholder
    }
}
$vbLabelText   $csharpLabel

一貫性のない請求書フォーマットを扱う方法

AI抽出の真価が発揮されるのは、複数のベンダーからの請求書を処理する場合です。 スマートプロセッサーは、最初にパターンベースの抽出を試み(より速く、無料で)、必要なときだけAIにフォールバックすることができます:

using IronPdf;
using System.Threading.Tasks;

// Hybrid processor that optimizes for cost and capability
// Tries fast regex patterns first, uses AI only when patterns fail
public class SmartInvoiceProcessor
{
    private readonly AIInvoiceParser _aiParser;

    public SmartInvoiceProcessor(string aiApiKey)
    {
        _aiParser = new AIInvoiceParser(aiApiKey);
    }

    public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        // First attempt: regex patterns (fast and free)
        var patternParser = new InvoiceParser();
        var standardResult = patternParser.ParseInvoiceFromText(text);

        // If pattern matching found all required fields, use that result
        if (IsComplete(standardResult))
        {
            Console.WriteLine("Pattern extraction successful");
            return standardResult;
        }

        // Fallback: use AI for complex or unusual invoice formats
        // This costs money but handles any layout
        Console.WriteLine("Using AI extraction for complex invoice format");
        var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath);

        return aiResult;
    }

    // Validates that we have the minimum required fields
    private bool IsComplete(InvoiceData data)
    {
        return !string.IsNullOrEmpty(data.InvoiceNumber) &&
               !string.IsNullOrEmpty(data.InvoiceDate) &&
               data.TotalAmount > 0;
    }
}
using IronPdf;
using System.Threading.Tasks;

// Hybrid processor that optimizes for cost and capability
// Tries fast regex patterns first, uses AI only when patterns fail
public class SmartInvoiceProcessor
{
    private readonly AIInvoiceParser _aiParser;

    public SmartInvoiceProcessor(string aiApiKey)
    {
        _aiParser = new AIInvoiceParser(aiApiKey);
    }

    public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath)
    {
        var pdf = PdfDocument.FromFile(pdfPath);
        string text = pdf.ExtractAllText();

        // First attempt: regex patterns (fast and free)
        var patternParser = new InvoiceParser();
        var standardResult = patternParser.ParseInvoiceFromText(text);

        // If pattern matching found all required fields, use that result
        if (IsComplete(standardResult))
        {
            Console.WriteLine("Pattern extraction successful");
            return standardResult;
        }

        // Fallback: use AI for complex or unusual invoice formats
        // This costs money but handles any layout
        Console.WriteLine("Using AI extraction for complex invoice format");
        var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath);

        return aiResult;
    }

    // Validates that we have the minimum required fields
    private bool IsComplete(InvoiceData data)
    {
        return !string.IsNullOrEmpty(data.InvoiceNumber) &&
               !string.IsNullOrEmpty(data.InvoiceDate) &&
               data.TotalAmount > 0;
    }
}
$vbLabelText   $csharpLabel

買掛金自動化パイプラインを構築する方法

これらすべてのピースを組み合わせることで、請求書を処理し、データを抽出し、検証し、会計システム用に準備する完全な自動化パイプラインが完成します:

using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

// Tracks the outcome of processing each invoice
public class ProcessingResult
{
    public string FileName { get; set; }
    public bool Success { get; set; }
    public string InvoiceNumber { get; set; }
    public string ErrorMessage { get; set; }
}

// Complete automation pipeline for accounts payable
// Watches a folder, extracts data, validates, and routes to accounting system
public class InvoiceAutomationPipeline
{
    private readonly SmartInvoiceProcessor _processor;
    private readonly string _inputFolder;
    private readonly string _processedFolder;
    private readonly string _errorFolder;

    public InvoiceAutomationPipeline(string apiKey, string inputFolder)
    {
        _processor = new SmartInvoiceProcessor(apiKey);
        _inputFolder = inputFolder;
        _processedFolder = Path.Combine(inputFolder, "processed");
        _errorFolder = Path.Combine(inputFolder, "errors");

        // Create output directories if they don't exist
        Directory.CreateDirectory(_processedFolder);
        Directory.CreateDirectory(_errorFolder);
    }

    // Main entry point - processes all PDFs in the input folder
    public async Task<List<ProcessingResult>> ProcessInvoiceBatch()
    {
        string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf");
        Console.WriteLine($"Found {invoiceFiles.Length} invoices to process");

        var results = new List<ProcessingResult>();

        foreach (string invoicePath in invoiceFiles)
        {
            string fileName = Path.GetFileName(invoicePath);

            try
            {
                Console.WriteLine($"Processing: {fileName}");

                // Extract data using smart processor (patterns first, then AI)
                var invoiceData = await _processor.ProcessAnyInvoice(invoicePath);

                // Ensure we have minimum required fields before proceeding
                if (ValidateInvoiceData(invoiceData))
                {
                    // Send to accounting system (QuickBooks, Xero, etc.)
                    await SaveToAccountingSystem(invoiceData);

                    // Archive successful invoices
                    string destPath = Path.Combine(_processedFolder, fileName);
                    File.Move(invoicePath, destPath, overwrite: true);

                    results.Add(new ProcessingResult
                    {
                        FileName = fileName,
                        Success = true,
                        InvoiceNumber = invoiceData.InvoiceNumber
                    });

                    Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}");
                }
                else
                {
                    throw new Exception("Validation failed - missing required fields");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}");

                // Quarantine failed invoices for manual review
                string destPath = Path.Combine(_errorFolder, fileName);
                File.Move(invoicePath, destPath, overwrite: true);

                results.Add(new ProcessingResult
                {
                    FileName = fileName,
                    Success = false,
                    ErrorMessage = ex.Message
                });
            }
        }

        GenerateReport(results);
        return results;
    }

    // Checks for minimum required fields
    private bool ValidateInvoiceData(InvoiceData data)
    {
        return !string.IsNullOrEmpty(data.InvoiceNumber) &&
               !string.IsNullOrEmpty(data.VendorName) &&
               data.TotalAmount > 0;
    }

    // Placeholder for accounting system integration
    private async Task SaveToAccountingSystem(InvoiceData data)
    {
        // Integrate with your accounting system here
        // Examples: QuickBooks API, Xero API, SAP, or database storage
        Console.WriteLine($"  Saved invoice {data.InvoiceNumber} to accounting system");
        await Task.CompletedTask;
    }

    // Outputs a summary of the batch processing results
    private void GenerateReport(List<ProcessingResult> results)
    {
        int successful = results.Count(r => r.Success);
        int failed = results.Count(r => !r.Success);

        Console.WriteLine($"\n========== Processing Complete ==========");
        Console.WriteLine($"Total Processed: {results.Count}");
        Console.WriteLine($"Successful: {successful}");
        Console.WriteLine($"Failed: {failed}");

        if (failed > 0)
        {
            Console.WriteLine("\nFailed invoices requiring review:");
            foreach (var failure in results.Where(r => !r.Success))
            {
                Console.WriteLine($"  • {failure.FileName}: {failure.ErrorMessage}");
            }
        }
    }
}
using IronPdf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

// Tracks the outcome of processing each invoice
public class ProcessingResult
{
    public string FileName { get; set; }
    public bool Success { get; set; }
    public string InvoiceNumber { get; set; }
    public string ErrorMessage { get; set; }
}

// Complete automation pipeline for accounts payable
// Watches a folder, extracts data, validates, and routes to accounting system
public class InvoiceAutomationPipeline
{
    private readonly SmartInvoiceProcessor _processor;
    private readonly string _inputFolder;
    private readonly string _processedFolder;
    private readonly string _errorFolder;

    public InvoiceAutomationPipeline(string apiKey, string inputFolder)
    {
        _processor = new SmartInvoiceProcessor(apiKey);
        _inputFolder = inputFolder;
        _processedFolder = Path.Combine(inputFolder, "processed");
        _errorFolder = Path.Combine(inputFolder, "errors");

        // Create output directories if they don't exist
        Directory.CreateDirectory(_processedFolder);
        Directory.CreateDirectory(_errorFolder);
    }

    // Main entry point - processes all PDFs in the input folder
    public async Task<List<ProcessingResult>> ProcessInvoiceBatch()
    {
        string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf");
        Console.WriteLine($"Found {invoiceFiles.Length} invoices to process");

        var results = new List<ProcessingResult>();

        foreach (string invoicePath in invoiceFiles)
        {
            string fileName = Path.GetFileName(invoicePath);

            try
            {
                Console.WriteLine($"Processing: {fileName}");

                // Extract data using smart processor (patterns first, then AI)
                var invoiceData = await _processor.ProcessAnyInvoice(invoicePath);

                // Ensure we have minimum required fields before proceeding
                if (ValidateInvoiceData(invoiceData))
                {
                    // Send to accounting system (QuickBooks, Xero, etc.)
                    await SaveToAccountingSystem(invoiceData);

                    // Archive successful invoices
                    string destPath = Path.Combine(_processedFolder, fileName);
                    File.Move(invoicePath, destPath, overwrite: true);

                    results.Add(new ProcessingResult
                    {
                        FileName = fileName,
                        Success = true,
                        InvoiceNumber = invoiceData.InvoiceNumber
                    });

                    Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}");
                }
                else
                {
                    throw new Exception("Validation failed - missing required fields");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}");

                // Quarantine failed invoices for manual review
                string destPath = Path.Combine(_errorFolder, fileName);
                File.Move(invoicePath, destPath, overwrite: true);

                results.Add(new ProcessingResult
                {
                    FileName = fileName,
                    Success = false,
                    ErrorMessage = ex.Message
                });
            }
        }

        GenerateReport(results);
        return results;
    }

    // Checks for minimum required fields
    private bool ValidateInvoiceData(InvoiceData data)
    {
        return !string.IsNullOrEmpty(data.InvoiceNumber) &&
               !string.IsNullOrEmpty(data.VendorName) &&
               data.TotalAmount > 0;
    }

    // Placeholder for accounting system integration
    private async Task SaveToAccountingSystem(InvoiceData data)
    {
        // Integrate with your accounting system here
        // Examples: QuickBooks API, Xero API, SAP, or database storage
        Console.WriteLine($"  Saved invoice {data.InvoiceNumber} to accounting system");
        await Task.CompletedTask;
    }

    // Outputs a summary of the batch processing results
    private void GenerateReport(List<ProcessingResult> results)
    {
        int successful = results.Count(r => r.Success);
        int failed = results.Count(r => !r.Success);

        Console.WriteLine($"\n========== Processing Complete ==========");
        Console.WriteLine($"Total Processed: {results.Count}");
        Console.WriteLine($"Successful: {successful}");
        Console.WriteLine($"Failed: {failed}");

        if (failed > 0)
        {
            Console.WriteLine("\nFailed invoices requiring review:");
            foreach (var failure in results.Where(r => !r.Success))
            {
                Console.WriteLine($"  • {failure.FileName}: {failure.ErrorMessage}");
            }
        }
    }
}
$vbLabelText   $csharpLabel

このパイプラインは、完全なワークフローを実装しています。受信したPDFのフォルダをスキャンし、各PDFを処理し、抽出されたデータを検証し、成功した抽出を会計システムにルーティングし、手動レビューのために失敗を隔離します。 要約レポートは、処理結果を可視化します。


C#請求書処理と会計システムを統合する方法

抽出された請求書データは、最終的に支払いと記録管理のために会計システムに流れ込む必要があります。 詳細はプラットフォームによって異なりますが、統合パターンは一貫しています。

QuickBooks、Xero、SAPの一般的な統合パターンは何ですか?

ほとんどの会計プラットフォームは、請求書や送り状をプログラムで作成するためのREST APIを提供しています。 ここでは、一般的なパターンを紹介します:

using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// Generic integration layer for pushing invoice data to accounting systems
// Adapt the API calls based on your specific platform
public class AccountingSystemIntegration
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly string _baseUrl;

    public AccountingSystemIntegration(string apiKey, string baseUrl)
    {
        _apiKey = apiKey;
        _baseUrl = baseUrl;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
    }

    // Creates a Bill in QuickBooks (vendor invoices are called "Bills")
    public async Task SendToQuickBooks(InvoiceData invoice)
    {
        // QuickBooks Bill structure - see their API docs for full schema
        var bill = new
        {
            VendorRef = new { name = invoice.VendorName },
            TxnDate = invoice.InvoiceDate,
            DocNumber = invoice.InvoiceNumber,
            TotalAmt = invoice.TotalAmount,
            Line = new[]
            {
                new
                {
                    Amount = invoice.TotalAmount,
                    DetailType = "AccountBasedExpenseLineDetail",
                    AccountBasedExpenseLineDetail = new
                    {
                        AccountRef = new { name = "Accounts Payable" }
                    }
                }
            }
        };

        await PostToApi("/v3/company/{companyId}/bill", bill);
    }

    // Creates an accounts payable invoice in Xero
    public async Task SendToXero(InvoiceData invoice)
    {
        // ACCPAY type indicates this is a bill to pay (not a sales invoice)
        var bill = new
        {
            Type = "ACCPAY",
            Contact = new { Name = invoice.VendorName },
            Date = invoice.InvoiceDate,
            InvoiceNumber = invoice.InvoiceNumber,
            Total = invoice.TotalAmount
        };

        await PostToApi("/api.xro/2.0/Invoices", bill);
    }

    // Generic POST helper with error handling
    private async Task PostToApi(string endpoint, object payload)
    {
        string json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content);

        if (!response.IsSuccessStatusCode)
        {
            string error = await response.Content.ReadAsStringAsync();
            throw new Exception($"API Error: {response.StatusCode} - {error}");
        }

        Console.WriteLine($"Successfully posted to {endpoint}");
    }
}
using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

// Generic integration layer for pushing invoice data to accounting systems
// Adapt the API calls based on your specific platform
public class AccountingSystemIntegration
{
    private readonly HttpClient _httpClient;
    private readonly string _apiKey;
    private readonly string _baseUrl;

    public AccountingSystemIntegration(string apiKey, string baseUrl)
    {
        _apiKey = apiKey;
        _baseUrl = baseUrl;
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}");
    }

    // Creates a Bill in QuickBooks (vendor invoices are called "Bills")
    public async Task SendToQuickBooks(InvoiceData invoice)
    {
        // QuickBooks Bill structure - see their API docs for full schema
        var bill = new
        {
            VendorRef = new { name = invoice.VendorName },
            TxnDate = invoice.InvoiceDate,
            DocNumber = invoice.InvoiceNumber,
            TotalAmt = invoice.TotalAmount,
            Line = new[]
            {
                new
                {
                    Amount = invoice.TotalAmount,
                    DetailType = "AccountBasedExpenseLineDetail",
                    AccountBasedExpenseLineDetail = new
                    {
                        AccountRef = new { name = "Accounts Payable" }
                    }
                }
            }
        };

        await PostToApi("/v3/company/{companyId}/bill", bill);
    }

    // Creates an accounts payable invoice in Xero
    public async Task SendToXero(InvoiceData invoice)
    {
        // ACCPAY type indicates this is a bill to pay (not a sales invoice)
        var bill = new
        {
            Type = "ACCPAY",
            Contact = new { Name = invoice.VendorName },
            Date = invoice.InvoiceDate,
            InvoiceNumber = invoice.InvoiceNumber,
            Total = invoice.TotalAmount
        };

        await PostToApi("/api.xro/2.0/Invoices", bill);
    }

    // Generic POST helper with error handling
    private async Task PostToApi(string endpoint, object payload)
    {
        string json = JsonSerializer.Serialize(payload);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        var response = await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content);

        if (!response.IsSuccessStatusCode)
        {
            string error = await response.Content.ReadAsStringAsync();
            throw new Exception($"API Error: {response.StatusCode} - {error}");
        }

        Console.WriteLine($"Successfully posted to {endpoint}");
    }
}
$vbLabelText   $csharpLabel

各プラットフォームには独自の認証メカニズム(QuickBooksとXeroはOAuth、SAPはさまざまな方法)、必須フィールド、API規約があります。 詳細については、ターゲット・プラットフォームのドキュメントを参照してください。しかし、抽出された請求書データをAPIペイロードに変換するパターンは一貫しています。

何百もの請求書をバッチ処理する方法

大量の請求書処理には、並行処理とリソース管理に細心の注意が必要です。 制御された並行性を持つ並列処理を使用するパターンを示します:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

// Tracks the result of processing a single invoice in a batch
public class BatchResult
{
    public string FilePath { get; set; }
    public bool Success { get; set; }
    public string InvoiceNumber { get; set; }
    public string Error { get; set; }
}

// High-volume invoice processor with controlled parallelism
// Prevents overwhelming APIs while maximizing throughput
public class BatchInvoiceProcessor
{
    private readonly SmartInvoiceProcessor _invoiceProcessor;
    private readonly AccountingSystemIntegration _accountingIntegration;
    private readonly int _maxConcurrency;

    public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey,
        string accountingUrl, int maxConcurrency = 5)
    {
        _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey);
        _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl);
        _maxConcurrency = maxConcurrency;  // Adjust based on API rate limits
    }

    // Processes multiple invoices in parallel with controlled concurrency
    public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths)
    {
        // Thread-safe collection for gathering results from parallel tasks
        var results = new ConcurrentBag<BatchResult>();

        // Semaphore limits how many invoices process simultaneously
        var semaphore = new SemaphoreSlim(_maxConcurrency);

        // Create a task for each invoice
        var tasks = invoicePaths.Select(async path =>
        {
            // Wait for a slot to become available
            await semaphore.WaitAsync();
            try
            {
                var result = await ProcessSingleInvoice(path);
                results.Add(result);
            }
            finally
            {
                // Release slot for next invoice
                semaphore.Release();
            }
        });

        // Wait for all invoices to complete
        await Task.WhenAll(tasks);

        // Output summary statistics
        var resultList = results.ToList();
        int successful = resultList.Count(r => r.Success);
        int failed = resultList.Count(r => !r.Success);

        Console.WriteLine($"\nBatch Processing Complete:");
        Console.WriteLine($"  Total: {resultList.Count}");
        Console.WriteLine($"  Successful: {successful}");
        Console.WriteLine($"  Failed: {failed}");

        return resultList;
    }

    // Processes one invoice: extract data and send to accounting system
    private async Task<BatchResult> ProcessSingleInvoice(string pdfPath)
    {
        try
        {
            Console.WriteLine($"Processing: {pdfPath}");

            var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath);
            await _accountingIntegration.SendToQuickBooks(invoiceData);

            Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}");

            return new BatchResult
            {
                FilePath = pdfPath,
                Success = true,
                InvoiceNumber = invoiceData.InvoiceNumber
            };
        }
        catch (Exception ex)
        {
            Console.WriteLine($"✗ Failed: {pdfPath}");

            return new BatchResult
            {
                FilePath = pdfPath,
                Success = false,
                Error = ex.Message
            };
        }
    }
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

// Tracks the result of processing a single invoice in a batch
public class BatchResult
{
    public string FilePath { get; set; }
    public bool Success { get; set; }
    public string InvoiceNumber { get; set; }
    public string Error { get; set; }
}

// High-volume invoice processor with controlled parallelism
// Prevents overwhelming APIs while maximizing throughput
public class BatchInvoiceProcessor
{
    private readonly SmartInvoiceProcessor _invoiceProcessor;
    private readonly AccountingSystemIntegration _accountingIntegration;
    private readonly int _maxConcurrency;

    public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey,
        string accountingUrl, int maxConcurrency = 5)
    {
        _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey);
        _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl);
        _maxConcurrency = maxConcurrency;  // Adjust based on API rate limits
    }

    // Processes multiple invoices in parallel with controlled concurrency
    public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths)
    {
        // Thread-safe collection for gathering results from parallel tasks
        var results = new ConcurrentBag<BatchResult>();

        // Semaphore limits how many invoices process simultaneously
        var semaphore = new SemaphoreSlim(_maxConcurrency);

        // Create a task for each invoice
        var tasks = invoicePaths.Select(async path =>
        {
            // Wait for a slot to become available
            await semaphore.WaitAsync();
            try
            {
                var result = await ProcessSingleInvoice(path);
                results.Add(result);
            }
            finally
            {
                // Release slot for next invoice
                semaphore.Release();
            }
        });

        // Wait for all invoices to complete
        await Task.WhenAll(tasks);

        // Output summary statistics
        var resultList = results.ToList();
        int successful = resultList.Count(r => r.Success);
        int failed = resultList.Count(r => !r.Success);

        Console.WriteLine($"\nBatch Processing Complete:");
        Console.WriteLine($"  Total: {resultList.Count}");
        Console.WriteLine($"  Successful: {successful}");
        Console.WriteLine($"  Failed: {failed}");

        return resultList;
    }

    // Processes one invoice: extract data and send to accounting system
    private async Task<BatchResult> ProcessSingleInvoice(string pdfPath)
    {
        try
        {
            Console.WriteLine($"Processing: {pdfPath}");

            var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath);
            await _accountingIntegration.SendToQuickBooks(invoiceData);

            Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}");

            return new BatchResult
            {
                FilePath = pdfPath,
                Success = true,
                InvoiceNumber = invoiceData.InvoiceNumber
            };
        }
        catch (Exception ex)
        {
            Console.WriteLine($"✗ Failed: {pdfPath}");

            return new BatchResult
            {
                FilePath = pdfPath,
                Success = false,
                Error = ex.Message
            };
        }
    }
}
$vbLabelText   $csharpLabel

SemaphoreSlimは、外部APIを圧迫したり、システムリソースを使い果たしたりしないようにします。 _maxConcurrencyは、APIレートの制限とサーバーの容量に基づいて調整してください。 ConcurrentBagは、並列処理の結果を安全に収集します。


次のステップ

請求書の自動化は、手作業を減らし、ミスを最小限に抑え、ビジネスプロセスを加速させる大きなチャンスです。 このガイドでは、完全なライフサイクルを説明します:HTMLテンプレートからプロフェッショナルな請求書を作成し、ZUGFeRDおよびFactur-X電子請求書標準に準拠する、パターンマッチングとAI-powered processingの両方を使用して、受信した請求書からデータを抽出し、スケーラブルな自動化パイプラインを構築する。

IronPDFは、堅牢なHTMLからPDFへのレンダリング、信頼性の高いテキスト抽出、およびPDF/A-3電子請求書コンプライアンスに必要な添付機能を提供し、これらの機能の基盤として機能します。 Chromeベースのレンダリングエンジンにより、請求書がデザインどおりに表示され、抽出メソッドにより、複雑なPDFテキストエンコーディングが自動的に処理されます。

ここに示すパターンは出発点です。 実際の実装では、特定の請求書フォーマット、会計システム、ビジネスルールに適合させる必要があります。 大量のシナリオのために、バッチ処理チュートリアルでは、制御された並行実行とエラー回復をカバーしています。

構築開始の準備はできましたか? IronPdfをダウンロードして無料トライアルでお試しください。 このライブラリには無料の開発ライセンスが含まれているため、本番用ライセンスにコミットする前に、請求書生成、データ抽出、PDFレポート機能を十分に評価することができます。 請求書の自動化や会計システムの統合について質問がある場合は、当社のエンジニアリング・サポート・チームにお問い合わせください。

よくある質問

C#の請求書処理でIronPDFは何に使われていますか?

IronPdfはC#の請求書処理で使用され、ZUGFeRDやFactur-Xのような標準に準拠しながら、プロフェッショナルなPDF請求書の生成、構造化データの抽出、請求書ワークフローの自動化を実現します。

C#でIronPDFを使ってPDF請求書を作成するにはどうすればいいですか?

C#でIronPDFを使い、そのAPIを活用してプログラムでPDFドキュメントを作成しカスタマイズすることで、PDF請求書を作成することができます。これには請求書を構成するテキスト、表、画像などの要素の追加が含まれます。

ZUGFeRDとFactur-Xとは何ですか?IronPDFはどのようにサポートしていますか?

ZUGFeRDとFactur-Xは、請求書が人間にも機械にも読めることを保証する電子請求書規格です。IronPDFはこれらの仕様に準拠したPDF請求書を作成することで、これらの標準をサポートします。

IronPdfは買掛金処理の自動化にどのように役立ちますか?

IronPDFは請求書から構造化されたデータを抽出し、自動化パイプラインと統合することで買掛金処理を自動化し、手作業によるデータ入力を減らし、効率を改善します。

IronPdfは既存のPDF請求書からデータを抽出できますか?

IronPDFは既存のPDF請求書から構造化データを抽出し、請求書情報の自動処理と分析を容易にします。

C#での請求書処理にIronPDFを使う利点は何ですか?

C#での請求書処理にIronPdfを使用する利点には、請求書生成の合理化、国際的な請求書発行基準への準拠、効率的なデータ抽出、自動化機能の強化などがあります。

IronPDFでPDF請求書の外観をカスタマイズすることは可能ですか?

IronPDFでは、ロゴ、テキストフォーマット、レイアウト調整などの様々なデザイン要素を追加して、PDF請求書の外観をカスタマイズすることができます。

IronPDFを使って請求書処理を自動化するにはどのような手順がありますか?

IronPdfを使って請求書処理を自動化するには、通常、請求書を生成し、必要なデータを抽出し、他のシステムや自動化ツールと統合してワークフローを合理化します。

IronPDFはどのように異なる請求書フォーマットを扱うのですか?

IronPdfはPDFドキュメントを生成、操作、読み取るツールを提供することで様々な請求書フォーマットを扱うことができ、一般的な電子請求書規格との互換性を保証します。

カーティス・チャウ
テクニカルライター

Curtis Chauは、カールトン大学でコンピュータサイエンスの学士号を取得し、Node.js、TypeScript、JavaScript、およびReactに精通したフロントエンド開発を専門としています。直感的で美しいユーザーインターフェースを作成することに情熱を持ち、Curtisは現代のフレームワークを用いた開発や、構造の良い視覚的に魅力的なマニュアルの作成を楽しんでいます。

開発以外にも、CurtisはIoT(Internet of Things)への強い関心を持ち、ハードウェアとソフトウェアの統合方法を模索しています。余暇には、ゲームをしたりDiscordボットを作成したりして、技術に対する愛情と創造性を組み合わせています。

準備はできましたか?
Nuget ダウンロード 17,570,948 | バージョン: 2026.2 リリース