Invoice Processing in C#: Generate, Extract, and Automate PDF Invoices with .NET

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

發票處理C# .NET 中,使用IronPDF涵蓋了完整的文件生命週期:從HTML模板生成專業的PDF發票,符合ZUGFeRDFactur-X電子發票標準,使用文字解析和AI驅動的處理提取從收到的發票中提取結構化資料,並建立可以與QuickBooks、Xero和SAP等會計系統整合的批量自動化流水線

精讀:快速入門指南

本教程涵蓋在C# .NET中生成、提取和自動化PDF發票,包括電子發票合規性、AI驅動的解析以及會計系統整合。

  • 適用物件: 對.NET開發者正在建構發票模組、應付帳款自動化或電子發票合規性的開發者。
  • 您將構建的內容: 使用HTML模板的發票生成,包含行項目和稅費計算、支付連結的QR碼、符合ZUGFeRD/Factur-X的PDF/A-3輸出、使用正則表達式的文字提取、AI驅動的發票解析以及與會計系統整合的批量處理。
  • 運行環境: .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發票:

  1. 使用NuGet套件管理器安裝https://www.nuget.org/packages/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

在您購買或註冊30天試用版IronPDF後,在應用程式的開始處新增您的授權金鑰。

:path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing-2.cs
IronPdf.License.LicenseKey = "KEY";
Imports IronPdf

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

今天就使用IronPDF開始專案,免費試用。

第一步:
green arrow pointer
NuGet 使用NuGet安裝

PM >  Install-Package IronPdf

查看在NuGet上的https://www.nuget.org/packages/IronPdf,快速安裝。超過1000萬次下載,正在用C#轉變PDF開發。 您也可以下載DLLWindows安裝程式

目錄

NuGet 使用NuGet安裝

PM >  Install-Package IronPdf

查看在NuGet上的https://www.nuget.org/packages/IronPdf,快速安裝。超過1000萬次下載,正在用C#轉變PDF開發。 您也可以下載DLLWindows安裝程式

什麼是發票生命週期,為什麼PDF仍然是標準?

在深入程式碼之前,理解發票在現代業務系統中的完整旅程是有幫助的。 發票的生命週期包括五個不同的階段:生成、分發、接收、資料提取和會計整合。

發票處理始於生成。 企業建立包含行項目、定價、稅費計算、付款條款和品牌化的發票。 發票需要看起來專業,並滿足所有法律要求。 接下來是分發,通過電子郵件、客戶門戶或傳統郵件將發票發送給客戶。 當客戶收到後,應付帳款團隊將捕獲文件,並準備好進行處理。 資料提取從發票中提取關鍵資訊,如供應商詳細資訊、行項目、總額和到期日,以便可以檢查並與採購訂單比對。 最後,會計整合將這些資料移入財務系統如QuickBooks、Xero或SAP進行付款和記錄保存。

為什麼在這麼多年後,PDF仍然是最廣泛使用的格式? 這歸結於一個獨特的優勢組合。 PDF讓您的發票格式不變,無論您使用什麼裝置或操作系統。 無論有人是在Windows、Mac或他們的手機上打開您的發票,它看起來正如您設計的那樣。 PDF也很難以錯誤方式更改,因此它比Word或Excel等格式更好地保護了您的文件完整性。 您可以新增數位簽章以獲得真實性並使用加密來保護安全。 最重要的是,PDF已經成為每個業務系統都認識和支持的通用標準。

當然,也有挑戰。 PDF旨在對人友好閱讀,而非計算機處理。 而不是以結構化資料儲存資訊,PDF會根據其在頁面上的出現位置來保存文字、線條、形狀和圖像。 這就是為什麼像IronPDF這樣的工具如此有幫助,因為它們使得將對人友好的文件轉變為軟體可處理的資料成為可能。


How to Generate Professional PDF Invoices in C

程式化生成發票需要將結構化資料(如客戶資訊、行項目和計算)轉為精美的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 = @"
<!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-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; }
</style>
</head>
<body>
<div class='header'>
<div class='company-name'>Your Company Name</div>
<div>123 Business Street</div>
<div>City, State 12345</div>
</div>

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

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

<table>
<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>
</table>

<div class='total'>Total: $1,750.00</div>
</body>
</html>";

// 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");
Imports IronPdf

' Define the HTML template for a basic invoice
' Uses inline CSS for styling headers, tables, and totals
Dim invoiceHtml As String = "
<!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-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; }
</style>
</head>
<body>
<div class='header'>
<div class='company-name'>Your Company Name</div>
<div>123 Business Street</div>
<div>City, State 12345</div>
</div>

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

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

<table>
<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>
</table>

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

' Initialize the Chrome-based PDF renderer
Dim renderer As New ChromePdfRenderer()

' Convert the HTML string to a PDF document
Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml)

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

範例輸出

