# ExecuteStockMovement

## Permission Scope

stockOperation

## Overview

executeStockMovement transitions a stock movement from CONFIRMED to DONE status. This is the critical command that actually updates stock levels in the system. It processes each move line, adjusting on-hand, incoming, blocked, and reserved quantities as appropriate for the movement type. It also updates lot stock levels and serial number states when applicable, and triggers inventory valuation recalculation.

## Business Rules

- Stock movement must exist and be in CONFIRMED status
- For GOODS_ISSUE: source location must have sufficient available stock
- For BLOCK: must have sufficient available stock to block
- For UNBLOCK: must have sufficient blocked stock to unblock
- Updates StockLevel quantities based on movement type
- Updates LotStockLevel if a lot is specified on the move line
- Transitions SerialNumber state based on movement type and current serial state:
  - GOODS_RECEIPT (initial receipt): serial is assigned as AVAILABLE via AssignSerialNumber, not by this command
  - GOODS_ISSUE: AVAILABLE → ISSUED
  - GOODS_RECEIPT (customer return): ISSUED → RETURNED
  - GOODS_RECEIPT (restock after return): RETURNED → AVAILABLE
- SCRAP serial transitions (AVAILABLE → SCRAPPED, RETURNED → SCRAPPED) are handled by ConfirmInventoryAdjustment, not by this command
- An ISSUED serial cannot transition directly to AVAILABLE; it must pass through RETURNED first
- Triggers valuation recalculation after execution

## Process Flow

```mermaid
flowchart TD
    A[Receive execute request] --> B{Movement exists?}
    B -->|No| C[Return error: STOCK_MOVEMENT_NOT_FOUND]
    B -->|Yes| D{Status is CONFIRMED?}
    D -->|No| E[Return error: STOCK_MOVEMENT_NOT_CONFIRMED]
    D -->|Yes| F[Process each move line]
    F --> G{Sufficient stock?}
    G -->|No - available| H[Return error: INSUFFICIENT_STOCK]
    G -->|No - blocked| I[Return error: INSUFFICIENT_BLOCKED_STOCK]
    G -->|No - lot| J[Return error: LOT_STOCK_INSUFFICIENT]
    G -->|Yes| K[Update stock levels]
    K --> L{Lot specified?}
    L -->|Yes| M[Update lot stock levels]
    L -->|No| N{Serial specified?}
    M --> N
    N -->|Yes| O{Valid serial state?}
    O -->|No| P[Return error: SERIAL_INVALID_STATE]
    O -->|Yes| Q[Transition serial number state]
    N -->|No| R[Trigger valuation recalculation]
    Q --> R
    R --> S[Update status to DONE]
    S --> T[Return executed movement]
```

## External Dependencies

- None (all internal inventory models: [StockLevel](../models/StockLevel.md), [LotStockLevel](../models/LotStockLevel.md), [SerialNumber](../models/SerialNumber.md), [ItemValuation](../models/ItemValuation.md))

## Error Scenarios

- **STOCK_MOVEMENT_NOT_FOUND**: Referenced stock movement does not exist
- **STOCK_MOVEMENT_NOT_CONFIRMED**: Stock movement is not in CONFIRMED status
- **INSUFFICIENT_STOCK**: Source location does not have enough available stock for goods issue or block
- **INSUFFICIENT_BLOCKED_STOCK**: Source location does not have enough blocked stock for unblock
- **LOT_STOCK_INSUFFICIENT**: Lot does not have sufficient stock at the location
- **SERIAL_INVALID_STATE**: Serial number is not in a valid state for the movement type

## Test Cases

- returns error when not found
- returns error when not CONFIRMED
- returns error when insufficient available stock for goods issue
- returns error when insufficient stock for block
- returns error when insufficient blocked stock for unblock
- returns error when lot stock insufficient for goods issue
- executes goods receipt (increases on-hand, decreases incoming)
- executes goods issue (decreases on-hand)
- executes internal transfer (decreases source, increases destination)
- executes block (increases blocked)
- executes unblock (decreases blocked)
- updates lot stock levels when lot specified
- transitions serial from AVAILABLE to ISSUED on goods issue
- transitions serial from ISSUED to RETURNED on goods receipt (customer return)
- transitions serial from RETURNED to AVAILABLE on goods receipt (restock after return)
- returns error when serial state is invalid for the movement type
- triggers valuation recalculation
