Commit graph

1 commit

Author SHA1 Message Date
ee28275bf3 feat(ops): atm-reconcile — check cassette ledgers against recorded history
The cassettes table is a running total, so it can be re-derived: an
absolute truth point (a recount, or an empty) plus the refills and
dispenses since. A derived count that disagrees with the stored one is
evidence of something the ledger never saw.

Reconciliation deliberately refuses to start from a refill. A refill is
a delta, and applying deltas on top of a wrong number just carries the
error forward — which is how sintra's 20-EUR bay ran 10 notes high for
weeks while its 50-EUR bay, zeroed by an `empty` before refilling,
reconciled exactly. A bay with no baseline is reported as
unreconcilable rather than silently assumed good.

Also surfaces the two things that make a count untrustworthy: the
counts-uncertain flag, and any transaction still sitting in
dispense_error / partial.

The SQL uses scalar subqueries rather than joins on purpose — joining
transaction_bills to cassettes fans out across bays, and a LEFT JOIN
whose rows are all excluded by the baseline cutoff collapses to NULL
and poisons the arithmetic downstream (the first draft read "expected:
blank" for exactly that reason).

Exits non-zero on any gap or missing baseline so it can be run as a
check after a test session.

Refs #122
2026-10-09 09:40:41 +02:00