# Plan: Recibos Multi-Factura, Notas de Crédito Aplicadas y Retenciones

> **Estado**: En producción — Fases 1–4 completas, F5 (retenciones emitidas) y refinamientos abiertos
> **Fecha**: 2026-05-19 (creación) / 2026-05-21 (última actualización)
> **Autor**: Análisis técnico-funcional consensuado con cliente
> **Módulos impactados**: `cobros`, `nota-creditos`, `tesoreria`, `contabilidad`, `clientes`, (preparación) `compras`/`proveedores`
> **Ubicación frontend**: **Finanzas → Recibos** (pantalla nueva, no reemplaza el cobro single-factura existente)

---

## 1. Contexto y problema

### 1.1 Situación actual

- El sistema permite generar un recibo asociado a **una sola factura** (puede incluir varias cuotas de esa factura).
- Las **notas de crédito (NC)** hoy nacen ligadas a una factura específica y se aplican sólo contra ella.
- **No existe** soporte para retenciones (ni recibidas ni emitidas).
- No hay forma de procesar una "orden de pago" del cliente que cubra **varias facturas + NC + retenciones** en un único acto contable.

### 1.2 Caso real descrito por el cliente

> "Viene una orden de pago de un local del cliente, te pasan esta orden con todas las facturas que te van a pagar y también todas las notas de crédito que te van a descontar, independientemente de que esas NC correspondan o no a esas facturas. Las NC y retenciones se van descontando de las facturas."

### 1.3 Objetivo

Permitir registrar **un recibo que cancela total o parcialmente N facturas del mismo cliente**, aplicando N notas de crédito (configurable estricto/flexible) y N retenciones (una por factura), con reversa total automática, integración con Tesorería existente y trazabilidad contable completa. Además, dejar la base de datos lista para emisión de retenciones a proveedores en una fase posterior.

---

## 2. Decisiones de diseño confirmadas

| #   | Decisión                                                    | Valor confirmado                                                                                                                                     |
| --- | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1   | Soporte de retenciones                                      | **Ambos casos**: agente que emite + contribuyente que recibe                                                                                         |
| 2   | Modelo de NC                                                | **Configurable** por empresa y/o permiso: estricto (NC ↔ factura origen) o flexible (saldo a favor del cliente, aplicable contra cualquier factura) |
| 3   | Diferencias en recibo (cobrado ≠ facturas − NC − retención) | Permitir: saldo a favor (anticipo), pago parcial, ajuste manual con motivo. **Pagos parciales por cuota** habilitados                                |
| 4   | Orden de imputación NC/retención sobre facturas             | **FIFO automático con override manual** antes de confirmar                                                                                           |
| 5   | Granularidad retención                                      | **Una retención por factura** (correcto fiscalmente: el comprobante de retención SET referencia una factura específica)                              |
| 6   | Intereses moratorios                                        | Línea separada **dentro del mismo recibo**                                                                                                           |
| 7   | Comisión de cobrador                                        | Calculada sobre **total del recibo**                                                                                                                 |
| 8   | Cliente por recibo                                          | **Un recibo = un cliente**                                                                                                                           |
| 9   | Anulación                                                   | **Reversa total automática** (no anulación parcial en v1)                                                                                            |
| 10  | Timbrado del recibo                                         | **Comprobante interno** sin timbrado SET                                                                                                             |
| 11  | Integración Tesorería                                       | Reutilizar módulo existente (reglas de tesorería → cuenta destino) — ver `plan-tesoreria-bancos.md`                                                  |
| 12  | Retenciones recibidas                                       | Carga **manual** sin adjunto + **libro de retenciones recibidas**                                                                                    |
| 13  | Retenciones emitidas                                        | Sólo dejar **DB preparada**; flujo completo de pago a proveedores queda para plan posterior                                                          |
| 14  | UI                                                          | **Pantalla nueva** en `Finanzas → Recibos`, convive con flujo single-factura existente                                                               |
| 15  | Migración históricos                                        | **No migrar** — recibos viejos quedan en su modelo actual                                                                                            |
| 16  | Permisos                                                    | **Códigos nuevos específicos** (no reusar `COB_*` existentes)                                                                                        |

---

## 3. Marco conceptual (para alinear vocabulario)

### 3.1 Recibo multi-factura

Documento interno (sin timbrado) que registra el cobro de un cliente, donde:

- `Σ líneas de factura cobradas + intereses` (lo que entra) = `Σ medios de pago + Σ NC aplicadas + Σ retenciones recibidas + ajuste/anticipo` (lo que se imputa)

### 3.2 Nota de crédito como crédito disponible

La NC siempre **nace ligada a una factura** (por exigencia SET). Pero contablemente, según parámetro de empresa, puede convertirse en **saldo a favor** del cliente y aplicarse contra cualquier factura pendiente del mismo cliente al momento del cobro.

| Modo       | Comportamiento                                                                       |
| ---------- | ------------------------------------------------------------------------------------ |
| `ESTRICTO` | NC sólo aplicable contra la factura origen (la que modifica)                         |
| `FLEXIBLE` | NC convertida en saldo a favor; aplicable contra cualquier factura del mismo cliente |

Selector: parámetro de empresa + permiso `COB_REC_NC_FLEXIBLE` para sobrescribir caso a caso.

### 3.3 Retención recibida

Cuando un cliente (gran contribuyente / Estado) **nos retiene** IVA y/o Renta al pagarnos. Trae un comprobante de retención timbrado del cliente, que debemos:

1. Registrar como **descuento** dentro del recibo (afecta saldo de la factura).
2. Acumular en un **libro/reporte de retenciones recibidas** para descontar del IVA/Renta a pagar mensual.

### 3.4 Retención emitida (preparación DB)

Cuando **somos agente de retención** designado por SET y al pagar a un proveedor le retenemos IVA/Renta. **Fuera de alcance funcional**: sólo dejamos tablas + catálogos preparados para la fase posterior de "Pagos a Proveedores".

### 3.5 Diferencias en el cobro

| Tipo               | Resultado                                                                                             |
| ------------------ | ----------------------------------------------------------------------------------------------------- |
| Cobrado = imputado | Recibo cerrado                                                                                        |
| Cobrado > imputado | **Saldo a favor del cliente** (anticipo) → queda disponible para futuros cobros                       |
| Cobrado < imputado | **Pago parcial** de la última factura (o la que el usuario elija) → factura queda con saldo pendiente |
| Diferencia menor   | **Ajuste manual con motivo** (campo obligatorio)                                                      |

