# CreateInboundShipment

## Permission Scope

inboundShipmentOperation

## Overview

createInboundShipment creates a DRAFT InboundShipment. Each line records the inbound stock effect that will be posted later by postInboundShipment.

Because source-document context lives at the line level, a single inbound shipment can cover lines drawn from more than one source document, for example receiving several purchase orders on one shipment.

## Business Rules

- At least one line is required
- effectiveDate is required and is stored as the intended posting date
- Each line must carry source references: sourceDocumentType, sourceDocumentId, and sourceLineId
- sourceDocumentType, when present, must be `PURCHASE_ORDER`
- Each line quantity must be greater than zero
- Each line item must exist; inactive items are accepted because receipts may post long after the source document was created
- Each line storage location must exist; inactive locations are accepted for historical continuity
- quantity and unitId preserve the source/business receipt unit, such as the purchase order unit
- primaryQuantity must be greater than zero and stores the converted quantity in primaryUnitId
- primaryUnitId must equal Item.unitId
- unitConversionRate must be greater than zero and records the multiplier used to derive primaryQuantity from quantity
- Unit conversion is resolved before calling this command; this command stores both source-unit and primary-unit snapshots
- unitCost is not accepted as input; it stays null while DRAFT and is derived from the source purchase-order line price when the shipment is posted
- No StockLevel, InventoryLedger, or valuation changes occur while the shipment is DRAFT

## Process Flow

```mermaid
flowchart TD
    A[Create inbound shipment request] --> G{Items and locations valid?}
    G -->|No| H[Return validation error]
    G -->|Yes| D{At least one line?}
    D -->|No| E0[Return error: EMPTY_SHIPMENT_LINES]
    D -->|Yes| F0{All quantities positive?}
    F0 -->|No| F[Return error: INVALID_QUANTITY]
    F0 -->|Yes| I[Create DRAFT InboundShipment]
    I --> J[Create InboundShipmentLine rows]
    J --> K[Return created shipment id]
```

## External Dependencies

- [item-management::Item](../../../item-management/docs/model/Item.md) - Validates referenced items
- [inventory::StorageLocation](../../../inventory/docs/model/StorageLocation.md) - Validates receiving locations via inventory queries

## Error Scenarios

- **EMPTY_SHIPMENT_LINES**: No inbound shipment lines were provided
- **INVALID_QUANTITY**: Quantity is zero or negative
- **PRIMARY_UNIT_MISMATCH**: primaryUnitId differs from Item.unitId
- **INVALID_UNIT_CONVERSION_RATE**: Unit conversion rate is zero or negative
- **ITEM_NOT_FOUND**: Referenced item does not exist
- **STORAGE_LOCATION_NOT_FOUND**: Referenced storage location does not exist

## Test Cases

- returns error when no lines are provided
- returns error when line quantity is zero or negative
- returns error when line primary quantity is zero or negative
- returns error when line unit conversion rate is zero or negative
- returns error when item does not exist
- returns error when primary unit differs from item primary unit
- returns error when storage location does not exist
- creates a DRAFT inbound shipment with inbound lines
- creates a DRAFT inbound shipment covering lines from multiple source documents
- accepts inactive item and location references
