feat(pendientes): cotizador (mensualidades, bloqueo de unidad, totales, numMensualidades), cobranza (recibos, atrasos, mora configurable, recordatorios), soporte (adjuntos, filtros, estados), clientes maestro (catalogos reales), contactos (ficha y duplicados) y permisos de captura por rol

This commit is contained in:
2026-09-21 08:34:53 +00:00
parent b5a20aed4e
commit 2fc80e7f9e
66 changed files with 3792 additions and 436 deletions

View File

@@ -18,6 +18,7 @@ import {
listar,
obtener,
contar,
catalogos,
exportar,
importarDesdeExcel,
} from './cliente-maestro.service.js'
@@ -49,23 +50,31 @@ describe('listar', () => {
await listar({ busqueda: 'Juan', pagina: 1, limite: 50 })
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({
OR: expect.arrayContaining([
{ nombre: { contains: 'Juan' } },
]),
}),
where: {
AND: [
expect.objectContaining({
OR: expect.arrayContaining([{ nombre: { contains: 'Juan' } }]),
}),
],
},
}),
)
})
it('aplica filtro de desarrollo', async () => {
it('aplica filtro de desarrollo (valor exacto o con sufijo " - ...")', async () => {
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
prismaMock.clienteMaestro.count.mockResolvedValue(0)
await listar({ desarrollo: 'SENDA', pagina: 1, limite: 50 })
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({ desarrollo: 'SENDA' }),
where: {
AND: [
{
OR: [{ desarrollo: 'SENDA' }, { desarrollo: { startsWith: 'SENDA -' } }],
},
],
},
}),
)
})
@@ -77,10 +86,48 @@ describe('listar', () => {
await listar({ estatus: 'VENDIDO', pagina: 1, limite: 50 })
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
expect.objectContaining({
where: expect.objectContaining({ estatus: 'VENDIDO' }),
where: { AND: [{ estatus: 'VENDIDO' }] },
}),
)
})
it('combina búsqueda, desarrollo y estatus en la misma consulta', async () => {
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
prismaMock.clienteMaestro.count.mockResolvedValue(0)
await listar({ busqueda: 'Ana', desarrollo: 'SOLE', estatus: 'ENTREGA', pagina: 1, limite: 50 })
const llamada = prismaMock.clienteMaestro.findMany.mock.calls[0]![0] as { where: { AND: unknown[] } }
expect(llamada.where.AND).toHaveLength(3)
})
it('sin filtros no arma condiciones', async () => {
prismaMock.clienteMaestro.findMany.mockResolvedValue([])
prismaMock.clienteMaestro.count.mockResolvedValue(0)
await listar({ pagina: 1, limite: 50 })
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith(
expect.objectContaining({ where: {} }),
)
})
})
// ── catalogos ────────────────────────────────────────────────────────
describe('catalogos', () => {
it('devuelve los desarrollos y estatus reales, sin vacíos ni nulos', async () => {
prismaMock.clienteMaestro.findMany
.mockResolvedValueOnce([{ desarrollo: 'MUTUO - EDIFICACION' }, { desarrollo: 'SOLE' }, { desarrollo: null }, { desarrollo: ' ' }])
.mockResolvedValueOnce([{ estatus: 'ENTREGA' }, { estatus: 'VENDIDO' }, { estatus: null }])
const result = await catalogos()
expect(result.desarrollos).toEqual(['MUTUO - EDIFICACION', 'SOLE'])
expect(result.estatuses).toEqual(['ENTREGA', 'VENDIDO'])
expect(prismaMock.clienteMaestro.findMany).toHaveBeenCalledWith({
distinct: ['desarrollo'],
select: { desarrollo: true },
orderBy: { desarrollo: 'asc' },
})
})
})
// ── obtener ──────────────────────────────────────────────────────────
@@ -113,7 +160,11 @@ describe('contar', () => {
prismaMock.clienteMaestro.count.mockResolvedValue(5)
expect(await contar({ desarrollo: 'SENDA' })).toBe(5)
expect(prismaMock.clienteMaestro.count).toHaveBeenCalledWith(
expect.objectContaining({ where: { desarrollo: 'SENDA' } }),
{
where: {
AND: [{ OR: [{ desarrollo: 'SENDA' }, { desarrollo: { startsWith: 'SENDA -' } }] }],
},
},
)
})
})