# TransferOrder

## Description

TransferOrder is the inventory-owned control document for moving stock from one site to another. It captures the site-level transfer intent and document lifecycle, while shipment and receipt remain separate inventory facts recorded in the InventoryLedger through `postInventoryLedger`.

TransferOrder deliberately keeps the header lifecycle small. Partial shipment and partial receipt are not header states; they are derived from TransferOrderLine ordered, shipped, and received quantities.

`plannedShipmentDate` is the planned source-site shipment date. `expectedReceiptDate` is the expected destination-site receipt date. When the order is opened, these dates become the source-site StockReservation `requiredDate` and the destination-site InventorySupplyPlan `expectedDate`.

TransferOrder does not determine which transit storage location should be used. The module treats the transit storage location as an execution input supplied by the application layer when shipping and receiving. Applications that do not need users to distinguish transit locations can resolve this outside erp-kit by always passing a single `defaultInTransitLocationId`, or by maintaining one in-transit storage location per site and passing the site-specific in-transit location for transfer execution.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Draft: createTransferOrder
    Draft --> Draft: updateTransferOrder
    Draft --> Open: openTransferOrder
    Draft --> Closed: closeTransferOrder
    Open --> Closed: closeTransferOrder
```

| Operation | From | To | Command |
|-----------|------|----|---------|
| update | DRAFT | DRAFT | [updateTransferOrder](../command/UpdateTransferOrder.md) |
| open | DRAFT | OPEN | [openTransferOrder](../command/OpenTransferOrder.md) |
| close | DRAFT, OPEN | CLOSED | [closeTransferOrder](../command/CloseTransferOrder.md) |

### Command Definitions

- [createTransferOrder](../command/CreateTransferOrder.md) - Creates a DRAFT transfer order with lines
- [updateTransferOrder](../command/UpdateTransferOrder.md) - Updates a DRAFT transfer order and replaces its lines
- [openTransferOrder](../command/OpenTransferOrder.md) - Opens a DRAFT transfer order for execution
- [shipTransferOrder](../command/ShipTransferOrder.md) - Ships transfer lines into in-transit stock and increments shipped progress
- [receiveTransferOrder](../command/ReceiveTransferOrder.md) - Receives in-transit stock into the destination site and increments received progress
- [closeTransferOrder](../command/CloseTransferOrder.md) - Closes a DRAFT or OPEN transfer order, including short close when no in-transit quantity remains

### Query Definitions

- [getTransferOrder](../query/GetTransferOrder.md) - Retrieves one transfer order with lines and derived progress status
- [listTransferOrders](../query/ListTransferOrders.md) - Lists transfer orders with lines and derived progress status

### Models

- TransferOrder
- TransferOrderLine

### Invariants

- Header `status` expresses document control only: DRAFT, OPEN, or CLOSED
- Partial shipment and receipt are derived from TransferOrderLine quantities, not persisted as header lifecycle states
- Source and destination sites must differ
- Source and destination sites must belong to the same company; cross-company transfers are not supported
- `plannedShipmentDate` and `expectedReceiptDate` are required before opening
- `plannedShipmentDate` is used for source reservation demand; `expectedReceiptDate` is used for destination supply projection
- DRAFT transfer orders can be edited; open and closed transfer orders cannot be edited
- OPEN transfer orders may be executed by posting through `postInventoryLedger`, which writes InventoryLedger rows with `sourceType=TRANSFER_ORDER`
- Transit storage location selection is outside the TransferOrder model; ship and receive commands use the application-supplied transit storage location as an execution fact
- `shippedQuantity` is incremented only by shipTransferOrder
- `receivedQuantity` is incremented only by receiveTransferOrder
- CLOSED transfer orders are terminal
- OPEN transfer orders can be closed only when shipped quantity equals received quantity on every line, leaving no in-transit quantity
- Closing an OPEN transfer order closes its remaining open StockReservation and InventorySupplyPlan rows
- `closedAt` is set only by closeTransferOrder

### Relationships

- **Has TransferOrderLine**: Each transfer order owns one or more lines
- **References Site**: Source and destination sites identify the business-level transfer endpoints
- **Referenced by InventoryLedger**: Shipment and receipt postings reference the transfer order generically via `sourceType=TRANSFER_ORDER` and `sourceId=TransferOrder.id`
