Plan de Pruebas — Módulo de Recibos Multi-Factura, NC, Retenciones y Mora

---

Conceptos clave antes de empezar

┌──────────────────────┬──────────────────────────────────────────────────────────────────────────────────┐
│ Término              │ Qué es en la práctica                                                            │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Recibo multi-factura │ Un comprobante de cobro que imputa pagos a una o varias facturas a la vez       │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ FIFO                 │ Las facturas más antiguas se cancelan primero (orden por vencimiento)            │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Nota de Crédito (NC) │ Documento que reduce la deuda del cliente. ESTRICTO = sólo sobre factura origen; │
│                      │ FLEXIBLE = aplicable a cualquier factura del mismo cliente                        │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Saldo a favor        │ Excedente del cliente. Se usa como medio de pago o queda como crédito             │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Retención recibida   │ Comprobante de retención que el cliente entrega (IVA o RENTA). Reduce la cobranza │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Interés moratorio    │ Cargo automático por cuota vencida según `config_mora` (tasa, gracia, base)      │
├──────────────────────┼──────────────────────────────────────────────────────────────────────────────────┤
│ Comisión cobranza    │ Comisión del cobrador, % sobre el total cobrado del recibo                       │
└──────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘

---

Flujo obligatorio antes de cualquier prueba

1. Empresa de prueba con `config_mora.activo=true`, tasa 1.5% diaria, gracia 3 días, base capital
2. Cliente PRUEBA con: 1 factura contado, 1 factura crédito 3 cuotas (cuota 1 vencida), 1 NC con saldo
3. Cuentas tesorería: Caja Efectivo, Banco BNF, Cheques en Cartera
4. Cobrador asignado con `comision_cobranza = 5`
5. Usuarios: cajero@, cobrador@, tesorero@, gerente@, contador@ con permisos según matriz §5

---

PRUEBA 1 — Listar facturas pendientes del cliente (wizard)

Dónde: Finanzas → Recibos → Nuevo Recibo → seleccionar cliente

Pasos:

1. Abrir el panel Recibos
2. Clic en "+ Nuevo Recibo"
3. Seleccionar cliente PRUEBA en el autocomplete
4. Esperar respuesta de `GET /recibos-multi/cliente/:id/pendientes`

Resultado esperado:

- Se listan todas las facturas con saldo pendiente del cliente
- Orden FIFO: por fecha de vencimiento ascendente
- Cada fila muestra: N° factura, fecha emisión, fecha vencimiento, total, saldo
- Las facturas a crédito aparecen como expandibles con sus cuotas
- Si no hay facturas pendientes → empty state "Sin facturas pendientes"

Verificar en BD:
SELECT id, dnumdoc, saldo_pendiente FROM factura_cab WHERE cliente_id = '<id>' AND saldo_pendiente > 0;

---

PRUEBA 2 — Preview FIFO sin persistir

Dónde: Wizard Recibo → completar montos → ver panel "Preview"

Escenario: Cliente con 3 facturas vencidas de Gs. 500.000 c/u. Se ingresa pago de Gs. 1.200.000.

Pasos:

1. Tildar las 3 facturas
2. Ingresar EFECTIVO Gs. 1.200.000 como medio de pago
3. Verificar el panel de Preview lateral

Resultado esperado:

┌──────────────┬─────────┬───────────┬─────────────┐
│ Factura      │ Saldo   │ A imputar │ Saldo final │
├──────────────┼─────────┼───────────┼─────────────┤
│ 0000001 (más antigua) │ 500.000 │ 500.000 │ 0           │
├──────────────┼─────────┼───────────┼─────────────┤
│ 0000002      │ 500.000 │ 500.000   │ 0           │
├──────────────┼─────────┼───────────┼─────────────┤
│ 0000003      │ 500.000 │ 200.000   │ 300.000     │
└──────────────┴─────────┴───────────┴─────────────┘

- Total imputado: 1.200.000
- Diferencia: 0 (calza exacto, oculta sugerencia)
- Indicador "Balanceado ✓" en verde
- No se persiste nada en BD (es sólo cálculo)

