Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Glemynart/SaaS/llms.txt

Use this file to discover all available pages before exploring further.

La Oficina Nítida includes a proactive alerts engine that surfaces two categories of risk: contracts that are approaching expiration, and employees who have active contracts but no registered contract document in their expediente. Alerts are created as internal platform records scoped to the authenticated tenant. They do not trigger external notifications (email or WhatsApp) in the current phase, but they power the dashboard indicators that operations teams check daily.

Alert Types

Contract expiration (CONTRATO_POR_VENCER / CONTRATO_VENCIDO)

Generated by POST /alerts/generate-contract-alerts. The engine scans all contracts in VIGENTE or RENOVADO state whose fechaFin falls within the next 30 calendar days. For each qualifying contract it creates up to three alerts — one per threshold — based on how many days remain:
Days remainingSeverityLabel
≤ 30 daysINFO”Contrato próximo a vencer (30 días)”
≤ 15 daysWARNING”Contrato próximo a vencer (15 días)”
≤ 7 daysCRITICAL”Contrato próximo a vencer (7 días)”
Each alert includes a human-readable descripcion that names the employee (via formatNombreCompleto() from @saas/types) and the exact expiration date formatted for the es-CO locale.

Missing document alerts

Generated by POST /alerts/generate-missing-doc-alerts. The engine checks every ACTIVO employee who does not have any contract in VIGENTE or RENOVADO state. For each such employee it creates a WARNING-severity alert titled "Empleado sin contrato registrado".

Alert Severity and Status

Severity levels

ValueMeaning
INFOInformational notice — action advisable but not urgent
WARNINGAction recommended soon
CRITICALImmediate action required

Alert status lifecycle

ValueMeaning
OPENActive alert — not yet acknowledged or resolved
RESOLVEDAlert has been acknowledged and closed by an operator
The Prisma schema also defines a legacy Alerta model with AlertaEstado values (PENDIENTE, ENVIADA, FALLIDA) and AlertaTipo values (CONTRATO_POR_VENCER, CONTRATO_VENCIDO). The active alerts system described on this page uses the Alert model with AlertStatus (OPEN, RESOLVED) and AlertSeverity (INFO, WARNING, CRITICAL). All new development must target the Alert model exclusively.

Generating Alerts

Contract expiration alerts

POST /alerts/generate-contract-alerts
Requires ADMIN role. The engine:
  1. Finds all VIGENTE/RENOVADO contracts with fechaFin between now and now + 30 days.
  2. For each contract, calculates diasRestantes = floor((fechaFin - now) / ms_per_day).
  3. Iterates the three severity thresholds (30, 15, 7 days) and skips any for which diasRestantes > threshold.
  4. For each qualifying threshold, checks for an existing OPEN alert with the same (expedienteDocId, severity) or (employeeId, severity, titulo) combination to avoid duplicates.
  5. Creates the alert only if no duplicate exists.
Returns:
{
  "created": 3,
  "message": "3 alertas de contrato generadas"
}

Missing document alerts

POST /alerts/generate-missing-doc-alerts
Requires ADMIN role. The engine:
  1. Finds all ACTIVO employees for the tenant (excluding soft-deleted records).
  2. For each employee, checks whether any contract exists in VIGENTE or RENOVADO state.
  3. If no active contract is found and no open "Empleado sin contrato registrado" alert already exists for that employee, creates a new WARNING alert.
Returns:
{
  "created": 5,
  "message": "5 alertas de documentos faltantes generadas"
}

Querying Alerts

List all alerts

GET /alerts?severity=WARNING&status=OPEN&page=1&limit=20
Supported query parameters (AlertQueryDto):
ParameterTypeDescription
severityINFO | WARNING | CRITICALFilter by severity level
statusOPEN | RESOLVEDFilter by alert status
pagenumberPage number (default 1)
limitnumberPage size (default 20)
The response includes a stats object alongside the paginated data:
{
  "data": [...],
  "stats": {
    "open": 12,
    "resolved": 34,
    "critical": 2
  },
  "meta": {
    "total": 12,
    "page": 1,
    "limit": 20,
    "totalPages": 1
  }
}
Each alert record includes:
  • The linked employee object with all four name fields (primerNombre, segundoNombre, primerApellido, segundoApellido), cedula, and cargo.
  • The linked expedienteDoc object (id, nombre, categoria) when the alert was created with an expedienteDocId reference.

Get a single alert

GET /alerts/:id
Returns 404 if the alert does not exist within the authenticated tenant.

Resolving Alerts

PATCH /alerts/:id/resolve
Requires ADMIN or OPERADOR role. Sets the alert’s status to RESOLVED and records a resolvedAt timestamp. Resolved alerts remain in the system for audit and historical reporting — they are never deleted.

Deduplication

The alerts engine never creates duplicate alerts for the same risk. Before inserting a new alert, it checks for an existing OPEN record using one of two strategies: When an expedienteDocId is available (the contract has a linked document in the expediente):
WHERE tenantId = :tenantId
  AND expedienteDocId = :expedienteDocId
  AND severity = :severity
  AND status = 'OPEN'
When no expedienteDocId is available (the contract has no expediente document yet):
WHERE tenantId = :tenantId
  AND employeeId = :employeeId
  AND severity = :severity
  AND status = 'OPEN'
  AND titulo = :titulo
If a match is found, the alert is skipped. This means running the generation endpoints multiple times per day is safe — only genuinely new risk states produce new alerts.
Schedule POST /alerts/generate-contract-alerts to run daily via a cron job (e.g., at 07:00 Colombian time). Because the 30-day lookahead window slides forward every day, a contract expiring in 14 days will trigger a WARNING alert today that wasn’t created yesterday. Without a daily run, operators may miss the transition between severity levels entirely.
The dueDate field on a contract alert is set to the contract’s actual fechaFin — not to a calculated offset from today. This means the dueDate remains stable across repeated generation runs for the same contract: it always reflects the real expiration date regardless of when the alert was created.

Build docs developers (and LLMs) love