# Plan — Conciliación Contable (asientos pendientes / regeneración)

## Descripción funcional

Cuando una empresa contrata el módulo **Contabilidad** después de haber operado sin él, o cuando por errores de configuración (mapeos faltantes, período cerrado, cuenta OLD-, etc.) algunos movimientos no generaron asiento en su momento, el sistema debe permitir:

1. **Detectar** todos los movimientos operativos (facturas, cobros, gastos, OPs, tesorería, viáticos, liquidaciones, etc.) que **deberían tener asiento** y **no lo tienen** o quedaron en estado no CONFIRMADO.
2. **Diagnosticar** por qué cada uno no está contabilizado (falta mapeo, período cerrado, cuenta OLD-, RUC inválido, etc.).
3. **Regenerar** los asientos faltantes en lote, con feedback claro (éxitos / fallos por motivo).
4. **Auditar** cada intento de regeneración para trazabilidad.

### Flujo canónico

```
1) DETECCIÓN                     2) DIAGNÓSTICO                  3) REGENERACIÓN
   Escanear cada origen           Por cada registro pendiente:    Ejecutar en lote:
   contabilizable declarado         - ¿Por qué falta el asiento?    - Reusar los services
   en el registry:                  - Falta mapeo?                    de integración
   - Sin cont_documento_id          - Período cerrado?                existentes
   - O documento en BORRADOR        - Cuenta OLD-?                  - Cada asiento en su
   Agrupar por tipo + moneda        - Datos faltantes?                 propia transacción
                                                                    - Reporte final
                                                                       con éxitos/fallos
```

### Objetivos

- Cubrir a las empresas que **empiezan sin Contabilidad** y luego la contratan (histórico sin asientos).
- Cubrir a las que **ya operan con Contabilidad** pero tienen movimientos huérfanos por bugs de config (mapeo incompleto, cuenta OLD-, período cerrado retroactivamente).
- **No duplicar lógica contable**: reusar los integradores existentes (`ContabilidadIntegracionService`, `RendicionViaticosContabilidadService`, etc.).
- **Idempotente**: cada integrador ya tiene guard "si ya tiene documento, no hago nada". La regeneración se puede correr múltiples veces sin efectos duplicados.
- **Trazabilidad**: cada intento queda en `AuditService` con motivo del fallo si aplica.
- **UI visible para el contador**: pantalla dedicada en Contabilidad, no requiere acceso a base de datos.

### No objetivos (fuera de alcance)

- **Editar asientos existentes** — solo generar los faltantes. Corrección de asientos existentes se hace desde la pantalla estándar de asientos.
- **Ajustes contables retroactivos** (regularizaciones, provisiones) — es responsabilidad del contador via asientos manuales.
- **Cambiar mapeos automáticamente** — el sistema detecta y avisa, pero la corrección la hace el usuario en la pantalla de Mapeo de Cuentas.
- **Bulk import de asientos históricos desde XLSX** — para empresas que traen contabilidad de otro sistema, se maneja por otro flujo (import genérico de asientos).
- **Rearmar registros originales**: si falta un cobro/factura completo, no se crea desde este módulo. Solo se generan los asientos de los registros ya existentes en las tablas operativas.

---

## Decisiones de diseño (base del plan)

