Skip to main content

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: InventoryDetailsService orchestrates CRUD operations with event emission. Utility functions handle bulk operations: generating details from purchase/GRN items, increasing/decreasing quantities, and validating quantity totals.
  • Infrastructure: InventoryDetailsRepository implements the domain port using Kysely queries against PostgreSQL.
  • Interfaces: InventoryDetailsController maps 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:

FieldDescription
batchNumberBatch/lot identifier (nullable)
serialNumberUnique serial number (nullable)
quantityCurrent available quantity
quantityReceivedOriginal quantity when received
statusCurrent status (see enum below)
productionDateWhen the batch was produced
expirationDateWhen the batch expires
receivedDateWhen the inventory was received

Inventory Statuses

  • available — ready for sale or use
  • damaged — physically damaged
  • expired — past expiration date
  • quarantined — held for inspection
  • refurbishing — undergoing repair/reconditioning
  • reserved — allocated for a pending order
  • sold — sold and dispatched
  • under_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:

  1. generateInventoryDetailsFromItems() — Creates new detail records from purchase or GRN line items. Used during goods receipt and purchase completion.

  2. decreaseQuantityInventoryDetailsFromItems() — Reduces quantity on existing records. Used during sales, transfer dispatch, and decrease adjustments. Validates stock availability (prevents negative quantities).

  3. increaseQuantityInventoryDetailsFromItems() — Increases quantity on existing records, or creates new records if no match is found. Used during transfer receipt and increase adjustments.

  4. 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

MethodPathDescription
POST/inventory-detailsCreate an inventory detail
GET/inventory-detailsList with pagination and filters
GET/inventory-details/:id?businessId=Get by ID
PATCH/inventory-details/:idUpdate (businessId in body)
DELETE/inventory-details/:id?businessId=Delete

Query Parameters (GET list)

ParamTypeDescription
businessIdUUIDFilter by business
locationIdUUIDFilter by location
productIdUUIDFilter by product
getSerialbooleanOnly records with serial numbers
getBatchbooleanOnly records with batch numbers
searchstringSearch product name, description, SKU, barcode
sizenumberPage size (default: 10)
pagenumberPage number (default: 1)
orderBystringSort field: batchNumber, serialNumber
orderstringSort direction: asc, desc

Event System

All mutations emit domain events for downstream processing:

EventPayload
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

ModuleUsage
InventoriesPrimary consumer of bulk utility functions for stock changes
Goods Received NotesCreates details from received items, validates quantities
PurchasesValidates detail quantities match purchase items
Purchase OrdersValidates detail quantities match PO items
Production RunsCreates details for finished goods, manages WIP returns

Design Decisions

  1. 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.

  2. Event-driven integration: Other modules react to inventory detail events rather than being directly coupled. Events are suppressed during transactions to prevent premature reactions.

  3. Cascading lookups: The identifier resolution strategy (serial+batch → serial → batch → ID) ensures flexible matching across different data quality scenarios.

  4. Repository injection via Symbol token: Uses INVENTORY_DETAILS_REPOSITORY Symbol for proper dependency inversion, allowing the service to depend on the domain interface rather than the concrete implementation.