---

PRUEBA 3 — Preview con excedente → Saldo a Favor

Escenario: Total deuda Gs. 1.500.000. Pago Gs. 2.000.000.

Resultado esperado:

- Las 3 facturas quedan saldadas en preview
- Panel inferior muestra "Diferencia: Gs. 500.000"
- Selector `diferencia_tipo` aparece y sugiere "SALDO_FAVOR" por defecto
- Al confirmar → se generará registro en `saldos_favor_cliente`

---

PRUEBA 4 — Preview con pago parcial

Escenario: Total deuda Gs. 1.500.000. Pago Gs. 1.000.000.

Resultado esperado:

- Factura más antigua queda saldada
- Segunda factura queda con saldo parcial
- Tercera factura no se toca
- `diferencia_tipo` sugiere "PAGO_PARCIAL"

---

PRUEBA 5 — Crear recibo simple (1 factura, efectivo)

Dónde: Wizard Recibo → confirmar

Pasos:

1. Cliente con factura de Gs. 500.000 contado pendiente
2. Tildar la factura
3. Medio de pago: EFECTIVO Gs. 500.000, cuenta tesorería "Caja Efectivo"
4. Clic en "Confirmar Recibo"

Resultado esperado:

- Toast verde "Recibo N° R-0000001 creado"
- Redirige a vista de detalle
- La factura pasa a estado `PAGADA`, `saldo_pendiente = 0`

Verificar en BD:
SELECT estado, numero, total_pagado FROM recibos_multi WHERE id = '<id>';
SELECT estado, saldo_pendiente FROM factura_cab WHERE id = '<factura_id>';
SELECT * FROM tes_movimientos WHERE origen_tipo = 'recibos_multi' AND origen_id = '<id>';
-- Debe haber 1 movimiento INGRESO CONFIRMADO de Gs. 500.000

---

PRUEBA 6 — Crear recibo multi-factura con varios medios de pago

Escenario: Cliente con 2 facturas de Gs. 1.000.000 c/u. Se cobra Gs. 2.000.000 mezclando: efectivo 500.000 + transferencia 1.000.000 + cheque 500.000.

Pasos:

1. Tildar las 2 facturas
2. Agregar 3 medios de pago:
   - EFECTIVO Gs. 500.000 → Caja Efectivo
   - TRANSFERENCIA Gs. 1.000.000 → Banco BNF, referencia "TRX-9988"
   - CHEQUE Gs. 500.000 → Cheques en Cartera, N° 12345
3. Confirmar

Resultado esperado:

- Recibo creado con `total_pagado = 2.000.000`
- 3 filas en `recibos_medios_pago`
- 3 movimientos en `tes_movimientos` (uno por cuenta), todos `estado=CONFIRMADO`
- Saldo de cada cuenta tesorería incrementado por el monto correspondiente
- Cheque registrado en `tes_cheques` con `estado=EN_CARTERA`
- Ambas facturas en `estado=PAGADA`

Verificar en BD:
SELECT cuenta_id, monto FROM tes_movimientos WHERE origen_id = '<recibo_id>';
SELECT estado FROM tes_cheques WHERE movimiento_id IN (SELECT id FROM tes_movimientos WHERE origen_id = '<recibo_id>');

---

PRUEBA 7 — Aplicar Nota de Crédito modo ESTRICTO

Escenario: Cliente tiene factura A con saldo Gs. 1.000.000 y NC sobre esa misma factura con saldo Gs. 300.000. Se quiere cancelar la factura aplicando la NC + efectivo.

Pasos:

1. En el wizard, tildar factura A
2. Sección "Notas de Crédito" → aplicar NC Gs. 300.000 sobre factura A
3. Medio de pago: EFECTIVO Gs. 700.000
4. Confirmar

Resultado esperado:

- Recibo se crea OK (no requiere `COB_REC_NC_FLEXIBLE`)
- Factura A queda en `PAGADA`, saldo 0
- NC: `saldo_disponible` decrementado en 300.000