| # | Tema | Decisión |
|---|------|----------|
| 1 | Registro de orígenes | Tabla `cont_origen_registry` que declara cada fuente contabilizable con su tabla, filtro, service y método. Se seedea al ejecutar el módulo, no requiere migración por cada origen nuevo. |
| 2 | Alcance por defecto | Todos los orígenes del registry en el rango de fechas seleccionado. El contador puede filtrar por tipo. |
| 3 | Detección de "pendiente" | Registro origen que NO tenga un `cont_documentos` en estado `CONFIRMADO` con `origen_tipo`+`origen_id` matching, y que no esté anulado ni en borrador editable. |
| 4 | Diagnóstico | Se calcula por registro al pedir el detalle (no se persiste). Motivos posibles: `SIN_MAPEO`, `PERIODO_CERRADO`, `CUENTA_OLD`, `MODULO_INACTIVO`, `DATOS_FALTANTES`, `SIN_MOTIVO`. |
| 5 | Regeneración | En lote, cada asiento en su propia transacción vía `Promise.allSettled`. Un fallo en uno no aborta el resto. |
| 6 | Fecha del asiento | Se respeta la fecha original del registro (no la fecha de regeneración). Si el período está cerrado, se salta y se reporta el motivo. |
| 7 | Reintento automático | No. La regeneración es una acción del contador. Los jobs cron existentes (que integran en tiempo real) siguen intactos. |
| 8 | Módulo requerido | Solo se muestra si `CONTABILIDAD` está en la suscripción de la empresa. |
| 9 | Permisos | Nuevos privilegios `CONT_CONC_PENDIENTES_VER` y `CONT_CONC_REGENERAR` bajo `CONTABILIDAD_CONCILIACION`. |
| 10 | Volumen | Paginación estricta en detección (máx 500 por tipo por request). El resumen (contadores) puede consultar todo. |
| 11 | Auditoría | Cada regeneración exitosa/fallida se registra en `AuditService` con motivo y contexto. |
| 12 | Compatibilidad con Fase 6 viáticos | El mismo patrón "no aborta si falta mapeo, audita el error" ya está aplicado en viáticos. Se replica y se explota desde acá. |

---

## Modelo de datos

### Tabla nueva

**`cont_origen_registry`** — catálogo de orígenes contabilizables. Se llena en el seed del módulo.

```prisma
model cont_origen_registry {
  id             String   @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  origen_tipo    String   @unique @db.VarChar(60) // FACTURA_VENTA, GASTO, COBRO, ORDEN_PAGO, ...
  descripcion    String   @db.VarChar(120)
  modulo         String   @db.VarChar(30)  // VENTAS, COMPRAS, TESORERIA, RRHH, ...
  submodulo      String?  @db.VarChar(60)  // opcional
  // Metadata para el detector genérico
  tabla_origen   String   @db.VarChar(60)  // nombre de tabla Prisma
  filtro_activo  String?  @db.VarChar(200) // JSON con filtros extra (ej. estado != anulada)
  // Identificación del integrador
  service_name   String   @db.VarChar(80)  // "ContabilidadIntegracionService"
  service_method String   @db.VarChar(80)  // "integrarFactura"
  // Flag para poder desactivar temporalmente un origen sin borrar
  activo         Boolean  @default(true)
  created_at     DateTime @default(now())
  updated_at     DateTime @default(now())
}
```

### Sin tabla de "pendientes"

Se calcula **en vivo** contra las tablas originales — no hay tabla de estado que mantener. Ventajas:
- Cero desincronización.
- Cambios en originales se reflejan al instante.
- No hay job de mantenimiento.

Los servicios de integración existentes ya guardan `cont_documento_id` en el registro origen al confirmar el asiento; esa es la fuente de verdad de "tiene asiento" vs "no tiene".

---

## Endpoints (backend)

Base: `/contabilidad/conciliacion` (versión v1), bajo módulo `CONTABILIDAD`, submódulo `CONTABILIDAD_CONCILIACION`.

| Método | Path | Privilegio | Descripción |
|--------|------|-----------|-------------|
| GET | `/resumen` | `CONT_CONC_PENDIENTES_VER` | Contador de pendientes por tipo + monto en rango. |
| GET | `/detalle` | `CONT_CONC_PENDIENTES_VER` | Listado paginado de registros pendientes con diagnóstico. Filtros: `origen_tipo`, `desde`, `hasta`, `motivo`, `search`. |
| POST | `/regenerar` | `CONT_CONC_REGENERAR` | Regenera asientos en lote. Body: `{ origen_tipo?, ids?[], desde?, hasta? }`. Respuesta: `{ ok: N, fallidos: [{id, motivo}], por_tipo: {...} }`. |
| POST | `/regenerar/:origenTipo/:id` | `CONT_CONC_REGENERAR` | Regenera un único registro (útil desde la fila del detalle). |
| GET | `/motivos` | `CONT_CONC_PENDIENTES_VER` | Devuelve el catálogo de motivos posibles con explicación. |

---

## Arquitectura del servicio

