v1.35.0 -- Inventory rows retained at zero
Inventory rows are retained at zero instead of being deleted when a bin empties. Shipped as a single-change release, because it alters what the inventory snapshot contains.
Fixed
Inventory rows retained at zero when a bin empties (#448). When stock fully left a bin, five mutation paths deleted the inventory row outright while picking, receiving reversal, and cycle-count approval kept it at 0. A (item, bin) pair could silently vanish from the snapshot, so downstream consumers could not distinguish "went to zero" from "never tracked", and stale mirrors accumulated phantom on-hand.
Zero is now a first-class state. A shared set_inventory_quantity() helper always updates in place, and the bin transfer, inter-warehouse transfer, direct adjustment, CSV adjustment import, and inbound inventory-set paths all route through it. Rows are only removed when their item or bin is itself deleted. The snapshot endpoint already emitted rows unfiltered, so every pair now stays reconcilable to its true on-hand, including 0.
Behavior change
The inventory snapshot now contains (item, bin) rows at quantity_on_hand = 0 that it previously omitted.
- A consumer that treated "row absent" as "no stock" still reads correctly.
- A consumer that treated row count as SKU-locations-in-use will see higher counts.
Distinguishing absence from zero is the point of the change.
Upgrade note
The fix is forward-only. A deployment that already lost rows to the delete paths keeps those gaps until it reconciles its mirror against a fresh snapshot. Nothing in this release reconstructs history.
Migrations
None.
Mobile
Zero mobile/ diffs. The current build (version 1.33.0, versionCode 12) remains current; no new APK for v1.35.0.
Verification
Merged-main suites: api 2903 passed / 9 skipped (including a new zero-retention regression suite covering all five paths), admin 134 passed, mobile 55 passed.