Saltar al contenido principal

Inventory Adjustments

Overview

The inventory adjustments module handles stock quantity corrections — both increases and decreases — outside of normal purchasing or sales flows. Common scenarios include damage write-offs, expiration, promotional giveaways, internal use, and manual count corrections (via the stock-count module).

Each adjustment creates a document with a cost snapshot at the time of creation, then emits an event that triggers downstream inventory processing in the inventories module.

Architecture

inventory-adjustments/
├── inventory-adjustments.module.ts
├── application/
│ ├── inventory-adjustments.service.ts # Use cases
│ └── events/
│ ├── on-create-inventory-adjustment.event.ts
│ └── on-update-inventory-adjustment.event.ts
├── domain/
│ └── inventory-adjustments-repository.domain.ts # Port interface
├── infrastructure/
│ └── inventory-adjustments.repository.ts # Kysely adapter
└── interfaces/
├── inventory-adjustments.controller.ts # HTTP endpoints
├── dtos/
│ ├── create-inventory-adjustment.dto.ts
│ └── update-inventory-adjustment.dto.ts
└── query/
└── paginate-inventory-adjustments.query.ts

Layer responsibilities:

LayerResponsibility
DomainRepository interface (port) — no framework dependencies
ApplicationOrchestrates creation with cost snapshots, emits events
InfrastructureKysely queries against inventoryAdjustment table
InterfacesHTTP controllers, request validation (DTOs/queries)

Domain Concepts

Adjustment Direction

  • INCREASE — Adds stock (e.g., found items, corrections)
  • DECREASE — Removes stock (e.g., damage, expiration, internal use)

Adjustment Type

  • MANUAL — Operator-initiated correction
  • DAMAGE — Stock damaged and written off
  • EXPIRATION — Stock expired
  • PROMOTIONAL — Used for promotional purposes
  • INTERNAL_USE — Consumed internally

Status Lifecycle

DRAFT → READY → POSTED
→ CANCELED
  • DRAFT — Created but not finalized
  • READY — Finalized, triggers inventory processing
  • POSTED — Fully applied to inventory
  • CANCELED — Voided

Cost Snapshot

On creation, the service snapshots current inventory costs into costDetail (JSON). This ensures historical accuracy — cost changes after the adjustment don't affect the document.

Event Flow

Create/Update adjustment
→ Emit OnCreateInventoryAdjustmentEvent / OnUpdateInventoryAdjustmentEvent
→ InventoryAdjustmentHandler (inventories module) listens
→ If status is READY or POSTED:
→ INCREASE: processInventoryIncrease() → emit OnStockIncreasedByInventoryAdjustmentEvent
→ DECREASE: processInventoryDecrease() (FIFO batch) → emit OnStockDecreasedByInventoryAdjustmentEvent

API Endpoints

MethodPathDescription
POST/inventory-adjustmentsCreate an adjustment
GET/inventory-adjustmentsList adjustments (paginated, filtered by businessId/locationId)
GET/inventory-adjustments/searchAdvanced search (status, date range, direction, type)
GET/inventory-adjustments/:idGet single adjustment by ID
GET/inventory-adjustments/:id/pdfDownload PDF document
GET/inventory-adjustments/:id/printHTML print preview
PATCH/inventory-adjustments/:idPartial update
DELETE/inventory-adjustments/:idDelete adjustment

Query Parameters

List endpoint (GET /):

ParamRequiredDescription
businessIdYesBusiness UUID
locationIdNoFilter by location
sizeNoPage size (default: all)
pageNoPage number
searchNoSearch by location name
orderByNoSort field (locationName)
orderNoSort direction (asc/desc)

Search endpoint (GET /search):

All list params plus:

ParamRequiredDescription
statusNodraft, ready, posted, canceled
adjustmentDirectionNoINCREASE, DECREASE
adjustmentTypeNoMANUAL, DAMAGE, EXPIRATION, PROMOTIONAL, INTERNAL_USE
createdAtFromNoISO 8601 date
createdAtToNoISO 8601 date

Integration Points

  • Stock Count module — Creates adjustments automatically from count session variances
  • Inventories module — Listens for create/update events to process stock changes
  • PDF module — Generates PDF documents and HTML print previews

Design Decisions

  1. Cost snapshot at creationcostDetail is a JSON snapshot of inventory costs at the time the adjustment is created. This prevents retroactive cost changes from affecting already-posted documents.

  2. Event-driven inventory processing — Adjustments don't directly modify inventory. Instead, events are emitted and the inventories module handles stock updates. This decouples the adjustment document lifecycle from inventory processing.

  3. Unscoped internal lookup (findByIdInternal) — PDF/print generation uses an unscoped lookup (no businessId filter) because these operations are triggered after the adjustment is already authorized. The scoped findById is used for all user-facing operations.

  4. Separate list and search endpoints — The list endpoint uses simple equality filters while the search endpoint supports range filters (date) and advanced filtering. This keeps the common case simple.