---

## 4. Arquitectura de datos

### 4.1 Tablas nuevas

#### `recibos`

Cabecera del recibo multi-factura.

```
id                  UUID PK
empresa_id          UUID FK
cliente_id          UUID FK
numero              VARCHAR (correlativo interno por empresa, formato configurable)
fecha               TIMESTAMP
sucursal_id         UUID FK
cobrador_id         UUID FK NULL
moneda              VARCHAR(3) (PYG/USD)
tipo_cambio         DECIMAL NULL
total_facturas      DECIMAL  (suma bruta de facturas/cuotas imputadas)
total_intereses     DECIMAL  (default 0)
total_nc_aplicadas  DECIMAL  (default 0)
total_retenciones   DECIMAL  (default 0)
total_pagado        DECIMAL  (suma de medios de pago + saldo a favor consumido)
diferencia          DECIMAL  (positiva = saldo a favor generado, negativa = pago parcial / ajuste)
diferencia_tipo     ENUM(SALDO_FAVOR, PAGO_PARCIAL, AJUSTE_MANUAL, EXACTO)
diferencia_motivo   TEXT NULL  (obligatorio si tipo=AJUSTE_MANUAL)
estado              ENUM(CONFIRMADO, ANULADO)
anulado_motivo      TEXT NULL
anulado_at          TIMESTAMP NULL
anulado_por         UUID NULL
created_by          UUID
created_at          TIMESTAMP
updated_at          TIMESTAMP
INDEX (empresa_id, cliente_id, fecha)
INDEX (empresa_id, numero)
```

#### `recibos_facturas`

Detalle: facturas/cuotas imputadas en el recibo.

```
id                  UUID PK
recibo_id           UUID FK CASCADE
factura_id          UUID FK (a la factura cancelada)
venta_id            UUID FK NULL (si proviene de venta a crédito)
cuota_id            UUID FK NULL (si imputa contra cuota específica)
monto_factura       DECIMAL (saldo previo de la factura/cuota)
monto_pagado        DECIMAL (cuánto se cancela)
monto_nc_aplicado   DECIMAL (default 0)
monto_retencion     DECIMAL (default 0)
monto_interes       DECIMAL (default 0)
orden               INT (orden visual / FIFO)
INDEX (recibo_id)
INDEX (factura_id)
```

#### `recibos_nc_aplicadas`

NC consumidas en el recibo (con su distribución sobre las facturas).

```
id                      UUID PK
recibo_id               UUID FK CASCADE
nota_credito_id         UUID FK
recibo_factura_id       UUID FK (a qué línea de factura imputó)
monto                   DECIMAL
modo                    ENUM(ESTRICTO, FLEXIBLE)
INDEX (recibo_id)
INDEX (nota_credito_id)
```

#### `recibos_retenciones`

Una retención recibida por factura imputada.

```
id                      UUID PK
recibo_id               UUID FK CASCADE
recibo_factura_id       UUID FK (a qué línea de factura aplica — exactamente una)
tipo                    ENUM(IVA, RENTA)
porcentaje              DECIMAL
monto                   DECIMAL
numero_comprobante      VARCHAR
timbrado                VARCHAR
fecha_comprobante       DATE
declarada_set           BOOLEAN DEFAULT false  (para el libro)
periodo_declaracion     VARCHAR(7) NULL        (YYYY-MM cuando se declara)
INDEX (recibo_id)
INDEX (empresa_id, periodo_declaracion)
```

#### `recibos_medios_pago`

Medios de pago del recibo (efectivo, cheque, transferencia, tarjeta, saldo a favor consumido).

```
id                  UUID PK
recibo_id           UUID FK CASCADE
medio               ENUM(EFECTIVO, CHEQUE, TRANSFERENCIA, TARJETA, SALDO_FAVOR, OTRO)
monto               DECIMAL
moneda              VARCHAR(3)
tipo_cambio         DECIMAL NULL
cuenta_tesoreria_id UUID FK NULL  (cuenta destino — integra con Tesorería)
cheque_id           UUID FK NULL  (si medio=CHEQUE)
referencia          VARCHAR NULL  (nro de transferencia / autorización / etc.)
saldo_favor_id      UUID FK NULL  (si medio=SALDO_FAVOR — referencia a saldos_cliente)
```

#### `saldos_cliente`

Saldos a favor del cliente (anticipos generados por diferencias positivas en recibos o NC en modo FLEXIBLE).

```
id                  UUID PK
empresa_id          UUID FK
cliente_id          UUID FK
origen              ENUM(RECIBO_DIFERENCIA, NC_FLEXIBLE, MANUAL)
origen_id           UUID  (recibo_id o nota_credito_id)
monto_original      DECIMAL
monto_disponible    DECIMAL  (se va consumiendo)
moneda              VARCHAR(3)
created_at          TIMESTAMP
INDEX (empresa_id, cliente_id, monto_disponible)
```

#### `saldos_cliente_movimientos`

Historial de consumos del saldo a favor.

```
id                  UUID PK
saldo_id            UUID FK
recibo_id           UUID FK NULL
tipo                ENUM(CARGA, CONSUMO, REVERSA)
monto               DECIMAL
created_at          TIMESTAMP
```

#### `retenciones_recibidas_libro` (vista materializada o tabla denormalizada)

Vista del libro mensual de retenciones recibidas (para declaración SET).

```
empresa_id, periodo (YYYY-MM), tipo (IVA/RENTA), cliente_ruc, cliente_nombre,
numero_comprobante, timbrado, fecha, monto, recibo_id, factura_numero, factura_id
```

### 4.2 Tablas preparadas para retenciones emitidas (sin uso funcional en v1)

#### `retenciones_emitidas` (estructura, sin endpoints en v1)

```
id, empresa_id, proveedor_id, factura_compra_id, tipo (IVA/RENTA),
regimen_id FK, porcentaje, monto, numero, timbrado, fecha, estado (BORRADOR/EMITIDO/ANULADO),
xml_set TEXT NULL, cdc VARCHAR NULL, ...
```

