ELAI S.r.l.

AI-read invoices: how to keep wrong amounts out of your business software

A reproducible example shows which invoice errors checks catch, which escape, and how to count review and repair work before automating.

AI-read invoices: how to keep wrong amounts out of your business software

The invoice looks correctly read. Can we import it?

A small office receives invoices from different suppliers. Copying amounts and line items into business software takes care; an AI assistant promises ready-to-use data. But a tidy table does not complete the job: a plausible total may belong to the wrong document, a line may disappear, and the supplier may be confused with the recipient. This article helps people managing administration or document automation decide which checks should stand between automatic reading and posting. The practical question is: what do numerical checks prove, and what can they never prove?

The short answer is that arithmetic consistency catches some errors but does not establish fidelity to the document. We demonstrate this with twelve synthetic records, an executed validator, and a work-time simulation. We did not run OCR, query a model, process real invoices, or measure workers. The results belong to the stated example, do not estimate any product' s reliability, and do not constitute tax validation. The contribution is a verifiable method for designing the handoff to business software.

First choice: do we need to read a page at all?

If a usable structured file arrives, with identified fields and a supported format, compare a deterministic importer with the AI route. Deterministic means explicit rules transform the same inputs in the same way, without asking a model to guess where the total is. It does not mean the file is truthful or error-free: identity, authorization, and reconciliation remain. It means avoiding visual reconstruction when information is already structured. Assess this alternative before buying image-recognition capabilities.

For scans and unstructured PDFs, distinguish three jobs. OCR, optical character recognition, attempts to read characters and words. Extraction assigns those words to fields such as supplier and amount. Validation checks whether fields satisfy constraints and match the source. A number can be transcribed correctly but assigned to the wrong field. Separating these stages makes errors diagnosable: changing the model does not automatically fix a rule that misinterprets a decimal comma.

An example we can reconstruct from beginning to end

The ideal document has two net line amounts of 120 and 80 euros, net total 200 euros, stated tax 40 euros, total 240 euros, supplier SUP-A, and currency EUR. These are invented experimental data. We do not derive tax from a rate or recommend tax treatment. We exclude document-level discounts and charges, prepayments, special rounding, other currencies, and credit notes. The scope explains the logic; it does not reduce a real process to four numbers. A document outside scope should be flagged, not forced into the schema.

The code stores amounts as integer cents: 120 euros becomes 12000. The sums are exact without arbitrary tolerances for binary floating-point values. This does not authorize silently rounding every price to two decimals: unit prices, quantities, and rounding may require different rules. Here the lines are already final net amounts in cents. A complete system preserves the original value and applies an explicit decimal policy. Python' s Decimal documentation distinguishes representation from rounding.

Making the numbers add up: necessary but insufficient

What question does the formula answer? If extracted lines cover the document and are assigned correctly, their sum must equal the net; net and tax must reconstruct the total. l_i is line i' s amount, N the net, T tax, and G the total. All are cents; m is dimensionless line count. We are not asking AI to judge itself: we apply explicit external constraints.

N = sum(l_i, i=1,...,m) G = N + T r_line = N - sum(l_i) r_total = G - N - T

Initially 12000 + 8000 = 20000 and 20000 + 4000 = 24000: the residuals are zero. If extraction changes only G to 25000, the second residual is 1000 cents, ten euros. The fields cannot all be correct under the stated assumptions, but we do not know which to correct. Automatically changing the total to match the sum erases evidence and may alter a correctly read total when tax is wrong. A residual calls for source inspection, not repair of a particular column.

Peppol BR-CO-10 and BR-CO-15 document relationships between line sums and totals. Our validator uses their arithmetic logic within a simplified subset: it does not implement the standard, XML, all rules, or rounding. Tax-inclusive total is not always outstanding amount payable. A real validator needs a defined document family and applicable rules: this example does not demonstrate Peppol or Italian invoicing compliance.

Every sum is right, but the record is wrong

Construct an error neither equation detects. The extractor returns lines of 132 and 88 euros, net 220, tax 44, and total 264: every amount is multiplied by 1.1. The sums reconcile but do not match the 240-euro source. This counterexample refutes “if the totals reconcile, we can import.” No frequency estimate is needed to show the logical implication is false. Real frequency, however, requires representative data and reliable labels.

A second case leaves amounts unchanged and changes SUP-A to SUP-B. The check still approves. Document number, date, and order reference face the same limitation: constraints cannot protect fields they never examine. A master-data check detects a nonexistent code but cannot distinguish two existing suppliers. That needs reconciliation between identity and document, retaining each field' s source page and crop. Each check covers a specific error; a reassuring name does not make it universal.

Twelve cases to test an import policy

The dataset has six correct copies of the base record, C1–C6, and six incorrect records, E1–E6. E1 changes only total, E2 net, and E3 tax: they violate at least one sum. E4 is the coherent transformation described above; E5 and E6 change only supplier. These are designed challenges, not a random sample of business invoices. Having half the records wrong is an educational choice, not a real-world estimate. There is no seed: the dataset is deterministic and fully included in the code.

