Files
soft_usite/PLAN_RENOVACIONES.md
T
2026-04-29 23:36:30 -05:00

23 KiB

Plan de Trabajo — Módulo de Renovaciones y Contratos

Sistema completo de gestión de servicios, clientes, contratos y notificaciones automáticas de vencimiento.


Índice

  1. Visión General
  2. Arquitectura de Datos
  3. Módulos del Sistema
  4. Flujo de Trabajo
  5. Sistema de Notificaciones
  6. Plantillas de Correo
  7. Configuración SMTP
  8. Plan de Implementación por Fases
  9. Estructura de Archivos
  10. API Endpoints

1. Visión General

El módulo de Renovaciones permite a la empresa gestionar el ciclo de vida completo de sus servicios vendidos a clientes: creación de catálogo, asignación a clientes con fechas de vencimiento, envío automático y manual de correos de cobro/renovación, y configuración total de notificaciones programadas.

Capacidades principales

Capacidad Descripción
Catálogo de servicios Crear servicios con precio, tipo (renovable/único), periodicidad
Gestión de clientes CRUD completo de clientes con datos de contacto
Contratos / Asignaciones Relacionar clientes con servicios, definir fechas de inicio y vencimiento
Agrupación de correos Si varios servicios vencen la misma fecha → un solo correo con el total
Notificaciones programadas Configurar N días antes del vencimiento, con cron interno
Editor de plantillas Editor visual con preview en tiempo real
Configuración SMTP Panel para cambiar servidor de correo sin reiniciar

2. Arquitectura de Datos

Modelo Entidad-Relación

clientes ─────────── contratos ─────────── servicios
    │                    │                     │
    │                    ├── fecha_inicio       ├── tipo: renovable / unico
    │                    ├── fecha_vencimiento  ├── precio
    │                    ├── estado             ├── periodicidad (mensual/anual/etc)
    │                    └── notas              └── descripcion
    │
    └── email, telefono, empresa, ...

contratos ──── notificaciones_log
                    ├── fecha_envio
                    ├── tipo (aviso / vencimiento / recordatorio)
                    └── estado (enviado / fallido / pendiente)

smtp_config (tabla singleton)
plantillas_correo
notificacion_reglas (días antes, activo, plantilla_id)

Tablas SQL

-- Servicios / Productos
CREATE TABLE servicios (
    id              BIGSERIAL PRIMARY KEY,
    created_at      TIMESTAMPTZ,
    updated_at      TIMESTAMPTZ,
    deleted_at      TIMESTAMPTZ,
    nombre          TEXT NOT NULL,
    descripcion     TEXT,
    precio          NUMERIC(12,2) NOT NULL DEFAULT 0,
    moneda          TEXT NOT NULL DEFAULT 'COP',
    tipo            TEXT NOT NULL DEFAULT 'renovable', -- 'renovable' | 'unico'
    periodicidad    TEXT,                              -- 'mensual' | 'trimestral' | 'semestral' | 'anual' | NULL
    activo          BOOLEAN NOT NULL DEFAULT true
);

-- Clientes
CREATE TABLE clientes (
    id              BIGSERIAL PRIMARY KEY,
    created_at      TIMESTAMPTZ,
    updated_at      TIMESTAMPTZ,
    deleted_at      TIMESTAMPTZ,
    nombre          TEXT NOT NULL,
    empresa         TEXT,
    email           TEXT NOT NULL,
    email_cc        TEXT,             -- correos adicionales separados por coma
    telefono        TEXT,
    documento       TEXT,
    notas           TEXT,
    activo          BOOLEAN NOT NULL DEFAULT true
);

