Saltar al contenido principal

Low Stock Alerts Module

Overview

The low-stock-alerts module monitors inventory levels across all business locations and creates alerts when products fall below their reorder threshold. Alerts are created either manually via the API or automatically by a daily scheduled job.

On creation, each alert emits an OnCreateLowStockAlertEvent consumed by the communications module to send notifications (email, SMS, WhatsApp) to business users.


Architecture

low-stock-alerts/
├── low-stock-alerts.module.ts
├── domain/
│ ├── low-stock-alerts-repository.domain.ts # Repository port (ILowStockAlertsRepository)
│ └── low-stock-alerts.constants.ts # Re-exports LowStockAlertStatus, SystemIdentifiers
├── application/
│ ├── low-stock-alerts.service.ts # Use cases: create, list, update, delete, detect
│ └── events/
│ └── on-create-low-stock-alert.event.ts # Domain event emitted on creation
├── infrastructure/
│ └── low-stock-alerts.repository.ts # Kysely implementation of ILowStockAlertsRepository
├── interfaces/
│ ├── low-stock-alerts.controller.ts # HTTP REST controller
│ ├── dtos/
│ │ ├── create-low-stock-alert.dto.ts
│ │ └── update-low-stock-alert.dto.ts
│ └── query/
│ └── paginate-low-stock-alerts.query.ts
└── jobs/
└── low-stock-alerts.scheduler.ts # Daily cron job (7am)

Dependencies

  • InventoriesModule — provides InventoriesRepository.detectLowStock() for identifying products below threshold
  • CommunicationsModule — listens to OnCreateLowStockAlertEvent for notification delivery (separate module, event-driven coupling only)

Domain Concepts

Low Stock Alert

A record indicating that one or more products at a specific business location have fallen below their reorder threshold.

FieldTypeDescription
idUUIDPrimary key
businessIdUUIDOwning business
locationIdUUIDLocation with low stock
locationNamestringDenormalized location name
alertDatetimestampWhen the alert was generated
statusenumACTIVE, RESOLVED, or DISMISSED
detailJSON{ items: LowStockProduct[] } — snapshot of low-stock products
createdBystringUser UUID or system identifier

LowStockAlertStatus

  • ACTIVE — alert is current and unresolved
  • RESOLVED — underlying stock issue has been addressed
  • DISMISSED — acknowledged but no action taken

SystemIdentifiers

  • LOW_STOCK_CHECK ("SYSTEM_checkLowStock") — used as createdBy for scheduler-generated alerts
  • MANUAL_CREATION ("SYSTEM_manualCreation") — for alerts created via the API

Data Flows

Manual creation (POST /low-stock-alerts)

  1. Client sends CreateLowStockAlertDTO (businessId, locationId, status, etc.)
  2. Service calls InventoriesRepository.detectLowStock(businessId, locationId) to auto-populate detail
  3. Alert is persisted via repository
  4. OnCreateLowStockAlertEvent is emitted
  5. Communications module sends notifications to business users

Automated daily check (scheduler)

  1. Cron fires at 7am daily
  2. detectLowStock() scans all businesses/locations
  3. Results grouped by business, then by location
  4. For each location, findAlertForToday() checks idempotency
  5. New alerts are bulk-created per business
  6. Events emitted for each alert → notifications sent

API Endpoints

All endpoints require Bearer token authentication.

MethodPathDescription
POST/low-stock-alertsCreate an alert (auto-detects low stock items)
GET/low-stock-alertsList alerts (paginated, filtered by businessId)
GET/low-stock-alerts/:idGet a single alert by UUID
PATCH/low-stock-alerts/:idUpdate an alert (e.g., change status)
DELETE/low-stock-alerts/:idDelete an alert

Query parameters (GET list)

ParamRequiredDescription
businessIdYesFilter by business UUID
locationIdNoFilter by location UUID
pageNoPage number (default 1)
sizeNoPage size
orderByNoSort field: locationName
orderNoSort direction: asc or desc
searchNoSearch by location name (ILIKE)

Example: Create alert

POST /low-stock-alerts
Content-Type: application/json
Authorization: Bearer <token>

{
"businessId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"locationId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"locationName": "Main Store",
"status": "ACTIVE",
"alertDate": "2026-03-25T07:00:00.000Z",
"createdBy": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}

Example: Update status

PATCH /low-stock-alerts/:id
Content-Type: application/json
Authorization: Bearer <token>

{
"status": "RESOLVED",
"updatedBy": "c3d4e5f6-a7b8-9012-cdef-123456789012"
}

Design Decisions

  1. Idempotent schedulerfindAlertForToday() prevents duplicate alerts if the cron runs multiple times in a day
  2. Bulk inserts — Alerts for the same business are inserted in a single batch for performance
  3. Event-driven notifications — Alert creation is decoupled from notification delivery via EventEmitter2, keeping the module independent from the communications system
  4. Detail snapshot — Low stock items are stored as a JSON snapshot at alert time, providing a historical record even if inventory changes later
  5. Denormalized locationName — Stored on the alert record to avoid joins in list queries and preserve the name at alert time