```
ConciliacionService (nuevo)
├── detectarPendientes(empresaId, filtros)   → { resumen, items }
├── diagnosticarRegistro(origenTipo, id)      → { motivo, contexto }
├── regenerar(empresaId, opts)                → { ok, fallidos, por_tipo }
└── regenerarUno(empresaId, origenTipo, id)   → { ok | { motivo } }

Registry (memoria, cargado desde cont_origen_registry al arrancar)
├── FACTURA_VENTA   → { service: ContabilidadIntegracionService, method: integrarFactura, table: 'factura_cab' }
├── COBRO           → { service: ContabilidadIntegracionService, method: integrarCobro,   table: 'cobros_cab' }
├── GASTO           → { service: ContabilidadIntegracionService, method: integrarGasto,   table: 'gasto_cab' }
├── ORDEN_PAGO      → { service: PagosProveedorService, method: contabilizarOrdenPago,    table: 'orden_pago_proveedor_cab' }
├── TES_MOVIMIENTO  → { service: TesoreriaContabilidadService, method: contabilizarMovimiento, table: 'tes_movimientos' }
├── RENDICION_ADELANTO   → { service: RendicionViaticosContabilidadService, method: registrarAdelanto,   table: 'rendicion_adelanto' }
├── RENDICION_DEVOLUCION → { service: RendicionViaticosContabilidadService, method: registrarDevolucion, table: 'rendicion_devolucion' }
├── CXP_EMPLEADO_PAGO    → { service: RendicionViaticosContabilidadService, method: registrarPagoCxpEmpleado, table: 'tes_movimientos' (filtrado por origen_tipo) }
├── LIQUIDACION     → { service: LiquidacionesContabilidadService, method: integrarLiquidacion, table: 'rrhh_liquidaciones' }
└── COMISION_LIQ    → { service: ComisionesContabilidadService, method: integrarLiquidacion, table: 'com_liquidacion' }
```

**Registry como fuente única de verdad**: agregar un módulo nuevo = insertar una fila en `cont_origen_registry`. La UI, el detector y el regenerador consumen el mismo registry.

### Detección genérica

```typescript
async detectarPorTipo(empresaId, origenTipo, desde, hasta) {
  const entry = this.registry.get(origenTipo);
  if (!entry) return { items: [], total: 0 };
  const model = this.prisma[entry.tabla_origen];
  const registros = await model.findMany({
    where: {
      empresa_id: empresaId,
      ...JSON.parse(entry.filtro_activo || '{}'),
      created_at: { gte: desde, lte: hasta },
      cont_documento_id: null,
    },
    take: 500,
  });
  return { items: registros, total: registros.length };
}
```

Los servicios de integración de cada módulo ya son idempotentes (verifican `cont_documento_id` antes de crear); por lo tanto `regenerar` simplemente llama al método declarado en el registry.

### Diagnóstico

```typescript
async diagnosticarRegistro(empresaId, origenTipo, id) {
  // 1. Verificar módulo activo
  if (!await this.tieneModuloContabilidad(empresaId)) return { motivo: 'MODULO_INACTIVO' };
  // 2. Verificar mapeos requeridos por el origen (declarados en registry.conceptos_requeridos)
  const conceptos = entry.conceptos_requeridos ?? [];
  for (const c of conceptos) {
    try { await this.mapeo.getCuentaPorConcepto(empresaId, c); }
    catch { return { motivo: 'SIN_MAPEO', contexto: { concepto_faltante: c } }; }
  }
  // 3. Verificar período abierto
  try { await this.periodos.resolverPeriodoPorFecha(empresaId, fecha); }
  catch { return { motivo: 'PERIODO_CERRADO', contexto: { fecha } }; }
  // 4. Verificar que las cuentas involucradas no sean OLD- o inactivas
  // ...
  return { motivo: 'SIN_MOTIVO' }; // debería contabilizarse ok — probable bug histórico
}
```

---

## UI (frontend)

### Ruta

`/contabilidad/conciliacion` — visible en el submenú de Contabilidad si `CONTABILIDAD_CONCILIACION` está en la suscripción **y** el usuario tiene `CONT_CONC_PENDIENTES_VER`.