-- Contratos / Asignaciones
CREATE TABLE contratos (
    id                  BIGSERIAL PRIMARY KEY,
    created_at          TIMESTAMPTZ,
    updated_at          TIMESTAMPTZ,
    deleted_at          TIMESTAMPTZ,
    cliente_id          BIGINT NOT NULL REFERENCES clientes(id),
    servicio_id         BIGINT NOT NULL REFERENCES servicios(id),
    fecha_inicio        DATE NOT NULL,
    fecha_vencimiento   DATE NOT NULL,
    precio_acordado     NUMERIC(12,2),  -- puede diferir del precio base
    estado              TEXT NOT NULL DEFAULT 'activo', -- 'activo' | 'vencido' | 'cancelado' | 'renovado'
    auto_renovar        BOOLEAN NOT NULL DEFAULT false,
    notas               TEXT
);

-- Reglas de notificación (configurable por días antes del vencimiento)
CREATE TABLE notificacion_reglas (
    id              BIGSERIAL PRIMARY KEY,
    created_at      TIMESTAMPTZ,
    updated_at      TIMESTAMPTZ,
    nombre          TEXT NOT NULL,          -- ej: "Aviso 30 días antes"
    dias_antes      INT NOT NULL,           -- ej: 30, 15, 7, 1
    plantilla_id    BIGINT REFERENCES plantillas_correo(id),
    activo          BOOLEAN NOT NULL DEFAULT true,
    aplica_a        TEXT NOT NULL DEFAULT 'todos' -- 'todos' | 'renovable' | 'unico'
);

-- Plantillas de correo
CREATE TABLE plantillas_correo (
    id              BIGSERIAL PRIMARY KEY,
    created_at      TIMESTAMPTZ,
    updated_at      TIMESTAMPTZ,
    nombre          TEXT NOT NULL,
    asunto          TEXT NOT NULL,
    cuerpo_html     TEXT NOT NULL,   -- soporta variables: {{.ClienteNombre}}, {{.Servicios}}, {{.Total}}, etc.
    tipo            TEXT NOT NULL DEFAULT 'renovacion' -- 'renovacion' | 'vencimiento' | 'pago' | 'personalizado'
);

-- Log de notificaciones enviadas
CREATE TABLE notificaciones_log (
    id              BIGSERIAL PRIMARY KEY,
    created_at      TIMESTAMPTZ,
    cliente_id      BIGINT REFERENCES clientes(id),
    regla_id        BIGINT REFERENCES notificacion_reglas(id),
    contratos_ids   TEXT,           -- JSON array de IDs agrupados
    fecha_envio     TIMESTAMPTZ,
    estado          TEXT NOT NULL DEFAULT 'pendiente', -- 'enviado' | 'fallido' | 'pendiente'
    error_msg       TEXT,
    asunto          TEXT,
    preview_html    TEXT            -- copia del correo enviado
);

-- Configuración SMTP (singleton, solo 1 fila activa)
CREATE TABLE smtp_config (
    id              BIGSERIAL PRIMARY KEY,
    updated_at      TIMESTAMPTZ,
    host            TEXT NOT NULL,
    port            INT NOT NULL DEFAULT 587,
    username        TEXT NOT NULL,
    password        TEXT NOT NULL,  -- almacenado cifrado (AES-256)
    encryption      TEXT NOT NULL DEFAULT 'tls', -- 'tls' | 'ssl' | 'none'
    from_address    TEXT NOT NULL,
    from_name       TEXT NOT NULL,
    activo          BOOLEAN NOT NULL DEFAULT true
);

3. Módulos del Sistema

3.1 Catálogo de Servicios (/app/servicios)

Funcionalidades:

  • Listar servicios con filtro por tipo y estado
  • Crear/editar/eliminar servicio
  • Campos: nombre, descripción, precio, moneda, tipo (renovable | único), periodicidad
  • Badge visual diferenciando renovables de únicos
  • Indicador de cuántos contratos activos tiene cada servicio

Campos del formulario:

Campo Tipo Requerido Notas
Nombre texto
Descripción textarea
Precio decimal
Moneda select COP / USD / EUR
Tipo radio Renovable / Único
Periodicidad select si renovable Mensual / Trimestral / Semestral / Anual
Activo toggle

3.2 Clientes (/app/clientes)

