Skip to content

Decision 0019 — Outbound Allocation Authority

  • Status: ACCEPTED
  • Task: NS2-021
  • Date: 2026-08-26

Context

Phase 2 needs one tenant-neutral Sale -> Outbound Fulfillment -> Inventory Demand -> Allocation flow. Current source already separates Sale/fulfillment persistence from explicit Inventory allocation: projectAllocationPreview is read-only, applyAllocation owns the transaction and reverseAllocation owns release. The remaining implementation must complete this contract rather than introduce another workflow owner.

Decision

  1. Commercial Sale owns commercial intent. Fulfillment owns the 1:1 execution order and 1:N demand lines. Saving either never allocates inventory.
  2. Inventory owns read-only preview and explicit apply/reverse commands. One demand line may receive many allocations and one lot may serve many demands; each allocation relates exactly one lot to one demand line.
  3. Partial and multi-lot allocation are allowed. Quantity and weight must not exceed either the locked lot balance or cumulative demand. Zero requested weight means quantity is authoritative; otherwise both dimensions conserve.
  4. db-mysql remains the single transaction owner. Apply locks/resolves tenant-scoped UUID relations, claims the demand ALLOCATION_VERSION, conditionally decrements the lot, inserts the UUID allocation and appends the signed immutable ledger before commit. A lost version or balance race is a conflict.
  5. Allocation UUID is the idempotency key. Identical retry returns authoritative lot readback; different payload conflicts. Unknown result is resolved only by rereading that UUID and exact relation/quantity/weight facts.
  6. Reallocation is explicit reverse then apply with a new UUID. Reverse soft-deletes the relation, restores lot quantity/weight, appends a compensating ledger row and recomputes demand state.
  7. Reverse is allowed only while the demand is OPEN, PARTIAL or ALLOCATED. Any execution state after allocation (picked/loaded/shipped/completed or an active progressed equipment/transport bridge) blocks release. NS2-022 must bind current persisted bridge facts to this guard.
  8. Booking, equipment and carrier remain optional context owners. They may consume a ready outbound order but never own allocation or infer it from a Sale save.
  9. Routes retain tenant UUID resolution and explicit inventory.allocation.apply / .reverse capabilities. Menus are navigation, not business authorization.

Conservation

For each lot: inbound - reversed receipts - active allocations = remain.

For each demand line: requested - active allocations = unallocated, with active allocations never exceeding requested quantity or positive requested weight. Apply ledger deltas are negative; reversal deltas are equal positive compensation.

Alternatives

Alternative 1 — Automatic Sale Allocation

Rejected: hides side effects, cannot choose lots and makes retry/correction ambiguous.

Alternative 2 — New Allocation Service

Rejected: current Inventory plus db-mysql owners already provide the admitted command, lock and ledger boundary; another owner duplicates them.

Alternative 3 — Booking-Owned Reservation

Rejected: optional transportation context cannot become the inventory writer and would exclude non-Booking outbound flows.

Alternative 4 — Existing Inventory Owner

Accepted: harden the existing Inventory command and db-mysql transaction owner with the smallest missing progressed-state and multi-lot proofs.

Consequences

  • Allocation remains explicit, tenant-scoped and independently retryable.
  • Multi-lot work composes existing one-lot commands; no orchestration framework is added.
  • Transportation progress is a reversal guard, never an inventory writer.
  • NS2-022 must prove the remaining bridge-state and concurrent runtime cases.

Revisit

Revisit only if an admitted shipment owner introduces a persisted state that cannot be projected into the existing progressed-state guard, or measured throughput proves the current transaction boundary insufficient. Neither condition is currently present.

NS2-022 Boundary

Reuse existing packages/routes/UI. Add no package, service, queue, event bus or customer branch. Implement only gaps required to prove multi-lot partial allocation, concurrent conflict, safe retry, reallocation/reversal, progressed-state guard, tenant/capability denial and authoritative readback.

---

Source: new-system/docs/decisions/0019-outbound-allocation-authority.md — ctc repository · This page is an English translation of the source document. Identifiers, file paths and code symbols are unchanged.