# SerialNumber

## Description

SerialNumber represents a unique identifier assigned to an individual unit of an item, enabling unit-level traceability throughout the item's lifecycle. Serial numbers follow a state machine: AVAILABLE (in stock) → ISSUED (dispatched) → RETURNED (received back) → AVAILABLE (restocked) or SCRAPPED (disposed). SCRAPPED is a terminal state.

Serial-tracked items always have a quantity of exactly 1 per serial number. State transitions are driven by stock movements rather than direct commands.

## Domain Model Definitions

### Model type

Stateful

#### State Transitions

```mermaid
stateDiagram-v2
    [*] --> Available: assignSerialNumber
    Available --> Issued: issueStock
    Issued --> Returned: receiveStock (return)
    Returned --> Available: receiveStock (restock)
    Returned --> Scrapped: confirmStockAdjustment (SCRAP)
    Available --> Scrapped: confirmStockAdjustment (SCRAP)
```

| Operation | From | To | Command |
|-----------|------|----|---------|
| issueStock | AVAILABLE | ISSUED | [issueStock](../commands/IssueStock.md) |
| return | ISSUED | RETURNED | [receiveStock](../commands/ReceiveStock.md) |
| restock | RETURNED | AVAILABLE | [receiveStock](../commands/ReceiveStock.md) |
| scrap | RETURNED, AVAILABLE | SCRAPPED | [confirmStockAdjustment](../commands/ConfirmStockAdjustment.md) |

### Command Definitions

- [assignSerialNumber](../commands/AssignSerialNumber.md) - Assign a serial number to an item at a storage location
- [issueStock](../commands/IssueStock.md) - Issue stock, transitioning serial number from AVAILABLE to ISSUED
- [receiveStock](../commands/ReceiveStock.md) - Receive stock, transitioning serial number from ISSUED to RETURNED (customer return) or from RETURNED to AVAILABLE (restock after inspection). When `toBlocked=true`, stock is marked as blocked at the StockLevel layer, but serial number state itself is not blocked
- [confirmStockAdjustment](../commands/ConfirmStockAdjustment.md) - Confirm a stock adjustment (SCRAP), transitioning serial number to SCRAPPED

### Query Definitions

- [getSerialNumber](../queries/GetSerialNumber.md) - Retrieve a serial number by id or serial number value
- [listSerialNumbers](../queries/ListSerialNumbers.md) - List serial numbers by item, storage location, or status
- [getSerialNumberHistory](../queries/GetSerialNumberHistory.md) - Retrieve the full movement history of a serial number

### Models

- SerialNumber

### Invariants

- Quantity is always exactly 1
- Serial number must be unique per item type (different items may share the same serial number)
- Serial number is immutable after assignment
- SCRAPPED is a terminal state
- ISSUED cannot transition directly to AVAILABLE — must pass through RETURNED
- State transitions are driven by stock movements

### Relationships

- **References Item**: Each serial number belongs to an Item from the item-management module
- **References StorageLocation**: Each serial number tracks its current storage location