Funcionalidades:

  • CRUD completo de clientes
  • Vista detalle del cliente con todos sus contratos activos/vencidos
  • Historial de correos enviados al cliente
  • Múltiples emails CC por cliente

Campos del formulario:

Campo Tipo Requerido
Nombre completo texto
Empresa / Razón social texto
Email principal email
Emails CC texto (separados por coma)
Teléfono / WhatsApp texto
Documento (NIT/CC) texto
Notas internas textarea
Activo toggle

3.3 Contratos / Asignaciones (/app/contratos)

Vista principal:

  • Tabla con columnas: Cliente, Servicio, Fecha inicio, Fecha vencimiento, Estado, Días restantes
  • Filtros: por cliente, por servicio, por estado, por rango de fechas
  • Indicador visual de urgencia (verde > 30 días, amarillo 1-30 días, rojo vencido)
  • Botón "Renovar" que crea un nuevo contrato a partir del vencido

Formulario de asignación:

Campo Tipo Notas
Cliente select buscable autocompletado
Servicio select buscable muestra precio base
Fecha inicio date default: hoy
Fecha vencimiento date calculada automáticamente según periodicidad
Precio acordado decimal editable, pre-rellena con precio del servicio
Auto-renovar toggle crea nuevo contrato automáticamente al vencer
Notas textarea

Vista detalle del contrato:

  • Historial de renovaciones anteriores
  • Timeline de notificaciones enviadas
  • Botón "Enviar correo manual"
  • Botón "Renovar ahora"

3.4 Reglas de Notificación (/app/notificaciones/reglas)

Permite configurar cuándo y qué correo enviar antes del vencimiento.

Ejemplos de reglas:

Regla Días antes Plantilla Aplica a
Aviso temprano 30 Plantilla "Renovación próxima" Renovables
Recordatorio 7 Plantilla "Vence en 7 días" Todos
Último aviso 1 Plantilla "Vence mañana" Todos
Vencido 0 Plantilla "Servicio vencido" Todos

Campos:

Campo Tipo Notas
Nombre de la regla texto
Días antes del vencimiento número 0 = día del vencimiento
Plantilla de correo select
Aplica a select Todos / Solo renovables / Solo únicos
Activo toggle

3.5 Plantillas de Correo (/app/notificaciones/plantillas)

Editor visual con:

  • Editor de HTML (textarea con resaltado)
  • Panel de variables disponibles (click to insert)
  • Preview en tiempo real lado a lado
  • Envío de correo de prueba a email específico

Variables disponibles en plantillas:

{{.ClienteNombre}}        → Nombre del cliente
{{.ClienteEmpresa}}       → Empresa del cliente
{{.Servicios}}            → Tabla HTML de servicios (nombre, vencimiento, precio)
{{.Total}}                → Total en moneda local
{{.FechaVencimiento}}     → Fecha de vencimiento (la más próxima si hay varias)
{{.DiasRestantes}}        → Días que faltan para vencer
{{.LinkPago}}             → URL de pago (configurable)
{{.EmpresaNombre}}        → Nombre de la empresa (desde config)
{{.FechaActual}}          → Fecha de hoy

Tipo de plantillas:

Tipo Uso
renovacion Aviso de renovación próxima
vencimiento El servicio está a punto de vencer / vencido
pago Confirmación o solicitud de pago
personalizado Uso libre / envío manual

3.6 Historial de Envíos (/app/notificaciones/historial)

  • Tabla con todos los correos enviados
  • Filtro por cliente, fecha, estado (enviado/fallido)
  • Ver HTML del correo enviado (modal preview)
  • Reenviar correo fallido con un clic

3.7 Configuración SMTP (/app/configuracion/smtp)

Panel para configurar el servidor de correo saliente sin tocar archivos.

Campos:

Campo Tipo Default
Host SMTP texto smtp.gmail.com
Puerto número 587
Usuario email
Contraseña password (cifrada)
Cifrado select TLS / SSL / Ninguno
Nombre remitente texto U-site
Email remitente email
  • Botón "Probar conexión" — envía correo de prueba y muestra resultado
  • La config se guarda en BD (cifrada) y sobrescribe la del config.yml en runtime

