diff --git a/PLAN_RENOVACIONES.md b/PLAN_RENOVACIONES.md new file mode 100644 index 0000000..bcab74a --- /dev/null +++ b/PLAN_RENOVACIONES.md @@ -0,0 +1,681 @@ +# 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](#1-visión-general) +2. [Arquitectura de Datos](#2-arquitectura-de-datos) +3. [Módulos del Sistema](#3-módulos-del-sistema) +4. [Flujo de Trabajo](#4-flujo-de-trabajo) +5. [Sistema de Notificaciones](#5-sistema-de-notificaciones) +6. [Plantillas de Correo](#6-plantillas-de-correo) +7. [Configuración SMTP](#7-configuración-smtp) +8. [Plan de Implementación por Fases](#8-plan-de-implementación-por-fases) +9. [Estructura de Archivos](#9-estructura-de-archivos) +10. [API Endpoints](#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 + +```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. + +```go +// 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 + +```html + + +
+ + + +``` + +### Preview en tiempo real + +- Frontend: Alpine.js + `