#### `regimenes_retencion` (catálogo seed)

```
id, codigo, descripcion, tipo (IVA/RENTA), porcentaje_default, base_calculo (NETO/IVA), activo
```

Seed inicial con regímenes SET PY:

- IVA 30% sobre IVA — designación general
- IVA 100% sobre IVA — casos especiales
- Renta Servicios Personales 9% sobre neto
- Renta Servicios No Personales 6% sobre neto
- Renta Estado / Otros (configurable)

### 4.3 Tablas existentes a modificar

#### `nota_credito` (agregar)

- `modo_aplicacion ENUM(ESTRICTO, FLEXIBLE) DEFAULT 'ESTRICTO'` — se setea según parámetro empresa al crear NC
- `saldo_disponible DECIMAL` — en modo FLEXIBLE, indica cuánto queda sin aplicar

#### `parametros_empresa` (agregar)

- `recibos_nc_modo_default ENUM(ESTRICTO, FLEXIBLE) DEFAULT 'ESTRICTO'`
- `recibos_diferencia_max_ajuste_manual DECIMAL DEFAULT 1000` — tope para ajuste manual sin permiso especial
- `recibos_numeracion_formato VARCHAR DEFAULT 'REC-{YYYY}-{####}'`

#### `factura` (verificar campos saldo)

Confirmar que existe `saldo_pendiente` calculado o materializado; si no, agregar campo o vista.

---

## 5. Permisos nuevos (módulo `COBROS` extendido)

| Código                        | Descripción                                         | Roles sugeridos                     |
| ----------------------------- | --------------------------------------------------- | ----------------------------------- |
| `COB_REC_VER`                 | Ver recibos multi-factura                           | Cajero, Cobrador, Tesorero, Gerente |
| `COB_REC_CREAR`               | Crear recibo multi-factura                          | Cajero, Cobrador, Tesorero          |
| `COB_REC_ANULAR`              | Anular recibo multi-factura (reversa total)         | Tesorero, Gerente                   |
| `COB_REC_NC_FLEXIBLE`         | Aplicar NC en modo flexible (sobreescribe estricto) | Gerente, Contador                   |
| `COB_REC_AJUSTE_MANUAL`       | Cerrar recibo con diferencia tipo AJUSTE_MANUAL     | Gerente                             |
| `COB_REC_RETENCION_REGISTRAR` | Registrar retenciones recibidas                     | Cajero, Tesorero, Contador          |
| `COB_REC_LIBRO_RETENCIONES`   | Ver y exportar libro de retenciones recibidas       | Contador, Gerente                   |
| `COB_REC_OVERRIDE_FIFO`       | Sobreescribir imputación FIFO automática            | Tesorero, Gerente                   |

---

## 6. Backend — Endpoints

### 6.1 Módulo nuevo `recibos-multi` (o extender `cobros`)

Ubicación sugerida: `src/cobros/multi/` (subcarpeta) o `src/recibos/` (módulo separado). Recomendación: **módulo separado `recibos`** para no contaminar `cobros` single-factura.

```
GET    /recibos                              Listar (filtros: cliente, fecha, estado, cobrador)
GET    /recibos/:id                           Detalle completo
POST   /recibos                               Crear (transacción atómica)
POST   /recibos/:id/anular                    Anular + reversa total
GET    /recibos/preview                       Vista previa de imputación (sin guardar) — recibe payload y devuelve cálculo FIFO
GET    /recibos/cliente/:id/pendientes        Facturas + cuotas + NC + saldo a favor pendientes del cliente
GET    /recibos/cliente/:id/saldos-favor      Saldos a favor disponibles del cliente
```

### 6.2 Libro de retenciones recibidas

```
GET    /retenciones-recibidas/libro?periodo=YYYY-MM      Libro mensual
PATCH  /retenciones-recibidas/:id/marcar-declarada       Marcar como declarada en SET
GET    /retenciones-recibidas/libro/export?formato=xlsx  Exportar
```

### 6.3 Configuración

```
GET/PATCH /parametros-empresa/recibos       Configurar modo NC default, formato numeración, tope ajuste
```

### 6.4 Catálogo de regímenes de retención (preparación)

```
GET    /regimenes-retencion                  Listar (para futuro pago a proveedores)
```

---

## 7. Lógica de negocio crítica

### 7.1 Algoritmo de creación de recibo (servicio `recibos.service.ts`)

```
1. Validar:
   - Cliente activo, mismo empresa_id que el usuario
   - Permiso COB_REC_CREAR
   - Todas las facturas pertenecen al cliente y tienen saldo > 0
   - NC aplicadas: si modo ESTRICTO → cada NC debe pertenecer a alguna factura del recibo
   - NC aplicadas en modo FLEXIBLE → requiere permiso COB_REC_NC_FLEXIBLE
   - Cada retención apunta a exactamente una factura del recibo
   - Tope retención no excede el monto de la factura asociada

2. Calcular imputación FIFO (si no hay override manual):
   - Ordenar facturas por fecha ascendente
   - Para cada factura, restar en orden: retención propia → NC aplicadas → monto pagado
   - Si la línea es una cuota, validar que cuota.saldo >= imputado

3. Calcular intereses:
   - Por cada cuota vencida, recalcular interés moratorio según config-mora
   - Agregar como línea separada (recibos_facturas.monto_interes)

4. Calcular diferencia:
   - imputado = Σ (facturas + intereses - nc - retención)
   - cobrado = Σ medios_pago
   - diferencia = cobrado - imputado
   - Asignar diferencia_tipo según signo y permisos

5. Validaciones de diferencia:
   - Si diferencia > 0 y tipo=SALDO_FAVOR → generar saldos_cliente
   - Si diferencia < 0 y tipo=PAGO_PARCIAL → reducir monto_pagado de la última factura
   - Si tipo=AJUSTE_MANUAL → motivo obligatorio + permiso COB_REC_AJUSTE_MANUAL + monto ≤ tope empresa

6. Transacción atómica (Prisma $transaction):
   a. Insertar recibos
   b. Insertar recibos_facturas
   c. Insertar recibos_nc_aplicadas (descontar nota_credito.saldo_disponible si FLEXIBLE)
   d. Insertar recibos_retenciones
   e. Insertar recibos_medios_pago
   f. Si genera saldo a favor → insertar saldos_cliente + saldos_cliente_movimientos
   g. Si consume saldo a favor → descontar saldos_cliente.monto_disponible + movimiento CONSUMO
   h. Actualizar saldo de cada factura/cuota afectada
   i. Generar movimientos de Tesorería (uno por medio de pago, vía reglas de tesorería)
   j. Generar asiento contable automático (si módulo contabilidad activo)
   k. Si hay cobrador → registrar comisión sobre total del recibo
   l. Auditoría (audit log)

7. Devolver recibo con relaciones cargadas.
```

