IronPDF 튜토리얼 C#을 이용한 송장 처리 Invoice Processing in C#: Generate, Extract, and Automate PDF Invoices with .NET 커티스 차우 업데이트됨:1월 20, 2026 다운로드 IronPDF NuGet 다운로드 DLL 다운로드 윈도우 설치 프로그램 무료 체험 시작하기 LLM용 사본 LLM용 사본 LLM용 마크다운 형식으로 페이지를 복사하세요 ChatGPT에서 열기 ChatGPT에 이 페이지에 대해 문의하세요 제미니에서 열기 제미니에게 이 페이지에 대해 문의하세요 Grok에서 열기 Grok에게 이 페이지에 대해 문의하세요 혼란 속에서 열기 Perplexity에게 이 페이지에 대해 문의하세요 공유하다 페이스북에 공유하기 트위터에 공유하기 LinkedIn에 공유하기 URL 복사 이메일로 기사 보내기 This article was translated from English: Does it need improvement? Translated View the article in English IronPDF를 사용한 C# .NET 기반 송장 처리는 문서 수명 주기 전반을 포괄합니다. HTML 템플릿에서 전문적인 PDF 송장을 생성하고, ZUGFeRD 및 Factur-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 청구서를 생성해 보세요. 지금 바로 NuGet을 사용하여 PDF 만들기를 시작하세요. NuGet 패키지 관리자를 사용하여 IronPDF를 설치하세요. PM > Install-Package IronPdf 다음 코드 조각을 복사하여 실행하세요. var renderer = new IronPdf.ChromePdfRenderer(); var pdf = renderer.RenderHtmlAsPdf("<h1>Invoice #1001</h1><p>Total: $500.00</p>"); pdf.SaveAs("invoice.pdf"); 실제 운영 환경에서 테스트할 수 있도록 배포하세요. 지금 바로 무료 체험판을 통해 프로젝트에서 IronPDF를 사용해 보세요. 30일 무료 체험 IronPDF를 구매하거나 30일 무료 체험판에 가입한 후, 애플리케이션 시작 부분에 라이선스 키를 입력하세요. IronPdf.License.LicenseKey = "KEY"; IronPdf.License.LicenseKey = "KEY"; $vbLabelText $csharpLabel 지금 바로 무료 체험판을 통해 IronPDF을 프로젝트에서 사용해 보세요. 첫 번째 단계: 무료로 시작하세요 !{--010011000100100101000010010100100100000101010010010110010101111101001110010101010001110100010101010100010111110100100101001110010100110101010001000001010011000100110001001100010111110100001001001100010011110100001101001011--} 목차 요약: 빠른 시작 가이드 간략한 개요 전문적인 PDF 청구서 생성 송장 HTML 템플릿 만들기 동적 품목 추가 및 합계 계산 회사 브랜딩 및 워터마크 추가 결제 링크용 QR 코드 삽입 전자송장 발행 기준을 준수합니다 ZUGFeRD란 무엇이며 어떻게 작동합니까? 팩터-X란 무엇인가요? PDF/A-3 송장에 XML 데이터 삽입 EU 규정에 맞춰 미래 지향적인 송장 발행 PDF 송장에서 데이터 추출 PDF 송장에서 텍스트 추출 품목별 테이블 데이터 추출 송장 번호, 날짜 및 합계에 대한 패턴 매칭 AI 기반 송장 처리 송장 분석에 AI 통합 구조화된 JSON 데이터 추출 일관성 없는 송장 형식 처리 매입채무 자동화 파이프라인 구축 회계 시스템과의 연동 QuickBooks, Xero 및 SAP 통합 패턴 수백 건의 송장 일괄 처리 !{--010011000100100101000010010100100100000101010010010110010101111101001110010101010001110100010101010100010111110100100101001110010100110101010001000001010011000100110001001100010111110100001001001100010011110100001101001011--} 송장 수명 주기란 무엇이며, PDF가 여전히 표준으로 남아 있는 이유는 무엇입니까? 코딩에 들어가기 전에, 최신 비즈니스 시스템에서 송장이 거치는 전체 과정을 이해하는 것이 도움이 됩니다. 송장 수명 주기는 생성, 배포, 수령, 데이터 추출 및 회계 통합의 다섯 가지 단계로 구성됩니다. 송장 발행 절차가 시작됩니다. 기업은 품목별 내역, 가격, 세금 계산, 지불 조건 및 브랜드 정보를 포함하는 송장을 작성합니다. 청구서는 전문적인 느낌을 주어야 하며 모든 법적 요건을 충족해야 합니다. 다음 단계는 유통 단계로, 고객에게 이메일, 고객 포털 또는 일반 우편을 통해 청구서가 발송됩니다. 고객이 서류를 받으면, 회계팀에서 해당 서류를 기록하고 처리 준비를 합니다. 데이터 추출은 공급업체 정보, 품목, 총액, 납기일과 같은 송장의 주요 정보를 추출하여 구매 주문서와 대조 및 일치 여부를 확인할 수 있도록 합니다. 마지막으로, 회계 통합을 통해 이 데이터를 QuickBooks, Xero 또는 SAP와 같은 재무 시스템으로 옮겨 결제 및 기록 관리를 수행합니다. 오랜 시간이 지났는데도 PDF가 여전히 가장 널리 사용되는 형식인 이유는 무엇일까요? 여러 가지 장점이 독특하게 결합된 결과입니다. PDF 파일은 사용하는 기기나 운영 체제에 관계없이 송장 형식을 일관되게 유지합니다. 누군가가 윈도우, 맥 또는 휴대전화에서 청구서를 열더라도 디자인한 그대로 표시됩니다. 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의 크롬 기반 렌더링 엔진이 이를 픽셀 단위로 완벽한 PDF로 변환하도록 할 수 있습니다. 다음은 이러한 접근 방식을 보여주는 기본적인 송장 템플릿입니다. :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/basic-invoice-template.cs using IronPdf; // Define the HTML template for a basic invoice // Uses inline CSS for styling headers, tables, and totals string invoiceHtml = @" E html> le> body { font-family: Arial, sans-serif; padding: 40px; } .header { text-align: right; margin-bottom: 40px; } .company-name { font-size: 24px; font-weight: bold; color: #333; } .invoice-title { font-size: 32px; margin: 20px 0; } .bill-to { margin: 20px 0; } table { width: 100%; border-collapse: collapse; margin: 20px 0; } th { background-color: #2A95D5; color: white; padding: 10px; text-align: left; } td { padding: 10px; border-bottom: 1px solid #ddd; } .total { text-align: right; font-size: 20px; font-weight: bold; margin-top: 20px; } yle> class='header'> <div class='company-name'>Your Company Name</div> <div>123 Business Street</div> <div>City, State 12345</div> v> class='invoice-title'>INVOICE</div> class='bill-to'> <strong>Bill To:</strong><br> Customer Name<br> 456 Customer Avenue<br> City, State 67890 v> le> <tr> <th>Description</th> <th>Quantity</th> <th>Price</th> <th>Total</th> </tr> <tr> <td>Web Development Services</td> <td>10 hours</td> <td>$100.00</td> <td>$1,000.00</td> </tr> <tr> <td>Consulting</td> <td>5 hours</td> <td>$150.00</td> <td>$750.00</td> </tr> ble> class='total'>Total: $1,750.00</div> ; // Initialize the Chrome-based PDF renderer var renderer = new ChromePdfRenderer(); // Convert the HTML string to a PDF document var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Save the generated PDF to disk pdf.SaveAs("basic-invoice.pdf"); $vbLabelText $csharpLabel 샘플 출력 이 접근 방식은 엄청난 유연성을 제공합니다. Chrome에서 작동하는 모든 CSS는 flexbox, 그리드 레이아웃, 사용자 지정 글꼴과 같은 최신 기능을 포함하여 PDF에서도 작동합니다. URL이나 로컬 파일 경로를 참조하여 외부 스타일시트와 이미지를 사용할 수도 있습니다. 동적 품목 추가 및 합계 계산 방법 실제 청구서에는 정적인 내용이 거의 없습니다. 데이터베이스에서 품목을 가져와 채우고, 소계를 계산하고, 세율을 적용하고, 통화 값을 형식화해야 합니다. 다음 예시는 동적 송장 생성을 위한 즉시 사용 가능한 패턴을 보여줍니다. using IronPdf; using System; using System.Collections.Generic; using System.Linq; // Represents a single line item on an invoice public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } // Auto-calculates line total from quantity and unit price public decimal Total => Quantity * UnitPrice; } // Represents a complete invoice with customer details and line items public class Invoice { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public string CustomerName { get; set; } public string CustomerAddress { get; set; } public List<InvoiceLineItem> LineItems { get; set; } // Computed properties for invoice totals public decimal Subtotal => LineItems.Sum(item => item.Total); public decimal TaxRate { get; set; } = 0.08m; // Default 8% tax rate public decimal Tax => Subtotal * TaxRate; public decimal Total => Subtotal + Tax; } // Generates PDF invoices from Invoice objects using HTML templates public class InvoiceGenerator { public PdfDocument GenerateInvoice(Invoice invoice) { // Build HTML table rows dynamically from line items string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@" <tr> <td>{item.Description}</td> <td>{item.Quantity}</td> <td>${item.UnitPrice:F2}</td> <td>${item.Total:F2}</td> </tr> ")); // Build the complete HTML invoice using string interpolation // All invoice data is injected into the template dynamically string invoiceHtml = $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .header {{ text-align: right; margin-bottom: 40px; }} .company-name {{ font-size: 24px; font-weight: bold; color: #333; }} .invoice-details {{ margin: 20px 0; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }} td {{ padding: 10px; border-bottom: 1px solid #ddd; }} .totals {{ text-align: right; margin-top: 20px; }} .totals div {{ margin: 5px 0; }} .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }} </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> </div> <h1>INVOICE</h1> <div class='invoice-details'> <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br> <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br> <strong>Bill To:</strong> {invoice.CustomerName}<br> {invoice.CustomerAddress} </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Unit Price</th> <th>Total</th> </tr> {lineItemsHtml} </table> <div class='totals'> <div>Subtotal: ${invoice.Subtotal:F2}</div> <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div> <div class='grand-total'>Total: ${invoice.Total:F2}</div> </div> </body> </html>"; // Render HTML to PDF and return the document var renderer = new ChromePdfRenderer(); return renderer.RenderHtmlAsPdf(invoiceHtml); } } using IronPdf; using System; using System.Collections.Generic; using System.Linq; // Represents a single line item on an invoice public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } // Auto-calculates line total from quantity and unit price public decimal Total => Quantity * UnitPrice; } // Represents a complete invoice with customer details and line items public class Invoice { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public string CustomerName { get; set; } public string CustomerAddress { get; set; } public List<InvoiceLineItem> LineItems { get; set; } // Computed properties for invoice totals public decimal Subtotal => LineItems.Sum(item => item.Total); public decimal TaxRate { get; set; } = 0.08m; // Default 8% tax rate public decimal Tax => Subtotal * TaxRate; public decimal Total => Subtotal + Tax; } // Generates PDF invoices from Invoice objects using HTML templates public class InvoiceGenerator { public PdfDocument GenerateInvoice(Invoice invoice) { // Build HTML table rows dynamically from line items string lineItemsHtml = string.Join("", invoice.LineItems.Select(item => $@" <tr> <td>{item.Description}</td> <td>{item.Quantity}</td> <td>${item.UnitPrice:F2}</td> <td>${item.Total:F2}</td> </tr> ")); // Build the complete HTML invoice using string interpolation // All invoice data is injected into the template dynamically string invoiceHtml = $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} .header {{ text-align: right; margin-bottom: 40px; }} .company-name {{ font-size: 24px; font-weight: bold; color: #333; }} .invoice-details {{ margin: 20px 0; }} table {{ width: 100%; border-collapse: collapse; margin: 20px 0; }} th {{ background-color: #2A95D5; color: white; padding: 10px; text-align: left; }} td {{ padding: 10px; border-bottom: 1px solid #ddd; }} .totals {{ text-align: right; margin-top: 20px; }} .totals div {{ margin: 5px 0; }} .grand-total {{ font-size: 20px; font-weight: bold; color: #2A95D5; }} </style> </head> <body> <div class='header'> <div class='company-name'>Your Company Name</div> </div> <h1>INVOICE</h1> <div class='invoice-details'> <strong>Invoice Number:</strong> {invoice.InvoiceNumber}<br> <strong>Date:</strong> {invoice.InvoiceDate:MMM dd, yyyy}<br> <strong>Bill To:</strong> {invoice.CustomerName}<br> {invoice.CustomerAddress} </div> <table> <tr> <th>Description</th> <th>Quantity</th> <th>Unit Price</th> <th>Total</th> </tr> {lineItemsHtml} </table> <div class='totals'> <div>Subtotal: ${invoice.Subtotal:F2}</div> <div>Tax ({invoice.TaxRate:P0}): ${invoice.Tax:F2}</div> <div class='grand-total'>Total: ${invoice.Total:F2}</div> </div> </body> </html>"; // Render HTML to PDF and return the document var renderer = new ChromePdfRenderer(); return renderer.RenderHtmlAsPdf(invoiceHtml); } } $vbLabelText $csharpLabel 샘플 출력 Invoice 클래스는 소계, 세금 및 총액에 대한 계산 속성을 포함하여 모든 송장 데이터를 캡슐화합니다. 생성기는 문자열 보간을 사용하여 이 데이터를 HTML로 변환한 다음 PDF로 렌더링합니다. 이러한 관심사 분리는 코드의 유지보수성과 테스트성을 향상시킵니다. 송장에 회사 브랜딩 및 워터마크를 추가하는 방법 전문적인 청구서에는 로고와 같은 브랜딩 요소가 필요하며, 경우에 따라 결제 상태를 나타내는 워터마크도 필요합니다. IronPDF는 HTML에 이미지를 삽입하는 방식과 렌더링 후 프로그래밍 방식으로 워터마크를 추가하는 방식을 모두 지원합니다. :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/branding-watermarks.cs using IronPdf; using IronPdf; var renderer = new ChromePdfRenderer(); // Invoice HTML template with company logo embedded via URL // Logo can also be Base64-encoded or a local file path string htmlWithLogo = @" E html> le> body { font-family: Arial, sans-serif; padding: 40px; } .logo { width: 200px; margin-bottom: 20px; } yle> style='text-align: center;'> <img src='https://yourcompany.com/logo.png' alt='Company Logo' class='logo' /> v> INVOICE</h1> strong>Invoice Number:</strong> INV-2024-001</p> strong>Total:</strong> $1,250.00</p> ; // Render the HTML to PDF var pdf = renderer.RenderHtmlAsPdf(htmlWithLogo); // Apply a diagonal "UNPAID" watermark to mark invoice status // 30% opacity keeps the content readable while the watermark is visible pdf.ApplyWatermark("<h1 style='color: red;'>UNPAID</h1>", opacity: 30, rotation: 45, verticalAlignment: IronPdf.Editing.VerticalAlignment.Middle); pdf.SaveAs("invoice-with-watermark.pdf"); using IronPdf; $vbLabelText $csharpLabel 샘플 출력 ApplyWatermark 메서드는 HTML 콘텐츠를 허용하므로 워터마크의 모양을 완벽하게 제어할 수 있습니다. 투명도, 회전 및 위치를 조정하여 원하는 모양을 정확하게 구현할 수 있습니다. 이 기능은 전체 문서를 다시 생성하지 않고도 송장을 "지불됨", "초안" 또는 "취소됨"으로 표시하는 데 특히 유용합니다. 결제 링크용 QR 코드 삽입 방법 최근 청구서에는 고객이 스캔하여 빠르게 결제할 수 있는 QR 코드가 포함되는 경우가 많습니다. IronPDF는 PDF 생성에 중점을 두지만, 바코드 생성을 위해 IronQR과도 원활하게 연동됩니다. :path=/static-assets/pdf/content-code-examples/tutorials/csharp-invoice-processing/qr-code-payment.cs using IronPdf; using IronQr; using IronSoftware.Drawing; string invoiceNumber = "INV-2026-002"; decimal amount = 1500.00m; // Create a payment URL with invoice details as query parameters string paymentUrl = $"https://yourcompany.com/pay?invoice={invoiceNumber}&amount={amount}"; // Generate QR code from the payment URL using IronQR QrCode qrCode = QrWriter.Write(paymentUrl); AnyBitmap qrImage = qrCode.Save(); qrImage.SaveAs("payment-qr.png", AnyBitmap.ImageFormat.Png); // Build invoice HTML with the QR code image embedded // Customers can scan the QR to pay directly from their phone string invoiceHtml = $@" E html> le> body {{ font-family: Arial, sans-serif; padding: 40px; }} .payment-section {{ margin-top: 40px; text-align: center; border-top: 2px solid #eee; padding-top: 20px; }} .qr-code {{ width: 150px; height: 150px; }} yle> INVOICE {invoiceNumber}</h1> strong>Amount Due:</strong> ${amount:F2}</p> class='payment-section'> <p><strong>Scan to Pay Instantly:</strong></p> <img src='payment-qr.png' alt='Payment QR Code' class='qr-code' /> <p style='font-size: 12px; color: #666;'> Or visit: {paymentUrl} </p> v> ; // Convert HTML to PDF and save var renderer = new ChromePdfRenderer(); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); pdf.SaveAs($"invoice-{invoiceNumber}.pdf"); $vbLabelText $csharpLabel 샘플 출력 QR 코드는 결제 페이지로 바로 연결되어 고객의 불편함을 줄이고 현금 흐름을 가속화합니다. 이 패턴은 URL 기반 결제 시작을 지원하는 모든 결제 제공업체에서 작동합니다. How to Comply with ZUGFeRD and Factur-X E-Invoicing Standards in C# 전자 송장은 유럽 전역에서 빠르게 의무화되고 있습니다. 독일이 ZUGFeRD로 선두를 달렸고, 프랑스가 Factur-X로 그 뒤를 이었다. 이러한 표준은 기계가 읽을 수 있는 XML 데이터를 PDF 송장 내에 포함시켜 자동 처리를 가능하게 하면서도 사람이 읽을 수 있는 문서 형태를 유지합니다. 유럽 시장에서 사업을 운영하는 기업에게는 이러한 기준을 이해하고 적용하는 것이 점점 더 중요해지고 있습니다. ZUGFeRD란 무엇이며 어떻게 작동합니까? ZUGFeRD(독일 전자송장 포럼 중앙 사용자 가이드)는 송장 데이터를 PDF/A-3 규격 문서 내에 XML 파일 첨부 형태로 포함하는 독일 전자송장 표준입니다. 내장된 XML을 통해 OCR이나 구문 분석 없이 자동 데이터 추출이 가능합니다. 이 표준은 세 가지 적합성 수준을 정의하며, 각 수준은 점진적으로 더 구조화된 데이터를 제공합니다. 기본: 간단한 자동 처리에 적합한 핵심 송장 데이터가 포함되어 있습니다. 편의성: 상세 정보를 추가하여 완전 자동화된 송장 처리를 가능하게 합니다. 확장 버전: 다양한 산업 분야의 복잡한 비즈니스 시나리오에 필요한 포괄적인 데이터를 포함합니다. XML은 UN/CEFACT 범산업 송장(CII) 스키마를 따르며, 이는 유럽 전자 송장 표준화의 기반이 되었습니다. Factur-X는 무엇이며 ZUGFeRD와는 어떻게 다른가요? Factur-X는 동일한 기본 표준을 기반으로 하는 프랑스 구현체입니다. ZUGFeRD 2.0과 Factur-X는 기술적으로 동일합니다. 두 시스템은 유럽 표준 EN 16931을 기반으로 동일한 XML 스키마와 적합성 프로파일을 공유합니다. 차이점은 순전히 지역별 명칭 차이일 뿐입니다. ZUGFeRD 사양에 따라 생성된 송장은 Factur-X에서도 유효하며, 그 반대의 경우도 마찬가지입니다. PDF/A-3 송장에 XML 데이터를 삽입하는 방법 IronPDF는 규정을 준수하는 전자 송장을 생성하는 데 필요한 첨부 파일 기능을 제공합니다. 이 과정에는 송장 PDF 생성, CII 스키마에 따른 XML 데이터 생성, 그리고 올바른 명명 규칙에 따라 XML을 첨부 파일로 포함하는 작업이 포함됩니다. using System; using System.Xml.Linq; // Generates ZUGFeRD-compliant invoices by embedding structured XML data // ZUGFeRD allows automated processing while keeping a human-readable PDF public class ZUGFeRDInvoiceGenerator { public void GenerateZUGFeRDInvoice(Invoice invoice) { // First, create the visual PDF that humans will read var renderer = new ChromePdfRenderer(); string invoiceHtml = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Define the UN/CEFACT namespaces required by the ZUGFeRD standard // These are mandatory for compliance with European e-invoicing regulations XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"; XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"; XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"; // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema var zugferdXml = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement(rsm + "CrossIndustryInvoice", new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName), new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName), new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName), // Document context identifies which e-invoicing guideline is being followed new XElement(rsm + "ExchangedDocumentContext", new XElement(ram + "GuidelineSpecifiedDocumentContextParameter", new XElement(ram + "ID", "urn:cen.eu:en16931:2017") ) ), // Core document identification: invoice number, type, and date new XElement(rsm + "ExchangedDocument", new XElement(ram + "ID", invoice.InvoiceNumber), new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT new XElement(ram + "IssueDateTime", new XElement(udt + "DateTimeString", new XAttribute("format", "102"), invoice.InvoiceDate.ToString("yyyyMMdd") ) ) ), // A complete implementation would include additional sections: // - Seller information (ram:SellerTradeParty) // - Buyer information (ram:BuyerTradeParty) // - Line items (ram:IncludedSupplyChainTradeLineItem) // - Payment terms (ram:SpecifiedTradePaymentTerms) // - Tax summaries (ram:ApplicableTradeTax) // Financial summary with all monetary totals new XElement(rsm + "SupplyChainTradeTransaction", new XElement(ram + "ApplicableHeaderTradeSettlement", new XElement(ram + "InvoiceCurrencyCode", "EUR"), new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation", new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal), new XElement(ram + "TaxTotalAmount", new XAttribute("currencyID", "EUR"), invoice.Tax), new XElement(ram + "GrandTotalAmount", invoice.Total), new XElement(ram + "DuePayableAmount", invoice.Total) ) ) ) ) ); // Save the XML to a temp file for embedding string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml"; zugferdXml.Save(xmlPath); // Attach the XML to the PDF - filename must follow ZUGFeRD conventions pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data"); // Final PDF contains both visual invoice and machine-readable XML pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf"); } // Generates simple HTML for the visual portion of the invoice private string BuildInvoiceHtml(Invoice invoice) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} h1 {{ color: #333; }} .zugferd-notice {{ margin-top: 30px; padding: 10px; background: #f0f0f0; font-size: 11px; }} </style> </head> <body> <h1>RECHNUNG / INVOICE</h1> <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p> <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p> <p><strong>Betrag:</strong> €{invoice.Total:F2}</p> <div class='zugferd-notice'> This invoice contains embedded ZUGFeRD data for automated processing. </div> </body> </html>"; } } using System; using System.Xml.Linq; // Generates ZUGFeRD-compliant invoices by embedding structured XML data // ZUGFeRD allows automated processing while keeping a human-readable PDF public class ZUGFeRDInvoiceGenerator { public void GenerateZUGFeRDInvoice(Invoice invoice) { // First, create the visual PDF that humans will read var renderer = new ChromePdfRenderer(); string invoiceHtml = BuildInvoiceHtml(invoice); var pdf = renderer.RenderHtmlAsPdf(invoiceHtml); // Define the UN/CEFACT namespaces required by the ZUGFeRD standard // These are mandatory for compliance with European e-invoicing regulations XNamespace rsm = "urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100"; XNamespace ram = "urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"; XNamespace udt = "urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"; // Build the ZUGFeRD XML structure following the Cross-Industry Invoice schema var zugferdXml = new XDocument( new XDeclaration("1.0", "UTF-8", null), new XElement(rsm + "CrossIndustryInvoice", new XAttribute(XNamespace.Xmlns + "rsm", rsm.NamespaceName), new XAttribute(XNamespace.Xmlns + "ram", ram.NamespaceName), new XAttribute(XNamespace.Xmlns + "udt", udt.NamespaceName), // Document context identifies which e-invoicing guideline is being followed new XElement(rsm + "ExchangedDocumentContext", new XElement(ram + "GuidelineSpecifiedDocumentContextParameter", new XElement(ram + "ID", "urn:cen.eu:en16931:2017") ) ), // Core document identification: invoice number, type, and date new XElement(rsm + "ExchangedDocument", new XElement(ram + "ID", invoice.InvoiceNumber), new XElement(ram + "TypeCode", "380"), // 380 = Commercial Invoice per UN/CEFACT new XElement(ram + "IssueDateTime", new XElement(udt + "DateTimeString", new XAttribute("format", "102"), invoice.InvoiceDate.ToString("yyyyMMdd") ) ) ), // A complete implementation would include additional sections: // - Seller information (ram:SellerTradeParty) // - Buyer information (ram:BuyerTradeParty) // - Line items (ram:IncludedSupplyChainTradeLineItem) // - Payment terms (ram:SpecifiedTradePaymentTerms) // - Tax summaries (ram:ApplicableTradeTax) // Financial summary with all monetary totals new XElement(rsm + "SupplyChainTradeTransaction", new XElement(ram + "ApplicableHeaderTradeSettlement", new XElement(ram + "InvoiceCurrencyCode", "EUR"), new XElement(ram + "SpecifiedTradeSettlementHeaderMonetarySummation", new XElement(ram + "TaxBasisTotalAmount", invoice.Subtotal), new XElement(ram + "TaxTotalAmount", new XAttribute("currencyID", "EUR"), invoice.Tax), new XElement(ram + "GrandTotalAmount", invoice.Total), new XElement(ram + "DuePayableAmount", invoice.Total) ) ) ) ) ); // Save the XML to a temp file for embedding string xmlPath = $"zugferd-{invoice.InvoiceNumber}.xml"; zugferdXml.Save(xmlPath); // Attach the XML to the PDF - filename must follow ZUGFeRD conventions pdf.Attachments.AddFile(xmlPath, "zugferd-invoice.xml", "ZUGFeRD Invoice Data"); // Final PDF contains both visual invoice and machine-readable XML pdf.SaveAs($"invoice-{invoice.InvoiceNumber}-zugferd.pdf"); } // Generates simple HTML for the visual portion of the invoice private string BuildInvoiceHtml(Invoice invoice) { return $@" <!DOCTYPE html> <html> <head> <style> body {{ font-family: Arial, sans-serif; padding: 40px; }} h1 {{ color: #333; }} .zugferd-notice {{ margin-top: 30px; padding: 10px; background: #f0f0f0; font-size: 11px; }} </style> </head> <body> <h1>RECHNUNG / INVOICE</h1> <p><strong>Rechnungsnummer:</strong> {invoice.InvoiceNumber}</p> <p><strong>Datum:</strong> {invoice.InvoiceDate:dd.MM.yyyy}</p> <p><strong>Betrag:</strong> €{invoice.Total:F2}</p> <div class='zugferd-notice'> This invoice contains embedded ZUGFeRD data for automated processing. </div> </body> </html>"; } } $vbLabelText $csharpLabel 샘플 출력 규정 준수의 핵심은 올바른 XML 네임스페이스를 사용하고, CII 스키마 구조를 따르며, 적절한 파일 이름으로 XML을 포함하는 것입니다. 유형 코드 "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>"; } } $vbLabelText $csharpLabel 이 아키텍처를 사용하면 핵심 송장 생성 로직을 재구성하지 않고도 새로운 표준이 등장할 때 추가할 수 있습니다. 열거형 기반 접근 방식을 사용하면 사용자가 구성 설정을 통해 사용할 규정 준수 모드를 쉽게 결정할 수 있습니다. 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); } } } $vbLabelText $csharpLabel 페이지별 추출은 여러 페이지로 구성된 송장에서 특정 섹션을 찾아야 할 때 특히 유용합니다. 예를 들어, 헤더 정보는 첫 페이지에만 나타나지만 여러 페이지에 걸쳐 있는 품목을 찾는 데 유용합니다. 품목별 테이블 데이터 추출 방법 송장 품목은 일반적으로 표 형식으로 표시됩니다. PDF 파일에는 기본적으로 테이블 구조가 없지만, 텍스트를 추출하고 구문 분석하여 테이블 데이터를 재구성할 수 있습니다. using IronPdf; using System; using System.Collections.Generic; // Data model for a single invoice line item public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts tabular line item data from PDF invoices // Note: PDFs don't have native table structure, so this uses text parsing public class InvoiceTableExtractor { public List<InvoiceLineItem> ExtractLineItems(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var lineItems = new List<InvoiceLineItem>(); string[] lines = text.Split('\n'); foreach (string line in lines) { // Currency symbols indicate potential line items with amounts if (line.Contains("$") || line.Contains("€")) { Console.WriteLine($"Potential line item: {line.Trim()}"); // Split on whitespace to separate columns // Actual parsing logic depends on your invoice format string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); // Try to find numeric values that could be amounts foreach (string part in parts) { string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", ""); if (decimal.TryParse(cleaned, out decimal amount)) { Console.WriteLine($" Found amount: {amount:C}"); } } } } return lineItems; } } using IronPdf; using System; using System.Collections.Generic; // Data model for a single invoice line item public class InvoiceLineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts tabular line item data from PDF invoices // Note: PDFs don't have native table structure, so this uses text parsing public class InvoiceTableExtractor { public List<InvoiceLineItem> ExtractLineItems(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var lineItems = new List<InvoiceLineItem>(); string[] lines = text.Split('\n'); foreach (string line in lines) { // Currency symbols indicate potential line items with amounts if (line.Contains("$") || line.Contains("€")) { Console.WriteLine($"Potential line item: {line.Trim()}"); // Split on whitespace to separate columns // Actual parsing logic depends on your invoice format string[] parts = line.Split(new[] { '\t', ' ' }, StringSplitOptions.RemoveEmptyEntries); // Try to find numeric values that could be amounts foreach (string part in parts) { string cleaned = part.Replace("$", "").Replace("€", "").Replace(",", ""); if (decimal.TryParse(cleaned, out decimal amount)) { Console.WriteLine($" Found amount: {amount:C}"); } } } } return lineItems; } } $vbLabelText $csharpLabel 구문 분석 로직은 송장 형식에 따라 달라집니다. 잘 알려진 공급업체의 일관된 레이아웃을 가진 송장의 경우, 형식별 구문 분석기를 구축할 수 있습니다. 다양한 형식의 경우, 이 글 후반부에 다루는 AI 기반 추출 기능을 고려해 보세요. 송장 번호, 날짜 및 합계에 대한 패턴 매칭 사용 방법 정규 표현식은 송장 텍스트에서 특정 데이터 포인트를 추출하는 데 매우 유용합니다. 송장 번호, 날짜, 합계와 같은 주요 항목은 일반적으로 알아볼 수 있는 패턴을 따릅니다. using IronPdf; using System; using System.Text.RegularExpressions; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public decimal TotalAmount { get; set; } public string VendorName { get; set; } } // Extracts key invoice fields using regex pattern matching // Multiple patterns handle variations across different vendors public class InvoiceParser { public InvoiceData ParseInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var invoiceData = new InvoiceData(); // Try multiple patterns to find invoice number // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer" string[] invoiceNumberPatterns = new[] { @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)", @"INV[-\s]?(\d+)", @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)", @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)" }; foreach (string pattern in invoiceNumberPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceNumber = match.Groups[1].Value; Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}"); break; } } // Date patterns for US, European, and written formats string[] datePatterns = new[] { @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"(\d{1,2}\.\d{1,2}\.\d{4})", // European: DD.MM.YYYY @"(\w+\s+\d{1,2},?\s+\d{4})" // Written: January 15, 2024 }; foreach (string pattern in datePatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceDate = match.Groups[1].Value; Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}"); break; } } // Look for total amount with various labels string[] totalPatterns = new[] { @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})" }; foreach (string pattern in totalPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { // Remove commas before parsing string amountStr = match.Groups[1].Value.Replace(",", ""); if (decimal.TryParse(amountStr, out decimal amount)) { invoiceData.TotalAmount = amount; Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}"); break; } } } return invoiceData; } } using IronPdf; using System; using System.Text.RegularExpressions; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public decimal TotalAmount { get; set; } public string VendorName { get; set; } } // Extracts key invoice fields using regex pattern matching // Multiple patterns handle variations across different vendors public class InvoiceParser { public InvoiceData ParseInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); var invoiceData = new InvoiceData(); // Try multiple patterns to find invoice number // Handles: "Invoice #123", "INV-123", "Invoice Number: 123", German "Rechnungsnummer" string[] invoiceNumberPatterns = new[] { @"Invoice\s*#?\s*:?\s*([A-Z0-9-]+)", @"INV[-\s]?(\d+)", @"Invoice\s+Number\s*:?\s*([A-Z0-9-]+)", @"Rechnungsnummer\s*:?\s*([A-Z0-9-]+)" }; foreach (string pattern in invoiceNumberPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceNumber = match.Groups[1].Value; Console.WriteLine($"Found Invoice Number: {invoiceData.InvoiceNumber}"); break; } } // Date patterns for US, European, and written formats string[] datePatterns = new[] { @"Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"Invoice\s+Date\s*:?\s*(\d{1,2}[/-]\d{1,2}[/-]\d{2,4})", @"(\d{1,2}\.\d{1,2}\.\d{4})", // European: DD.MM.YYYY @"(\w+\s+\d{1,2},?\s+\d{4})" // Written: January 15, 2024 }; foreach (string pattern in datePatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { invoiceData.InvoiceDate = match.Groups[1].Value; Console.WriteLine($"Found Date: {invoiceData.InvoiceDate}"); break; } } // Look for total amount with various labels string[] totalPatterns = new[] { @"Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Amount\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Grand\s+Total\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})", @"Balance\s+Due\s*:?\s*[\$€]?\s*([\d,]+\.\d{2})" }; foreach (string pattern in totalPatterns) { var match = Regex.Match(text, pattern, RegexOptions.IgnoreCase); if (match.Success) { // Remove commas before parsing string amountStr = match.Groups[1].Value.Replace(",", ""); if (decimal.TryParse(amountStr, out decimal amount)) { invoiceData.TotalAmount = amount; Console.WriteLine($"Found Total: ${invoiceData.TotalAmount:F2}"); break; } } } return invoiceData; } } $vbLabelText $csharpLabel 이러한 패턴 기반 접근 방식은 형식이 예측 가능한 송장에 효과적입니다. 다양한 패턴 변형은 "송장 번호"와 "송장 번호:"와 같이 공급업체별로 공통적으로 나타나는 서식 차이를 처리합니다. 스캔본이나 이미지 기반 청구서는 어떻습니까? 위에 제시된 텍스트 추출 방법은 텍스트가 내장된 PDF 파일에서 작동합니다. 하지만 스캔한 문서나 이미지 기반 PDF 파일에는 텍스트를 추출할 수 없습니다. 이것들은 본질적으로 청구서 사진입니다. [{i:(스캔한 송장을 처리하려면 OCR(광학 문자 인식) 기능이 필요합니다. Iron Suite의 일부인 IronOCR은 이러한 시나리오에서 IronPDF와 완벽하게 통합됩니다. 스캔한 문서와 이미지에서 텍스트를 추출하는 방법에 대해 자세히 알아보려면 https://ironsoftware.com/csharp/ocr/을 방문하세요. .NET에서 AI를 사용하여 송장을 처리하는 방법 기존의 패턴 매칭 방식은 표준화된 송장에는 효과적이지만, 실제 회계 부서에서는 무수히 많은 형식의 문서를 받습니다. 바로 이 부분에서 AI 기반 추출 기술이 빛을 발합니다. 대규모 언어 모델은 송장의 의미론을 이해하고 익숙하지 않은 레이아웃에서도 구조화된 데이터를 추출할 수 있습니다. 송장 분석에 AI를 통합하는 방법 AI 기반 송장 처리 패턴은 IronPDF의 텍스트 추출 기능과 LLM API 호출을 결합합니다. 다음은 OpenAI 호환 API와 함께 작동하는 일반적인 구현 예입니다. using IronPdf; using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public string VendorName { get; set; } public decimal TotalAmount { get; set; } } // Leverages AI/LLM APIs to extract structured data from any invoice format // Works with OpenAI or any compatible API endpoint public class AIInvoiceParser { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _apiUrl; public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions") { _apiKey = apiKey; _apiUrl = apiUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath) { // First extract raw text from the PDF using IronPDF var pdf = PdfDocument.FromFile(pdfPath); string invoiceText = pdf.ExtractAllText(); // Construct a prompt that instructs the AI to return structured JSON // Being explicit about the format reduces parsing errors string prompt = $@"Extract the following information from this invoice text. Return ONLY valid JSON with no additional text or markdown formatting. Required fields: - InvoiceNumber: The invoice or document number - InvoiceDate: The invoice date in YYYY-MM-DD format - VendorName: The company or person who sent the invoice - TotalAmount: The total amount due as a number (no currency symbols) Invoice text: {invoiceText} JSON response:"; // Build the API request with a system prompt for context var requestBody = new { model = "gpt-4", messages = new[] { new { role = "system", content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only." }, new { role = "user", content = prompt } }, temperature = 0.1 // Low temperature ensures consistent, deterministic results }; var json = JsonSerializer.Serialize(requestBody); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_apiUrl, content); var responseJson = await response.Content.ReadAsStringAsync(); // Navigate the API response structure to get the extracted content using var doc = JsonDocument.Parse(responseJson); var messageContent = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); Console.WriteLine("AI Extracted Data:"); Console.WriteLine(messageContent); // Deserialize the AI's JSON response into our data class var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return invoiceData; } } using IronPdf; using System; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; // Data model for extracted invoice information public class InvoiceData { public string InvoiceNumber { get; set; } public string InvoiceDate { get; set; } public string VendorName { get; set; } public decimal TotalAmount { get; set; } } // Leverages AI/LLM APIs to extract structured data from any invoice format // Works with OpenAI or any compatible API endpoint public class AIInvoiceParser { private readonly HttpClient _httpClient; private readonly string _apiKey; private readonly string _apiUrl; public AIInvoiceParser(string apiKey, string apiUrl = "https://api.openai.com/v1/chat/completions") { _apiKey = apiKey; _apiUrl = apiUrl; _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_apiKey}"); } public async Task<InvoiceData> ParseInvoiceWithAI(string pdfPath) { // First extract raw text from the PDF using IronPDF var pdf = PdfDocument.FromFile(pdfPath); string invoiceText = pdf.ExtractAllText(); // Construct a prompt that instructs the AI to return structured JSON // Being explicit about the format reduces parsing errors string prompt = $@"Extract the following information from this invoice text. Return ONLY valid JSON with no additional text or markdown formatting. Required fields: - InvoiceNumber: The invoice or document number - InvoiceDate: The invoice date in YYYY-MM-DD format - VendorName: The company or person who sent the invoice - TotalAmount: The total amount due as a number (no currency symbols) Invoice text: {invoiceText} JSON response:"; // Build the API request with a system prompt for context var requestBody = new { model = "gpt-4", messages = new[] { new { role = "system", content = "You are an invoice data extraction assistant. Extract structured data from invoices and return valid JSON only." }, new { role = "user", content = prompt } }, temperature = 0.1 // Low temperature ensures consistent, deterministic results }; var json = JsonSerializer.Serialize(requestBody); var content = new StringContent(json, Encoding.UTF8, "application/json"); var response = await _httpClient.PostAsync(_apiUrl, content); var responseJson = await response.Content.ReadAsStringAsync(); // Navigate the API response structure to get the extracted content using var doc = JsonDocument.Parse(responseJson); var messageContent = doc.RootElement .GetProperty("choices")[0] .GetProperty("message") .GetProperty("content") .GetString(); Console.WriteLine("AI Extracted Data:"); Console.WriteLine(messageContent); // Deserialize the AI's JSON response into our data class var invoiceData = JsonSerializer.Deserialize<InvoiceData>(messageContent, new JsonSerializerOptions { PropertyNameCaseInsensitive = true }); return invoiceData; } } $vbLabelText $csharpLabel 낮은 온도 설정(0.1)은 결정론적 출력을 유도하는데, 이는 동일한 입력에 대해 일관된 결과를 얻고자 하는 데이터 추출 작업에 중요합니다. 송장에서 구조화된 JSON 데이터를 추출하는 방법 품목, 공급업체 정보, 고객 정보 등이 포함된 복잡한 송장의 경우, 더 풍부한 JSON 구조를 요청할 수 있습니다. using IronPdf; using System; using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; // Comprehensive invoice data model with all details public class DetailedInvoiceData { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public VendorInfo Vendor { get; set; } public CustomerInfo Customer { get; set; } public List<LineItem> LineItems { get; set; } public decimal Subtotal { get; set; } public decimal Tax { get; set; } public decimal Total { get; set; } } public class VendorInfo { public string Name { get; set; } public string Address { get; set; } public string TaxId { get; set; } } public class CustomerInfo { public string Name { get; set; } public string Address { get; set; } } public class LineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts comprehensive invoice data including line items and party details public class StructuredInvoiceExtractor { private readonly AIInvoiceParser _aiParser; public StructuredInvoiceExtractor(string apiKey) { _aiParser = new AIInvoiceParser(apiKey); } public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // Define the exact JSON structure we want the AI to return // This schema guides the AI to extract all relevant fields string jsonSchema = @"{ ""InvoiceNumber"": ""string"", ""InvoiceDate"": ""YYYY-MM-DD"", ""DueDate"": ""YYYY-MM-DD"", ""Vendor"": { ""Name"": ""string"", ""Address"": ""string"", ""TaxId"": ""string or null"" }, ""Customer"": { ""Name"": ""string"", ""Address"": ""string"" }, ""LineItems"": [ { ""Description"": ""string"", ""Quantity"": 0.0, ""UnitPrice"": 0.00, ""Total"": 0.00 } ], ""Subtotal"": 0.00, ""Tax"": 0.00, ""Total"": 0.00 }"; // Prompt includes both the schema and the extracted text string prompt = $@"Extract all invoice data and return it in this exact JSON structure: {jsonSchema} Invoice text: {text} Return only valid JSON, no markdown formatting or additional text."; // Call AI API and parse response (implementation as shown above) // Return deserialized DetailedInvoiceData return new DetailedInvoiceData(); // Placeholder } } using IronPdf; using System; using System.Collections.Generic; using System.Text.Json; using System.Threading.Tasks; // Comprehensive invoice data model with all details public class DetailedInvoiceData { public string InvoiceNumber { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public VendorInfo Vendor { get; set; } public CustomerInfo Customer { get; set; } public List<LineItem> LineItems { get; set; } public decimal Subtotal { get; set; } public decimal Tax { get; set; } public decimal Total { get; set; } } public class VendorInfo { public string Name { get; set; } public string Address { get; set; } public string TaxId { get; set; } } public class CustomerInfo { public string Name { get; set; } public string Address { get; set; } } public class LineItem { public string Description { get; set; } public decimal Quantity { get; set; } public decimal UnitPrice { get; set; } public decimal Total { get; set; } } // Extracts comprehensive invoice data including line items and party details public class StructuredInvoiceExtractor { private readonly AIInvoiceParser _aiParser; public StructuredInvoiceExtractor(string apiKey) { _aiParser = new AIInvoiceParser(apiKey); } public async Task<DetailedInvoiceData> ExtractDetailedData(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // Define the exact JSON structure we want the AI to return // This schema guides the AI to extract all relevant fields string jsonSchema = @"{ ""InvoiceNumber"": ""string"", ""InvoiceDate"": ""YYYY-MM-DD"", ""DueDate"": ""YYYY-MM-DD"", ""Vendor"": { ""Name"": ""string"", ""Address"": ""string"", ""TaxId"": ""string or null"" }, ""Customer"": { ""Name"": ""string"", ""Address"": ""string"" }, ""LineItems"": [ { ""Description"": ""string"", ""Quantity"": 0.0, ""UnitPrice"": 0.00, ""Total"": 0.00 } ], ""Subtotal"": 0.00, ""Tax"": 0.00, ""Total"": 0.00 }"; // Prompt includes both the schema and the extracted text string prompt = $@"Extract all invoice data and return it in this exact JSON structure: {jsonSchema} Invoice text: {text} Return only valid JSON, no markdown formatting or additional text."; // Call AI API and parse response (implementation as shown above) // Return deserialized DetailedInvoiceData return new DetailedInvoiceData(); // Placeholder } } $vbLabelText $csharpLabel 일관성 없는 송장 형식을 처리하는 방법 AI 기반 정보 추출의 진정한 힘은 각기 다른 형식을 가진 여러 공급업체의 송장을 처리할 때 드러납니다. 스마트 프로세서는 패턴 기반 추출을 먼저 시도하고(더 빠르고 비용도 들지 않음) 필요한 경우에만 인공지능을 활용할 수 있습니다. using IronPdf; using System.Threading.Tasks; // Hybrid processor that optimizes for cost and capability // Tries fast regex patterns first, uses AI only when patterns fail public class SmartInvoiceProcessor { private readonly AIInvoiceParser _aiParser; public SmartInvoiceProcessor(string aiApiKey) { _aiParser = new AIInvoiceParser(aiApiKey); } public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // First attempt: regex patterns (fast and free) var patternParser = new InvoiceParser(); var standardResult = patternParser.ParseInvoiceFromText(text); // If pattern matching found all required fields, use that result if (IsComplete(standardResult)) { Console.WriteLine("Pattern extraction successful"); return standardResult; } // Fallback: use AI for complex or unusual invoice formats // This costs money but handles any layout Console.WriteLine("Using AI extraction for complex invoice format"); var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath); return aiResult; } // Validates that we have the minimum required fields private bool IsComplete(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.InvoiceDate) && data.TotalAmount > 0; } } using IronPdf; using System.Threading.Tasks; // Hybrid processor that optimizes for cost and capability // Tries fast regex patterns first, uses AI only when patterns fail public class SmartInvoiceProcessor { private readonly AIInvoiceParser _aiParser; public SmartInvoiceProcessor(string aiApiKey) { _aiParser = new AIInvoiceParser(aiApiKey); } public async Task<InvoiceData> ProcessAnyInvoice(string pdfPath) { var pdf = PdfDocument.FromFile(pdfPath); string text = pdf.ExtractAllText(); // First attempt: regex patterns (fast and free) var patternParser = new InvoiceParser(); var standardResult = patternParser.ParseInvoiceFromText(text); // If pattern matching found all required fields, use that result if (IsComplete(standardResult)) { Console.WriteLine("Pattern extraction successful"); return standardResult; } // Fallback: use AI for complex or unusual invoice formats // This costs money but handles any layout Console.WriteLine("Using AI extraction for complex invoice format"); var aiResult = await _aiParser.ParseInvoiceWithAI(pdfPath); return aiResult; } // Validates that we have the minimum required fields private bool IsComplete(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.InvoiceDate) && data.TotalAmount > 0; } } $vbLabelText $csharpLabel 매입채무 자동화 파이프라인 구축 방법 이 모든 요소를 종합하면, 수신된 송장을 처리하고, 데이터를 추출하고, 유효성을 검사하고, 회계 시스템에 입력할 수 있도록 준비하는 완벽한 자동화 파이프라인이 완성됩니다. using IronPdf; using System; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; // Tracks the outcome of processing each invoice public class ProcessingResult { public string FileName { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string ErrorMessage { get; set; } } // Complete automation pipeline for accounts payable // Watches a folder, extracts data, validates, and routes to accounting system public class InvoiceAutomationPipeline { private readonly SmartInvoiceProcessor _processor; private readonly string _inputFolder; private readonly string _processedFolder; private readonly string _errorFolder; public InvoiceAutomationPipeline(string apiKey, string inputFolder) { _processor = new SmartInvoiceProcessor(apiKey); _inputFolder = inputFolder; _processedFolder = Path.Combine(inputFolder, "processed"); _errorFolder = Path.Combine(inputFolder, "errors"); // Create output directories if they don't exist Directory.CreateDirectory(_processedFolder); Directory.CreateDirectory(_errorFolder); } // Main entry point - processes all PDFs in the input folder public async Task<List<ProcessingResult>> ProcessInvoiceBatch() { string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf"); Console.WriteLine($"Found {invoiceFiles.Length} invoices to process"); var results = new List<ProcessingResult>(); foreach (string invoicePath in invoiceFiles) { string fileName = Path.GetFileName(invoicePath); try { Console.WriteLine($"Processing: {fileName}"); // Extract data using smart processor (patterns first, then AI) var invoiceData = await _processor.ProcessAnyInvoice(invoicePath); // Ensure we have minimum required fields before proceeding if (ValidateInvoiceData(invoiceData)) { // Send to accounting system (QuickBooks, Xero, etc.) await SaveToAccountingSystem(invoiceData); // Archive successful invoices string destPath = Path.Combine(_processedFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }); Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}"); } else { throw new Exception("Validation failed - missing required fields"); } } catch (Exception ex) { Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}"); // Quarantine failed invoices for manual review string destPath = Path.Combine(_errorFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = false, ErrorMessage = ex.Message }); } } GenerateReport(results); return results; } // Checks for minimum required fields private bool ValidateInvoiceData(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.VendorName) && data.TotalAmount > 0; } // Placeholder for accounting system integration private async Task SaveToAccountingSystem(InvoiceData data) { // Integrate with your accounting system here // Examples: QuickBooks API, Xero API, SAP, or database storage Console.WriteLine($" Saved invoice {data.InvoiceNumber} to accounting system"); await Task.CompletedTask; } // Outputs a summary of the batch processing results private void GenerateReport(List<ProcessingResult> results) { int successful = results.Count(r => r.Success); int failed = results.Count(r => !r.Success); Console.WriteLine($"\n========== Processing Complete =========="); Console.WriteLine($"Total Processed: {results.Count}"); Console.WriteLine($"Successful: {successful}"); Console.WriteLine($"Failed: {failed}"); if (failed > 0) { Console.WriteLine("\nFailed invoices requiring review:"); foreach (var failure in results.Where(r => !r.Success)) { Console.WriteLine($" • {failure.FileName}: {failure.ErrorMessage}"); } } } } using IronPdf; using System; using System.IO; using System.Threading.Tasks; using System.Collections.Generic; using System.Linq; // Tracks the outcome of processing each invoice public class ProcessingResult { public string FileName { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string ErrorMessage { get; set; } } // Complete automation pipeline for accounts payable // Watches a folder, extracts data, validates, and routes to accounting system public class InvoiceAutomationPipeline { private readonly SmartInvoiceProcessor _processor; private readonly string _inputFolder; private readonly string _processedFolder; private readonly string _errorFolder; public InvoiceAutomationPipeline(string apiKey, string inputFolder) { _processor = new SmartInvoiceProcessor(apiKey); _inputFolder = inputFolder; _processedFolder = Path.Combine(inputFolder, "processed"); _errorFolder = Path.Combine(inputFolder, "errors"); // Create output directories if they don't exist Directory.CreateDirectory(_processedFolder); Directory.CreateDirectory(_errorFolder); } // Main entry point - processes all PDFs in the input folder public async Task<List<ProcessingResult>> ProcessInvoiceBatch() { string[] invoiceFiles = Directory.GetFiles(_inputFolder, "*.pdf"); Console.WriteLine($"Found {invoiceFiles.Length} invoices to process"); var results = new List<ProcessingResult>(); foreach (string invoicePath in invoiceFiles) { string fileName = Path.GetFileName(invoicePath); try { Console.WriteLine($"Processing: {fileName}"); // Extract data using smart processor (patterns first, then AI) var invoiceData = await _processor.ProcessAnyInvoice(invoicePath); // Ensure we have minimum required fields before proceeding if (ValidateInvoiceData(invoiceData)) { // Send to accounting system (QuickBooks, Xero, etc.) await SaveToAccountingSystem(invoiceData); // Archive successful invoices string destPath = Path.Combine(_processedFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }); Console.WriteLine($"✓ Processed: {invoiceData.InvoiceNumber}"); } else { throw new Exception("Validation failed - missing required fields"); } } catch (Exception ex) { Console.WriteLine($"✗ Failed: {fileName} - {ex.Message}"); // Quarantine failed invoices for manual review string destPath = Path.Combine(_errorFolder, fileName); File.Move(invoicePath, destPath, overwrite: true); results.Add(new ProcessingResult { FileName = fileName, Success = false, ErrorMessage = ex.Message }); } } GenerateReport(results); return results; } // Checks for minimum required fields private bool ValidateInvoiceData(InvoiceData data) { return !string.IsNullOrEmpty(data.InvoiceNumber) && !string.IsNullOrEmpty(data.VendorName) && data.TotalAmount > 0; } // Placeholder for accounting system integration private async Task SaveToAccountingSystem(InvoiceData data) { // Integrate with your accounting system here // Examples: QuickBooks API, Xero API, SAP, or database storage Console.WriteLine($" Saved invoice {data.InvoiceNumber} to accounting system"); await Task.CompletedTask; } // Outputs a summary of the batch processing results private void GenerateReport(List<ProcessingResult> results) { int successful = results.Count(r => r.Success); int failed = results.Count(r => !r.Success); Console.WriteLine($"\n========== Processing Complete =========="); Console.WriteLine($"Total Processed: {results.Count}"); Console.WriteLine($"Successful: {successful}"); Console.WriteLine($"Failed: {failed}"); if (failed > 0) { Console.WriteLine("\nFailed invoices requiring review:"); foreach (var failure in results.Where(r => !r.Success)) { Console.WriteLine($" • {failure.FileName}: {failure.ErrorMessage}"); } } } } $vbLabelText $csharpLabel 이 파이프라인은 완전한 워크플로를 구현합니다. 폴더에서 들어오는 PDF 파일을 스캔하고, 각 파일을 처리하고, 추출된 데이터를 검증하고, 추출에 성공한 파일은 회계 시스템으로 전송하고, 실패한 파일은 수동 검토를 위해 격리합니다. 요약 보고서는 처리 결과를 명확하게 보여줍니다. 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}"); } } $vbLabelText $csharpLabel 각 플랫폼은 고유한 인증 메커니즘(QuickBooks 및 Xero의 경우 OAuth, SAP의 경우 다양한 방법), 필수 입력란 및 API 규칙을 가지고 있습니다. 자세한 내용은 대상 플랫폼의 설명서를 참조하십시오. 하지만 추출된 송장 데이터를 API 페이로드로 변환하는 패턴은 동일합니다. 수백 건의 송장을 일괄 처리하는 방법 대량의 송장 처리에는 동시 처리 및 리소스 관리에 대한 세심한 주의가 필요합니다. 제어된 동시성을 이용한 병렬 처리 패턴은 다음과 같습니다. using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; // Tracks the result of processing a single invoice in a batch public class BatchResult { public string FilePath { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string Error { get; set; } } // High-volume invoice processor with controlled parallelism // Prevents overwhelming APIs while maximizing throughput public class BatchInvoiceProcessor { private readonly SmartInvoiceProcessor _invoiceProcessor; private readonly AccountingSystemIntegration _accountingIntegration; private readonly int _maxConcurrency; public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey, string accountingUrl, int maxConcurrency = 5) { _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey); _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl); _maxConcurrency = maxConcurrency; // Adjust based on API rate limits } // Processes multiple invoices in parallel with controlled concurrency public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths) { // Thread-safe collection for gathering results from parallel tasks var results = new ConcurrentBag<BatchResult>(); // Semaphore limits how many invoices process simultaneously var semaphore = new SemaphoreSlim(_maxConcurrency); // Create a task for each invoice var tasks = invoicePaths.Select(async path => { // Wait for a slot to become available await semaphore.WaitAsync(); try { var result = await ProcessSingleInvoice(path); results.Add(result); } finally { // Release slot for next invoice semaphore.Release(); } }); // Wait for all invoices to complete await Task.WhenAll(tasks); // Output summary statistics var resultList = results.ToList(); int successful = resultList.Count(r => r.Success); int failed = resultList.Count(r => !r.Success); Console.WriteLine($"\nBatch Processing Complete:"); Console.WriteLine($" Total: {resultList.Count}"); Console.WriteLine($" Successful: {successful}"); Console.WriteLine($" Failed: {failed}"); return resultList; } // Processes one invoice: extract data and send to accounting system private async Task<BatchResult> ProcessSingleInvoice(string pdfPath) { try { Console.WriteLine($"Processing: {pdfPath}"); var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath); await _accountingIntegration.SendToQuickBooks(invoiceData); Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}"); return new BatchResult { FilePath = pdfPath, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }; } catch (Exception ex) { Console.WriteLine($"✗ Failed: {pdfPath}"); return new BatchResult { FilePath = pdfPath, Success = false, Error = ex.Message }; } } } using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; // Tracks the result of processing a single invoice in a batch public class BatchResult { public string FilePath { get; set; } public bool Success { get; set; } public string InvoiceNumber { get; set; } public string Error { get; set; } } // High-volume invoice processor with controlled parallelism // Prevents overwhelming APIs while maximizing throughput public class BatchInvoiceProcessor { private readonly SmartInvoiceProcessor _invoiceProcessor; private readonly AccountingSystemIntegration _accountingIntegration; private readonly int _maxConcurrency; public BatchInvoiceProcessor(string aiApiKey, string accountingApiKey, string accountingUrl, int maxConcurrency = 5) { _invoiceProcessor = new SmartInvoiceProcessor(aiApiKey); _accountingIntegration = new AccountingSystemIntegration(accountingApiKey, accountingUrl); _maxConcurrency = maxConcurrency; // Adjust based on API rate limits } // Processes multiple invoices in parallel with controlled concurrency public async Task<List<BatchResult>> ProcessInvoiceBatch(List<string> invoicePaths) { // Thread-safe collection for gathering results from parallel tasks var results = new ConcurrentBag<BatchResult>(); // Semaphore limits how many invoices process simultaneously var semaphore = new SemaphoreSlim(_maxConcurrency); // Create a task for each invoice var tasks = invoicePaths.Select(async path => { // Wait for a slot to become available await semaphore.WaitAsync(); try { var result = await ProcessSingleInvoice(path); results.Add(result); } finally { // Release slot for next invoice semaphore.Release(); } }); // Wait for all invoices to complete await Task.WhenAll(tasks); // Output summary statistics var resultList = results.ToList(); int successful = resultList.Count(r => r.Success); int failed = resultList.Count(r => !r.Success); Console.WriteLine($"\nBatch Processing Complete:"); Console.WriteLine($" Total: {resultList.Count}"); Console.WriteLine($" Successful: {successful}"); Console.WriteLine($" Failed: {failed}"); return resultList; } // Processes one invoice: extract data and send to accounting system private async Task<BatchResult> ProcessSingleInvoice(string pdfPath) { try { Console.WriteLine($"Processing: {pdfPath}"); var invoiceData = await _invoiceProcessor.ProcessAnyInvoice(pdfPath); await _accountingIntegration.SendToQuickBooks(invoiceData); Console.WriteLine($"✓ Completed: {invoiceData.InvoiceNumber}"); return new BatchResult { FilePath = pdfPath, Success = true, InvoiceNumber = invoiceData.InvoiceNumber }; } catch (Exception ex) { Console.WriteLine($"✗ Failed: {pdfPath}"); return new BatchResult { FilePath = pdfPath, Success = false, Error = ex.Message }; } } } $vbLabelText $csharpLabel SemaphoreSlim는 외부 API에 과부하가 걸리거나 시스템 리소스가 고갈되는 것을 방지합니다. API 사용량 제한 및 서버 용량에 따라 _maxConcurrency 값을 조정하세요. ConcurrentBag는 병렬 작업의 결과를 안전하게 수집합니다. 다음 단계 송장 자동화는 수작업을 줄이고, 오류를 최소화하며, 비즈니스 프로세스를 가속화할 수 있는 중요한 기회를 제공합니다. 이 가이드에서는 HTML 템플릿을 사용하여 전문적인 송장을 생성하는 것부터 ZUGFeRD 및 Factur-X 전자 송장 표준을 준수하고, 패턴 매칭 및 AI 기반 처리를 통해 수신된 송장에서 데이터를 추출하고 , 확장 가능한 자동화 파이프라인을 구축하는 것까지 전체 라이프사이클을 안내했습니다. IronPDF는 이러한 기능의 기반이 되며, 강력한 HTML-PDF 변환, 안정적인 텍스트 추출 , 그리고 PDF/A-3 전자 송장 규정 준수 에 필요한 첨부 파일 기능을 제공합니다. 크롬 기반 렌더링 엔진을 통해 청구서가 디자인된 그대로 표시되도록 보장하며, 추출 방식은 PDF 텍스트 인코딩의 복잡성을 자동으로 처리합니다. 여기에 제시된 패턴은 시작점일 뿐입니다. 실제 구현 시에는 특정 송장 형식, 회계 시스템 및 비즈니스 규칙에 맞게 조정해야 합니다. 대용량 처리 시나리오의 경우, 배치 처리 튜토리얼에서는 제어된 동시성 및 오류 복구를 통한 병렬 실행에 대해 다룹니다. 건축을 시작할 준비가 되셨나요? IronPDF를 다운로드 하고 무료 체험판을 사용해 보세요. 이 라이브러리에는 무료 개발 라이선스가 포함되어 있으므로, 정식 라이선스를 구매하기 전에 송장 생성, 데이터 추출 및 PDF 보고서 기능을 충분히 평가해 볼 수 있습니다. 송장 자동화 또는 회계 시스템 통합에 대해 궁금한 점이 있으면 엔지니어링 지원팀에 문의하십시오 . 자주 묻는 질문 C# 송장 처리에서 IronPDF는 무엇에 사용되나요? IronPDF는 C# 송장 처리에서 전문적인 PDF 송장을 생성하고, 구조화된 데이터를 추출하고, 송장 워크플로를 자동화하는 동시에 ZUGFeRD 및 Factur-X와 같은 표준을 준수하도록 지원합니다. C#에서 IronPDF를 사용하여 PDF 송장을 생성하는 방법은 무엇인가요? IronPDF를 C#에서 사용하면 API를 활용하여 PDF 문서를 프로그래밍 방식으로 생성하고 사용자 지정함으로써 PDF 송장을 생성할 수 있습니다. 여기에는 송장을 구성하는 텍스트, 표, 이미지 등의 요소를 추가하는 작업이 포함됩니다. ZUGFeRD와 Factur-X는 무엇이며, IronPDF는 어떻게 이를 지원합니까? ZUGFeRD와 Factur-X는 전자 송장 표준으로, 송장이 사람이 읽기 쉬울 뿐만 아니라 기계도 읽을 수 있도록 보장합니다. IronPDF는 이러한 표준을 준수하는 PDF 송장을 생성할 수 있도록 지원하여 해당 표준을 준수하는 PDF 송장을 생성할 수 있도록 합니다. IronPDF는 어떻게 회계 부서의 지급 업무 자동화를 지원할 수 있을까요? IronPDF는 송장에서 구조화된 데이터를 추출하고 자동화 파이프라인과 통합하여 지급 계정 프로세스를 자동화함으로써 수동 데이터 입력을 줄이고 효율성을 향상시킬 수 있습니다. IronPDF는 기존 PDF 송장에서 데이터를 추출할 수 있습니까? 네, IronPDF는 기존 PDF 송장에서 구조화된 데이터를 추출하여 송장 정보를 자동으로 처리하고 분석하는 작업을 더욱 쉽게 만들어 줍니다. C#에서 IronPDF를 사용하여 송장을 처리할 때의 이점은 무엇입니까? C#에서 IronPDF를 사용하여 송장을 처리하면 송장 생성이 간소화되고, 국제 송장 표준을 준수하며, 데이터 추출이 효율적이고, 자동화 기능이 향상되는 등의 이점을 누릴 수 있습니다. IronPDF를 사용하여 PDF 송장의 모양을 사용자 지정할 수 있습니까? 네, IronPDF를 사용하면 로고, 텍스트 서식, 레이아웃 조정 등 다양한 디자인 요소를 추가하여 브랜드 요구 사항에 맞춰 PDF 송장의 모양을 맞춤 설정할 수 있습니다. IronPDF를 사용하여 송장 처리를 자동화하는 일반적인 단계는 무엇입니까? IronPDF를 사용하여 송장 처리를 자동화하려면 일반적으로 송장을 생성하고 필요한 데이터를 추출한 다음 다른 시스템이나 자동화 도구와 통합하여 워크플로를 간소화합니다. IronPDF는 다양한 송장 형식을 어떻게 처리하나요? IronPDF는 PDF 문서를 생성, 조작 및 읽는 도구를 제공하여 다양한 송장 형식을 처리할 수 있으며, 일반적인 전자 송장 표준과의 호환성을 보장합니다. 커티스 차우 지금 바로 엔지니어링 팀과 채팅하세요 기술 문서 작성자 커티스 차우는 칼턴 대학교에서 컴퓨터 과학 학사 학위를 취득했으며, Node.js, TypeScript, JavaScript, React를 전문으로 하는 프론트엔드 개발자입니다. 직관적이고 미적으로 뛰어난 사용자 인터페이스를 만드는 데 열정을 가진 그는 최신 프레임워크를 활용하고, 잘 구성되고 시각적으로 매력적인 매뉴얼을 제작하는 것을 즐깁니다. 커티스는 개발 분야 외에도 사물 인터넷(IoT)에 깊은 관심을 가지고 있으며, 하드웨어와 소프트웨어를 통합하는 혁신적인 방법을 연구합니다. 여가 시간에는 게임을 즐기거나 디스코드 봇을 만들면서 기술에 대한 애정과 창의성을 결합합니다. 시작할 준비 되셨나요? Nuget 다운로드 17,527,568 | 버전: 2026.2 방금 출시되었습니다 NuGet 무료 다운로드 총 다운로드 수: 17,527,568 라이선스 보기