4. Flujo de Trabajo

Flujo de creación de un contrato

1. Crear servicio en catálogo
       ↓
2. Crear/seleccionar cliente
       ↓
3. Crear contrato (asignar servicio + fechas + precio)
       ↓
4. Sistema calcula automáticamente
   cuándo disparar notificaciones
   según reglas configuradas
       ↓
5. Cron diario evalúa contratos
   y dispara correos agrupados

Lógica de agrupación de correos

Para cada cliente con contratos próximos a vencer:
  │
  ├── mismo_dia = contratos donde fecha_vencimiento == misma fecha
  │     └── → UN solo correo con tabla de servicios + total sumado
  │
  └── fechas_distintas = contratos con fechas diferentes
        └── → UN correo por cada fecha de vencimiento distinta

Ejemplo práctico:

  • Cliente "Empresa ABC" tiene:
    • Dominio → vence 15 mayo → correo independiente
    • Hosting + SSL → ambos vencen 15 mayo → un solo correo con total combinado
    • Mantenimiento → vence 30 mayo → correo independiente

5. Sistema de Notificaciones

Arquitectura del Cron

El sistema usa un cron interno en Go (librería robfig/cron) que se inicia al arrancar la aplicación.

// Se ejecuta diariamente a las 8:00 AM
cron.AddFunc("0 8 * * *", jobs.ProcesarVencimientos)

Job: ProcesarVencimientos

1. Obtener todas las reglas activas (ordenadas por dias_antes)
2. Para cada regla:
   a. Buscar contratos donde:
      - estado = 'activo'
      - fecha_vencimiento = HOY + dias_antes
      - NO exista ya un envío del mismo tipo en notificaciones_log
   b. Agrupar contratos encontrados por cliente + fecha_vencimiento
   c. Para cada grupo:
      - Renderizar plantilla con datos del grupo
      - Enviar correo (con CC si aplica)
      - Registrar en notificaciones_log
3. Si auto_renovar = true y estado = vencido:
   - Crear nuevo contrato con nueva fecha de vencimiento
   - Actualizar estado del anterior a 'renovado'

Estados de un contrato

activo ──────────── (cron notifica) ──────────── vence
  │                                                 │
  ├── renovado (si auto_renovar o manual)            │
  │                                                  │
  └── cancelado (manual)                         vencido

6. Plantillas de Correo

Variables en el cuerpo HTML

El motor de plantillas usa html/template de Go (ya usado en el proyecto).

Plantilla base sugerida — Renovación

<!DOCTYPE html>
<html>
<body style="font-family:Inter,sans-serif; background:#f8fafc; padding:20px">
  <div style="max-width:600px; margin:0 auto; background:white; border-radius:12px; overflow:hidden">
    <!-- Header -->
    <div style="background:#1e293b; padding:24px; text-align:center">
      <h1 style="color:#8eb02f; margin:0">U-site</h1>
    </div>
    <!-- Body -->
    <div style="padding:32px">
      <p>Hola <strong>{{.ClienteNombre}}</strong>,</p>
      <p>Te recordamos que los siguientes servicios vencen próximamente:</p>

      <!-- Tabla de servicios -->
      <table style="width:100%; border-collapse:collapse; margin:20px 0">
        <thead>
          <tr style="background:#f1f5f9">
            <th style="padding:10px; text-align:left">Servicio</th>
            <th style="padding:10px; text-align:right">Vencimiento</th>
            <th style="padding:10px; text-align:right">Valor</th>
          </tr>
        </thead>
        <tbody>
          {{range .Servicios}}
          <tr>
            <td style="padding:10px; border-bottom:1px solid #e2e8f0">{{.Nombre}}</td>
            <td style="padding:10px; border-bottom:1px solid #e2e8f0; text-align:right">{{.FechaVencimiento}}</td>
            <td style="padding:10px; border-bottom:1px solid #e2e8f0; text-align:right">{{.Precio}}</td>
          </tr>
          {{end}}
        </tbody>
        <tfoot>
          <tr>
            <td colspan="2" style="padding:10px; font-weight:bold">Total</td>
            <td style="padding:10px; font-weight:bold; text-align:right; color:#8eb02f">{{.Total}}</td>
          </tr>
        </tfoot>
      </table>

      <p style="text-align:center; margin:30px 0">
        <a href="{{.LinkPago}}" style="background:#8eb02f; color:white; padding:14px 28px; border-radius:8px; text-decoration:none; font-weight:bold">
          Renovar ahora →
        </a>
      </p>
    </div>
    <!-- Footer -->
    <div style="background:#f8fafc; padding:16px; text-align:center; font-size:12px; color:#64748b">
      © 2026 U-site — {{.EmpresaNombre}}
    </div>
  </div>
