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
OnCreateLowStockAlertEventfor 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.
| Field | Type | Description |
|---|---|---|
id | UUID | Primary key |
businessId | UUID | Owning business |
locationId | UUID | Location with low stock |
locationName | string | Denormalized location name |
alertDate | timestamp | When the alert was generated |
status | enum | ACTIVE, RESOLVED, or DISMISSED |
detail | JSON | { items: LowStockProduct[] } — snapshot of low-stock products |
createdBy | string | User 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 ascreatedByfor scheduler-generated alertsMANUAL_CREATION("SYSTEM_manualCreation") — for alerts created via the API
Data Flows
Manual creation (POST /low-stock-alerts)
- Client sends
CreateLowStockAlertDTO(businessId, locationId, status, etc.) - Service calls
InventoriesRepository.detectLowStock(businessId, locationId)to auto-populatedetail - Alert is persisted via repository
OnCreateLowStockAlertEventis emitted- Communications module sends notifications to business users
Automated daily check (scheduler)
- Cron fires at 7am daily
detectLowStock()scans all businesses/locations- Results grouped by business, then by location
- For each location,
findAlertForToday()checks idempotency - New alerts are bulk-created per business
- Events emitted for each alert → notifications sent
API Endpoints
All endpoints require Bearer token authentication.
| Method | Path | Description |
|---|---|---|
POST | /low-stock-alerts | Create an alert (auto-detects low stock items) |
GET | /low-stock-alerts | List alerts (paginated, filtered by businessId) |
GET | /low-stock-alerts/:id | Get a single alert by UUID |
PATCH | /low-stock-alerts/:id | Update an alert (e.g., change status) |
DELETE | /low-stock-alerts/:id | Delete an alert |
Query parameters (GET list)
| Param | Required | Description |
|---|---|---|
businessId | Yes | Filter by business UUID |
locationId | No | Filter by location UUID |
page | No | Page number (default 1) |
size | No | Page size |
orderBy | No | Sort field: locationName |
order | No | Sort direction: asc or desc |
search | No | Search 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
- Idempotent scheduler —
findAlertForToday()prevents duplicate alerts if the cron runs multiple times in a day - Bulk inserts — Alerts for the same business are inserted in a single batch for performance
- Event-driven notifications — Alert creation is decoupled from notification delivery via
EventEmitter2, keeping the module independent from the communications system - Detail snapshot — Low stock items are stored as a JSON snapshot at alert time, providing a historical record even if inventory changes later
- Denormalized
locationName— Stored on the alert record to avoid joins in list queries and preserve the name at alert time
Related Documentation
- Communication Integration — how alerts trigger notifications
- Implementation Summary — history of scheduler and communication improvements