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:
co-authored by
Claude Sonnet 5
parent
84506a98e2
commit
c69b904b03
@@ -0,0 +1,66 @@
|
||||
package models
|
||||
|
||||
import (
|
||||
"github.com/sujit-baniya/fiber-boilerplate/app"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// UmindPlan define qué puede hacer un tenant y a qué precio. Los precios se
|
||||
// copian a cada UmindUso al momento de registrarlo, así que subir un precio
|
||||
// nunca revalúa consumo ya facturado ni pendiente.
|
||||
type UmindPlan struct {
|
||||
gorm.Model
|
||||
Nombre string `json:"nombre" gorm:"column:nombre;size:100;not null"`
|
||||
MaxAgentes int `json:"max_agentes" gorm:"column:max_agentes;default:1"` // 0 = ilimitado
|
||||
PrecioMensual float64 `json:"precio_mensual" gorm:"column:precio_mensual;default:0"`
|
||||
PrecioPor1kTokens float64 `json:"precio_por_1k_tokens" gorm:"column:precio_por_1k_tokens;default:0"`
|
||||
PrecioPorOCR float64 `json:"precio_por_ocr" gorm:"column:precio_por_ocr;default:0"`
|
||||
PrecioPorTranscripcion float64 `json:"precio_por_transcripcion" gorm:"column:precio_por_transcripcion;default:0"`
|
||||
Moneda string `json:"moneda" gorm:"column:moneda;size:3;default:'COP'"`
|
||||
// TopeConsumoMensual solo dispara un aviso al superarse — no corta el
|
||||
// servicio. 0 = sin tope.
|
||||
TopeConsumoMensual float64 `json:"tope_consumo_mensual" gorm:"column:tope_consumo_mensual;default:0"`
|
||||
Activo bool `json:"activo" gorm:"column:activo;default:true"`
|
||||
}
|
||||
|
||||
func (UmindPlan) TableName() string { return "umind_planes" }
|
||||
|
||||
func CreateUmindPlan(p *UmindPlan) error {
|
||||
return app.Http.Database.DB.Create(p).Error
|
||||
}
|
||||
|
||||
func GetUmindPlanes() ([]UmindPlan, error) {
|
||||
var items []UmindPlan
|
||||
err := app.Http.Database.DB.Order("id ASC").Find(&items).Error
|
||||
return items, err
|
||||
}
|
||||
|
||||
func GetUmindPlanByID(id uint) (*UmindPlan, error) {
|
||||
var p UmindPlan
|
||||
if err := app.Http.Database.DB.First(&p, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func UpdateUmindPlan(id uint, updates map[string]interface{}) error {
|
||||
return app.Http.Database.DB.Model(&UmindPlan{}).Where("id = ?", id).Updates(updates).Error
|
||||
}
|
||||
|
||||
func DeleteUmindPlan(id uint) error {
|
||||
return app.Http.Database.DB.Delete(&UmindPlan{}, id).Error
|
||||
}
|
||||
|
||||
// GetPlanDeTenant devuelve el plan del tenant, o nil si no tiene uno asignado
|
||||
// (tenants viejos). Los llamadores tratan nil como "sin límites ni precios".
|
||||
func GetPlanDeTenant(tenantID uint) *UmindPlan {
|
||||
t, err := GetUmindTenantByID(tenantID)
|
||||
if err != nil || t.PlanID == nil {
|
||||
return nil
|
||||
}
|
||||
p, err := GetUmindPlanByID(*t.PlanID)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return p
|
||||
}
|
||||
Reference in New Issue
Block a user