Resolving invoice exceptions across PDFs and ERP
Follow a EUR 400 invoice difference from the supplier PDF to a clear review report. Build the record checks, add policy search where it helps, and test the workflow before connecting real ERP actions.
The question: Why does this invoice disagree with the order and delivery records, and who should resolve it?
You’ll learn: Build a checked invoice record and an evidence-linked review report; choose the document reader, exact checks and optional policy search.
For: Finance teams, product owners and engineers. No AI background needed.
Executed teaching prototype · AI-assisted source check . This is not a human expert endorsement.
1. Follow one invoice#
An invoice arrives for EUR 2,400. The order and delivery record support EUR 2,000. Finance needs to know what explains the extra EUR 400, who should check it, and which records to open.
We will build a review queue that answers those questions. It reads the invoice PDF, checks the purchasing records, and prepares a short report. A person handles the exception through the company’s normal process.
| Record | What it says | Where to check |
|---|---|---|
| Invoice INV1042 | 120 units × EUR 20 = EUR 2,400 | Invoice PDF, page 1, line 1 |
| Order PO778 | 100 units approved at EUR 20 | Purchasing system, order line 1 |
| Receipt GR552 | 100 units received | Purchasing system, receipt line 1 |
| Earlier bills against this line | 0 units | Posted invoice history |
All amounts in this example exclude tax, discounts and freight. The unit is each, and the currency is EUR. There is one invoice line and one order line. We will keep those limits visible rather than pretend this first version covers every invoice.
An ERP is the business system that stores orders, receipts and invoices. Comparing those three records is often called three-way matching. First check whether your ERP already does this well. If it does, use its result and improve document capture and the review screen around it.
Source: How Dynamics 365 compares invoice prices and received quantities ↗
Open each step to see its role. The comparison uses the actual numbers; a text-search result cannot change them.
Select a box to inspect its role. On a small screen, scroll the diagram sideways.The PDF is the supplier’s claim. Preserve the original file and the page that shows the line.
Read the complete diagram as text
- Invoice: 120 units: The PDF is the supplier’s claim. Preserve the original file and the page that shows the line.
- Order: 100 units: The approved order supplies the supplier ID, item, unit, quantity and EUR 20 price.
- Receipt: 100 units: The posted receipt records delivery. Missing evidence must stay missing, not become zero.
- Compare the records: Match exact IDs, confirm currency and units, subtract earlier billed quantities, then calculate with decimal arithmetic.
- Finance reviews EUR 400: Show all three sources and the reason for the hold. Do not change the invoice or approve payment.
- Invoice: 120 units → billed → Compare the records
- Order: 100 units → order → Compare the records
- Receipt: 100 units → received → Compare the records
- Compare the records → review → Finance reviews EUR 400
Step 1. Agree the first version with finance#
Start with one legal entity, one ERP, purchase-order invoices, and one invoice currency. Choose a small set of suppliers. Ask finance to label sample invoices as matched, missing receipt, wrong price, duplicate, or unclear. Keep some suppliers for the final test so the reader must handle layouts it has not seen before.
| Collect | Why we need it | Stop when |
|---|---|---|
| PDF and file version | Show exactly what the supplier sent | The file is unreadable or unsafe to process |
| Supplier ID, invoice number and date | Find the right account and check duplicates | Two suppliers could be the match |
| Order ID and line ID | Read the approved quantity and price | The order is missing, closed or unapproved |
| Posted receipts, returns and earlier bills | Work out the quantity still available to bill | The snapshot is incomplete or out of date |
| Written tolerances and policy dates | Know which differences need review | Two rules conflict or no applicable rule exists |
| Reviewer and permitted company scope | Send the exception to a person allowed to see it | The caller lacks access |
Our teaching rule uses zero tolerance and no returns. Your production adapter must read net accepted receipts, returns, earlier invoices and credit notes using the ERP’s rules. A service invoice may need proof of completed work instead of a goods receipt. Give those cases their own rule.
2. Build the workflow#
Step 2. Give each component one job#
| Choice for this example | Why it fits here | When to change it |
|---|---|---|
| Document Intelligence prebuilt-invoice as a starting reader | We need standard invoice fields and line items | Test a Content Understanding analyzer if varied layouts or custom fields cause too much correction |
| ERP read API and decimal calculations | Order IDs, quantities and prices need exact checks | Reuse the ERP’s matching result where available |
| PostgreSQL for cases, versions and review state | Keep the report and its evidence references together | Follow the company’s existing approved data platform |
| Keyword search, then optional pgvector | A policy may use different wording from the reviewer’s question | Keep keyword search alone if it finds the right clauses reliably |
| A saved workflow with a retry queue | The steps are known and a reviewer may return tomorrow | Add an agent only for a tested investigation that needs flexible follow-up |
| A short report template | The main result is already known from code | Add a language model only if it makes explanations more useful in testing |
These are starting choices, not benchmark winners. Microsoft’s current guide distinguishes standard invoice fields from highly varied documents. Test both reading routes on your suppliers. Measure the time spent correcting them, not just how tidy the JSON looks.
Source: Choose a document reader for the layouts you receive ↗
Step 3. Read the PDF and keep the evidence#
Save the original PDF under a generated file ID. Ask the reader for supplier name, invoice number, order reference, currency, quantity, unit price and line amount. Document Intelligence’s prebuilt-invoice model returns structured fields and line items. Its documented v4.0 API version is 2024-11-30.
Source: Invoice model and supported extraction fields ↗
Store three things together: the original text, the checked value, and its page or source region. For example, keep 20,00 beside the decimal value 20.00. If the quantity is unclear, send that field for review before doing the match. A high model confidence score does not prove the supplier billed correctly.
Step 4. Read the exact order and receipt#
Resolve the supplier against the approved supplier list. Then use the order ID and line ID to fetch the order, posted receipts and earlier bills. The server must obtain the allowed company from the signed-in session. It must not accept a company ID chosen by the PDF or the model.
Keep the source system, record ID, version and read time. Match the supplier, item, unit and currency before comparing amounts. If any of those checks fail, hold the case. Similar-looking supplier names or nearby invoice numbers are suggestions for a reviewer, never an automatic match.
Step 5. Calculate the difference#
The invoice’s own maths is correct: 120 × 20 = 2,400. The problem is that the available order and receipt support only 100 units. No units have been billed before. The extra 20 units are worth EUR 400 at the approved price.
| Check | Result in this example |
|---|---|
| Quantity available to bill | min(100 ordered, 100 received) − 0 already billed = 100 |
| Extra billed quantity | max(120 invoiced − 100 available, 0) = 20 units |
| Quantity difference | 20 × EUR 20 approved price = EUR 400 |
| Price difference | 120 × (EUR 20 invoice price − EUR 20 approved price) = EUR 0 |
| Outcome | Hold for review: EUR 400 quantity difference |
Check quantity and price separately. A lower price must not hide an unsupported quantity. Do not decide from a net total alone. Missing or stale receipts mean “cannot check delivery yet”; they do not justify treating the receipt as zero.
Use decimal arithmetic for money. Keep both the invoice values and the difference. Never silently replace 120 with 100 to make the invoice pass.
Step 6. Find the policy that explains the next action#
Now finance asks: “What do we do if some goods have not arrived?” The approved policy may call this “partial delivery” or “quantity variance.” This is where searching by meaning can help.
Keep two paths. Exact ERP records decide the numbers. Search finds the policy text that helps a reviewer act on the result. If the case already has a trusted rule ID, fetch that rule directly. Use search when the question or policy is not yet identified.
Search provides relevant text. It does not supply a replacement order, receipt or payment decision.
Select a box to inspect its role. On a small screen, scroll the diagram sideways.Invoice ID, exact order reference, quantity, currency, unit and source page.
Read the complete diagram as text
- Checked invoice fields: Invoice ID, exact order reference, quantity, currency, unit and source page.
- ERP lookup and checks: Use exact authorized records and decimal calculations to identify the exception.
- Policy question: Example: What should finance do when part of a delivery is missing?
- Approved policy search: Search only allowed versions that apply on the chosen business date; retain the clause and page.
- Short evidence report: Combine the calculated exception with a relevant policy passage and a named next owner.
- Checked invoice fields → exact IDs → ERP lookup and checks
- Policy question → words or meaning → Approved policy search
- ERP lookup and checks → checked numbers → Short evidence report
- Approved policy search → cited guidance → Short evidence report
| Decision here | Reason | Where to learn the details |
|---|---|---|
| Chunk policy by heading and clause | A rule and its exceptions need to stay together. Start with a roughly 400-token cap; repeat up to 50 tokens only when a long clause must continue. Test these settings. | The chunking lesson below |
| Keep invoice rows as fields | Splitting every 500 tokens could separate a price from its quantity. The comparison does not need invoice embeddings. | The PDF and chunking lessons |
| Try text-embedding-3-small for policy search | It is already used in this stack. It is a baseline to test against other embedding models, not a claim that it is best. | The embedding-model comparison below |
| Add pgvector to PostgreSQL if needed | This design already stores cases and policy metadata in PostgreSQL. One database is simpler to operate at this starting scale. | The pgvector lesson below |
Filter by company, permissions, policy status and effective date before choosing the passages to show. Decide with finance which date governs the rule: invoice date, posting date or review date. Save that choice. The newest document is not automatically the applicable one.
Start with keyword search. Add semantic search when your test questions miss relevant clauses with different wording. Combine their ranked results, then inspect a small set of passages. No useful passage means “policy not found”; conflicting rules go to the policy owner. Neither situation changes the calculated invoice difference.
Step 7. Save progress so the job can resume#
Use a durable queue: a saved list of jobs that survives a restart. In this stack, a worker can use Bull and Redis, while PostgreSQL stores the case state and completed steps. A retry must check those records before repeating work.
A direct ERP API is enough for these fixed steps. Use MCP only if the company already exposes the same narrow tools through it. An agent may help investigate an unusual case later, but it still needs a runtime that limits tools, retries and spend.
| Saved step | What happens next | On failure |
|---|---|---|
| Received | Scan the file and save its identity | Reject or quarantine the file |
| Fields ready | Check required fields and evidence | Ask for a correction |
| Records matched | Compare approved, received and billed amounts | Hold if an ID, unit, currency or source is unclear |
| Difference found | Fetch the applicable rule or search the policy | Keep the numbers; mark missing guidance |
| Ready for review | Show the report and assign the next owner | Keep the case queued for a person |
Use one intake key per company and file hash to stop repeat uploads creating repeat jobs. Check business duplicates separately using the supplier and invoice identity; two different PDFs can contain the same bill. Bound retries and service calls. Send exhausted jobs to a failed queue with a clear reason.
3. Review, test and roll out#
Step 8. Show a report people can use#
| On the review screen | Example |
|---|---|
| What is wrong? | The invoice bills 20 more units than the order and receipt support. |
| How much needs checking? | EUR 400, excluding tax. |
| What can I open? | Invoice page 1; order line 1; receipt line 1; the applicable policy clause. |
| Who acts next? | Purchasing checks for an approved amendment; receiving checks for a missing receipt. |
| What happens after that? | Finance checks the new evidence or requests a corrected invoice. |
| Has anything been paid? | No. This report is a review record. |
Put the invoice page beside the checked fields. Highlight the source region only when extraction actually supplied it. Let reviewers correct a field with a reason; keep the old value and file version in the history. Record who resolved the exception and what evidence changed.
If you later add an ERP write, show the exact proposed change first. Recheck permission and source versions when the reviewer approves. A changed order invalidates the old proposal. Use a separate write adapter and a unique action key so a retry cannot submit the same change twice.
Put controls at each hand-off#
| Place | Control to implement |
|---|---|
| Upload | Allow expected file types; check content, size and page limits; scan files; parse in an isolated worker with no ERP credentials. |
| Files and records | Use company-scoped storage and expiring source links. Recheck access when someone opens an attachment. |
| Models and search | Treat PDF and policy text as untrusted data. They cannot grant permissions, change tools or approve payments. |
| ERP adapter | Use narrow read permissions. Keep credentials on the server and restrict which hosts it can call. |
| Logs and retention | Log IDs and failure codes by default. Set retention with finance; remove expired copies, search chunks and vectors together. |
| Provider calls | Confirm the approved processing location, retention and data agreement before sending real invoices. |
Source: File-upload controls ↗
Source: Why document text must not control tools or permissions ↗
Go deeper · optional · freeWhat happens when a PDF tells the AI to ignore the rules? ↗Prompt injection and trust boundariesStep 9. Test the failures before expanding#
| Test invoice or question | Expected behavior |
|---|---|
| 120 billed; 100 ordered and received | Hold the EUR 400 quantity difference. |
| Missing or stale receipt | Hold; delivery support is unknown. |
| Some quantity already billed | Subtract it before checking the current invoice. |
| Different supplier, currency or unit | Stop; do not compare incompatible records. |
| Duplicate invoice | Flag the duplicate; do not create another payment proposal. |
| Discounted price and excess quantity | Show both checks; do not let one cancel the other. |
| Old or wrong-company policy ranks first | Do not show it; use an authorized applicable passage or abstain. |
| Instruction hidden in the PDF | Treat it as document content; permissions remain unchanged. |
| Worker restarts or ERP times out | Resume or fail clearly without a repeated action. |
Measure missed exceptions, false alarms, wrong field values, useful policy passages and reviewer correction time. Include provider calls, retries and human review in the cost. Compare with the current process on the same invoices. Set the acceptance limits with finance before reading the results.
Run the downloadable example#
Download the invoice exception lab. Unzip it, open a terminal in the extracted folder, and use Python 3.11 or later. The checker needs only Python’s standard library.
python invoice_lab.py --input fixtures/base.json
python -m unittest discover -s tests -vOpen the synthetic PDF, compare it with fixtures/base.json, and run the command. Read the reason codes and source references in the output. Then try the supplied missing-receipt and partial-billing examples. The README names exactly which parts run locally and which connections you still need to build.
The lab treats one fixed order-line ID as the item match. The live ERP adapter must also verify the item itself. Its company selector and saved timestamps are teaching inputs, not a login system or a live connection. The example selects its rule using the stated review date.
Start with a small review queue#
First run beside the existing process. Let finance compare the reports without changing ERP records. Fix recurring field and matching errors. Add policy search only if it helps reviewers answer questions they could not resolve quickly. Expand suppliers and invoice types one group at a time.
The useful result is simple: a person can see what differs, which records prove it, and what to do next. The linked articles explain the technology when the reader wants that detail.