fix(mi-dia): dedup capturas que ya aparecen en leads/oportunidades

Cuando un lead se crea con Monday activo, aparece tanto en el tablero
Monday (misLeads) como en LeadCaptura (misCapturas). Filtrar de
'misCapturas' los items cuyo leadId ya existe en misLeads/misOportunidades
para evitar el duplicado visual que reportan los vendedores.

597 tests verdes.
This commit is contained in:
2026-09-06 08:26:03 +00:00
parent 60649890eb
commit 50e6eca7da
3 changed files with 24 additions and 26 deletions

View File

@@ -70,7 +70,7 @@ describe('resumenMiDiaLocal (Monday caído)', () => {
expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['loc_o1']) expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['loc_o1'])
expect(r.misOportunidades.items[0]).toMatchObject({ etapa: 'Afluencia', desarrollo: 'SENDA', modeloInteres: '1R' }) expect(r.misOportunidades.items[0]).toMatchObject({ etapa: 'Afluencia', desarrollo: 'SENDA', modeloInteres: '1R' })
expect(r.misApartados.map((a) => a.id)).toEqual(['loc_a1']) expect(r.misApartados.map((a) => a.id)).toEqual(['loc_a1'])
expect(r.misCapturas).toHaveLength(6) expect(r.misCapturas).toHaveLength(4) // loc_l1 y loc_o1 se excluyen (ya en misLeads/misOportunidades)
// Lo que no existe en local va vacío/cero sin romper el front // Lo que no existe en local va vacío/cero sin romper el front
expect(r.misActividadesHoy).toEqual([]) expect(r.misActividadesHoy).toEqual([])
expect(r.apartadosPorVencer).toEqual([]) expect(r.apartadosPorVencer).toEqual([])

View File