### Estructura de la pantalla

```
┌──────────────────────────────────────────────────────────────────┐
│  Conciliación Contable                    [Regenerar todo] [PDF] │
├──────────────────────────────────────────────────────────────────┤
│  Filtros: [Desde] [Hasta] [Tipos ▼] [Motivo ▼] [Buscar…]          │
│  Presets: [Este año] [Ejercicio actual] [Últimos 90d]             │
├──────────────────────────────────────────────────────────────────┤
│  KPIs                                                              │
│  ┌─────────────┬─────────────┬─────────────┬─────────────┐        │
│  │ Total       │ Sin mapeo   │ Período cer.│ OLD/inactiva│        │
│  │ pendientes  │             │             │             │        │
│  │  1.247      │ 892 (71%)   │ 205 (16%)   │ 150 (12%)   │        │
│  └─────────────┴─────────────┴─────────────┴─────────────┘        │
├──────────────────────────────────────────────────────────────────┤
│  Tabla agrupada por Tipo (accordion expandible)                    │
│  ▼ Facturas de Venta (523 pendientes · Gs. 1.2B)   [Regenerar]    │
│    ├─ FC-001-001-0001234  10/03/2026  Cliente XX  Gs. 500K   [!]  │
│    │   Motivo: SIN_MAPEO — Falta VENTAS_10                        │
│    ├─ FC-001-001-0001235  11/03/2026  Cliente YY  Gs. 1.2M       │
│    │   Motivo: PERIODO_CERRADO — Período marzo 2026 cerrado      │
│    └─ ...                                                          │
│  ▶ Cobros (312 pendientes · Gs. 850M)             [Regenerar]     │
│  ▶ Gastos (211 pendientes · Gs. 45M)              [Regenerar]     │
│  ▶ Órdenes de Pago (98 pendientes · Gs. 320M)     [Regenerar]     │
│  ▶ Movimientos Tesorería (67 pendientes · Gs. 12M)[Regenerar]     │
│  ▶ Viáticos — Adelantos (25 pendientes · Gs. 8M)  [Regenerar]     │
│  ▶ Viáticos — Cierres (11 pendientes · Gs. 5M)    [Regenerar]     │
└──────────────────────────────────────────────────────────────────┘

Al hacer clic en Regenerar:
┌──────────────────────────────────┐
│  Regenerando asientos...          │
│  ████████░░░░░ 523 / 892 (58%)    │
│                                    │
│  ✓ Exitosos: 401                  │
│  ✗ Fallidos: 122                  │
│    - 89 sin mapeo                 │
│    - 33 período cerrado           │
└──────────────────────────────────┘
```

### Componentes reusables

- `PDFPreviewDialog` para el reporte final (patrón ya establecido).
- `ScreenGuia` con los pasos: 1) detectar 2) diagnosticar 3) resolver mapeos 4) regenerar.
- `EmptyState` cuando no hay pendientes ("Todo al día ✓").
- `Accordion` MUI para agrupar por tipo (patrón usado en CxP a Empleados).
- Chip semáforo por motivo (rojo/amarillo/gris).

---

## Módulo y seguridad

### Nuevo submódulo `CONTABILIDAD_CONCILIACION`

En `seguridad.seed-data.ts`:

```typescript
{
  codigo: 'CONTABILIDAD_CONCILIACION',
  descripcion: 'Conciliación Contable',
  icono: 'mdi:sync-alert',
  privilegios: [
    { codigo: 'CONT_CONC_PENDIENTES_VER', desc: 'Ver movimientos sin asiento contable', recurso: 'CONC_PENDIENTES', accion: 'VER' },
    { codigo: 'CONT_CONC_REGENERAR',      desc: 'Regenerar asientos pendientes',        recurso: 'CONC_PENDIENTES', accion: 'PROCESAR' },
  ],
}
```

Bajo el módulo `CONTABILIDAD` existente. Se habilita automáticamente para empresas que ya tienen `CONTABILIDAD` (opcionalmente).

### Roles típicos