### 7.2 Algoritmo de anulación (reversa total)

```
1. Validar:
   - Permiso COB_REC_ANULAR
   - Recibo en estado CONFIRMADO
   - Motivo obligatorio
   - Si algún medio_pago es CHEQUE depositado/cobrado → bloquear y exigir reversa de cheque primero
   - Si algún saldo_favor generado ya fue consumido en otro recibo → bloquear (debe anularse primero el recibo dependiente)

2. Transacción atómica:
   a. Revertir saldo de facturas/cuotas (sumar monto_pagado + nc + retención)
   b. Si consumió saldos_cliente → restituir monto_disponible + movimiento REVERSA
   c. Si generó saldos_cliente → marcar como anulado (no se puede borrar para mantener trazabilidad)
   d. Si aplicó NC en modo FLEXIBLE → devolver saldo_disponible a la NC
   e. Revertir movimientos de Tesorería (movimiento inverso)
   f. Revertir asiento contable (asiento de reversa)
   g. Revertir comisión del cobrador
   h. Marcar recibo.estado = ANULADO
   i. Auditoría
```

### 7.3 Integración con Tesorería

Cada `recibos_medios_pago` genera un movimiento en Tesorería usando las **reglas de tesorería existentes** (ver `plan-tesoreria-bancos.md`):

- `EFECTIVO` → caja de la sucursal
- `CHEQUE` → cuenta "Cheques en Cartera" (1.1.1.06)
- `TRANSFERENCIA` → cuenta bancaria configurada en la regla
- `TARJETA` → cuenta de procesador
- `SALDO_FAVOR` → **no genera movimiento de tesorería** (es contable interno)

### 7.4 Integración contable (alineada con módulo `contabilidad` ya implementado)

Se reutiliza el patrón existente del módulo contable (ver `plan-contabilidad.md`):

#### 7.4.1 Nuevo método en `ContabilidadIntegracionService`

Agregar `integrarReciboMulti(reciboId)` análogo a `integrarCobro` pero con soporte para NC, retenciones, intereses y diferencias.

**Hook**: al confirmar un recibo multi (`recibos.service.ts → crear()`), invocar `ContabilidadIntegracionService.integrarReciboMulti(recibo.id)`.

#### 7.4.2 Nuevo tipo en `cont_documentos`

Extender el enum `cont_documentos.tipo` con un valor nuevo:

- `RECIBO_MULTI` — para recibos multi-factura
- (`COBRO` existente sigue siendo el del flujo single-factura — no se mezclan)

`origen_tipo = 'recibos'`, `origen_id = recibo.id` → garantiza **idempotencia** (índice único `origen_tipo + origen_id`).

#### 7.4.3 Nuevos conceptos en `cont_mapeo_cuentas`

Agregar al enum de conceptos mapeables + seed default:

| Concepto                                | Cuenta sugerida (seed)                                                                  | Uso                                                                 |
| --------------------------------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `NC_APLICADA_PUENTE`                    | crear `1.1.2.04 Notas de Crédito a Aplicar`                                             | Puente cuando NC modo FLEXIBLE se aplica fuera de su factura origen |
| `RETENCION_IVA_FAVOR`                   | crear `1.1.3.03 Retenciones de IVA a Favor`                                             | Retenciones de IVA recibidas (a descontar de IVA Débito mensual)    |
| `RETENCION_RENTA_FAVOR`                 | crear `1.1.3.04 Retenciones de Renta a Favor`                                           | Retenciones de Renta recibidas (a descontar de Renta a pagar)       |
| `ANTICIPOS_CLIENTES`                    | crear `2.1.5.01 Anticipos de Clientes`                                                  | Saldos a favor del cliente (pasivo: les debemos producto/servicio)  |
| `INTERESES_MORATORIOS_GANADOS`          | reutilizar `4.2.1.01 Intereses Ganados` o crear `4.2.1.03 Intereses Moratorios Ganados` | Línea de interés en recibo                                          |
| `RETENCION_IVA_EMITIDA` (preparación)   | crear `2.1.3.03 Retenciones de IVA a Pagar`                                             | Para fase futura de retenciones emitidas                            |
| `RETENCION_RENTA_EMITIDA` (preparación) | crear `2.1.3.04 Retenciones de IRP a Pagar`                                             | Para fase futura                                                    |

Estas cuentas se agregan al **seed `plan-cuentas-paraguay.seed.ts`** y al seed de `cont_mapeo_cuentas` default. Empresas existentes reciben las cuentas vía migración + asignación automática del mapeo.

#### 7.4.4 Asiento contable típico

**Escenario**: recibo cubre 3 facturas (saldo total 10.000.000 PYG) con:

- 1 NC modo flexible aplicada por 500.000
- Retención IVA por 200.000 (factura A) + Retención Renta por 600.000 (factura B)
- Intereses moratorios 100.000
- Cobro: efectivo 8.800.000

```
DEBE
  Caja General (1.1.1.01)                          8.800.000
  Retenciones IVA a Favor (1.1.3.03)                 200.000
  Retenciones Renta a Favor (1.1.3.04)               600.000
  NC Aplicada Puente (1.1.2.04)                      500.000

HABER
  Clientes (1.1.2.01)                             10.000.000
  Intereses Moratorios Ganados (4.2.1.03)            100.000
```

**Si genera saldo a favor** (cobrado > imputado en 50.000):

```
DEBE
  Caja General                                     8.850.000  ← +50.000
HABER
  ... (igual que arriba)
  Anticipos de Clientes (2.1.5.01)                    50.000
```

**Si consume saldo a favor** (medio_pago tipo SALDO_FAVOR por 200.000):