</body>
</html>

Preview en tiempo real

  • Frontend: Alpine.js + <iframe srcdoc="..."> actualizado en cada keystroke con debounce de 500ms
  • El preview sustituye las variables por datos de ejemplo configurables
  • Botón "Enviar prueba" → POST /app/notificaciones/plantillas/:id/test con email destino

7. Configuración SMTP

Almacenamiento seguro

La contraseña SMTP se cifra con AES-256-GCM antes de guardar en BD, usando el app_jwt_secret como clave de cifrado (ya disponible en config.yml).

// Guardar: cifrar contraseña antes de INSERT
encrypted := utils.Encrypt(password, app.Http.Token.AppJwtSecret)

// Usar: descifrar antes de conectar
plain := utils.Decrypt(encrypted, app.Http.Token.AppJwtSecret)

Override en runtime

Al guardar una nueva configuración SMTP el sistema:

  1. Descifra la contraseña
  2. Actualiza app.Http.Mail.* en memoria
  3. No requiere reiniciar el servidor

8. Plan de Implementación por Fases

Fase 1 — Modelos y migración de BD (~1-2 días)

  • Crear modelos Go: Servicio, Cliente, Contrato, NotificacionRegla, PlantillaCorreo, NotificacionLog, SmtpConfig
  • Agregar al Migrate() todas las nuevas tablas
  • Seed inicial: 1 plantilla por defecto, reglas básicas (30, 7, 1 día)

Fase 2 — CRUD Servicios y Clientes (~2 días)

  • Controller + rutas para Servicios
  • Controller + rutas para Clientes
  • Vistas HTML: lista, formulario (modal), detalle
  • Búsqueda y paginación

Fase 3 — Contratos / Asignaciones (~2-3 días)

  • Controller + rutas para Contratos
  • Select2 para cliente y servicio con autocompletado AJAX
  • Cálculo automático de fecha de vencimiento según periodicidad
  • Vista principal con indicadores de color por días restantes
  • Botón renovar (clona contrato con nueva fecha)
  • Historial de renovaciones

Fase 4 — Plantillas de correo con preview (~2 días)

  • CRUD de plantillas
  • Editor HTML con preview en <iframe srcdoc>
  • API /plantillas/:id/preview → renderiza con datos de ejemplo
  • Endpoint /plantillas/:id/test → envía correo de prueba

Fase 5 — Reglas de notificación y Cron (~2 días)

  • CRUD de reglas
  • Job ProcesarVencimientos con lógica de agrupación
  • Inicializar cron al arrancar (app.go)
  • Evitar doble envío (check en notificaciones_log)

Fase 6 — Historial y reenvío (~1 día)

  • Vista historial de envíos con filtros
  • Modal preview del correo enviado
  • Botón reenviar

Fase 7 — Configuración SMTP (~1 día)

  • Panel CRUD de configuración SMTP
  • Cifrado AES de contraseña
  • Override en runtime de app.Http.Mail
  • Botón "Probar conexión"

Fase 8 — Integración al menú y permisos (~0.5 días)

  • Agregar módulo "Renovaciones" al sidebar
  • Asignar permisos RBAC (Casbin)
  • Registrar módulo y submodules en BD

