The maintenance module provides a structured, auditable workflow for preventive maintenance across the entire water dispenser fleet. Every month, HDB Service generates one schedule per active dispenser, technicians submit digital checklists with photos and condition ratings, and customers sign off on completed visits using a cryptographically-secured digital signature. The complete chain of evidence — from schedule creation through to signed approval — is stored and verifiable at any time.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.
Schedule generation
Monthly maintenance schedules are created either automatically by a cron job or on demand by callingPOST /api/maintenance/generate.
month field must follow the YYYY-MM format. The endpoint queries all dispensers whose status is IN_SERVICE, BACKUP, UNDER_REPAIR, or IN_TECHNICAL_SERVICE (i.e., any dispenser that is still in the fleet), then creates a MaintenanceSchedule record for each one — unless a record for that (dispenserId, scheduledMonth) pair already exists.
This upsert pattern (check-then-create on the unique composite index @@unique([dispenserId, scheduledMonth])) guarantees that running the generator multiple times for the same month is fully idempotent — no duplicate schedules are ever created. The response reports how many records were created vs. skipped.
MaintenanceSchedule record carries the following statuses:
PENDING
The schedule has been generated but no checklist has been submitted yet. The default state on creation.
COMPLETED
A checklist has been submitted and accepted. The schedule may be linked to a
MaintenanceApproval group for customer sign-off.OVERDUE
The scheduled month has passed and the visit was not completed. Set by the daily cron job when
scheduledMonth is in the past and status is still PENDING.EXPIRED
The grace period has closed and the schedule can no longer be completed. Used for archival/reporting purposes.
SIGNED
A completed schedule that has been included in a
MaintenanceApproval and signed by the customer.Overdue and expiry
The daily cron job scans allMaintenanceSchedule records and applies two escalation rules:
- PENDING → OVERDUE — Any schedule whose
scheduledMonthrepresents a month that has already ended and whose status is stillPENDINGis markedOVERDUE. This surfaces missed visits on the dashboard and in reports. - OVERDUE → EXPIRED — Schedules that remain pending even further into the future (past a system-defined grace period) are transitioned to
EXPIREDand removed from the active workload view.
Maintenance checklist
When a technician visits a dispenser, they submit a checklist viaPOST /api/maintenance/[id]/checklist. A schedule can only have one checklist — submitting twice returns 400 Bad Request.
MaintenanceChecklist model fields:
| Field | Type | Description |
|---|---|---|
condicionGeneral | ConditionRating | Overall condition: GOOD, FAIR, POOR, or CRITICAL. Required. |
fallas | Json? | JSON string array of detected operational failures (e.g., ["Presión baja"]). |
problemasEstructurales | Json? | JSON string array of structural issues found during inspection. |
observaciones | String? | Free-text technician notes. |
fotosUrls | Json? | JSON array of photo URLs or base64-encoded images uploaded during the visit. |
completedById | String | The technician who submitted the checklist. |
completedAt | DateTime | Timestamp of submission. |
consumablesUsed:
- Serialized consumables (
type: "SERIALIZED") — the specificConsumablerecord is marked inactive and the plant’s aggregateStockEntryis decremented by 1. - Bulk materials (
type: "BULK") — the plant’sStockEntryquantity is decremented bycantidad. If there is insufficient stock, the entire transaction is rolled back and a descriptive error is returned.
status is automatically advanced to COMPLETED.
The
consumablesUsed array in the checklist payload directly drives DispenserConsumableHistory — previous open records for the same materialCode are closed (removedAt = now()) and new records are created with a calculated expiresAt based on MaterialCatalog.expirationMonths.Digital approval signatures
Once one or more maintenance visits are completed, a technician groups them into aMaintenanceApproval and presents it to the customer for signature. The approval flow is:
Create approval intent
Call
POST /api/maintenance/approvals with a scheduleIds array. All referenced schedules must have status = COMPLETED. An empty MaintenanceApproval record is created and linked to those schedules. The response returns the id of the approval.Customer signs
The customer fills in their name, identity (DNI), and draws their signature on the digital pad. The signature image is captured as a base64 string (
signatureData).Cryptographic signing
The system builds a deterministic payload from: sorted dispenser IDs, exact timestamp, device info, technician ID, customer name, and customer identity. This payload is hashed with SHA-512 (
signatureHash) and then the hash is encrypted with AES-256-GCM (encryptedHash). Both the raw payload (hashPayload), the hash, and the encrypted hash are stored.MaintenanceApproval record:
| Field | Description |
|---|---|
customerName | Full name of the signing customer representative. |
customerIdentity | DNI or national ID number. |
signatureData | Base64-encoded PNG of the handwritten signature. |
signatureHash | SHA-512 hex digest (128 characters) of the audit payload. |
encryptedHash | AES-256-GCM encrypted form of signatureHash, stored as base64(iv):base64(authTag):base64(ciphertext). |
hashPayload | The raw string used to produce the hash (stored for re-verification). |
signedAt | Exact timestamp of signing. |
ipAddress | IPv4/IPv6 address of the signer’s device. |
deviceInfo | Browser user-agent or device fingerprint string. |
technicianId | The technician who initiated the approval. |
Signature verification
Any stored approval can be cryptographically verified — without requiring re-entry of any data — by callingPOST /api/maintenance/approvals/[id]/verify. The endpoint is authenticated (requires maintenance:read) but is otherwise read-only and non-destructive.
- Retrieve the stored
encryptedHashandhashPayloadfor the approval. - Decrypt
encryptedHashusing the server’s AES-256-GCM key to recover the original SHA-512 hash. - Recompute SHA-512 from the stored
hashPayload. - Compare both hashes using a timing-safe equality check (
crypto.timingSafeEqual) to prevent timing-based attacks.
valid is false, the approval record has been tampered with after signing and should be treated as compromised.
Maintenance reports
Completed and signed maintenance data can be exported to a PDF report viaPOST /api/dashboard/pdf. The PDF is generated server-side using @react-pdf/renderer and streamed directly to the client as an attachment.
type field controls the report template:
performance— Operational performance report, including SLA, MTTR, and MTBF KPIs.salud— Fleet health report, including dispenser health scores, distribution (óptimo / estable / crítico), and a top-10 ranking.
Content-Type: application/pdf and Content-Disposition: attachment; filename="Reporte_<type>.pdf".
Required permissions
| Permission | Roles |
|---|---|
maintenance:read | ADMIN, SUPERVISOR, TECHNICIAN, CLIENT_RESPONSIBLE, CLIENT_REQUESTER |
maintenance:write | ADMIN, SUPERVISOR, TECHNICIAN |