Plan de Pruebas — Módulo de Cobranzas (Fases A.3, B, Submódulos 4 y 5)

---

Conceptos clave antes de empezar

┌────────────────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Término                │ Qué es en la práctica                                                            │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Cuota                  │ Vencimiento individual de una factura a crédito (factura_cuotas)                 │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Mora / Interés         │ Recargo por pago atrasado. Se factura con comprobante fiscal propio              │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Promesa de pago        │ Compromiso del cliente de pagar en una fecha futura                              │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Gestión de mora        │ Expediente de cobranza con etapas: GESTION_INTERNA → JUDICIAL → RECUPERADA/PERDIDA│
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Refinanciación         │ Cuotas atrasadas se cierran y se genera nuevo plan de pago                       │
├────────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Autorización descuento │ Quita de capital o exoneración de interés solicitada por el cobrador             │
└────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘

---

Pre-requisitos antes de probar

1. Login con un usuario que tenga perfil ADMIN o equivalente con todos los permisos COB_*
2. Existir al menos:
   - 1 empresa configurada con timbrado vigente
   - 1 cliente con crédito habilitado (con límite > 0)
   - 2 facturas a crédito con cuotas vencidas hace > 5 días (para probar mora)
   - 1 cobrador activo asignado al cliente
3. Configurar Mora: Cobranzas → Config Mora → tasa mensual (ej: 3%), días de gracia (ej: 3)
4. Configurar Intereses Fiscales: Cobranzas → Config Intereses → punto de expedición + timbrado

═══════════════════════════════════════════════════════════════════
SECCIÓN A — Intereses moratorios con comprobante fiscal (A.3)
═══════════════════════════════════════════════════════════════════

PRUEBA A.1 — Configurar política de interés

Dónde: Cobranzas → Configuración → Intereses Moratorios

Pasos:
1. Abrir la pantalla por primera vez (sin configuración previa)
2. Verificar banner "Sin configuración de intereses"
3. Completar:
   - Tasa mensual: 3.0
   - Días de gracia: 3
   - Punto de expedición a usar: seleccionar uno habilitado
   - Timbrado: seleccionar uno vigente
4. Guardar

Resultado esperado:
- Toast verde "Configuración guardada"
- El banner desaparece
- El header muestra la tasa y timbrado vigente

Resultado negativo:
- Si timbrado vencido → toast rojo "Timbrado no vigente"
- Si punto de expedición no habilitado → error de validación


PRUEBA A.2 — Calcular interés sobre una cuota vencida

Dónde: Cobranzas → Cobros (menú Cobranzas → Cobros) → seleccionar cliente con cuota vencida

Pasos:
1. Buscar cliente con cuota vencida hace > días_gracia
2. Click en la cuota
3. Verificar que se muestra:
   - Días de atraso
   - Interés calculado (capital × tasa × días / 30)
   - Total a cobrar (capital + interés)

Resultado esperado:
- El interés se calcula sólo después de los días de gracia
- Aparece etiqueta "Interés sujeto a comprobante fiscal"


PRUEBA A.3 — Cobrar cuota con interés (emisión de comprobante fiscal de interés)

Dónde: Wizard de Cobro (desde Cobros (menú Cobranzas → Cobros) o Recibos)

Pasos:
1. Iniciar cobro de la cuota vencida
2. Verificar que el desglose muestra: Capital | Interés moratorio
3. Confirmar el recibo

Resultado esperado:
- Se emite un recibo de cobro
- Adicionalmente se emite una factura electrónica por el interés (línea "Interés moratorio")
- En Cobranzas → Reporte Intereses Cobrados aparece la fila

Resultado negativo:
- Si SIFEN rechaza la factura de interés, el cobro debe revertirse (transacción) y mostrar error


PRUEBA A.3.1 — Configurar modo de IVA (INCLUIDO vs POR_FUERA)

Dónde: Configuración → Intereses Moratorios

Pasos:
1. Setear IVA = 10%, modo = "IVA incluido", tasa diaria = 0.1%
2. Guardar y emitir un cobro con mora > 0 → verificar que el monto cobrado
   coincide con la tasa simple (sin recargo)
3. Cambiar modo a "IVA por fuera", repetir el cobro con la misma cuota base
4. Verificar que el monto de mora ahora incluye un 10% adicional sobre el
   cálculo base

Resultado esperado:
- En modo INCLUIDO: mora_total = tasa × días × saldo
- En modo POR_FUERA: mora_total = (tasa × días × saldo) × 1.10
- La factura de mora respeta el modo configurado (línea con/sin recargo)


PRUEBA A.3.2 — Emisión con IVA Exento (mora 0%)

Dónde: Configuración → Intereses Moratorios → IVA = 0%

Pasos:
1. Setear iva_porcentaje = 0
2. Emitir cobro con mora de Gs 517
3. Abrir el KUDE de la factura de interés

Resultado esperado:
- Columna "Exentas" muestra exactamente Gs 517 (no 1.034)
- afectacion_iva en el XML/DE es 3 (Exento), no 2 (Exonerado)

Resultado negativo:
- Si se ve doble en Exentas, el bug de suma_exenta no está corregido en el KUDE


PRUEBA A.3.3 — Vínculo bidireccional recibo ↔ factura de mora

Dónde: Cobranzas → Recibos y Ventas → Facturas

Pasos:
1. Emitir un cobro con mora (que genere factura de intereses)
2. Abrir el drawer del recibo → buscar sección "Documento de intereses emitido"
3. Click sobre el documento → debe abrir la factura
4. En el dialog de la factura, verificar chip "Origen: Mora" y bloque
   "Recibo de origen" con número y monto del recibo

Resultado esperado:
- Ambos lados resuelven el vínculo automáticamente (sin refresh manual)
- Si se abre el recibo desde el listado, el drawer hace fetch del detalle
  completo (intereses_cobrados + factura_emitida)
