# UpdateStockAdjustment

## Permission Scope

inventoryControl

## Overview

updateStockAdjustment modifies a stock adjustment that is in DRAFT or REJECTED status. When updating a REJECTED adjustment, its status transitions back to DRAFT, allowing revision and resubmission. Mutable fields include reasonCode and adjustmentDate via `headerPatch`, plus adjustment lines via `addLines`, `updateLines`, and `removeLineIds`. The adjustmentType cannot be changed after creation.

## Business Rules

- Stock adjustment must exist
- Stock adjustment must be in DRAFT or REJECTED status
- If in REJECTED status, transitions back to DRAFT
- adjustmentType cannot be changed
- reasonCode can be updated
- adjustmentDate can be updated before confirmation
- Lines can be added, updated, or removed (including line-level fields such as `fromStockCategory` for SCRAP adjustments)
- `updateLines` and `removeLineIds` must target a line that exists on the adjustment
- `updateLines` patches the existing line in place, so its id and createdAt are preserved
- After update, at least one line must remain
- Each line quantity must be greater than zero
- Each line item must exist and be in ACTIVE status
- Each line storage location must exist and be in ACTIVE status
- `lotId` is optional on each line; when provided, the referenced lot must exist. The caller is responsible for supplying it when the item is lot-tracked.
- `serialNumberId` is optional on each line; when provided, the referenced serial number must exist. The caller is responsible for supplying it when the item is serial-tracked.
- No inventory or ledger changes occur

## Process Flow

```mermaid
flowchart TD
    A[Update stock adjustment request] --> B{Adjustment exists?}
    B -->|No| C[Return error: STOCK_ADJUSTMENT_NOT_FOUND]
    B -->|Yes| D{Status is DRAFT or REJECTED?}
    D -->|No| E[Return error: INVALID_STATUS]
    D -->|Yes| N{Target lines exist?}
    N -->|No| O[Return error: STOCK_ADJUSTMENT_LINE_NOT_FOUND]
    N -->|Yes| F{Validate updated lines}
    F --> G{All validations pass?}
    G -->|No| H[Return validation error]
    G -->|Yes| I{Status is REJECTED?}
    I -->|Yes| J[Transition to DRAFT]
    I -->|No| K[Stay in DRAFT]
    J --> L[Apply updates]
    K --> L
    L --> M[Return updated stock adjustment]
```

## External Dependencies

- [item-management::Item](../../../item-management/docs/model/Item.md) - Validates that referenced items exist and are ACTIVE
- [inventory::StorageLocation](../model/StorageLocation.md) - Validates that storage locations exist and are ACTIVE
- [inventory::Lot](../model/Lot.md) - Validates lot existence when lotId is provided on a line
- [inventory::SerialNumber](../model/SerialNumber.md) - Validates serial number existence when serialNumberId is provided on a line

## Error Scenarios

- **STOCK_ADJUSTMENT_NOT_FOUND**: Referenced stock adjustment does not exist
- **STOCK_ADJUSTMENT_LINE_NOT_FOUND**: Referenced stock adjustment line does not exist on the target adjustment
- **INVALID_STATUS**: Target entity is not in a valid status for this operation
- **EMPTY_ADJUSTMENT_LINES**: No adjustment lines were provided
- **ITEM_NOT_FOUND**: Referenced item does not exist
- **ITEM_NOT_ACTIVE**: Referenced item is not in ACTIVE status
- **STORAGE_LOCATION_NOT_FOUND**: Referenced storage location does not exist
- **STORAGE_LOCATION_NOT_ACTIVE**: Referenced storage location is not in ACTIVE status
- **INVALID_QUANTITY**: Quantity is zero or negative
- **INVALID_FROM_STOCK_CATEGORY**: `fromStockCategory` was set on a non-SCRAP line
- **LOT_NOT_FOUND**: Referenced lot does not exist
- **SERIAL_NOT_FOUND**: Referenced serial number does not exist

## Test Cases

- returns error when stock adjustment does not exist
- returns error when stock adjustment is not in DRAFT or REJECTED status
- returns error when status is CONFIRMED
- updates reason code on a DRAFT adjustment
- updates a line in place so its id and createdAt survive the edit
- adds a stock adjustment line via addLines
- removes a stock adjustment line via removeLineIds
- transitions REJECTED adjustment back to DRAFT
- returns error when an update targets a missing line
- returns error when a removal would leave the adjustment without lines
- returns error when item does not exist
- returns error when line quantity is zero or negative
- returns error when storage location does not exist
- returns error when item is not active
- returns error when storage location is not active
- rejects fromStockCategory set on non-SCRAP lines
- returns error when lot does not exist
- returns error when serial number does not exist
