PhoenixKitWarehouse.GoodsIssues (PhoenixKitWarehouse v0.1.0)

Copy Markdown View Source

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

add_source_ref(issue, type, uuid)

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.

correct_goods_issue(issue, attrs)

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.

create_from_internal_order(internal_order, actor_uuid)

Creates a draft goods issue from a posted internal order.

Lines are copied from the internal order with:

  • issued_quantity defaulting to the internal order line's required_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.

create_goods_issue(attrs)

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.

get_goods_issue(uuid)

Returns {:ok, issue} or {:error, :not_found}.

get_goods_issue!(uuid)

Returns the goods issue or raises.

import_from_internal_orders(issue, io_uuids, actor_uuid)

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}.

list_goods_issues(opts \\ [])

Lists non-deleted goods issues ordered by number descending (newest first).

post_goods_issue(issue, performed_by_uuid)

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.
  • Lines with issued_quantity == 0 contribute no stock change.
  • Merges previous_quantity into 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.

remove_source_ref(issue, type, uuid)

Detaches a traceability reference from a goods issue. No-op when the {type, uuid} pair isn't present.

set_storage_folder(issue, storage_folder_uuid)

Sets the storage_folder_uuid on a goods issue. Works on documents in any status.

soft_delete(issue, actor_uuid)

Soft-deletes a draft goods issue. Returns {:error, :not_draft} for posted documents.

update_draft(goods_issue, attrs)

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.