# InventoryLedger

## Description

InventoryLedger is an immutable, append-only record that captures every physical stock change in the inventory system at the item-location level. Each record represents a single stock change for one item at one location as a flat entity.

Source types include GOODS_RECEIPT, GOODS_ISSUE, BLOCK, UNBLOCK, CORRECTION, and SCRAP. Quantity is always positive (> 0); the dcIndicator (DR or CR) determines the double-entry direction of the stock effect. Each entry carries a sourceId referencing the originating document (e.g., a purchase order goods receipt, a sales order goods issue, or a StockAdjustment), and a sourceType that classifies the nature of the change. The optional amount field records the monetary value of the stock change (quantity multiplied by unit cost) for accounting integration.

InventoryLedger has no lifecycle status and no state machine — once created, it is permanent and cannot be updated or deleted. The executedAt timestamp records when the physical stock change occurred and is always non-nullable. Only createdAt is tracked (no updatedAt) to enforce immutability.

When an InventoryLedger entry is created, it triggers stock level updates at the relevant location via the stock-tracking feature, and feeds downstream processes such as inventory valuation and lot/serial tracking.

## Domain Model Definitions

### Model type

AppendOnly

### Command Definitions

- InventoryLedger entries are created internally by the following commands:
  - [receiveStock](../commands/ReceiveStock.md) - Creates a GOODS_RECEIPT entry (DR) when stock is received at a location. When `toBlocked=true`, also creates BLOCK entries (CR + DR) to place stock in blocked/inspection status
  - [issueStock](../commands/IssueStock.md) - Creates a GOODS_ISSUE entry (CR) when stock is issued from a location
  - [confirmStockAdjustment](../commands/ConfirmStockAdjustment.md) - Creates CORRECTION, SCRAP, BLOCK, or UNBLOCK entries based on the adjustment type

### Query Definitions

- getInventoryLedger - Retrieve a ledger entry by id (to be added)
- listInventoryLedger - List ledger entries with optional filters by sourceType, sourceId, itemId, storageLocationId, dcIndicator, date range (to be added)

### Models

- InventoryLedger

### Invariants

- InventoryLedger is immutable — once created, it cannot be updated or deleted
- sourceType must be one of: GOODS_RECEIPT, GOODS_ISSUE, BLOCK, UNBLOCK, CORRECTION, SCRAP
- sourceId is required — references the originating document that created this entry
- dcIndicator must be one of: DR (debit, stock increase), CR (credit, stock decrease)
- itemId is required — each entry pertains to a specific item
- quantity is always positive (> 0); dcIndicator determines the direction of the stock effect
- amount is optional — when present, represents quantity multiplied by unit cost
- storageLocationId is required — the location where the stock change occurs
- For GOODS_RECEIPT: dcIndicator is DR, entry records stock arriving at the destination location
- For GOODS_ISSUE: dcIndicator is CR, entry records stock leaving the source location
- For BLOCK: dcIndicator is DR on blocked bucket, entry records stock being quarantined
- For UNBLOCK: dcIndicator is CR on blocked bucket, entry records stock being released
- For CORRECTION: dcIndicator is DR for INCREASE, CR for DECREASE
- For SCRAP: dcIndicator is CR, entry records stock being written off
- executedAt is required (non-nullable)
- Only createdAt is tracked (no updatedAt)

### Relationships

- **References Item**: Each entry pertains to an Item from the item-management module
- **References StorageLocation**: The location where the stock change occurs
- **References Lot (optional)**: For lot-tracked items, the specific lot affected
- **References SerialNumber (optional)**: For serial-tracked items, the specific serial number affected
- **Referenced by CostLayer**: Cost layers reference the InventoryLedger entry that created them via inventoryLedgerId
