Every record in HDB Service — dispensers, tickets, stock entries, maintenance schedules, and users — lives within a strict five-level hierarchy: Client → Plant → Sector → Location → Dispenser. This hierarchy exists to model real-world installations accurately (a company may run dozens of factories, each with distinct operational zones) and to enforce data isolation automatically: users always operate within the scope of their Client and, when relevant, their assigned Plants.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/GianlucaBessone/HDB-Service/llms.txt
Use this file to discover all available pages before exploring further.
The five levels
Client
A Client represents an organization or company that contracts HDB Service for water dispenser fleet management. It is the top-level ownership boundary — every plant, dispenser, user, and SLA configuration belongs to exactly one Client.| Field | Type | Notes |
|---|---|---|
id | String (cuid) | Primary key |
nombre | String | Company name |
email | String? | Contact email |
telefono | String? | Contact phone |
direccion | String? | Physical address |
active | Boolean | Soft-delete flag |
Plants and optionally has one SlaConfig that overrides platform defaults for all its tickets.
Plant
A Plant represents a physical facility or site belonging to a Client — a factory, office building, warehouse, or distribution centre. Each Plant is geo-tagged and can hold its own inventory stock.| Field | Type | Notes |
|---|---|---|
id | String (cuid) | Primary key |
clientId | String | Foreign key → Client |
nombre | String | Facility name |
direccion | String? | Address |
lat / lng | Float? | GPS coordinates |
active | Boolean | Soft-delete flag |
Sectors (via PlantSector junction), many Locations, and has its own StockEntry records. The UserPlantAccess join table grants CLIENT_REQUESTER users access to specific Plants.
Sector
A Sector represents a functional or physical zone within a Plant — for example, Production, Office, or Warehouse. Sectors are defined globally and can be reused across multiple Plants through thePlantSector junction table.
| Field | Type | Notes |
|---|---|---|
id | String (cuid) | Primary key |
nombre | String | Sector name |
descripcion | String? | Optional description |
active | Boolean | Soft-delete flag |
PlantSector, and contains many Locations. Because Sectors are reused across Plants, the Plant–Sector relationship is many-to-many.
Location
A Location is a specific physical spot within a Plant and Sector where a dispenser can be installed — a corridor, break room, or production line station. Each Location has a globally unique, user-defined ID that typically mirrors a physical label or QR code.| Field | Type | Notes |
|---|---|---|
id | String | User-defined, globally unique |
plantId | String | Foreign key → Plant |
sectorId | String? | Optional FK → Sector |
nombre | String | Display name |
piso | String? | Floor |
area | String? | Area within the sector |
descripcion | String? | Free-text notes |
active | Boolean | Soft-delete flag |
Dispenser
A Dispenser is the core physical asset being managed — a water dispenser unit identified by a user-defined, globally unique ID (often matching the unit’s serial label). Dispensers carry full lifecycle, repair, consumable, and spare-parts history.| Field | Type | Notes |
|---|---|---|
id | String | User-defined, globally unique |
marca | String | Brand |
modelo | String | Model |
status | DispenserStatus | Current operational state |
locationId | String? | Current location (nullable) |
plantId | String? | Home plant |
clientId | String? | Owning client |
lifecycleMonths | Int | Expected service lifetime (default 60) |
lifecycleStartDate | DateTime? | When active lifecycle began |
lifecyclePausedAt | DateTime? | Set when status → BACKUP |
lifecycleAccumulatedPauseDays | Int | Total days paused over lifetime |
numeroSerie | String? | Manufacturer serial number |
fechaCompra | DateTime? | Purchase date |
Hierarchy in practice
Consider AquaCorp S.A., a manufacturing company using HDB Service across two facilities:Client: AquaCorp S.A.
The top-level entity. All plants, users, and the client-specific SLA configuration live here.
Plants: North Factory & South Office
Two physical sites registered under the AquaCorp client. Each has its own GPS coordinates, stock, and user access list.
Sectors per Plant
- North Factory → Production sector, Warehouse sector
- South Office → Office Floor 1 sector, Office Floor 2 sector
PlantSector junction table.Locations per Sector
- Production →
LOC-NF-001(Assembly Line A),LOC-NF-002(Assembly Line B) - Warehouse →
LOC-NF-010(Loading Bay) - Office Floor 1 →
LOC-SO-001(Reception),LOC-SO-002(Meeting Room)
Dispenser lifecycle states
TheDispenserStatus enum controls where a dispenser appears in operations, maintenance scheduling, and reporting.
IN_SERVICE
The dispenser is actively deployed at a location and available for normal operation. Tickets, maintenance schedules, and SLA tracking are fully active.
UNDER_REPAIR
The dispenser has been removed from service for repairs. It may not have a location assigned while in this state.
OUT_OF_SERVICE
The dispenser is permanently or temporarily decommissioned. It is excluded from active fleet reporting.
BLOCKED
The dispenser has been blocked and requires an OC (Order of Correction) release from a
CLIENT_RESPONSIBLE user before it can return to service. The blockedReason and blockedAt fields are populated.BACKUP
The dispenser is a reserve unit held in stock. Its lifecycle timer is paused — time spent in this state does not count toward the unit’s service lifetime. Accumulated pause time is tracked in
lifecycleAccumulatedPauseDays.IN_TECHNICAL_SERVICE
The dispenser is with technical service staff — deeper diagnostic or refurbishment work beyond a standard repair.
BLOCKED_WAITING_OC
A transitional blocked state indicating that an OC document is pending review. The unit remains offline until the OC is processed.
Data scoping
Every user in HDB Service is scoped to their data automatically — no route handler needs to manually filter by client or plant. This is achieved through two complementary mechanisms:clientIdon users —CLIENT_RESPONSIBLEandCLIENT_REQUESTERusers have aclientIdfield linking them to their organization.UserPlantAccessjoin table —CLIENT_REQUESTERusers are granted access to a specific subset of Plants within their Client.
getDataFilter() from lib/auth.ts is called with the current SessionUser and returns the appropriate Prisma WHERE clause:
A Dispenser can be in
BACKUP status without a Location — locationId is nullable for this reason. While in BACKUP, the dispenser’s lifecycle clock is paused: lifecyclePausedAt records when it entered backup, and lifecycleAccumulatedPauseDays accumulates the total number of days spent in reserve over the unit’s lifetime.