v1.21.0: pre-allocation and POS phone orders
"Pre-allocation and POS phone orders" release. Two threads.
Admin-created and marketplace-inbound sales orders now reserve inventory at create time instead of leaving stock claimable until a picker batches the order, and the picking flow was taught to handle a line that arrives already reserved. On top of that reservation work, POS checkout gains a phone-order path: a phoned-in order is paid at the register but enters the warehouse pick queue with a real structured ship-to, reserving stock rather than decrementing it.
Mobile. Zero mobile/ diffs on this release. The current mobile build (version 1.19.0, versionCode 9) remains current; no new APK for v1.21.0.
Added
- Inventory pre-allocation for admin SOs (#396):
POST /admin/sales-ordersnow follows each line insert with a greedy reservation walk -- it locks the available inventory rows for the line's(item_id, warehouse_id)FOR UPDATE, takes from the fullest bin first, and bumpsquantity_allocateduntil the line is satisfied or stock is exhausted. The order no longer sits OPEN withquantity_allocated=0until a picker batches it, closing the window where POS sales and concurrent inbound orders could claim the same on-hand pool. Partial reservation is allowed: a short warehouse still creates the SO and the picker handles the short at fulfillment. A manualPATCH /admin/sales-orders/<so_id>/lines/<so_line_id>/allocationknob (signed delta, clamped to[quantity_picked, quantity_ordered]) is surfaced as a minus/plus stepper on the Allocated cell of the SO line table. - POS phone orders (#397): POS checkout gains an
is_phone_orderflag (default false; the counter-sale contract is preserved). A phone order creates the SO atstatus=OPENwithshipped_atNULL and reserves each line viaquantity_allocatedinstead of decrementingquantity_on_hand, so it enters the existing pick queue and the unit stays in its bin until the picker removes it. Checkout accepts an optional customer (customer_name/customer_phone/customer_email) and a structuredshipping_addressobject; on a phone order the structured fields persist to thesales_orders.shipping_address_*columns so the picking ticket renders a real label without a cross-system lookup, while the flatship_addressstays as the floor-screen mirror.order_originis now wire-driven (the POS sends "POS" / "Phone Order" verbatim; older clients that omit it land as NULL). POS sales-order numbers drop the redundant prefix:SO-POS-<n>becomesPOS-<n>andSO-POS-REF-<n>becomesPOS-REF-<n>.
Changed
- Pickable pre-reserved lines (#396): a sales-order line can now reach picking already fully reserved -- from pre-allocation, POS phone-order checkout, or a stranded prior allocation. The coverage and allocation passes keyed on
quantity_ordered > quantity_allocated, so such a line produced zero pick_tasks and stranded every order in the batch. The picking flow now normalizes each line's standing reservation to its picked floor before planning (releasingquantity_allocated - quantity_pickedback to inventory inside the one transaction), then re-reserves and tasks it like any fresh line. Applies tocreate_pick_batchandwave_create.
No new migrations.