# OutboundShipment

## Description

OutboundShipment is the outbound-shipment-owned dispatch document for goods leaving a warehouse, together with its posting state. It is the stock-moving warehouse dispatch record: when posted, it decrements inventory. OutboundShipmentLine records the line-level outbound stock effects owned by that header.

One outbound shipment is represented as one OutboundShipment header; each outbound source line is represented by an OutboundShipmentLine. Because a single dispatch can consolidate demand from several source documents, source-document context lives at the line level rather than on the header, so one shipment can cover multiple sales orders.

OutboundShipment has a deliberately small posting lifecycle:

- `DRAFT`: created but not posted to stock, ledger, balances, or valuation
- `CANCELLED`: draft shipment was discarded before posting; no stock facts were created
- `POSTED`: posted to stock and represented by inventory InventoryLedger entries

Inventory's InventoryLedger references the outbound shipment through its generic source references. Ledger rows do not duplicate outbound-shipment fields; the dispatch document context is owned by OutboundShipment.

`effectiveDate` is the user/business date used for inventory reporting and period controls. `postedAt` is the system timestamp when the shipment transitions to POSTED.

OutboundShipmentLine is the unit-conversion boundary. It keeps both the business/input quantity and the normalized inventory quantity:

- `quantity` + `unitId`: the quantity and unit provided by the source document or user
- `primaryQuantity` + `primaryUnitId`: the quantity converted to the item's primary unit at shipment creation time
- `unitConversionRate`: the multiplier used to convert `quantity` into `primaryQuantity`

InventoryLedger stores only primary-unit quantities derived from `primaryQuantity`.

Outbound shipment has no receipt cost, so OutboundShipmentLine carries no unit cost. Cost is determined by inventory valuation at issue time when the shipment is posted.

For sales-order sources, OutboundShipment is the shipment evidence linked to the commercial commitment through `sourceDocumentId` and `sourceLineId`. Sales owns the order baseline and fulfillment progress projection; outbound-shipment owns the shipment document and its posting lifecycle. Propagating posted quantities back to sales is a cross-module integration concern rather than a second sales-owned shipment model.

Module boundary: outbound-shipment owns the dispatch document lifecycle and its line-level source references, and it triggers stock movement by calling inventory's posting API at post time. It does not own stock balances, the ledger, or valuation. Inventory owns the InventoryLedger, StockLevel, and valuation records that posting produces. Keeping the dispatch document in its own module avoids overloading inventory with outbound document lifecycle while still letting posting create immutable stock facts in inventory.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Draft: createOutboundShipment
    Draft --> Draft: updateOutboundShipment
    Draft --> Cancelled: cancelOutboundShipment
    Draft --> Posted: postOutboundShipment
```

Creation is represented as the initial state transition into `DRAFT`. The transition table below lists operations on existing OutboundShipment records.

| Operation | From | To | Command |
|-----------|------|----|---------|
| update | DRAFT | DRAFT | [updateOutboundShipment](../command/UpdateOutboundShipment.md) |
| cancel | DRAFT | CANCELLED | [cancelOutboundShipment](../command/CancelOutboundShipment.md) |
| post | DRAFT | POSTED | [postOutboundShipment](../command/PostOutboundShipment.md) |

### Command Definitions

- [createOutboundShipment](../command/CreateOutboundShipment.md) - Creates a DRAFT outbound shipment
- [updateOutboundShipment](../command/UpdateOutboundShipment.md) - Updates a DRAFT outbound shipment
- [cancelOutboundShipment](../command/CancelOutboundShipment.md) - Cancels a DRAFT outbound shipment without deleting it
- [postOutboundShipment](../command/PostOutboundShipment.md) - Posts a DRAFT outbound shipment, issuing stock through inventory

### Query Definitions

- [getOutboundShipment](../query/GetOutboundShipment.md) - Retrieves one OutboundShipment with its shipment lines
- [listOutboundShipments](../query/ListOutboundShipments.md) - Lists OutboundShipment records with their shipment lines
- Traceability queries can join inventory InventoryLedger to OutboundShipment through the ledger's generic source references after posting.

### Models

- OutboundShipment
- OutboundShipmentLine

### Invariants

- status must be DRAFT, CANCELLED, or POSTED
- DRAFT shipments must not affect InventoryLedger, StockLevel, or valuation
- CANCELLED shipments must not affect InventoryLedger, StockLevel, or valuation
- POSTED shipments are immutable outbound stock facts; reversal is modeled as a separate document when introduced
- Posting is one-shot: a shipment can transition DRAFT to POSTED exactly once, and posting applies all line stock effects together
- effectiveDate is required and represents the business effective date
- postedAt is set when status becomes POSTED
- Every OutboundShipmentLine references its OutboundShipment header
- OutboundShipmentLine itemId, quantity, unitId, primaryQuantity, primaryUnitId, unitConversionRate, storageLocationId, and stockType are required
- OutboundShipmentLine quantity, primaryQuantity, and unitConversionRate must be positive
- OutboundShipmentLine primaryUnitId is a snapshot of Item.unitId when the shipment line is created
- OutboundShipmentLine primaryQuantity equals quantity multiplied by unitConversionRate after inventory rounding rules
- OutboundShipmentLine carries no unit cost; issue cost is determined by inventory valuation at posting
- OutboundShipmentLine sourceDocumentType, when present, must be SALES_ORDER
- OutboundShipmentLine sourceDocumentType, sourceDocumentId, and sourceLineId are required generic source references and are not cross-module foreign keys

### Relationships

- **Has OutboundShipmentLine**: Each shipment owns one or more line-level outbound stock effects
- **OutboundShipmentLine references Item**: Each line issues one Item from item-management
- **OutboundShipmentLine references StorageLocation**: Each line issues from one inventory location
- **Referenced by inventory InventoryLedger**: Ledger rows produced at posting point back to the shipment through inventory's generic source references