Verificar en BD:
SELECT saldo_disponible FROM nota_credito_cab WHERE id = '<nc_id>';
SELECT * FROM recibos_nc_aplicadas WHERE recibo_id = '<recibo_id>';

---

PRUEBA 8 — Aplicar NC modo FLEXIBLE (otra factura)

Escenario: NC fue emitida originalmente sobre factura A, pero se quiere aplicar a factura B del mismo cliente.

Pasos:

1. Tildar factura B
2. Aplicar NC apuntando a factura B (modo flexible)
3. Confirmar

Resultado esperado SIN permiso `COB_REC_NC_FLEXIBLE`:

- Error 403 "No tiene permiso para aplicar NC en modo flexible"

Resultado esperado CON permiso (Gerente/Contador):

- Recibo se crea con `recibos_nc_aplicadas.factura_id_destino = B`
- Factura B reduce su saldo
- Factura A queda intacta
- NC `saldo_disponible` decrementado

---

PRUEBA 9 — Registrar retenciones recibidas (IVA + RENTA)

Escenario: Factura A de Gs. 1.100.000 (IVA 100.000). El cliente entrega: retención IVA Gs. 30.000 + retención RENTA Gs. 20.000. El resto en efectivo.

Pasos:

1. Tildar factura A
2. Sección "Retenciones" → agregar 2 retenciones:
   - IVA: monto 30.000, comprobante "001-001-001", timbrado "12345678", fecha hoy
   - RENTA: monto 20.000, comprobante "001-001-002", timbrado "12345678", fecha hoy
3. Medio de pago: EFECTIVO Gs. 1.050.000
4. Confirmar

Resultado esperado:

- Recibo creado, factura A saldo 0
- 2 filas en `recibos_retenciones`
- `factura_cab.monto_retencion` actualizado con la suma

Prueba de validación negativa:

- Repetir el `numero_comprobante` de una retención ya registrada → error 400 "Comprobante ya registrado"
- Retención IVA con monto > IVA de la factura → error 400

Verificar en BD:
SELECT tipo, monto, numero_comprobante FROM recibos_retenciones WHERE recibo_id = '<id>';

---

PRUEBA 10 — Cobro con interés moratorio automático (F2.D)

Escenario: Factura crédito con cuota 1 vencida hace 10 días. Capital de cuota: Gs. 1.000.000. `config_mora`: tasa 1.5% diaria, gracia 3 días, base capital.

Cálculo esperado: días mora = 10 − 3 = 7 → interés = 1.000.000 × 1.5% × 7 = Gs. 105.000

Pasos:

1. Tildar la cuota 1 de la factura crédito
2. Verificar que el preview muestra columna "Interés mora: 105.000"
3. Monto a pagar sugerido: 1.105.000 (capital + interés)
4. Pagar en efectivo
5. Confirmar

Resultado esperado:

- Recibo se crea con `recibos_facturas.monto_interes = 105.000`
- Cuota queda en `PAGADA`
- Total recibo refleja capital + interés

Pruebas adicionales:

- Cuota vencida 2 días (dentro de gracia) → `monto_interes = 0`
- Con `config_mora.monto_minimo_mora = 200.000` → si cálculo da 105.000, interés baja a 0
- Con `config_mora.monto_maximo_mora = 50.000` → interés se topa en 50.000
- Con `config_mora.activo = false` → interés 0 aunque haya días de atraso

Verificar en BD:
SELECT monto_pagado, monto_interes FROM recibos_facturas WHERE recibo_id = '<id>';

---

PRUEBA 11 — Comisión automática del cobrador (F2.C)

Escenario: Cobrador "Juan" con `comision_cobranza = 5%`. Cobra recibo de Gs. 2.000.000.

Pasos:

1. En el wizard, seleccionar cobrador "Juan"
2. Crear el recibo con total Gs. 2.000.000

Resultado esperado:

- Recibo creado normalmente
- Nueva fila en `comisiones` con:
  - `tipo = 'cobranza'`
  - `monto_base = 2.000.000`
  - `porcentaje_aplicado = 5`
  - `monto_comision = 100.000`
  - `estado = 'pendiente'`
  - `observaciones = 'RECIBO_MULTI:<id-recibo>'`

Casos a validar:

- Cobrador con `comision_cobranza = 0` → NO se crea comisión
- Recibo sin cobrador asignado → NO se crea comisión

Verificar en BD:
SELECT monto_comision, estado FROM comisiones WHERE observaciones = 'RECIBO_MULTI:<id-recibo>';

---

PRUEBA 12 — Diferencia tipo AJUSTE_MANUAL (permiso)

Escenario: Se cobra Gs. 1.000.000 pero la deuda es Gs. 1.050.000. El gerente decide cerrar con ajuste manual (perdona Gs. 50.000).

Pasos:

1. Imputar Gs. 1.000.000
2. Diferencia Gs. 50.000 → `diferencia_tipo = AJUSTE_MANUAL`
3. Motivo: "Descuento aprobado por gerencia"
4. Confirmar

Resultado esperado SIN permiso `COB_REC_AJUSTE_MANUAL`:

- Error 403 "No tiene permiso para aplicar ajuste manual"

Resultado esperado CON permiso (Gerente):

- Recibo creado con `diferencia_tipo = AJUSTE_MANUAL`, `diferencia_motivo` guardado
- Factura queda saldada (sin la diferencia)

---

PRUEBA 13 — Anular recibo con efectivo (caso simple)

Dónde: Detalle del recibo → botón "Anular"

Pasos:

1. Abrir un recibo CONFIRMADO pagado con efectivo
2. Clic en "Anular"
3. Motivo: "Error de carga"
4. Confirmar

Resultado esperado:

- Recibo pasa a `estado=ANULADO`
- Factura recupera `saldo_pendiente` original y `estado=PENDIENTE` o `PARCIAL`
- Movimientos de tesorería pasan a `estado=ANULADO` con `motivo_anulacion`
- `tes_cuentas.saldo_actual` se decrementa por el monto del recibo
- Toast verde "Recibo anulado"

Verificar en BD:
SELECT estado FROM recibos_multi WHERE id = '<id>';
SELECT estado, saldo_pendiente FROM factura_cab WHERE id = '<factura_id>';
SELECT estado FROM tes_movimientos WHERE origen_id = '<id>';

---

PRUEBA 14 — Anular recibo con NC aplicada

Pasos:

1. Anular el recibo de la PRUEBA 7

Resultado esperado:

- NC recupera su `saldo_disponible` (se devuelve lo aplicado)
- Factura recupera saldo
- `recibos_nc_aplicadas` queda con flag/registro de reversa (auditoría)

Verificar en BD:
SELECT saldo_disponible FROM nota_credito_cab WHERE id = '<nc_id>';
-- Debe volver al valor previo al recibo

---

PRUEBA 15 — Anular recibo con CHEQUE EN_CARTERA

Escenario: Recibo con cheque que sigue en cartera (no se depositó aún).

Resultado esperado:

- Anulación procede normalmente
- El cheque pasa a `estado=ANULADO`
- Movimiento de tesorería revertido

---

PRUEBA 16 — Anular recibo con cheque DEPOSITADO (debe BLOQUEAR) (F2.B)

Escenario: Recibo con cheque que ya fue depositado o cobrado.

Pasos:

1. Crear recibo con cheque
2. Desde Tesorería, cambiar el cheque a estado `DEPOSITADO`
3. Volver al recibo y clic en "Anular"

Resultado esperado:

- Error 400 con mensaje detallado:
  "No se puede anular: hay cheques fuera de cartera. Reviértalos primero desde Tesorería: 12345 (DEPOSITADO)"
- El recibo permanece en `CONFIRMADO`
- Nada cambia en BD

Casos a validar:

- Cheque `COBRADO` → mismo bloqueo
- Cheque `RECHAZADO` → mismo bloqueo
- Cheque `ANULADO` previamente → permite anular el recibo