這種方法提供了豐富的靈活性。 在Chrome中可以使用的任何CSS都能在您的PDF中工作,包括像flexbox、grid布局和自定義字體等現代功能。 您甚至可以通過引用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);
    }
}
Imports IronPdf
Imports System
Imports System.Collections.Generic
Imports System.Linq

' Represents a single line item on an invoice
Public Class InvoiceLineItem
    Public Property Description As String
    Public Property Quantity As Decimal
    Public Property UnitPrice As Decimal

    ' Auto-calculates line total from quantity and unit price
    Public ReadOnly Property Total As Decimal
        Get
            Return Quantity * UnitPrice
        End Get
    End Property
End Class

' Represents a complete invoice with customer details and line items
Public Class Invoice
    Public Property InvoiceNumber As String
    Public Property InvoiceDate As DateTime
    Public Property CustomerName As String
    Public Property CustomerAddress As String
    Public Property LineItems As List(Of InvoiceLineItem)

    ' Computed properties for invoice totals
    Public ReadOnly Property Subtotal As Decimal
        Get
            Return LineItems.Sum(Function(item) item.Total)
        End Get
    End Property

    Public Property TaxRate As Decimal = 0.08D ' Default 8% tax rate

    Public ReadOnly Property Tax As Decimal
        Get
            Return Subtotal * TaxRate
        End Get
    End Property

    Public ReadOnly Property Total As Decimal
        Get
            Return Subtotal + Tax
        End Get
    End Property
End Class

' Generates PDF invoices from Invoice objects using HTML templates
Public Class InvoiceGenerator
    Public Function GenerateInvoice(invoice As Invoice) As PdfDocument
        ' Build HTML table rows dynamically from line items
        Dim lineItemsHtml As String = String.Join("", invoice.LineItems.Select(Function(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
        Dim invoiceHtml As String = $"
<!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
        Dim renderer As New ChromePdfRenderer()
        Return renderer.RenderHtmlAsPdf(invoiceHtml)
    End Function
End Class
$vbLabelText   $csharpLabel

範例輸出

Invoice類別包含所有發票資料及計算屬性,如小計、稅金和總額。 生成器使用字串插值將這些資料轉換為HTML,然後渲染為PDF。 這種責任分離使程式碼更易於維護和測試。

如何向發票新增公司品牌和水印

專業的發票需要品牌元素,如標誌,有時還需要水印來指示付款狀態。 IronPDF支持在HTML中嵌入圖像以及在渲染後進行程式化水印。

:path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/branding-watermarks.cs
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 = @"
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; padding: 40px; }
.logo { width: 200px; margin-bottom: 20px; }
</style>
</head>
<body>
<div style='text-align: center;'>
<img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' />
</div>
<h1>INVOICE</h1>
<p><strong>Invoice Number:</strong> INV-2024-001</p>
<p><strong>Total:</strong> $1,250.00</p>
</body>
</html>";

// 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");
Imports IronPdf

Dim renderer As New ChromePdfRenderer()

' Invoice HTML template with company logo embedded via URL
' Logo can also be Base64-encoded or a local file path
Dim htmlWithLogo As String = "
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; padding: 40px; }
.logo { width: 200px; margin-bottom: 20px; }
</style>
</head>
<body>
<div style='text-align: center;'>
<img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' />
</div>
<h1>INVOICE</h1>
<p><strong>Invoice Number:</strong> INV-2024-001</p>
<p><strong>Total:</strong> $1,250.00</p>
</body>
</html>"

' Render the HTML to PDF
Dim 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")
$vbLabelText   $csharpLabel

範例輸出

ApplyWatermark 方法接受HTML內容,讓您可以完全控制水印的外觀。 您可以調整透明度、旋轉和位置,以達到確切需要的外觀。 這對於標記發票為"已付款"、"草稿"或"已取消"而不需重新生成整個文件特別有用。

如何嵌入付款連結的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 = $@"
<!DOCTYPE html>
<html>
<head>
<style>
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; }}
</style>
</head>
<body>
<h1>INVOICE {invoiceNumber}</h1>
<p><strong>Amount Due:</strong> ${amount:F2}</p>

<div 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>
</div>
</body>
</html>";

// Convert HTML to PDF and save
var renderer = new ChromePdfRenderer();
var pdf = renderer.RenderHtmlAsPdf(invoiceHtml);
pdf.SaveAs($"invoice-{invoiceNumber}.pdf");
Imports IronPdf
Imports IronQr
Imports IronSoftware.Drawing

Dim invoiceNumber As String = "INV-2026-002"
Dim amount As Decimal = 1500.0D

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

' Generate QR code from the payment URL using IronQR
Dim qrCode As QrCode = QrWriter.Write(paymentUrl)
Dim qrImage As AnyBitmap = 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
Dim invoiceHtml As String = $"
<!DOCTYPE html>
<html>
<head>
<style>
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; }}
</style>
</head>
<body>
<h1>INVOICE {invoiceNumber}</h1>
<p><strong>Amount Due:</strong> ${amount:F2}</p>

