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>
This commit is contained in:
Lizandro Guarnizo
2026-08-13 11:32:38 -05:00
co-authored by Claude Sonnet 5
parent 84506a98e2
commit c69b904b03
14 changed files with 493 additions and 33 deletions
@@ -61,6 +61,8 @@ type umindTenantReq struct {
Nombre string `json:"nombre"`
DominiosPermitidos []string `json:"dominios_permitidos"`
Activo bool `json:"activo"`
ClienteID *uint `json:"cliente_id"`
PlanID *uint `json:"plan_id"`
}
func (r umindTenantReq) dominiosLimpios() []string {
@@ -92,6 +94,8 @@ func CreateUmindTenantHandler(c *fiber.Ctx) error {
DominiosPermitidos: strings.Join(dominios, ","),
Activo: true,
CreadoPorID: extraerUserID(c),
ClienteID: req.ClienteID,
PlanID: req.PlanID,
}
if err := models.CreateUmindTenant(tenant); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
@@ -113,6 +117,8 @@ func UpdateUmindTenantHandler(c *fiber.Ctx) error {
"nombre": strings.TrimSpace(req.Nombre),
"dominios_permitidos": strings.Join(dominios, ","),
"activo": req.Activo,
"cliente_id": req.ClienteID,
"plan_id": req.PlanID,
}
if err := models.UpdateUmindTenant(uint(id), updates); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})