Context for managing goods issues (materials written off to production).
A goods issue registers materials leaving the warehouse into production. It DECREASES warehouse stock when posted. There is NO repost — posting a goods issue is a conditional decrement operation. If any line has insufficient stock the ENTIRE Multi rolls back. Correction after posting is limited to note and storage_folder only.
Summary
Functions
Manually attaches a traceability reference to a goods issue.
Corrects the note and/or storage_folder_uuid of a goods issue without changing status or lines. Works on documents in any status. Lines are immutable once posted.
Creates a draft goods issue from a posted internal order.
Creates a new draft goods issue.
Returns {:ok, issue} or {:error, :not_found}.
Returns the goods issue or raises.
Imports lines from one or more posted internal orders into an existing draft goods issue.
Lists non-deleted goods issues ordered by number descending (newest first).
Posts a goods issue in an Ecto.Multi transaction. DECREASES warehouse stock.
Detaches a traceability reference from a goods issue. No-op when the
{type, uuid} pair isn't present.
Sets the storage_folder_uuid on a goods issue.
Works on documents in any status.
Soft-deletes a draft goods issue. Returns {:error, :not_draft} for posted documents.
Updates a draft goods issue. Returns {:error, :not_draft} when not in
draft status.
Functions
Manually attaches a traceability reference to a goods issue.
"internal_order" is always valid (intra-module). Any other type must be
a kind registered via PhoenixKitWarehouse.SourceKinds.
Pure metadata — does not touch lines and is not gated to draft status.
A duplicate {type, uuid} pair is a no-op.
Corrects the note and/or storage_folder_uuid of a goods issue without changing status or lines. Works on documents in any status. Lines are immutable once posted.
Creates a draft goods issue from a posted internal order.
Lines are copied from the internal order with:
issued_quantitydefaulting to the internal order line'srequired_quantity(the keeper adjusts down if fewer are actually available/needed).
Sets internal_order_uuid, location_uuid, and propagates source_refs
(the internal order's own registered-kind refs plus an "internal_order" ref
for the source itself) so traceability is preserved.
Creates a new draft goods issue.
location_uuid defaults to the configured default warehouse when not given.
created_by_uuid is set programmatically — not via cast.
Returns {:ok, issue} or {:error, :not_found}.
Returns the goods issue or raises.
Imports lines from one or more posted internal orders into an existing draft goods issue.
Lines are merged by item_uuid (quantities summed). The primary internal
order is taken as the first UUID in io_uuids; its own UUID is set on the
goods issue when it currently has no internal_order_uuid set. Source
references (source_refs) are enriched with the traceability chain — each
selected internal order plus the customer order(s)/sub-order(s) that fed it
(from its own source_refs) — deduplicating by {type, uuid}.
issued_quantity is set to required_quantity (same as create_from_internal_order/2).
unit_value is left nil.
Returns {:ok, updated_issue} or {:error, reason}.
Lists non-deleted goods issues ordered by number descending (newest first).
Posts a goods issue in an Ecto.Multi transaction. DECREASES warehouse stock.
- Locks the row FOR UPDATE and re-checks status == "draft" (prevents double-posting).
- Deduplicates lines by item_uuid.
- For each line with issued_quantity > 0:
- Captures
previous_quantity= current on-hand for audit. - Calls
StockLedger.issue_quantity/3(conditional decrement — WHERE quantity >= qty). - If ANY line returns
{:error, {:insufficient_stock, _}}, the WHOLE Multi rolls back: stock is unchanged and the document stays draft.
- Captures
- Lines with issued_quantity == 0 contribute no stock change.
- Merges
previous_quantityinto the persisted lines (audit trail). - Flips status → "posted", sets posted_at and performed_by_uuid.
Returns {:error, :not_draft} for non-draft issues.
Returns {:error, {:insufficient_stock, item_uuid}} when any line cannot be
fulfilled; the entire transaction is rolled back.
Detaches a traceability reference from a goods issue. No-op when the
{type, uuid} pair isn't present.
Sets the storage_folder_uuid on a goods issue.
Works on documents in any status.
Soft-deletes a draft goods issue. Returns {:error, :not_draft} for posted documents.
Updates a draft goods issue. Returns {:error, :not_draft} when not in
draft status.
Locks the row FOR UPDATE and re-checks status == "draft" in the DB (not just the in-memory struct) so a stale tab cannot overwrite an issue that was posted concurrently by another tab/user.