# Outbound inventory — consistency

Outbound discipline must mirror **inbound / receiving** integrity: one **authoritative** ledger per domain, **transactional** pairing where required, and **idempotent** retries.

---

## stock_out synchronization guarantees

- **stock_out** uses **available** (not only on-hand) when reservations exist — oversell is rejected at the engine.
- Fulfillment commands pass **reference** metadata: `sales_order_id`, `sales_order_line_id`, `shipment_id`, `goods_issue_id` as stable keys for reconciliation.

---

## Rollback behavior

- **Failed** fulfillment inside one DB transaction: **no** consume row, **no** stock movement, **no** shipment “posted” flag.
- **Async** carrier confirm: do not mark fulfilled until stock and reservation facts are committed; use explicit **pending** states.

---

## Replay / idempotency rules

- **Outer** idempotency key per **ship attempt** (scanner, API, worker).
- **Inner** reservation consume and stock_out keys must be **deterministically derived** from the outer key or stored together in one outer idempotency payload (same pattern as procurement post + per-line stock idempotency).

---

## Fulfillment-to-inventory coordination

- Only **InventoryMovementApplyService** (or equivalent) writes stock movements.
- Sales/fulfillment modules call **inventory** and **reservation** application services — never insert into `inventory_stock_movements` directly.

---

## Reconciliation

- Detect: shipment line **posted** without matching **consume + stock_out** pair (severity **critical**).
- Owner: **inventory** for movement completeness; **reservation** module for open qty vs sales attribution; joint report for ops.

---

## Critical stance

**Outbound inventory consistency must match inbound integrity discipline** — same intolerance for silent half-posts.
