# PostGoodsIssue

## Permission Scope

inventoryOperation

## Overview

postGoodsIssue posts a DRAFT goods issue InventoryExecution. It creates GOODS_ISSUE InventoryLedger rows, decreases AVAILABLE StockLevel balances, updates lot stock when a lot is present, applies valuation, transitions serial numbers to ISSUED when present, and marks the execution POSTED.

## Business Rules

- The target InventoryExecution must exist, have `executionType=GOODS_ISSUE`, and be in DRAFT status
- Posting is based on the InventoryExecutionLine snapshot
- At least one execution line must exist before posting
- Each line posts a GOODS_ISSUE / CR ledger row linked by inventoryExecutionId
- Ledger quantity uses primaryQuantity
- The issuing location must have enough available stock for each line
- Matching StockReservation rows are consumed by increasing consumedQuantity
- Stock reserved for other demand is excluded from available-to-issue checks
- The selected lot must have enough on-hand stock at the issuing location when lotId is present
- The selected serial number must be available when serialNumberId is present
- The execution is marked POSTED with postedAt after all stock effects are applied

## Process Flow

```mermaid
flowchart TD
    A[Post goods issue request] --> B[Lock InventoryExecution]
    B --> C{GOODS_ISSUE and DRAFT?}
    C -->|No| D[Return error: INVALID_STATUS]
    C -->|Yes| E[Load InventoryExecutionLine rows]
    E --> F[Validate item, location, lot, and serial references]
    F --> G[Create GOODS_ISSUE ledger rows]
    G --> H[Decrease StockLevel and LotStockLevel]
    H --> I[Apply valuation]
    I --> J[Mark InventoryExecution POSTED]
    J --> K[Return posted execution and ledger entries]
```

## External Dependencies

- [item-management::Item](../../../item-management/docs/model/Item.md) - Validates referenced items
- [inventory::InventoryExecution](../model/InventoryExecution.md) - Reads and posts the execution header, applying outbound execution lines
- [inventory::InventoryLedger](../model/InventoryLedger.md) - Records immutable stock ledger rows
- [inventory::StockLevel](../model/StockLevel.md) - Decreases available stock

## Error Scenarios

- **INVALID_STATUS**: Target entity is not in a valid status for this operation
- **EMPTY_ISSUE_LINES**: No goods issue lines were provided
- **ITEM_NOT_FOUND**: Referenced item does not exist
- **STORAGE_LOCATION_NOT_FOUND**: Referenced storage location does not exist
- **INSUFFICIENT_STOCK**: Available stock (onHand - reserved - blocked) is less than the requested quantity (fromBlocked=false)
- **INSUFFICIENT_LOT_STOCK**: Lot stock is below the issue quantity
- **LOT_NOT_FOUND**: Referenced lot does not exist
- **SERIAL_NOT_FOUND**: Referenced serial number does not exist
- **SERIAL_NOT_AVAILABLE**: Serial number is not in AVAILABLE state

## Test Cases

- returns error when execution is missing
- returns error when execution has no lines
- posts execution lines into ledger and stock
- consumes matching stock reservations when posting
- does not consume stock reserved for other demand
