# CreateStockAdjustment

## Permission Scope

inventoryControl

## Overview

createStockAdjustment creates a new stock adjustment document in DRAFT status. Stock adjustments are used to correct inventory discrepancies, scrap damaged goods, or block/unblock stock for quality holds. The adjustment contains an adjustmentDate and one or more lines, each specifying an item, location, quantity, and direction of change. The adjustment must go through a workflow (submit, then confirm) before any inventory changes take effect.

## Business Rules

- At least one adjustment line is required
- Reason code is required
- adjustmentDate is required and is the business date used as InventoryExecution.effectiveDate when the adjustment is confirmed
- 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.
- For CORRECTION type, adjustmentDirection (INCREASE or DECREASE) is required on each line
- For SCRAP type, the direction is implicitly DECREASE (credit); lines may optionally specify `fromStockCategory` (AVAILABLE default, or BLOCKED) to indicate whether the scrap is drawn from available stock or from blocked (quarantined) stock
- For BLOCK/UNBLOCK types, direction is not applicable (internal transfer) and `fromStockCategory` must not be set
- For CORRECTION type, `fromStockCategory` must not be set; it is only valid for SCRAP. Setting it on a non-SCRAP line returns `INVALID_FROM_STOCK_CATEGORY`.
- unitCost is optional and used for CORRECTION/INCREASE to specify the cost of new stock
- Creates a StockAdjustment record in DRAFT status with adjustmentDate and associated lines
- No inventory changes occur at this stage

## Process Flow

```mermaid
flowchart TD
    A[Create stock adjustment request] --> D{Reason code provided?}
    D -->|No| E[Return error: REASON_CODE_REQUIRED]
    D -->|Yes| F{At least one line?}
    F -->|No| G[Return error: EMPTY_ADJUSTMENT_LINES]
    F -->|Yes| H[Validate each line]
    H --> I{All items exist and ACTIVE?}
    I -->|No| J[Return error]
    I -->|Yes| K{All locations exist and ACTIVE?}
    K -->|No| L[Return error]
    K -->|Yes| M{All quantities > 0?}
    M -->|No| N[Return error: INVALID_QUANTITY]
    M -->|Yes| O{Lot/serial tracking valid?}
    O -->|No| P[Return error]
    O -->|Yes| Q[Create StockAdjustment in DRAFT\nwith lines]
    Q --> R[Return 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::StockAdjustment](../model/StockAdjustment.md) - Creates the adjustment header record in DRAFT status
- [inventory::StockAdjustmentLine](../model/StockAdjustmentLine.md) - Creates individual adjustment line records
- [inventory::Lot](../model/Lot.md) - Validates lot existence for lot-tracked items
- [inventory::SerialNumber](../model/SerialNumber.md) - Validates serial number existence for serial-tracked items

## Error Scenarios

- **EMPTY_ADJUSTMENT_LINES**: No adjustment lines were provided
- **REASON_CODE_REQUIRED**: Reason code was not 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 reason code is missing
- returns error when no lines are provided
- returns error when line quantity is zero or negative
- returns error when item does not exist
- returns error when item is not active
- returns error when storage location does not exist
- 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
- creates stock adjustment in DRAFT status
- accepts SCRAP type with fromStockCategory=BLOCKED
