Creating E-Invoices with Free and Open Source Software
Starting 1 January 2025, electronic invoicing will become mandatory for all B2B transactions in Germany. Several other countries in the European Economic Area have already adopted similar requirements, or will soon follow suit. As a result, many businesses in Germany are racing to implement electronic invoicing solutions, though many face challenges along the way. Discover how you can simplify this process by generating electronic invoices in multiple formats — using only free and open-source software.
Table Of Contents
e-invoice-eu
GitHub Repository
The software used for creating electronic invoices is organized in the GitHub repository e-invoice-eu
. It is a Node.JS application, more precisely a NestJS server that can be installed locally and should run on every platform, with the usual limitations also on MS-Windows.
Legal Background - EN16931
The European Union is planning to digitalize invoicing completely in the course of the next years. It has created a standard EN16931 which defines the requirements of electronic invoices.
Formats
Two formats are currently accepted for invoices conforming to EN16931. One is UBL (Universal Business Language), the other one CII (Cross-Industry Invoice). Both of them are XML formats.
Another variant is Factur-X resp. ZUGFeRD. Factur-X was developed in France, while ZUGFeRD is the German equivalent. Today, the two formats are identical. Internally, the format uses CII but the XML document is not exchanged directly with the customer but attached to a PDF.
Many readers will not know that it is possible to attach documents to a PDF, and the only GUI software capable of displaying or extracting PDF attachments is Adobe Acrobat Reader.
The functionality is hidden behind a paperclip icon at the right side of the window. Clicking it reveals the list of attachments to the document. In this case, not only the XML invoice file but an additional file for informational purposes is attached, too.
Business Terms
The standard uses "business terms" to describe information in an invoice. For example the invoice number is business term number 1 or BT-1 in short, the country code of the seller postal address is BT-40, and so on.
Business Rules
The standard also includes numerous business rules that define requirements for a valid invoice. For example, the business rule BR-CO-27 stipulates that the "Sum of Invoice line net amount (BT-106) = Σ Invoice line net amount (BT-131)".
And this is where the standardization kind of ends. Member countries may define their own business rules. An electronic invoice that is valid in Germany is not necessarily valid and accepted in Denmark and vice versa. For example, Germany has defined their own standards XRECHNUNG-UBL and XRECHNUNG-CII. Invoices issued to public authorities have to follow these standards.
What are Electronic Invoices?
PDF documents are not considered electronic invoices in the context of EN16931, and Word documents are certainly not. Electronic invoices must be machine-readable. Factur-X/ZUGFeRD documents are considered machine-readable because the invoice in XML format can be extracted from the PDF File.
Documentation
The text of EN16931 is not freely available, at least not available for download on the internet. Fortunately, OpenPeppol - an organization that governs PEPPOL (Pan-European Public Procurement OnLine) - has published the so-called Business Interoperability Specifications (BIS). One of these specifications is that for UBL invoices, available at https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/tree/
This documentation describes the structure and all requirements to UBL invoice data in a very user-friendly way. This is one reason why the e-invoice-eu
project has chosen this format as the base for its internal format for invoice data. The most significant difference is that e-invoice-eu
uses JSON resp. YAML instead of XML but the structure is mostly identical.
Business Terms and Business Rules
Business terms and business rules had already been described above and they are mentioned all over the documentation. The business rules are listed below the documentation for the respective elements. For example, for the element /ubl:Invoice/cbc:LineExtensionamount
, the business rules BR-12, BR-CO-10, BR-DEC-09, UBL-DT-01, and BR-CL-03 are defined.
They often mention "business terms", and they are unfortunately harder to find. But Google can jump in here. If you want to know what the business term BT-131 is, you can search google with "site:docs.peppol.eu BT-131". One of the first hits will contain the desired information.
Understanding Cardinality
The standard uses "cardinalities" to express to what extent a certain element is required. It consists of two numbers separated by two dots, for example 0..1
. The first number is the minimum number of occurencies, the second one the maximum number. A minimum number 0
means that the element is optional.
The maximum number can also be the letter n
. This stands for any integer number greater than 0.
But a cardinality of 1..1
does not necessarily mean that this element must be present in an invoice document. It depends on its parents elements.
Example: The element /ubl:Invoice/cac:InvoiceLine/cac:AllowanceCharge/cbc:Amount
has the cardinality 1..1
. But the parent element /ubl:Invoice/cac:InvoiceLine/cac:AllowanceCharge
has the cardinality 0..n
which means that it is optional, but can occur an arbitrary number of times. That means that the cbc:Amount
is only mandatory if the invoice line also has a cac:AllowanceCharge
.
Business rules can also demand the presence of a certain element. For example, BR-CO-25 states "In case the Amount due for payment (BT-115) is positive, either the Payment due date (BT-9) or the Payment terms (BT-20) shall be present", see https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/cac-PaymentTerms/cbc-Note/.
The presence of certain elements can also be required by law. For example, specifying the country code of the address of the seller and buyer is enough for the standard. But such an invoice will certainly not be valid according to your country's legislation because it lacks other address elements as the street address and city.
Code Lists
To make electronic invoices not only machine-readable but also machine-understandable, some textual information has to be standardized by restricting the set of possible values to values from code lists.
For example, the invoiced quantity /ubl:Invoice/cac:InvoiceLine/cbc:InvoicedQuantity has an attribute "@unitCode" that must contain a standardized unit code instead of text like pieces, pcs, kilogramm, or hour. The documentation states that the "Unit code MUST be coded according to the UN/ECE Recommendation 20 with Rec 21 extension".
You can find all code lists on the PEPPOL site at https://docs.peppol.eu/poacc/billing/3.0/ in the section "Code lists". Clicking the link Recommendation 20, including Recommendation 21 codes - prefixed with X (UN/ECE) then opens the page with the code list showing all legal values. You can also see for which elements this code list is used.
For example, "pieces" are encoded as H87
or XPP
. A lot of units can be encoded with two codes; one with and one without a leading X.
By the way, the validation in e-invoice-eu
enforces use of the correct code lists.
Status of e-invoice-eu
My business has issued electronic invoices for years already. I have used a toolchain consisting of a self-written Perl module, LibreOffice, Java, and exiftool to create the invoices but the approach was not very flexible and so I have decided to re-write it from scratch replacing Perl with Node.JS because JavaScript is more widely accepted nowadays.
At the time of this writing, the software can create invoices in the following formats:
- CII
- Factur-X/ZUGFeRD EN16931
- Factur-X/ZUGFeRD Extended
- Factur-X/ZUGFeRD XRechnung
- UBL (PEPPOL)
- XRECHNUNG-CII
- XRECHNUNG-UBL
Unlike initially planned, the creation of revision-safe PDF/A documents for Factur-X/ZUGFeRD could be implemented with pure JavaScript without GhostScript and Exiftool.
The roadmap for the future looks like this:
- Implementation of the missing conformance levels of Factur-X/ZUGFeRD (Basic, Basic WL, and Minimum)
- Integration of a validator
- Dockerfile for deployment in containers
General Workflow
The intended audience of the software are small and medium businesses that do not use ERP software to generate invoices but use spreadsheets.
Mapping Spreadsheet Data
The first step consists of mapping the data contained in the spreadsheet file to invoice data. The corresponding endpoint therefore expects a mapping file (in YAML or JSON) and the spreadsheet file, and creates a JSON document containing the invoice data. The schema for this JSON is 100 % equivalent to the XML structure described in the PEPPOL docs at https://docs.peppol.eu/poacc/billing/3.0/syntax/ubl-invoice/.
This format is referred to as the "internal format".
Creating Electronic Invoice
Another endpoint is used to generate the actual invoice document as either XML or PDF from the data provided in the internal format. If you do not use spreadsheets but can generate the data in the internal format with your own tools, you can use the endpoint to generate the invoice document that can be shipped to your customers.
Mapping and Creating at Once
You can also map the data and create the invoice directly. This is the recommended way of using the software, when you are using spreadsheet files for your invoice data.
Validation
The service validates the input data against schemas (more precisely JSON schema) but that only checks structural constraints (for example the cardinality of elements). Business rules are not checked. This may change in the future but for the time being you have to either use online validators or install an offline validator. All available offline validators require a Java Runtime Environment and are therefore quite slow.
The e-invoice-eu
project contains wrapper scripts around the validators, see https://github.com/gflohr/e-invoice-eu/tree/main/contrib/validators for more information.
You should really not skip the validation step, especially in the beginning. Experience shows that invoices initially almost never pass the validation and it is quite likely that your customers will reject your documents.
Running the Service
Requirements
You need Node.JS and a package manager like npm
, yarn
, pnpm
, or bun
. This document assumes that you are using npm
but the other package managers also work.
If you plan to generate Factur-X/ZUGFeRD documents, you also need LibreOffice. If you are on Linux, you will also need Perl but that is almost always pre-installed.
For offline validation, you need a Java Runtime Environment and additional Java libraries, depending on the types of documents you want to validate. See https://github.com/gflohr/e-invoice-eu/tree/main/contrib/validators for more information.
Installation
You first have to install the dependencies of the project.
$ npm install
Important! If you are using bun
, you have to make sure that optional dependencies are installed, use bun install --optional
!
Build
Next, build the server:
$ npm run build
This will create a directory dist
that you can copy wherever you like.
Run
Change directory to where you have copied dist
and start the server:
$ node dist/src/main.js
You should also create a file .env
and put this line inside:
NODE_ENV=production
If you get an error saying "address already in use", it means that port 3000 is used by another application. Pick another, free port by putting this into .env
:
NODE_ENV=production
PORT=8080
Test
Point your browser to http://localhost:3000/api. You should see the OpenAPI/Swagger documentation of the service.
Typical Usage
The following examples assume that you have cloned the e-invoice-eu
repository, started the server in development mode with npm run start
, and you have changed directory to where you have cloned the repository.
Creating an Invoice from a Spreadsheet
The repository e-invoice-eu
ships with sample files and a sample mapping. You can try this out as follows:
$ curl -v -X POST \
http://localhost:3000/api/invoice/transform-and-create/UBL \
-F mapping=@contrib/mappings/default-invoice.yaml \
-F data=@contrib/templates/1234567890-consulting/default-invoice.ods
This is the catch-all endpoint that transforms the invoice spreadsheet contrib/templates/1234567890-consulting/default-invoice.ods
with the mapping contrib/mappings/default-invoice.yaml
to UBL. The output format is specified as a path parameter after the actual endpoint /api/invoice/transform-and-create
. The formats UBL
and XRECHNUNG-
UBL` are the only one supported at the time of this writing.
At the moment, the created invoice is not valid because the input data in the spreadsheet contains nonsense for development purposes.
Transforming an Invoice to the Internal Format
You can also just transform the data into the internal format:
$ curl -v -X POST \
http://localhost:3000/api/mapping/transform \
-F mapping=@contrib/mappings/default-invoice.yaml \
-F data=@contrib/templates/1234567890-consulting/default-invoice.ods
Creating an Invoice from Data in the Internal Format
This is currently not yet implemented but will go like this:
$ curl -v -X POST \
http://localhost:3000/api/invoice/transform/UBL \
-F data=@contrib/templates/1234567890-consulting/default-invoice.ods
Viewing the Swagger/OpenAPI Documentation
Point your browser to http://localhost:3000/api.
Downloading the Mapping Schema
The JSON schema for mappings (that map spreadsheet data to invoice data) is available at http://localhost:3000/api/schema/mapping.
Downloading the Invoice Schema
The JSON schema for the internal invoice format is available at http://localhost:3000/api/schema/invoice.
Getting Started
If you want to use the service for your own business, it is strongly recommended to use the provided example files as a starting point and adapt them to your needs.
The Spreadsheet
You can find an example spreadsheet in contrib/templates/1234567890-consulting/default-invoice.ods
.
Formats
The sample spreadsheet uses the standard open document format with the filename extension .ods
. You can also use Excel .xlsx
files. In fact, you can use any format that is supported by the SheetJS library. See the file formats page of their documentation for more information.
Print Ranges
The spreadsheet is cluttered with a lot of extra information for the electronic version of the invoice. It is important to hide the information in the PDF, resp. paper version by defining print ranges.
If you want to create only XML invoice documents, you can ignore this. But if you want to use the hybrid Factur-X/ZUGFeRD format in the future, you must also keep an eye on the design of the document. Even for XML, you can embed a PDF version of the invoice as a base64 string.
If you are shipping hybrid invoices, at least German tax authorities stipulate that the PDF and the XML versions are content-equivalent duplicates (inhaltsgleiche Mehrstücke in German). That means that both documents must carry the exact same tax-relevant information. Keep that in mind!
Invoice Number
I organize my invoices inside directories with names that begin with the invoice number. In the past, I often had trouble using duplicate invoice numbers or skipping numbers by mistake. Now I use a formula inside of the spreadsheet for extracting the invoice number from the directory name:
In the upper cell, the directory portion of the path gets extracted. In the lower cell, the first ten characters of the directory name which hold the invoice number are extracted with this formula:
=LEFT(R1, 10)
If you are using a different file system structure or your invoice numbers have a different length, you must change this formula.
In the PDF version, I want to use the string "Invoice № NNNNNNNNNN" but the invoice number must be available in isolated form for the electronic invoice. Therefore, the displayed version is created with the formula =CONCATENATE("Invoice № ", R2)
.
Code Lists
As mentioned above, a lot of data has to be encoded using code lists, for example units or tax categories. For tax categories, I also had to map the category to the applicable tax rate.
In general, I do that by defining these code lists in separate tabs. The tab "Tax" contains five columns, for example. The first column code holds the text that is displayed on the PDF version of the invoice. These are essentially the values from the respective code list Duty or tax or fee category code (Subset of UNCL5305) with the exception of the category "S" where I added the actual tax rate as a suffix, using current VAT rates in Germany.
Wherever tax information is used, I defined a data validity range pointing to this list of tax categories. In LibreOffice, you can do that by selecting the cells, and then definining it with the menu entry Data -> Validity...
. That allows you to select the value from a drop down, when clicking into the cell.
However, S19 and S7 are not valid codes. The numbers 19 and 7 were simply appended to indicate the tax rate in percent. The correct code for both of them is just S
. The "Tax" sheet has that code in column C. How can that be copied into the relevant cells in the right part of the sheet? It is done with the VLOOKUP
formula.
Example:
=VLOOKUP(H23, Tax.$A$2:$C$12, 3, 0)
This formula performs a vertical lookup. Here is a breakdown of its components:
Function:
VLOOKUP
stands for "Vertical Lookup." It searches for a value in the first column of a range and returns a value in the same row from a specified column.Lookup Value:
$H23
- This is the value being searched for. The dollar sign before the column letter ($H
) indicates that the column reference is absolute, meaning it will not change if the formula is copied to another cell. However, the row number (23
) is relative, so if the formula is copied down, this reference will change to$H24
,$H25
, etc.Table Array:
Tax.$A$2:$C$12
- This specifies the range where theVLOOKUP
function will search. The use of$A$2:$C$12
means both the column (A
andC
) and the row numbers (2
and12
) are absolute references. This means that if the formula is copied elsewhere, this range will always refer to exactly these cells, which is critical in ensuring the formula always looks at the intended data set.Column Index: 3 - This specifies that
VLOOKUP
should return a value from the third column of the defined range. In this case, it will return a value from columnC
, since the range starts at columnA
. You can see that columnC
of the sheetTax
contains the correct code from the code list.Range Lookup: 0 - This indicates that the lookup is for an exact match. A value of 0 (or
FALSE
in some other Excel contexts) tells the function to find an exact match for the value in$H23
.
The lookup for the tax rate in percent is done in the same way. The same applies to the mapping of units to unit codes.
You can, of course, fill all these fields by hand. But this is somewhat error-prone. It might pay out to try to understand the formula instead.
The rest of the spreadsheet should be pretty self-explanatory and uses only standard features. Until you get used to the file, make sure that you look at the current value of a cell before editing it so that you do not accidentally overwrite a formula.
Mapping Definition
The example invoice template comes with an accompanying mapping file in YAML format, contrib/mappings/default-invoice.yaml
. Note that YAML is a strict superset of JSON, so you can also use JSON if you prefer. YAML, however, has the advantage that you can put comments into the file.
The purpose of the mapping file is to convert the tabular data in the spreadsheet file into the tree structure of the invoice data in the internal format.
General Structure
meta:
sectionColumn:
Invoice: L
ubl:Invoice:
\# ... omitted for brevity
The first section of the file is the meta
section which holds auxiliary information for the mapping. At the moment it only has the property sectionColumn
. Its purpose is explained later.
The second section is the ubl:Invoice
section. The JSON schema for this section is identical to the invoice data schema, the "internal format" with the one difference that the values of each leaf are not (necessarily) actual values but mostly references into the spreadsheet that tell the service where the particular piece of information can be found.
Cell References
Cell references look exactly like references in your spreadsheet application. They always start with an equals sign =
followed by the name of a cell.
Example:
ubl:Invoice:
\# ...
cbc:ID: =R2
cbc:IssueDate: =Invoice.I6
\# ...
The first reference =R2
means that the value of cbc:ID can be found in column R
row 2
of the spreadsheet.
The second reference =Invoice.I6
also contains the name of the sheet (some people prefer the word tab instead). If you omit the sheet name, the cell is searched in the first sheet in the file. If all of your invoice data is contained in one single sheet, you can just forget about this, and always use just the cell name.
Literal Values
Everything that is not begin with an equals sign is considered a literal value. This comes in handy, when you want to hardcode things:
ubl:Invoice:
\# ...
cbc:Name: Acme Ltd.
cbc:DocumentTypeCode: "380"
\# ...
The cbc:Name
in the example above is copied verbatim into the invoice data.
Note that only strings are allowed as literal values. Therefore, numbers have to put into quotes like in the cbc:DocumentTypeCode
above.
If you want to use a literal value that starts with an equals sign, you have to prepend a (straight) single quote '
which gets discarded. Consequently, if you want to use a literal value that starts with a single quote, you have to use two single quotes at the beginning, for example ''quoted value'
.
Make sure to not confuse the straight single quote with backticks \`` or other quotation marks like
‘, or
’`, especially when copy and pasting from other software.
Sections
Unless your invoice has just one line item, some cells do not have a fixed position. For example, the totals of the invoice are pushed down, if you add more line items. This is supported by the concepts of sections in the spreadsheet.
You must reserve one column in each sheet for the section labels. This is the /meta/sectionColumns
object in the mapping. The keys are the names of the sheets, the values are the column names.
Section names are case-sensitive!
You could, for example, name the section that starts with the sum of all invoice lines Subtotal
. You put that name into the row where this section begins into the section column.
You now have to use a slightly different syntax for referencing the cell. Section names always start with a colon :
(because colons are not allowed as part of a sheet name):
cbc:Amount: =:Subtotal.J1
This will now be interpreted as the 1st row in the section Subtotal
. If you have multiple sheets, you would use =Invoice:Subtotal.J1
to specify that the cell is in the sheet Invoice
.
Bound Sections
In general, section names can only be used once per sheet, and using a section name more than once will cause an error with the exception of bound sections.
Some parts of an invoice can be repeated. An invoice line is a typical example for that as you have one invoice line per invoiced item. A mapping for it looks something like this:
ubl:Invoice:
\# ...
cac:InvoiceLine:
section: :InvoiceLine
cbc:ID: =:InvoiceLine.A1
cbc:InvoicedQuantity: =:InvoiceLine.E1
cbc:InvoicedQuantity@unitCode: =:InvoiceLine.M1
cbc:LineExtensionAmount: =:InvoiceLine.J1
cbc:LineExtensionAmount@currencyID: EUR
The element /ubl:Invoice/cac:InvoiceLine
occurs once per invoice line. It is a list, or an array if you prefer that term. For such arrays you must specify a special property section
with the name of the section that is bound to this list or array, in this case the section InvoiceLine
. You can optionally specify a sheet name with section: Invoice:InvoiceLine
. Make sure that the sheet has a corresponding section column entry.
References with bound sections work pretty much like with normal sections only that the row number is not relative to the first section but relative to the current section.
Most of the time, references inside these list-like items will always refer to the same section. But you can also refer to another section but only if it is not a bound section. It is also possible to refer to information from the invoice header (typically not part of a section) or literal values.
Nested Bound Sections
Sections may be arbitrarily nested. An important example are charges and allowances for an invoice line item. Just like the line item itself, there may be an arbitrary number of charges or allowances for each line item.
All you have to do is to make sure to use a different section name for each level. Allowances and charges can occur on the document level, on the line item level, and also on the price level. If you have all three types of them, you must use a different section name for all of them.
Example:
In this example, the name ACInvoiceLine
was chosen for charges and allowances on the line item level. The first line item has two charges or allowances with the amount of 33.50 € and 12.28 €. The second line item has one charge or allowance with the amount of 23.04 €. The rest of the line items do not have charges or allowances. The relevant part of the mapping looks like this:
ubl:Invoice:
\# ...
cac:InvoiceLine:
section: :InvoiceLine
cbc:LineExtensionAmount: =:InvoiceLine.J1
cac:AllowanceCharge:
section: :ACInvoiceLine
cbc:Amount: :ACInvoiceLine.J1
Both the line extension amount and the amount of the charge or allowance are in column J
but the line extension amount is on the first row of the corresponding invoice line section, and the amount of the charge or allowance is on the first row of the allowance/charge section inside of the invoice line section.
Attributes
Some elements have (XML) attributes. For example, most monetary amounts have a mandatory attribute currencyID
. To define a reference for such attributes you simply append the attribute name with a leading at-sign @
to the element name:
cbc:Amount: :ACInvoiceLine.J1
cbc:Amount@currencyID: EUR
Alternative Mappings
The mapping of spreadsheet data to invoice data is admittedly somewhat complicated.
If you think it is too complicated, or the features are not sufficient for your particular use case, feel free to roll your own version. As long as you manage to produce invoice data in the internal format, you can just skip the mapping step.
Common Mapping Problems
Although the invoice data format is well documented, some fields often lead to questions.
Document Type Code
The /ubl:Invoice/cbc:DocumentTypeCode
must come from the list Invoice type code (UNCL1001 subset). The most common type code is 380 for commercial invoices.
For self-billing invoices, the documentation for Factur-X/ZUGFeRD suggests the type code 389. However, this code is not included in the list provided by PEPPOL.
The same problem exists for invoice corrections where the Factur-X documentation suggests 384 which is again not included in the PEPPOL list.
If you have more information on this, please leave a comment.
Buyer Reference
The field /ubl:Invoice/cbc:BuyerReference
is actually an optional field. The business rule PEPPOL-EN16931-R003 demands, however, that either the buyer reference or the order reference /ubl:Invoice/cac:OrderReference/cbc:ID
must be present.
The order reference is usually an order number issued by the customer when they have ordered the goods or services from your business.
The buyer reference has special semantics for invoices issued to German public institutions. The German XRECHNUNG standard demands that the field must be present and contain the so-called Leitweg-ID. You can often look up this Leitweg-ID on the internet, or otherwise just ask the customer who will know their own ID.
Endpoint-IDs
Both the supplier and the customer have to be identified by an endpoint ID. The respective fields are /ubl:Invoice/cac:AccountingSupplierParty/cac:Party/cbc:EndpointID (BT-34) and /ubl:Invoice/cac:AccountingCustomerParty/cac:Party/cbc:EndpointID (BT-49). A safe choice is the respective VAT ID which has the @schemeID
9930 for German VAT numbers. For other countries, you can look up the @schemeID
in the code list CEF Electronic Address Scheme (EAS). Other popular choices are the above mentioned Leitweg-ID (@schemeID
0204), or the EAN location code (@schemeID
0088) also known as Global Location Number GLN.
The Factur-X/ZUGFeRD documentation also allows using email addresses with the @schemeID
EM
but this is another discrepancy between that documentation and the PEPPOL documentation because EM
is missing in the PEPPOL list. If you can shed some light on this, please leave a comment.
It is quite common for bigger corporations that they have certain requirements for which endpoint ID and @schemeID
to use, and they will usually inform you about this. The VAT ID is a good default, though.
IBAN and BIC
Both your IBAN and BIC/SWIFT Code are actually optional. But many customers will insist on getting them so that the payment can be organized. You will also need the account holder name and the payment type. The respective fields are ubl:Invoice/cac:PaymentMeans/cac:CardAccount/cbc:HolderName
for the account holder name, ubl:Invoice/cac:PaymentMeans/cbc:PaymentMeansCode
for the payment type code (a bank transfer has code 30), ubl:Invoice/cac:PaymentMeans/cac:PayeeFinancialAccount/cbc:ID
for the IBAN, and ` ubl:Invoice/cac:PaymentMeans/cac:PayeeFinancialAccount/cac:FinancialInstitutionBranch/cbc:ID for the BIC/SWIFT code.
Surcharges and Discounts
Surcharges and discounts that are referred to as charges and allowances in the standard, can be applied at the document level, line-item level, or price level. They are defined within cac:AllowanceCharge
groups, for example /ubl:Invoice/cac:AllowanceCharge
. Unlike the common practice on paper invoices, discounts also have a positive value in this case. Whether it is a surcharge or a discount is determined by the field cbc:ChargeIndicator
within the group, such as /ubl:Invoice/cac:AllowanceCharge/cbc:ChargeIndicator
, which must contain true
for surcharges and false
for discounts.
Customization-ID und Profile-ID
The fields /ubl:Invoice/cbc:CustomizationID
and /ubl:Invoice/cbc:ProfileID
are mandatory in the standard EN16931. In mappings or as invoice input data for e-invoice-eu
, they are optional because the values can be deduced from the selected invoice format. If you omit these fields - which is recommended - the currently correct values are used automatically. However, if you know what your are doing, you can override this and pass your own customization and profile IDs.
Conclusion
The information given here should be enough to get you started creating your first electronic invoice. The e-invoice-eu
project is still under heavy development. Information about new features will be available on the e-invoice-eu
GitHub page and here.
Please understand that I can give no fixed dates for the introduction of certain features. But you can contact my company at info@cantanea.com for commercial support for the software and general consulting services around electronic invoices.
Leave a comment