9. Estructura de Archivos

pkg/
  models/
    servicio.go
    cliente.go
    contrato.go
    notificacion_regla.go
    plantilla_correo.go
    notificacion_log.go
    smtp_config.go

rest/
  controllers/
    servicio_controller.go
    cliente_controller.go
    contrato_controller.go
    notificacion_controller.go
    plantilla_controller.go
    smtp_config_controller.go
  routes/
    renovaciones.go          ← agrupa todas las rutas del módulo

pkg/
  services/
    renovacion_service.go    ← lógica de agrupación y envío
    cron_service.go          ← job diario de vencimientos

resources/views/
  servicios/
    index.html
    form.html
  clientes/
    index.html
    form.html
    detalle.html
  contratos/
    index.html
    form.html
    detalle.html
  notificaciones/
    reglas.html
    plantillas.html
    historial.html
    editor.html              ← editor con preview
  configuracion/
    smtp.html
  emails/
    renovacion.html          ← plantilla base de correo
    vencimiento.html
    pago.html

10. API Endpoints

Servicios

Método Ruta Descripción
GET /app/servicios Lista de servicios
GET /app/loadservicios JSON para DataTable
POST /app/servicios Crear servicio
PUT /app/servicios/:id Actualizar
DELETE /app/servicios/:id Eliminar

Clientes

Método Ruta Descripción
GET /app/clientes Lista de clientes
GET /app/loadclientes JSON para DataTable / Select2
GET /app/clientes/:id Detalle con contratos
POST /app/clientes Crear
PUT /app/clientes/:id Actualizar
DELETE /app/clientes/:id Eliminar

Contratos

Método Ruta Descripción
GET /app/contratos Lista con filtros
GET /app/loadcontratos JSON para DataTable
POST /app/contratos Crear asignación
PUT /app/contratos/:id Actualizar
POST /app/contratos/:id/renovar Renovar contrato
POST /app/contratos/:id/correo Envío manual de correo
DELETE /app/contratos/:id Cancelar

Notificaciones / Plantillas

Método Ruta Descripción
GET /app/notificaciones/reglas Lista de reglas
POST /app/notificaciones/reglas Crear regla
PUT /app/notificaciones/reglas/:id Actualizar
DELETE /app/notificaciones/reglas/:id Eliminar
GET /app/notificaciones/plantillas Lista de plantillas
POST /app/notificaciones/plantillas Crear
PUT /app/notificaciones/plantillas/:id Actualizar
POST /app/notificaciones/plantillas/:id/preview Renderiza con datos de ejemplo
POST /app/notificaciones/plantillas/:id/test Enviar correo de prueba
GET /app/notificaciones/historial Log de envíos
POST /app/notificaciones/historial/:id/reenviar Reenviar

SMTP

Método Ruta Descripción
GET /app/configuracion/smtp Ver configuración actual
POST /app/configuracion/smtp Guardar configuración
POST /app/configuracion/smtp/test Probar conexión

Notas Técnicas

  • Cron: usar github.com/robfig/cron/v3 (agregar a go.mod)
  • Preview correo: <iframe srcdoc> + Alpine x-data con debounce — sin backend para el preview inicial, JS reemplaza variables con datos de ejemplo en el cliente
  • Agrupación de correos: lógica en renovacion_service.go, agrupa por cliente_id + fecha_vencimiento antes de enviar
  • Doble envío: antes de enviar cada grupo, consultar notificaciones_log filtrando DATE(created_at) = TODAY AND cliente_id = X AND regla_id = Y AND contratos_ids CONTAINS Z
  • Moneda: formatear con fmt.Sprintf("$ %,.2f", valor) o librería golang.org/x/text
  • Auto-renovar: el job crea el nuevo contrato con fecha_inicio = fecha_vencimiento_anterior + 1 día y la nueva fecha_vencimiento calculada según periodicidad del servicio

Documento generado el 29 de abril de 2026 — U-site Admin System