<div 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>
</div>
</body>
</html>"

' Convert HTML to PDF and save
Dim renderer As New ChromePdfRenderer()
Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml)
pdf.SaveAs($"invoice-{invoiceNumber}.pdf")
$vbLabelText   $csharpLabel

範例輸出

QR碼直接連結到支付頁面,減少客戶摩擦,並加快現金流。 這種模式可用於支持基於URL支付啟動的任何支付提供商。


How to Comply with ZUGFeRD and Factur-X E-Invoicing Standards in C

電子發票快速成為歐洲各地的強制性要求。 德國率先推出ZUGFeRD,法國隨後推出Factur-X。 這些標準將機器可讀的XML資料嵌入到PDF發票中,使自動化處理成為可能,同時保持可人閱讀的文件。 了解和實施這些標準對於在歐洲市場營運的企業越來越重要。

什麼是ZUGFeRD及其工作原理?

ZUGFeRD(Zentraler User Guide des Forums elektronische Rechnung Deutschland)是德國的電子發票標準,將發票資料作為XML文件附件嵌入在PDF/A-3合規文件內。 嵌入的XML允許無需OCR或解析即可自動提取資料。

該標準定義了三個合規層次,每個層次提供越來越多的結構化資料:

  • 基本: 包含核心發票資料,適合簡單的自動化處理
  • 舒適: 增加詳細資訊,支持完全自動化的發票處理
  • 擴展: 包括跨行業的複雜業務場景的全面資料

XML遵循UN/CEFACT跨行業發票(CII)模式,這已成為歐洲電子發票標準化的基礎。

什麼是Factur-X,與ZUGFeRD有何不同?

Factur-X是相同基礎標準的法國實施。 ZUGFeRD 2.0和Factur-X技術上是相同的。 它們共享相同的XML模式和基於歐洲規範EN 16931的合規概況。區別純屬地區性命名:按ZUGFeRD規格建立的發票在Factur-X下有效,反之亦然。

如何將XML資料嵌入PDF/A-3發票

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>";
    }
}
Imports System
Imports 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 Sub GenerateZUGFeRDInvoice(invoice As Invoice)
        ' First, create the visual PDF that humans will read
        Dim renderer As New ChromePdfRenderer()
        Dim invoiceHtml As String = BuildInvoiceHtml(invoice)
        Dim pdf = renderer.RenderHtmlAsPdf(invoiceHtml)

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

        ' Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema
        Dim zugferdXml As New XDocument(
            New XDeclaration("1.0", "UTF-8", Nothing),
            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
        Dim xmlPath As String = $"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")
    End Sub

    ' Generates simple HTML for the visual portion of the invoice
    Private Function BuildInvoiceHtml(invoice As Invoice) As String
        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>"
    End Function
End Class
$vbLabelText   $csharpLabel

範例輸出

合規的關鍵在於使用正確的XML名字空間,遵循CII模式結構,並以適當的文件名嵌入XML。 型別程式碼"380"明確將文件標識為UN/CEFACT標準中的商業發票。

如何為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>";
    }
}
Imports IronPdf
Imports 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
End Enum

' 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 Function GenerateCompliantInvoice(invoice As Invoice, standard As InvoiceStandard) As PdfDocument
        ' Generate the base PDF from HTML
        Dim renderer As New ChromePdfRenderer()
        Dim html As String = BuildInvoiceHtml(invoice)
        Dim pdf As PdfDocument = renderer.RenderHtmlAsPdf(html)

        ' Attach the appropriate XML format based on target market/regulation
        Select Case standard
            Case InvoiceStandard.ZUGFeRD, InvoiceStandard.FacturX
                ' Both use Cross-Industry Invoice format, just different filenames
                EmbedCIIXmlData(pdf, invoice, standard)
            Case InvoiceStandard.Peppol
                ' Peppol uses Universal Business Language format
                EmbedUBLXmlData(pdf, invoice)
        End Select

        Return pdf
    End Function

    ' Creates and embeds CII-format XML (used by ZUGFeRD and Factur-X)
    Private Sub EmbedCIIXmlData(pdf As PdfDocument, invoice As Invoice, standard As InvoiceStandard)
        Dim xml As String = GenerateCIIXml(invoice)

        ' Filename convention differs between German and French standards
        Dim filename As String = If(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")
    End Sub

    ' Creates and embeds UBL-format XML for Peppol network compliance
    Private Sub EmbedUBLXmlData(pdf As PdfDocument, invoice As Invoice)
        ' UBL (Universal Business Language) is the Peppol standard format
        Dim xml As String = $"<?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")
    End Sub

    ' Generates minimal CII XML structure for demonstration
    Private Function GenerateCIIXml(invoice As Invoice) As String
        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>"
    End Function

    Private Function BuildInvoiceHtml(invoice As Invoice) As String
        Return $"<html><body><h1>Invoice {invoice.InvoiceNumber}</h1></body></html>"
    End Function