```
DEBE
  Caja General                                     8.600.000
  Anticipos de Clientes (2.1.5.01)                   200.000  ← se reduce el pasivo
  Retenciones...                                       ...
  NC Aplicada Puente                                   ...
HABER
  Clientes                                        10.000.000
  Intereses Moratorios Ganados                       100.000
```

**Asiento de cierre de NC puente** (cuando una NC modo FLEXIBLE se consume totalmente):
En realidad, la cuenta puente "NC Aplicada Puente" se compensa contra la NC original cuando se aplica. El asiento de la NC al emitirse ya debitó "Ventas/IVA Débito" y acreditó "Clientes". Al aplicarla en otro recibo, la cuenta puente actúa como el "Clientes" del cliente original (que ya tenía el crédito). Implementación detallada en `integracion.service.ts` con ayuda del contador del cliente.

#### 7.4.4.bis — Modos de NC y su impacto contable

| Modo NC      | ¿Genera línea en asiento del recibo?       | Razón contable                                                                                                                                                                                                                                         |
| ------------ | ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **ESTRICTO** | **NO**                                     | El asiento de la NC al emitirse ya debitó `IVA_DEBITO_10/5` + `VENTAS_*` y acreditó `CLIENTES`. La aplicación contra la factura origen es solo cancelación interna de CxC; no requiere nuevo asiento. El recibo solo registra la imputación operativa. |
| **FLEXIBLE** | **SÍ** — línea DEBE a `NC_APLICADA_PUENTE` | La NC compensa una factura distinta a la origen, por lo que se necesita una cuenta puente que conecte ambas: la NC original ya tiene su crédito reconocido, el puente refleja el movimiento al nuevo destino.                                          |

**Implicancia para el auditor**: si revisa un recibo con NC ESTRICTO y no encuentra líneas de NC en el asiento, es correcto. La trazabilidad de la aplicación queda en `recibo_cobro_nc_aplicada` (auditoría operativa).

#### 7.4.4.ter — Cheques al día vs diferidos

| Estado del cheque                                       | Cuenta contable                            | Concepto              |
| ------------------------------------------------------- | ------------------------------------------ | --------------------- |
| Al día (`fecha_vencimiento ≤ fecha_recibo` o sin fecha) | `1.1.1.06 Cheques en Cartera`              | `CHEQUES_EN_CARTERA`  |
| Diferido (`fecha_vencimiento > fecha_recibo`)           | `1.1.1.07 Cheques en Tránsito / Diferidos` | `CHEQUES_EN_TRANSITO` |

Los cheques **no** se mezclan con la cuenta de tesorería del medio (que sí se usa para efectivo, transferencia y tarjeta). Esto permite ver el monto exacto pendiente de cobro en la cartera de cheques sin acceso al detalle de Tesorería.

**Administración del mapeo**: ambos conceptos están en el seed default (`plan-cuentas-paraguay.seed.ts`). Aparecen automáticamente en la UI **Contabilidad → Mapeo de Cuentas → sección Caja y Bancos**, y el contador del cliente puede reasignarlos a cuentas distintas si su plan no usa esa numeración.

#### 7.4.5 Anulación (reversa contable)

Sigue el patrón existente:

```
ContabilidadIntegracionService.revertirDocumento(documentoId)
→ Crea cont_documento tipo=ANULACION, reversion_de_id = doc original
→ Genera asiento espejo (DEBE ↔ HABER invertidos)
→ Original queda estado=REVERTIDO
```

#### 7.4.6 Validaciones que ya aplica el módulo contable (heredadas)

1. **Partida doble** (Σ DEBE = Σ HABER en PYG)
2. **Período abierto** (rechaza si el período de la fecha del recibo está cerrado)
3. **Idempotencia** (no duplica asiento del mismo recibo)
4. **Cuenta acepta movimientos** (todas las cuentas usadas son hoja)
5. **Centro de costo obligatorio** (si la cuenta lo exige — configurable por empresa)

#### 7.4.7 Multi-moneda

Si el recibo es en USD (con `tipo_cambio` definido), el asiento se genera con:

- `moneda_origen = 'USD'` en `cont_asientos`
- `tipo_cambio_id` apuntando a la tasa del día (tabla `cont_tipo_cambio`)
- Cada línea de `cont_asientos_det` lleva `debe_moneda` / `haber_moneda` (USD) **y** `debe_pyg` / `haber_pyg` (convertido)
- Si hay diferencia de cambio entre la factura original (registrada a TC histórico) y el cobro (TC del día), se genera línea adicional a `4.2.1.02 Diferencia de Cambio Ganada` o `6.3.1.03 Diferencia de Cambio Perdida`.

#### 7.4.8 Auditoría

El recibo en sí se audita en `audit_log` (módulo de auditoría existente) con `entity_type='recibos'` y acciones `CREATE` / `ANULAR`. **El asiento contable generado automáticamente NO genera audit log propio** (regla del módulo contable) — su trazabilidad está en `cont_documentos.origen_tipo='recibos'` + `origen_id=recibo.id`.

---

## 8. Frontend — Pantalla nueva

### 8.1 Ubicación

`Finanzas → Recibos → Nuevo Recibo Multi-Factura`

### 8.2 Layout (3 columnas o wizard)

**Paso 1 — Cliente y facturas**

- Selector de cliente (autocomplete)
- Una vez seleccionado, mostrar tabla de facturas pendientes con checkbox
- Mostrar saldos a favor disponibles (botón "Usar saldo a favor")

**Paso 2 — Aplicación de NC y retenciones**

- Lista de NC disponibles del cliente (estricto: sólo las de las facturas seleccionadas; flexible: todas con saldo)
- Drag & drop o asignación por dropdown: cada NC se imputa a una factura
- Botón "+ Agregar retención recibida" por cada factura → modal con datos del comprobante de retención (timbrado, número, fecha, tipo IVA/Renta, monto)

**Paso 3 — Medios de pago**

- Tabla editable con filas (medio, monto, cuenta destino, referencia/cheque)
- Resumen lateral en tiempo real:
  - Total a cobrar (facturas + intereses)
  - Total descuentos (NC + retenciones)
  - Total medios de pago
  - **Diferencia** (resaltada en verde/rojo) con selector de qué hacer

**Paso 4 — Confirmación**

