Create Factur-X, ZUGFeRD and XRechnung Electronic Invoices

HiQPdf HTML to PDF Converter creates electronic invoices in the Factur-X and ZUGFeRD formats (including the XRechnung profile) and electronic orders in the Order-X format. An electronic invoice is a PDF/A-3 document that shows the invoice as a page a person reads and carries the same invoice as an XML file that accounting software reads automatically. Factur-X and ZUGFeRD are the French and the German name of the same format.

The invoice XML is loaded in an HiQPdf.NextElectronicInvoice object with ElectronicInvoiceFromXml(Byte), ElectronicInvoiceFromXml(String) or ElectronicInvoiceFromFile(String). The profile, the document type and the invoice number are taken from the XML and exposed by the Profile, DocumentType and InvoiceNumber properties. The invoice is then added to the generated document with PdfDocumentControlAddElectronicInvoice(ElectronicInvoice). The same method is available on PdfDocumentAddElectronicInvoice(ElectronicInvoice) for documents created from scratch and on PdfEditorAddElectronicInvoice(ElectronicInvoice) for existing PDF/A-3 documents. The produced document embeds the invoice XML and carries the invoice identification that the receiving software looks for.

Supported profiles

Invoices in the UBL syntax and ZUGFeRD 1.0 invoices are not supported. The XML is embedded as given.

Requirements

Accessible invoices. With the PdfStandardPdfUa1PdfA3a standard the invoice page is also a PDF/UA-1 document, readable with screen readers and assistive technologies, as required for the invoices delivered to consumers under the European Accessibility Act. The HTML of the invoice should use headings, tables with header cells and alternate text for images.

Setting PdfDocumentControlViewer.PageMode to PdfPageModeAttachments opens the Attachments panel when the document is loaded, so the embedded invoice XML is visible to the reader.

Code Sample - Create a Factur-X / ZUGFeRD Electronic Invoice

C#
using System;
using System.IO;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using HiQPdf_Next_AspNetDemo.Models;

using HiQPdf.Next;

namespace HiQPdf_Next_AspNetDemo.Controllers
{
    public class CreateElectronicInvoicePdfController : Controller
    {
        private readonly IWebHostEnvironment m_hostingEnvironment;
        public CreateElectronicInvoicePdfController(IWebHostEnvironment hostingEnvironment)
        {
            m_hostingEnvironment = hostingEnvironment;
        }

        public IActionResult Index()
        {
            var model = SetViewModel();
            return View(model);
        }

        [HttpPost]
        public async Task<ActionResult> ConvertToPdf(CreateElectronicInvoicePdfViewModel model)
        {
            if (!ModelState.IsValid)
            {
                var errorMessage = ModelStateHelper.GetModelErrors(ModelState);
                throw new ValidationException(errorMessage);
            }

            // Set the serial number received after purchase to use the library in licensed mode; leave it commented for demo mode
            // Licensing.SerialNumber = "your-serial-number";

            // The invoice XML: the uploaded file when there is one, otherwise the selected sample
            byte[] invoiceXml = await ReadInvoiceXml(model);

            // Read the invoice. The profile and the document type (invoice, credit note, order...) are
            // taken from the XML itself
            ElectronicInvoice invoice = ElectronicInvoice.FromXml(invoiceXml);

            // How the embedded XML relates to the visible page. MINIMUM and BASIC WL do not carry
            // the whole invoice, so for them the relationship is always Data
            invoice.Relationship = model.Relationship;

            // Create an HTML to PDF converter object with default settings
            HtmlToPdf htmlToPdfConverter = new HtmlToPdf();

            // An electronic invoice needs a PDF/A-3 carrier. PDF/UA-1 + PDF/A-3a produces an
            // invoice that is also accessible
            htmlToPdfConverter.Document.PdfStandard = model.PdfStandard;

            // Embed the invoice XML and write the metadata invoice software looks for
            htmlToPdfConverter.Document.AddElectronicInvoice(invoice);

            // Open the Attachments panel on document load, so the embedded XML is visible
            htmlToPdfConverter.Document.Viewer.PageMode = PdfPageMode.Attachments;

            // convert HTML code to a PDF memory buffer
            byte[] pdfBuffer = htmlToPdfConverter.ConvertHtmlToMemory(model.HtmlCode, model.BaseUrl);

            FileResult fileResult = new FileContentResult(pdfBuffer, "application/pdf");
            fileResult.FileDownloadName = string.Format("{0}_{1}_{2}.pdf", invoice.DocumentType, invoice.InvoiceNumber, invoice.Profile);

            return fileResult;
        }

        private async Task<byte[]> ReadInvoiceXml(CreateElectronicInvoicePdfViewModel model)
        {
            if (model.InvoiceXmlFile != null && model.InvoiceXmlFile.Length > 0)
            {
                using var ms = new MemoryStream();
                await model.InvoiceXmlFile.CopyToAsync(ms);
                return ms.ToArray();
            }

            // Only file names from the samples folder are accepted
            string fileName = Path.GetFileName(model.SampleInvoice ?? string.Empty);
            string filePath = Path.Combine(GetInvoiceFilesPath(), fileName);
            if (fileName.Length == 0 || !System.IO.File.Exists(filePath))
                throw new Exception("No invoice XML provided: upload a file or select a sample");

            return await System.IO.File.ReadAllBytesAsync(filePath);
        }

        private CreateElectronicInvoicePdfViewModel SetViewModel()
        {
            var model = new CreateElectronicInvoicePdfViewModel();

            var contentRootPath = Path.Combine(m_hostingEnvironment.ContentRootPath, "wwwroot");

            HttpRequest request = ControllerContext.HttpContext.Request;
            UriBuilder uriBuilder = new UriBuilder();
            uriBuilder.Scheme = request.Scheme;
            uriBuilder.Host = request.Host.Host;
            if (request.Host.Port != null)
                uriBuilder.Port = (int)request.Host.Port;
            uriBuilder.Path = request.PathBase.ToString() + request.Path.ToString();
            uriBuilder.Query = request.QueryString.ToString();

            string currentPageUrl = uriBuilder.Uri.AbsoluteUri;
            string rootUrl = currentPageUrl.Substring(
                0, currentPageUrl.Length - "CreateElectronicInvoicePdf".Length);

            model.HtmlCode = System.IO.File.ReadAllText(Path.Combine(contentRootPath, "DemoFiles/Html/Electronic_Invoice.html"));
            model.BaseUrl = rootUrl + "DemoFiles/Html/";

            return model;
        }

        private string GetDemoFilesPath() => m_hostingEnvironment.ContentRootPath + "/wwwroot" + "/DemoFiles/";
        private string GetInvoiceFilesPath() => Path.Combine(GetDemoFilesPath(), "Invoices");
    }
}

See Also