The PDF Invoice a German Customer Stopped Accepting
By Ergini, Software & AI Developer
A composite story. The company and the people in it are invented. The problem, the rules and the system are real, and the full blueprint is in the use case library.
TL;DR
A composite story: an invented metal parts maker in Lüdenscheid sends its biggest customer a PDF invoice and gets it back within minutes, rejected for having no structured data. From 1 January 2027 its domestic B2B invoices must be e-invoices anyway, and its 2008 ERP cannot produce XRechnung or ZUGFeRD. The build I would do is a converter layer that reads each invoice from the ERP's database, maps it to EN 16931, validates it with KoSIT and sends it once. It is mostly deterministic code: a model only proposes mappings, and a person fixes every stopped invoice at its source.
Document rejected
"Document rejected: no structured invoice data (EN 16931) found. Please resend as XRechnung or ZUGFeRD."
The reply takes four minutes, and no person wrote it. Silke had sent a test: invoice 26-40218, exactly as every invoice leaves the building, a PDF from the ERP's print routine with the logo, the delivery note numbers and the alloy surcharge on its own line. She sent it to the new invoice address her biggest customer announced in August, to see what would happen. Now she knows.
Silke runs billing at a maker of stamped and bent metal parts in Lüdenscheid, a composite company built for this story: 38 people, around €6 million in turnover, brackets and contact springs for heating systems and household appliances, most of them priced per thousand pieces. About 400 invoices a month leave through that print routine.
The customer that answered makes heating systems and takes about a third of everything the company produces. Its accounts payable team wrote in August: from 1 January 2027 it accepts supplier invoices only as XRechnung or ZUGFeRD, invoices without structured data will be returned unprocessed, and every supplier should send a test by the end of October. A returned invoice is an unpaid one, and this customer already pays on 60-day terms.
The law says the same thing from the other side. From 1 January 2027, a German business whose turnover in the previous year exceeded €800,000 must issue e-invoices for its domestic B2B sales, in a structured format that follows EN 16931, which in practice means XRechnung or ZUGFeRD. Receiving them has been mandatory for every business since 1 January 2025, so the customer already has the intake. It is not being difficult. It is simply ready before its suppliers are.
Fifteen weeks and an ERP from 2008
Reinhard, who runs the company his father founded, has already called the ERP vendor. The e-invoicing module exists, but only in the current version, and moving to it means rebuilding eighteen years of customizations: the alloy surcharge that follows the monthly metal price, the customer price lists, the tooling invoices split into a down payment and a final bill. The earliest migration slot is next spring.
A billing tool is no way out either. Tools such as sevDesk and easybill create compliant e-invoices out of the box, but every invoice here comes out of an order, a delivery note and a price per thousand that live in the ERP. Retyping 400 a month into a second system would trade one problem for a worse one.
So it is the middle of September, with fifteen weeks to go, a system that can print invoices but has never been asked to export one, and a customer that has already shown what it does with a PDF. Reinhard's question on our first call is short: can anything be ready by January without touching the ERP?
Where the invoice lives before it prints
Yes, if the data can be read. So before we talk about formats I ask Silke for three things: read-only access to wherever the ERP keeps an invoice before it becomes a PDF, a list of every kind of document billing produces, and three hundred real invoices from the past year, including the ones she remembers for the wrong reasons.
The database turns out to be the easy part. The ERP runs on an SQL Server that Olaf, who looks after the IT one day a week, can reach, and a read-only login shows two tables with names like RE_KOPF and RE_POS, invoice headers and invoice lines. Everything on the printed invoice is in there. Nothing has to be retyped or read off a PDF.
That decides the design. A converter layer sits beside the ERP and picks up each invoice once it is finalized, maps it to the EN 16931 model, generates the e-invoice, runs the KoSIT validator on it, then sends and archives it. The ERP stays the source of truth for customers, prices and invoice numbers, and the layer never writes back. Invoices to customers in Austria and the Czech Republic stay PDFs, because only domestic business invoices fall under the rule. The design is the one in the blueprint for issuing e-invoices from a legacy ERP.
And I say plainly what this is: mostly not an AI project. An invoice is a legal document, so everything that ends up inside one is produced by code, the same way every time. A model gets two small jobs. It proposes mappings from the sample invoices during setup, and it finds the customer's order number where the ERP never had a field for it, in a remark that reads "Ihre Bestellung 4500912231 vom 02.09." Silke confirms every mapping and the order-number pattern once.
Three hundred invoices, run until they pass
The test set is the project. Olaf restores a copy of the ERP for testing, the three hundred invoices are generated from it, and on the first run 131 of them stop. None of the stops is a bug in the XML. They are the places where eighteen years of billing data meet a standard they never had to satisfy:
| Why it stopped | Invoices | Fixed where |
|---|---|---|
| Price per thousand read as a price per piece | 49 | Mapping: the ERP's price unit sets the base quantity |
| No invoice email address for the customer | 38 | Customer record in the ERP, by Silke |
| Unit written as "St." or "Stck" instead of "Stk" | 21 | Mapping table: all three become H87, confirmed once |
| VAT one cent off the ERP's figure | 14 | Rounding setting in the ERP, by Olaf |
| Credit note printed as a negative invoice | 9 | Layer rule: type 381, positive amounts, the original invoice number |
The cent is the one people argue about. The ERP calculates VAT on each line and adds up the rounded amounts, while 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 stops on any difference and never patches the amount, because then the e-invoice and the books would disagree. Olaf changes one rounding setting in the ERP instead.
The price per thousand is the one that would have hurt. A bracket at €38.40 per thousand, 20,000 pieces, €768.00 on the line. My first mapping carried the price across as it stood, and in EN 16931 a price with nothing more said applies to a single unit: €38.40 per bracket. The layer recomputes every line in code before it generates anything, and 20,000 times €38.40 is not €768.00, so 49 invoices stopped instead of leaving with a price a customer's software would show per piece. The standard has a field for the number of units a price applies to, and the ERP's per-thousand unit now fills it with 1,000. The line check stays, for the next unit nobody has mentioned yet.
Rejected again, for a better reason
By mid-October the whole set passes, and real e-invoices go to three friendly customers alongside the usual PDF, with one question: what does your accounting software show? Two want ZUGFeRD, because their accounts teams still read invoices on screen. They get the familiar layout as a PDF/A-3 with the XML embedded, and before each one leaves, a check confirms that the totals on the page and in the XML are identical.
Then comes the test the heating-systems customer asked for, as XRechnung. It comes back in four minutes again, rejected again, and this time the reason is a better one: "Buyer reference not recognized."
The file is valid, and the KoSIT report says so. What the customer cannot do is route it. The layer had filled the buyer reference with the order number, the default when a customer has named nothing else, and this customer routes invoices by the plant that received the goods: a short code per plant, listed on page two of its August letter. Silke calls Carola, who runs supplier invoicing there, to confirm which code belongs to which delivery address, and has the answer in ten minutes.
So references are agreed per customer, never inferred. The layer's customer table holds, for each account, the format, the channel and what goes into the buyer reference, and for this customer it derives the plant code from the delivery address. An account with an agreed rule and no match gets a stopped invoice, not a guess. The resend is a deliberate action with its own log entry, because the layer sends each invoice number once and logs it with the file's hash. A retry after a mail server timeout cannot put the same invoice in front of Carola twice.
Fixed in the ERP, never in the file
The rule I will not bend is that nobody edits a generated file. A stopped invoice lands in Silke's queue with the failing rule in plain words: this customer has no invoice address, this unit has no code, these totals differ from the ERP's by €0.01. She fixes the cause where it lives, in the ERP or the mapping table, and releases the invoice to run again. A hand-edited XML would leave the e-invoice and the books telling two different stories.
- Code decides everything inside an invoice: mapping, VAT, the XML and the totals checks, the same way every time.
- The KoSIT validator decides whether a file is valid, not a model's opinion of it, and its report is archived with the invoice.
- Silke confirms every mapping once, fixes master data and releases every stopped invoice.
- Reinhard agrees the format and channel with each customer: XRechnung, ZUGFeRD or Peppol is a conversation, not a setting.
Every issued file is archived with its validation report and delivery receipt, as GoBD requires, and the booking data goes to DATEV, so the tax adviser books from the same data the customer received.
The first invoice run of January
The rollout is dull on purpose: the friendly customers first, then the rest in groups through November and December, the heating-systems customer on the date its letter set. From 1 January, domestic business invoices leave only as e-invoices. The print routine still runs, but its PDFs now go only abroad and to the few invoices up to €250.
On the first Monday of the year the converter has picked up the weekend's invoices before Silke has her coat off. Her queue holds two: a new customer with no invoice address, and a unit someone typed as "Rolle" for a coil of spring steel strip, sold for the first time. She adds the address in the ERP, confirms the unit code the model proposes, and releases both. Neither reaches a customer wrong, and the four-minute reply does not come back.
Upgrade, switch or convert
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. If billing is simple, a tool such as sevDesk or easybill, or DATEV's own invoicing if you already work in DATEV, creates compliant e-invoices out of the box. And if your ERP can already export clean structured data, an e-invoicing service provider can convert per document, and the only custom part is the export.
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 software nobody wants to touch. It is custom software, quoted per scope, and the effort depends mostly on how the old system can be read and how complete its master data is. The full blueprint has the mapping table, the flow and the order of work to January. Receiving is the other half of the change, covered in e-invoice intake.
Frequently asked questions
Do I have to send e-invoices in Germany from 2027?
If your turnover in the previous year exceeded EUR 800,000, yes: from 1 January 2027 your domestic B2B invoices must be e-invoices in a structured format that follows EN 16931, which in practice means XRechnung or ZUGFeRD. From 1 January 2028 every business must. Invoices to consumers, invoices up to EUR 250 and invoices to customers abroad are outside the obligation for now.
Our ERP cannot create XRechnung. What are our options?
Take the vendor's e-invoicing module if it exists for your version at a fair price. For simple billing, tools such as sevDesk or easybill create compliant e-invoices. If the ERP has to stay and cannot export them, a converter layer reads each invoice from its database, maps it to EN 16931, generates XRechnung or ZUGFeRD, validates it with the KoSIT validator and sends it once.
Is a ZUGFeRD invoice still a PDF?
Yes, with the structured data inside. ZUGFeRD in the EN 16931 profile or higher is a PDF/A-3 with the XML embedded, so the customer's accounts team sees a familiar layout while its software reads the data, and it counts as an e-invoice. A converter should check that the totals shown on the page and the totals in the XML are identical before it sends anything.
Why would a customer reject a valid e-invoice?
Because validation checks the format and the business rules, not whether a value is the one the customer needs. A buyer reference can pass every rule and still mean nothing to the customer's invoice routing. Agree the format, the channel and the references with each customer, send a few test invoices before switching everyone, and treat every query or rejection as a rule to add.