feat: mis cobranzas, recordatorios de pago y agente IA con acciones

- PlanPago.vendedorId + filtro ?mios=1; toggle Mis planes/Todos en /pay
- Motor de recordatorios: próximos a vencer (3d) y vencidos (re-cada 7d),
  canal Monday Actividad + stub WhatsApp listo para WA CRM, log persistente,
  cron diario 9:30, run manual con dry-run, historial en /api/cobranza/recordatorios
- Agente IA: memoria por usuario (conversaciones aisladas) y tool calling:
  cotizar_unidad (motor real por desarrollo) y buscar_unidad (inventario Monday)
- Tests: api 261, cobranza 75, agente-ia 13
This commit is contained in:
2026-08-28 08:07:12 +00:00
parent 59944668a8
commit aa27e89c75
18 changed files with 1437 additions and 17 deletions

View File

@@ -335,3 +335,44 @@ describe('repository async (API primero, fallback localStorage)', () => {
expect(guardados[0].status).toBe('CANCELADO')
})
})
describe('mis cobranzas (toggle Mis planes / Todos)', () => {
it('rutaListadoPlanes agrega ?mios=1 solo cuando se piden los míos', async () => {
const { rutaListadoPlanes } = await import('./repository')
expect(rutaListadoPlanes(true)).toBe('/planes?mios=1')
expect(rutaListadoPlanes(false)).toBe('/planes')
})
it('miosPorDefecto: vendedor arranca en "Mis planes"; el resto en "Todos"', async () => {
const { miosPorDefecto } = await import('./repository')
expect(miosPorDefecto('vendedor')).toBe(true)
expect(miosPorDefecto('admin')).toBe(false)
expect(miosPorDefecto('coordinador')).toBe(false)
expect(miosPorDefecto('corporativo')).toBe(false)
expect(miosPorDefecto(null)).toBe(false)
expect(miosPorDefecto(undefined)).toBe(false)
})
it('listarPlanes con mios=true llama al API con ?mios=1', async () => {
const fetchMock = fetchQue(async () => respuestaJson([planApi()]))
vi.stubGlobal('fetch', fetchMock)
const r = await listarPlanes(crearStorageStub(), true)
expect(r.origen).toBe('api')
expect(fetchMock).toHaveBeenCalledWith(
'http://192.168.10.123:3000/api/planes?mios=1',
expect.objectContaining({ headers: { 'Content-Type': 'application/json' } }),
)
})
it('listarPlanes con mios=true cae a localStorage igual que sin el filtro', async () => {
vi.stubGlobal('fetch', fetchSinRed())
vi.spyOn(console, 'warn').mockImplementation(() => {})
const r = await listarPlanes(crearStorageStub(), true)
expect(r.origen).toBe('local') // el respaldo local no tiene vendedorId
expect(r.planes.length).toBeGreaterThan(0)
})
})