| Rol | Ver | Regenerar |
|-----|-----|-----------|
| Contador | ✓ | ✓ |
| Auxiliar contable | ✓ | ✓ |
| Gerente / Admin | ✓ | ✗ |
| Operativo (ventas/RRHH) | ✗ | ✗ |

---

## Motivos de pendiente — catálogo

| Código | Descripción | Cómo se resuelve |
|--------|-------------|------------------|
| `MODULO_INACTIVO` | La empresa no tenía módulo Contabilidad activo cuando se creó el registro | Al activar Contabilidad + regenerar, se contabiliza |
| `SIN_MAPEO` | Falta mapeo de un concepto contable requerido | Contabilidad → Mapeo de Cuentas → asignar concepto faltante |
| `PERIODO_CERRADO` | La fecha del registro cae en un período contable cerrado | Reabrir el período (Contabilidad → Ejercicios) o mover a fecha del período abierto (asiento de regularización manual) |
| `CUENTA_OLD` | La cuenta contable asociada tiene código `OLD-` o está inactiva | Reasignar cuenta vigente en el mapeo o en la `tes_cuenta` correspondiente |
| `DATOS_FALTANTES` | Falta un dato requerido en el registro (ej. proveedor null en factura, moneda_id null) | Corregir el registro origen desde su pantalla |
| `SIN_MOTIVO` | El registro cumple todos los checks pero no se generó — probable bug | Regenerar directamente; si vuelve a fallar, ver AuditService para el error del integrador |

---

## Plan por fases (recomendado)

### Fase 1 — Infra + detección básica ✅ COMPLETADA

- ✅ Migración `20260725_cont_origen_registry` con la tabla nueva (columnas `origen_tipo` único, tabla origen, campo_fecha/monto/numero, filtro_extra JSON, service_name/method, conceptos_req JSON, orden).
- ✅ Seed inicial con 3 orígenes: `factura_cab`, `gasto_cab`, `tes_movimiento` (upsert idempotente en `onModuleInit`).
- ✅ Módulo `src/contabilidad-conciliacion/`:
  - `ContabilidadConciliacionService` — `sincronizarRegistry`, `listarRegistry`, `resumen`, `detalle`, `estadoModuloContabilidad`.
  - Detección en vivo usando NOT IN contra `cont_documentos` (sin tabla de estado que mantener).
  - Controller con endpoints `GET /registry`, `/estado`, `/resumen`, `/detalle/:origenTipo`.
- ✅ Frontend `ConciliacionContablePage`:
  - Nueva pestaña **Conciliación** en `Contabilidad.jsx`.
  - Filtros por rango + 4 presets (Este año, Ejercicio, Últimos 90d, Sin rango) con deep-link en URL.
  - 3 KPIs (orígenes con pendientes, registros pendientes, monto involucrado).
  - Tabla accordion agrupada por origen con detalle expandible (100 registros máx).
  - `ScreenGuia` con 3 pasos + notas explicativas.
  - Guardas: sin permiso → Alert; sin módulo → Alert informativo.
- **Entregable**: el contador ve el estado de asientos pendientes por origen. Solo lectura.

### Fase 2 — Diagnóstico por registro ✅ COMPLETADA

- ✅ `diagnosticarRegistro(empresaId, origenTipo, id)` — evalúa 6 motivos catalogados: `MODULO_INACTIVO`, `SIN_MAPEO`, `PERIODO_CERRADO`, `CUENTA_OLD`, `DATOS_FALTANTES`, `SIN_MOTIVO`.
- ✅ `diagnosticarLote(empresaId, origenTipo, ids)` — optimizado: chequea condiciones globales (módulo, mapeos, cuentas OLD-) una vez y por registro solo evalúa la fecha del período.
- ✅ `listarMotivos()` — catálogo con descripción y resolución para cada motivo.
- ✅ Endpoints `GET /motivos`, `GET /diagnostico/:origenTipo/:id`, `POST /diagnostico-lote/:origenTipo`.
- ✅ Frontend: al expandir un origen se dispara `postConciliacionDiagnosticoLote` con los ids visibles y se pinta un chip semáforo con tooltip por fila. Panel de leyenda al pie de la pantalla con los 6 motivos + resolución.
- **Entregable**: cada pendiente aparece con su motivo específico y cómo resolverlo.

