A drawer that balances and is still short
Your cash-up screen says the till took £685 and there is £685 in the drawer. It took £849. Nothing on the screen is red, the count gets signed off, and the shortfall is now filed as proof that nothing went missing.
Here is a way to lose £164.40 on a Saturday with nobody stealing anything and nobody making a mistake.
The till takes 620 cash orders. Your cash-up screen works out what should be in the drawer, and it does that by fetching the cash orders for the period and adding them up. Except the query that fetches them stops at 500 rows — a limit somebody put there years ago so the list underneath would not take a minute to draw. The list was the point. The total was an afterthought that quietly came off the same fetch.
So the screen says £685.00 expected. The drawer holds £685.00. Balanced. Somebody signs it off, and that signature goes into a record designed to be permanent — the record whose entire job is to say that on this date, at this till, the money was all there.
Why this shape is nastier than a normal bug
A wrong number that looks wrong gets found on day one. This one is plausible. £685 is a perfectly reasonable Saturday. Nothing is red, nothing is missing, no error appears. And it gets quieter as you get busier: at 300 orders a day the figure is exactly right, so the screen earns your trust for months before the first day it lies to you. The busiest days — the ones where a shortfall matters most and where somebody light-fingered has the most cover — are the only days it is wrong.
It also runs the wrong way round from what you would expect. A capped total is always lower than the truth, so it can only ever make a short drawer look right. It cannot make an honest drawer look short. It is a bug that only ever exonerates.
What to ask your own system
You do not need to read anyone's code to test for this. Take a busy day, one till, and count the cash orders on it yourself from the orders list. Then look at what the cash-up screen says the expected figure was built from. If the screen shows you a list of the orders behind the number, count that list. If it stops at a round figure — 100, 200, 500 — ask whether the total came off that same list.
Any honest answer to that question has two parts: the number of rows shown, and the number of rows there actually were. If a screen cannot tell you the second one, it does not know.
What we changed
Two things, and the second matters more than the first. The expected figure is now read in pages until there are no pages left, so the cap is gone. And the screen now prints the difference out loud: this list shows 500 of 620 orders. The list is still capped, because a person is not going to read 620 rows and drawing them all would make the page crawl. A cap on a list somebody reads is fine. A cap on a sum is a lie. The fix is not removing every limit — it is knowing which is which, and saying so.
We also found the same screen had a second, smaller problem worth mentioning because it is so easy to write: the Today button built its window from today's date in UTC and then stuck local midnight on the end. Between midnight and 1am British Summer Time, "Today" quietly included the whole of yesterday. If your venue cashes up after midnight — and most late bars do — that is worth five minutes of your time to check.
The cash side of what we build is described on the register page, and what a signed count is for is on the stock and reporting page.