Catching an invoice that doesn’t add up
Finance apps: worked example
When an app reads an invoice, it may miss lines or misread numbers. This check compares the sum of the lines read with the printed total, flagging differences for human review.
All names and numbers on this page are made up. The screenshot shows the real app; the example below was written for this portfolio.
In the real app
How the check works
1. Add up what was read
| Item 1 | RM60.00 |
|---|---|
| Item 2 | RM40.00 |
| Total | RM100.00 |
| Item 1 | RM60.00 |
|---|---|
| Item 2 | RM37.00 (differs from the invoice) |
| Adds up to | RM97.00 |
RM3.00 short: needs checking
The gap needs human review: the app may have misread a number, or the invoice itself may be wrong.
2. Give every invoice a clear status
| Invoice | Printed total | Lines add up to | Status |
|---|---|---|---|
| A | RM100.00 | RM97.00 | Needs checking |
| B | RM150.00 | RM150.00 | Totals match |
| C | RM50.00 | Nothing read | Lines missing |
Invoice C has missing lines, not RM0. With nothing read, it must never appear to have passed.
3. A person checks and fixes it
The reviewer checks the invoice and corrects item 2 from RM37.00 to RM40.00.
| Item 1 | RM60.00 |
|---|---|
| Item 2 | RM40.00 |
| Total | RM100.00 |
| Item 1 | RM60.00 |
|---|---|
| Item 2 | RM40.00 (corrected) |
| Adds up to | RM100.00 |
Totals match. The invoice can move on to approval.
Matching totals confirm arithmetic, not approval. Invoice approval remains a separate step.
For technical reviewers
Sample data, SQL and eight runnable tests reproduce this example.
Show the data model, SQL and tests
- Invoice: one row per invoice ID, with its declared total.
- Invoice line: one row per invoice ID and line number, linked by a foreign key. The composite key rejects repeated line keys.
- Amounts: integer cents keep arithmetic exact. One currency, non-negative amounts only; tax, discounts and refunds are intentionally excluded.
Sum lines by invoice before joining declared totals. A left join preserves invoices with missing lines. The duplicate-key check cannot catch a document reimported under a different invoice ID.
WITH line_totals AS (
SELECT invoice_id,
SUM(amount_cents) AS parsed_cents
FROM invoice_line
GROUP BY invoice_id
)
SELECT i.invoice_id, i.declared_cents,
t.parsed_cents,
t.parsed_cents - i.declared_cents
AS difference_cents
FROM invoice AS i
LEFT JOIN line_totals AS t
ON t.invoice_id = i.invoice_id;
The full SQL also returns MISSING_LINES, TOTALS_MATCH or REVIEW_DIFFERENCE. Tests cover mismatches, matching totals, missing lines, reviewed corrections, duplicate line keys, orphan lines, fractional cents, and zero amounts versus missing data.
Download SQL and sample data Download Python tests
Save both files together and run the command with Python 3; only the standard library is needed.
python check_invoice_example.py