Assign a synthetic score to each record. C1–C6 receive 0.99, 0.97, 0.94, 0.85, 0.98, and 0.92. E1–E6 receive 0.99, 0.98, 0.96, 0.99, 0.97, and 0.90. The score is not model output or a calibrated probability. It tests the practice of accepting data when checks pass and the system appears confident enough. Threshold 0.95 is an example parameter, not a production recommendation.

Formalize the decision to count it. V_i is one if record i passes checks, zero otherwise; s_i is score and tau threshold. A_i indicates simulated acceptance. Y_i is one only if every field matches the reference, known in the experiment but unavailable to the system during use. V and Y differ: V is observable without knowing the answer; Y needs independent truth. The next formula separates how much we automate from how many errors escape.

A_i = 1 if (V_i = 1 and s_i >= tau), else 0 coverage = sum(A_i) / n error_accepted = sum(A_i * (1-Y_i)) / sum(A_i)

At threshold 0.95, C1, C2, C5, E4, and E5 are accepted: five records. Seven go to review. Coverage is 5/12, about 41.7%; two of five accepted records are wrong, or 40%. The second percentage does not divide by twelve: it measures risk in the subset avoiding review. When nothing is accepted, conditional risk is undefined, not zero, because that subset does not exist: the code records None. Importing nothing is not equivalent to importing correctly.

ThresholdAcceptedReviewAccepted errorsAccepted risk
093333.3%
0.984337.5%
0.9557240.0%
0.9839133.3%
1.010120—

The table compares five thresholds on the same set. Raising them does not necessarily eliminate the most serious errors: E4, coherent but different from the source, scores 0.99. Conditional error can rise as coverage falls because correct lower-scoring records are excluded. This does not prove thresholds useless: it shows why their relationship with errors must be tested in the target domain. A number is not a probability simply because it lies between zero and one.

Measure savings after corrections

Assume two minutes to enter a document manually, ten seconds of active work to initiate each extraction, one minute to check each referred document, and five minutes to repair each accepted error. These are not measured durations. Review includes correction and entry of referred cases; the five minutes are later work only on escaped errors. Assume review corrects everything and all accepted errors are eventually found. Both assumptions can fail: the time model does not guarantee quality.

To assess work reduction, add previously hidden activities. n is document count, a automatically accepted count, and e errors among them. t_0, t_r, t_e, and t_m are seconds for initiation, review, repair, and manual entry. W is total active work. We do not calculate customer waiting or service duration: parallelism can shorten elapsed time without reducing human attention, and vice versa. The final inequality states how much repair work can be absorbed before losing the advantage.

W = n*t_0 + (n-a)*t_r + e*t_e W_manual = n*t_m W < W_manual iff e*t_e < n*(t_m-t_0) - (n-a)*t_r

At threshold 0.95, initial work is 12×10 + 7×60 = 540 seconds, nine minutes, seemingly less than twenty-four manual minutes. But two errors add 2×300 = 600 seconds: total 1140 seconds, nineteen minutes. Five hypothetical minutes remain. If each repair took more than 450 seconds, the advantage would vanish; at 450 the routes tie. These are calculations under assumptions, not promised savings. Software, setup, retention, error consequences, and follow-up checks are omitted: a complete economic decision must add them without double-counting activities.

Twelve synthetic records: acceptances and errors, active work before and after repairs. Assumed durations; no model or worker measured.
Twelve synthetic records: acceptances and errors, active work before and after repairs. Assumed durations; no model or worker measured.

On the left, examine acceptance and errors together, not just the tallest bar. On the right, compare work before repairs, including repairs, and the manual reference. Threshold 1.01 excludes every record: the model yields fourteen minutes, two for initiation and twelve for review assumed faster than manual entry. This apparent advantage is not evidence of AI merit: it follows from assuming one minute to check prefilled data. Measure that difference before justifying investment.

What research measures, and what to measure in the office

DocILE, by Šimsa and colleagues, arXiv v2 dated 3 May 2023, separates localization from line-item grouping. Primary metrics do not equal posting correctness. It includes layouts unseen during training. This is a historical methodological reference, not a current leaderboard. We did not reproduce its models or results.

The actual process needs a separate evaluation. Freeze authorized documents, define a verifiable reference, and separate development from evaluation by supplier, layout, and time to avoid testing only forms used to choose rules. Field exactness, line matching, whole-document correctness, coverage, and accepted errors answer different questions. Ten correct fields out of eleven can still make a document unusable if the eleventh identifies the payment recipient. Do not infer whole-document correctness by multiplying field accuracies without justifying error independence.

Time needs the same care. Compare equivalent tasks and record initiation, source inspection, correction, posting, and rework. If workers check only highlighted fields, unhighlighted errors may never enter the count: independently audit some accepted records. The experiment assumes known truth; a real project must establish and fund it. We did not test people or present assumed times as user observations. Manual review can also fail: a comparison assuming perfect review must say so, not hide mistakes behind that assumption.

From validator to business software: a controllable path

