# Plan — Facturas por acuerdo comercial en recibo multi-factura

## 1. Contexto y problema

Algunos clientes que le compran a la empresa (la que usa el ERP) **también le emiten facturas a la empresa** por acuerdos comerciales (rebates por volumen, comisiones por exhibición, publicidad compartida, alquiler de espacios, etc.). Esas facturas son **facturas de compra** desde el punto de vista de la empresa: el cliente actúa simultáneamente como **cliente** (vía `clientes`) y como **proveedor** (vía `proveedores`), unidos por la misma `persona`.

Cuando ese cliente paga su deuda, en su orden de pago **compensa** parte de lo que debe contra las facturas que él mismo le emitió a la empresa. Hoy el wizard de **recibo multi-factura** no soporta esa compensación — solo acepta NC propias del cliente y retenciones. Hay que extender el paso 2 ("NC y retenciones") para agregar una tercera fuente de descuento: **Facturas por acuerdo comercial**.

Resultado esperado:
- El cliente selecciona en el paso 2 una o más compras pendientes de pago a ese proveedor (== mismo `persona_id` del cliente).
- El monto aplicado se descuenta del total a cubrir por medios de pago.
- Al confirmar, la compra queda marcada como pagada (total o parcial) por compensación, y aparece detallada en el recibo (PDF + UI).
- Contablemente: débito `cuentas_pagar` (proveedor) / crédito `cuentas_cobrar` (cliente) — sin afectar caja.

## 2. Stack actual relevante

**Frontend** (`pos-ventas`):
- `src/components/organismos/RecibosMulti/NuevoReciboMultiWizard.jsx` — wizard de 4 pasos.
- Paso 2 (`StepNcRetenciones`): usa `useNcDisponiblesQuery(clienteId)` y `RetencionFormModal`.
- `src/api/recibos-multi.service.js` — API client.
- `src/tanstack/RecibosMultiStack.js` — hooks de TanStack Query.

**Backend** (`smartfactvoice-backend`):
- `src/recibos/recibos.service.ts` — lógica multi-factura (`crearReciboMulti`, `previewReciboMulti`, `findOne`, `anular`).
- Tablas: `recibos_cobro`, `recibo_cobro_facturas`, `recibo_cobro_medios_pago`, `recibo_cobro_nc_aplicadas`, `recibo_cobro_retenciones`.
- `compra_cab` (cabecera de factura de compra) → `proveedor_id` → `proveedores.persona_id`.
- `clientes.persona_id` permite encontrar al "mismo titular como proveedor".
- `cuentas_pagar` — saldo pendiente de cada compra.

**Confirmación de match cliente↔proveedor**: ambos modelos referencian `personas`. La consulta para buscar compras compensables es:
```sql
SELECT cc.* FROM compra_cab cc
JOIN proveedores p ON p.id = cc.proveedor_id
WHERE p.persona_id = (SELECT persona_id FROM clientes WHERE id = :cliente_id)
  AND cc.anulado = false
  AND cc.saldo_pendiente > 0
  AND cc.empresa_id = :empresa_id
```

## 3. Diseño funcional

### 3.1 UI — Paso 2 del wizard

El paso actual "NC y retenciones" pasa a tener **tres secciones colapsables** dentro del mismo step (no se agrega un paso nuevo, para no romper la numeración):

1. **Notas de crédito disponibles** (existente).
2. **Retenciones** (existente).
3. **Facturas por acuerdo comercial** (nuevo) — solo visible si la persona del cliente tiene contraparte como proveedor con compras pendientes.

Sección 3 muestra una tabla:
| ☐ | Nº factura compra | Fecha | Total | Saldo pendiente | Importe a aplicar |
|---|---|---|---|---|---|

- El check selecciona la compra; el campo "Importe a aplicar" es editable, default = saldo pendiente, máximo = saldo pendiente, mínimo = 0.
- Total seleccionado se resta del "total a pagar" en el paso 3 (Medios de pago).
- Si la persona del cliente no existe como proveedor o no tiene compras con saldo, **la sección no aparece** (no agregar ruido).

