Context for computing per-item stock deficits (§5, full variant).
Combines three existing signals — current on-hand stock
(StockLedger.stock_map/0), how much of that stock is reserved by posted
internal orders (reserved_by_item/0), and the configured per-item
minimum (MinStockSettings.min_stock_map/0) — into "how much is actually
available" and "which items have fallen below their minimum".
Wave-1 limitations (see dev_docs/DEVELOPMENT_PLAN.md):
- The minimum stock threshold is global per item, not per
{item_uuid, location_uuid}pair —available_by_item/0sums on-hand quantity across every warehouse (viaStockLedger.stock_map/0). - Neither
reserved_by_item/0noravailable_by_item/0see stock "in transit" on an unfinished (in_transit)Transfer— it has already left the source warehouse'sStockrow but hasn't yet landed on the destination's, so it's invisible to both. Not compensated for here — the same limitation is noted onPhoenixKitWarehouse.TurnoverandWeb.StockLive.
Summary
Functions
Returns %{item_uuid => Decimal} — on-hand quantity (summed across every
warehouse via StockLedger.stock_map/0) minus reserved_by_item/0, for
the union of items appearing in either source. An item missing from one
side is treated as 0 there: an item with stock but no open reservation
is simply its full on-hand quantity; an item reserved but with no Stock
row at all yields a negative available quantity, surfacing the
over-commitment rather than hiding it.
Returns a list of %{item_uuid:, min_quantity:, available:, deficit:} —
one entry per item with a configured minimum
(MinStockSettings.min_stock_map/0, which already excludes unset or
zero minimums) whose available_by_item/0 quantity has fallen below that
minimum. Items at or above their minimum are omitted.
Returns %{item_uuid => Decimal} — the quantity currently reserved
against each item by posted internal orders.
Functions
Returns %{item_uuid => Decimal} — on-hand quantity (summed across every
warehouse via StockLedger.stock_map/0) minus reserved_by_item/0, for
the union of items appearing in either source. An item missing from one
side is treated as 0 there: an item with stock but no open reservation
is simply its full on-hand quantity; an item reserved but with no Stock
row at all yields a negative available quantity, surfacing the
over-commitment rather than hiding it.
Returns a list of %{item_uuid:, min_quantity:, available:, deficit:} —
one entry per item with a configured minimum
(MinStockSettings.min_stock_map/0, which already excludes unset or
zero minimums) whose available_by_item/0 quantity has fallen below that
minimum. Items at or above their minimum are omitted.
Returns %{item_uuid => Decimal} — the quantity currently reserved
against each item by posted internal orders.
Only status == "posted" internal orders reserve stock
(InternalOrders.list_posted_internal_orders/0 already filters for this
in SQL) — drafts are unconfirmed proposals and reserve nothing, otherwise
every draft would manufacture a false deficit.
For each posted order, each line's reservation is
max(0, required_quantity - already_issued), where already_issued is
how much of that order's own required_quantity has already shipped out
via a posted Goods Issue referencing it (via
CommittedQuantities.compute/5, status: "posted" — a draft Goods Issue
hasn't decremented stock yet, so it must not shrink the reservation early;
doing so would let available_by_item/0 overstate what's actually free).
This is computed per line, per order, then summed by item_uuid
across every order — deliberately not as a single global
Σrequired - Σissued per item. The per-order clamp to zero matters:
without it, fully (or over-) issuing one internal order could drive that
order's own line negative, which would then wrongly cancel out a
different internal order's still-open reservation for the same item once
summed globally.