- Si se abre la factura desde el listado, el dialog hace fetch para resolver
  `origen_intereses`


PRUEBA A.3.4 — Coherencia de montos en drawer de recibo

Dónde: Cobranzas → Recibos → abrir recibo con mora cobrada

Pasos:
1. Verificar que "Total cobrado" = suma de "Cuotas incluidas"
2. Verificar que "Total cobrado" = suma de "Formas de pago"
3. Las cuotas deben mostrar el monto con mora prorrateada por cuota

Resultado esperado:
- La mora se desglosa por cuota leyendo `intereses_cobrados` (no `mora_monto`
  del detalle, que viene en 0)
- En "Formas de pago", la mora total se acumula al método dominante para que
  cuadre el total


PRUEBA A.4 — Reporte Intereses Cobrados

Dónde: Cobranzas → Reportes → Intereses Cobrados

Pasos:
1. Filtrar por fecha (rango con la prueba A.3)
2. Exportar a CSV/PDF si está disponible

Resultado esperado:
- Lista con: fecha, cliente, factura origen, monto capital, monto interés, número factura interés
- Totales al pie


═══════════════════════════════════════════════════════════════════
SECCIÓN B — Gestión de Mora (Submódulo 1)
═══════════════════════════════════════════════════════════════════

PRUEBA B.1 — Crear expediente de gestión de mora

Dónde: Cobranzas → Gestión de Mora → botón "Nueva gestión"

Pasos:
1. Seleccionar cliente con facturas vencidas (Autocomplete: buscar por razón
   social / RUC / CI)
2. En la grilla "Facturas pendientes", marcar las facturas que entran en el
   expediente (mínimo una)
3. Estado inicial: "Gestión interna"
4. Cargar observación / motivo de apertura
5. (Opcional) URL de documento si el estado lo requiere — DEMANDA exige
   carátula judicial, INCOBRABLE exige acta de directorio
6. Confirmar con "Crear gestión"

Resultado esperado (qué verificar y dónde):
- **Listado de gestiones** (Cobranzas → Gestión de Mora): aparece la nueva
  fila con estado `GESTION_INTERNA`, monto involucrado (suma de saldos
  snapshot) y la cantidad de facturas incluidas.
- **Marca de cliente en mora** — listado de Clientes
  (`ClientesListConfig.jsx`): el cliente muestra un Badge con ícono
  `mdi:gavel` y el código del estado (ej. `GESTION_INTERNA`). El selector
  "Todos los estados de mora" permite filtrarlo. El campo
  `mora_estado_activo` lo enriquece el backend en
  `clientes.service.ts` mirando si hay gestión en estado activo
  (`GESTION_INTERNA`, `INFORMCONF`, `DEMANDA`).
- **Marca en facturas del cliente** — Ventas → Facturas
  (`FacturasTab.jsx`): cada factura del cliente muestra debajo del RUC un
  chip `EN GESTIÓN · <ESTADO>` (rojo para DEMANDA/INCOBRABLE, ámbar para
  GESTION_INTERNA/REFINANCIADA, celeste para INFORMCONF). El marcador
  es a nivel **cliente**, no por cuota: todas las facturas del cliente lo
  llevan mientras la gestión esté activa. El backend lo expone en cada
  fila del listado como `cliente_en_gestion_mora: { activa, estado }`
  (`facturas.service.ts:2100`).

Resultado negativo:
- Si alguna de las facturas seleccionadas ya está incluida en una gestión
  activa del mismo cliente, el backend rechaza con
  `BadRequestException` listando los números de factura conflictivos
  (formato `dest-dpunexp-dnumdoc`, ej. `001-001-0001678`). El frontend
  muestra el mensaje en un toast/banner sin crear el expediente.
- Hint para la IA: validar contra `cob_gestion_mora_factura` con
  `gestion_mora.estado_actual ∈ ESTADOS_ACTIVOS` (ver
  `gestion-mora.service.ts::create`).


PRUEBA B.2 — Transicionar estado de gestión

Dónde: Gestión de Mora → abrir un expediente → botón "Cambiar estado"

Pasos:
1. Estado actual: GESTION_INTERNA → transicionar a JUDICIAL
2. Cargar motivo y fecha
3. Confirmar

Resultado esperado:
- Estado actualizado a JUDICIAL
- En el tab "Historial" aparece la transición con usuario y fecha/hora
- Auditoría registra la acción

Resultado negativo:
- Transiciones no permitidas (ej: RECUPERADA → JUDICIAL) deben bloquearse


PRUEBA B.3 — Cierre automático por cobro total

Dónde: emitir un cobro que liquide TODAS las cuotas del cliente

Pasos:
1. Cliente tiene gestión activa
2. Emitir recibo de cobro que cubra todo el saldo pendiente
3. Volver a Gestión de Mora

Resultado esperado:
- El expediente pasa automáticamente a RECUPERADA (hook desde CobrosService)
- Aparece en el historial con usuario "sistema (auto)"
- El cliente pierde flag `mora_estado_activo`


PRUEBA B.4 — Generar PDF del expediente

Dónde: Gestión de Mora → expediente → botón "PDF"

Pasos:
1. Click en "Ver PDF"
2. Click en "Descargar PDF"

Resultado esperado:
- Se abre el PDF en nueva pestaña con: datos cliente, cuotas en gestión, historial completo, sección garantes (placeholder), firmas
- La descarga guarda el archivo `gestion-mora-{id}.pdf`


PRUEBA B.5 — Dashboard de gestión de mora (PENDIENTE DE UI)

Estado: el endpoint backend existe, la pestaña/tarjetas en UI **no están
implementadas** todavía. Pantalla actual `Cobranzas → Gestión de Mora`
muestra solo el listado tabular (`GestionMoraHub.jsx` renderiza
`<GestionMoraTab>` sin tabs ni cards de resumen).

