Update: nueva version Horux Despachos
This commit is contained in:
513
docs/plans/2026-04-25-despacho-tareas-papeleria.md
Normal file
513
docs/plans/2026-04-25-despacho-tareas-papeleria.md
Normal file
@@ -0,0 +1,513 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user