From 560dc261e91808dff47ec0534d674c7cb5ba6987 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Sat, 15 Aug 2026 21:08:24 -0500 Subject: [PATCH] feat(telegram): el bot puede corregir facturas y registrar cobros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Auditoría de qué expone /api/v2 contra qué puede hacer el bot. El bot no llama a la API: usa su propio registro de herramientas, así que la API puede crecer y el bot queda atrás sin que nada lo señale. De 46 herramientas contra ~300 endpoints de v2, el hueco más caro estaba en el flujo que se estuvo usando hoy: - Se podían crear cuentas por cobrar y por pagar, pero no marcarlas pagadas — y marcar pagado es justamente lo que genera la transacción en contabilidad. O sea que el bot podía registrar deuda pero nunca cerrarla. - No había forma de corregir una factura mal cargada, que es exactamente lo que hacía falta después del bug del monto en cero. Se agregan actualizar_factura, marcar_cuenta_cobro_pagada y marcar_cuenta_pagar_pagada. actualizar_factura solo pisa los campos que vinieron, así que corregir el monto no borra el número ni el concepto. Quedan sin herramientas áreas enteras que v2 sí cubre (hostinger, cloudflare, websms, portal-usuarios, uMind, api-keys, saas, plantillas). No se agregan a ciegas: cada herramienta ocupa lugar en el prompt y compite por la atención del modelo, así que conviene sumarlas cuando haya un uso real detrás. Co-Authored-By: Claude Sonnet 5 --- pkg/services/telegram_agent_service.go | 81 ++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/pkg/services/telegram_agent_service.go b/pkg/services/telegram_agent_service.go index b1c39fd..5652c84 100644 --- a/pkg/services/telegram_agent_service.go +++ b/pkg/services/telegram_agent_service.go @@ -216,6 +216,24 @@ func agentTools() []agentTool { "fecha_emision": str("Fecha de emisión YYYY-MM-DD; si no se indica, hoy"), "fecha_vencimiento": str("Fecha de vencimiento YYYY-MM-DD, opcional"), }, []string{"cliente_id", "monto"})), + tool("actualizar_factura", "Corrige los datos de una factura de venta ya registrada: monto, número, concepto o estado. Útil cuando quedó mal cargada.", + obj(map[string]agentToolParam{ + "factura_id": num("ID de la factura (usa listar_facturas si no lo sabes)"), + "monto": num("Nuevo monto, si hay que corregirlo"), + "numero": str("Nuevo número de factura"), + "descripcion": str("Nuevo concepto"), + "estado": agentToolParam{Type: "string", Description: "pendiente | pagada | vencida | cancelada", Enum: []string{"pendiente", "pagada", "vencida", "cancelada", ""}}, + }, []string{"factura_id"})), + tool("marcar_cuenta_cobro_pagada", "Marca como pagada una cuenta por cobrar y registra el ingreso en contabilidad.", + obj(map[string]agentToolParam{ + "cuenta_id": num("ID de la cuenta por cobrar (usa listar_cuentas_cobro)"), + "fecha_pago": str("Fecha del pago YYYY-MM-DD; si no se indica, hoy"), + }, []string{"cuenta_id"})), + tool("marcar_cuenta_pagar_pagada", "Marca como pagada una cuenta por pagar y registra el egreso en contabilidad.", + obj(map[string]agentToolParam{ + "cuenta_id": num("ID de la cuenta por pagar (usa listar_cuentas_pagar)"), + "fecha_pago": str("Fecha del pago YYYY-MM-DD; si no se indica, hoy"), + }, []string{"cuenta_id"})), tool("adjuntar_factura", "Guarda como factura de VENTA el documento (PDF/foto) que el usuario acaba de enviar por Telegram: U-SITE es quien factura/cobra al cliente. Solo funciona si hay un archivo adjunto pendiente. Si el documento es al revés (un proveedor le factura a U-SITE), usa adjuntar_factura_compra en su lugar, no esta.", obj(map[string]agentToolParam{ "cliente_id": num("ID del cliente al que pertenece la factura (usa listar_clientes si no lo sabes)"), @@ -714,6 +732,69 @@ func runTool(chatID int64, name string, a map[string]interface{}) (interface{}, "numero": f.Numero, "cliente_id": f.ClienteID, }, nil + case "actualizar_factura": + id := uint(getInt("factura_id", 0)) + if id == 0 { + return nil, fmt.Errorf("factura_id requerido") + } + f, err := models.GetFacturaByID(id) + if err != nil { + return nil, fmt.Errorf("no se encontró la factura %d", id) + } + // Solo se pisan los campos que vinieron: así corregir el monto no + // borra el número ni el concepto que ya estaban bien. + if v := getFloat("monto", -1); v >= 0 { + f.Monto = v + } + if v := getStr("numero"); v != "" { + f.Numero = v + } + if v := getStr("descripcion"); v != "" { + f.Descripcion = v + } + if v := getStr("estado"); v != "" { + f.Estado = v + } + if err := models.UpdateFactura(f); err != nil { + return nil, err + } + return map[string]interface{}{ + "ok": true, "factura_id": f.ID, "monto_guardado": f.Monto, + "numero": f.Numero, "estado": f.Estado, + }, nil + + case "marcar_cuenta_cobro_pagada": + id := uint(getInt("cuenta_id", 0)) + if id == 0 { + return nil, fmt.Errorf("cuenta_id requerido") + } + fecha := time.Now() + if v := getStr("fecha_pago"); v != "" { + if t, err := time.Parse("2006-01-02", v); err == nil { + fecha = t + } + } + if err := models.MarcarCuentaCobroPagada(id, fecha); err != nil { + return nil, err + } + return map[string]interface{}{"ok": true, "cuenta_id": id, "fecha_pago": fecha.Format("2006-01-02")}, nil + + case "marcar_cuenta_pagar_pagada": + id := uint(getInt("cuenta_id", 0)) + if id == 0 { + return nil, fmt.Errorf("cuenta_id requerido") + } + fecha := time.Now() + if v := getStr("fecha_pago"); v != "" { + if t, err := time.Parse("2006-01-02", v); err == nil { + fecha = t + } + } + if err := models.MarcarCuentaPagarPagada(id, fecha); err != nil { + return nil, err + } + return map[string]interface{}{"ok": true, "cuenta_id": id, "fecha_pago": fecha.Format("2006-01-02")}, nil + case "adjuntar_factura": att, ok := PopTelegramAttachment(chatID) if !ok {