Hint para la IA implementadora:
- **Endpoint disponible:** `GET /api/v1/gestion-mora/dashboard` (permiso
  `COB_GMR_GESTION_MORA_VER`), implementado en
  `gestion-mora.service.ts::dashboard(empresa_id)`.
- **Forma de respuesta:**
  ```ts
  {
    por_estado: Array<{ estado: CobMoraEstado, cantidad: number, monto: number }>,
    total_general: { cantidad: number, monto: number },
    total_activos: { cantidad: number, monto: number },  // GESTION_INTERNA + INFORMCONF + DEMANDA
  }
  ```
- **Estados disponibles:** `AL_DIA`, `GESTION_INTERNA`, `INFORMCONF`,
  `DEMANDA`, `RECUPERADA`, `REFINANCIADA`, `INCOBRABLE` (no existe
  `JUDICIAL` como estado — la versión judicial es `DEMANDA`).
- **UI sugerida:** agregar `Tabs` en `GestionMoraHub.jsx` con `Listado` y
  `Dashboard`. En `Dashboard` renderizar una grilla de cards (una por
  estado de `ESTADOS_ACTIVOS` + total general + total activos). Reusar
  `getCobMoraEstadoChip()` para colores.
- **Métrica "Recuperadas últimos 30 días":** NO existe en el endpoint
  actual. Si se quiere, extender `dashboard()` con un parámetro `desde`
  contando transiciones a `RECUPERADA` en `cob_gestion_mora_historial`.

Pasos de prueba (cuando esté implementado):
1. Verificar tarjetas con: Total en GESTION_INTERNA, Total en INFORMCONF,
   Total en DEMANDA, Monto total bajo gestión (= `total_activos.monto`).
2. Comparar contra los conteos del listado filtrado por cada estado.

Resultado esperado:
- Números coinciden con `por_estado[k].cantidad` y `por_estado[k].monto`
  del endpoint para cada estado `k`.


PRUEBA B.6 — Filtros en listados de Clientes y Facturas

Dónde: Clientes y Facturas (listados principales)

Pasos:
1. En Clientes: aplicar filtro "Sólo clientes en mora"
2. En Facturas: verificar chip "En gestión" en facturas asociadas

Resultado esperado:
- Filtro funciona y muestra contador en el header
- Chip "En gestión" lleva al expediente al hacer click


═══════════════════════════════════════════════════════════════════
SECCIÓN C — Autorizaciones de Descuento (parte de A.3.7)
═══════════════════════════════════════════════════════════════════

PRUEBA C.1 — Cobrador solicita exoneración de mora

Dónde: Cobros (menú Cobranzas → Cobros) → cuota con mora → botón "Solicitar exoneración"

Pasos:
1. Seleccionar cuota con interés calculado
2. Click "Solicitar exoneración"
3. Cargar motivo (ej: "Cliente vino al día por primera vez en 2 años")
4. Enviar

Resultado esperado:
- Autorización queda en estado PENDIENTE
- Aparece notificación al supervisor (badge con contador)
- El cobrador ve la solicitud en su lista "Pendientes"


PRUEBA C.2 — Supervisor aprueba

Dónde: Cobranzas → Autorizaciones → Pendientes

Pasos:
1. Ver la solicitud de C.1
2. Click "Aprobar" → cargar nota
3. Confirmar

Resultado esperado:
- Estado APROBADO
- El cobrador, al cobrar, ve la exoneración aplicada automáticamente (interés=0 para esa cuota)
- Después del cobro, la autorización pasa a USADO


PRUEBA C.3 — Supervisor rechaza

Pasos:
1. Crear otra solicitud
2. Rechazar con nota

Resultado esperado:
- Estado RECHAZADO
- El cobrador no puede aplicarla


PRUEBA C.4 — Descuento admin (creación directa por supervisor)

Dónde: Autorizaciones → "Crear descuento (admin)"

Pasos:
1. Seleccionar cliente + tipo QUITA_CAPITAL
2. Monto fijo (o porcentaje)
3. Motivo + fecha expiración (default 30 días)
4. Guardar

Resultado esperado:
- Queda en estado APROBADO directo
- El cobrador asignado lo ve en "Descuentos activos"
- Expira automáticamente pasado expires_at


═══════════════════════════════════════════════════════════════════
SECCIÓN D — Promesas de Pago Ampliadas (Submódulo 4)
═══════════════════════════════════════════════════════════════════

PRUEBA D.1 — Promesa básica (1 factura)

Dónde: dos puntos de entrada equivalentes
- `Cobranzas → Promesas` → botón "Nueva promesa" (selecciona cliente dentro del modal)
- `Cobros (menú Cobranzas → Cobros)` → seleccionar cliente → "Nueva promesa"

Pasos:
1. Buscar cliente — el Autocomplete debe encontrarlo por razón social, RUC o CI
2. Seleccionar factura
3. Fecha prometida: 5 días en el futuro
4. Monto prometido (MonedaInput respeta la moneda de la factura: PYG, USD, etc.)
5. Tipo evidencia: WHATSAPP
6. Pagador: CLIENTE
7. Notas: "Confirmado por WhatsApp"
8. Guardar

Resultado esperado:
- Promesa en estado PENDIENTE
- En el listado `Cobranzas → Promesas` aparece con el tipo de evidencia como ícono (tooltip al hover)
- El monto se muestra con `fmtMoneda` según la moneda de la factura
- Recordatorio queda agendado para 1 día antes

Resultado negativo:
- Si en el Autocomplete se escribe un RUC y no aparecen resultados, el bug es del backend (`/clientes/search` debe matchear razón social, RUC y documento) — no del filtro local del frontend (ya tiene `filterOptions={(x) => x}`)


PRUEBA D.2 — Promesa multi-factura

Dónde: Cobros (menú Cobranzas → Cobros) → cliente → "Nueva promesa multi-factura"

Pasos:
1. Marcar 2 o 3 facturas del cliente
2. Asignar monto a cada una
3. Fecha prometida única
4. Confirmar