End Class
$vbLabelText   $csharpLabel

此架構允許您在不重構核心發票生成邏輯的情況下新增新興標準。 基於枚舉的方法讓使用者或配置輕鬆決定使用何種合規模式。


Icon Quote related to 如何為EU規定未來證明發票

我最喜歡的程式庫是IronPDF。它允許快速高效地操作PDF文件。它還有許多有價值的功能,例如導出到PDF/A格式和數位簽署PDF文件。

Milan Jovanovic related to 如何為EU規定未來證明發票

Milan Jovanovic

Microsoft MVP

查看案例研究
Icon Quote related to 如何為EU規定未來證明發票

IronOCR意味著我們每年可以從手動處理中節省$40,000,同時提高生產力,釋放資源以進行高影響的任務。我會強烈推薦它。

Brent Matzelle related to 如何為EU規定未來證明發票

Brent Matzelle

首席技術官,OPYN

查看案例研究
Icon Quote related to 如何為EU規定未來證明發票

IronSuite在我們的運營中扮演著至關重要的角色。這些工具增加了包括建立平面圖和改善庫存管理在內的業務效率。

David Jones related to 如何為EU規定未來證明發票

David Jones

首席軟體工程師,Agorus Build

查看案例研究

How to Extract Data from PDF Invoices in C

生成發票只是問題的一半。 大多數企業還會收到供應商的發票,必須提取資料以進行處理。 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);
        }
    }
}
Imports IronPdf
Imports 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 Function ExtractInvoiceText(pdfPath As String) As String
        Dim pdf = PdfDocument.FromFile(pdfPath)

        ' IronPDF handles the complexity of PDF text encoding and positioning
        Dim allText As String = pdf.ExtractAllText()
        Console.WriteLine("Full invoice text:")
        Console.WriteLine(allText)

        Return allText
    End Function

    ' Extracts text page by page - useful for multi-page invoices
    ' Allows you to process header info separately from line items
    Public Sub ExtractTextByPage(pdfPath As String)
        Dim pdf = PdfDocument.FromFile(pdfPath)

        ' Iterate through each page (0-indexed)
        For i As Integer = 0 To pdf.PageCount - 1
            Dim pageText As String = pdf.ExtractTextFromPage(i)
            Console.WriteLine(vbCrLf & "--- Page " & (i + 1).ToString() & " ---")
            Console.WriteLine(pageText)
        Next
    End Sub
End Class
$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;
    }
}
Imports IronPdf
Imports System
Imports System.Collections.Generic

' Data model for a single invoice line item
Public Class InvoiceLineItem
    Public Property Description As String
    Public Property Quantity As Decimal
    Public Property UnitPrice As Decimal
    Public Property Total As Decimal
End Class

' 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 Function ExtractLineItems(pdfPath As String) As List(Of InvoiceLineItem)
        Dim pdf = PdfDocument.FromFile(pdfPath)
        Dim text As String = pdf.ExtractAllText()

        Dim lineItems As New List(Of InvoiceLineItem)()
        Dim lines() As String = text.Split(ControlChars.Lf)

        For Each line As String In lines
            ' Currency symbols indicate potential line items with amounts
            If line.Contains("$") OrElse line.Contains("€") Then
                Console.WriteLine($"Potential line item: {line.Trim()}")

                ' Split on whitespace to separate columns
                ' Actual parsing logic depends on your invoice format
                Dim parts() As String = line.Split(New Char() {ControlChars.Tab, " "c}, StringSplitOptions.RemoveEmptyEntries)

                ' Try to find numeric values that could be amounts
                For Each part As String In parts
                    Dim cleaned As String = part.Replace("$", "").Replace("€", "").Replace(",", "")
                    Dim amount As Decimal
                    If Decimal.TryParse(cleaned, amount) Then
                        Console.WriteLine($"  Found amount: {amount:C}")
                    End If
                Next
            End If
        Next

        Return lineItems
    End Function
End Class
$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;
    }
}
Imports IronPdf
Imports System
Imports System.Text.RegularExpressions

' Data model for extracted invoice information
Public Class InvoiceData
    Public Property InvoiceNumber As String
    Public Property InvoiceDate As String
    Public Property TotalAmount As Decimal
    Public Property VendorName As String
End Class