### 3.2 Backend — modelo y endpoints

**Nueva tabla** `recibo_cobro_facturas_compra_aplicadas`:
```prisma
model recibo_cobro_facturas_compra_aplicadas {
  id                String                @id @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  recibo_cobro_id   String                @db.Uuid
  compra_cab_id     String                @db.Uuid
  monto             Decimal               @db.Decimal(18, 2)
  created_at        DateTime?             @default(now()) @db.Timestamp(6)
  recibo            recibos_cobro         @relation(fields: [recibo_cobro_id], references: [id], onDelete: Cascade)
  compra            compra_cab            @relation(fields: [compra_cab_id], references: [id], onDelete: Restrict)

  @@index([recibo_cobro_id])
  @@index([compra_cab_id])
}
```

Decisión: una compra compensa el recibo **como conjunto**, no se vincula a una factura de venta puntual (a diferencia de las NC). Si en el futuro contabilidad pide trazabilidad por factura, agregamos `recibo_factura_id` nullable.

**Endpoint nuevo**: `GET /recibos-multi/compras-compensables?cliente_id=...`
- Retorna compras pendientes del proveedor que comparte `persona_id` con el cliente.
- Permission: `COB_REC_RECIBO_VER` (misma del recibo).

**Modificaciones a endpoints existentes**:
- `POST /recibos-multi` (`crearReciboMulti`): aceptar `facturas_compra_aplicadas: [{ compra_cab_id, monto }]` en el DTO.
- `POST /recibos-multi/preview` (`previewReciboMulti`): incluir `total_facturas_compra` en la respuesta para que el wizard muestre el preview correcto.
- `GET /recibos-multi/:id` (`findOne`): retornar `facturas_compra_aplicadas[]` con datos enriquecidos (nº, fecha, proveedor).
- `DELETE /recibos-multi/:id` (`anular`): restituir saldo de cada `compra_cab` aplicada.

### 3.3 Cálculo

Antes:
```
total_a_pagar = total_facturas - total_nc - total_saldos_favor - total_retenciones
```

Después:
```
total_a_pagar = total_facturas
              - total_nc
              - total_saldos_favor
              - total_retenciones
              - total_facturas_compra_aplicadas
```

### 3.4 Persistencia transaccional al confirmar

Dentro de `prisma.$transaction(...)` en `crearReciboMulti`:
1. Validar que cada `compra_cab_id` pertenece a un proveedor cuya `persona_id` == `cliente.persona_id` (anti-fraude).
2. Validar que `monto_aplicado <= saldo_pendiente` actual de la compra.
3. Insertar fila en `recibo_cobro_facturas_compra_aplicadas`.
4. Actualizar `compra_cab.saldo_pendiente` (-= monto) y `estado` ('pagada' / 'parcial').
5. Actualizar `cuentas_pagar` correspondientes (FIFO por vencimiento, mismo patrón que NC).
6. Registrar movimiento contable: débito cuenta proveedor / crédito cuenta cliente — vía `AuditService` + `integracion.service`.

### 3.5 Anulación

`recibos.service.anular` debe iterar `facturas_compra_aplicadas` del recibo y restituir saldos en `compra_cab` y `cuentas_pagar`, igual que ya hace con `nc_aplicadas`.

### 3.6 PDF del recibo

`recibos.service.findOne` ya construye el payload del PDF. Agregar bloque:
```
=== FACTURAS POR ACUERDO COMERCIAL ===
| Nº            | Fecha       | Monto aplicado |
| ...           | ...         | Gs. ...        |
                              Subtotal: Gs. XXX
```

## 4. Permisos

- `COB_REC_RECIBO_VER` — ver el listado de compras compensables (mismo que recibo).
- `COB_REC_RECIBO_CREAR` — aplicar una factura de compra en un recibo.
- Validación dura: solo `compra_cab.empresa_id == user.empresa_id` y `proveedor.persona_id == cliente.persona_id`. No se permite atajo por RUC para evitar suplantación.

