# README

## Overview

The Inventory module manages physical stock tracking across storage locations. It provides real-time visibility into on-hand quantities, records all stock movements (receipts, issues, adjustments), and supports lot/batch and serial number traceability. The module also handles inventory valuation, physical inventory counts, and scrap disposal.

Inventory operates at the Item level (from item-management) and tracks stock within StorageLocations that the module owns. Organization's Site entity represents the physical facility (with address); each StorageLocation belongs directly to a Site.

## Key Features

- **Location Management**: Define StorageLocations under organizational Sites
- **Stock Tracking**: Real-time on-hand, reserved, incoming, and blocked quantities per item per location
- **Stock Movement**: Atomic `receiveStock` / `issueStock` commands for externally driven changes, plus StockAdjustment workflow (CORRECTION / SCRAP / BLOCK / UNBLOCK) for operator-initiated changes — all producing immutable InventoryLedger records
- **Inbound Inspection**: Optional hold-on-receipt pattern via `receiveStock(toBlocked=true)`; applications layer their own inspection workflow on top
- **Inventory Adjustment**: Manual corrections for discrepancies, damage, and shrinkage via StockAdjustment(CORRECTION)
- **Lot and Batch Tracking**: Lot/batch-level traceability with expiration date management
- **Serial Number Tracking**: Individual item tracking by unique serial number across lifecycle
- **Inventory Valuation**: Cost calculation using FIFO, weighted average, or standard cost methods
- **Inventory Count**: Physical inventory counts and cycle counts with variance review and approval
- **Scrap Management**: Record and track disposal of damaged, expired, or defective stock via StockAdjustment(SCRAP)

## Module Scope

### In Scope

- Storage location definition and management (flat, no zone/bin hierarchy)
- Real-time stock level tracking per item per location (on-hand, reserved, incoming, blocked)
- Atomic stock-change commands (`receiveStock`, `issueStock`) and StockAdjustment approval workflow (CORRECTION / SCRAP / BLOCK / UNBLOCK)
- Immutable inventory ledger recording for all stock changes (InventoryLedger) with `sourceType` + generic `sourceId` back to the originating document
- Inbound quality hold on receipt via `receiveStock(toBlocked=true)` (inspection workflow itself is application-owned)
- Inventory adjustment with reason codes via StockAdjustment(CORRECTION)
- Lot/batch assignment, tracking, and expiration date management
- Serial number assignment and lifecycle tracking
- Inventory valuation method configuration (FIFO, weighted average, standard cost)
- Cost-per-unit tracking and valuation amount calculation
- Physical inventory count and cycle count workflows
- Scrap/disposal recording with quantity and valuation impact via StockAdjustment(SCRAP)

### Out of Scope

- Purchase order lifecycle management (purchasing module)
- Sales order and fulfillment workflows (sales module)
- Automated reorder point and replenishment rules (purchasing module)
- Pick/pack/ship multi-step fulfillment (warehouse operations / sales module)
- Putaway rules and automated storage location assignment
- Removal strategies — automated FIFO/FEFO/LIFO picking rules
- Demand forecasting and planning
- Drop-shipping and cross-docking
- Landed cost allocation (purchasing / accounting module)
- Journal entry posting to general ledger (accounting module)
- Barcode scanning device integration (application layer)
- Shipping carrier integration (application layer)

### Scope Decision Rationale

Inventory owns **physical stock state** -- where items are, how many exist, and what they cost. This is separated from purchasing (which owns the procurement lifecycle) and sales (which owns the fulfillment lifecycle) because stock tracking follows its own business rules and update patterns.

StorageLocation is owned by this module (not organization) because it represents an operational storage bucket with inventory-specific attributes (capacity, storage conditions). Organization's Site represents the physical facility identity and address; StorageLocation layers the storage layout directly on top of a Site. We intentionally do not introduce an intermediate "Warehouse" entity — locations belong directly to a Site, matching SAP MM's Plant → Storage Location pattern.

Lot/batch and serial number tracking live here (not in item-management) because they are inventory-layer concepts -- they track "which specific instances" of an item exist, not "what the item is." Item-management explicitly defers these concerns to the inventory module.

Stock changes flow through two complementary patterns, both landing in the immutable **InventoryLedger**:

- **Atomic commands** (`receiveStock`, `issueStock`) apply stock changes in a single step. They are driven by external modules — purchase goods receipts, sales shipments, manufacturing completions — that have already gone through their own approval lifecycle
- **StockAdjustment** provides an approval workflow (DRAFT → SUBMITTED → CONFIRMED) for operator-initiated changes (CORRECTION, SCRAP, BLOCK, UNBLOCK). Ledger writes occur only on confirmation

InventoryLedger entries carry a `sourceType` (GOODS_RECEIPT, GOODS_ISSUE, CORRECTION, SCRAP, BLOCK, UNBLOCK) and a generic `sourceId` pointing to the originating document (e.g., the calling PurchaseOrder, SalesShipment, or StockAdjustment). Keeping `sourceId` as a generic reference avoids coupling the ledger to any one caller's schema while still making every ledger entry traceable back to its trigger.

StockLevel is maintained as a per-(item, location) cache derived from the ledger and reservation commands. Reservation (`reserveStock` / `releaseReservation`) mutates StockLevel directly without producing ledger entries, because reservations represent commitment against available stock rather than physical movement.

**Inbound Inspection** is an application-level extension point. `receiveStock(toBlocked=true)` admits stock into the destination location as BLOCKED, preventing it from being issued or reserved until a subsequent StockAdjustment (UNBLOCK to release, SCRAP to dispose) resolves the hold. The inspection workflow itself — inspector assignment, pass/fail decision, documentation — is implemented by the application and is not part of the erp-kit schema. Outbound inspection is handled by the Sales module.

Inventory valuation is included because cost-per-unit is intrinsically tied to inventory transactions (receipt costs, adjustment impacts). CostLayer references InventoryLedger for accurate cost traceability. However, posting journal entries to the general ledger is excluded -- that responsibility belongs to a future accounting module. Inventory will emit valuation events for downstream consumption.

## Module Dependencies

- [item-management](../item-management/README.md) — Item entity as the primary trackable unit (itemId/SKU)
- [organization](../organization/README.md) — Company for multi-company scoping, Site for physical facility (address, and parent of StorageLocation)
- [primitives](../primitives/README.md) — Unit of Measure for stock quantities, Currency for valuation amounts