- Vista previa con detalle completo
- Botón "Recalcular FIFO" + botón "Editar imputación manual" (si permiso)
- Imprimir/PDF al confirmar

### 8.3 Componentes a reutilizar

- `SearchableSelect` (cliente)
- `FormShell` (estructura)
- `ConfirmDialog` (anular recibo)
- `PermissionGate` (gates de permisos nuevos)
- `RetencionFormModal` (nuevo, específico)

### 8.4 Pantalla Libro Retenciones Recibidas

`Finanzas → Libro de Retenciones`

- Filtro por período (YYYY-MM)
- Tabla con todos los comprobantes
- Botón "Marcar declaradas" (bulk)
- Export Excel

---

## 9. Roadmap de implementación

### Fase 1 — Cimientos ✅ COMPLETA

- [x] Schema Prisma + migración SQL — **consolidado en tabla única `recibos_cobro` con discriminador `modo` (LEGACY|MULTI)** en lugar de tablas separadas. Tablas hijas: `recibo_cobro_facturas`, `recibo_cobro_nc_aplicadas`, `recibo_cobro_retenciones`, `recibo_cobro_medios_pago`, `rec_saldos_cliente`, `rec_saldos_cliente_mov`. Las tablas `rec_multi*` originales fueron eliminadas tras migración de datos.
- [x] Seed `regimenes_retencion` (catálogo SET PY)
- [x] Seed permisos nuevos `COB_REC_*` + asignación a roles
- [x] Cuentas contables nuevas en `cont_plan_cuentas` seed + `CHEQUES_EN_CARTERA` / `CHEQUES_EN_TRANSITO` (al día vs diferido)
- [x] Nuevos conceptos en `cont_mapeo_cuentas` + seed default
- [x] Migración para empresas existentes (cuentas + mapeo automático)
- [x] Extensión enum `cont_documentos.tipo` con `RECIBO_MULTI`
- [x] Parámetros empresa (`nc_modo_default`, `diferencia_max_ajuste`, `numeracion_formato`)
- [x] DTOs + validaciones
- [x] Estructura módulo `recibos/`

### Fase 2 — Backend recibos ✅ COMPLETA

- [x] `recibos.service.ts`: crear, listar, detalle, preview FIFO
- [x] `recibos.service.ts`: anular con reversa total
- [x] Gestión de saldos a favor (`rec_saldos_cliente` + movimientos)
- [x] Integración Tesorería (F2.A — movimientos automáticos en crear/anular)
- [x] Bloqueo de anulación si cheque depositado/cobrado (F2.B)
- [x] Comisión cobrador sobre total del recibo (F2.C)
- [x] Intereses moratorios automáticos por cuota vencida (F2.D)
- [x] Integración Contabilidad con asiento balanceado (NC ESTRICTO descontada de `totalClientes` para evitar duplicación)
- [x] Audit log de integración contable (éxito/fallo) e integración SIFEN (send/sync/eventos/reversiones)
- [x] Defensa numeración: salto del contador `numeraciones_documento` cuando colisiona con número ya emitido (resync automático)
- [ ] Tests unitarios + e2e exhaustivos (verificación manual hecha; suite formal pendiente)

### Fase 3 — Backend retenciones + refinamientos ✅ COMPLETA

- [x] `retenciones-recibidas.service.ts`: libro mensual + export
- [x] Endpoint marcar declarada
- [x] F3.A — Subform cheque completo en wizard + DTO extendido (numero, banco, titular, cuenta, emisión, vencimiento) + alta en `tes_cheques`
- [x] F3.B — Cheque al día vs diferido diferenciado en asiento (`CHEQUES_EN_CARTERA` vs `CHEQUES_EN_TRANSITO`)
- [x] F3.C — Pantalla "Saldos a favor por cliente" con movimientos
- [x] F3.D — Documentación y tooltip NC ESTRICTO sin línea contable
- [x] Reversa automática de aplicación de NC cuando SIFEN rechaza/cancela/inutiliza (`rec_aplicacion_reversada` + datafix retroactivo)

### Fase 4 — Frontend ✅ COMPLETA

- [x] `RecibosUnificadoPanel` en Finanzas (listar ambos modos con filtro `modo`)
- [x] Wizard `NuevoReciboMultiWizard` (4 pasos)
- [x] `RetencionFormModal`
- [x] `LibroRetencionesPanel`
- [x] PDF A4 multi-factura + ticket térmico 58/80mm
- [x] Visibilidad de NC ESTRICTO en wizard paso 2 según facturas seleccionadas (FLEXIBLE bajo permiso)
- [x] Filtro de medios de pago por sucursales SIFEN activas
- [x] Detalle de NC muestra recibos donde fue aplicada (endpoint `/recibos-multi/nc/:id/aplicaciones`)
- [x] Lista de NC muestra factura vinculada + chip de modo
- [x] Aplicación de estándares UI (`src/components/_standards/`) en RecibosMultiPanel y LibroRetencionesPanel
- [x] Pestaña antigua "Recibos" + "Recibos Multi" unificadas en una sola

### Fase 5 — Retenciones emitidas (preparación DB)

- [x] Tablas `retenciones_emitidas` + `regimenes_retencion` (schema + seed)
- [ ] Documentación de interfaces para flujo de pagos a proveedores (pendiente al iniciar plan futuro)

### Fase 6 — QA + documentación

- [x] Verificación end-to-end con datos reales del cliente (varios ciclos)
- [x] Recibos legacy (single-factura) siguen funcionando sin cambios
- [x] Rediseño de ticket térmico al estilo "Holding" (cuota X/Y, cobrador, sucursal)
- [ ] Onboarding/tour formal de la pantalla nueva
- [ ] Guía de usuario en `docs/` o en-app

---

## 10. Casos de uso de validación (QA)