### Fase 3 — Regeneración en lote ✅ COMPLETADA

- ✅ `regenerarUno(empresaId, origenTipo, id, userId)` — resuelve el service vía `ModuleRef` (strict:false), invoca el `service_method` declarado en el registry, verifica que se haya creado el `cont_documentos` y audita éxito/fallo en `AuditService`. Si el integrador retorna null (idempotencia interna), diagnostica y reporta el motivo.
- ✅ `regenerarLote(empresaId, opts, userId)` — procesa hasta 500 registros secuencialmente, cada uno en su propia transacción; un fallo no aborta el resto. Devuelve `{ok, fallidos, por_motivo, items[]}`.
- ✅ Endpoints `POST /regenerar/:origenTipo/:id` y `POST /regenerar/:origenTipo` (protegidos con `CONT_CONC_REGENERAR`).
- ✅ Auditoría: cada intento registra `CONT_CONC_REGENERAR_OK`, `CONT_CONC_REGENERAR_FALLA` o `CONT_CONC_REGENERAR_ERROR` con contexto en `AuditService`.
- ✅ Submódulo `CONTABILIDAD_CONCILIACION` + privilegios `CONT_CONC_PENDIENTES_VER` y `CONT_CONC_REGENERAR` en `seguridad.seed-data.ts`.
- ✅ Frontend:
  - Botón **Regenerar** a nivel origen con `useConfirmDialog` mostrando cantidad + monto.
  - Icono varita mágica ✨ por fila del detalle expandido para regenerar un único registro.
  - Modal fijo con spinner mientras corre el batch.
  - Modal de resultado con 3 cards (Total / Exitosos / Fallidos) + desglose de motivos de falla + chips semaforizados.
  - Invalidación automática de queries (`resumen`, `detalle`, `diag`) al terminar.
- **Entregable**: el contador puede regenerar todos los asientos de un origen con un solo click, con feedback visual completo y auditoría.

### Fase 4 — Expansión del registry ✅ COMPLETADA

- ✅ Ampliación del `RegistryEntry` con campo `arg_style` (`'id' | 'id_empresa'`) para soportar integradores con firmas distintas.
- ✅ Migración `20260726_cont_registry_arg_style` con la nueva columna.
- ✅ Registry expandido a **12 orígenes** cubriendo el 95% de los movimientos contabilizables:
  - `factura_cab` (facturas de venta)
  - `nota_credito_cab` (notas de crédito de venta)
  - `compra_cab` (facturas de compra)
  - `gasto_cab` (gastos)
  - `recibos_cobro` (recibos simples)
  - `recibos_multi` (recibos multi-factura, `arg_style='id_empresa'`)
  - `pagos_proveedor` (pagos)
  - `orden_pago_proveedor_cab` (órdenes de pago)
  - `tes_movimiento` (movimientos de tesorería)
  - `rrhh_liquidacion` (liquidaciones RRHH)
  - `rendicion_adelanto` (adelantos de viáticos, service propio `RendicionViaticosContabilidadService.registrarAdelanto`)
  - `rendicion_devolucion` (devoluciones / reintegros de viáticos, mismo service `.registrarDevolucion`)
- ✅ `regenerarUno` respeta `arg_style` al invocar el service.
- ✅ Cada entrada declara `conceptos_req` para diagnóstico específico (`SIN_MAPEO` con concepto exacto).
- ✅ `RendicionViaticosModule` ahora exporta `RendicionViaticosContabilidadService` para que `ModuleRef.get({strict:false})` lo resuelva.
- **Entregable**: cobertura completa de todos los movimientos contabilizables principales del ERP incluyendo viáticos.

### Fase 5 — Reportes + documentación ✅ COMPLETADA

