feat(purga): Monday eliminado completamente del sistema

14 servicios eliminados, 7 controllers local-only, crons locales,
config sin MONDAY_*, frontend sin banners/avisos Monday.
552 tests API + 7 apps construidas.
This commit is contained in:
2026-09-11 11:47:17 +00:00
parent 64b01246d6
commit 89d95eb53b
130 changed files with 2018 additions and 14747 deletions

View File

@@ -1,49 +1,5 @@
import { describe, expect, it } from 'vitest'
import { claveTelefono, detalleContacto } from './contactos-detalle.service.js'
import type { LeadMonday, OportunidadMonday, ApartadoMonday } from './monday.service.js'
const MI_ID = 106736727
const leadBase: LeadMonday = {
id: 'l1', nombre: 'Ana Demo', createdAt: '', updatedAt: '', estado: 'Nuevo', origen: 'PASE',
telefono: '+52 6641234567', email: 'ana@correo.com', vendedorIds: [MI_ID],
}
const opBase: OportunidadMonday = {
id: 'o1', nombre: 'Ana Demo', createdAt: '', updatedAt: '', etapa: 'Cotizador', unidadCodigo: '',
precioFinal: 1500000, asesorIds: [MI_ID], telefono: '6641234567', email: '', origen: 'PASE',
desarrollo: 'SENDA', modeloInteres: 'SOHO',
}
const apBase: ApartadoMonday = {
id: 'a1', nombre: 'Ana Demo', unidadCodigo: 'SENDA-409-1-2', fechaLimite: '2026-09-10',
estado: 'Pago Apartado', vendedorIds: [MI_ID], telefono: '6641234567', email: '',
precioFinal: 2646000, fechaCompra: '2026-09-01',
}
function mondayMock(overrides: Record<string, unknown> = {}) {
return {
async getContactoDetalle() {
return { nombre: 'Ana Demo', Teléfono: '+52 6641234567', Email: 'ana@correo.com', Estado: 'CLIENTE', CURP: 'XAXX010101XXXXXXX0' }
},
async getLeads() { return [leadBase] },
async getOportunidades() { return [opBase] },
async getApartados() { return [apBase] },
async getHistorialItems(_b: string, ids: string[]) {
return ids.map((id, i) => ({
itemId: id,
fechaISO: `2026-09-0${(i % 9) + 1}T10:00:00.000Z`,
usuarioId: MI_ID,
evento: 'update_column_value',
}))
},
async getUnidad() { return { desarrollo: 'SENDA' } },
...overrides,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any
}
const nombres = new Map([[MI_ID, 'Martin Zavala']])
import { claveTelefono } from './contactos-detalle.service.js'
describe('claveTelefono', () => {
it('últimos 10 dígitos, tolerante a lada, espacios y símbolos', () => {
@@ -54,69 +10,3 @@ describe('claveTelefono', () => {
expect(claveTelefono('123')).toBe('')
})
})
describe('detalleContacto (GET /api/contactos/:id/detalle)', () => {
it('arma contacto + propiedad + relaciones + historial con nombres resueltos', async () => {
const d = await detalleContacto(
mondayMock(),
'c1',
nombres,
[{ leadId: 'l1', nombreLead: 'Ana Demo', createdAt: new Date('2026-09-02T08:00:00Z'), usuario: { nombre: 'Martin Zavala' } }],
{
id: 'cl1', nombreCompleto: 'Ana Demo', proyecto: 'SENDA', unidadCodigo: 'SENDA-409-1-2',
createdAt: new Date('2026-09-03T08:00:00Z'), capturadoPor: { nombre: 'Martin Zavala' },
},
)
expect(d).not.toBeNull()
// El contacto del detalle se devuelve con campos nombrados del modelo local
expect(d!.contacto['curp']).toBe('XAXX010101XXXXXXX0')
// Propiedad comprada (apartado relacionado, enriquecido con el desarrollo)
expect(d!.propiedad).toMatchObject({
unidadCodigo: 'SENDA-409-1-2',
desarrollo: 'SENDA',
estado: 'Pago Apartado',
precioFinal: 2646000,
atendidoPor: 'Martin Zavala',
})
// Relaciones por teléfono/email con quién atiende
expect(d!.relaciones.leads[0]).toMatchObject({ id: 'l1', atendidoPor: 'Martin Zavala' })
expect(d!.relaciones.oportunidades[0]).toMatchObject({ id: 'o1', atendidoPor: 'Martin Zavala' })
expect(d!.relaciones.apartados[0]).toMatchObject({ id: 'a1', atendidoPor: 'Martin Zavala' })
// Historial: activity log + captura + alta local, ordenado desc
expect(d!.historial.some((h) => h.fuente === 'App' && h.detalle.includes('Captura'))).toBe(true)
expect(d!.historial.some((h) => h.detalle.includes('Alta de cliente'))).toBe(true)
expect(d!.historial.every((h) => h.usuario === 'Martin Zavala')).toBe(true)
const fechas = d!.historial.map((h) => h.fechaISO)
expect(fechas).toEqual([...fechas].sort((a, b) => b.localeCompare(a)))
})
it('sin relaciones ni registros locales: listas vacías y propiedad null', async () => {
const d = await detalleContacto(
mondayMock({
async getLeads() { return [] },
async getOportunidades() { return [] },
async getApartados() { return [] },
}),
'c1',
new Map(),
[],
null,
)
expect(d!.propiedad).toBeNull()
expect(d!.relaciones).toEqual({ leads: [], oportunidades: [], apartados: [] })
// El historial al menos tiene los eventos del propio contacto
expect(d!.historial).toHaveLength(1)
expect(d!.historial[0]?.fuente).toBe('Contactos')
})
it('contacto inexistente devuelve null', async () => {
const d = await detalleContacto(
mondayMock({ async getContactoDetalle() { return null } }),
'no-existe',
new Map(),
[],
null,
)
expect(d).toBeNull()
})
})