Flow
Where each fake zero got stopped
Each change blocks one kind of guessed zero.
1Starter case for new products
No stock and no sales no longer forces the order to zero. The row gets one case and a review flag.
2Tag every shelf count
Counted or filled in. A filled-in count blocks the row; a counted zero passes.
3Tag every sales read
Missing, partial, stale or unknown coverage blocks the row. A full read with no sales passes.
4Same check in two places
The order screen and the cart rebuild both use it, and skipped rows keep their reason.
5Start the window at midnight
The first day's sales bucket is no longer left out.
Flow
Can this missing sales row count as no sales?
Any no means the row stays blocked.
1Is a sale on record?
If yes, use it. If a sale exists but no inventory record matches, the row stays blocked.
2Was the whole window kept?
If part of the window is missing, the row stays blocked.
3Is the product identified?
If the system cannot tell which product it is, the row stays blocked.
4Is there an inventory record?
If not, the row stays blocked.
5All yes
Count it as no sales for readiness only. Do not save a zero into sales history.
The reorder report for a vendor I'd just set up came out at zero units. The math said: if a product sold nothing and has nothing on the shelf, order nothing. For a product that has never been stocked, that rule can never let it in.
I took that rule out. A new product with no stock and no sales now gets one starter case, flagged for review. But the first version also let those rows count as ready to order, with a shelf count of zero that nobody had taken. That's what I spent the rest of the day on.
Two zeros that look the same
When the inventory read had no row for a product at a store, the code wrote zero. A zero from a real count of an empty shelf is the best reason to reorder. A zero because no row came back means nobody knows. On the screen they look the same, and the ready check only saw the number.
So each number now carries a tag saying where it came from: counted, or filled in. The ready check looks at the tag first. A filled-in shelf count blocks the row, with a reason. A counted zero still passes and still gets its case. I made sure there was a test for that second case, because blocking every zero would break the main reason the screen exists.
The same gap in sales
Sales had the same problem. No sales row meant "sold zero", which looks like a product that doesn't move, and that drives the timing math. Now the ready check refuses sales that are missing, only partly covered, stale, or of unknown coverage. A read that covered the whole window and found no sales still passes.
The same check runs in two places: the order screen and the cart rebuild. When the rebuild skips a row, it records why. The screen now also shows sales per day, days of stock left, and when to reorder.
The open question for me is that starter case. A brand-new product has no sales. Unless the sales side can vouch for a full, empty window, my reading is that the row can be previewed but never becomes ready. That may be right, or it may be the old loop with a label on it.
The missing first day
Sales are saved as one bucket per day, stamped at midnight. The window start was "now minus N days", which kept the current hour, so the first day's bucket fell just outside. The start now snaps back to midnight.
Most products only lost one day of sales. A product whose only sale was on that first day lost everything and would now show as blocked. I haven't checked whether any real product hit that. A test covers exactly that case.
A zero that does count
The allocation work for one vendor's lineup had the opposite case. Some rows had no sales at all, but the full window was there, the product was clearly identified, and there was an inventory record. The system looked everywhere and found nothing, so that silence means something.
A narrow policy now counts those rows as no-sale evidence when deciding readiness, after separate define, review and apply steps. It never saves a zero as a sales fact, so it can be thrown away later if the window turns out incomplete. A handful of rows moved to ready.
Rows where a sale was seen but no inventory record matches are still blocked. There are more of those than rows that moved, and this policy doesn't touch them.