Resultado esperado:
- Una sola promesa que cubre N facturas (tabla `cob_promesa_factura`)
- En el listado aparece con un chip "N facturas"
- El monto prometido total = suma de asignaciones

Resultado negativo:
- Si alguna factura no pertenece al cliente → error


PRUEBA D.3 — Promesa con garante o tercero

Dónde: Nueva promesa → cambiar pagador_tipo

Pasos:
1. Seleccionar pagador_tipo = GARANTE
2. Buscar y seleccionar otro cliente como garante
3. Guardar

Resultado esperado:
- En el detalle figura "Pagador: GARANTE — {nombre}"


PRUEBA D.4 — Cancelar promesa con motivo

Dónde: `Cobranzas → Promesas` → fila pendiente → dropdown "Acciones" → "Cancelar"

Pasos:
1. Cargar motivo: "Cliente avisó que no puede"
2. Confirmar

Resultado esperado:
- Estado CANCELADA (chip gris en la fila)
- En notas queda registrado `[cancelada]: motivo`
- Auditoría registra acción CANCEL
- El botón solo aparece si el usuario tiene `COB_PRM_PROMESA_CANCELAR`

Resultado negativo:
- Sobre una promesa ya CUMPLIDA debe rechazar
- Sin permiso, el item del dropdown ni siquiera se renderiza


PRUEBA D.5 — Renegociar promesa

Dónde: `Cobranzas → Promesas` → fila PENDIENTE o INCUMPLIDA → "Acciones" → "Renegociar"

Pasos:
1. Nueva fecha (10 días en el futuro)
2. Nuevo monto (opcional)
3. Notas obligatorias
4. Tipo evidencia
5. Confirmar

Resultado esperado:
- Promesa original pasa a RENEGOCIADA
- Se crea nueva promesa PENDIENTE enlazada (`promesa_origen_id`)
- Si era multi-factura, las asignaciones se copian a la nueva
- En el listado se ve cadena origen → nueva


PRUEBA D.6 — Cierre automático al cobrar

Dónde: hacer un cobro a un cliente con promesa pendiente sobre la factura/cuota

Pasos:
1. Cliente tiene promesa PENDIENTE sobre factura F1 (o cuota específica)
2. Emitir recibo cobrando F1 (o esa cuota) completo
3. Volver al listado de promesas

Resultado esperado:
- Promesa con `cuota_id` específica: si el saldo de la cuota post-cobro es 0 → CUMPLIDA con `monto_cumplido = monto del cobro sobre esa cuota`; si la cuota se tocó pero queda saldo → CUMPLIDA_PARCIAL
- Promesa a nivel factura (sin `cuota_id`): se compara `monto_cobrado` vs `monto_prometido` → CUMPLIDA si cubre, CUMPLIDA_PARCIAL si parcial
- Se llena `cumplida_recibo_id`, `monto_cumplido`, `fecha_cumplimiento`
- En auditoría aparece acción `AUTO_CLOSE` (no `UPDATE` manual)
- El hook se dispara desde `CobrosService` pasando `cuota_ids` a `promesasService.cerrarPorCobro` — la granularidad por cuota evita falsos positivos cuando un cobro toca múltiples cuotas pero solo una está prometida


PRUEBA D.7 — Job de incumplimiento automático

Dónde: esperar al día siguiente de fecha_prometida (o forzar el job)

Pasos:
1. Promesa con fecha_prometida = ayer y aún PENDIENTE
2. Ejecutar `marcarIncumplidas` (cron diario)

Resultado esperado:
- Estado pasa a INCUMPLIDA
- Aparece contador rojo en el dashboard del cobrador


PRUEBA D.8 — Reporte de Cumplimiento

Dónde: **Reportes → Cobranzas → "Cumplimiento de Promesas"** (`/reportes/cobranzas/promesas`)

Nota: el reporte vive en el hub de Reportes (no en el sidebar de Cobranzas) para no
duplicar entradas. La tarjeta solo aparece si la empresa tiene habilitado el
submódulo `COB_PROMESAS`. Requiere permiso `COB_PRM_REPORTE_CUMPLIMIENTO`.

Pasos:
1. Filtrar rango de fechas
2. Revisar tabla por cobrador

Resultado esperado:
- Columnas: total, cumplidas, parciales, incumplidas, pendientes, tasa cumpl., Gs. cumplido
- Tasa cumplimiento = (cumplidas + parciales) / total × 100


PRUEBA D.9 — Dashboard Cashflow

Dónde: **Reportes → Cobranzas → "Cumplimiento de Promesas"** (panel derecho)

Resultado esperado:
- Total pendiente (Gs.)
- Tabla de proyección por fecha futura (sumando montos prometidos)
- No incluye promesas canceladas, renegociadas o incumplidas pasadas


PRUEBA D.10 — Banner de promesas vigentes en Cobros

Dónde: `Cobranzas → Cobros` → buscar y seleccionar un cliente

Pasos:
1. Cliente con al menos 1 promesa PENDIENTE (incluyendo alguna con fecha en el pasado para probar el caso "vencida")
2. Seleccionar el cliente en el buscador del wizard

Resultado esperado:
- Aparece banner sobre el panel de facturas con borde-izquierdo de acento
  (ámbar si no hay vencidas, rojo si hay alguna vencida)
- KPI principal: cantidad de promesas vigentes + badge "N vencida(s)" en rojo si aplica
- Totales por moneda en línea secundaria (`fmtMoneda` con código de cada factura)
- Chips ordenados: vencidas primero (rojo), luego próximas por fecha ascendente
- Cada chip muestra fecha + monto; máximo 4 visibles + "+N más"
- Link "Ver detalle →" lleva al listado `/cobranzas/promesas?cliente_id=X`
- El banner solo se renderiza si el usuario tiene permiso `COB_PRM_PROMESA_VER` —
  sin ese permiso ni siquiera se hace el fetch de promesas vigentes


