refactor: las afluencias viven en Oportunidades (no en Leads)

- POST /api/leads crea UN solo item en Oportunidades (Estado Afluencia)
  con todo el perfil (desarrollo, modelo, estado civil, credito, etc.)
  en las columnas del board; dedup por telefono contra Oportunidades;
  asignacion al capturista via columna Asesor o round-robin del equipo.
- Round-robin, escalado e inactividad operan sobre Oportunidades (solo
  etapa Afluencia sin asesor entra al reparto).
- Acciones del vendedor (contactado/cita/descartar) escriben directo en
  la oportunidad; descarte = Perdido + Motivo No Compra.
- Mi dia y reportes leen Oportunidades; el funnel de /dash arranca en
  Afluencia; misOportunidades.porEtapa ya es real (columna Asesor).
- El picker del alta busca en Leads (historico) Y Oportunidades, con
  insignia de origen; moverAAparto tolera ambos ids.
- Lectura de status con value null = vacio (quirk del default Monday).
- Script de migracion del historial de agosto Leads -> Oportunidades.
This commit is contained in:
2026-09-04 04:46:34 +00:00
parent d4fc9ee892
commit 845f314560
26 changed files with 1496 additions and 985 deletions

View File

@@ -206,7 +206,7 @@ describe('AltaAfluenciaForm', () => {
})
it('tras el éxito, "Pasar a Alta de Cliente" entrega el lead recién creado', async () => {
fetchMock.mockResolvedValueOnce(respuesta({ id: 'l-1', estado: 'NUEVO', oportunidadId: 'op-1' }))
fetchMock.mockResolvedValueOnce(respuesta({ id: 'op-1', estado: 'Afluencia', oportunidadId: 'op-1' }))
const onPasar = vi.fn()
render(<AltaAfluenciaForm onVolver={() => undefined} onPasarAAltaCliente={onPasar} />)
llenarRequeridos()
@@ -216,12 +216,13 @@ describe('AltaAfluenciaForm', () => {
fireEvent.click(await screen.findByRole('button', { name: /Pasar a Alta de Cliente/ }))
expect(onPasar).toHaveBeenCalledWith({
id: 'l-1',
id: 'op-1',
nombre: 'Ana Demo',
telefono: '6641234567',
email: 'ana@correo.com',
estado: 'NUEVO',
estado: 'Afluencia',
origen: '',
fuente: 'oportunidad', // la captura nueva vive en Oportunidades
})
})
})