blog·5 min read

Ten suppliers, ten invoice formats: reading PDF invoices to the cent

How we automatically read and check purchase invoices from ten suppliers for a wholesaler with Exact Online. Without clever recognition, with one reader per supplier and three years of archive as a test.

Read in this supplier invoice, check the sums and show which lines do not
match what we have in Exact.

Processing purchase invoices automatically sounds like a solved problem. There are plenty of services that read a PDF and pull fields out of it. But if you buy from a fixed group of suppliers and want to compare every line with your own items, you need more than fields. You want to know whether the invoice is right, to the cent, and if not, which line it is.

We built that for a drinks wholesaler with a shop that works with Exact Online. It now reads the invoices of ten suppliers, and about 97 percent adds up to the cent. This is how, and what is on invoices that you do not expect. The client and the suppliers stay anonymous and there are no amounts, prices or margins here.

One reader per supplier, no clever recognition

The first choice was the most striking one: we use no AI and no text recognition to read the invoices. Every supplier has a reader of its own, a piece of ordinary code that knows exactly what its invoice looks like.

That sounds old-fashioned, but it has three advantages. The same invoice always gives the same result. Every reader can be tested on real invoices. And if a supplier changes its layout, the reader fails visibly instead of quietly reading something else.

For ten fixed suppliers that is cheaper and more precise than recognition that usually gets it right. The readers were built with an AI assistant, though. The difference is in what runs: ordinary code that you can check.

First text, with the layout intact

A PDF with a text layer first becomes plain text, with the columns in place. There the app recognises the supplier by a fixed text, such as a web address or a company name, and hands it to the right reader.

At one supplier the whole letterhead is a picture. There is then no name to recognise it by. We recognise that one by a fixed line at the bottom of the invoice, a legal notice that is always the same. Electronic invoices in UBL are the easiest: you read those without recognition, field by field.

Checking the sums, to the cent

Reading is half of it. After that the app recalculates. Do the lines plus the invoice costs add up to the amount excluding VAT, without deposit? Is the VAT right per rate? If there is a difference of more than a cent, the invoice is shown at the top with the line that causes it.

An important choice here: calculating with exact decimals, not with the floating point numbers a computer uses by default. The latter turn 0.1 plus 0.2 into not quite 0.3, and over forty lines you add up a cent that is not there. Money is rounded by a fixed rule, and that rule is in the code and in the tests.

What is on an invoice that you do not expect

This is where the real work is. A selection of what we came across with ten suppliers:

A surcharge that is only in the footer. A fixed amount per bottle over the whole invoice, not on the lines. If you only read the lines, you calculate with a purchase price that is too low.

Free bottles on a line of their own. Sometimes as a line with price zero, sometimes as a discount with a minus in front, sometimes as sample bottles at the bottom. Every variant has to lead to the same booking.

Price per case, per bottle, per keg. One supplier invoices per piece, another per case of six or per crate. The quantity on the invoice says nothing without the unit.

Deposit and packaging. Per line, in the footer, or as a separate line with its own VAT rate. It does not belong to the purchase price of the product, so it has to come out.

Costs that look like transport. A fuel surcharge is not a product. It goes along as transport costs, not as an item.

Three years of archive as a test

Invoices from recent weeks are a good start, but the real exceptions are in older invoices. So we pulled three years of purchase invoices out of the archive in Exact, more than 1,800 documents, and ran all of them through the readers.

That produced a list you never make up: a supplier that used English number format two years ago, a column that is missing in old invoices, an invoice date that is sometimes not on it. Every reader was adjusted for that, with the old invoice added as a test.

Credit notes we only read in a separate mode. A normal upload refuses them, so a credit note is never booked as an invoice by accident. Scans without a text layer and loose statements cannot be read; those are on a list under the numbers, so you know how complete the picture is.

Every reader, a test on a real invoice

Every reader has tests on real invoices from that supplier: what goes in and exactly what has to come out. When a reader is improved, all old invoices run along again. Invoices that are already in the app can then be read again with the improved reader.

Common mistakes

Calculating with ordinary floating point numbers. Over many lines that produces cents that are not there. Use exact decimals and a fixed rounding rule.

One reader for all suppliers. A general reader does everything a little. A reader per supplier does its one invoice perfectly, and fails visibly when it changes.

Only checking the total. Two errors can cancel each other out. Check the lines, the costs and the VAT per rate separately.

Reading credit notes as ordinary invoices. Then you book a credit note as a purchase. Give them a mode of their own.

Throwing away what cannot be read. Keep a list of what is not in. A figure without completeness is a guess.

How this fits into the whole app, from invoice to purchase order in Exact, you can read in the case of the drinks wholesaler. What an Exact integration can do besides that is on connecting Exact Online.

MK
Maarten Keizer

Founder of Invoker. Over twenty years in hosting, system administration and web development; builds the Claude hosting himself and tests everything on our own servers first.

about maarten