PRUEBA D.11 — Listado operativo de Promesas

Dónde: `Cobranzas → Promesas` (`/cobranzas/promesas`)

Pasos:
1. Filtrar por estado (default: Pendientes)
2. Filtrar por rango de fechas, cobrador, cliente (Autocomplete por nombre/RUC/CI)
3. Verificar columna acciones con dropdown "Acciones"

Resultado esperado:
- Filtros en una sola fila compacta: Estado (160px) + rango Desde/Hasta (320px) + Cobrador + Cliente
- Tabla sin scroll horizontal en pantalla full-HD
- Columna Cliente muestra el cobrador como caption con ícono (no en columna separada)
- Columna evidencia es solo ícono con tooltip
- Total prometido se agrupa **por moneda** (no en una sola línea PYG)
- Acciones gateadas por permiso: solo aparecen los items para los que el usuario tiene `COB_PRM_PROMESA_*`


═══════════════════════════════════════════════════════════════════
SECCIÓN E — Refinanciación (Submódulo 5)
═══════════════════════════════════════════════════════════════════

PRUEBA E.1 — Simular refinanciación

Dónde: Cobranzas → Refinanciaciones → "Nueva refinanciación" → paso "Simular"

Pasos:
1. Seleccionar cliente
2. Marcar cuotas atrasadas a refinanciar
3. Política de interés: NORMAL (ajusta tasa) | EXONERAR (perdona interés) | NEGOCIAR (custom)
4. Cantidad de cuotas nuevas: 6
5. Fecha primera cuota: 2 semanas adelante
6. Click "Simular"

Resultado esperado:
- Muestra:
  - Capital original / interés original
  - Interés exonerado (si EXONERAR)
  - Capital nuevo
  - Tabla de cuotas propuestas (6 filas con fechas y montos)


PRUEBA E.2 — Crear refinanciación en BORRADOR

Dónde: continuar wizard → paso "Confirmación"

Pasos:
1. Cargar motivo
2. Confirmar

Resultado esperado:
- Refinanciación queda en estado BORRADOR
- Las cuotas originales NO se tocan todavía
- Aparece en el listado


PRUEBA E.3 — Generar PDF del acuerdo

Dónde: Refinanciaciones → fila → "PDF"

Resultado esperado:
- PDF con: datos cliente, chip "BORRADOR" (azul), resumen 4 col, política, cuotas originales, plan nuevo, firmas
- Encabezado de la empresa correcto


PRUEBA E.4 — Ejecutar refinanciación

Dónde: detalle de la refinanciación → "Ejecutar"

Pasos:
1. Cliente firmó el PDF
2. Click "Ejecutar"
3. Confirmar diálogo

Resultado esperado:
- Estado pasa a EJECUTADA
- Las cuotas originales pasan a estado "REFINANCIADA" con saldo 0
- Se recalcula `factura_cab.saldo_pendiente`
- Se crea nueva `solicitud_credito` con `tipo_origen='REFINANCIACION'` y nuevo plan
- Si el cliente tenía gestión de mora activa sobre esas cuotas → transición a REFINANCIADA automática (hook)
- PDF ahora muestra chip "EJECUTADA" (verde)

Resultado negativo:
- Si alguna cuota ya está pagada → debe bloquear
- Si cuotas ya están en otra refinanciación BORRADOR/EJECUTADA → bloquear


PRUEBA E.5 — Anular refinanciación

Dónde: refinanciación BORRADOR o EJECUTADA → "Anular"

Pasos:
1. Cargar motivo
2. Confirmar

Resultado esperado:
- Estado ANULADA
- Si era EJECUTADA: las cuotas originales vuelven a su saldo previo (rollback)
- Si era BORRADOR: simplemente cambia estado
- PDF muestra chip "ANULADA" (rojo) y marca "ACUERDO ANULADO" en el cuerpo


PRUEBA E.6 — Listado y filtros

Dónde: Refinanciaciones

Resultado esperado:
- Filtros: estado, cliente, rango fechas
- Columnas: fecha, cliente, política, cuotas orig, capital nuevo, estado


PRUEBA E.7 — Sección Refinanciaciones en historial del cliente

Dónde: ficha del cliente → drawer historial crediticio → tab Refinanciaciones

Resultado esperado:
- Lista todas las refinanciaciones del cliente con chip estado
- Link al detalle


═══════════════════════════════════════════════════════════════════
SECCIÓN F — Cruces e integraciones
═══════════════════════════════════════════════════════════════════

PRUEBA F.1 — Promesa + Mora + Cobro

Pasos:
1. Crear gestión de mora sobre cliente con cuota atrasada
2. Crear promesa multi-factura sobre las cuotas del expediente
3. Emitir cobro que liquide todo

Resultado esperado:
- Promesa → CUMPLIDA
- Gestión de mora → RECUPERADA
- Cliente sale de mora (flag false)


PRUEBA F.2 — Refinanciación cierra gestión de mora

Pasos:
1. Cliente en gestión JUDICIAL
2. Refinanciar las cuotas en gestión y EJECUTAR

Resultado esperado:
- Gestión de mora transiciona automáticamente a REFINANCIADA
- Aparece registro en el historial con la nueva solicitud_credito enlazada


PRUEBA F.3 — Auditoría completa

Dónde: Administración → Auditoría (o `cob_audit` directo)

Resultado esperado:
- Para cada prueba anterior debe existir fila con: user_id, action (CREATE/UPDATE/CANCEL/RENEGOTIATE/EXECUTE/ANULAR), entity_type, entity_id, descripcion legible


PRUEBA F.4 — Permisos por perfil

Pasos:
1. Crear perfil "Cobrador básico" con sólo: `COB_PRM_PROMESA_VER`, `COB_PRM_PROMESA_CREAR`, `COB_CBR_PANEL_COBRADOR_VER`
2. Loguearse con ese perfil

