# 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 adjustment lines (add, update, or remove lines). 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
- Lines can be added, updated, or removed (including line-level fields such as `fromStockCategory` for SCRAP adjustments)
- 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| 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/models/Item.md) - Validates that referenced items exist and are ACTIVE
- [inventory::StorageLocation](../models/StorageLocation.md) - Validates that storage locations exist and are ACTIVE
- [inventory::Lot](../models/Lot.md) - Validates lot existence when lotId is provided on a line
- [inventory::SerialNumber](../models/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
- **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
- returns error when update would leave zero 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
- updates reason code on a DRAFT adjustment
- replaces lines when provided
- transitions REJECTED adjustment back to DRAFT
