682 lines
23 KiB
Markdown
682 lines
23 KiB
Markdown
# 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
|
|
<!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`).
|
|
|
|
```go
|
|
// 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*
|