# ReceiveTransferOrder

## Permission Scope

transferOrder

## Overview

receiveTransferOrder posts a transfer receipt for an OPEN TransferOrder. It moves stock from IN_TRANSIT stock into destination-site AVAILABLE stock and increments TransferOrderLine `receivedQuantity`.

The caller supplies the transit storage location being consumed. erp-kit records and validates that execution fact, but does not choose the transit location. Applications can pass a global default in-transit location or a site-specific in-transit location when users do not need to distinguish transit locations.

## Business Rules

- The TransferOrder must exist and be OPEN
- Each transfer line must belong to the TransferOrder
- Receipt quantity must be greater than zero
- Receipt quantity cannot exceed `shippedQuantity - receivedQuantity`
- Transit storage location must exist and hold enough IN_TRANSIT stock
- Available in-transit quantity is validated by reading InventoryLedger rows with `sourceType=TRANSFER_ORDER`, matching `sourceId`/`sourceLineId`, and `stockType=IN_TRANSIT`, netting IN minus OUT for the same transfer line and transit storage location
- Destination storage location must belong to the TransferOrder destination site
- Receipt is posted through `postInventoryLedger` as one posting event with `sourceType=TRANSFER_ORDER`, `sourceId` set to the transfer order and `sourceLineId` set to each transfer line, and `action=TRANSFER` (no costing)
- The posting event carries a paired set of movement lines per transfer line: an OUT line on transit IN_TRANSIT stock and an IN line on destination AVAILABLE stock
- The posting writes two InventoryLedger rows per transfer line (transit IN_TRANSIT credit and destination AVAILABLE debit), consumes matching TransferOrder InventorySupplyPlan quantity for the destination line, and updates transit and destination StockLevel balances
- Being an internal relocation, the receipt does not affect valuation
- Receipt increments TransferOrderLine `receivedQuantity`

## Process Flow

```mermaid
flowchart TD
    A[Receive transfer receipt request] --> B[Lock TransferOrder]
    B --> C{TransferOrder is OPEN?}
    C -->|No| D[Return not found or state error]
    C -->|Yes| E[Validate each line and location]
    E --> F[Read InventoryLedger IN_TRANSIT rows for the line to confirm in-transit quantity]
    F --> G[Build paired transit IN_TRANSIT OUT and destination AVAILABLE IN movement lines]
    G --> H[Post through postInventoryLedger sourceType=TRANSFER_ORDER action=TRANSFER]
    H --> I[Consume destination supply plan, write ledger rows, update StockLevel]
    I --> J[Increment receivedQuantity]
    J --> K[Return ledger entries and updated lines]
```

## External Dependencies

- [inventory::TransferOrder](../model/TransferOrder.md) - Must be OPEN
- [inventory::TransferOrderLine](../model/TransferOrderLine.md) - Provides line item and receipt progress
- [inventory::InventorySupplyPlan](../model/InventorySupplyPlan.md) - Destination supply projection is consumed
- [inventory::StockLevel](../model/StockLevel.md) - Transit IN_TRANSIT and destination AVAILABLE balances are updated
- [inventory::postInventoryLedger](./PostInventoryLedger.md) - Posts the paired receipt movement lines to the InventoryLedger
- [inventory::InventoryLedger](../model/InventoryLedger.md) - Records immutable stock movement entries

## Error Scenarios

- **TRANSFER_ORDER_NOT_FOUND**: Transfer order does not exist
- **TRANSFER_ORDER_LINE_NOT_FOUND**: Transfer order line does not exist or does not belong to the order
- **INVALID_STATE_TRANSITION**: Transfer order lifecycle transition is not allowed
- **INVALID_QUANTITY**: Quantity is zero or negative
- **INVALID_TRANSFER_LOCATIONS**: Transfer location or site is invalid
- **STORAGE_LOCATION_NOT_FOUND**: Referenced storage location does not exist
- **INSUFFICIENT_STOCK**: Available stock cannot cover the requested quantity

## Test Cases

- receives in-transit stock into the destination site
- receives the same transfer line from multiple transit locations
- returns error when receiving more than in-transit transfer quantity
- returns error when transit stock belongs to another transfer line
- returns error when no lines are provided
