fix(mi-dia): leads y oportunidades en pestañas separadas + limpieza
- misLeads: solo muestra items del tablero Leads (captura ligera) - misOportunidades: muestra items del tablero Oportunidades (afluencia) - Eliminado el merge que causaba que el mismo item apareciera en ambas - apartadosPorVencer: filtrado por esMio (solo propios) - Limpiado duplicado MONICA RODRIGUEZ VEGA de Monday Leads - 630 tests verdes
This commit is contained in:
@@ -105,7 +105,7 @@ describe('resumenMiDiaEndpoint (modo local y unión)', () => {
|
|||||||
expect(mondayMock.getLeads).not.toHaveBeenCalled()
|
expect(mondayMock.getLeads).not.toHaveBeenCalled()
|
||||||
expect(mondayMock.getOportunidades).not.toHaveBeenCalled()
|
expect(mondayMock.getOportunidades).not.toHaveBeenCalled()
|
||||||
const resumen = json.mock.calls[0]?.[0] as Record<string, unknown>
|
const resumen = json.mock.calls[0]?.[0] as Record<string, unknown>
|
||||||
expect((resumen['misLeads'] as unknown[]).length).toBe(2)
|
expect((resumen["misLeads"] as unknown[]).length).toBe(1) // solo leads, no oportunidades
|
||||||
expect((resumen['misOportunidades'] as { items: unknown[] }).items).toHaveLength(1)
|
expect((resumen['misOportunidades'] as { items: unknown[] }).items).toHaveLength(1)
|
||||||
expect(resumen['misActividadesHoy']).toEqual([])
|
expect(resumen['misActividadesHoy']).toEqual([])
|
||||||
expect(resumen['conteos']).toMatchObject({ actividadesAbiertas: 0 })
|
expect(resumen['conteos']).toMatchObject({ actividadesAbiertas: 0 })
|
||||||
|
|||||||
@@ -62,9 +62,8 @@ describe('resumenMiDiaLocal (Monday caído)', () => {
|
|||||||
const r = resumenMiDiaLocal(vendedor, capturas, [], AHORA)
|
const r = resumenMiDiaLocal(vendedor, capturas, [], AHORA)
|
||||||
|
|
||||||
expect(r.sinMapeoMonday).toBe(false)
|
expect(r.sinMapeoMonday).toBe(false)
|
||||||
expect(r.misLeads.map((l) => l.id).sort()).toEqual(['loc_l1', 'loc_o1'])
|
expect(r.misLeads.map((l) => l.id).sort()).toEqual(['loc_l1']) // solo leads, no oportunidades
|
||||||
expect(r.misLeads.find((l) => l.id === 'loc_l1')?.fuente).toBe('lead')
|
expect(r.misLeads.find((l) => l.id === 'loc_l1')?.fuente).toBe('lead')
|
||||||
expect(r.misLeads.find((l) => l.id === 'loc_o1')?.fuente).toBe('oportunidad')
|
|
||||||
// porEtapa cuenta TODAS las oportunidades (incluye terminales, como Monday)
|
// porEtapa cuenta TODAS las oportunidades (incluye terminales, como Monday)
|
||||||
expect(r.misOportunidades.porEtapa).toEqual({ Afluencia: 1, Perdido: 1 })
|
expect(r.misOportunidades.porEtapa).toEqual({ Afluencia: 1, Perdido: 1 })
|
||||||
expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['loc_o1'])
|
expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['loc_o1'])
|
||||||
|
|||||||
@@ -126,8 +126,8 @@ describe('resumenMiDia (GET /api/mi-dia/resumen)', () => {
|
|||||||
})
|
})
|
||||||
const r = await resumenMiDia(monday, vendedor, new Map(), [], [], AHORA)
|
const r = await resumenMiDia(monday, vendedor, new Map(), [], [], AHORA)
|
||||||
expect(r.sinMapeoMonday).toBe(false)
|
expect(r.sinMapeoMonday).toBe(false)
|
||||||
expect(r.misLeads.map((l) => l.id)).toEqual(['l2', 'o1', 'o2', 'l1']) // updatedAt desc mezclando tableros
|
expect(r.misLeads.map((l) => l.id)).toEqual(['l2', 'l1']) // solo leads, no oportunidades // updatedAt desc mezclando tableros
|
||||||
expect(r.misLeads.map((l) => l.fuente)).toEqual(['lead', 'oportunidad', 'oportunidad', 'lead'])
|
expect(r.misLeads.map((l) => l.fuente)).toEqual(['lead', 'lead']) // solo leads
|
||||||
expect(r.misLeads[0]?.telefono).toBe('4771234567')
|
expect(r.misLeads[0]?.telefono).toBe('4771234567')
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -147,12 +147,7 @@ describe('resumenMiDia (GET /api/mi-dia/resumen)', () => {
|
|||||||
usuario: 'Martin Zavala',
|
usuario: 'Martin Zavala',
|
||||||
detalle: 'Actualización',
|
detalle: 'Actualización',
|
||||||
})
|
})
|
||||||
// usuarioId sin nombre en el mapa → fallback null
|
// 'o1' ya no esta en misLeads (es oportunidad)
|
||||||
expect(r.misLeads.find((l) => l.id === 'o1')?.ultimoCambio).toEqual({
|
|
||||||
fechaISO: '2026-08-28T09:00:00.000Z',
|
|
||||||
usuario: null,
|
|
||||||
detalle: 'Alta',
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('ultimoCambio: null cuando el item no tiene eventos en el activity log', async () => {
|
it('ultimoCambio: null cuando el item no tiene eventos en el activity log', async () => {
|
||||||
@@ -562,7 +557,7 @@ describe('resumenMiDia — cuenta compartida / uso solo-app (sin Monday)', () =>
|
|||||||
{ leadId: 'o1', nombreLead: 'Lead o1', telefono: null, createdAt: new Date('2026-08-27') },
|
{ leadId: 'o1', nombreLead: 'Lead o1', telefono: null, createdAt: new Date('2026-08-27') },
|
||||||
]
|
]
|
||||||
const r = await resumenMiDia(monday, sinMapeo, new Map(), capturas, [], AHORA)
|
const r = await resumenMiDia(monday, sinMapeo, new Map(), capturas, [], AHORA)
|
||||||
expect(r.misLeads.map((l) => l.id)).toEqual(['o1', 'l1'])
|
expect(r.misLeads.map((l) => l.id)).toEqual(['l1']) // solo leads
|
||||||
expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['o1'])
|
expect(r.misOportunidades.items.map((o) => o.id)).toEqual(['o1'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -282,11 +282,9 @@ export async function resumenMiDia(
|
|||||||
fuente: 'oportunidad',
|
fuente: 'oportunidad',
|
||||||
ultimoCambio: null,
|
ultimoCambio: null,
|
||||||
}))
|
}))
|
||||||
// Excluir de "Mis leads" los que ya aparecen en "Mis oportunidades"
|
// "Mis leads" solo muestra items del tablero Leads (captura ligera).
|
||||||
// para no mostrar el mismo item en dos pestañas
|
// Las oportunidades van exclusivamente en "Mis oportunidades".
|
||||||
const idsEnOportunidades = new Set(misLeadsOportunidades.map((o) => o.id))
|
const misLeads: LeadMiDia[] = misLeadsLeads.sort((a, b) =>
|
||||||
const misLeadsLeadsSinDuplicados = misLeadsLeads.filter((l) => !idsEnOportunidades.has(l.id))
|
|
||||||
const misLeads: LeadMiDia[] = [...misLeadsLeadsSinDuplicados, ...misLeadsOportunidades].sort((a, b) =>
|
|
||||||
b.updatedAt.localeCompare(a.updatedAt),
|
b.updatedAt.localeCompare(a.updatedAt),
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -589,37 +587,23 @@ export function resumenMiDiaLocal(
|
|||||||
const oportunidadesLocales = capturas.filter((c) => c.board === 'OPORTUNIDADES')
|
const oportunidadesLocales = capturas.filter((c) => c.board === 'OPORTUNIDADES')
|
||||||
const apartadosLocales = capturas.filter((c) => c.board === 'APARTADO')
|
const apartadosLocales = capturas.filter((c) => c.board === 'APARTADO')
|
||||||
|
|
||||||
// "Mis leads" mezcla Leads + Oportunidades, igual que el camino Monday
|
// "Mis leads" solo muestra leads (board LEADS), no oportunidades
|
||||||
const misLeads: LeadMiDia[] = [
|
const misLeads: LeadMiDia[] = leadsLocales
|
||||||
...leadsLocales
|
.filter((c) => !esEstadoTerminalLead(c.etapa))
|
||||||
.filter((c) => !esEstadoTerminalLead(c.etapa))
|
.map((c) => ({
|
||||||
.map((c) => ({
|
id: c.leadId,
|
||||||
id: c.leadId,
|
nombre: c.nombreLead,
|
||||||
nombre: c.nombreLead,
|
telefono: c.telefono ?? '',
|
||||||
telefono: c.telefono ?? '',
|
email: c.email ?? '',
|
||||||
email: c.email ?? '',
|
estado: c.etapa,
|
||||||
estado: c.etapa,
|
origen: c.origen ?? '',
|
||||||
origen: c.origen ?? '',
|
updatedAt: c.updatedAt.toISOString(),
|
||||||
updatedAt: c.updatedAt.toISOString(),
|
fuente: 'lead' as const,
|
||||||
fuente: 'lead' as const,
|
ultimoCambio: ultimoCambioLocal(c, usuario.nombre),
|
||||||
ultimoCambio: ultimoCambioLocal(c, usuario.nombre),
|
pendienteSync: c.pendienteSync,
|
||||||
pendienteSync: c.pendienteSync,
|
}))
|
||||||
})),
|
.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))
|
||||||
...oportunidadesLocales
|
// (oportunidades se muestran exclusivamente en misOportunidades)
|
||||||
.filter((c) => !esEtapaTerminal(c.etapa))
|
|
||||||
.map((c) => ({
|
|
||||||
id: c.leadId,
|
|
||||||
nombre: c.nombreLead,
|
|
||||||
telefono: c.telefono ?? '',
|
|
||||||
email: c.email ?? '',
|
|
||||||
estado: c.etapa,
|
|
||||||
origen: c.origen ?? '',
|
|
||||||
updatedAt: c.updatedAt.toISOString(),
|
|
||||||
fuente: 'oportunidad' as const,
|
|
||||||
ultimoCambio: ultimoCambioLocal(c, usuario.nombre),
|
|
||||||
pendienteSync: c.pendienteSync,
|
|
||||||
})),
|
|
||||||
].sort(porUpdatedAtDesc)
|
|
||||||
|
|
||||||
// Conteo por etapa de TODAS las oportunidades (incluye terminales, como Monday)
|
// Conteo por etapa de TODAS las oportunidades (incluye terminales, como Monday)
|
||||||
const porEtapa: Record<string, number> = {}
|
const porEtapa: Record<string, number> = {}
|
||||||
|
|||||||
Reference in New Issue
Block a user