## 5. Plan de implementación por fases

### Fase 1 — Backend base (estimado: 1 día)
1. Migración Prisma: nueva tabla `recibo_cobro_facturas_compra_aplicadas` + relación en `recibos_cobro` y `compra_cab`.
2. DTO `crear-recibo-multi.dto.ts`: agregar `facturas_compra_aplicadas?: { compra_cab_id, monto }[]`.
3. Servicio `recibos.service`:
   - `getComprasCompensables(empresaId, clienteId)` — nuevo método.
   - `previewReciboMulti` — sumar `total_facturas_compra`.
   - `crearReciboMulti` — validar, persistir, actualizar saldos.
   - `findOne` — incluir compras aplicadas.
   - `anular` — restituir saldos.
4. Endpoint `GET /recibos-multi/compras-compensables`.
5. Tests unitarios del servicio (mock Prisma).

### Fase 2 — Frontend (estimado: 1 día)
1. `recibos-multi.service.js`: método `getComprasCompensables(clienteId)`.
2. `RecibosMultiStack.js`: hook `useComprasCompensablesQuery(clienteId)`.
3. `NuevoReciboMultiWizard.jsx`:
   - State `facturasCompraAplicadas` en el wizard.
   - Sección nueva en `StepNcRetenciones` (renombrar a `StepDescuentos` si quedan 3 fuentes).
   - Restar el total en el paso 3 y 4.
   - Mandar en el payload final del `createMutation`.
4. `RecibosMultiPanel.jsx` (modal de detalle): mostrar la nueva sección si el recibo tiene compras aplicadas.

### Fase 3 — Contabilidad y reportes (estimado: 0.5 día)
1. `integracion.service.ts`: asiento contable de compensación cliente↔proveedor.
2. PDF del recibo: incluir bloque "Facturas por acuerdo comercial".
3. Auditoría: registrar evento `RECIBO_COMPENSA_COMPRA` con detalle.

### Fase 4 — QA y deploy (estimado: 0.5 día)
1. Caso feliz: cliente con saldo deudor + 1 compra pendiente → compensar parcial.
2. Caso 0 compras compensables: la sección no aparece.
3. Caso anulación: restituir todos los saldos.
4. Caso edge: compra anulada después del preview pero antes de confirmar (race) → re-validar dentro de la transacción.
5. Caso edge: cliente sin contraparte como proveedor → endpoint retorna `[]`, no error.

## 6. Riesgos y decisiones abiertas

- **¿Una compra puede compensarse parcialmente entre varios recibos a lo largo del tiempo?** Sí. El campo `monto` por aplicación es independiente; el saldo de `compra_cab` se actualiza progresivamente. Igual que NC parciales.
- **¿Y si el cliente no tiene persona vinculada como proveedor?** La sección no aparece. Operativamente, se sugiere agregar en el modal de cliente un botón "Crear como proveedor" para habilitar este flujo a futuro — fuera de alcance de esta fase.
- **¿Aplica IVA crédito al recibir la compra?** Sí, pero eso ya lo maneja el flujo normal de compras al registrarlas; este plan **no toca el IVA crédito** — solo compensa saldos.
- **SIFEN / Marangatu**: no impacta. La factura de compra ya fue declarada cuando se la registró; la compensación es un evento contable interno.

## 7. Checklist final (PR)

- [ ] Migración aplicada en dev + prod.
- [ ] Validación de mismatch `persona_id` cliente vs proveedor.
- [ ] Saldo de compra se actualiza dentro de la transacción del recibo.
- [ ] Anulación de recibo restituye saldos.
- [ ] Sección visible solo si hay compras compensables.
- [ ] PDF del recibo lista las compras aplicadas.
- [ ] Asiento contable correcto (validar con contador).
- [ ] Test e2e: crear recibo con compensación → verificar saldos → anular → verificar restitución.
- [ ] Documentación en `docs/ui-standards.md` si se agregó algún componente reutilizable.