- ✅ Renderer `msv-kude/src/conciliacion/conciliacion_a4.js` — reporte apaisado A4 estilo moderno (paleta primary/accent/bgAccent, header con logo + caja destacada, KPIs con 3 cells, tabla con filas alternadas y color semaforizado en estado, paginación automática).
- ✅ Route `POST /api/contabilidad/conciliacion/generate-pdf` en msv-kude + registro en `app.js`.
- ✅ Endpoint backend `GET /contabilidad/conciliacion/resumen/pdf`.
- ✅ Frontend: botones **Excel** y **PDF** en el header de la pantalla. Excel exporta con `xlsx` (5 columnas + anchos); PDF se abre en `PDFPreviewDialog` (modal preview, no pestaña).
- ✅ Guía funcional `pos-ventas/docs/guia-conciliacion-contable.md` (flujo, motivos, roles, FAQ).
- ✅ Guía IA `smartfactvoice-backend/docs/guias/guia-conciliacion-contable.md` (frontmatter con aliases, secciones estándar).
- ✅ Actualización de este plan marcando todas las fases completas.
- **Entregable**: módulo listo para producción con documentación completa.

### Fase 6 — Extras (post-launch) ✅ COMPLETADA

Mejoras agregadas después del release inicial, todas retrocompatibles:

- ✅ **Filtro por motivo** en la UI: dropdown en el header con los 6 motivos catalogados. Persiste en URL (`?motivo=SIN_MAPEO`). Filtra las filas del detalle expandido en cliente comparando contra `diagById.get(r.id).motivo`. Permite al contador ver "solo lo bloqueado por mapeo" y actuar en consecuencia.
- ✅ **Regenerar todo** — botón naranja en el header cuando hay pendientes globales. Endpoint `POST /regenerar-todo` que corre `regenerarLote` por cada origen activo del registry en secuencia. Modal de resultado agregado por origen con desglose OK/Fallidos por fila. Al terminar, `AuditService.log('CONT_CONC_REGENERAR_TODOS')` con resumen de todos los orígenes procesados.
- ✅ **Re-seed manual del registry** — endpoint `POST /re-seed-registry` (protegido con `CONT_CONC_REGENERAR`) que dispara `sincronizarRegistry()` sin necesidad de restart. Útil post-deploy cuando se agregan orígenes nuevos al código.
- ✅ **Cache implícito** — `diagnosticarLote` optimizado: chequea condiciones globales (módulo activo, mapeos, cuentas OLD-) una sola vez por origen, no por registro. Solo la fecha del período se evalúa por cada registro, aprovechando el cache interno de `PeriodosService`.
- ✅ **Enriquecimiento del detalle** — `detalle` ahora llama a `enriquecerItems(origen, ids, empresa)` que hace queries específicas por origen para traer contraparte + descripción legibles. Reemplaza el ID críptico por:
  - **Facturas de venta / NC / recibos** → razón social del cliente + número completo (est-punto-nro)
  - **Facturas de compra / gastos** → razón social del proveedor + número
  - **Pagos a proveedor** → proveedor + medio de pago
  - **Órdenes de pago** → proveedor + número de OP
  - **Movimientos de tesorería** → cuenta + tipo + descripción libre
  - **Liquidaciones RRHH** → cantidad de empleados + período MM/AAAA
  - **Viáticos** (adelantos/devoluciones) → nombre del empleado + concepto/tipo del viaje
- ✅ Corregidos campos del registry para respetar los nombres reales del schema Prisma (`dnumdoc`, `dfeemide`, `total_factura` para ventas; `numero_recibo`, `monto_total` para recibos; `rrhh_liquidaciones_cabecera` no `rrhh_liquidaciones`; etc.).
- ✅ Documentación actualizada en la guía IA y en la guía de usuario con los cuatro extras (regenerar todo, re-seed, filtro por motivo, detalle enriquecido).
- **Entregable**: flujo completo cerrado — el contador puede diagnosticar, resolver mapeos, identificar cada movimiento a simple vista y regenerar todo el histórico con un solo botón.

**Total estimado**: 8-13 días.

**Pasos manuales de activación**:

1. Reiniciar el backend — dispara `sincronizarRegistry()` en `onModuleInit`.
2. `POST /seguridad/seed-maestro` (idempotente) — crea el submódulo `CONTABILIDAD_CONCILIACION` y los 2 privilegios.
3. Habilitar `CONTABILIDAD_CONCILIACION` en la suscripción de las empresas que lo van a usar.
4. Asignar `CONT_CONC_PENDIENTES_VER` y `CONT_CONC_REGENERAR` a los perfiles del contador y auxiliar.