@@ -372,7 +372,7 @@ describe('resumenMiDia (GET /api/mi-dia/resumen)', () => {
} }
}) })
it('mis capturas: enriquecidas con la etapa/origen actual de la oportunidad en Monday', async () => { it('mis capturas: items en misLeads/misOportunidades no se duplican en misCapturas', async () => {
const monday = mondayMock({ const monday = mondayMock({
oportunidades: [oportunidad({ id: '100', nombre: 'Ana López', etapa: 'Contactado', origen: 'WhatsApp', asesorIds: [OTRO_ID] })], oportunidades: [oportunidad({ id: '100', nombre: 'Ana López', etapa: 'Contactado', origen: 'WhatsApp', asesorIds: [OTRO_ID] })],
}) })
@@ -380,20 +380,11 @@ describe('resumenMiDia (GET /api/mi-dia/resumen)', () => {
{ leadId: '100', nombreLead: 'Ana López', telefono: '6641112233', createdAt: new Date('2026-08-27T09:00:00Z') }, { leadId: '100', nombreLead: 'Ana López', telefono: '6641112233', createdAt: new Date('2026-08-27T09:00:00Z') },
] ]
const r = await resumenMiDia(monday, vendedor, new Map(), capturas, [], AHORA) const r = await resumenMiDia(monday, vendedor, new Map(), capturas, [], AHORA)
// Aunque la afluencia quedó asignada a OTRO por round-robin, la captura es mía // La captura ya aparece en misLeads (via esMio → idsCapturados), asi que no se duplica
expect(r.misCapturas).toEqual([ expect(r.misCapturas).toEqual([])
{
leadId: '100',
nombre: 'Ana López',
telefono: '6641112233',
estado: 'Contactado',
origen: 'WhatsApp',
capturadoEn: '2026-08-27T09:00:00.000Z',
},
])
}) })
it('mis capturas: capturas rápidas cruzan contra el tablero Leads por id', async () => { it('mis capturas: captura rapida no se duplica si ya esta en misLeads', async () => {
const monday = mondayMock({ const monday = mondayMock({
leads: [lead({ id: 'lead-9', nombre: 'Pepe Rápido', estado: 'Contactado', origen: 'Web', vendedorIds: [OTRO_ID] })], leads: [lead({ id: 'lead-9', nombre: 'Pepe Rápido', estado: 'Contactado', origen: 'Web', vendedorIds: [OTRO_ID] })],
}) })
@@ -401,16 +392,8 @@ describe('resumenMiDia (GET /api/mi-dia/resumen)', () => {
{ leadId: 'lead-9', nombreLead: 'Pepe Rápido', telefono: '4771112233', createdAt: new Date('2026-08-27T09:00:00Z') }, { leadId: 'lead-9', nombreLead: 'Pepe Rápido', telefono: '4771112233', createdAt: new Date('2026-08-27T09:00:00Z') },
] ]
const r = await resumenMiDia(monday, vendedor, new Map(), capturas, [], AHORA) const r = await resumenMiDia(monday, vendedor, new Map(), capturas, [], AHORA)
expect(r.misCapturas).toEqual([ // La captura ya aparece en misLeads (via esMio → idsCapturados), asi que no se duplica
{ expect(r.misCapturas).toEqual([])
leadId: 'lead-9',
nombre: 'Pepe Rápido',
telefono: '4771112233',
estado: 'Contactado',
origen: 'Web',
capturadoEn: '2026-08-27T09:00:00.000Z',
},
])
}) })
it('mis capturas: si el item ya no existe en ningún tablero, estado y origen van vacíos', async () => { it('mis capturas: si el item ya no existe en ningún tablero, estado y origen van vacíos', async () => {

View File

@@ -400,6 +400,14 @@ export async function resumenMiDia(
} }
}) })
.sort((a, b) => b.capturadoEn.localeCompare(a.capturadoEn)) .sort((a, b) => b.capturadoEn.localeCompare(a.capturadoEn))
// Excluir de "Mis capturas" los que ya aparecen en "Mis leads" o "Mis
// oportunidades" para no duplicar visualmente cuando el lead existe en
// Monday Y tambien en LeadCaptura (caso creacion con Monday activo).
const idsEnOtrosTabs = new Set([
...misLeads.map((l) => l.id),
...mios.map((o) => o.id),
])
const misCapturasSinDuplicados = misCapturas.filter((c) => !idsEnOtrosTabs.has(c.leadId))
// "Mis clientes" es local + cruce con Monday: el alta en progreso lleva // "Mis clientes" es local + cruce con Monday: el alta en progreso lleva
// proyecto '', el alta completada lleva el proyecto lleno. estadoApartado // proyecto '', el alta completada lleva el proyecto lleno. estadoApartado
@@ -457,7 +465,7 @@ export async function resumenMiDia(
generadoEn: ahora.toISOString(), generadoEn: ahora.toISOString(),
sinMapeoMonday: sinMapeo, sinMapeoMonday: sinMapeo,
misLeads, misLeads,
misCapturas, misCapturas: misCapturasSinDuplicados,
misClientes, misClientes,
misActividadesHoy, misActividadesHoy,
apartadosPorVencer, apartadosPorVencer,
@@ -661,6 +669,13 @@ export function resumenMiDiaLocal(
pendienteSync: c.pendienteSync, pendienteSync: c.pendienteSync,
})) }))
.sort((a, b) => b.capturadoEn.localeCompare(a.capturadoEn)) .sort((a, b) => b.capturadoEn.localeCompare(a.capturadoEn))
// Excluir de "Mis capturas" los que ya aparecen en "Mis leads" u "Mis
// oportunidades" para no duplicar visualmente.
const idsEnOtrosTabsLocal = new Set([
...misLeads.map((l) => l.id),
...itemsOportunidades.map((o) => o.id),
])
const misCapturasSinDuplicadosLocal = misCapturas.filter((c) => !idsEnOtrosTabsLocal.has(c.leadId))
const etapaApartadoPorId = new Map(apartadosLocales.map((a) => [a.leadId, a.etapa])) const etapaApartadoPorId = new Map(apartadosLocales.map((a) => [a.leadId, a.etapa]))
const misClientes: ClienteMiDia[] = clientes const misClientes: ClienteMiDia[] = clientes
@@ -680,7 +695,7 @@ export function resumenMiDiaLocal(
generadoEn: ahora.toISOString(), generadoEn: ahora.toISOString(),
sinMapeoMonday: usuario.mondayUserId === null, sinMapeoMonday: usuario.mondayUserId === null,
misLeads, misLeads,
misCapturas, misCapturas: misCapturasSinDuplicadosLocal,
misClientes, misClientes,
misActividadesHoy: [], // no hay tabla local de actividades misActividadesHoy: [], // no hay tabla local de actividades
apartadosPorVencer: [], // los apartados locales aún no guardan fecha límite apartadosPorVencer: [], // los apartados locales aún no guardan fecha límite