Resultado esperado:
- Sidebar muestra "Cobros" y "Promesas" (el segundo gateado por `COB_PRM_PROMESA_VER`)
- En `Cobranzas → Promesas` puede crear una nueva promesa pero el dropdown "Acciones" no muestra "Cancelar" ni "Renegociar"
- En `Reportes → Cobranzas` **no** aparece "Cumplimiento de Promesas" (faltan `COB_PRM_REPORTE_CUMPLIMIENTO` y/o el toggle del submódulo)
- En `Cobros` el banner de promesas vigentes se muestra (tiene VER), pero el link "Ver detalle" puede llevar al listado en modo solo-lectura
- No puede acceder a Refinanciaciones, Gestión de Mora ni Autorizaciones (rutas devuelven 403)


PRUEBA F.5 — Submódulo COB_PROMESAS deshabilitado en la empresa

Pasos:
1. Desde Administración, quitar el submódulo `COB_PROMESAS` de la empresa
2. Loguearse con un usuario admin

Resultado esperado:
- El item "Promesas" desaparece del sidebar (gateado por `submodulo: "COB_PROMESAS"`)
- En `Reportes → Cobranzas` desaparece la tarjeta "Cumplimiento de Promesas"
- En `Cobros` el banner de promesas vigentes no se muestra (aunque haya datos en BD)
- El backend rechaza las llamadas a `/api/v1/promesas-pago/*` con 403 por falta de submódulo


═══════════════════════════════════════════════════════════════════
SECCIÓN G — Casos negativos y validaciones
═══════════════════════════════════════════════════════════════════

PRUEBA G.1 — Datos inválidos

| Caso                                              | Resultado esperado                        |
|---------------------------------------------------|-------------------------------------------|
| Promesa con fecha pasada                          | Error de validación                       |
| Promesa multi-factura con monto negativo          | Error de validación                       |
| Renegociar promesa CANCELADA                      | "Sólo pendientes o incumplidas"           |
| Ejecutar refi sin cuotas marcadas                 | "Debe seleccionar al menos una cuota"     |
| Refinanciar cuotas de otro cliente                | Error 400                                 |
| Crear gestión de mora sin cuotas vencidas         | Error de validación                       |
| Aprobar autorización ya resuelta                  | "Autorización ya resuelta"                |
| Cobrar con autorización expirada                  | "Autorización vencida"                    |


PRUEBA G.2 — Concurrencia

Pasos:
1. Dos usuarios abren la misma refinanciación BORRADOR
2. Usuario A ejecuta, usuario B intenta ejecutar después

Resultado esperado:
- Segundo intento devuelve "Refinanciación ya ejecutada" (409 Conflict)


PRUEBA G.3 — Idempotencia del hook de cobros

Pasos:
1. Anular un recibo de cobro que ya cerró una promesa
2. Verificar estado de la promesa

Resultado esperado (documentar comportamiento actual):
- La promesa permanece como CUMPLIDA (la anulación de cobro NO revierte la promesa)
- (TODO si se requiere: hook de anulación reabre la promesa)


═══════════════════════════════════════════════════════════════════
SECCIÓN H — Mesa de Gestión y Workflow de cobranza (Submódulo 7)
═══════════════════════════════════════════════════════════════════

Contexto: el gestor de oficina (telemarketing) trabaja desde **Cobranzas → Mesa de Gestión**.
La bandeja prioriza clientes morosos. Cada interacción se registra como **gestión**;
si el cliente compromete pago, se crea la **promesa** en la misma acción (Opción A unificada).
"Mora avanzada (legal)" queda para casos formales (INFORMCONF / DEMANDA / INCOBRABLE).

Pre-requisito específico H:
- Usuario con permisos `COB_MG_WORKLIST_VER`, `COB_WFL_GESTION_REGISTRAR`, `COB_PRM_PROMESA_VER`.
- Al menos 5 clientes con cuotas vencidas con saldo > 0 (rangos variados: <30d, 30-60d, 60-90d, >90d).
- Al menos 1 cliente con promesa pendiente vigente (fecha futura) y 1 con promesa vencida.

──────────────────────────────────────────────────────────────────
PRUEBA H.1 — Worklist priorizada

Dónde: Cobranzas → Mesa de Gestión → tab "Bandeja de gestión"

Pasos:
1. Verificar que cargan los 6 KPIs (Clientes morosos / Saldo vencido / Sin gestión / +30 / +60 / +90).
2. Verificar que la tabla ordena por defecto en "Prioridad (recomendado)" y los clientes con score más alto (≥70) salen primero, con chip rojo de prioridad.
3. Cambiar orden a "Más vencidos" — confirmar que se reordena por `dias_max_vencido` desc.
4. Cambiar orden a "Sin gestión hace más tiempo" — confirmar que los "Nunca" aparecen arriba.

Resultado esperado:
- KPIs coinciden con el número total de morosos del sistema.
- Cada fila muestra: razón social + RUC + zona, contacto (cel/tel), saldo vencido + cantidad de facturas, chip de mora con días + fecha de origen, última gestión (o "Nunca"), promesas (vigentes verdes + incumplidas naranjas), prioridad (0-100), 4 botones de acción.

Resultado negativo:
- Si no hay vencidos, mensaje "Sin clientes morosos en este filtro" con ícono inbox.


PRUEBA H.2 — Filtros y debounce

Dónde: Mesa de Gestión → tab "Bandeja de gestión"

Pasos:
1. Tipear en el buscador "ave" → esperar 400 ms → debe disparar **una sola** request (no una por tecla).
2. Aplicar filtro "Mora ≥ días = 60" → la tabla recarga con solo clientes con mora ≥ 60 días.
3. Aplicar "Sin gestión ≥ días = 30" → solo morosos no contactados en 30+ días o nunca.
4. Toggle "Promesas incumplidas" → solo morosos con promesas vencidas no cumplidas.
5. Toggle "Mis gestiones" → solo morosos donde el usuario actual registró al menos una gestión.
6. Tabs de estado: Por contactar → muestra "Nunca" o gestión hace ≥7 días; En gestión → gestión hace <7 días; Con promesa vigente → solo con `promesas_vigentes > 0`.

