diff --git a/docs/sessions/2026-05-23-asignaciones-tareas-admin-ui.md b/docs/sessions/2026-05-23-asignaciones-tareas-admin-ui.md new file mode 100644 index 0000000..f2328fc --- /dev/null +++ b/docs/sessions/2026-05-23-asignaciones-tareas-admin-ui.md @@ -0,0 +1,163 @@ +# Sesión 2026-05-23: Asignaciones, Tareas, Admin UI y Fixes + +## Resumen + +Sesión extensa con múltiples fixes de bugs críticos descubiertos tras deploy de la feature de asignaciones, más nuevas funcionalidades de UX solicitadas por el usuario. + +--- + +## 1. Fixes de build y deploy inicial de Asignaciones + +### Problemas de build de Next.js +- **`Badge` no exportado desde `@horux/shared-ui`**: Reemplazado por `` con clases CSS equivalentes en `seguimiento-auxiliares.tsx`. +- **`Tabs` requería `defaultValue`**: Agregado `defaultValue="carteras"` al componente `` en `carteras/page.tsx`. + +### Migración tenant 046 aplicada +- Archivo: `apps/api/src/migrations/tenant/046_asignaciones_obligaciones_tareas.sql` +- Tablas creadas: `obligacion_asignaciones`, `tarea_asignaciones` +- Aplicada a 7 tenants activos. + +--- + +## 2. Fix: Invitación de usuarios no enviaba correo + +**Root cause:** `usuarios.service.ts` → `inviteUsuario` generaba `tempPassword` pero nunca llamaba `emailService.sendWelcome()`. + +**Fix:** +- Importado `emailService` en `usuarios.service.ts` +- Agregada llamada `emailService.sendWelcome()` cuando se crea un usuario nuevo (`tempPassword !== null`) +- Aplicado también a `createUsuarioGlobal` + +### Reenvío de correos a usuarios invitados previos +- Identificados 9 usuarios creados entre jueves 21/05 y hoy sin `lastLogin` +- Generadas nuevas contraseñas temporales, hasheadas en BD, y enviados correos de bienvenida + +--- + +## 3. Fix: Subcartera mostraba contribuyentes de todo el tenant + +**Root cause:** `SubcarteraCard` filtraba `available` solo excluyendo los ya asignados a la subcartera, sin verificar que pertenecieran a la cartera padre. + +**Fixes:** +- **Frontend:** `SubcarteraCard` ahora recibe `parentEntidadIds` y filtra: `(parentEntidadIds ?? []).includes(c.id)` +- **Backend:** `cartera.service.ts` → `addEntidadToCartera` valida que si es subcartera (`parentId !== null`), la entidad previamente exista en la cartera padre. + +--- + +## 4. Fix: Obligaciones y Tareas no se mostraban (error 500) + +**Root cause:** Las queries de `obligaciones.service.ts`, `tareas.service.ts` y `asignaciones.service.ts` hacían `LEFT JOIN users` en la BD de tenant, pero la tabla `users` solo existe en la BD central (`horux360`). + +**Error:** `relation "users" does not exist` + +**Fixes:** +- Quitados todos los `LEFT JOIN users` de queries de tenant +- En `obligaciones.service.ts` y `tareas.service.ts`: se mantiene solo `auxiliarAsignadoId` (sin nombre) +- En `asignaciones.service.ts`: creada función `resolveUserNames()` que consulta Prisma (BD central) para obtener nombres de usuarios y mapearlos en los resultados +- **Fix adicional:** `asignaciones.controller.ts` usaba `req.params.id` como `obligacionId` cuando en realidad `req.params.id` era `contribuyenteId`. Corregido a `req.params.obligacionId`. + +--- + +## 5. Fix: Endpoint `/carteras/asignaciones` devolvía 500 + +**Root cause:** Ruta dinámica `GET /:id` estaba definida antes de `GET /asignaciones` en `cartera.routes.ts`. Express interpretaba `"asignaciones"` como parámetro `:id`. + +**Error:** `invalid input syntax for type uuid: "asignaciones"` + +**Fix:** Reordenadas las rutas estáticas (`/asignaciones`, `/asignaciones/mias`, `/asignaciones/sin-asignar`) antes de las rutas dinámicas (`/:id`). + +--- + +## 6. Fix: Owner no veía asignaciones en "Asignadas" + +**Root cause:** `getAsignacionesPorSupervisor` filtraba por `asp.supervisor_user_id = $1`. Un owner no aparece en `auxiliar_supervisores`, por lo que no veía sus propias asignaciones. + +**Fix:** La función ahora recibe `role` como parámetro. Si es `owner/cfo/contador`, no filtra por supervisor. Si es `supervisor`, filtra por `auxiliar_supervisores`. + +--- + +## 7. Fix: Lista "Sin asignar" no se refrescaba tras asignar + +**Root cause:** Las mutations `useAsignarObligacion`, `useDesasignarObligacion`, `useAsignarTarea`, `useDesasignarTarea` invalidaban `['asignaciones-supervisor']` pero no `['asignaciones-sin-asignar']`. + +**Fix:** Agregada invalidación de `['asignaciones-sin-asignar']` a las 4 mutations. + +--- + +## 8. Feature: Seguimiento de Auxiliares — pestaña "Sin asignar" + +**Nuevo endpoint:** `GET /carteras/asignaciones/sin-asignar` +- Devuelve obligaciones y tareas activas que NO están en las tablas de asignaciones +- Respeta permisos: owner/cfo ven todo, supervisor solo sus contribuyentes + +**Frontend:** +- Reestructurado `SeguimientoAuxiliares` con tabs principales: **"Asignadas"** | **"Sin asignar"** +- Dentro de cada uno, subtabs: **Obligaciones** | **Tareas** +- El modal de asignación funciona en ambas vistas + +--- + +## 9. Feature: Nueva página `/tareas` (Tareas Operativas) + +**Nuevo endpoint:** `GET /tareas/mis-tareas` +- Devuelve todas las tareas activas con su periodo actual para los contribuyentes visibles del usuario +- Usa `materializarPeriodos` para cada contribuyente y luego `listTareasConPeriodoPorContribuyentes` para traer todo en batch + +**Frontend:** +- Nueva ruta: `/tareas` +- Agregada al sidebar principal en todos los layouts (icono `CheckSquare2`) +- Muestra tareas agrupadas por contribuyente +- Filtros: Todas / Pendientes / Completadas +- Permite marcar/desmarcar tareas como completadas +- Muestra indicadores: atrasada, supervisor-only, recurrencia, fecha límite + +--- + +## 10. Feature: Obligaciones Fiscales ya no se pueden marcar como completadas + +En `/pendientes`, se quitó el botón interactivo de check de las obligaciones fiscales. Ahora solo muestra un ícono visual del estado sin acción. + +--- + +## 11. Feature: Invitaciones Trial movido a Admin Usuarios + +**Sidebar:** Quitado "Invitaciones Trial" de `adminNavigation` en los 4 layouts (`sidebar.tsx`, `sidebar-compact.tsx`, `sidebar-floating.tsx`, `topnav.tsx`). + +**Admin Usuarios:** Agregados tabs con ``: +- **"Usuarios"** — gestión de usuarios global existente +- **"Invitaciones Trial"** — formulario de envío + historial (extraído a componente `admin/_components/invitaciones-trial-tab.tsx`) + +--- + +## Archivos modificados + +### Backend +- `apps/api/src/services/usuarios.service.ts` +- `apps/api/src/services/cartera.service.ts` +- `apps/api/src/services/obligaciones.service.ts` +- `apps/api/src/services/tareas.service.ts` +- `apps/api/src/services/asignaciones.service.ts` +- `apps/api/src/controllers/asignaciones.controller.ts` +- `apps/api/src/controllers/tareas.controller.ts` +- `apps/api/src/routes/cartera.routes.ts` +- `apps/api/src/routes/contribuyente.routes.ts` +- `apps/api/src/routes/tareas.routes.ts` + +### Frontend +- `apps/web/app/(dashboard)/carteras/page.tsx` +- `apps/web/app/(dashboard)/carteras/seguimiento-auxiliares.tsx` +- `apps/web/app/(dashboard)/pendientes/page.tsx` +- `apps/web/app/(dashboard)/admin/usuarios/page.tsx` +- `apps/web/app/(dashboard)/admin/_components/invitaciones-trial-tab.tsx` +- `apps/web/app/(dashboard)/tareas/page.tsx` +- `apps/web/components/layouts/sidebar.tsx` +- `apps/web/components/layouts/sidebar-compact.tsx` +- `apps/web/components/layouts/sidebar-floating.tsx` +- `apps/web/components/layouts/topnav.tsx` +- `apps/web/lib/api/asignaciones.ts` +- `apps/web/lib/api/tareas-mis.ts` +- `apps/web/lib/hooks/use-asignaciones.ts` +- `apps/web/lib/hooks/use-tareas-mis.ts` + +### Migraciones +- `apps/api/src/migrations/tenant/046_asignaciones_obligaciones_tareas.sql`