' Extracts key invoice fields using regex pattern matching
' Multiple patterns handle variations across different vendors
Public Class InvoiceParser
    Public Function ParseInvoice(pdfPath As String) As InvoiceData
        Dim pdf = PdfDocument.FromFile(pdfPath)
        Dim text As String = pdf.ExtractAllText()

        Dim invoiceData As New InvoiceData()

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

        For Each pattern As String In invoiceNumberPatterns
            Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase)
            If match.Success Then
                invoiceData.InvoiceNumber = match.Groups(1).Value
                Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}")
                Exit For
            End If
        Next

        ' Date patterns for US, European, and written formats
        Dim datePatterns As String() = {
            "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
        }

        For Each pattern As String In datePatterns
            Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase)
            If match.Success Then
                invoiceData.InvoiceDate = match.Groups(1).Value
                Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}")
                Exit For
            End If
        Next

        ' Look for total amount with various labels
        Dim totalPatterns As String() = {
            "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})"
        }

        For Each pattern As String In totalPatterns
            Dim match = Regex.Match(text, pattern, RegexOptions.IgnoreCase)
            If match.Success Then
                ' Remove commas before parsing
                Dim amountStr As String = match.Groups(1).Value.Replace(",", "")
                Dim amount As Decimal
                If Decimal.TryParse(amountStr, amount) Then
                    invoiceData.TotalAmount = amount
                    Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}")
                    Exit For
                End If
            End If
        Next

        Return invoiceData
    End Function
End Class
$vbLabelText   $csharpLabel

這種模式化的方法非常適合具有可預測格式的發票。 多種模式變體可處理供應商間常見的格式差異,如"Invoice #"和"Invoice Number:"

那麼掃描或基於圖像的發票怎麼辦?

上面顯示的文字提取方法適用於包含內嵌文字的PDF。 然而,掃描的文件和基於圖像的PDF無法提取文字。 它們本質上是發票的圖片。

請注意對於處理掃描的發票,您將需要OCR(光學字元識別)功能。 IronOCR是Iron Suite的組成部分,在這些場景中與IronPDF無縫整合。 存取 https://ironsoftware.com/csharp/ocr/ 以了解更多有關從掃描文件和圖像中提取文字的資訊。


如何使用AI在.NET中處理發票

傳統模式匹配在標準化的發票上效果良好,但實際上的應付帳款部門會收到不同格式的文件。 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;
    }
}
Imports IronPdf
Imports System
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports System.Threading.Tasks

' Data model for extracted invoice information
Public Class InvoiceData
    Public Property InvoiceNumber As String
    Public Property InvoiceDate As String
    Public Property VendorName As String
    Public Property TotalAmount As Decimal
End Class

' 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 As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _apiUrl As String

    Public Sub New(apiKey As String, Optional apiUrl As String = "https://api.openai.com/v1/chat/completions")
        _apiKey = apiKey
        _apiUrl = apiUrl
        _httpClient = New HttpClient()
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}")
    End Sub

    Public Async Function ParseInvoiceWithAI(pdfPath As String) As Task(Of InvoiceData)
        ' First extract raw text from the PDF using IronPDF
        Dim pdf = PdfDocument.FromFile(pdfPath)
        Dim invoiceText As String = pdf.ExtractAllText()

        ' Construct a prompt that instructs the AI to return structured JSON
        ' Being explicit about the format reduces parsing errors
        Dim prompt As String = $"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
        Dim requestBody = New With {
            .model = "gpt-4",
            .messages = New Object() {
                New With {
                    .role = "system",
                    .content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only."
                },
                New With {
                    .role = "user",
                    .content = prompt
                }
            },
            .temperature = 0.1  ' Low temperature ensures consistent, deterministic results
        }

        Dim json As String = JsonSerializer.Serialize(requestBody)
        Dim content As New StringContent(json, Encoding.UTF8, "application/json")

        Dim response = Await _httpClient.PostAsync(_apiUrl, content)
        Dim responseJson As String = Await response.Content.ReadAsStringAsync()

        ' Navigate the API response structure to get the extracted content
        Using doc = JsonDocument.Parse(responseJson)
            Dim messageContent As String = 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
            Dim invoiceData As InvoiceData = JsonSerializer.Deserialize(Of InvoiceData)(messageContent, New JsonSerializerOptions With {.PropertyNameCaseInsensitive = True})

            Return invoiceData
        End Using
    End Function
End Class
$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
    }
}
Imports IronPdf
Imports System
Imports System.Collections.Generic
Imports System.Text.Json
Imports System.Threading.Tasks

' Comprehensive invoice data model with all details
Public Class DetailedInvoiceData
    Public Property InvoiceNumber As String
    Public Property InvoiceDate As DateTime
    Public Property DueDate As DateTime
    Public Property Vendor As VendorInfo
    Public Property Customer As CustomerInfo
    Public Property LineItems As List(Of LineItem)
    Public Property Subtotal As Decimal
    Public Property Tax As Decimal
    Public Property Total As Decimal
End Class

Public Class VendorInfo
    Public Property Name As String
    Public Property Address As String
    Public Property TaxId As String
End Class

Public Class CustomerInfo
    Public Property Name As String
    Public Property Address As String
End Class

Public Class LineItem
    Public Property Description As String
    Public Property Quantity As Decimal
    Public Property UnitPrice As Decimal
    Public Property Total As Decimal