---

PRUEBA 17 — Anular recibo cancela la comisión (F2.C)

Pasos:

1. Crear recibo con cobrador (PRUEBA 11) → comisión `pendiente`
2. Anular el recibo

Resultado esperado:

- Comisión pasa a `estado = 'cancelada'`
- Toast verde "Recibo anulado"

Caso especial:

- Si la comisión ya estaba `liquidada` → NO se cambia (auditoría). El recibo se anula igual pero queda registrada la comisión liquidada.

Verificar en BD:
SELECT estado FROM comisiones WHERE observaciones = 'RECIBO_MULTI:<id-recibo>';

---

PRUEBA 18 — Anular recibo con SALDO A FAVOR generado

Pasos:

1. Crear recibo con excedente (PRUEBA 3) → genera saldo a favor
2. Anular el recibo

Resultado esperado:

- El saldo a favor generado se anula (estado o monto 0)
- No queda crédito disponible para el cliente

Caso especial:

- Si el saldo a favor ya fue consumido por otro recibo → error 400 "Saldo ya consumido, no se puede anular"

---

PRUEBA 19 — Doble anulación (idempotencia)

Pasos:

1. Anular un recibo
2. Intentar anularlo nuevamente

Resultado esperado:

- Segunda llamada: error 400 "Recibo ya anulado"
- Estado no cambia

---

PRUEBA 20 — Libro de Retenciones Recibidas

Dónde: Finanzas → Recibos → tab "Libro de Retenciones"

Pasos:

1. Seleccionar período "2026-05"
2. Verificar tabla con columnas: Fecha | N° Comprobante | Timbrado | Cliente RUC | Tipo (IVA/RENTA) | Monto | Recibo origen | Estado declaración
3. Verificar totales por tipo en el resumen

Resultado esperado:

- Aparecen todas las retenciones de recibos NO anulados del período
- Retenciones de recibos ANULADOS no figuran
- Total IVA y total RENTA correctos
- Exportar a CSV/Excel funciona

Verificar en BD:
SELECT tipo, SUM(monto) FROM recibos_retenciones r
JOIN recibos_multi rm ON rm.id = r.recibo_id
WHERE rm.empresa_id = 'tu-id' AND rm.estado != 'ANULADO'
  AND EXTRACT(MONTH FROM r.fecha_comprobante) = 5
  AND EXTRACT(YEAR FROM r.fecha_comprobante) = 2026
GROUP BY tipo;

---

PRUEBA 21 — Marcar retención como declarada al SET

Dónde: Libro Retenciones → acción en cada fila

Pasos:

1. Clic en "Marcar declarada" en una retención
2. Confirmar período "2026-05"

Resultado esperado:

- La fila muestra badge verde "Declarada 2026-05"
- Segundo clic muestra error 400 "Ya declarada"

Verificar en BD:
SELECT declarada_set, periodo_declaracion FROM recibos_retenciones WHERE id = '<id>';

---

PRUEBA 22 — Permisos (control de acceso)

Escenario: Verificar que los botones y endpoints respeten la matriz de permisos.

Pasos:

1. Login con usuario que tiene solo `COB_REC_VER`
2. Ir a Recibos

Resultado esperado:

- Puede ver listado y detalle de recibos
- NO ve botón "+ Nuevo Recibo" (falta `COB_REC_CREAR`)
- NO ve botón "Anular" (falta `COB_REC_ANULAR`)
- NO ve tab "Libro de Retenciones" (falta `COB_REC_LIBRO_RETENCIONES`)

3. Login con usuario sin módulo COBROS → 403 en todas las rutas

4. Intentar llamar `POST /recibos-multi` desde un usuario sin `COB_REC_CREAR` → 403 Forbidden directo del backend

Matriz mínima a validar:

┌─────────────────────────────┬──────────────────────────────────┐
│ Permiso                     │ Roles típicos                    │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_VER                 │ Cajero, Cobrador, Tesorero, Gte. │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_CREAR               │ Cajero, Cobrador, Tesorero       │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_ANULAR              │ Tesorero, Gerente                │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_NC_FLEXIBLE         │ Gerente, Contador                │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_AJUSTE_MANUAL       │ Gerente                          │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_RETENCION_REGISTRAR │ Cajero, Tesorero, Contador       │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_LIBRO_RETENCIONES   │ Contador, Gerente                │
├─────────────────────────────┼──────────────────────────────────┤
│ COB_REC_OVERRIDE_FIFO       │ Tesorero, Gerente                │
└─────────────────────────────┴──────────────────────────────────┘

---

PRUEBA 23 — Concurrencia: dos recibos sobre la misma factura

Escenario: Dos cajeros intentan cobrar la misma factura al mismo tiempo.

Pasos:

1. Cajero A abre el wizard con factura X (saldo 500.000), monto 500.000
2. Cajero B abre el wizard con factura X (saldo 500.000), monto 500.000
3. Ambos confirman simultáneamente

Resultado esperado:

- Uno de los dos recibos se crea correctamente y deja la factura en saldo 0
- El otro recibe error 400 "Saldo insuficiente para imputar"
- NO se queda la factura en saldo negativo
- NO se duplican movimientos de tesorería

---

PRUEBA 24 — Transacción atómica: rollback si falla un paso

Escenario: Simular error después de insertar el recibo pero antes de actualizar la factura.

Resultado esperado:

- Rollback total: el recibo NO queda persistido
- `recibos_facturas`, `recibos_medios_pago`, `tes_movimientos` no quedan huérfanos
- Saldos de cuentas tesorería intactos
- La factura no se modifica

Cómo provocarlo: forzar excepción en `ContabilidadIntegracionService` con datos inválidos en config_contabilidad.

---

PRUEBA 25 — Integración cruzada: tesorería refleja el recibo

Pasos:

1. Crear recibo PRUEBA 6 (3 medios de pago)
2. Ir a Tesorería → Caja Efectivo → extracto del día

Resultado esperado:

- Aparece movimiento INGRESO Gs. 500.000 con `origen_tipo = 'recibos_multi'`
- Link clickeable que navega al detalle del recibo
- Idem en Banco BNF y Cheques en Cartera

---

PRUEBA 26 — Integración cruzada: liquidación de comisiones

Pasos:

1. Crear recibo PRUEBA 11 → comisión pendiente Gs. 100.000
2. Ir a Comisiones → tab Pendientes de Liquidar

Resultado esperado:

- Aparece la comisión del recibo
- Al liquidarla, pasa a estado `liquidada`
- Si luego se anula el recibo: la comisión `liquidada` NO se cancela, queda como evidencia

---

PRUEBA 27 — Integración cruzada: cuentas por cobrar

Pasos:

1. Antes del recibo: consultar resumen de cuentas a cobrar del cliente → Gs. 2.000.000
2. Crear recibo PRUEBA 6 (cobra los 2.000.000)
3. Volver a consultar resumen

Resultado esperado:

- Saldo cliente pasa a 0
- Anular el recibo → saldo vuelve a 2.000.000

---

PRUEBA 28 — Imprimir ticket del recibo (QZ Tray)

Dónde: Detalle del recibo → botón "Imprimir"

Pasos:

1. Abrir un recibo confirmado
2. Clic en "Imprimir"

Resultado esperado:

- Se abre el diálogo de QZ Tray (si está configurado)
- Impresión en papel 80mm sin sobrepasar el ancho
- Incluye: cabecera empresa, datos cliente, facturas imputadas, medios de pago, totales, número de recibo

---

PRUEBA 29 — Smoke test end-to-end (escenario completo)

Recorrer el flujo en una empresa limpia, en orden:

1. Crear cliente y emitir factura crédito 3 cuotas (cuota 1 con vencimiento pasado)
2. Generar NC parcial sobre esa factura
3. Login Cajero → crear recibo pagando cuota 1 con efectivo + aplicando NC parcial → verificar interés moratorio calculado, factura cuota 1 PAGADA, NC saldo disminuido
4. Verificar en Tesorería el movimiento de caja
5. Verificar en Comisiones la comisión generada del cobrador
6. Login Contador → consultar Libro de Retenciones del mes (si se añadió una retención)
7. Login Tesorero → anular el recibo → cheque en cartera OK, saldos vuelven, comisión cancelada
8. Intentar anular dos veces → error
9. Crear segundo recibo cubriendo cuota 1 + cuota 2 con mora recalculada
10. Verificar `monto_interes` en detalle y total final
11. Dashboard ventas: ingresos del día reflejan recibos no anulados

---

Checklist de "todo funciona"

MÓDULO RECIBOS MULTI-FACTURA

[ ] Listado de facturas pendientes ordenado FIFO por vencimiento
[ ] Preview muestra imputación correcta sin persistir
[ ] Preview marca diferencia/excedente con sugerencia de `diferencia_tipo`
[ ] Crear recibo simple con efectivo deja factura PAGADA
[ ] Crear recibo con múltiples medios genera 1 movimiento de tesorería por medio
[ ] Aplicar NC modo ESTRICTO funciona sin permiso especial
[ ] Aplicar NC modo FLEXIBLE bloqueado sin `COB_REC_NC_FLEXIBLE` (403)
[ ] Registrar retenciones IVA + RENTA actualiza `monto_retencion` de la factura
[ ] Retención con número de comprobante duplicado es rechazada
[ ] Interés moratorio calculado correctamente: capital × tasa × días (mora − gracia)
[ ] Mora respeta `monto_minimo_mora` y `monto_maximo_mora`
[ ] Mora 0 si cuota dentro del período de gracia
[ ] Comisión cobrador se genera con `monto_base = total_pagado` y `tipo = 'cobranza'`
[ ] Comisión NO se genera si `comision_cobranza = 0` o sin cobrador
[ ] Ajuste manual bloqueado sin `COB_REC_AJUSTE_MANUAL`
[ ] Anular recibo restaura saldo de facturas y cuentas tesorería
[ ] Anular recibo libera saldo de NC aplicadas
[ ] Anular recibo BLOQUEADO si hay cheques fuera de cartera (F2.B)
[ ] Anular recibo cancela comisión pendiente; respeta comisión liquidada
[ ] Anular recibo invalida saldo a favor generado
[ ] Doble anulación da error 400
[ ] Libro de Retenciones muestra sólo recibos no anulados
[ ] Marcar retención declarada al SET es idempotente (2da llamada falla)
[ ] Permisos: usuario sin `COB_REC_VER` no entra al módulo
[ ] Permisos: botones de acción ocultos según el perfil del usuario
[ ] Endpoint POST devuelve 403 si falta el permiso requerido
[ ] Concurrencia: dos recibos simultáneos sobre la misma factura → uno falla
[ ] Rollback total si una etapa de la transacción falla
[ ] Movimiento de tesorería referencia el recibo (`origen_tipo='recibos_multi'`)
[ ] Comisión del recibo aparece para liquidar en módulo Comisiones
[ ] Cuentas por cobrar reflejan el saldo nuevo tras el cobro
[ ] Ticket impreso respeta ancho 80mm

---

⚠️ Nota mora: La columna `monto_interes` en `recibos_facturas` SOLO se calcula si el pago se imputa a una cuota específica (`cuota_id`). Pagos directos a la factura (contado) no generan mora. Si los intereses se cobran fuera del recibo (recargo manual), no figurarán en `monto_interes`.

⚠️ Nota NC flexible: El modo FLEXIBLE rompe la trazabilidad documental (NC apunta a factura distinta de la original). Por eso requiere permiso de Gerente/Contador. En entornos de auditoría estricta dejarlo restringido.

⚠️ Nota cheques: La validación F2.B sólo bloquea cheques `DEPOSITADO`, `COBRADO` o `RECHAZADO`. Los cheques `EN_CARTERA` se anulan automáticamente. Si el cheque ya está `ANULADO` previamente, también se permite anular el recibo (es idempotente).