1. **Recibo simple multi-factura**: 3 facturas, sin NC ni retención, pago efectivo exacto → cierra OK, 3 mov. tesorería.
2. **Recibo con NC estricta**: NC ligada a factura A se aplica a factura A → OK. Intento aplicarla a factura B → bloqueo.
3. **Recibo con NC flexible**: NC ligada a factura A se aplica a factura B (con permiso `COB_REC_NC_FLEXIBLE`) → OK + descuenta saldo_disponible de NC.
4. **Recibo con retención IVA + Renta** sobre la misma factura → 2 registros en `recibos_retenciones`, libro mensual los lista correctamente.
5. **Diferencia positiva**: cliente paga 100.000 de más → genera saldo_favor; siguiente recibo consume saldo_favor como medio de pago.
6. **Diferencia negativa (pago parcial)**: cobrado < imputado → última factura queda con saldo pendiente.
7. **Ajuste manual**: diferencia de 500 PYG con motivo "redondeo" → permitido si monto ≤ tope empresa y usuario tiene `COB_REC_AJUSTE_MANUAL`.
8. **Anulación con cheque depositado**: bloqueo correcto, exige revertir cheque primero.
9. **Anulación con saldo a favor ya consumido**: bloqueo, exige anular recibo dependiente.
10. **FIFO override manual**: usuario reordena imputación → resultado distinto al FIFO, persiste.
11. **Recibo cubriendo cuotas de 3 ventas a crédito distintas**: cada cuota se descuenta individualmente, comisión cobrador sobre total recibo.
12. **Intereses moratorios**: cuotas vencidas generan línea de interés automática.
13. **Libro de retenciones**: exportar mes de marzo → coincide con asientos contables del período.

---

## 11. Fuera de alcance (próximos planes)

- Emisión de comprobantes de retención electrónicos (SIFEN/DE tipo retención).
- Flujo completo de pagos a proveedores con retención automática.
- Importación masiva de retenciones desde XML SIFEN del cliente que retiene.
- OCR de comprobantes de retención en papel.
- Reportes IA sobre patrones de cobro (ya cubierto en `plan-ia-dashboard-rrhh.md` y `plan-dashboard-ia.md`).
- Anulación parcial (quitar 1 factura de un recibo de 5) — sólo reversa total en v1.
- Multi-moneda compleja (cobros en USD imputando facturas en PYG con varias TC) — soporte básico con tipo_cambio único por recibo.
- Migración de recibos históricos al nuevo modelo.

---

## 12. Preguntas abiertas para resolver durante implementación

1. **Numeración del recibo**: ¿correlativo único por empresa, por sucursal, o por cobrador? — _Sugerencia: por empresa + sucursal, formato configurable._
2. **Asiento contable del saldo a favor del cliente**: ¿cuenta "Clientes - Anticipos" en pasivo o en CxC negativa? — _Sugerencia: pasivo (2.1.x.xx "Anticipos de Clientes") para mayor claridad._
3. **NC en modo flexible que vence**: ¿caducan después de X meses? — _Sugerencia: no caducan por defecto, parámetro opcional._
4. **Comisión del cobrador sobre intereses moratorios**: ¿se comisiona o no? — _Definir con el cliente._
5. **PDF del recibo**: ¿con QR? ¿plantilla configurable por empresa? — _Sugerencia: plantilla básica + futuro configurable._

---

## 12.bis Gaps detectados en revisión post-implementación

Estado a 2026-05-21 — todos los gaps de Fase 3 fueron cerrados:

| Gap | Resumen                                               | Estado                                                                                                                                               |
| --- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| G1  | Datos de cheque insuficientes en `RecMedioPagoDto`    | ✅ **Resuelto (F3.A)** — sub-objeto `cheque` completo, alta en `tes_cheques`, `cheque_id` linkeado en `recibo_cobro_medios_pago`                     |
| G2  | Cheque al día vs diferido sin diferenciación contable | ✅ **Resuelto (F3.B)** — conceptos `CHEQUES_EN_CARTERA` (1.1.1.06) y `CHEQUES_EN_TRANSITO` (1.1.1.07) seeded; asiento decide por `fecha_vencimiento` |
| G3  | NC modo ESTRICTO sin línea contable                   | ✅ **Resuelto (F3.D)** — documentado en §7.4.4.bis + tooltip en wizard. Asiento balanceado al restar NC ESTRICTO de `totalClientes`                  |
| G4  | Saldos a favor sin pantalla de administración         | ✅ **Resuelto (F3.C)** — `SaldosFavorPanel` con filtros, expansor de movimientos y endpoint `/recibos-multi/saldos-favor`                            |
| G5  | Saldo a favor sin tope de caducidad                   | 🟡 **Abierto** — pendiente decisión de negocio (ver Q12.3). Funcionalmente no caduca                                                                 |

## 12.ter Gaps detectados post-go-live (2026-05-21)

Hallados durante uso productivo. Resueltos en la misma iteración:

| ID  | Hallazgo                                                                               | Resolución                                                                                                     |
| --- | -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| H1  | NC con `estado_sifen` Rechazado/Cancelado/Inutilizado seguían disponibles en wizard    | Filtro en `ncDisponiblesCliente` + flag `rec_aplicacion_reversada` + datafix retroactivo                       |
| H2  | Subquery `nc_aplicadas` usaba columna inexistente `total`                              | Corregido a `nota_credito_subtotal.dtotgralope`                                                                |
| H3  | Asiento contable desbalanceado (DEBE ≠ HABER) cuando NC ESTRICTO presente              | `totalClientes -= Σ NC ESTRICTO` en `integracion.service.ts`                                                   |
| H4  | NCs ESTRICTO no aparecían en wizard paso 2 (gate por permiso `COB_REC_NC_FLEXIBLE`)    | Visibles si su `factura_cab_id ∈ selFacturas`; FLEXIBLE sigue gateada por permiso                              |
| H5  | Errores y éxitos de integración contable sin traza                                     | Audit log dedicado en `setImmediate` post-recibo                                                               |
| H6  | Eventos SIFEN (envío, sync, integración, reversión, reversa NC) sin traza centralizada | Helper `auditSifenEvent` instrumentando doc-sync y event-sync loops                                            |
| H7  | Circular dependency JS al inyectar `NotaCreditosService` desde middleware-sifen        | Reversión de aplicación de NC inlined como método privado en middleware                                        |
| H8  | Detalle de NC no mostraba en qué recibos fue aplicada                                  | Endpoint `GET /recibos-multi/nc/:id/aplicaciones` + tabla nueva en `NotaCreditoDetalleDialog`                  |
| H9  | Tabla de NC del wizard no mostraba factura vinculada ni modo                           | Columnas "Factura vinculada" + "Modo" agregadas; `ncDisponiblesCliente` ahora JOIN-ea `factura_cab`            |
| H10 | Unique constraint `(empresa_id, numero_recibo)` por contador desincronizado            | Loop defensivo en `_generarNumeroTx` y `cobros.service.ts` que salta hasta número libre + actualiza contador   |
| H11 | Ticket térmico básico, sin diseño productivo                                           | Rediseño estilo "Holding": sucursal en header, key:value alineado, `Cuota X/Y`, cobrador, footer "Novasis ERP" |