End Class

' Extracts comprehensive invoice data including line items and party details
Public Class StructuredInvoiceExtractor
    Private ReadOnly _aiParser As AIInvoiceParser

    Public Sub New(apiKey As String)
        _aiParser = New AIInvoiceParser(apiKey)
    End Sub

    Public Async Function ExtractDetailedData(pdfPath As String) As Task(Of DetailedInvoiceData)
        Dim pdf = PdfDocument.FromFile(pdfPath)
        Dim text As String = pdf.ExtractAllText()

        ' Define the exact JSON structure we want the AI to return
        ' This schema guides the AI to extract all relevant fields
        Dim jsonSchema As String = "{
  ""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
        Dim prompt As String = $"
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
    End Function
End Class
$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;
    }
}
Imports IronPdf
Imports 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 _aiParser As AIInvoiceParser

    Public Sub New(aiApiKey As String)
        _aiParser = New AIInvoiceParser(aiApiKey)
    End Sub

    Public Async Function ProcessAnyInvoice(pdfPath As String) As Task(Of InvoiceData)
        Dim pdf = PdfDocument.FromFile(pdfPath)
        Dim text As String = pdf.ExtractAllText()

        ' First attempt: regex patterns (fast and free)
        Dim patternParser = New InvoiceParser()
        Dim standardResult = patternParser.ParseInvoiceFromText(text)

        ' If pattern matching found all required fields, use that result
        If IsComplete(standardResult) Then
            Console.WriteLine("Pattern extraction successful")
            Return standardResult
        End If

        ' 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")
        Dim aiResult = Await _aiParser.ParseInvoiceWithAI(pdfPath)

        Return aiResult
    End Function

    ' Validates that we have the minimum required fields
    Private Function IsComplete(data As InvoiceData) As Boolean
        Return Not String.IsNullOrEmpty(data.InvoiceNumber) AndAlso
               Not String.IsNullOrEmpty(data.InvoiceDate) AndAlso
               data.TotalAmount > 0
    End Function
End Class
$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}");
            }
        }
    }
}
Imports IronPdf
Imports System
Imports System.IO
Imports System.Threading.Tasks
Imports System.Collections.Generic
Imports System.Linq

' Tracks the outcome of processing each invoice
Public Class ProcessingResult
    Public Property FileName As String
    Public Property Success As Boolean
    Public Property InvoiceNumber As String
    Public Property ErrorMessage As String
End Class

' Complete automation pipeline for accounts payable
' Watches a folder, extracts data, validates, and routes to accounting system
Public Class InvoiceAutomationPipeline
    Private ReadOnly _processor As SmartInvoiceProcessor
    Private ReadOnly _inputFolder As String
    Private ReadOnly _processedFolder As String
    Private ReadOnly _errorFolder As String

    Public Sub New(apiKey As String, inputFolder As String)
        _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)
    End Sub

    ' Main entry point - processes all PDFs in the input folder
    Public Async Function ProcessInvoiceBatch() As Task(Of List(Of ProcessingResult))
        Dim invoiceFiles As String() = Directory.GetFiles(_inputFolder, "*.pdf")
        Console.WriteLine($"Found {invoiceFiles.Length} invoices to process")

        Dim results As New List(Of ProcessingResult)()

        For Each invoicePath As String In invoiceFiles
            Dim fileName As String = Path.GetFileName(invoicePath)

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

                ' Extract data using smart processor (patterns first, then AI)
                Dim invoiceData = Await _processor.ProcessAnyInvoice(invoicePath)

                ' Ensure we have minimum required fields before proceeding
                If ValidateInvoiceData(invoiceData) Then
                    ' Send to accounting system (QuickBooks, Xero, etc.)
                    Await SaveToAccountingSystem(invoiceData)

                    ' Archive successful invoices
                    Dim destPath As String = Path.Combine(_processedFolder, fileName)
                    File.Move(invoicePath, destPath, overwrite:=True)

                    results.Add(New ProcessingResult With {
                        .FileName = fileName,
                        .Success = True,
                        .InvoiceNumber = invoiceData.InvoiceNumber
                    })

                    Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}")
                Else
                    Throw New Exception("Validation failed - missing required fields")
                End If
            Catch ex As Exception
                Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}")

                ' Quarantine failed invoices for manual review
                Dim destPath As String = Path.Combine(_errorFolder, fileName)
                File.Move(invoicePath, destPath, overwrite:=True)

                results.Add(New ProcessingResult With {
                    .FileName = fileName,
                    .Success = False,
                    .ErrorMessage = ex.Message
                })
            End Try
        Next

        GenerateReport(results)
        Return results
    End Function

    ' Checks for minimum required fields
    Private Function ValidateInvoiceData(data As InvoiceData) As Boolean
        Return Not String.IsNullOrEmpty(data.InvoiceNumber) AndAlso
               Not String.IsNullOrEmpty(data.VendorName) AndAlso
               data.TotalAmount > 0
    End Function

    ' Placeholder for accounting system integration
    Private Async Function SaveToAccountingSystem(data As InvoiceData) As Task
        ' 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
    End Function

    ' Outputs a summary of the batch processing results
    Private Sub GenerateReport(results As List(Of ProcessingResult))
        Dim successful As Integer = results.Count(Function(r) r.Success)
        Dim failed As Integer = results.Count(Function(r) Not r.Success)

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

        If failed > 0 Then
            Console.WriteLine(vbCrLf & "Failed invoices requiring review:")
            For Each failure In results.Where(Function(r) Not r.Success)
                Console.WriteLine($"  • {failure.FileName}: {failure.ErrorMessage}")
            Next
        End If
    End Sub
