feat: contrato soporta múltiples servicios (many2many) con precio sugerido
This commit is contained in:
+41
-20
@@ -10,16 +10,15 @@ import (
|
||||
|
||||
type Contrato struct {
|
||||
gorm.Model
|
||||
ClienteID uint `json:"cliente_id" gorm:"column:cliente_id"`
|
||||
Cliente Cliente `json:"cliente" gorm:"foreignKey:ClienteID"`
|
||||
ServicioID uint `json:"servicio_id" gorm:"column:servicio_id"`
|
||||
Servicio Servicio `json:"servicio" gorm:"foreignKey:ServicioID"`
|
||||
FechaInicio time.Time `json:"fecha_inicio" gorm:"column:fecha_inicio"`
|
||||
FechaVencimiento time.Time `json:"fecha_vencimiento" gorm:"column:fecha_vencimiento"`
|
||||
PrecioAcordado float64 `json:"precio_acordado" gorm:"column:precio_acordado"`
|
||||
Estado string `json:"estado" gorm:"column:estado;default:'activo'"` // activo | vencido | cancelado | renovado
|
||||
AutoRenovar bool `json:"auto_renovar" gorm:"column:auto_renovar;default:false"`
|
||||
Notas string `json:"notas" gorm:"column:notas"`
|
||||
ClienteID uint `json:"cliente_id" gorm:"column:cliente_id"`
|
||||
Cliente Cliente `json:"cliente" gorm:"foreignKey:ClienteID"`
|
||||
Servicios []Servicio `json:"servicios" gorm:"many2many:contrato_servicios"`
|
||||
FechaInicio time.Time `json:"fecha_inicio" gorm:"column:fecha_inicio"`
|
||||
FechaVencimiento time.Time `json:"fecha_vencimiento" gorm:"column:fecha_vencimiento"`
|
||||
PrecioAcordado float64 `json:"precio_acordado" gorm:"column:precio_acordado"`
|
||||
Estado string `json:"estado" gorm:"column:estado;default:'activo'"` // activo | vencido | cancelado | renovado
|
||||
AutoRenovar bool `json:"auto_renovar" gorm:"column:auto_renovar;default:false"`
|
||||
Notas string `json:"notas" gorm:"column:notas"`
|
||||
}
|
||||
|
||||
func (Contrato) TableName() string { return "contratos" }
|
||||
@@ -28,7 +27,7 @@ func GetAllContratos(limit, offset int, search, estado string) ([]Contrato, int6
|
||||
var items []Contrato
|
||||
var total int64
|
||||
db := app.Http.Database.DB.Model(&Contrato{}).
|
||||
Preload("Cliente").Preload("Servicio")
|
||||
Preload("Cliente").Preload("Servicios")
|
||||
if search != "" {
|
||||
db = db.Joins("JOIN clientes ON clientes.id = contratos.cliente_id").
|
||||
Where("clientes.nombre ILIKE ? OR clientes.empresa ILIKE ?",
|
||||
@@ -48,7 +47,7 @@ func GetAllContratos(limit, offset int, search, estado string) ([]Contrato, int6
|
||||
|
||||
func GetContratoByID(id uint) (*Contrato, error) {
|
||||
var item Contrato
|
||||
if err := app.Http.Database.DB.Preload("Cliente").Preload("Servicio").First(&item, id).Error; err != nil {
|
||||
if err := app.Http.Database.DB.Preload("Cliente").Preload("Servicios").First(&item, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &item, nil
|
||||
@@ -57,7 +56,7 @@ func GetContratoByID(id uint) (*Contrato, error) {
|
||||
// GetContratosByCliente devuelve contratos de un cliente específico
|
||||
func GetContratosByCliente(clienteID uint) ([]Contrato, error) {
|
||||
var items []Contrato
|
||||
if err := app.Http.Database.DB.Preload("Servicio").
|
||||
if err := app.Http.Database.DB.Preload("Servicios").
|
||||
Where("cliente_id = ?", clienteID).
|
||||
Order("fecha_vencimiento ASC").Find(&items).Error; err != nil {
|
||||
return nil, err
|
||||
@@ -72,7 +71,7 @@ func GetContratosProximosVencer(diasAntes int) ([]Contrato, error) {
|
||||
startOfDay := time.Date(target.Year(), target.Month(), target.Day(), 0, 0, 0, 0, time.UTC)
|
||||
endOfDay := startOfDay.Add(24 * time.Hour)
|
||||
|
||||
if err := app.Http.Database.DB.Preload("Cliente").Preload("Servicio").
|
||||
if err := app.Http.Database.DB.Preload("Cliente").Preload("Servicios").
|
||||
Where("estado = 'activo' AND fecha_vencimiento >= ? AND fecha_vencimiento < ?", startOfDay, endOfDay).
|
||||
Find(&items).Error; err != nil {
|
||||
log.Printf("Error getting contratos proximos: %v", err)
|
||||
@@ -81,21 +80,42 @@ func GetContratosProximosVencer(diasAntes int) ([]Contrato, error) {
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func CreateContrato(c Contrato) error {
|
||||
return app.Http.Database.DB.Create(&c).Error
|
||||
func CreateContrato(c Contrato, servicioIDs []uint) error {
|
||||
db := app.Http.Database.DB
|
||||
if err := db.Create(&c).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(servicioIDs) > 0 {
|
||||
var servicios []Servicio
|
||||
if err := db.Find(&servicios, servicioIDs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Model(&c).Association("Servicios").Replace(servicios)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdateContrato(c Contrato) error {
|
||||
return app.Http.Database.DB.Model(&Contrato{}).Where("id = ?", c.ID).Updates(map[string]interface{}{
|
||||
func UpdateContrato(c Contrato, servicioIDs []uint) error {
|
||||
db := app.Http.Database.DB
|
||||
if err := db.Model(&Contrato{}).Where("id = ?", c.ID).Updates(map[string]interface{}{
|
||||
"cliente_id": c.ClienteID,
|
||||
"servicio_id": c.ServicioID,
|
||||
"fecha_inicio": c.FechaInicio,
|
||||
"fecha_vencimiento": c.FechaVencimiento,
|
||||
"precio_acordado": c.PrecioAcordado,
|
||||
"estado": c.Estado,
|
||||
"auto_renovar": c.AutoRenovar,
|
||||
"notas": c.Notas,
|
||||
}).Error
|
||||
}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if servicioIDs != nil {
|
||||
var servicios []Servicio
|
||||
if err := db.Find(&servicios, servicioIDs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return db.Model(&Contrato{Model: gorm.Model{ID: c.ID}}).Association("Servicios").Replace(servicios)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteContrato(id uint) error {
|
||||
@@ -103,5 +123,6 @@ func DeleteContrato(id uint) error {
|
||||
if err := app.Http.Database.DB.First(&c, id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
app.Http.Database.DB.Model(&c).Association("Servicios").Clear()
|
||||
return app.Http.Database.DB.Delete(&c).Error
|
||||
}
|
||||
|
||||
@@ -56,12 +56,18 @@ func ProcesarVencimientos() {
|
||||
for _, c := range contratos {
|
||||
switch regla.AplicaA {
|
||||
case "renovable":
|
||||
if c.Servicio.Tipo == "renovable" {
|
||||
filtrados = append(filtrados, c)
|
||||
for _, s := range c.Servicios {
|
||||
if s.Tipo == "renovable" {
|
||||
filtrados = append(filtrados, c)
|
||||
break
|
||||
}
|
||||
}
|
||||
case "unico":
|
||||
if c.Servicio.Tipo == "unico" {
|
||||
filtrados = append(filtrados, c)
|
||||
for _, s := range c.Servicios {
|
||||
if s.Tipo == "unico" {
|
||||
filtrados = append(filtrados, c)
|
||||
break
|
||||
}
|
||||
}
|
||||
default:
|
||||
filtrados = append(filtrados, c)
|
||||
|
||||
@@ -82,6 +82,15 @@ func EnviarCorreoManual(contrato *models.Contrato) error {
|
||||
return err
|
||||
}
|
||||
dias := int(contrato.FechaVencimiento.Sub(time.Now()).Hours() / 24)
|
||||
var items []ItemServicio
|
||||
for _, s := range contrato.Servicios {
|
||||
items = append(items, ItemServicio{
|
||||
Nombre: s.Nombre,
|
||||
Precio: s.Precio,
|
||||
Moneda: s.Moneda,
|
||||
FechaVenc: contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
})
|
||||
}
|
||||
datos := DatosPlantilla{
|
||||
ClienteNombre: contrato.Cliente.Nombre,
|
||||
ClienteEmpresa: contrato.Cliente.Empresa,
|
||||
@@ -89,12 +98,7 @@ func EnviarCorreoManual(contrato *models.Contrato) error {
|
||||
FechaVencimiento: contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
DiasRestantes: dias,
|
||||
Total: contrato.PrecioAcordado,
|
||||
Servicios: []ItemServicio{{
|
||||
Nombre: contrato.Servicio.Nombre,
|
||||
Precio: contrato.PrecioAcordado,
|
||||
Moneda: contrato.Servicio.Moneda,
|
||||
FechaVenc: contrato.FechaVencimiento.Format("02/01/2006"),
|
||||
}},
|
||||
Servicios: items,
|
||||
}
|
||||
html, err := RenderPlantilla(p, datos)
|
||||
if err != nil {
|
||||
@@ -115,12 +119,14 @@ func EnviarNotificacionGrupo(regla *models.NotificacionRegla, cliente *models.Cl
|
||||
var fechaVenc time.Time
|
||||
|
||||
for _, c := range contratos {
|
||||
items = append(items, ItemServicio{
|
||||
Nombre: c.Servicio.Nombre,
|
||||
Precio: c.PrecioAcordado,
|
||||
Moneda: c.Servicio.Moneda,
|
||||
FechaVenc: c.FechaVencimiento.Format("02/01/2006"),
|
||||
})
|
||||
for _, s := range c.Servicios {
|
||||
items = append(items, ItemServicio{
|
||||
Nombre: s.Nombre,
|
||||
Precio: s.Precio,
|
||||
Moneda: s.Moneda,
|
||||
FechaVenc: c.FechaVencimiento.Format("02/01/2006"),
|
||||
})
|
||||
}
|
||||
total += c.PrecioAcordado
|
||||
if fechaVenc.IsZero() || c.FechaVencimiento.Before(fechaVenc) {
|
||||
fechaVenc = c.FechaVencimiento
|
||||
|
||||
Reference in New Issue
Block a user