Inventory Details Module
Overview
The inventory details module provides granular, unit-level inventory tracking with support for batch numbers, serial numbers, production/expiration dates, and per-location quantity management. It is the core mechanism for traceability and lot tracking in FlowPOS.
Inventory details are consumed by purchases, goods received notes (GRNs), inventory transfers, inventory adjustments, retail sales, and production runs.
Architecture
inventory-details/
├── inventory-details.module.ts # Module definition
├── application/
│ ├── inventory-details.service.ts # CRUD + event emission
│ ├── events/
│ │ ├── on-create-inventory-detail.event.ts # Create event payload
│ │ ├── on-update-inventory-detail.event.ts # Update event payload
│ │ └── on-delete-inventory-detail.event.ts # Delete event payload
│ └── utils/
│ ├── create-inventory-details.ts # Bulk create/increase/decrease utilities
│ └── validate-inventory-details.ts # Quantity validation against purchases
├── domain/
│ └── inventory-details-repository.domain.ts # Repository port (interface)
├── infrastructure/
│ └── inventory-details.repository.ts # Kysely DB adapter
└── interfaces/
├── inventory-details.controller.ts # HTTP endpoints
├── dtos/
│ ├── create-inventory-detail.dto.ts
│ └── update-inventory-detail.dto.ts
└── query/
└── paginate-inventory-details.query.ts
Layer Responsibilities
- Domain: Repository interface (
IInventoryDetailsRepository) — defines CRUD and lookup contracts for inventory detail records. - Application:
InventoryDetailsServiceorchestrates CRUD operations with event emission. Utility functions handle bulk operations: generating details from purchase/GRN items, increasing/decreasing quantities, and validating quantity totals. - Infrastructure:
InventoryDetailsRepositoryimplements the domain port using Kysely queries against PostgreSQL. - Interfaces:
InventoryDetailsControllermaps HTTP routes to service methods. DTOs validate request payloads with Swagger documentation.
Domain Concepts
Inventory Detail
An inventory detail record tracks a specific batch or serialized unit of a product at a location. Key attributes:
| Field | Description |
|---|---|
batchNumber | Batch/lot identifier (nullable) |
serialNumber | Unique serial number (nullable) |
quantity | Current available quantity |
quantityReceived | Original quantity when received |
status | Current status (see enum below) |
productionDate | When the batch was produced |
expirationDate | When the batch expires |
receivedDate | When the inventory was received |
Inventory Statuses
available— ready for sale or usedamaged— physically damagedexpired— past expiration datequarantined— held for inspectionrefurbishing— undergoing repair/reconditioningreserved— allocated for a pending ordersold— sold and dispatchedunder_inspection— quality check in progress
Tracking Types
Inventory details support two tracking modes, which can be combined:
- Batch tracking: Multiple units share a batch number (e.g., lot "BATCH-2026-001" with quantity 24)
- Serial tracking: Each unit has a unique serial number (e.g., "SN-ABC-123456" with quantity 1)
Use Cases
Direct CRUD (via HTTP endpoints)
- Create a new inventory detail record
- List inventory details with pagination, search, and filtering
- Get a single inventory detail by ID
- Update an inventory detail (status, quantity, batch/serial numbers)
- Delete an inventory detail
Bulk Operations (via utility functions)
These are consumed programmatically by other modules:
-
generateInventoryDetailsFromItems()— Creates new detail records from purchase or GRN line items. Used during goods receipt and purchase completion. -
decreaseQuantityInventoryDetailsFromItems()— Reduces quantity on existing records. Used during sales, transfer dispatch, and decrease adjustments. Validates stock availability (prevents negative quantities). -
increaseQuantityInventoryDetailsFromItems()— Increases quantity on existing records, or creates new records if no match is found. Used during transfer receipt and increase adjustments. -
validateInventoryDetailsQuantitiesMatchPurchase()— Validates that inventory detail quantities sum to the parent item quantity. Used in POs and GRNs.
Identifier Lookup Strategy
When matching existing inventory details, the system searches in priority order:
For decreases (sales, transfers out): ID → serial+batch → serial → batch For increases (transfers in, adjustments): serial+batch → serial → batch → ID (with location validation)
API Endpoints
| Method | Path | Description |
|---|---|---|
POST | /inventory-details | Create an inventory detail |
GET | /inventory-details | List with pagination and filters |
GET | /inventory-details/:id?businessId= | Get by ID |
PATCH | /inventory-details/:id | Update (businessId in body) |
DELETE | /inventory-details/:id?businessId= | Delete |
Query Parameters (GET list)
| Param | Type | Description |
|---|---|---|
businessId | UUID | Filter by business |
locationId | UUID | Filter by location |
productId | UUID | Filter by product |
getSerial | boolean | Only records with serial numbers |
getBatch | boolean | Only records with batch numbers |
search | string | Search product name, description, SKU, barcode |
size | number | Page size (default: 10) |
page | number | Page number (default: 1) |
orderBy | string | Sort field: batchNumber, serialNumber |
order | string | Sort direction: asc, desc |
Event System
All mutations emit domain events for downstream processing:
| Event | Payload |
|---|---|
inventory-detail.create | { createdInventoryDetail } |
inventory-detail.update | { previousInventoryDetail, newInventoryDetail } |
inventory-detail.delete | { deletedInventoryDetail, quantity } |
Events are suppressed when operating within a transaction (trx parameter), allowing callers to emit events after the transaction commits.
Integration Points
| Module | Usage |
|---|---|
| Inventories | Primary consumer of bulk utility functions for stock changes |
| Goods Received Notes | Creates details from received items, validates quantities |
| Purchases | Validates detail quantities match purchase items |
| Purchase Orders | Validates detail quantities match PO items |
| Production Runs | Creates details for finished goods, manages WIP returns |
Design Decisions
-
Soft quantity tracking: Details track quantities rather than individual unit records. A batch of 24 units is one record with
quantity: 24, not 24 records. -
Event-driven integration: Other modules react to inventory detail events rather than being directly coupled. Events are suppressed during transactions to prevent premature reactions.
-
Cascading lookups: The identifier resolution strategy (serial+batch → serial → batch → ID) ensures flexible matching across different data quality scenarios.
-
Repository injection via Symbol token: Uses
INVENTORY_DETAILS_REPOSITORYSymbol for proper dependency inversion, allowing the service to depend on the domain interface rather than the concrete implementation.