End Class
$vbLabelText   $csharpLabel

這條流水線實現了完整的工作流程:掃描文件夾中的傳入PDF,處理每一個,驗證提取的資料,將成功提取的資料引導至您的會計系統,並將失敗記錄設置隔離以供人工審查。 摘要報告提供了對處理結果的可見性。


How to Integrate C# Invoice Processing with Accounting Systems

提取的發票資料最終需要流入會計系統以便付款和記錄保存。 具體方式因平台而異,但整合模式是一致的。

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}");
    }
}
Imports System
Imports System.Net.Http
Imports System.Text
Imports System.Text.Json
Imports 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 As HttpClient
    Private ReadOnly _apiKey As String
    Private ReadOnly _baseUrl As String

    Public Sub New(apiKey As String, baseUrl As String)
        _apiKey = apiKey
        _baseUrl = baseUrl
        _httpClient = New HttpClient()
        _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}")
    End Sub

    ' Creates a Bill in QuickBooks (vendor invoices are called "Bills")
    Public Async Function SendToQuickBooks(invoice As InvoiceData) As Task
        ' QuickBooks Bill structure - see their API docs for full schema
        Dim bill = New With {
            .VendorRef = New With {.name = invoice.VendorName},
            .TxnDate = invoice.InvoiceDate,
            .DocNumber = invoice.InvoiceNumber,
            .TotalAmt = invoice.TotalAmount,
            .Line = New Object() {
                New With {
                    .Amount = invoice.TotalAmount,
                    .DetailType = "AccountBasedExpenseLineDetail",
                    .AccountBasedExpenseLineDetail = New With {
                        .AccountRef = New With {.name = "Accounts Payable"}
                    }
                }
            }
        }

        Await PostToApi("/v3/company/{companyId}/bill", bill)
    End Function

    ' Creates an accounts payable invoice in Xero
    Public Async Function SendToXero(invoice As InvoiceData) As Task
        ' ACCPAY type indicates this is a bill to pay (not a sales invoice)
        Dim bill = New With {
            .Type = "ACCPAY",
            .Contact = New With {.Name = invoice.VendorName},
            .Date = invoice.InvoiceDate,
            .InvoiceNumber = invoice.InvoiceNumber,
            .Total = invoice.TotalAmount
        }

        Await PostToApi("/api.xro/2.0/Invoices", bill)
    End Function

    ' Generic POST helper with error handling
    Private Async Function PostToApi(endpoint As String, payload As Object) As Task
        Dim json As String = JsonSerializer.Serialize(payload)
        Dim content = New StringContent(json, Encoding.UTF8, "application/json")

        Dim response = Await _httpClient.PostAsync($"{_baseUrl}{endpoint}", content)

        If Not response.IsSuccessStatusCode Then
            Dim error As String = Await response.Content.ReadAsStringAsync()
            Throw New Exception($"API Error: {response.StatusCode} - {error}")
        End If

        Console.WriteLine($"Successfully posted to {endpoint}")
    End Function
End Class
$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
            };
        }
    }
}
Imports System
Imports System.Collections.Concurrent
Imports System.Collections.Generic
Imports System.Linq
Imports System.Threading
Imports System.Threading.Tasks

' Tracks the result of processing a single invoice in a batch
Public Class BatchResult
    Public Property FilePath As String
    Public Property Success As Boolean
    Public Property InvoiceNumber As String
    Public Property Error As String
End Class

