# UpdateInboundShipment

## Permission Scope

inboundShipmentOperation

## Overview

updateInboundShipment updates a DRAFT InboundShipment via `headerPatch` and revises its InboundShipmentLine set via `addLines`, `updateLines`, and `removeLineIds`.

## Business Rules

- The target InboundShipment must exist and be in DRAFT status
- Posted inbound shipments cannot be updated
- effectiveDate can be updated independently
- Source references (sourceDocumentType, sourceDocumentId, sourceLineId) are line-level data and can be revised through `addLines` and `updateLines`; they are not immutable header fields
- `updateLines` and `removeLineIds` must target a line that exists on the shipment
- `updateLines` patches the existing line in place, so its id and createdAt are preserved
- At least one line must remain after the changes
- Added and updated lines follow the same validation rules as createInboundShipment
- Added and updated lines must provide both source-unit quantity/unitId and converted primaryQuantity/unitConversionRate snapshots
- Added and updated line primaryUnitId must equal Item.unitId
- 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 stock or ledger changes occur during update

## Process Flow

```mermaid
flowchart TD
    A[Update inbound shipment request] --> B[Lock InboundShipment]
    B --> C{DRAFT?}
    C -->|No| D[Return error: INVALID_STATUS]
    C -->|Yes| E{Target lines exist?}
    E -->|No| F[Return error: SHIPMENT_LINE_NOT_FOUND]
    E -->|Yes| H{At least one line remains?}
    H -->|No| I[Return error: EMPTY_SHIPMENT_LINES]
    H -->|Yes| J[Validate added and updated lines]
    J --> K[Apply header updates and line changes]
    K --> M[Return updated 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

- **INVALID_STATUS**: Target entity is not in a valid status for this operation
- **SHIPMENT_LINE_NOT_FOUND**: Referenced inbound shipment line does not exist on the target shipment
- **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

- updates header metadata on a draft inbound shipment
- updates a line in place so its id survives the edit
- updates the source references on an existing line
- adds an inbound shipment line via addLines
- removes an inbound shipment line via removeLineIds
- returns error when the inbound shipment does not exist
- returns error when inbound shipment is not draft
- returns error when an update targets a missing line
- returns error when a removal would leave the shipment without lines
- returns error when an added line primary quantity is zero or negative
- returns error when an added line unit conversion rate is zero or negative
- returns error when an added line primary unit differs from item primary unit
- revalidates the item when only the primary unit changes on an existing line
