feat: contrato soporta múltiples servicios (many2many) con precio sugerido

This commit is contained in:
Lizandro Guarnizo
2026-04-30 22:15:31 -05:00
parent 1b0374204c
commit e751a9f894
5 changed files with 157 additions and 76 deletions
+36 -23
View File
@@ -31,8 +31,9 @@ func GetContratos(c *fiber.Ctx) error {
// Enriquecer con días restantes
type ContratoDTO struct {
models.Contrato
DiasRestantes int `json:"dias_restantes"`
Urgencia string `json:"urgencia"` // verde | amarillo | rojo
DiasRestantes int `json:"dias_restantes"`
Urgencia string `json:"urgencia"` // verde | amarillo | rojo
PrecioSugerido float64 `json:"precio_sugerido"`
}
dtos := make([]ContratoDTO, len(records))
now := time.Now()
@@ -44,7 +45,11 @@ func GetContratos(c *fiber.Ctx) error {
} else if dias <= 30 {
urgencia = "amarillo"
}
dtos[i] = ContratoDTO{Contrato: r, DiasRestantes: dias, Urgencia: urgencia}
var sugerido float64
for _, s := range r.Servicios {
sugerido += s.Precio
}
dtos[i] = ContratoDTO{Contrato: r, DiasRestantes: dias, Urgencia: urgencia, PrecioSugerido: sugerido}
}
return c.JSON(fiber.Map{
"registros": dtos,
@@ -57,13 +62,13 @@ func GetContratos(c *fiber.Ctx) error {
func CreateContrato(c *fiber.Ctx) error {
type Input struct {
ClienteID uint `json:"cliente_id" form:"cliente_id"`
ServicioID uint `json:"servicio_id" form:"servicio_id"`
FechaInicio string `json:"fecha_inicio" form:"fecha_inicio"`
FechaVencimiento string `json:"fecha_vencimiento" form:"fecha_vencimiento"`
PrecioAcordado float64 `json:"precio_acordado" form:"precio_acordado"`
AutoRenovar bool `json:"auto_renovar" form:"auto_renovar"`
Notas string `json:"notas" form:"notas"`
ClienteID uint `json:"cliente_id"`
ServicioIDs []uint `json:"servicio_ids"`
FechaInicio string `json:"fecha_inicio"`
FechaVencimiento string `json:"fecha_vencimiento"`
PrecioAcordado float64 `json:"precio_acordado"`
AutoRenovar bool `json:"auto_renovar"`
Notas string `json:"notas"`
}
var inp Input
if err := c.BodyParser(&inp); err != nil {
@@ -79,7 +84,6 @@ func CreateContrato(c *fiber.Ctx) error {
}
m := models.Contrato{
ClienteID: inp.ClienteID,
ServicioID: inp.ServicioID,
FechaInicio: fi,
FechaVencimiento: fv,
PrecioAcordado: inp.PrecioAcordado,
@@ -87,7 +91,7 @@ func CreateContrato(c *fiber.Ctx) error {
Notas: inp.Notas,
Estado: "activo",
}
if err := models.CreateContrato(m); err != nil {
if err := models.CreateContrato(m, inp.ServicioIDs); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.Status(201).JSON(fiber.Map{"message": "Contrato creado", "ok": true})
@@ -99,11 +103,12 @@ func UpdateContrato(c *fiber.Ctx) error {
return c.Status(400).JSON(fiber.Map{"error": "ID inválido"})
}
type Input struct {
Estado string `json:"estado" form:"estado"`
FechaVencimiento string `json:"fecha_vencimiento" form:"fecha_vencimiento"`
PrecioAcordado float64 `json:"precio_acordado" form:"precio_acordado"`
AutoRenovar bool `json:"auto_renovar" form:"auto_renovar"`
Notas string `json:"notas" form:"notas"`
ServicioIDs []uint `json:"servicio_ids"`
Estado string `json:"estado"`
FechaVencimiento string `json:"fecha_vencimiento"`
PrecioAcordado float64 `json:"precio_acordado"`
AutoRenovar bool `json:"auto_renovar"`
Notas string `json:"notas"`
}
var inp Input
if err := c.BodyParser(&inp); err != nil {
@@ -125,7 +130,7 @@ func UpdateContrato(c *fiber.Ctx) error {
existing.PrecioAcordado = inp.PrecioAcordado
existing.AutoRenovar = inp.AutoRenovar
existing.Notas = inp.Notas
if err := models.UpdateContrato(*existing); err != nil {
if err := models.UpdateContrato(*existing, inp.ServicioIDs); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
return c.JSON(fiber.Map{"message": "Actualizado", "ok": true})
@@ -140,13 +145,21 @@ func RenovarContrato(c *fiber.Ctx) error {
if err != nil {
return c.Status(404).JSON(fiber.Map{"error": "No encontrado"})
}
// Calcular nueva fecha según periodicidad del servicio
// Usar periodicidad del primer servicio asociado
periodicidad := ""
if len(existing.Servicios) > 0 {
periodicidad = existing.Servicios[0].Periodicidad
}
nuevaInicio := existing.FechaVencimiento.AddDate(0, 0, 1)
nuevaVenc := calcularFechaVencimiento(nuevaInicio, existing.Servicio.Periodicidad)
nuevaVenc := calcularFechaVencimiento(nuevaInicio, periodicidad)
// Copiar IDs de servicios del contrato anterior
var servicioIDs []uint
for _, s := range existing.Servicios {
servicioIDs = append(servicioIDs, s.ID)
}
nuevo := models.Contrato{
ClienteID: existing.ClienteID,
ServicioID: existing.ServicioID,
FechaInicio: nuevaInicio,
FechaVencimiento: nuevaVenc,
PrecioAcordado: existing.PrecioAcordado,
@@ -154,12 +167,12 @@ func RenovarContrato(c *fiber.Ctx) error {
Estado: "activo",
Notas: "Renovación automática desde contrato #" + strconv.Itoa(int(existing.ID)),
}
if err := models.CreateContrato(nuevo); err != nil {
if err := models.CreateContrato(nuevo, servicioIDs); err != nil {
return c.Status(500).JSON(fiber.Map{"error": err.Error()})
}
// Marcar anterior como renovado
existing.Estado = "renovado"
models.UpdateContrato(*existing)
models.UpdateContrato(*existing, nil)
return c.JSON(fiber.Map{"message": "Renovado", "ok": true})
}