Resultado esperado:
- Debounce visible en la pestaña Network (no más de 1 request por filtro tras 400 ms de inactividad).
- Combinación de filtros se acumula (todos AND).
- Paginación vuelve a página 1 al cambiar cualquier filtro.

Resultado negativo:
- Si "Mora ≥ días = 9999" → 0 resultados → estado vacío.


PRUEBA H.3 — Acciones rápidas: Llamar / WhatsApp

Pasos:
1. En una fila con celular → click ícono teléfono → debe abrir el discador (`tel:`).
2. Click ícono WhatsApp → debe abrir wa.me en nueva pestaña con mensaje precargado:
   "Hola {razón social}, le contactamos por su saldo vencido de Gs. {saldo}. ¿Cuándo podría regularizar?"
3. En una fila SIN teléfono → ambos botones deben verse deshabilitados (gris).

Resultado negativo:
- Toast warning "Sin teléfono registrado" si se intenta clickear deshabilitado.
- Para WhatsApp, número se normaliza a formato internacional (595…).


PRUEBA H.4 — Ver historial de cliente (drawer in-place)

Pasos:
1. Click ícono ojo en una fila.
2. Debe abrirse el `ClienteHistorialDrawer` SIN salir de Mesa de Gestión.
3. Verificar 5 secciones: datos del cliente, gestión activa de mora (si hay), refinanciaciones (si hay), timeline de gestiones (últimas 8), panel de facturas con cuotas.
4. Expandir una factura a crédito con cuotas vencidas → ver KPIs Vencido / Por vencer / Pagadas y chips de filtro "Todas / Vencidas / Por vencer / Pagadas" — debe arrancar pre-seleccionado en "Vencidas".
5. Cambiar a "Pagadas" → ver solo cuotas con estado pagado y fecha de pago.
6. La tabla de cuotas debe permitir scroll si son muchas (header sticky).
7. Cerrar drawer → volver a la Mesa con filtros y página intactos.


PRUEBA H.5 — Registrar gestión simple (sin promesa)

Pasos:
1. Click ícono "Registrar gestión" en una fila.
2. Verificar header: "Cliente: {razon_social} ({RUC})" — NUNCA debe aparecer UUID.
3. Seleccionar Tipo = "Llamada", Resultado = "No atiende".
4. Escribir Observación: "Llamado a las 14:30, repica y no atienden".
5. Próxima acción: "Re-llamar"; Próxima fecha: hoy + 2.
6. Click "Registrar gestión".

Resultado esperado:
- Toast verde "Gestión registrada".
- La fila pasa a estado "En gestión" (gestión < 7 días).
- "Última gestión" muestra fecha/hora y badge "Yo".
- En el timeline del cliente (drawer) aparece la nueva entrada arriba.

Resultado negativo:
- Si NO hay cliente → toast rojo "Cliente no definido".
- Si el backend devuelve 403 (sin permiso REGISTRAR) → toast con mensaje del backend.


PRUEBA H.6 — Registrar gestión + promesa unificada (Opción A) ⭐

Dónde: Mesa de Gestión → "Registrar gestión" en cliente con cuotas vencidas

Pasos:
1. Abrir modal de gestión.
2. Seleccionar Resultado = "Promesa de pago" → el modal **expande** a `maxWidth=md` mostrando el bloque "Detalle del compromiso de pago".
3. Verificar tabla de cuotas pendientes cargadas (debe llamar `getCuotasPendientes`).
4. Botón "Solo vencidas" → marca todas las cuotas con días > 0.
5. Botón "Todas" → marca todas; click otra vez → "Limpiar".
6. Seleccionar 2 cuotas vencidas manualmente.
7. Verificar que "Saldo seleccionado" en el footer suma los saldos correctos.
8. Verificar que "Monto prometido" se autocompleta con la suma de saldos.
9. Editar monto a un valor menor (ej. 80% del saldo) → helper text aparece: "Si difiere del saldo total, se prorratea entre cuotas".
10. Cambiar Tipo de evidencia → "WhatsApp".
11. Cambiar Pagador → "Garante" → debe aparecer Autocomplete obligatorio.
12. Buscar y seleccionar un cliente como garante.
13. Click "Registrar gestión + promesa".

Resultado esperado:
- Toast: "Gestión + 2 promesa(s) registradas".
- Una transacción crea: 1 cob_gestion + 2 promesas_pago (una por cuota seleccionada).
- Cada promesa tiene `monto_prometido` prorrateado por saldo (ej. cuota A saldo 100k + cuota B saldo 50k, monto total 120k → A=80k, B=40k).
- Cada promesa tiene `tipo_evidencia=WHATSAPP`, `pagador_tipo=GARANTE`, `pagador_cliente_id={garante.id}`.
- `cob_gestion.promesa_creada_id` apunta a la PRIMERA promesa creada (compat).
- En la fila, columna "Promesas" ahora muestra chip verde "2" (vigentes).
- Filtro "Con promesa vigente" debe incluir al cliente.

Resultado negativo:
- Sin cuotas seleccionadas → botón "Registrar gestión + promesa" deshabilitado.
- Pagador GARANTE/TERCERO sin selección → toast rojo "Indicá el garante/tercero pagador".
- Cuotas de otro cliente → backend devuelve 400 "Alguna cuota no existe o no pertenece al cliente/empresa".


PRUEBA H.7 — Tab "Promesas de pago" embebido

Dónde: Mesa de Gestión → tab "Promesas de pago"

Pasos:
1. Verificar que carga el listado completo de promesas con sus propios filtros (estado, fechas, cobrador, cliente) y botón "Nueva promesa".
2. Acciones por fila: "Marcar cumplida", "Marcar incumplida", "Renegociar", "Cancelar" (gated por permisos respectivos).
3. Crear una "Nueva promesa" desde acá (caso atípico: promesa sin gestión previa).
4. Volver al tab "Bandeja de gestión" — el estado de cliente debe reflejar el cambio de promesas.

