From 0eef3f80a33324121914714e4bad11fd83fc53b9 Mon Sep 17 00:00:00 2001 From: Lizandro GD Date: Fri, 7 Aug 2026 03:00:02 +0000 Subject: [PATCH] =?UTF-8?q?Corrige=20tablas=20faltantes=20en=20producci?= =?UTF-8?q?=C3=B3n=20y=20bug=20de=20listado=20en=20Entidades?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - AutoMigrate migraba ~50 modelos en un solo llamado que se detiene en el primer error, dejando todo lo posterior (plantillas_documento, tarifas, documentos_generados, arquitecturas, telegram_staff_tokens) sin tabla, en silencio. Ahora se migra modelo por modelo: uno roto ya no bloquea al resto. - /app/contabilidad/entidades pedía datos a la misma URL de la página (devuelve HTML) en vez de /list (JSON) — el listado nunca traía nada. - Mensaje de bienvenida del asistente: estaba generado por la IA cada vez con calidad variable; ahora es determinístico para /ayuda y /start, con redacción revisada, y el system prompt pide el mismo formato cuando el usuario solo saluda. --- main.go | 18 +++++++---- migrations/migrate.go | 14 ++++++--- pkg/services/telegram_agent_service.go | 33 +++++++++++++++------ resources/views/contabilidad/entidades.html | 2 +- 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/main.go b/main.go index d974888..93815d8 100755 --- a/main.go +++ b/main.go @@ -61,8 +61,13 @@ func main() { migrations.MigratePortal() // Crear tabla de Landing Generator migrations.MigrateLanding() - // Crear tablas de integraciones y pasarelas si no existen - if err := app.Http.Database.DB.AutoMigrate( + // Crear tablas de integraciones y pasarelas si no existen. + // AutoMigrate(a, b, c...) se detiene en el primer modelo que falle: todo lo + // que viene después en la lista se queda sin tabla, en silencio (fue la + // causa real de que plantillas_documento/tarifas desaparecieran de + // producción — un modelo antes en la lista falló y todo lo posterior no se + // llegó a crear). Migrando uno por uno, un modelo roto no bloquea al resto. + modelosBase := []interface{}{ // Tablas base del sistema (columnas nuevas) &models.Users{}, &models.Roles{}, @@ -141,10 +146,11 @@ func main() { &models.Arquitectura{}, // Vinculación de Telegram para staff interno &models.TelegramStaffToken{}, - ); err != nil { - // AutoMigrate se detiene en el primer error: si esto falla, todos los - // modelos que venían después en la lista se quedan sin crear en silencio. - log.Printf("[MIGRATE] Error en AutoMigrate: %v", err) + } + for _, m := range modelosBase { + if err := app.Http.Database.DB.AutoMigrate(m); err != nil { + log.Printf("[MIGRATE] Error migrando %T: %v", m, err) + } } // Seed automático (idempotente) de módulos del sistema migrations.SeedRenovaciones() diff --git a/migrations/migrate.go b/migrations/migrate.go index 1dbb52c..459e664 100755 --- a/migrations/migrate.go +++ b/migrations/migrate.go @@ -25,8 +25,11 @@ type Session struct { func Migrate() { log.Println("Initiating migration...") - // Migrate the main application models - if err := app.Http.Database.DB.Migrator().AutoMigrate( + // Migrate the main application models. + // Un solo AutoMigrate(a, b, c...) se detiene en el primer modelo que falle: + // todo lo que viene después en la lista se queda sin tabla. Migrando uno por + // uno, un modelo roto no bloquea al resto (mismo fix que en main.go). + modelosPrincipales := []interface{}{ &models.Roles{}, &models.Users{}, &models.Modules{}, @@ -114,8 +117,11 @@ func Migrate() { &models.Arquitectura{}, // Vinculación de Telegram para staff interno &models.TelegramStaffToken{}, - ); err != nil { - log.Fatalf("Error during main migration: %v", err) + } + for _, m := range modelosPrincipales { + if err := app.Http.Database.DB.Migrator().AutoMigrate(m); err != nil { + log.Printf("[MIGRATE] Error migrando %T: %v", m, err) + } } // Open (or create) a SQLite database for sessions diff --git a/pkg/services/telegram_agent_service.go b/pkg/services/telegram_agent_service.go index dfda5c8..d29ec14 100644 --- a/pkg/services/telegram_agent_service.go +++ b/pkg/services/telegram_agent_service.go @@ -1505,6 +1505,7 @@ COMPORTAMIENTO: - Para listar datos, muestra los más relevantes en formato legible - Los valores monetarios son en COP (pesos colombianos) - Si una herramienta falla, explica el error y sugiere alternativas +- Si el usuario solo saluda o pregunta qué puedes hacer (sin pedir una acción concreta), responde igual que el comando /ayuda: breve, organizado por categoría, sin inventar capacidades que no tienes - En Coolify: si solo hay una instancia activa se usa automáticamente, no hace falta preguntar por config_id; si hay varias, consulta coolify_instancias y pregunta cuál. Para actuar sobre algo necesitas su UUID: búscalo con coolify_listar por nombre antes de llamar a coolify_accion. Reiniciar/desplegar es rutinario, hazlo directo; detener algo en producción sí confírmalo antes. Si un despliegue falla, revisa coolify_logs y coolify_deployments para explicar la causa concreta - Cuando el mensaje empiece con "[Documento adjunto recibido: ...]", el usuario acaba de enviar un archivo por Telegram (puede ser cualquier formato: PDF, foto, Word, Excel, etc.). Si es PDF o foto, viene adjunto a este mismo mensaje y puedes leerlo directamente. Si es otro formato (Word, Excel, zip, etc.) no puedes ver su contenido, solo el nombre — de todas formas guárdalo con la herramienta que corresponda usando lo que el usuario haya escrito como contexto, no rechaces el archivo por el formato. Decide primero de qué se trata el archivo antes de guardarlo: · Si es una factura → primero determina la dirección leyendo quién emite y quién recibe: si el emisor/vendedor es "U-Site S.A.S BIC" (o similar) y el destinatario es un cliente, es una factura de VENTA → identifica cliente, monto y número, resuelve el cliente_id con listar_clientes, y llama a adjuntar_factura. Si es al revés — un proveedor (AWS, Coolify, un hosting, cualquier tercero) le está facturando A U-Site — es una factura de COMPRA → llama a adjuntar_factura_compra con el nombre de ese proveedor, no busques cliente_id. Ante la duda, mira a nombre de quién sale la plata: si U-Site cobra, es venta; si U-Site paga, es compra @@ -1532,21 +1533,35 @@ func ProcessAgentMessage(chatID int64, userText string, ai *models.AiConfig) (st } return "Historial borrado. ¿En qué puedo ayudarte?", nil case "/ayuda", "/start": - return `Soy el agente administrador de U-Site. Puedo ayudarte con: + return `¡Hola! 👋 Soy el asistente de administración de U-Site S.A.S BIC. Esto es lo que puedo hacer por ti: -• Clientes y contratos -• Contabilidad (ingresos, egresos, cuentas por cobrar/pagar) -• Proyectos y tickets -• Tareas del equipo -• Deploy y gestión de apps en Coolify -• Monitoreo de servidores y URLs +✅ Clientes y facturación +Listar y crear clientes · facturas de venta y compra · cuentas por cobrar y pagar -Escríbeme en lenguaje natural, por ejemplo: +✅ Contratos y cotizaciones +Crear, renovar y gestionar contratos · generar cotizaciones y propuestas técnicas + +✅ Proyectos y soporte +Crear proyectos y fases · gestionar tickets · adjuntar documentos (PDF, fotos, Word, Excel, etc.) + +✅ Tareas del equipo +Crear y asignar tareas · ver el tablero Kanban + +✅ Coolify (deploy y control) +Listar apps, servicios y bases de datos · deploy, reinicio y detención · logs y despliegues + +✅ Contabilidad +Dashboard de ingresos/egresos · transacciones · tarifas + +✅ Monitoreo +Servidores y URLs vigiladas + +¿Qué necesitas? Escríbeme en lenguaje natural, por ejemplo: "muéstrame los clientes de esta semana" "haz deploy de la app abc123 en coolify 1" "¿cuánto ingresamos este mes?" -Comandos: /reset /instancias /ayuda`, nil +Comandos: /reset · /instancias · /ayuda`, nil case "/instancias": cfgs, err := models.GetAllCoolifyConfigs() if err != nil { diff --git a/resources/views/contabilidad/entidades.html b/resources/views/contabilidad/entidades.html index 69a230f..7035394 100644 --- a/resources/views/contabilidad/entidades.html +++ b/resources/views/contabilidad/entidades.html @@ -138,7 +138,7 @@ function entidadesApp() { async load(){ this.loading=true; try { - const r=await axios.get(`/app/contabilidad/entidades?page=${this.page}&search=${encodeURIComponent(this.search)}`); + const r=await axios.get(`/app/contabilidad/entidades/list?page=${this.page}&search=${encodeURIComponent(this.search)}`); this.items=r.data.items; this.total=r.data.total; this.totalPages=r.data.totalPages; } finally{ this.loading=false; } },