---

## 12.quinquies Fase 5 — Retenciones Recibidas (UX completa)

**Origen**: Tras el go-live de Fases 1–4, surgió la duda real del usuario: _"cuando el cliente me entrega el comprobante de retención, ¿dónde lo cargo?"_ y _"¿cómo sabe el sistema cuáles de mis clientes son agentes de retención?"_. El wizard ya tenía el subform de retención pero asumía:

1. Que el cobrador sabe a priori si el cliente es agente de retención.
2. Que basta con datos textuales del comprobante, sin adjuntar el archivo escaneado/PDF.

Esta fase cierra ambos gaps.

### F5.A — Flag `es_agente_retencion` en cliente

- **DB**: agregar `clientes.es_agente_retencion BOOLEAN DEFAULT false`.
- **Backend**: incluir en `CreateClienteDto` / `UpdateClienteDto` y persistir en `clientes.service.ts`.
- **Frontend** (`ClienteFormDialog`): nueva sección "Régimen tributario" con switch "Es agente de retención (designado por SET)".
- **Decisión**: arrancamos con un único booleano. Si más adelante aparecen casos reales donde un cliente esté designado sólo para IVA o sólo para Renta, se agrega granularidad (retrocompatible).

### F5.B — Comportamiento condicional en el wizard

- **Step 2 (NC y retenciones)**:
  - Si `cliente.es_agente_retencion === true` → sección "Retenciones recibidas" se muestra **expandida con alert verde** ("Este cliente es agente de retención. Cargá el comprobante si te lo entregó.").
  - Si `false` → sección **colapsada** con link discreto "El cliente me entregó un comprobante de retención" para casos atípicos (cliente recién designado y no actualizado, retención manual autorizada por contador, etc.).
  - Warning suave (no bloqueante) si el usuario carga retención sin el flag activo: _"Este cliente no figura como agente de retención. ¿Querés actualizar su ficha?"_

### F5.C — Adjuntar comprobante de retención (archivo)

- **DB**: agregar `recibo_cobro_retenciones.archivo_url TEXT NULL`.
- **Backend**: endpoint `POST /recibos-multi/retenciones/upload-comprobante` (multipart) que sube a DigitalOcean Spaces (mismo patrón que `uploadLogo` en `empresas.controller`), bajo prefijo `comprobantes-retencion/{empresa_id}/{recibo_id}/`. Acepta PDF, JPG, PNG, WEBP. Tamaño máx 5 MB. Devuelve URL pública.
- **Service**: `previewReciboMulti` y `createReciboMulti` aceptan `archivo_url` opcional en cada item del array `retenciones[]`.
- **Frontend wizard**: en el subform de retención, agregar input `<FileUpload>` con preview/icono según mime. Si el archivo ya fue subido, mostrar nombre + botón "Reemplazar"/"Quitar".
- **Detalle de recibo** (`useReciboMultiQuery`): si la retención tiene `archivo_url`, mostrar botón "Ver comprobante" que abre el archivo en nueva pestaña.

### F5.D — Pendiente Fase 6 (próxima): retenciones emitidas

- Cuando esta empresa **sea** agente de retención (a futuro), el flujo opuesto: emitir comprobante propio al pagar a proveedor. DB ya preparada (`rec_retenciones_emitidas`), falta integración SIFEN.

---

## 12.quater Pendientes activos (al 2026-05-21)

| Prioridad    | Tarea                                                                                                           | Notas                                                                         |
| ------------ | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| **Alta**     | **F5.A — flag `clientes.es_agente_retencion`**                                                                  | DB + DTO + form switch                                                        |
| **Alta**     | **F5.B — UI condicional en wizard step 2**                                                                      | Mostrar/colapsar sección retenciones según flag + warnings                    |
| **Alta**     | **F5.C — Upload de comprobante de retención**                                                                   | Columna `archivo_url`, endpoint S3 upload, file input wizard, link en detalle |
| Media        | F2.5 — Badge "descuentos pendientes" en `PanelSupervisor` + UI de aprobación (override `COB_REC_AJUSTE_MANUAL`) | Pendiente desde Fase 2; backend ya emite el evento, falta UI                  |
| Media        | Verificar controller/DTO + FK `rec_saldos_cliente_mov` (task #53)                                               | Auditoría liviana sobre integridad referencial                                |
| Baja         | Tour/onboarding formal de Recibos Multi                                                                         | Wizard ya tiene textos guía inline                                            |
| Baja         | Guía de usuario en `docs/` o en-app                                                                             |                                                                               |
| Baja         | Tests e2e formales del wizard (escenarios borde)                                                                | Verificación manual cubierta                                                  |
| Definir      | Q12.3 — caducidad de saldos a favor y NC FLEXIBLE                                                               | Necesita decisión de negocio                                                  |
| Definir      | Q12.4 — comisión cobrador sobre intereses moratorios                                                            | Hoy se comisiona sobre total recibo incluyendo intereses                      |
| Definir      | Q12.5 — QR y plantilla configurable de PDF de recibo                                                            | Ticket y A4 ya productivos; plantilla configurable es enhancement             |
| Próximo plan | Retenciones emitidas con SIFEN (flujo completo de pagos a proveedores)                                          | Sólo DB preparada                                                             |

---

## 13. Referencias

- `docs/plan-creditos-cobranzas.md` — módulo de créditos existente
- `docs/plan-tesoreria-bancos.md` — integración de medios de pago
- `docs/plan-contabilidad.md` — asientos automáticos
- `docs/plan-compras-gastos.md` — base para futuras retenciones emitidas
- `src/cobros/cobros.service.ts` — flujo single-factura actual (no se toca)
- `src/nota-creditos/nota-creditos.service.ts` — NC actual (se extiende con `modo_aplicacion`)
