feat: automatización de cotizaciones, contratos, actas y cuentas de cobro con IA

Implementa las 4 fases de la especificación de automatización: módulo de
plantillas/tarifas editable por el equipo, generación de PDF (HTML+JS vía
Chrome headless) para cotizaciones/contratos/arquitecturas/cuentas de cobro,
chat propio en el dashboard reutilizando el mismo motor y tools del bot de
Telegram, y nuevas tools del agente para crear estos documentos end-to-end.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Lizandro GD
2026-08-03 00:45:58 +00:00
co-authored by Claude Sonnet 5
parent 772a4a5656
commit 516220a8a1
32 changed files with 2272 additions and 132 deletions
+81
View File
@@ -104,6 +104,11 @@ func Migrate() {
// Tablero de tareas
&models.Tarea{},
&models.TareaComentario{},
// Automatización de cotizaciones, contratos, actas y cuentas de cobro con IA
&models.PlantillaDocumento{},
&models.Tarifa{},
&models.DocumentoGenerado{},
&models.Arquitectura{},
); err != nil {
log.Fatalf("Error during main migration: %v", err)
}
@@ -150,6 +155,9 @@ func Migrate() {
// Agregar submódulo "Partner Recursos" al módulo Portal de Clientes
SeedPartnerRecursos()
// Crear módulo y submódulos de Automatización IA (cotizaciones, contratos, asistente)
SeedAutomatizacionIA()
log.Println("Migration Completed...")
}
@@ -404,6 +412,79 @@ func SeedRenovaciones() {
log.Println("[SEED] Seed de Renovaciones completado.")
}
// SeedAutomatizacionIA inserta el módulo "Automatización IA" y sus submódulos
// (Asistente, Plantillas de Documento, Tarifas) si no existen, y los asigna al
// rol Administrador. Es idempotente.
func SeedAutomatizacionIA() {
db := app.Http.Database.DB
// 1. Crear/obtener módulo
var modulo models.Modules
result := db.Where("title = ?", "Automatización IA").First(&modulo)
if result.Error != nil {
modulo = models.Modules{
Title: "Automatización IA",
Description: "Cotizaciones, contratos, actas y cuentas de cobro generados por IA",
ModifiedAt: time.Now(),
}
if err := db.Create(&modulo).Error; err != nil {
log.Printf("[SEED] Error creando módulo Automatización IA: %v", err)
return
}
log.Printf("[SEED] Módulo 'Automatización IA' creado con ID %d", modulo.ID)
} else {
log.Printf("[SEED] Módulo 'Automatización IA' ya existe (ID %d)", modulo.ID)
}
// 2. Definir submódulos
entries := []struct{ title, desc, url string }{
{"Asistente", "Chat propio: mismo motor y tools que el bot de Telegram", "/app/asistente"},
{"Plantillas de Documento", "Fuente de verdad de cotización, contrato, acta y cuenta de cobro", "/app/automatizacion/plantillas"},
{"Tarifas", "Valor por hora, licencias, VMs y márgenes usados al cotizar", "/app/automatizacion/tarifas"},
}
var insertados []models.Submodules
for _, e := range entries {
var sub models.Submodules
if err := db.Where("url = ?", e.url).First(&sub).Error; err != nil {
sub = models.Submodules{
Title: e.title,
Description: e.desc,
Url: e.url,
ModuleId: modulo.ID,
ModifiedAt: time.Now(),
}
if err := db.Create(&sub).Error; err != nil {
log.Printf("[SEED] Error creando submódulo '%s': %v", e.title, err)
continue
}
log.Printf("[SEED] Submódulo '%s' creado (ID %d)", e.title, sub.ID)
} else {
if sub.ModuleId != modulo.ID {
if err := db.Model(&sub).Update("module_id", modulo.ID).Error; err != nil {
log.Printf("[SEED] Error actualizando module_id de '%s': %v", sub.Title, err)
} else {
log.Printf("[SEED] Submódulo '%s' reasignado a módulo Automatización IA", sub.Title)
}
} else {
log.Printf("[SEED] Submódulo '%s' ya existe (ID %d)", sub.Title, sub.ID)
}
}
insertados = append(insertados, sub)
}
// 3. Asignar solo al rol Administrador
var rolAdmin models.Roles
if err := db.Where("name = ?", "Administrador").First(&rolAdmin).Error; err != nil {
log.Printf("[SEED] Rol 'Administrador' no encontrado, se omite asignación: %v", err)
} else {
db.Model(&rolAdmin).Association("Submodules").Append(&insertados)
log.Printf("[SEED] Submódulos de Automatización IA asignados al rol 'Administrador'")
}
log.Println("[SEED] Seed de Automatización IA completado.")
}
// SeedIntegraciones crea el módulo "Integraciones" con los submódulos
// de Hostinger y Cloudflare, y los asigna a todos los roles existentes.
// Es idempotente: si el registro ya existe (por URL) lo reutiliza sin duplicar.