Resultado esperado:
- Tab "Promesas" muestra el mismo contenido funcional que la vieja ruta `/cobranzas/promesas`.
- Cambios hechos en este tab se reflejan en la worklist (refetch).


PRUEBA H.8 — Reporte de productividad

Dónde: Reportes → Cobranzas → Productividad de Cobradores

Pasos:
1. Abrir con rango "inicio de mes → hoy".
2. Verificar KPIs: Cobradores activos / Total gestiones / Efectivas / Tasa efectividad.
3. Tabla muestra una fila por cobrador con: total, efectivas, chip de tasa color-coded (verde ≥60 / ámbar ≥30 / rojo <30), conteo por tipo de gestión.
4. Click "Exportar" → descarga XLSX con sheet "Productividad" y columnas: cobrador, total, efectivas, tasa, columnas por tipo y por resultado.

Resultado esperado:
- Gestiones efectivas = COBRADO_TOTAL + COBRADO_PARCIAL + PROMESA_PAGO.
- Sin gestiones en el rango → estado vacío con ícono inbox.

Resultado negativo:
- Usuario sin `COB_WFL_REPORTE_PRODUCTIVIDAD` → AccesoRestringido.


PRUEBA H.9 — Edición y anulación de gestión

Pasos:
1. Como autor, ese mismo día, editar una gestión recién creada (modificar observación) → ✅ permitido.
2. Otro usuario intenta editar la misma gestión el mismo día → ❌ ForbiddenException "Solo el autor puede editar dentro del mismo día".
3. Al día siguiente, solo un supervisor con permiso `COB_WFL_GESTION_EDITAR` (no autor) puede editarla.
4. Anular una gestión con motivo "carga errónea" → flag `anulada=true`, `motivo_anulacion`, `anulada_at`, `anulada_por_id`.
5. Gestiones anuladas NO cuentan en productividad ni aparecen en timeline (a menos que se pase `incluir_anuladas=true`).


PRUEBA H.10 — Auditoría

Pasos:
1. Registrar una gestión → revisar log: action=CREATE, entity_type=cob_gestion.
2. Editar → action=UPDATE.
3. Anular → action=DELETE con descripción incluyendo motivo.
4. Crear gestión + promesa unificada → debe haber un log CREATE de cob_gestion (las N promesas no se auditan individualmente; la traza queda en `cob_gestion.promesa_creada_id`).


PRUEBA H.11 — Performance worklist con dataset grande

Pasos:
1. Empresa con ~3000 clientes activos y ~10.000 cuotas vencidas.
2. Abrir Mesa de Gestión → resumen + worklist primera página.
3. Medir tiempo de respuesta del endpoint `/cobranzas/mesa-gestion/worklist` desde Network.

Resultado esperado:
- < 2 segundos en primera carga (CTEs + índices `idx_cob_gestion_cliente` + `idx_clientes_cobrador_id`).
- Paginación sin degradación (LIMIT/OFFSET sobre la CTE).


═══════════════════════════════════════════════════════════════════
CHECKLIST FINAL — Antes de marcar como aprobado
═══════════════════════════════════════════════════════════════════

[ ] Todas las pruebas A.* pasan
[ ] Todas las pruebas B.* pasan
[ ] Todas las pruebas C.* pasan
[ ] Todas las pruebas D.* pasan
[ ] Todas las pruebas E.* pasan
[ ] Cruces F.* funcionan end-to-end
[ ] Casos negativos G.* dan los errores esperados
[ ] PDFs (gestión mora y refinanciación) abren correctamente y se descargan
[ ] Auditoría registra todas las acciones críticas
[ ] Permisos por perfil bloquean correctamente endpoints y botones
[ ] Migraciones aplicadas: `npx prisma migrate deploy` sin pendientes
[ ] Seed de seguridad ejecutado tras nuevos permisos: `npx ts-node ... seed-cli.ts catalogo` — verificar que el módulo `COBRANZAS` quedó con el submódulo `COB_PROMESAS` y sus 7 privilegios (`COB_PRM_PROMESA_VER/CREAR/EDITAR/CANCELAR/RENEGOCIAR`, `COB_PRM_AUTORIZAR_VENTA_CON_INCUMPLIMIENTOS`, `COB_PRM_REPORTE_CUMPLIMIENTO`)
[ ] Reportes de cobranzas viven en `/reportes/cobranzas/*` — verificar que **"Cumplimiento de Promesas"** aparece bajo `Reportes → Cobranzas` (no en el sidebar de cobranzas)
[ ] Todas las pruebas H.* pasan (Mesa de Gestión + Workflow)
[ ] Seed actualizado incluye submódulo `COB_MESA_GESTION` con privilegios `COB_MG_WORKLIST_VER`, `COB_MG_PASAR_A_RUTA`, `COB_MG_EXPORTAR`, y submódulo `COB_WORKFLOW` con `COB_WFL_GESTION_VER/REGISTRAR/EDITAR/ANULAR`, `COB_WFL_REPORTE_PRODUCTIVIDAD`, `COB_WFL_VER_GESTIONES_AJENAS`
[ ] Sidebar de Cobranzas tiene **"Mesa de Gestión"** y **"Mora avanzada (legal)"** — Promesas YA NO aparece como item (vive como tab dentro de Mesa de Gestión)
[ ] Modal de gestión muestra nombre del cliente con RUC entre paréntesis — verificar que NUNCA aparece el UUID en el header
[ ] Cuando resultado=Promesa de pago, el modal expande con selector de cuotas + evidencia + pagador (Opción A unificada)
[ ] Backend `GestionesService.create` crea N promesas con prorrateo cuando viene `promesa_cuota_ids[]`, mantiene path legacy para app móvil
