514 lines
20 KiB
Markdown
514 lines
20 KiB
Markdown
# Sesión 2026-04-25 — Módulo Despacho, Tareas y Papelería
|
|
|
|
Sesión enfocada en **features nuevos** del fork `Horux_despacho`. Los fixes
|
|
fiscales del día anterior están en
|
|
`docs/plans/2026-04-24-session-fixes-and-features.md`.
|
|
|
|
---
|
|
|
|
## Índice
|
|
|
|
1. [Banner "CSD recién tramitado"](#1-banner-csd-recién-tramitado)
|
|
2. [Preferencias de notificación por contribuyente](#2-preferencias-de-notificación-por-contribuyente)
|
|
3. [Tareas operativas (recurrentes)](#3-tareas-operativas-recurrentes)
|
|
4. [Papelería de Trabajo](#4-papelería-de-trabajo)
|
|
5. [Módulo "Despacho" — 3 páginas](#5-módulo-despacho-3-páginas)
|
|
6. [Selector de periodo global](#6-selector-de-periodo-global)
|
|
7. [Asignación de supervisor desde /usuarios](#7-asignación-de-supervisor-desde-usuarios)
|
|
8. [Ajustes de UI](#8-ajustes-de-ui)
|
|
9. [Migraciones aplicadas](#9-migraciones-aplicadas)
|
|
10. [Pendientes](#10-pendientes)
|
|
|
|
---
|
|
|
|
## 1. Banner "CSD recién tramitado"
|
|
|
|
### Problema
|
|
El SAT tarda 24-72h en propagar un CSD nuevo a la Lista de Contribuyentes
|
|
Obligados (LCO). Durante esa ventana, intentos de emisión vía Facturapi
|
|
fallan con mensajes tipo "RFC no encontrado en LCO". El user pierde
|
|
tiempo intentando emitir y abre tickets innecesarios.
|
|
|
|
### Solución
|
|
Banner contextual en `/facturacion` que aparece SOLO si hubo un rechazo
|
|
del SAT con patrón LCO en las últimas 24h.
|
|
|
|
### Archivos
|
|
- **Migración 033**: `facturapi_orgs.last_lco_rejection_at timestamptz`.
|
|
- `apps/api/src/controllers/facturacion.controller.ts`:
|
|
- Helper `isLcoRejection(message)` con regex contra patrones SAT.
|
|
- Helper `markLcoRejection(pool, contribuyenteId)` upsertea timestamp.
|
|
- En el catch de `emitir()` se dispara la marca cuando aplica.
|
|
- Endpoint `getLcoStatus` retorna `{ hasRecentLcoRejection, rejectedAt }`.
|
|
- `apps/api/src/routes/facturacion.routes.ts`: `GET /facturacion/lco-status`.
|
|
- `apps/web/app/(dashboard)/facturacion/page.tsx`: banner amber al inicio
|
|
del form, polling cada 15min.
|
|
|
|
### Mensaje
|
|
> CSD aún en proceso de validación — espera 24 horas antes de emitir tu factura.
|
|
|
|
### Auto-expiración
|
|
La condición es `rejectedAt > NOW() - 24h`. Pasadas las 24h el banner
|
|
desaparece sin necesidad de cron.
|
|
|
|
---
|
|
|
|
## 2. Preferencias de notificación por contribuyente
|
|
|
|
### Problema
|
|
Los emails informativos se enviaban siempre. El user quería poder
|
|
desactivar tipos específicos por contribuyente sin afectar a otros.
|
|
|
|
### Solución
|
|
JSONB `email_preferences` en `contribuyentes`; los handlers de envío
|
|
respetan la preferencia antes de mandar.
|
|
|
|
### Archivos
|
|
- **Migración 034**: `contribuyentes.email_preferences jsonb DEFAULT '{}'`.
|
|
- `apps/api/src/services/notification-preferences.service.ts`:
|
|
- `EMAIL_TYPES = ['documento_subido', 'weekly_update', 'subscription_expiring', 'recordatorio_fiscal']`.
|
|
- `getContribuyenteEmailPreferences()` con default true.
|
|
- `setContribuyenteEmailPreferences()` merge sobre JSONB.
|
|
- `apps/api/src/services/notify-upload.service.ts`: check
|
|
`prefs.documento_subido` antes de enviar.
|
|
- `apps/api/src/controllers/notification-preferences.controller.ts`:
|
|
`listPreferences` + `updatePreferences`.
|
|
- `apps/api/src/routes/notification-preferences.routes.ts`:
|
|
`GET / PUT /api/notificaciones`.
|
|
- `apps/web/app/(dashboard)/configuracion/notificaciones/page.tsx`:
|
|
toggles por tipo de email por contribuyente, optimistic update.
|
|
|
|
### Tipos de email
|
|
| Tipo | Estado | Notas |
|
|
|---|---|---|
|
|
| `documento_subido` | ✅ activo | check en `notify-upload.service.ts` |
|
|
| `weekly_update` | ⏳ próximamente | el job es tenant-wide, requiere refactor |
|
|
| `subscription_expiring` | ⏳ próximamente | no es per-contribuyente hoy |
|
|
| `recordatorio_fiscal` | ⏳ placeholder | para futuras alertas fiscales |
|
|
|
|
Los toggles "Próximamente" se persisten pero no bloquean el envío
|
|
todavía. Cuando se implemente cada email per-contribuyente, basta con
|
|
agregar el check.
|
|
|
|
### Default
|
|
Todo activado. Si la columna está vacía o le falta una key, asumimos
|
|
`true` para preservar comportamiento previo.
|
|
|
|
---
|
|
|
|
## 3. Tareas operativas (recurrentes)
|
|
|
|
Sistema completo de tareas operativas del despacho por contribuyente,
|
|
distinto del flujo de obligaciones fiscales (que ya existía).
|
|
|
|
### Características
|
|
- **Recurrencias**: semanal, quincenal, mensual, bimestral, trimestral,
|
|
semestral, anual.
|
|
- **Día de vencimiento**: día de la semana (1-7) o día del mes (1-31)
|
|
según recurrencia.
|
|
- **Estados**: pendiente / completada (en BD); "atrasada" se calcula en
|
|
frontend.
|
|
- **Permisos**:
|
|
- Rol `cliente`: bloqueado en TODOS los endpoints (403).
|
|
- Flag `solo_supervisor_completa`: si está en true, solo
|
|
owner/cfo/supervisor pueden marcarla completa.
|
|
|
|
### Archivos
|
|
- **Migración 035**:
|
|
```sql
|
|
tareas_catalogo (id, contribuyente_id, nombre, descripcion, recurrencia,
|
|
dia_semana, dia_mes, solo_supervisor_completa, es_default,
|
|
active, orden, created_at)
|
|
tarea_periodos (id, tarea_id, periodo, fecha_limite, completada,
|
|
completada_at, completada_por, notas, created_at)
|
|
```
|
|
- `apps/api/src/services/tareas.service.ts`:
|
|
- CRUD del catálogo.
|
|
- **Materialización lazy**: cuando el frontend pide tareas con periodo
|
|
actual, el backend genera periodos faltantes solo del presente en
|
|
adelante (códigos `2025-W12`, `2025-01`, `2025-B1`, `2025-Q1`,
|
|
`2025-S1`, `2025`).
|
|
- `seedTareasDefault()` con 4 tareas: estados de cuenta (día 5),
|
|
conciliación (día 10), contabilización (día 14), revisión fiscal
|
|
preliminar (día 15, `solo_supervisor_completa=true`).
|
|
- `getEventosTareasParaCalendario()` retorna shape `EventoFiscal` con
|
|
`tipo: 'tarea'`.
|
|
- `contarTareasProximasVencer()` para alertas auto.
|
|
- `getAuxiliarUserIdDeContribuyente()` resuelve cartera → auxiliar.
|
|
- `apps/api/src/controllers/tareas.controller.ts`:
|
|
- Bloqueo rol cliente.
|
|
- `notifyAuxiliarTareaCompletada()`: dispara email cuando una tarea
|
|
con `solo_supervisor_completa=true` se marca completa por
|
|
supervisor/owner — el auxiliar recibe aviso.
|
|
- `apps/api/src/routes/tareas.routes.ts`:
|
|
```
|
|
GET /api/tareas — list con periodos materializados
|
|
POST /api/tareas — crear
|
|
PATCH /api/tareas/:id — update
|
|
DELETE /api/tareas/:id — soft delete (active=false)
|
|
POST /api/tareas/seed — seed defaults
|
|
POST /api/tareas/periodo/:id/completar
|
|
DELETE /api/tareas/periodo/:id/completar
|
|
```
|
|
- `apps/api/src/services/email/templates/tarea-completada.ts`: template
|
|
para notificar al auxiliar.
|
|
- `apps/api/src/services/alertas-auto.service.ts`:
|
|
`alertaTareasProximasVencer()` (≤3 días, prioridad media), enchufada
|
|
en `generarAlertasAutomaticas`.
|
|
- `apps/api/src/controllers/calendario.controller.ts`: incluye tareas en
|
|
el GET de eventos cuando hay contribuyente seleccionado y rol no es
|
|
cliente.
|
|
- `packages/shared/src/types/calendario.ts`: agregado `'tarea'` a
|
|
`TipoEvento`.
|
|
|
|
### Frontend
|
|
- `apps/web/components/obligaciones/tareas-tab.tsx`: pestaña "Tareas" en
|
|
`/configuracion/obligaciones` con tabla, modal crear/editar, check
|
|
pendiente/completada.
|
|
- `apps/web/app/(dashboard)/configuracion/obligaciones/page.tsx`: tabs
|
|
"Obligaciones" / "Tareas".
|
|
|
|
### Flujo email "tarea revisada"
|
|
1. Auxiliar trabaja en algo del contribuyente.
|
|
2. Supervisor/owner marca como completada la tarea
|
|
`solo_supervisor_completa=true` (ej. "Revisión fiscal preliminar").
|
|
3. Backend resuelve `auxiliar_user_id` desde la cartera del
|
|
contribuyente.
|
|
4. Email al auxiliar con el detalle (quién marcó, periodo, notas).
|
|
|
|
---
|
|
|
|
## 4. Papelería de Trabajo
|
|
|
|
Sección nueva en `/documentos` para subir archivos de trabajo del
|
|
despacho con flujo opcional de aprobación.
|
|
|
|
### Características
|
|
- **No accesible para usuarios rol cliente** (oculta + 403 en API).
|
|
- **Formatos permitidos**: PDF, Word (doc/docx), Excel (xls/xlsx).
|
|
- **Tamaño máximo**: 5 MB por archivo (validado backend + frontend).
|
|
- **Periodo**: mes + año (selector en el modal de subida).
|
|
- **Aprobación opcional**: checkbox al subir; si está activo, queda en
|
|
estado `pendiente` y solo owner/supervisor pueden aprobar/rechazar.
|
|
- **Comentario opcional al rechazar**.
|
|
|
|
### Archivos
|
|
- **Migración 036**:
|
|
```sql
|
|
papeleria_trabajo (id, contribuyente_id, nombre, descripcion,
|
|
archivo bytea, archivo_filename, archivo_mime,
|
|
archivo_size, anio, mes,
|
|
requiere_aprobacion, estado, aprobado_por,
|
|
aprobado_at, comentario_rechazo, subido_por,
|
|
created_at)
|
|
```
|
|
Estados válidos: `pendiente | aprobado | rechazado`. NULL si no
|
|
requiere aprobación.
|
|
- `apps/api/src/services/papeleria.service.ts`:
|
|
- Validación MIME + tamaño.
|
|
- CRUD: `uploadPapeleria`, `listPapeleria` (filtros año/mes/estado),
|
|
`downloadArchivo`, `aprobar`, `rechazar`, `eliminar`.
|
|
- `aprobar`/`rechazar` validan rol owner/cfo/supervisor.
|
|
- `apps/api/src/controllers/papeleria.controller.ts`:
|
|
- Bloqueo rol cliente con `rejectClienteRole()`.
|
|
- Schema Zod estricto.
|
|
- **Notificaciones**:
|
|
- Al subir con `requiere_aprobacion=true`: email a owners + supervisores.
|
|
- Al aprobar/rechazar: email al uploader (excepto si es el mismo).
|
|
- Excepción al uploader: si el aprobador es el mismo user que subió
|
|
(ej. owner que sube su propia papelería), no se auto-notifica.
|
|
- `apps/api/src/services/email/templates/papeleria.ts`: 2 templates
|
|
(`papeleriaAprobacionRequeridaEmail` + `papeleriaDecisionEmail`).
|
|
- `apps/api/src/routes/papeleria.routes.ts`:
|
|
```
|
|
GET /api/papeleria — list con filtros
|
|
POST /api/papeleria — upload (base64 en JSON body)
|
|
GET /api/papeleria/:id/download — descarga binaria
|
|
POST /api/papeleria/:id/aprobar
|
|
POST /api/papeleria/:id/rechazar — body: { comentario? }
|
|
DELETE /api/papeleria/:id
|
|
```
|
|
|
|
### Frontend
|
|
- `apps/web/components/documentos/papeleria-tab.tsx`: pestaña con
|
|
filtros (año, mes, estado), modal de upload con base64 conversion,
|
|
badges de estado, modal de rechazo con comentario.
|
|
- `apps/web/app/(dashboard)/documentos/page.tsx`: pestaña condicional
|
|
`if (user.role !== 'cliente')`.
|
|
|
|
---
|
|
|
|
## 5. Módulo "Despacho" — 3 páginas
|
|
|
|
Reemplazo del módulo `/pendientes` con sub-secciones específicas por
|
|
rol. **Sidebar** renombrado: "Pendientes" → "Despacho".
|
|
|
|
### Estructura
|
|
|
|
```
|
|
/despachos → redirect según rol
|
|
/despachos/contribuyentes → owner/cfo (métricas globales)
|
|
/despachos/mis-asignados → owner/cfo/supervisor/auxiliar
|
|
/despachos/equipo → owner/cfo/supervisor (jerárquico)
|
|
```
|
|
|
|
### Sub-nav común
|
|
- `apps/web/components/despachos/despacho-subnav.tsx`: tabs visibles
|
|
según rol del user (filtrado en frontend).
|
|
- `defaultDespachoPathForRole(role)`: helper para el redirect en
|
|
`/despachos`.
|
|
|
|
### 5a. `/despachos/contribuyentes` (owner-only)
|
|
|
|
7 cards de métricas globales del despacho, todas filtrables por periodo:
|
|
|
|
| Card | Fuente | Notas |
|
|
|---|---|---|
|
|
| Total contribuyentes | `entidades_gestionadas` activos | independiente del periodo |
|
|
| Última extracción SAT | `MAX(sat_sync_jobs.completed_at)` BD central | independiente del periodo |
|
|
| Progreso del mes (%) | `obligaciones+tareas completadas / total` | barra de color |
|
|
| Declaraciones presentadas | `declaraciones_provisionales` con `created_at` en periodo | |
|
|
| Declaraciones pagadas | subset con `pdf_pago IS NOT NULL` | |
|
|
| Declaraciones atrasadas | obligaciones de declaración pendientes con `periodo < seleccionado` | heurística por categoría |
|
|
| Tareas atrasadas | tareas pendientes con `fecha_limite < primer día` | |
|
|
|
|
**Heurística "es declaración"** (sin flag explícito en el catálogo):
|
|
```sql
|
|
LOWER(categoria) ~ 'mensual|anual|declarac' OR LOWER(nombre) LIKE '%declarac%'
|
|
```
|
|
Si crece la base se puede agregar columna `es_declaracion boolean` y
|
|
ajustar.
|
|
|
|
### 5b. `/despachos/mis-asignados`
|
|
|
|
Tabla con contribuyentes filtrada por cartera del user:
|
|
|
|
| Rol | Filtro |
|
|
|---|---|
|
|
| owner / cfo | TODOS los contribuyentes |
|
|
| supervisor | contribuyentes en sus carteras top-level + subcarteras |
|
|
| auxiliar | contribuyentes en carteras donde es `auxiliar_user_id` |
|
|
|
|
**Columnas**:
|
|
- Contribuyente (link → `/configuracion/obligaciones` con contribuyente
|
|
preseleccionado).
|
|
- Cartera.
|
|
- **Avance** (barra de color verde≥80% / ámbar 50-79% / rojo <50% +
|
|
porcentaje numérico).
|
|
- **Atrasos** — badge agregado de obligaciones+tareas atrasadas; "Al
|
|
día" si no tiene.
|
|
- Obl. periodo (completadas / pendientes).
|
|
- Tareas periodo (completadas / pendientes).
|
|
|
|
**Resaltado de filas** con atrasos: `bg-red-50/50` para llamar la
|
|
atención.
|
|
|
|
**Orden**: por suma de atrasos descendente (rezagados arriba).
|
|
|
|
### 5c. `/despachos/equipo`
|
|
|
|
Vista jerárquica supervisor → auxiliares. Click en un supervisor
|
|
**expande** debajo a sus auxiliares.
|
|
|
|
**Resolución de la relación supervisor → auxiliar** (UNION de 3
|
|
fuentes):
|
|
```sql
|
|
1. carteras top-level con auxiliar_user_id + supervisor_user_id directos
|
|
2. subcarteras con auxiliar_user_id; supervisor del cartera padre
|
|
3. tabla legacy auxiliar_supervisores (fallback / override desde /usuarios)
|
|
```
|
|
|
|
**Sección "Auxiliares sin supervisor asignado"**: si después del UNION
|
|
queda un auxiliar activo (rol `auxiliar` en `tenant_memberships`) sin
|
|
supervisor, aparece en una sección al final con icono de advertencia
|
|
ámbar — solo el owner la ve.
|
|
|
|
**Permisos**:
|
|
- owner/cfo: ve TODOS los supervisores con sus auxiliares + huérfanos.
|
|
- supervisor: ve solo a sí mismo con sus auxiliares (sin huérfanos).
|
|
|
|
**Métricas por miembro**:
|
|
- Contribuyentes asignados.
|
|
- Avance del periodo (barra de color, igual que mis-asignados).
|
|
- Atrasos (obligaciones + tareas).
|
|
|
|
### Backend
|
|
- `apps/api/src/services/despacho-stats.service.ts`:
|
|
- `getContribuyentesStats(pool, tenantId, año?, mes?)`.
|
|
- `getMisAsignados(pool, userId, userRole, año?, mes?)`.
|
|
- `getEquipoStats(pool, userId, userRole, tenantId, año?, mes?)`.
|
|
- `apps/api/src/controllers/despacho-stats.controller.ts`: 3 endpoints
|
|
con guards de rol.
|
|
- `apps/api/src/routes/despacho-stats.routes.ts`: mountea en
|
|
`/api/despachos`.
|
|
|
|
---
|
|
|
|
## 6. Selector de periodo global
|
|
|
|
### Problema
|
|
Antes solo el mes en curso era visible. El user quiere navegar a otros
|
|
periodos para revisar avance histórico.
|
|
|
|
### Solución
|
|
Wrapper `<PeriodoSelector />` alrededor del `<PeriodSelector />` de
|
|
`@horux/shared-ui` (mismo patrón visual que `/dashboard`, `/impuestos`,
|
|
`/reportes`). Persiste selección en `localStorage` via Zustand.
|
|
|
|
### Archivos
|
|
- `apps/web/stores/periodo-store.ts`:
|
|
- `fechaInicio` / `fechaFin` (strings ISO YYYY-MM-DD).
|
|
- Default: primer y último día del mes en curso.
|
|
- Helper `añoMesFromFechaInicio()` para queries que usan año/mes.
|
|
- `apps/web/components/periodo-selector.tsx`: wrapper que se pasa como
|
|
children al `<Header>` en cada página `/despachos/*`.
|
|
|
|
### Endpoints que aceptan el filtro
|
|
- `GET /api/despachos/contribuyentes-stats?año=YYYY&mes=MM`
|
|
- `GET /api/despachos/mis-asignados?año=YYYY&mes=MM`
|
|
- `GET /api/despachos/equipo-stats?año=YYYY&mes=MM`
|
|
|
|
Default: mes en curso si no vienen los params.
|
|
|
|
---
|
|
|
|
## 7. Asignación de supervisor desde /usuarios
|
|
|
|
### Problema
|
|
Al invitar un auxiliar se pedía supervisor, pero **no había forma de
|
|
cambiarlo después**. Al abrir el modal aparecía "none" porque el
|
|
endpoint solo leía de `auxiliar_supervisores` ignorando carteras.
|
|
|
|
### Solución
|
|
Endpoint que consulta TODAS las fuentes de la relación supervisor↔auxiliar
|
|
y un modal "Supervisor" en `/usuarios`.
|
|
|
|
### Archivos backend
|
|
- `apps/api/src/controllers/usuarios.controller.ts`:
|
|
- `getSupervisor`: UNION priorizada — `auxiliar_supervisores` (1) →
|
|
cartera directa (2) → cartera padre de subcartera (3). El primer
|
|
resultado gana.
|
|
- `updateSupervisor`: upsert en `auxiliar_supervisores` (override
|
|
explícito sobre la cartera). Pasar `supervisorUserId: null` borra
|
|
la asignación.
|
|
- `apps/api/src/routes/usuarios.routes.ts`:
|
|
```
|
|
GET /api/usuarios/:id/supervisor
|
|
PUT /api/usuarios/:id/supervisor
|
|
```
|
|
|
|
### Frontend
|
|
- Botón "Supervisor" (icono `UserCheck`) en cada fila de auxiliar,
|
|
visible solo en tenant tipo despacho y para admins.
|
|
- Modal con `<Select>` de supervisores + opción "Sin supervisor
|
|
asignado".
|
|
|
|
### Trade-off
|
|
La tabla `auxiliar_supervisores` actúa como **override** sobre la
|
|
cartera. Si después de asignar via cartera, el owner cambia el
|
|
supervisor desde `/usuarios`, queda guardado en `auxiliar_supervisores`
|
|
y eso prevalece. Si quieres revertir, borrar la fila desde
|
|
`/usuarios` (opción "Sin supervisor asignado").
|
|
|
|
---
|
|
|
|
## 8. Ajustes de UI
|
|
|
|
### 8a. Sidebar sin selector de contribuyente
|
|
El `<ContribuyenteSelector />` ya estaba en el header — duplicado en
|
|
sidebar agregaba ruido. Removido del sidebar (`apps/web/components/layouts/sidebar.tsx`).
|
|
|
|
### 8b. Selector contribuyente oculto en `/despachos/*`
|
|
Las 3 vistas de Despacho son cross-contribuyente (métricas agregadas,
|
|
mis asignados es la lista completa, equipo es jerarquía). Filtrar por
|
|
contribuyente individual rompe el propósito.
|
|
|
|
`apps/web/components/contribuyente-selector.tsx`: array `HIDDEN_PATHS`
|
|
con `/despachos`. Si el pathname matchea, retorna `null` antes de
|
|
renderizar.
|
|
|
|
### 8c. Header
|
|
Selector de periodo se pasa como **children del `<Header>`** (al lado
|
|
del título), igual que en `/dashboard` — no en el lado derecho.
|
|
|
|
### 8d. PageNotificaciones centrada
|
|
`<main className="... mx-auto">` agregado.
|
|
|
|
### 8e. Configuración Obligaciones Fiscales — Header
|
|
Faltaba el `<Header />` en la página `/configuracion/obligaciones` así
|
|
que perdía el selector global. Agregado.
|
|
|
|
---
|
|
|
|
## 9. Migraciones aplicadas
|
|
|
|
| # | Archivo | Tabla / cambio |
|
|
|---|---|---|
|
|
| 033 | `033_facturapi_orgs_lco_rejection.sql` | `facturapi_orgs.last_lco_rejection_at` |
|
|
| 034 | `034_contribuyentes_email_preferences.sql` | `contribuyentes.email_preferences jsonb` |
|
|
| 035 | `035_tareas.sql` | `tareas_catalogo` + `tarea_periodos` |
|
|
| 036 | `036_papeleria_trabajo.sql` | `papeleria_trabajo` |
|
|
|
|
Aplicadas vía `pnpm db:migrate-tenants` en ambos tenants
|
|
(`DESPACHO_MO7JE8BZ_VDOPR` y `DESPACHO_MO3NI6U8_B9VGG`).
|
|
|
|
---
|
|
|
|
## 10. Pendientes
|
|
|
|
### Tareas
|
|
- Soporte de tipo "one-time" (no recurrente). Hoy todas son recurrentes.
|
|
- Asignación explícita de tarea a un user específico (hoy se asume
|
|
cartera).
|
|
|
|
### Notificaciones
|
|
- Implementar el envío per-contribuyente para `weekly_update`,
|
|
`subscription_expiring`, `recordatorio_fiscal` (los 3 toggles
|
|
"Próximamente"). Requiere refactor del cron de weekly-update para que
|
|
itere por contribuyente en lugar de por tenant.
|
|
|
|
### Métricas Despacho
|
|
- "Es declaración" en obligaciones: agregar flag explícito al catálogo
|
|
(`obligaciones_contribuyente.es_declaracion boolean`) y migrar la
|
|
heurística actual de regex.
|
|
- Drill-down: click en cada card del módulo Contribuyentes lleva a una
|
|
vista detallada (ej. listado de declaraciones atrasadas con
|
|
contribuyente y periodo).
|
|
|
|
### Equipo
|
|
- Mostrar % avance histórico (gráfica) de cada miembro a lo largo de
|
|
varios periodos.
|
|
- Asignar manualmente contribuyentes a un user desde la vista de equipo
|
|
(hoy solo via carteras).
|
|
|
|
### Papelería
|
|
- Versionado: si el user sube un nuevo archivo del mismo "concepto",
|
|
guardar histórico de versiones (hoy crea fila independiente cada vez).
|
|
- Filtros por uploader.
|
|
|
|
### Bugs reportados (por revisar)
|
|
- **Visibilidad de auxiliares en carteras de supervisores**: en la
|
|
pantalla de carteras, el supervisor está viendo a auxiliares que NO
|
|
están asignados a él. Debe filtrar para que cada supervisor solo vea
|
|
los auxiliares en sus propias carteras (top-level + subcarteras
|
|
donde es supervisor).
|
|
- **Tareas completadas > pendientes en módulo Despacho**: las métricas
|
|
muestran más tareas completadas de las que existen pendientes —
|
|
aritméticamente raro. Posible causa: el conteo de "completadas"
|
|
incluye periodos históricos mientras que "pendientes" se limita al
|
|
periodo seleccionado. Validar que ambos lados del ratio usen el
|
|
mismo universo (mes filtrado) o documentar explícitamente la
|
|
asimetría si es intencional.
|
|
- **Drill-down de alerta TipoRelacion sospechoso incompleto**: no
|
|
están saliendo todos los CFDIs tipo E con posible TipoRelacion
|
|
errónea en el listado. Revisar la heurística en
|
|
`apps/api/src/services/alertas-auto.service.ts` (constante
|
|
`SOSPECHOSA_TIPO_RELACION_WHERE` y reuso en
|
|
`getCfdisTipoRelacionSospechosa` del controller). Posibles causas:
|
|
(a) operador `&&` de PostgreSQL sobre `string_to_array` no detecta
|
|
algunos casos por case-sensitivity o caracteres extraños en
|
|
`cfdis_relacionados`; (b) el filtro excluye E con
|
|
`cfdi_tipo_relacion=NULL` cuando algunas inconsistencias podrían
|
|
tener ese estado; (c) RFC del contribuyente no se aplica
|
|
uniformemente en el JOIN.
|