Introduction
Automating invoices in C# usually means taking invoice data from a database, building a printable layout, and generating PDF files in bulk without manually opening and printing each invoice one by one. This is useful for finance teams, subscription businesses, logistics companies, service providers, and any application that has to print invoices every day, every week, or at month-end.
In this article, I will walk through the process step by step: starting with the invoice data table design, adding sample entries, then moving into the C# batch generation flow, and finally generating PDF invoices with DevExpress XtraReport.
Step 1: Design the Invoice Data Tables
A clean database design makes invoice automation much easier. At minimum, you usually need three tables:
- Customers: stores who the invoice is for
- Invoices: stores the invoice header information such as number, date, totals, and status
- InvoiceItems: stores line items for each invoice
Sample SQL Table Design
CREATE TABLE Customers (
CustomerId INT PRIMARY KEY IDENTITY(1,1),
CustomerName NVARCHAR(150) NOT NULL,
EmailAddress NVARCHAR(200) NULL,
BillingAddress NVARCHAR(300) NULL,
VatNumber NVARCHAR(50) NULL
);
CREATE TABLE Invoices (
InvoiceId INT PRIMARY KEY IDENTITY(1,1),
CustomerId INT NOT NULL,
InvoiceNumber NVARCHAR(30) NOT NULL,
InvoiceDate DATE NOT NULL,
DueDate DATE NOT NULL,
SubTotal DECIMAL(18,2) NOT NULL,
TaxAmount DECIMAL(18,2) NOT NULL,
TotalAmount DECIMAL(18,2) NOT NULL,
CurrencyCode NVARCHAR(10) NOT NULL,
InvoiceStatus NVARCHAR(20) NOT NULL,
PdfGenerated BIT NOT NULL DEFAULT 0,
PdfPath NVARCHAR(300) NULL,
FOREIGN KEY (CustomerId) REFERENCES Customers(CustomerId)
);
CREATE TABLE InvoiceItems (
InvoiceItemId INT PRIMARY KEY IDENTITY(1,1),
InvoiceId INT NOT NULL,
Description NVARCHAR(200) NOT NULL,
Quantity DECIMAL(18,2) NOT NULL,
UnitPrice DECIMAL(18,2) NOT NULL,
LineTotal DECIMAL(18,2) NOT NULL,
FOREIGN KEY (InvoiceId) REFERENCES Invoices(InvoiceId)
);
This structure is enough to support batch printing, invoice history, and reprints later.
Step 2: Insert Sample Invoice Data
Before building the report, it helps to work with sample invoice data.
| Invoice Number | Customer | Invoice Date | Due Date | Total | Status |
|---|---|---|---|---|---|
| INV-2026-0001 | Northwind Traders | 2026-03-01 | 2026-03-31 | $1,955.00 | Pending |
| INV-2026-0002 | BlueWave Foods | 2026-03-01 | 2026-03-31 | $860.00 | Pending |
| INV-2026-0003 | CityMed Supplies | 2026-03-01 | 2026-03-31 | $2,415.00 | Pending |
Sample Invoice Items
| Invoice Number | Description | Qty | Unit Price | Line Total |
|---|---|---|---|---|
| INV-2026-0001 | Monthly Hosting | 1 | $450.00 | $450.00 |
| INV-2026-0001 | Support Retainer | 10 | $120.00 | $1,200.00 |
| INV-2026-0001 | VAT | 1 | $305.00 | $305.00 |
| INV-2026-0002 | Consulting Hours | 8 | $95.00 | $760.00 |
| INV-2026-0002 | VAT | 1 | $100.00 | $100.00 |
| INV-2026-0003 | Software License Renewal | 3 | $650.00 | $1,950.00 |
| INV-2026-0003 | VAT | 1 | $465.00 | $465.00 |
Sample Insert Script
INSERT INTO Customers (CustomerName, EmailAddress, BillingAddress, VatNumber)
VALUES
('Northwind Traders', 'accounts@northwind.example', '12 King Street, London', 'GB123456'),
('BlueWave Foods', 'finance@bluewave.example', '88 Market Road, Cape Town', 'ZA998877'),
('CityMed Supplies', 'billing@citymed.example', '44 Main Avenue, Toronto', 'CA554433');
INSERT INTO Invoices (CustomerId, InvoiceNumber, InvoiceDate, DueDate, SubTotal, TaxAmount, TotalAmount, CurrencyCode, InvoiceStatus)
VALUES
(1, 'INV-2026-0001', '2026-03-01', '2026-03-31', 1650.00, 305.00, 1955.00, 'USD', 'Pending'),
(2, 'INV-2026-0002', '2026-03-01', '2026-03-31', 760.00, 100.00, 860.00, 'USD', 'Pending'),
(3, 'INV-2026-0003', '2026-03-01', '2026-03-31', 1950.00, 465.00, 2415.00, 'USD', 'Pending');
Step 3: Create C# Models
In your C# project, the invoice data is easier to work with if you have clear models for invoice headers and line items.
public class InvoiceModel
{
public int InvoiceId { get; set; }
public string InvoiceNumber { get; set; }
public string CustomerName { get; set; }
public string EmailAddress { get; set; }
public string BillingAddress { get; set; }
public DateTime InvoiceDate { get; set; }
public DateTime DueDate { get; set; }
public decimal SubTotal { get; set; }
public decimal TaxAmount { get; set; }
public decimal TotalAmount { get; set; }
public string CurrencyCode { get; set; }
public List<InvoiceItemModel> Items { get; set; }
}
public class InvoiceItemModel
{
public string Description { get; set; }
public decimal Quantity { get; set; }
public decimal UnitPrice { get; set; }
public decimal LineTotal { get; set; }
}
Step 4: Load Invoices That Need PDF Generation
The batch process usually starts by querying invoices where `PdfGenerated = 0` or where a reprint is requested.
public List<InvoiceModel> GetInvoicesForBatch()
{
using (var connection = new SqlConnection(connectionString))
{
// Load only invoices that still need PDFs.
var sql = @"
SELECT i.InvoiceId, i.InvoiceNumber, i.InvoiceDate, i.DueDate,
i.SubTotal, i.TaxAmount, i.TotalAmount, i.CurrencyCode,
c.CustomerName, c.BillingAddress
FROM Invoices i
INNER JOIN Customers c ON c.CustomerId = i.CustomerId
WHERE i.PdfGenerated = 0
ORDER BY i.InvoiceId";
// Map query results to InvoiceModel objects here.
}
return new List<InvoiceModel>();
}
In a real application, you would also load the invoice items for each invoice and attach them to the `Items` list.
Step 5: Design the Invoice Report in DevExpress XtraReport
If you have a DevExpress license, `XtraReport` is a strong choice for invoice automation because it gives you a professional layout engine, grouping, summaries, PDF export, and easy re-use across many invoices.
Basic XtraReport Setup
public class InvoiceXtraReport : DevExpress.XtraReports.UI.XtraReport
{
public InvoiceXtraReport()
{
Bands.Add(new TopMarginBand());
Bands.Add(new BottomMarginBand());
Bands.Add(new ReportHeaderBand());
Bands.Add(new DetailBand());
Bands.Add(new ReportFooterBand());
var title = new XRLabel
{
Text = "TAX INVOICE",
Font = new System.Drawing.Font("Arial", 18, System.Drawing.FontStyle.Bold),
WidthF = 300
};
ReportHeader.Controls.Add(title);
}
}
Normally, the full report would include:
- company logo and company details
- invoice number, invoice date, due date
- customer name and billing address
- a detail table for line items
- sub-total, tax, and total
- payment instructions or bank details
Binding the Report to Invoice Data
public InvoiceXtraReport BuildInvoiceReport(InvoiceModel invoice)
{
var report = new InvoiceXtraReport();
report.DataSource = invoice.Items;
// You can also assign label values directly.
// Example:
// xrInvoiceNumber.Text = invoice.InvoiceNumber;
// xrCustomerName.Text = invoice.CustomerName;
// xrTotalAmount.Text = invoice.TotalAmount.ToString("C");
return report;
}
If you want a concrete starting point, I also added sample C# assets to this project: InvoiceXtraReportSample.cs and InvoiceBatchEmailServiceSample.cs. These are sample files you can adapt inside a DevExpress-enabled project.
Step 6: Export PDF Invoices in Batches
Once the invoice data is loaded and the report template is ready, the batch process loops through each invoice and exports a PDF file.
public void GenerateInvoiceBatch(List<InvoiceModel> invoices, string outputFolder)
{
foreach (var invoice in invoices)
{
var report = BuildInvoiceReport(invoice);
var fileName = invoice.InvoiceNumber + ".pdf";
var fullPath = Path.Combine(outputFolder, fileName);
report.ExportToPdf(fullPath);
MarkInvoiceAsGenerated(invoice.InvoiceId, fullPath);
}
}
Update the Database After PDF Generation
public void MarkInvoiceAsGenerated(int invoiceId, string pdfPath)
{
using (var connection = new SqlConnection(connectionString))
using (var command = connection.CreateCommand())
{
command.CommandText = @"
UPDATE Invoices
SET PdfGenerated = 1,
PdfPath = @PdfPath
WHERE InvoiceId = @InvoiceId";
command.Parameters.AddWithValue("@PdfPath", pdfPath);
command.Parameters.AddWithValue("@InvoiceId", invoiceId);
connection.Open();
command.ExecuteNonQuery();
}
}
Step 7: Schedule the Batch Process
There are several ways to automate the batch run:
- Windows Task Scheduler: run a console app every night or month-end
- Hangfire: schedule recurring invoice jobs inside your ASP.NET application
- Windows Service: watch for new invoices continuously
- Manual trigger in admin UI: a finance user clicks “Generate Pending Invoices”
A common production pattern is to create pending invoices during the day, then generate the PDFs in one nightly batch.
Step 8: Email the Generated PDFs Automatically
In many finance workflows, PDF generation is only the first half. The next step is to email each invoice to the correct customer after the PDF is generated successfully.
public void GenerateAndEmailInvoices(List<InvoiceModel> invoices, string outputFolder)
{
var emailQueue = new List<InvoiceEmailMessage>();
foreach (var invoice in invoices)
{
var report = BuildInvoiceReport(invoice);
var pdfPath = Path.Combine(outputFolder, invoice.InvoiceNumber + ".pdf");
report.ExportToPdf(pdfPath);
MarkInvoiceAsGenerated(invoice.InvoiceId, pdfPath);
emailQueue.Add(new InvoiceEmailMessage
{
InvoiceNumber = invoice.InvoiceNumber,
CustomerName = invoice.CustomerName,
ToEmailAddress = invoice.EmailAddress,
PdfPath = pdfPath
});
}
var emailService = new InvoiceBatchEmailServiceSample();
emailService.EmailInvoices(emailQueue);
}
This is usually safer than emailing inside the same loop without queueing because it gives you a clear post-generation email stage and makes retries easier.
Sample Batch Output
The visual below shows the kind of invoice PDF layout you can generate with DevExpress XtraReport. It is a browser mockup of the final PDF style.
YOUR COMPANY LTD
123 Business Street
Johannesburg, South Africa
accounts@yourcompany.com
Northwind Traders
12 King Street
London
| Description | Qty | Unit Price | Total |
|---|---|---|---|
| Monthly Hosting | 1 | $450.00 | $450.00 |
| Support Retainer | 10 | $120.00 | $1,200.00 |
| VAT | 1 | $305.00 | $305.00 |
Step 9: Batch Printing Strategy
If your requirement is not only generating PDFs but also printing them in batches, the common workflow is:
- query all invoices that need printing
- generate each invoice to a PDF file using `ExportToPdf`
- store the PDF path back in the database
- send the PDFs to a print queue or let finance print the whole folder
- mark the invoices as printed after successful completion
This is much more reliable than rendering each invoice directly to a printer in a loop because the PDF files become your audit trail and reprint source.
Recommended Folder Structure
D:\Invoices\
2026\
03\
INV-2026-0001.pdf
INV-2026-0002.pdf
INV-2026-0003.pdf
This makes it easier to archive invoices by year and month.
Common Production Features
- Email after generation: attach the PDF invoice and send it automatically to the customer
- Retry queue: re-run failed invoices without regenerating successful ones
- Branding: customer-specific logos or company branches
- Multiple templates: tax invoice, pro-forma invoice, credit note
- Audit tracking: store who generated, exported, emailed, or printed the invoice
Conclusion
Automating invoices in C# is mostly about combining three things properly: a good invoice table design, a reliable batch process, and a professional PDF layout engine. Once those are in place, you can generate invoices in bulk, keep a proper archive, and reduce manual finance work dramatically.
If you already have DevExpress, `XtraReport` is a practical choice for this because it handles invoice formatting and PDF export very well. The general flow is straightforward: store invoice data, load pending invoices, build the report, export PDFs in a loop, and update the database after each successful export.
If you want to extend the same approach beyond invoices, the next logical step is to generate credit notes and customer statements using the same reporting pipeline. I added a follow-up article for that here: How to Generate Credit Notes and Customer Statements in C#.
Technologies I Use
Need Help Building Invoice Automation?
I build custom C# invoice automation systems, DevExpress report generation workflows, batch PDF generation tools, and back-office document automation solutions. If you need this implemented in your application, people can contact me using the details on this page.