⚠️ Nota comisión: El campo `observaciones` con formato `RECIBO_MULTI:<id>` es la única forma de vincular comisión↔recibo (no hay FK directa). Si se elimina manualmente esa cadena, la cancelación automática en `anular()` no funcionará.

⚠️ Nota integración contable: Si `ContabilidadIntegracionService` falla por mapeo de cuentas faltante (Caja, Clientes, Retenciones), el recibo se crea pero el asiento contable queda pendiente. Verificar logs y completar el mapeo en Contabilidad → tab Mapeo de Cuentas.

---

ESTADO DE IMPLEMENTACIÓN CONSOLIDADO (actualizado 2026-05-19)

┌─────────────────────────────────────────────────────┬──────────────────┬────────────────────────────────────────────────────────┐
│ Funcionalidad                                       │ Estado           │ Detalle                                                 │
├─────────────────────────────────────────────────────┼──────────────────┼────────────────────────────────────────────────────────┤
│ Listado/filtros/paginación de recibos               │ ✅ Implementado  │ GET /recibos-multi con filtros estado/fechas/cliente   │
│ Preview FIFO sin persistir                          │ ✅ Implementado  │ POST /recibos-multi/preview                            │
│ Facturas pendientes del cliente (FIFO)              │ ✅ Implementado  │ GET /cliente/:id/pendientes                            │
│ NC disponibles del cliente                          │ ✅ Implementado  │ GET /cliente/:id/nc-disponibles                        │
│ Saldos a favor disponibles                          │ ✅ Implementado  │ GET /cliente/:id/saldos-favor                          │
│ Crear recibo multi-factura (transacción atómica)    │ ✅ Implementado  │ POST /recibos-multi                                    │
│ NC modo ESTRICTO                                    │ ✅ Implementado  │ Sin permiso especial                                   │
│ NC modo FLEXIBLE                                    │ ✅ Implementado  │ Requiere COB_REC_NC_FLEXIBLE                           │
│ Retenciones IVA + RENTA                             │ ✅ Implementado  │ Validación unicidad de comprobante                     │
│ Saldos a favor (generación + consumo)               │ ✅ Implementado  │ diferencia_tipo=SALDO_FAVOR                            │
│ Diferencia tipo AJUSTE_MANUAL                       │ ✅ Implementado  │ Requiere COB_REC_AJUSTE_MANUAL + motivo                │
│ Anular recibo (reversa total)                       │ ✅ Implementado  │ POST /:id/anular                                       │
│ F2.A — Tesorería en crear/anular                    │ ✅ Implementado  │ tes_movimientos con origen_tipo='recibos_multi'        │
│ F2.B — Bloqueo anulación con cheques fuera cartera  │ ✅ Implementado  │ Mensaje detallado con N° + estado                      │
│ F2.C — Comisión automática del cobrador             │ ✅ Implementado  │ observaciones='RECIBO_MULTI:<id>'                      │
│ F2.D — Intereses moratorios por cuota vencida       │ ✅ Implementado  │ Usa config_mora; respeta gracia/min/max               │
│ Libro de Retenciones (mensual)                      │ ✅ Implementado  │ GET /retenciones/libro?periodo=YYYY-MM                 │
│ Marcar retención declarada al SET                   │ ✅ Implementado  │ PATCH /retenciones/:id/declarar                        │
│ Integración contable (asientos)                     │ ✅ Implementado  │ ContabilidadIntegracionService.integrarReciboMulti     │
│ Permisos granulares (8 codigos COB_REC_*)           │ ✅ Implementado  │ Privilegios en seed.ts; matriz documental              │
│ Frontend wizard RecibosPanel                        │ ✅ Implementado  │ Cliente, facturas, NC, retenciones, medios de pago     │
│ F2.5 — Badge descuentos pendientes PanelSupervisor  │ ⏳ Pendiente     │ Tarea #10 — UI de aprobación de autorizaciones         │
└─────────────────────────────────────────────────────┴──────────────────┴────────────────────────────────────────────────────────┘