The design consequence is to separate proposal from posting. Retain original, proposed fields, source coordinates, extractor version, normalization, and constraint outcomes so the decision can be reconstructed. Review should correct data without rewriting the original and show why a check fails. When preparing import, retain operation identifier and outcome state: a timeout does not prove the business system has not already posted. We do not implement integration or post real data here; we identify the boundary evaluation must cover.

Initially limiting scope to a document family and using an explicit exception queue makes the process inspectable. “Needs review” is insufficient: show which lines do not reconcile, which field is missing, and which page to check. An out-of-schema document must not become zeros just to pass checks. Approval remains a traceable decision, not automatic model certification. This does not establish that EL-AI already has an accounting product: we analyze a possible application architecture without announcing a completed service.

Read the code without losing the question

make creates records and retains comparison with the reference. coherent checks integers, currency, and equalities, not identity or legal rules. policy applies threshold, counts acceptance and errors against known truth, and sums assumed times. Assertions test detected errors and coherent escaped errors, fixing 5/7/2 and 540/1140 seconds at 0.95. Sum checking scales linearly with line count. That does not represent visual-model complexity or latency: none was run. Reproduce by running experiment.py then plot.py; results and versions are in the archive.

What we can decide, and what remains unproven

Within the stated scope, arithmetic checks refer inconsistent records but cannot alone authorize import. The 264-euro counterexample proves this without depending on a model. Correction cost can change the decision: nine initial minutes become nineteen under our assumptions; more than seven and a half repair minutes per error erase the advantage over manual entry. Twelve constructed cases cannot estimate how many real invoices can be automated, choose a universal threshold, or promise economic return.

The useful decision starts from available format, separates extraction from authorization, measures accepted-record correctness, and counts all work needed for usable data. If structured import meets the need, AI may be unnecessary. If variable pages require interpretation, the model must join a verifiable process, not replace checks with reassuring scores. The next necessary experiment is independent evaluation on the office' s actual documents and exceptions, with measured manual comparison and explicit stopping criteria, not a more impressive demo.

Sources and reproducible materials

Peppol BIS Billing 3.0 — BR-CO-10, May 2026 release.

Peppol BIS Billing 3.0 — BR-CO-15, May 2026 release.

Šimsa et al. — DocILE, arXiv:2302.05658v2, 3 May 2023; sections 3–5.

Python — decimal; documentation and runtime versions may differ.

import json,sys,copy
from pathlib import Path
BASE=dict(lines=[12000,8000],net=20000,tax=4000,total=24000,supplier="SUP-A",currency="EUR")
def make(name,score,change=None):
 x=copy.deepcopy(BASE)
 if change:x.update(change)
 return dict(id=name,score=score,data=x,correct=x==BASE)
rows=[make("C1",.99),make("C2",.97),make("C3",.94),make("C4",.85),make("C5",.98),make("C6",.92),
 make("E1",.99,dict(total=25000)),make("E2",.98,dict(net=21000)),make("E3",.96,dict(tax=4100)),
 make("E4",.99,dict(lines=[13200,8800],net=22000,tax=4400,total=26400)),
 make("E5",.97,dict(supplier="SUP-B")),make("E6",.90,dict(supplier="SUP-C"))]
def coherent(x):
 return (all(type(v) is int for v in x['lines']+[x['net'],x['tax'],x['total']])
  and sum(x['lines'])==x['net'] and x['net']+x['tax']==x['total'] and x['currency']=="EUR")
def policy(threshold):
 auto=[r for r in rows if coherent(r['data']) and r['score']>=threshold]
 errors=sum(not r['correct'] for r in auto);n=len(rows);accepted=len(auto)
 return dict(threshold=threshold,accepted=accepted,review=n-accepted,errors=errors,
  coverage=accepted/n,conditionalError=errors/accepted if accepted else None,
  activeSeconds=n*10+(n-accepted)*60,withRepairSeconds=n*10+(n-accepted)*60+errors*300)
for r in rows:r['coherent']=coherent(r['data'])
assert sum(r['correct'] for r in rows)==6
assert sum(not r['coherent'] for r in rows)==3
assert coherent(rows[9]['data']) and not rows[9]['correct']
assert coherent(rows[10]['data']) and not rows[10]['correct']
policies=[policy(t) for t in [0,.90,.95,.98,1.01]]
r=policy(.95)
assert (r['accepted'],r['review'],r['errors'],r['activeSeconds'],r['withRepairSeconds'])==(5,7,2,540,1140)
result=dict(python=sys.version.split()[0],seed=None,scope='Deterministic synthetic extracted records; no OCR, model, invoices, people or accounting system tested',currency='EUR',amountUnit='integer cents',baselineSeconds=12*120,assumptions=dict(intakeSeconds=10,reviewSeconds=60,repairSeconds=300,manualSeconds=120),records=rows,policies=policies)
Path(__file__).with_name('results.json').write_text(json.dumps(result,indent=2),encoding='utf8')
print(json.dumps(r))

Code, data, and instructions · JSON. Educational calculations executed with Python 3.14.0; figures with Matplotlib 3.11.2. AI-assisted analysis, without claiming peer review or human review. Original illustrative ImageGen cover: it does not document EL-AI people, premises, or installations. Sources accessed 26 October 2026.