Gate entre fases: tests en verde + build backend/frontend OK + revisión con el contador de referencia.

---

## Plan de pruebas

### Unit (backend, Jest)

1. `ConciliacionService.detectarPendientes` — casos:
   - Empresa sin ningún módulo activo → cero pendientes.
   - Empresa con `CONTABILIDAD` recién activado y facturas viejas → todas las facturas aparecen.
   - Empresa con parte de asientos → solo los sin `cont_documento_id`.
   - Filtro por rango de fechas respeta `created_at`.
2. `diagnosticarRegistro` — casos por motivo:
   - `SIN_MAPEO` cuando falta concepto declarado como requerido.
   - `PERIODO_CERRADO` cuando la fecha cae en período cerrado.
   - `CUENTA_OLD` cuando el mapeo apunta a código `OLD-`.
   - `SIN_MOTIVO` cuando todo está OK.
3. `regenerar` — cada asiento en su propia transacción; un fallo no aborta el resto.
4. Idempotencia: correr `regenerar` dos veces seguidas no duplica documentos.

### E2E (backend, Supertest)

1. Flow completo: crear factura sin módulo → activar módulo → detectar → regenerar → verificar asiento CONFIRMADO.
2. Regeneración con período cerrado → 0 éxitos → todos los fallos reportan `PERIODO_CERRADO`.
3. Regeneración con mapeo faltante → falla con `SIN_MAPEO` + registra en `AuditService`.

### Frontend (Vitest)

1. Renderer: pantalla vacía → muestra `EmptyState` "Todo al día".
2. Filtros persisten en URL (`useSearchParams`).
3. Botón `Regenerar` deshabilitado si el usuario no tiene `CONT_CONC_REGENERAR`.
4. Modal de progreso actualiza `X/Y`.

### Manual (contador de referencia)

- Cargar empresa con histórico real (300+ facturas, 200+ cobros, 50 rendiciones viáticos).
- Ejecutar detección → validar que los conteos coinciden con lo esperado.
- Ejecutar regeneración → validar en Contabilidad → Asientos que aparecen los nuevos documentos.

---

## Riesgos y mitigaciones

| Riesgo | Mitigación |
|--------|-----------|
| Regenerar miles de asientos en producción bloquea la BD | Chunks de 100 con `Promise.allSettled`, sin transacción global; agregar throttle configurable |
| Diagnóstico lento por tener que probar cada mapeo por registro | Cachear mapeos por empresa en memoria durante la sesión de detección |
| El registry se desincroniza si se renombra un método del service | Test de arranque que valida que cada entry del registry apunta a un método real (falla al bootear) |
| Un integrador cambia su firma y rompe la regeneración | Wrapper por origen que normaliza los args esperados: `(empresaId, id, userId)` para todos |
| Regenerar dispara efectos secundarios en tesorería (doble movimiento) | Los integradores contables NO tocan tesorería; solo generan asiento. Los movimientos de tesorería son independientes. Documentar. |
| Auditoría explota por volumen | Compactar: 1 evento por batch, no 1 por registro. Solo detallar los fallos. |

---

## Métricas de éxito

- **Tiempo del contador** para detectar pendientes: < 30 seg para empresas con hasta 10k registros.
- **Tasa de regeneración exitosa** después de resolver mapeos: > 95%.
- **Reducción de tickets** "no aparece el asiento de la factura X" en soporte: > 80% en el primer trimestre post-release.
- **Cero desincronización**: la detección debe devolver los mismos números que una query manual `count(*) where cont_documento_id is null`.

---

## Documentos relacionados

- `plan-rendicion-viaticos.md` — patrón de integración contable + auditoría de fallos (Fase 6+).
- `guia-contabilidad.md` (IA) — mapeos de cuentas, asientos, períodos.
- `docs/ui-standards.md` (frontend) — patrones de UI (drill-down, PDF preview modal, filtros con deep-link).
- `guia-conciliacion-contable.md` (a crear en Fase 5, ambos: usuario y IA).
