XRechnung is an XML-only format specifically created for processing digital invoices. ZUGFeRD, on the other hand, integrates a human-readable PDF format with a machine-readable XML format. Since the release of ZUGFeRD version 2.1, it is possible to combine the XML output from XRechnung with the method of embedding XML into a PDF file.
Generating ZUGFeRD Invoices
TX Text Control can be used to create ZUGFeRD documents by generating a PDF/A-3b document and attaching the XML content.
Learn More
This article shows how to create valid XRechnung and ZUGFeRD invoices with ASP.NET Core C#. The invoice is created with the help MailMerge component and the ZUGFeRD-csharp library.
Creating Valid XRechnung / ZUGFeRD Invoices with ASP.NET Core C#
The above article explains how to generate the XML that is required and how to attach the XML to a PDF document in order to generate a valid electronic invoice (e-invoice).
But what if you get the valid XML from your ERP system or any other billing system?
In this case, TX Text Control can be used to generate a human-readable, perfectly formatted invoice based on the valid XML data used as the data source for Mail
╰ DocumentServer Namespace
╰ MailMerge Class
The MailMerge class is a .NET component that can be used to effortlessly merge template documents with database content in .NET projects, such as ASP.NET web applications, web services or Windows services. .
MailMerge with XML Data Source
MailMerge takes a template with placeholder merge fields and repeating merge blocks and populates them with data from data sources such as JSON or XML. This example shows how to use the XRechnung / ZUGFeRD XML as a data source to create a PDF invoice.
Consider the following sample XRechnung 3.0 XML:
<?xml version="1.0" encoding="utf-8"?> | |
<rsm:CrossIndustryInvoice xmlns:a="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:rsm="urn:un:unece:uncefact:data:standard:CrossIndustryInvoice:100" xmlns:qdt="urn:un:unece:uncefact:data:standard:QualifiedDataType:100" xmlns:ram="urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:udt="urn:un:unece:uncefact:data:standard:UnqualifiedDataType:100"> | |
<rsm:ExchangedDocumentContext> | |
<ram:GuidelineSpecifiedDocumentContextParameter> | |
<ram:ID>urn:cen.eu:en16931:2017#compliant#urn:xeinkauf.de:kosit:xrechnung_3.0</ram:ID> | |
</ram:GuidelineSpecifiedDocumentContextParameter> | |
</rsm:ExchangedDocumentContext> | |
<rsm:ExchangedDocument> | |
<ram:ID>471102</ram:ID> | |
<ram:TypeCode>380</ram:TypeCode> | |
<ram:IssueDateTime> | |
<udt:DateTimeString format="102">20130605</udt:DateTimeString> | |
</ram:IssueDateTime> | |
</rsm:ExchangedDocument> | |
<rsm:SupplyChainTradeTransaction> | |
<ram:IncludedSupplyChainTradeLineItem> | |
<ram:AssociatedDocumentLineDocument> | |
<ram:LineID>1</ram:LineID> | |
</ram:AssociatedDocumentLineDocument> | |
<ram:SpecifiedTradeProduct> | |
<ram:Name>Item name</ram:Name> | |
<ram:Description>Detail description</ram:Description> | |
</ram:SpecifiedTradeProduct> | |
<ram:SpecifiedLineTradeAgreement> | |
<ram:GrossPriceProductTradePrice> | |
<ram:ChargeAmount>11.9000</ram:ChargeAmount> | |
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity> | |
</ram:GrossPriceProductTradePrice> | |
<ram:NetPriceProductTradePrice> | |
<ram:ChargeAmount>10.0000</ram:ChargeAmount> | |
<ram:BasisQuantity unitCode="H87">1.0000</ram:BasisQuantity> | |
</ram:NetPriceProductTradePrice> | |
</ram:SpecifiedLineTradeAgreement> | |
<ram:SpecifiedLineTradeDelivery> | |
<ram:BilledQuantity unitCode="H87">1.0000</ram:BilledQuantity> | |
</ram:SpecifiedLineTradeDelivery> | |
<ram:SpecifiedLineTradeSettlement> | |
<ram:ApplicableTradeTax> | |
<ram:TypeCode>VAT</ram:TypeCode> | |
<ram:CategoryCode>S</ram:CategoryCode> | |
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent> | |
</ram:ApplicableTradeTax> | |
<ram:SpecifiedTradeSettlementLineMonetarySummation> | |
<ram:LineTotalAmount>10.00</ram:LineTotalAmount> | |
</ram:SpecifiedTradeSettlementLineMonetarySummation> | |
</ram:SpecifiedLineTradeSettlement> | |
</ram:IncludedSupplyChainTradeLineItem> | |
<ram:ApplicableHeaderTradeAgreement> | |
<ram:BuyerReference>AB-312</ram:BuyerReference> | |
<ram:SellerTradeParty> | |
<ram:ID>+4942142706710</ram:ID> | |
<ram:Name>Lieferant GmbH</ram:Name> | |
<ram:DefinedTradeContact> | |
<ram:PersonName>Max Mustermann</ram:PersonName> | |
<ram:TelephoneUniversalCommunication> | |
<ram:CompleteNumber>+4942142706710</ram:CompleteNumber> | |
</ram:TelephoneUniversalCommunication> | |
<ram:EmailURIUniversalCommunication> | |
<ram:URIID>max@mustermann.de</ram:URIID> | |
</ram:EmailURIUniversalCommunication> | |
</ram:DefinedTradeContact> | |
<ram:PostalTradeAddress> | |
<ram:PostcodeCode>80333</ram:PostcodeCode> | |
<ram:LineOne>Lieferantenstra??e</ram:LineOne> | |
<ram:CityName>M??nchen</ram:CityName> | |
<ram:CountryID>DE</ram:CountryID> | |
</ram:PostalTradeAddress> | |
<ram:SpecifiedTaxRegistration> | |
<ram:ID schemeID="VA">DE123456789</ram:ID> | |
</ram:SpecifiedTaxRegistration> | |
</ram:SellerTradeParty> | |
<ram:BuyerTradeParty> | |
<ram:ID>22342424</ram:ID> | |
<ram:Name>Kunden Mitte AG</ram:Name> | |
<ram:DefinedTradeContact> | |
<ram:PersonName>Hans Muster</ram:PersonName> | |
</ram:DefinedTradeContact> | |
<ram:PostalTradeAddress> | |
<ram:PostcodeCode>69876</ram:PostcodeCode> | |
<ram:LineOne>Kundenstra??e</ram:LineOne> | |
<ram:CityName>Frankfurt</ram:CityName> | |
<ram:CountryID>DE</ram:CountryID> | |
</ram:PostalTradeAddress> | |
<ram:SpecifiedTaxRegistration> | |
<ram:ID schemeID="VA">DE234567890</ram:ID> | |
</ram:SpecifiedTaxRegistration> | |
</ram:BuyerTradeParty> | |
<ram:BuyerOrderReferencedDocument> | |
<ram:IssuerAssignedID>2013-471102</ram:IssuerAssignedID> | |
</ram:BuyerOrderReferencedDocument> | |
</ram:ApplicableHeaderTradeAgreement> | |
<ram:ApplicableHeaderTradeDelivery> | |
<ram:ActualDeliverySupplyChainEvent> | |
<ram:OccurrenceDateTime> | |
<udt:DateTimeString format="102">20130603</udt:DateTimeString> | |
</ram:OccurrenceDateTime> | |
</ram:ActualDeliverySupplyChainEvent> | |
</ram:ApplicableHeaderTradeDelivery> | |
<ram:ApplicableHeaderTradeSettlement> | |
<ram:InvoiceCurrencyCode>EUR</ram:InvoiceCurrencyCode> | |
<ram:SpecifiedTradeSettlementPaymentMeans> | |
<ram:TypeCode>97</ram:TypeCode> | |
<ram:Information /> | |
</ram:SpecifiedTradeSettlementPaymentMeans> | |
<ram:ApplicableTradeTax> | |
<ram:CalculatedAmount>1.90</ram:CalculatedAmount> | |
<ram:TypeCode>VAT</ram:TypeCode> | |
<ram:BasisAmount>10.00</ram:BasisAmount> | |
<ram:CategoryCode>S</ram:CategoryCode> | |
<ram:RateApplicablePercent>19.00</ram:RateApplicablePercent> | |
</ram:ApplicableTradeTax> | |
<ram:SpecifiedTradePaymentTerms> | |
<ram:Description>Zahlbar innerhalb 30 Tagen netto bis 04.07.2023</ram:Description> | |
<ram:DueDateDateTime> | |
<udt:DateTimeString format="102">20230704</udt:DateTimeString> | |
</ram:DueDateDateTime> | |
</ram:SpecifiedTradePaymentTerms> | |
<ram:SpecifiedTradeSettlementHeaderMonetarySummation> | |
<ram:LineTotalAmount>10.00</ram:LineTotalAmount> | |
<ram:ChargeTotalAmount>0.00</ram:ChargeTotalAmount> | |
<ram:AllowanceTotalAmount>0.00</ram:AllowanceTotalAmount> | |
<ram:TaxBasisTotalAmount>10.00</ram:TaxBasisTotalAmount> | |
<ram:TaxTotalAmount currencyID="EUR">1.90</ram:TaxTotalAmount> | |
<ram:GrandTotalAmount>11.90</ram:GrandTotalAmount> | |
<ram:TotalPrepaidAmount>0.00</ram:TotalPrepaidAmount> | |
<ram:DuePayableAmount>11.90</ram:DuePayableAmount> | |
</ram:SpecifiedTradeSettlementHeaderMonetarySummation> | |
</ram:ApplicableHeaderTradeSettlement> | |
</rsm:SupplyChainTradeTransaction> | |
</rsm:CrossIndustryInvoice> |
We can use this XML directly as a data source to create the template and merge the data to generate the PDF. In TX Words, our sample application, you can load the XML as a data source to see all available node elements.
The interesting node is SupplyChainTradeTransaction, which is used as our root node to generate the document. Now we can insert two merge blocks: ApplicableHeaderTradeAgreement and IncludedSupplyChainTradeLineItem.
The first merge block is not inserted to repeat content, but to access the data of this node. Here we can add the fields for the seller party and the buyer party. The second block repeats all line items of the invoice.
During the design of the template we can directly do a preview to see the results.
Programmatically Merging Data
After the template is designed, we can merge the data programmatically using the MailMerge class. The following code snippet shows how to merge the data from the XML data source and save the document as a PDF file:
using System.Data; | |
using System.Text; | |
using TXTextControl.DocumentServer; | |
string xmlData = File.ReadAllText("data.xml"); | |
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl()) | |
{ | |
tx.Create(); | |
tx.Load("zugferd_template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
MailMerge mailMerge = new MailMerge(); | |
mailMerge.TextComponent = tx; | |
// dataset from XML | |
DataSet ds = new DataSet(); | |
ds.ReadXml(new MemoryStream(Encoding.UTF8.GetBytes(xmlData))); | |
// merge data | |
mailMerge.Merge(ds.Tables["SupplyChainTradeTransaction"]); | |
// save as PDF | |
tx.Save("output.pdf", TXTextControl.StreamType.AdobePDF); | |
} |
In line 20, you can see that we are using the SupplyChainTradeTransaction node as the root node for the merge process.
Embedding XML Data in PDF/A-3b
In the next step, we can embed the used XML data in the PDF/A document to generate a valid ZUGFeRD PDF/A-3b document. The following code snippet shows how to embed the XML data in the PDF document:
using System.Data; | |
using System.Text; | |
using TXTextControl.DocumentServer; | |
string xmlData = File.ReadAllText("data.xml"); | |
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl()) | |
{ | |
tx.Create(); | |
tx.Load("zugferd_template.tx", TXTextControl.StreamType.InternalUnicodeFormat); | |
MailMerge mailMerge = new MailMerge(); | |
mailMerge.TextComponent = tx; | |
// dataset from XML | |
DataSet ds = new DataSet(); | |
ds.ReadXml(new MemoryStream(Encoding.UTF8.GetBytes(xmlData))); | |
// merge data | |
mailMerge.Merge(ds.Tables["SupplyChainTradeTransaction"]); | |
// load Xml file | |
string metaData = File.ReadAllText("metadata.xml"); | |
TXTextControl.SaveSettings saveSettings = new TXTextControl.SaveSettings(); | |
// create a new embedded file | |
var zugferdInvoice = new TXTextControl.EmbeddedFile( | |
"ZUGFeRD-invoice.xml", | |
Encoding.UTF8.GetBytes(xmlData), | |
metaData); | |
zugferdInvoice.Description = "ZUGFeRD-invoice"; | |
zugferdInvoice.Relationship = "Alternative"; | |
zugferdInvoice.MIMEType = "application/xml"; | |
zugferdInvoice.LastModificationDate = DateTime.Now; | |
// set the embedded files | |
saveSettings.EmbeddedFiles = new TXTextControl.EmbeddedFile[] { zugferdInvoice }; | |
// save as PDF | |
tx.Save("output.pdf", TXTextControl.StreamType.AdobePDFA, saveSettings); | |
} |
After the document is saved, it can be opened in any PDF viewer that supports PDF/A-3b such as Adobe Acrobat Reader.
Conclusion
TX Text Control provides a powerful template designer to create perfectly formatted invoices based on XML data sources. We learned how to generate the human readable part of the invoice based on the XML data. The MailMerge component can be used to merge the data into the template and generate a PDF document. By embedding the XML data in the PDF document, it is possible to generate valid ZUGFeRD PDF/A-3b documents.