up
This commit is contained in:
@@ -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
|
||||
<!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*
|
||||
Reference in New Issue
Block a user