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-0001Northwind Traders2026-03-012026-03-31$1,955.00Pending
INV-2026-0002BlueWave Foods2026-03-012026-03-31$860.00Pending
INV-2026-0003CityMed Supplies2026-03-012026-03-31$2,415.00Pending

Sample Invoice Items

Invoice Number Description Qty Unit Price Line Total
INV-2026-0001Monthly Hosting1$450.00$450.00
INV-2026-0001Support Retainer10$120.00$1,200.00
INV-2026-0001VAT1$305.00$305.00
INV-2026-0002Consulting Hours8$95.00$760.00
INV-2026-0002VAT1$100.00$100.00
INV-2026-0003Software License Renewal3$650.00$1,950.00
INV-2026-0003VAT1$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

TAX INVOICE
Invoice No: INV-2026-0001
Invoice Date: 01 Mar 2026
Due Date: 31 Mar 2026
Bill To

Northwind Traders
12 King Street
London

Description Qty Unit Price Total
Monthly Hosting1$450.00$450.00
Support Retainer10$120.00$1,200.00
VAT1$305.00$305.00
Sub Total$1,650.00
Tax$305.00
Total$1,955.00

Step 9: Batch Printing Strategy

If your requirement is not only generating PDFs but also printing them in batches, the common workflow is:

  1. query all invoices that need printing
  2. generate each invoice to a PDF file using `ExportToPdf`
  3. store the PDF path back in the database
  4. send the PDFs to a print queue or let finance print the whole folder
  5. 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

.NET Core ASP.NET MVC C# SQL Server Azure Entity Framework

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.