feat: automatización de cotizaciones, contratos, actas y cuentas de cobro con IA
Implementa las 4 fases de la especificación de automatización: módulo de plantillas/tarifas editable por el equipo, generación de PDF (HTML+JS vía Chrome headless) para cotizaciones/contratos/arquitecturas/cuentas de cobro, chat propio en el dashboard reutilizando el mismo motor y tools del bot de Telegram, y nuevas tools del agente para crear estos documentos end-to-end. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
772a4a5656
commit
516220a8a1
+93
-66
@@ -61,19 +61,19 @@ func (Transaccion) TableName() string { return "contab_transacciones" }
|
||||
|
||||
type CuentaCobro struct {
|
||||
gorm.Model
|
||||
ClienteID uint `json:"cliente_id" gorm:"column:cliente_id;index;not null"`
|
||||
Cliente Cliente `json:"cliente" gorm:"foreignKey:ClienteID"`
|
||||
EntidadID *uint `json:"entidad_id" gorm:"column:entidad_id;index"`
|
||||
Entidad *Entidad `json:"entidad" gorm:"foreignKey:EntidadID"`
|
||||
Fecha time.Time `json:"fecha" gorm:"column:fecha;not null"`
|
||||
Descripcion string `json:"descripcion" gorm:"column:descripcion;type:text"`
|
||||
Valor float64 `json:"valor" gorm:"column:valor;not null"`
|
||||
Estado string `json:"estado" gorm:"column:estado;size:20;default:'pendiente'"` // pendiente | pagado | parcial
|
||||
FechaVencimiento *time.Time `json:"fecha_vencimiento" gorm:"column:fecha_vencimiento"`
|
||||
FechaPago *time.Time `json:"fecha_pago" gorm:"column:fecha_pago"`
|
||||
TransaccionID *uint `json:"transaccion_id" gorm:"column:transaccion_id"`
|
||||
Transaccion *Transaccion `json:"transaccion" gorm:"foreignKey:TransaccionID"`
|
||||
Notas string `json:"notas" gorm:"column:notas;type:text"`
|
||||
ClienteID uint `json:"cliente_id" gorm:"column:cliente_id;index;not null"`
|
||||
Cliente Cliente `json:"cliente" gorm:"foreignKey:ClienteID"`
|
||||
EntidadID *uint `json:"entidad_id" gorm:"column:entidad_id;index"`
|
||||
Entidad *Entidad `json:"entidad" gorm:"foreignKey:EntidadID"`
|
||||
Fecha time.Time `json:"fecha" gorm:"column:fecha;not null"`
|
||||
Descripcion string `json:"descripcion" gorm:"column:descripcion;type:text"`
|
||||
Valor float64 `json:"valor" gorm:"column:valor;not null"`
|
||||
Estado string `json:"estado" gorm:"column:estado;size:20;default:'pendiente'"` // pendiente | pagado | parcial
|
||||
FechaVencimiento *time.Time `json:"fecha_vencimiento" gorm:"column:fecha_vencimiento"`
|
||||
FechaPago *time.Time `json:"fecha_pago" gorm:"column:fecha_pago"`
|
||||
TransaccionID *uint `json:"transaccion_id" gorm:"column:transaccion_id"`
|
||||
Transaccion *Transaccion `json:"transaccion" gorm:"foreignKey:TransaccionID"`
|
||||
Notas string `json:"notas" gorm:"column:notas;type:text"`
|
||||
}
|
||||
|
||||
func (CuentaCobro) TableName() string { return "contab_cuentas_cobro" }
|
||||
@@ -82,17 +82,17 @@ func (CuentaCobro) TableName() string { return "contab_cuentas_cobro" }
|
||||
|
||||
type CuentaPagar struct {
|
||||
gorm.Model
|
||||
EntidadID uint `json:"entidad_id" gorm:"column:entidad_id;index;not null"`
|
||||
Entidad Entidad `json:"entidad" gorm:"foreignKey:EntidadID"`
|
||||
Fecha time.Time `json:"fecha" gorm:"column:fecha;not null"`
|
||||
Descripcion string `json:"descripcion" gorm:"column:descripcion;type:text"`
|
||||
Valor float64 `json:"valor" gorm:"column:valor;not null"`
|
||||
Vencimiento *time.Time `json:"vencimiento" gorm:"column:vencimiento"`
|
||||
Estado string `json:"estado" gorm:"column:estado;size:20;default:'pendiente'"` // pendiente | pagado | parcial
|
||||
FechaPago *time.Time `json:"fecha_pago" gorm:"column:fecha_pago"`
|
||||
TransaccionID *uint `json:"transaccion_id" gorm:"column:transaccion_id"`
|
||||
Transaccion *Transaccion `json:"transaccion" gorm:"foreignKey:TransaccionID"`
|
||||
Notas string `json:"notas" gorm:"column:notas;type:text"`
|
||||
EntidadID uint `json:"entidad_id" gorm:"column:entidad_id;index;not null"`
|
||||
Entidad Entidad `json:"entidad" gorm:"foreignKey:EntidadID"`
|
||||
Fecha time.Time `json:"fecha" gorm:"column:fecha;not null"`
|
||||
Descripcion string `json:"descripcion" gorm:"column:descripcion;type:text"`
|
||||
Valor float64 `json:"valor" gorm:"column:valor;not null"`
|
||||
Vencimiento *time.Time `json:"vencimiento" gorm:"column:vencimiento"`
|
||||
Estado string `json:"estado" gorm:"column:estado;size:20;default:'pendiente'"` // pendiente | pagado | parcial
|
||||
FechaPago *time.Time `json:"fecha_pago" gorm:"column:fecha_pago"`
|
||||
TransaccionID *uint `json:"transaccion_id" gorm:"column:transaccion_id"`
|
||||
Transaccion *Transaccion `json:"transaccion" gorm:"foreignKey:TransaccionID"`
|
||||
Notas string `json:"notas" gorm:"column:notas;type:text"`
|
||||
}
|
||||
|
||||
func (CuentaPagar) TableName() string { return "contab_cuentas_pagar" }
|
||||
@@ -101,12 +101,12 @@ func (CuentaPagar) TableName() string { return "contab_cuentas_pagar" }
|
||||
|
||||
type ConsolidadoMensual struct {
|
||||
gorm.Model
|
||||
Anio int `json:"anio" gorm:"column:anio;not null"`
|
||||
Mes int `json:"mes" gorm:"column:mes;not null"`
|
||||
TotalIngresos float64 `json:"total_ingresos" gorm:"column:total_ingresos;default:0"`
|
||||
TotalEgresos float64 `json:"total_egresos" gorm:"column:total_egresos;default:0"`
|
||||
Anio int `json:"anio" gorm:"column:anio;not null"`
|
||||
Mes int `json:"mes" gorm:"column:mes;not null"`
|
||||
TotalIngresos float64 `json:"total_ingresos" gorm:"column:total_ingresos;default:0"`
|
||||
TotalEgresos float64 `json:"total_egresos" gorm:"column:total_egresos;default:0"`
|
||||
TotalRetenciones float64 `json:"total_retenciones" gorm:"column:total_retenciones;default:0"`
|
||||
Resultado float64 `json:"resultado" gorm:"column:resultado;default:0"`
|
||||
Resultado float64 `json:"resultado" gorm:"column:resultado;default:0"`
|
||||
}
|
||||
|
||||
func (ConsolidadoMensual) TableName() string { return "contab_consolidado" }
|
||||
@@ -307,7 +307,11 @@ func SeedBalanceData() {
|
||||
}
|
||||
|
||||
// ─── FACTURAS → Transacciones (ingresos) ────────────────────────────────
|
||||
facturas := []struct{ factura float64; valor float64; fecha string }{
|
||||
facturas := []struct {
|
||||
factura float64
|
||||
valor float64
|
||||
fecha string
|
||||
}{
|
||||
{54, 450000, "2026-01-01 00:00:00"},
|
||||
{55, 9000000, "2026-01-01 00:00:00"},
|
||||
{56, 119000, "2026-01-01 00:00:00"},
|
||||
@@ -347,7 +351,7 @@ func SeedBalanceData() {
|
||||
if db.Where("descripcion = ?", "Ajuste/NC ene 2026").First(&existing).Error != nil {
|
||||
db.Create(&Transaccion{
|
||||
Fecha: parseDate("2026-01-01 00:00:00"),
|
||||
Tipo: "egreso", Descripcion: "Ajuste/NC ene 2026",
|
||||
Tipo: "egreso", Descripcion: "Ajuste/NC ene 2026",
|
||||
Valor: 130000, CuentaID: cuentaIng, Estado: "registrada",
|
||||
})
|
||||
}
|
||||
@@ -369,7 +373,11 @@ func SeedBalanceData() {
|
||||
}
|
||||
|
||||
// ─── IVA → Transacciones (egresos) ──────────────────────────────────────
|
||||
ivas := []struct{ valor float64; entidad string; desc string }{
|
||||
ivas := []struct {
|
||||
valor float64
|
||||
entidad string
|
||||
desc string
|
||||
}{
|
||||
{285000, "DOCUXER", "IVA Fact #63"},
|
||||
{503500, "DOCUXER", "IVA Fact #70"},
|
||||
{19000, "GIAF SAS", "IVA Fact #56"},
|
||||
@@ -383,7 +391,7 @@ func SeedBalanceData() {
|
||||
eid := getEntidad(iv.entidad)
|
||||
db.Create(&Transaccion{
|
||||
Fecha: parseDate("2026-01-01 00:00:00"),
|
||||
Tipo: "egreso", Descripcion: iv.desc,
|
||||
Tipo: "egreso", Descripcion: iv.desc,
|
||||
Valor: iv.valor, CuentaID: cuentaImp,
|
||||
EntidadID: eid, Estado: "registrada",
|
||||
})
|
||||
@@ -391,7 +399,11 @@ func SeedBalanceData() {
|
||||
}
|
||||
|
||||
// ─── RETENCION → Transacciones (egresos) ────────────────────────────────
|
||||
rets := []struct{ valor float64; fecha string; estado string }{
|
||||
rets := []struct {
|
||||
valor float64
|
||||
fecha string
|
||||
estado string
|
||||
}{
|
||||
{195000, "2026-03-01 00:00:00", "pagado"},
|
||||
{257000, "2026-02-01 00:00:00", "pagado"},
|
||||
{431000, "2026-01-01 00:00:00", "pendiente"},
|
||||
@@ -402,7 +414,7 @@ func SeedBalanceData() {
|
||||
if db.Where("descripcion = ?", desc).First(&existing).Error != nil {
|
||||
db.Create(&Transaccion{
|
||||
Fecha: parseDate(r.fecha),
|
||||
Tipo: "egreso", Descripcion: desc,
|
||||
Tipo: "egreso", Descripcion: desc,
|
||||
Valor: r.valor, CuentaID: cuentaImp,
|
||||
Estado: "registrada",
|
||||
})
|
||||
@@ -410,7 +422,11 @@ func SeedBalanceData() {
|
||||
}
|
||||
|
||||
// ─── CUENTAS DE COBRO (las que te pasan a ti) → CuentasPagar ────────────
|
||||
cobros := []struct{ entidad string; valor float64; fecha string }{
|
||||
cobros := []struct {
|
||||
entidad string
|
||||
valor float64
|
||||
fecha string
|
||||
}{
|
||||
{"NATALIA", 2000000, "2026-01-01 00:00:00"},
|
||||
{"FELIPE", 2000000, "2026-01-01 00:00:00"},
|
||||
{"CONTADORA", 1780000, "2026-01-01 00:00:00"},
|
||||
@@ -439,7 +455,10 @@ func SeedBalanceData() {
|
||||
}
|
||||
|
||||
// ─── CONSOLIDADO POR MES ───────────────────────────────────────────────
|
||||
consols := []struct{ mes int; ing, egre, resul float64 }{
|
||||
consols := []struct {
|
||||
mes int
|
||||
ing, egre, resul float64
|
||||
}{
|
||||
{1, 12669000, 8467849, 4201151},
|
||||
{2, 8470500, 4300000, 4170500},
|
||||
{3, 2109000, 4300000, -2191000},
|
||||
@@ -459,6 +478,14 @@ func SeedBalanceData() {
|
||||
log.Println("[SEED] Balance data imported from BALANCE.numbers")
|
||||
}
|
||||
|
||||
func GetCuentaCobroByID(id uint) (*CuentaCobro, error) {
|
||||
var item CuentaCobro
|
||||
if err := app.Http.Database.DB.Preload("Cliente").Preload("Entidad").First(&item, id).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &item, nil
|
||||
}
|
||||
|
||||
func CreateCuentaCobro(cc *CuentaCobro) error {
|
||||
return app.Http.Database.DB.Create(cc).Error
|
||||
}
|
||||
@@ -565,12 +592,12 @@ func CalcularYGuardarConsolidado(mes, anio int) (*ConsolidadoMensual, error) {
|
||||
resultado := ingresos.Total - egresos.Total
|
||||
|
||||
c := &ConsolidadoMensual{
|
||||
Anio: anio,
|
||||
Mes: mes,
|
||||
TotalIngresos: ingresos.Total,
|
||||
TotalEgresos: egresos.Total,
|
||||
Anio: anio,
|
||||
Mes: mes,
|
||||
TotalIngresos: ingresos.Total,
|
||||
TotalEgresos: egresos.Total,
|
||||
TotalRetenciones: 0,
|
||||
Resultado: resultado,
|
||||
Resultado: resultado,
|
||||
}
|
||||
|
||||
var existing ConsolidadoMensual
|
||||
@@ -605,15 +632,15 @@ func ListConsolidados(anio int) ([]ConsolidadoMensual, error) {
|
||||
// ─── Datos para dashboard ────────────────────────────────────────────────────
|
||||
|
||||
type DashboardData struct {
|
||||
Mes int `json:"mes"`
|
||||
Anio int `json:"anio"`
|
||||
TotalIngresos float64 `json:"total_ingresos"`
|
||||
TotalEgresos float64 `json:"total_egresos"`
|
||||
Resultado float64 `json:"resultado"`
|
||||
CantTransacciones int64 `json:"cant_transacciones"`
|
||||
PendientesCobro float64 `json:"pendientes_cobro"`
|
||||
PendientesPago float64 `json:"pendientes_pago"`
|
||||
Transacciones []Transaccion `json:"transacciones"`
|
||||
Mes int `json:"mes"`
|
||||
Anio int `json:"anio"`
|
||||
TotalIngresos float64 `json:"total_ingresos"`
|
||||
TotalEgresos float64 `json:"total_egresos"`
|
||||
Resultado float64 `json:"resultado"`
|
||||
CantTransacciones int64 `json:"cant_transacciones"`
|
||||
PendientesCobro float64 `json:"pendientes_cobro"`
|
||||
PendientesPago float64 `json:"pendientes_pago"`
|
||||
Transacciones []Transaccion `json:"transacciones"`
|
||||
}
|
||||
|
||||
func GetDashboardData(mes, anio int) (*DashboardData, error) {
|
||||
@@ -657,15 +684,15 @@ func GetDashboardData(mes, anio int) (*DashboardData, error) {
|
||||
|
||||
func SeedContabilidad() {
|
||||
cuentas := []Cuenta{
|
||||
{Codigo: "ING-FAC", Nombre: "Facturación", Tipo: "ingreso", Color: "#22c55e"},
|
||||
{Codigo: "ING-OTR", Nombre: "Otros ingresos", Tipo: "ingreso", Color: "#16a34a"},
|
||||
{Codigo: "EGR-HOS", Nombre: "Hosting/Servidores", Tipo: "egreso", Color: "#ef4444"},
|
||||
{Codigo: "EGR-DOM", Nombre: "Dominios", Tipo: "egreso", Color: "#dc2626"},
|
||||
{Codigo: "EGR-SRV", Nombre: "Servicios", Tipo: "egreso", Color: "#f97316"},
|
||||
{Codigo: "ING-FAC", Nombre: "Facturación", Tipo: "ingreso", Color: "#22c55e"},
|
||||
{Codigo: "ING-OTR", Nombre: "Otros ingresos", Tipo: "ingreso", Color: "#16a34a"},
|
||||
{Codigo: "EGR-HOS", Nombre: "Hosting/Servidores", Tipo: "egreso", Color: "#ef4444"},
|
||||
{Codigo: "EGR-DOM", Nombre: "Dominios", Tipo: "egreso", Color: "#dc2626"},
|
||||
{Codigo: "EGR-SRV", Nombre: "Servicios", Tipo: "egreso", Color: "#f97316"},
|
||||
{Codigo: "EGR-GRAL", Nombre: "Gastos generales", Tipo: "egreso", Color: "#eab308"},
|
||||
{Codigo: "EGR-NOM", Nombre: "Nómina", Tipo: "egreso", Color: "#a855f7"},
|
||||
{Codigo: "EGR-IMPU", Nombre: "Impuestos", Tipo: "egreso", Color: "#6366f1"},
|
||||
{Codigo: "EGR-MKT", Nombre: "Marketing", Tipo: "egreso", Color: "#ec4899"},
|
||||
{Codigo: "EGR-NOM", Nombre: "Nómina", Tipo: "egreso", Color: "#a855f7"},
|
||||
{Codigo: "EGR-IMPU", Nombre: "Impuestos", Tipo: "egreso", Color: "#6366f1"},
|
||||
{Codigo: "EGR-MKT", Nombre: "Marketing", Tipo: "egreso", Color: "#ec4899"},
|
||||
}
|
||||
for _, c := range cuentas {
|
||||
var existing Cuenta
|
||||
@@ -675,13 +702,13 @@ func SeedContabilidad() {
|
||||
}
|
||||
|
||||
entidades := []Entidad{
|
||||
{Nombre: "DOCUXER", Tipo: "cliente"},
|
||||
{Nombre: "GIAF SAS", Tipo: "cliente"},
|
||||
{Nombre: "TECZONE", Tipo: "proveedor"},
|
||||
{Nombre: "FELIPE", Tipo: "proveedor"},
|
||||
{Nombre: "NATALIA", Tipo: "proveedor"},
|
||||
{Nombre: "CONTADORA", Tipo: "proveedor"},
|
||||
{Nombre: "ANDREMER", Tipo: "proveedor"},
|
||||
{Nombre: "DOCUXER", Tipo: "cliente"},
|
||||
{Nombre: "GIAF SAS", Tipo: "cliente"},
|
||||
{Nombre: "TECZONE", Tipo: "proveedor"},
|
||||
{Nombre: "FELIPE", Tipo: "proveedor"},
|
||||
{Nombre: "NATALIA", Tipo: "proveedor"},
|
||||
{Nombre: "CONTADORA", Tipo: "proveedor"},
|
||||
{Nombre: "ANDREMER", Tipo: "proveedor"},
|
||||
}
|
||||
for _, e := range entidades {
|
||||
var existing Entidad
|
||||
|
||||
Reference in New Issue
Block a user