Files
soft_usite/rest/controllers/umind_plan_controller.go
T
Lizandro GuarnizoandClaude Sonnet 5 c69b904b03 feat(umind): vincula tenants a clientes y agrega planes con precios
Base para que el cliente administre uMind desde su portal y para el cobro
por consumo. Todavía no cambia nada del comportamiento actual.

- UmindTenant gana ClienteID (punteros, sin not null: los tenants creados
  antes del portal quedan sin asignar y el ALTER TABLE no falla sobre
  datos existentes) y PlanID.
- GetUmindTenantsByClientes es fail-closed: sin clientes, no ve nada.
- UmindPlan define el máximo de agentes y los precios por 1k tokens, por
  imagen OCR, por transcripción y la mensualidad, más un tope de consumo
  que solo avisa. CRUD para staff en /app/umind-planes.
- El modelo va en AMBAS listas de AutoMigrate (main.go y migrations),
  no repetir el error de agregarlo solo en una.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 11:32:38 -05:00

116 lines
4.0 KiB
Go

package controllers
import (
"strconv"
"strings"
"github.com/gofiber/fiber/v2"
"github.com/sujit-baniya/fiber-boilerplate/pkg/models"
)
// UmindPlanesPage renderiza la administración de planes de uMind (solo staff).
func UmindPlanesPage(c *fiber.Ctx) error {
return c.Render("umind_planes", fiber.Map{
"user": c.Locals("user"),
"modules": c.Locals("userModules"),
}, "layouts/main")
}
func GetUmindPlanesHandler(c *fiber.Ctx) error {
items, err := models.GetUmindPlanes()
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"items": items})
}
type umindPlanReq struct {
Nombre string `json:"nombre"`
MaxAgentes int `json:"max_agentes"`
PrecioMensual float64 `json:"precio_mensual"`
PrecioPor1kTokens float64 `json:"precio_por_1k_tokens"`
PrecioPorOCR float64 `json:"precio_por_ocr"`
PrecioPorTranscripcion float64 `json:"precio_por_transcripcion"`
Moneda string `json:"moneda"`
TopeConsumoMensual float64 `json:"tope_consumo_mensual"`
Activo bool `json:"activo"`
}
func (r umindPlanReq) moneda() string {
m := strings.ToUpper(strings.TrimSpace(r.Moneda))
if len(m) != 3 {
return "COP"
}
return m
}
func CreateUmindPlanHandler(c *fiber.Ctx) error {
var req umindPlanReq
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
}
if strings.TrimSpace(req.Nombre) == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "nombre es requerido"})
}
if req.MaxAgentes < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "max_agentes no puede ser negativo"})
}
plan := &models.UmindPlan{
Nombre: strings.TrimSpace(req.Nombre),
MaxAgentes: req.MaxAgentes,
PrecioMensual: req.PrecioMensual,
PrecioPor1kTokens: req.PrecioPor1kTokens,
PrecioPorOCR: req.PrecioPorOCR,
PrecioPorTranscripcion: req.PrecioPorTranscripcion,
Moneda: req.moneda(),
TopeConsumoMensual: req.TopeConsumoMensual,
Activo: true,
}
if err := models.CreateUmindPlan(plan); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(fiber.StatusCreated).JSON(fiber.Map{"ok": true, "id": plan.ID})
}
func UpdateUmindPlanHandler(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
}
var req umindPlanReq
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "body inválido"})
}
if req.MaxAgentes < 0 {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "max_agentes no puede ser negativo"})
}
updates := map[string]interface{}{
"nombre": strings.TrimSpace(req.Nombre),
"max_agentes": req.MaxAgentes,
"precio_mensual": req.PrecioMensual,
"precio_por_1k_tokens": req.PrecioPor1kTokens,
"precio_por_ocr": req.PrecioPorOCR,
"precio_por_transcripcion": req.PrecioPorTranscripcion,
"moneda": req.moneda(),
"tope_consumo_mensual": req.TopeConsumoMensual,
"activo": req.Activo,
}
if err := models.UpdateUmindPlan(uint(id), updates); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"ok": true})
}
func DeleteUmindPlanHandler(c *fiber.Ctx) error {
id, err := strconv.ParseUint(c.Params("id"), 10, 64)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "id inválido"})
}
if err := models.DeleteUmindPlan(uint(id)); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"ok": true})
}