fix: seguridad del webhook de soporte, pagos en contabilidad y Telegram para tareas
- soporte: el webhook de correo entrante era público sin ninguna validación; ahora exige una API key (query ?key= o header) comparada en tiempo constante. Además evita tickets duplicados por reintentos del proveedor (dedup por Message-Id) y enhebra respuestas del mismo remitente en vez de abrir un ticket nuevo por cada correo. - contabilidad: marcar una cuenta por cobrar/pagar como pagada ahora crea y vincula la Transaccion correspondiente (antes el dashboard de ingresos/ egresos nunca reflejaba esos pagos). Se corrige además que actualizar una cuenta por cobrar borraba su transaccion_id en cada PUT. - tareas: se activa por defecto el canal Telegram para tarea_asignada (estaba apagado desde el seed original) y se agrega un flujo real de vinculación de Telegram para el staff interno (código temporal + verificación), igual al que ya existía para los usuarios del portal — sin esto el chat_id de cada usuario había que pegarlo a mano y la notificación nunca llegaba. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
1127d944c7
commit
d2bf699b60
@@ -490,17 +490,50 @@ func CreateCuentaCobro(cc *CuentaCobro) error {
|
||||
return app.Http.Database.DB.Create(cc).Error
|
||||
}
|
||||
|
||||
// UpdateCuentaCobro actualiza los campos editables de una cuenta por cobrar.
|
||||
// No toca transaccion_id a propósito: ese vínculo solo lo debe crear
|
||||
// MarcarCuentaCobroPagada, para no perderlo en una edición cualquiera.
|
||||
func UpdateCuentaCobro(cc *CuentaCobro) error {
|
||||
return app.Http.Database.DB.Model(&CuentaCobro{}).Where("id = ?", cc.ID).Updates(map[string]interface{}{
|
||||
"cliente_id": cc.ClienteID,
|
||||
"estado": cc.Estado,
|
||||
"fecha_vencimiento": cc.FechaVencimiento,
|
||||
"fecha_pago": cc.FechaPago,
|
||||
"transaccion_id": cc.TransaccionID,
|
||||
"notas": cc.Notas,
|
||||
}).Error
|
||||
}
|
||||
|
||||
// MarcarCuentaCobroPagada registra el pago de una cuenta por cobrar: crea la
|
||||
// Transaccion de tipo "ingreso" correspondiente y la vincula. Es idempotente —
|
||||
// si la cuenta ya estaba pagada, no crea una transacción duplicada.
|
||||
func MarcarCuentaCobroPagada(id uint, fechaPago time.Time) error {
|
||||
var cc CuentaCobro
|
||||
if err := app.Http.Database.DB.Preload("Cliente").First(&cc, id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if cc.Estado == "pagado" {
|
||||
return nil
|
||||
}
|
||||
desc := fmt.Sprintf("Pago cuenta por cobrar #%d: %s", cc.ID, cc.Descripcion)
|
||||
if cc.Cliente.Nombre != "" {
|
||||
desc = fmt.Sprintf("Pago cuenta por cobrar #%d (%s): %s", cc.ID, cc.Cliente.Nombre, cc.Descripcion)
|
||||
}
|
||||
t := &Transaccion{
|
||||
Fecha: fechaPago,
|
||||
Tipo: "ingreso",
|
||||
Descripcion: desc,
|
||||
Valor: cc.Valor,
|
||||
}
|
||||
if err := app.Http.Database.DB.Create(t).Error; err != nil {
|
||||
return fmt.Errorf("no se pudo crear la transacción de pago: %w", err)
|
||||
}
|
||||
return app.Http.Database.DB.Model(&CuentaCobro{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"estado": "pagado",
|
||||
"fecha_pago": fechaPago,
|
||||
"transaccion_id": t.ID,
|
||||
}).Error
|
||||
}
|
||||
|
||||
func DeleteCuentaCobro(id uint) error {
|
||||
return app.Http.Database.DB.Delete(&CuentaCobro{}, id).Error
|
||||
}
|
||||
@@ -548,10 +581,35 @@ func UpdateCuentaPagar(cp *CuentaPagar) error {
|
||||
}).Error
|
||||
}
|
||||
|
||||
// MarcarCuentaPagarPagada registra el pago de una cuenta por pagar: crea la
|
||||
// Transaccion de tipo "egreso" correspondiente y la vincula. Es idempotente —
|
||||
// si la cuenta ya estaba pagada, no crea una transacción duplicada.
|
||||
func MarcarCuentaPagarPagada(id uint, fechaPago time.Time) error {
|
||||
var cp CuentaPagar
|
||||
if err := app.Http.Database.DB.Preload("Entidad").First(&cp, id).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if cp.Estado == "pagado" {
|
||||
return nil
|
||||
}
|
||||
desc := fmt.Sprintf("Pago cuenta por pagar #%d: %s", cp.ID, cp.Descripcion)
|
||||
if cp.Entidad.Nombre != "" {
|
||||
desc = fmt.Sprintf("Pago cuenta por pagar #%d (%s): %s", cp.ID, cp.Entidad.Nombre, cp.Descripcion)
|
||||
}
|
||||
t := &Transaccion{
|
||||
Fecha: fechaPago,
|
||||
Tipo: "egreso",
|
||||
Descripcion: desc,
|
||||
Valor: cp.Valor,
|
||||
EntidadID: &cp.EntidadID,
|
||||
}
|
||||
if err := app.Http.Database.DB.Create(t).Error; err != nil {
|
||||
return fmt.Errorf("no se pudo crear la transacción de pago: %w", err)
|
||||
}
|
||||
return app.Http.Database.DB.Model(&CuentaPagar{}).Where("id = ?", id).Updates(map[string]interface{}{
|
||||
"estado": "pagado",
|
||||
"fecha_pago": fechaPago,
|
||||
"estado": "pagado",
|
||||
"fecha_pago": fechaPago,
|
||||
"transaccion_id": t.ID,
|
||||
}).Error
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user