Products missing from the app, and it looked random
A dealer at one depot could order a product and a dealer at the next could not. 1,416 pack-rows were hidden across 26 depots; one stock upload had delisted 185.
A handwritten list arrives from the client: thirteen products, “add these to the app”. Then a group leader reports that one product is visible to his dealers in one city and missing for his dealer in the next. Nobody at head office can say why a dealer at one depot sees about 600 packs and a dealer at another sees 150. It looks random, and random is the worst kind of problem to have, because nobody can promise a dealer anything.
The company is a crop-nutrition manufacturer, about 5,500 dealers ordering through 26 depots. When we checked the handwritten list against production, twelve of the thirteen products already existed and fifteen of the seventeen pack sizes were already live. The real gaps were two missing pack sizes, one product somebody had switched to inactive, and one depot that carried 134 of 598 packs.
The wider count was the cost. Across 26 depots, 1,416 pack-rows were hidden from dealers: 967 switched off, 449 never set up at all. At one depot a single stock upload had delisted 185 packs in one statement.
What was actually going on
For a product to appear in a dealer’s app it has to pass four gates: the product is active, it has a main photo, it has a pack size, and there is a live row saying that dealer’s depot carries that pack. Fail any one and the product is simply absent, with no message anywhere.
The forensic pass cross-referenced the audit log against the timestamps on the depot rows and found two different things going on, not one. At two depots, rows had been switched off one at a time by a person over 14 and 41 minutes. That is a depot admin deciding what the depot stocks, which is legitimate. At two others, hundreds of rows shared one identical microsecond: a single write. The clearest was a stock upload that set the depot’s catalogue to whatever was in the spreadsheet and delisted everything else, 185 packs, every one left at zero stock. All four episodes were depot admins working through the portal. No tampering, no head-office involvement.
One depot was different again. It had 449 packs that had never been set up, because a backfill run in November had never finished. The September complaint about one product was this depot: the product was live at 25 of 26 depots and had no row of any kind at the 26th. The group leader held dealers at two depots, which is why it looked intermittent.
Two code faults sat under all of this. A flag was written as “the value, or true”, so it could never store false. And the operation that mass-writes depot rows was a read request, which the audit log excludes by design.
What we changed
The gaps in the handwritten list were closed by a script rather than by hand: dry-run by default, one transaction, and it checks all four gates from inside the transaction and rolls back unless every one of the seventeen packs is visible at all 26 depots. Run again, it does nothing, which is how we know it is safe to run again.
Then a matrix for the client: 600 packs by 26 depots, each cell Visible, Switched off, Not set up or Blocked, computed from the same four gates the app uses. It surfaced three packs hidden at all 26 depots, two of them because nobody had uploaded a photo.
The stock screen in the portal was rebuilt as an assortment picker. It had read from the depot’s own rows, so it could only ever show packs the depot already had a row for; “never set up” was invisible everywhere in the portal, and the depot with 447 missing packs looked perfectly healthy. It now starts from the product master and shows all three states, and switching a pack on creates the row if there is none. Delete was removed, because it hid the product from the app while leaving it on screen, a silent delisting with no way to notice. Two scope holes went with it: a depot admin could act on another depot by asking.
We proved it through the real dealer login. Switching off one pack size left the product visible, because the other size was still carried. Switching off both made it vanish and the dealer’s count dropped from 82 to 81. Switching both back on restored 82.
What it did not fix
The assortment itself. That depot still carries 187 live packs against 632 to 634 everywhere else, with 196 active products holding no row there at all, and the same complaint will recur until the client marks up the whole column. The packs switched off by depot admins were left exactly as they were, because a depot deciding what it stocks is a decision, not a fault. And nothing yet prevents a duplicate depot-pack row, which a careless insert could put through the app’s join twice.
The mechanism
Visibility was an inner join across four tables. The screen meant to manage it read from the fourth table, so a missing row was not a state the screen could show. Starting from the product master with an outer join made “no row” a visible cell, and turning the toggle into an upsert let that cell be changed.
Where this ends up
Sazinga Field treats what a depot carries as a decision somebody can see and review, rather than the by-product of the last spreadsheet that was uploaded.