' High-volume invoice processor with controlled parallelism
' Prevents overwhelming APIs while maximizing throughput
Public Class BatchInvoiceProcessor
    Private ReadOnly _invoiceProcessor As SmartInvoiceProcessor
    Private ReadOnly _accountingIntegration As AccountingSystemIntegration
    Private ReadOnly _maxConcurrency As Integer

    Public Sub New(aiApiKey As String, accountingApiKey As String, accountingUrl As String, Optional maxConcurrency As Integer = 5)
        _invoiceProcessor = New SmartInvoiceProcessor(aiApiKey)
        _accountingIntegration = New AccountingSystemIntegration(accountingApiKey, accountingUrl)
        _maxConcurrency = maxConcurrency ' Adjust based on API rate limits
    End Sub

    ' Processes multiple invoices in parallel with controlled concurrency
    Public Async Function ProcessInvoiceBatch(invoicePaths As List(Of String)) As Task(Of List(Of BatchResult))
        ' Thread-safe collection for gathering results from parallel tasks
        Dim results = New ConcurrentBag(Of BatchResult)()

        ' Semaphore limits how many invoices process simultaneously
        Dim semaphore = New SemaphoreSlim(_maxConcurrency)

        ' Create a task for each invoice
        Dim tasks = invoicePaths.Select(Async Function(path)
                                            ' Wait for a slot to become available
                                            Await semaphore.WaitAsync()
                                            Try
                                                Dim result = Await ProcessSingleInvoice(path)
                                                results.Add(result)
                                            Finally
                                                ' Release slot for next invoice
                                                semaphore.Release()
                                            End Try
                                        End Function)

        ' Wait for all invoices to complete
        Await Task.WhenAll(tasks)

        ' Output summary statistics
        Dim resultList = results.ToList()
        Dim successful = resultList.Count(Function(r) r.Success)
        Dim failed = resultList.Count(Function(r) Not r.Success)

        Console.WriteLine(vbCrLf & "Batch Processing Complete:")
        Console.WriteLine($"  Total: {resultList.Count}")
        Console.WriteLine($"  Successful: {successful}")
        Console.WriteLine($"  Failed: {failed}")

        Return resultList
    End Function

    ' Processes one invoice: extract data and send to accounting system
    Private Async Function ProcessSingleInvoice(pdfPath As String) As Task(Of BatchResult)
        Try
            Console.WriteLine($"Processing: {pdfPath}")

            Dim invoiceData = Await _invoiceProcessor.ProcessAnyInvoice(pdfPath)
            Await _accountingIntegration.SendToQuickBooks(invoiceData)

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

            Return New BatchResult With {
                .FilePath = pdfPath,
                .Success = True,
                .InvoiceNumber = invoiceData.InvoiceNumber
            }
        Catch ex As Exception
            Console.WriteLine($"✗ Failed: {pdfPath}")

            Return New BatchResult With {
                .FilePath = pdfPath,
                .Success = False,
                .Error = ex.Message
            }
        End Try
    End Function
End Class
$vbLabelText   $csharpLabel

SemaphoreSlim 確保您不會對外部API造成過載,或耗盡系統資源。 根據您的API速率限制和伺服器容量調整_maxConcurrencyConcurrentBag 安全地收集並行操作的結果。


後續步驟

發票自動化代表著一個大幅減少人工工作、最小化錯誤和加速業務流程的重要機會。 本指南講解了完整的生命週期:從HTML模板生成專業發票,符合ZUGFeRD和Factur-X電子發票標準,從收到的發票中提取資料,使用模式匹配和AI驅動的處理,以及構建可擴展的自動化流程。

IronPDF是這些功能的基礎,提供強大的HTML到PDF渲染,可靠的文字提取及為PDF/A-3電子發票合規性所需的附件功能。 其基於Chrome的渲染引擎確保您的發票看起來正如您所設計,並且其提取方法自動處理PDF文字編碼的複雜性。

本文展示的模式是起點。 實際應用需要針對您特定的發票格式、會計系統和業務規則進行調整。 對於高容量情況,本批量處理教程涵蓋了控制的並行執行和錯誤恢復。

準備好開始構建了嗎? 下載IronPDF並嘗試免費試用。 該庫包含免費的開發授權,因此您可以在進入生產環境之前,完全評估發票生成、資料提取及PDF報告功能。 如果您對發票自動化或會計系統整合有疑問,請聯繫我們的工程支援團隊

常見問題

IronPDF在C#發票處理中有什麼應用?

IronPDF在C#發票處理中用於生成專業PDF發票,提取結構化資料,並自動化發票工作流程,同時確保符合ZUGFeRD和Factur-X等標準。

如何在C#中使用IronPDF生成PDF發票?

您可以通過利用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
技術作家

Curtis Chau擁有Carleton大學的電腦科學學士學位,專精於前端開發,擁有Node.js、TypeScript、JavaScript和React的專業知識。Curtis熱衷於建立直觀且美觀的使用者介面,喜愛使用現代框架並建立結構良好、視覺吸引力的手冊。

除了開發,Curtis對物聯網(IoT)有濃厚的興趣,探索創新的方法來整合硬體和軟體。在空閒時間,他喜歡玩遊戲和建立Discord機器人,結合他對技術的熱愛與創造力。

準備開始了嗎?
Nuget 下載 19,936,792 | 版本: 2026.7 剛剛發布
Still Scrolling Icon

還在捲動嗎?

想快速獲得證明嗎? PM > Install-Package IronPdf
執行範例 看您的HTML變成PDF。