Automatizacion controlada
Alertas MedSync y jobs
Alertas MedSync
alerts.expiredTrials: trials ya vencidos.alerts.trialsExpiringNext7Days: trials por vencer.alerts.suspendedBusinesses: negocios suspendidos.alerts.pastDueBusinesses: negocios con pago pendiente.alerts.cancelledBusinesses: negocios cancelados.
Jobs MedSync
El dashboard consulta el estado de automatizacion y permite un dry-run seguro. Por
defecto los jobs estan apagados y no modifican negocios sin variables explicitas.
| Configuracion | Variable | Modifica BD? | Uso recomendado |
|---|
| Jobs apagados | SAAS_JOBS_ENABLED=false | No | Produccion inicial. |
| Trial job apagado | SAAS_TRIAL_EXPIRATION_JOB_ENABLED=false | No | Evita cambios automaticos. |
| Modo alerta | SAAS_TRIAL_EXPIRATION_JOB_MODE=alert_only | No | Monitoreo seguro. |
| Marcar pago pendiente | SAAS_TRIAL_EXPIRATION_JOB_MODE=mark_past_due | Si, solo si jobs estan habilitados | Usar con control explicito. |
| Dry-run | dryRun=true | No | Pruebas seguras desde Super Admin. |
Job existente actualmente
El backend tiene un job MedSync real: saas_trial_lifecycle. Su objetivo es
revisar el ciclo de vida de negocios en trial, detectar trials vencidos,
detectar trials por vencer y, solo si la configuracion lo permite, convertir trials
vencidos a past_due.
No hay otros jobs MedSync productivos documentados en el codigo actual. Pagos,
notificaciones externas, emails, WhatsApp, facturacion y licencias avanzadas quedan
fuera de este job.
Que hace el boton dry-run
El boton del dashboard ejecuta el endpoint
POST /api/super-admin/jobs/saas/trial-lifecycle/run enviando
{ dryRun: true }. Eso obliga al backend a simular la ejecucion sin
actualizar negocios, aunque el servidor tenga jobs habilitados.
El resultado sirve para responder: cuantos trials estan vencidos, cuantos estan por
vencer, que negocios entran en cada grupo y si la configuracion actual permitiria
mutar datos en una ejecucion real.
Mapa actual de jobs MedSync
Para evitar confusion: en el codigo actual solo existe un job MedSync implementado. Las
otras cards muestran automatizaciones naturales del roadmap, pero no existen todavia
como jobs productivos.
saas_trial_lifecycle
Estado: implementado.
Detecta trials vencidos y trials por vencer. Puede convertir trials vencidos a
past_due solo con configuracion explicita.
saas_payment_reconciliation
Estado: no implementado.
Seria el job futuro para conciliacion de pagos o facturacion. No existe en el
backend actual y no se ejecuta.
saas_external_notifications
Estado: no implementado.
Seria el job futuro para avisos por email, WhatsApp o SMS. No existe actualmente y
no envia notificaciones.
Endpoints operativos de jobs MedSync
| Endpoint | Metodo | Uso en dashboard | Modifica datos? | Proteccion |
|---|
/api/super-admin/jobs/saas/status | GET | Cargar estado de automatizacion, modo y conteos. | No. | Token.Auth + requireRole(‘super_admin’) |
/api/super-admin/jobs/saas/trial-lifecycle/run | POST | Ejecutar dry-run desde el boton del dashboard. | No, si dryRun=true. | Token.Auth + requireRole(‘super_admin’) |
Variables para habilitar o deshabilitar jobs
La configuracion vive en src/config/saasJobs.js. Si una variable viene
ausente, invalida o fuera de rango, el codigo cae a defaults seguros.
| Variable | Default seguro | Que controla | Para habilitar cambios reales |
|---|
SAAS_JOBS_ENABLED | false | Interruptor global de jobs MedSync. | Debe estar en true. |
SAAS_TRIAL_EXPIRATION_JOB_ENABLED | false | Interruptor especifico del job de trials. | Debe estar en true. |
SAAS_TRIAL_EXPIRATION_JOB_MODE | alert_only | Define si solo detecta o tambien convierte a past_due. | Debe ser mark_past_due. |
SAAS_TRIAL_EXPIRING_SOON_DAYS | 7 | Ventana para trials por vencer. | No habilita cambios; solo cambia la ventana de alerta. |
Condicion exacta para que una ejecucion real pueda modificar negocios:
SAAS_JOBS_ENABLED=true,
SAAS_TRIAL_EXPIRATION_JOB_ENABLED=true,
SAAS_TRIAL_EXPIRATION_JOB_MODE=mark_past_due y request con
dryRun=false.
# Configuracion recomendada para produccion inicial: observar sin mutar
SAAS_JOBS_ENABLED=false
SAAS_TRIAL_EXPIRATION_JOB_ENABLED=false
SAAS_TRIAL_EXPIRATION_JOB_MODE=alert_only
SAAS_TRIAL_EXPIRING_SOON_DAYS=7
# Configuracion que permite cambios reales, solo con control explicito
SAAS_JOBS_ENABLED=true
SAAS_TRIAL_EXPIRATION_JOB_ENABLED=true
SAAS_TRIAL_EXPIRATION_JOB_MODE=mark_past_due
SAAS_TRIAL_EXPIRING_SOON_DAYS=7
Respuesta del dry-run: como leerla
| Campo | Significado | Uso operativo |
|---|
dryRun | Confirma si la ejecucion fue simulada. | Debe ser true desde el boton del dashboard. |
expiredTrials | Cantidad de trials vencidos encontrados. | Indica negocios que ya no deberian operar como trial vigente. |
trialsExpiringSoon | Cantidad de trials que vencen dentro de la ventana configurada. | Sirve para seguimiento comercial preventivo. |
updatedBusinesses | Cantidad de negocios actualizados. | En dry-run debe ser 0. |
canMutate | Indica si la configuracion permitiria cambios reales. | Ayuda a detectar si el servidor esta armado para mark_past_due. |
blockedReason | Explica por que una ejecucion real no podria modificar negocios. | Util para diagnosticar variables apagadas o modo incorrecto. |
expiredTrialBusinesses | Primeros negocios vencidos, compactados. | Permite revisar candidatos sin exponer datos clinicos. |
updatedBusinessIds | IDs actualizados en una corrida real. | En dry-run debe venir vacio. |
Flujo: consulta de status
- Dashboard carga
fetchSaasJobsStatus(). - Servicio llama
GET /api/super-admin/jobs/saas/status. - Backend valida token y rol Super Admin.
- DAO llama
getTrialLifecycleStatus(). - El job corre en modo lectura con
dryRun=true. - Devuelve configuracion, trials vencidos y trials por vencer.
Flujo: boton dry-run
- Usuario pulsa ejecutar validacion dry-run.
- Frontend envia
{ dryRun: true }. - Backend calcula snapshot de trials.
- No ejecuta
UPDATE. - No crea auditoria de cambio real.
- Frontend muestra vencidos, por vencer, actualizados y bloqueo si aplica.
Flujo completo del job
flowchart TD
A[Inicio saas_trial_lifecycle] --> B[Leer saasJobs config]
B --> C[Consultar trials vencidos]
B --> D[Consultar trials por vencer]
C --> E{dryRun es false?}
D --> R[Armar resumen]
E -- No --> R
E -- Si --> F{canMutateTrials?}
F -- No --> G[No modificar y devolver blockedReason]
F -- Si --> H[UPDATE business a past_due]
H --> I[Insertar auditoria SUPER_ADMIN_TRIAL_EXPIRED_TO_PAST_DUE]
I --> R
G --> R
R --> Z[Respuesta controlada al dashboard]
SQL real del snapshot
Estas consultas leen solo la tabla business. No consultan pacientes,
expedientes, recetas, citas, documentos clinicos, tratamientos ni odontograma.
-- Trials vencidos
SELECT id_business, name, subscription_status, plan, trial_ends_at
FROM business
WHERE subscription_status = 'trial'
AND active = 1
AND trial_ends_at IS NOT NULL
AND trial_ends_at < NOW()
ORDER BY trial_ends_at ASC;
-- Trials por vencer
SELECT id_business, name, subscription_status, plan, trial_ends_at
FROM business
WHERE subscription_status = 'trial'
AND active = 1
AND trial_ends_at IS NOT NULL
AND trial_ends_at >= NOW()
AND trial_ends_at <= DATE_ADD(NOW(), INTERVAL :days DAY)
ORDER BY trial_ends_at ASC;
SQL real cuando se permite modificar
Este UPDATE solo ocurre si dryRun=false y
canMutateTrials() devuelve verdadero. No cambia active,
status, plan, fechas de trial, usuarios ni datos clinicos.
UPDATE business
SET subscription_status = 'past_due',
status_reason = 'Trial vencido automaticamente',
updated_at = NOW(),
updated_by = 'saas_job'
WHERE id_business = :business_id
AND subscription_status = 'trial'
AND trial_ends_at IS NOT NULL
AND trial_ends_at < NOW();
Auditoria generada por cambio real
Si un trial vencido cambia realmente a past_due, el job registra
SUPER_ADMIN_TRIAL_EXPIRED_TO_PAST_DUE en admin_audit_log con
actor tecnico system y rol saas_job.
{
"business_id": "...",
"previous_subscription_status": "trial",
"new_subscription_status": "past_due",
"trial_ends_at": "...",
"job": "saas_trial_lifecycle",
"mode": "mark_past_due"
}
La metadata no debe incluir passwords, hashes, tokens, refresh tokens, secrets,
credenciales ni datos clinicos.
Que ejecuta en arranque del backend
La funcion startSaasTrialLifecycleJob() revisa la configuracion. Si los
jobs estan apagados, escribe un log operativo y no programa nada. Si estan activos,
ejecuta una primera corrida y despues agenda una ejecucion cada 24 horas con
setInterval.
| Estado | Comportamiento | Riesgo |
|---|
| Jobs apagados | No agenda ejecuciones automaticas. | Bajo; requiere operacion manual. |
alert_only | Detecta y reporta, pero no actualiza negocios. | Bajo; util para monitoreo. |
mark_past_due | Convierte trials vencidos a pago pendiente. | Medio; requiere configuracion explicita y supervision. |