Use caseCustom softwareDocument AI
An e-invoice layer that lets an old ERP issue XRechnung and ZUGFeRD before 2027
From 1 January 2027, German firms above EUR 800,000 turnover must issue e-invoices. A converter layer makes an old ERP produce valid XRechnung and ZUGFeRD.
A blueprint, not a client story. The business described is illustrative; the architecture, integrations and trade-offs are real, and this is how I would build it. By Ergini, .
The short version
Custom software for German companies whose ERP or billing system cannot produce e-invoices in time for the 2027 mandate. It reads invoice data from the database, an export or, as a last resort, the print output, maps it to the EN 16931 model, generates XRechnung or a ZUGFeRD PDF, validates each file with the KoSIT validator, then sends and archives it. Mostly deterministic code: a model helps during setup, and a person handles every stopped invoice.
- Best for
- German manufacturers and agencies above EUR 800,000 turnover whose billing runs on an old ERP, an Access database or custom software with no e-invoice export.
- Connects to
- The existing ERP or billing system, KoSIT validator, Email (Outlook or plain SMTP), Peppol access point, DATEV
- The AI does
- Proposes field and unit mappings during setup, reads print PDFs when no data export exists, and finds order numbers buried in free-text remarks.
- People do
- Confirm the mapping once, fix the master data the validator rejects, agree formats with customers, and release every stopped invoice.
- Built as
- Custom Software Development, quoted per scope
Less than four months, and an ERP from another decade
Say you make machine parts with a dozen people and about EUR 1.2 million turnover, billing from an industry ERP installed in 2009. The vendor's e-invoicing module needs an upgrade your customizations will not survive, or the vendor has stopped answering. Every invoice leaves as a PDF from the print routine. An agency billing from a homegrown timesheet tool is in the same position.
From 1 January 2027, a German business whose turnover in the previous year exceeded EUR 800,000 must issue e-invoices for domestic B2B sales, and from 1 January 2028 every business must. An e-invoice means a structured format that follows EN 16931, which in practice means XRechnung or ZUGFeRD. A PDF, however tidy, no longer meets the requirement.
Only domestic business invoices are covered. Invoices to consumers, small invoices up to EUR 250 and invoices to customers abroad can stay as they are for now, although the EU's ViDA package brings e-invoicing to cross-border B2B trade in 2030. Receiving is the other half of the change, covered in e-invoice intake.
The honest framing: this is mostly not an AI project. It is an integration and data-mapping job with a hard deadline, a converter layer between the old system and the outside world while the old system keeps doing what it does well. A model earns its place in three narrow spots, and they are marked below. Everything that ends up inside an invoice is deterministic.
What the old system gets wrong, field by field
Writing XML is the easy part. These are the places where legacy invoice data fails EN 16931, and what the layer does about each.
Mandatory fields that were never stored
XRechnung requires the seller's VAT ID, a buyer reference, payment instructions and an electronic address for both parties, and many old systems never had fields for some of them. Company-wide values come from configuration; per-customer values come from a small table the layer owns, filled in once.
Units written as free text
The ERP says 'Stk', 'St.', 'Stck' and 'Psch'. EN 16931 wants UN/ECE Recommendation 20 codes: H87 or C62 for pieces, LS for a lump sum, HUR for hours, KGM for kilograms. The layer keeps a mapping table, and an unmapped unit stops the invoice rather than being guessed.
VAT rounded per line
Many old systems calculate VAT on each line and add up the rounded amounts. EN 16931 calculates it per rate on the summed net amounts, so the two can differ by a cent and the file fails validation. The layer recomputes by the standard's method and compares; any difference stops the invoice, and the lasting fix is usually a rounding setting in the ERP, not a patch in the layer.
Credit notes printed as negative invoices
The old system prints a credit note as an invoice with minus signs. The layer issues it as type 381 with positive amounts and the original invoice number in BT-25. The German word Gutschrift can also mean self-billing, which is type 389, so the mapping decides per document type rather than per word.
Customers who still want a PDF
Some accounts teams still want something to look at. ZUGFeRD in the EN 16931 profile answers that: the ERP's familiar print layout, converted to PDF/A-3, with the XML embedded. Before sending, a check confirms that the totals in the picture and in the XML are identical.
The same invoice sent twice
A retry after a mail server timeout must not send the invoice again, and re-running an export must not issue a second XML for the same number. Each invoice number is sent once and logged with the file's hash; a resend is a deliberate action with its own log entry.
One invoice through the converter
Only one step in this path uses a model, and only when the old system offers no data at all. Everything that decides what goes into the invoice is code.
01 Trigger · Database, export folder or print queue
The ERP finalizes an invoice
A new invoice number appears in the invoice table, an export file lands in a folder, or the print spooler produces a PDF. The layer watches whichever of the three the old system can offer.
02 Plain code · ODBC or CSV
Read the invoice data
Header, lines, VAT and customer come straight from the database or the export, read-only. Nothing is retyped.
03 AI model · Structured output
Read the print output, when nothing else exists
For systems with no database access and no export, a vision model extracts the fields from the ERP's own PDF. Every value must then reconcile: lines to net, VAT to rate, total to the printed total. Anything that does not reconcile is not sent.
04 Plain code
Map to EN 16931
Fields go to their business terms (invoice number to BT-1, buyer reference to BT-10, payment terms to BT-20), units to UN/ECE codes, tax keys to VAT categories such as S or AE, and the VAT breakdown is recomputed per rate.
05 Plain code · KoSIT validator
Generate and validate
The layer writes XRechnung in UBL or CII syntax, or a ZUGFeRD PDF/A-3, then runs the KoSIT validator on the XML and keeps its report with the invoice.
06 Decision
Does it pass?
Validation and a totals comparison with the ERP, both in code.
- Valid, and totals equal the ERP's to the cent then send
- A validation error, a missing value or any difference from the ERP's totals then stop; the invoice goes to the billing queue with the failing rule in plain words
- Out of scope: a consumer, a customer abroad or an invoice up to EUR 250 then the ERP's PDF goes out as before, logged as out of scope
07 Person
Fix the cause, never the file
The billing person corrects the master data in the ERP (a missing VAT ID, a unit without a code, an invoice address) and releases the invoice again. Nobody edits generated XML by hand.
08 Result · SMTP, Peppol access point
Send once, archive, hand to DATEV
The e-invoice goes by email, or by Peppol where the customer uses it. One send per invoice number, logged with the file hash; the issued file, its validation report and the delivery receipt are archived as GoBD requires, and the booking data goes to DATEV.
The mapping, as it looks inside the layer
Most of the build is this table, completed for your system. An excerpt for an old ERP with a German print layout:
| Legacy field or value | EN 16931 business term | Rule in the layer |
|---|---|---|
| RE_NR (invoice number) | BT-1 Invoice number | Copied as is; a number already sent is refused |
| Kundenbestellnr. | BT-13 Purchase order reference | Also used as BT-10 when the customer has no other buyer reference |
| Leitweg-ID (new field) | BT-10 Buyer reference | Mandatory for public-sector customers; kept in the layer's customer table |
| Unit 'Stk', 'St.' or 'Stck' | BT-130 unit code H87 | Mapping table; an unmapped unit stops the invoice |
| Unit 'Psch' (lump sum) | BT-130 unit code LS | Mapping table |
| '14 Tage 2% Skonto, 30 Tage netto' | BT-20 Payment terms, BT-9 Due date | Text kept; for XRechnung, the discount also in the #SKONTO# syntax |
| Tax key 1 or 3 | BT-151 VAT category S (19%) or AE (reverse charge) | Mapping table; AE carries the reverse-charge reason the rules require |
| Document type 'Gutschrift' | BT-3 type 381, or 389 for self-billing | Decided per document type; amounts positive, BT-25 names the original invoice |
A first invoice for a new customer
New customers and unusual units are where stops happen. This one is caught before it leaves, fixed at the source, and sent on the next run.
Converter log, Monday 07:02
- read_invoice(source: "erp_db", number: "26-10433")customer 10277, first invoice / 6 lines / net EUR 8,941.37 / VAT 19% / 30 days net
- map_invoice(number: "26-10433", syntax: "cii")stopped / line 4 unit 'Satz' has no UN/ECE code / customer 10277 has no invoice email address
- suggest_mapping(value: "Satz", samples: 40)SET (set) / used for seal kits on 40 past invoices / needs confirmation
Converter · Billing queue
Invoice 26-10433 has not been sent. Two things are missing: the unit 'Satz' on line 4 has no standard code (suggested: SET, as used for seal kits on 40 earlier invoices), and customer 10277 has no invoice email address. Confirm the unit and add the address in the ERP, and the invoice goes out on the next run.
Billing · Billing queue
SET is right. Invoice address added to the customer record.
- kosit_validate(file: "26-10433.xml")schema valid / business rules: 0 errors / recommendation: accept
- check_totals(number: "26-10433")net, VAT and total equal the ERP's figures to the cent
- send_invoice(number: "26-10433", format: "zugferd_en16931")sent once by email / hash logged / customer prefers ZUGFeRD
- archive(number: "26-10433")file, validation report and delivery receipt stored / booking data queued for DATEV
Where a model helps, and where it stays out
The AI model
Proposing field and unit mappings during setup
It reads sample invoices and suggests which legacy value fits which business term; a person confirms each mapping once.
Reading print PDFs when there is no export
The fallback for systems that offer nothing else, always followed by a reconciliation of every total.
Finding values buried in free-text remarks
Old systems often keep the customer's order number in a remarks field. The model proposes it for BT-13, and a person confirms the pattern.
Plain code
Mapping, VAT calculation and XML generation
An invoice is a legal document, so it is generated the same way every time.
Validation
The KoSIT validator decides, not a model's opinion of the XML.
Sending once and archiving
Idempotent by invoice number and logged with the file hash. The pattern is in integration layer or automation platform.
A person
Fixing master data and releasing stopped invoices
The fix belongs in the ERP, made by whoever owns billing.
Agreeing format and channel with each customer
XRechnung, ZUGFeRD or Peppol is agreed with the customer, not inferred.
The road to 1 January 2027
With the deadline this close, the order of work matters. This is the sequence I would follow.
- List every invoice type the ERP produces: invoices, credit notes, down-payment and final invoices, and anything the system calls a Gutschrift.
- Collect a few hundred real invoices as test cases, including the strange ones billing remembers.
- Complete the mapping table with billing, and agree per customer: XRechnung, ZUGFeRD or Peppol.
- Generate every test invoice and run it through the KoSIT validator until the whole set passes.
- Send real e-invoices to two or three friendly customers alongside the PDF, and ask what their accounting software shows.
- Switch the remaining domestic business customers in groups before year end.
- From 1 January 2027, domestic B2B invoices leave only as e-invoices, and the layer's log shows every file and its delivery.
Starting in September leaves room for the two surprises every legacy system holds: an invoice type nobody mentioned, and master data that was never complete.
Upgrade the ERP, switch billing tools, or build a converter?
If your ERP vendor ships an e-invoicing module for your version at a fair price, take it. The vendor maintains it as XRechnung versions change, and nothing I build beats that. For simple billing, lexoffice, sevDesk and easybill create compliant e-invoices out of the box, and DATEV's own invoicing does too for companies already working in DATEV.
A converter layer wins when the old system stays: the upgrade would break years of customizations, the vendor has no answer before January, or billing lives in an Access database or custom software nobody wants to touch. Replacing the ERP to meet a deadline is the riskiest option of all, and a converter buys the time to do that migration properly, as described in legacy code modernization.
E-invoicing service providers also convert as a service, usually priced per document, which suits companies whose ERP can already export clean structured data. The custom part is then only the export, and that can be the cheapest route of the three.
How you would know it is working
A blueprint has no results to report, so here is what I would measure from the first week instead, on your own data.
- First-pass validation rate
- Share of invoices that pass KoSIT without a stop, by invoice type. It should approach all of them well before January.
- Stops by cause
- Missing master data, unmapped units, total mismatches. Each cause is fixed once in the ERP, so the list should shrink week by week.
- Customer queries and rejections
- Invoices customers question or send back, which catches what validation cannot, such as a buyer reference that is valid but wrong.
- Finalization to delivery time
- From the ERP finalizing an invoice to the send log, to catch runs that stall overnight.
What a build like this costs
This is built as Custom Software Development, which is quoted per scope after a free 30-minute call rather than from a fixed table, because the range of what counts as custom software is too wide for one honest number.
What it costs to run
Running costs are small. The layer runs on an ordinary server or next to the ERP, and the KoSIT validator is free and open source. A Peppol access point adds a per-document or monthly fee if some customers need it, and model usage exists only while the print-reading fallback is in use.
What moves the price
- How the invoice data can be read: a database with understandable tables, an export file, or only the print output
- How many invoice types exist beyond plain invoices: credit notes, down-payment and final invoices, self-billing
- How many customers need something other than email: Peppol, a public-sector portal or their own supplier portal
- The state of master data: missing VAT IDs, units and invoice addresses are fixed in the ERP, and that work is often larger than the code
- Whether one company or several legal entities bill from the same system
Who this is for
- German manufacturers above EUR 800,000 turnover billing from an industry ERP whose e-invoicing upgrade is expensive or not coming
- Agencies and service firms invoicing from custom software, an Access database or a spreadsheet macro
- Companies with public-sector customers who need XRechnung with a Leitweg-ID now, not in 2027
- Firms planning an ERP migration after 2027 that need compliant invoices in the meantime
Questions people ask about this
Who has to issue e-invoices in Germany from 2027?
Any business whose turnover in the previous calendar year exceeded EUR 800,000 must issue e-invoices for domestic B2B sales from 1 January 2027, and every business must from 1 January 2028. Receiving e-invoices has been mandatory for all businesses since 1 January 2025. Invoices to consumers and small invoices up to EUR 250 are outside the obligation.
How do I create an XRechnung from an old ERP?
Read the invoice data the ERP already holds, from its database or an export, map it to the EN 16931 business terms, and generate the XML in UBL or CII syntax. Then validate every file with the KoSIT validator before it leaves. The hard part is rarely the XML; it is the missing master data, the unit codes and the VAT rounding the old system never had to care about.
Can we keep sending PDF invoices after 2027?
Not for domestic B2B invoices once you are above the threshold: from 1 January 2027 those must be structured e-invoices, while smaller businesses may send PDFs until the end of 2027. A ZUGFeRD file is a PDF with the XML embedded, so customers who want something to read still get one, and it counts as an e-invoice when it uses a compliant profile.
XRechnung or ZUGFeRD: which should we send?
Both meet the German requirement. XRechnung is pure XML and is what public-sector customers expect. ZUGFeRD in the EN 16931 profile or higher is a PDF with embedded XML, which suits business customers whose accounts teams still read invoices on screen. The layer produces either one per customer from the same data, so the choice can change later without new work.
Is building an e-invoice converter an AI project?
Mostly not, and I say so upfront. Generating and validating e-invoices is deterministic code, because an invoice is a legal document. A model helps in three places: proposing field and unit mappings during setup, reading the print output when the ERP offers no export, and finding values such as order numbers buried in free-text remarks. People confirm all three.
What does an e-invoice converter cost?
It is custom software, quoted per scope after a free discovery call, because the effort depends on how the old system can be read and how complete its master data is. A system with a readable database and clean customer records is a small project; one that only prints PDFs takes longer and adds the model-based fallback.
Sources