# Plan — Comisiones, Liquidaciones, Vendedores, Cobradores y Supervisores

> **Objetivo del documento:** dejar mapeado el módulo **tal como está implementado hoy** (ingeniería inversa sobre código + UI), de manera que un dev nuevo pueda continuar agregando funcionalidades sin tener que leer todo el código.
>
> No es un plan de "qué queremos construir": es la fotografía del sistema vigente más una sección final con los gaps detectados y las extensiones naturales.

**Fecha del corte:** 2026-06-10.
**Repos involucrados:**
- Backend NestJS: `/var/www/html/proyectos/smartfactvoice-backend`
- Frontend React+MUI: `/var/www/html/proyectos/pos-ventas`

---

## 1. Visión general del dominio

El sistema modela un equipo de ventas y cobranzas con jerarquía de **supervisores → vendedores/cobradores → clientes/facturas**, y monetiza el trabajo de cada uno por dos vías independientes que se acumulan:

| Quién | Cobra comisión por… | Sobre qué monto |
|---|---|---|
| **Vendedor** | Cada **venta** (factura) | Total de la factura |
| **Cobrador** | Cada **cobranza** (recibo) | Monto cobrado del recibo |
| **Vendedor+Cobrador (`ambos`)** | Ambas | Cada una sobre su base |
| **Supervisor** | Ventas **y** cobranzas de **su equipo** en un período | Suma de bases del equipo |

Las comisiones nacen en estado **`pendiente`**, se agrupan en **liquidaciones** por período (vendedor o supervisor), pasan a **`liquidada`** cuando se incluyen en una, y la liquidación termina **`pagada`** (con asiento contable de salida) o **`anulada`** (devuelve las comisiones a pendiente).

Los cobradores rinden su recaudación diaria mediante **rendiciones de cobranza** que pasan por un workflow de verificación de tesorería antes de aprobarse y generar el asiento contable de ingreso a caja.

Las facturas pendientes se distribuyen a cobradores mediante **asignaciones** y se planifican en **rutas de cobranza** que se imprimen como **hoja de ruta**.

---

## 2. Modelo de datos (Prisma)

Todas las tablas viven en `prisma/schema.prisma`. Convención: snake_case, PK `id` UUID, soft delete con `active` o `deleted_at`, FK con índices explícitos.

### 2.1 Personas

#### `vendedores_cobradores`
Persona física que vende y/o cobra. Un único modelo cubre los tres roles vía `tipo`.

Campos clave:
- `id` (UUID PK), `empresa_id` (FK), `usuario_id` (FK nullable — vincula al login del sistema).
- `tipo` VARCHAR(20) default `"vendedor"` → valores: `"vendedor" | "cobrador" | "ambos"`.
- Identidad: `codigo`, `nombre`, `apellido`, `documento`, `telefono`, `email`, `direccion`.
- `zona_id` (FK → `zonas_cobranza`, nullable) — solo aplica para cobradores.
- **Comisiones configuradas:**
  - `comision_porcentaje` DECIMAL(5,2) — legacy / genérico.
  - `comision_fija` DECIMAL(18,2) — monto fijo por operación, se **suma** al porcentual.
  - `comision_venta` DECIMAL(5,2) — % sobre ventas (se aplica si `tipo` es `vendedor` o `ambos`).
  - `comision_cobranza` DECIMAL(5,2) — % sobre cobranzas (se aplica si `tipo` es `cobrador` o `ambos`).
- `meta_mensual` DECIMAL(18,2) — meta objetivo.
- `active` BOOLEAN.

Índices: `(empresa_id)`, `(usuario_id)`, `(tipo)`, `(zona_id)`.

#### `supervisores`
Jefe de equipo. Modelo independiente (no es un `vendedores_cobradores` con flag).

Campos: identidad estándar + `porcentaje_comision_ventas` y `porcentaje_comision_cobros` DECIMAL(5,2). `active` BOOLEAN.

Índices: `(empresa_id)`, `(active)`.

#### `supervisor_vendedores_cobradores`
Tabla puente M:M. PK compuesta `(supervisor_id, vendedor_cobrador_id)`, cascada en ambas FK.

#### `zonas_cobranza`
Catálogo de zonas geográficas para cobradores. CRUD básico (no se detalla aquí).

### 2.2 Trazabilidad de comisiones

#### `asignacion_facturas`
Vincula una factura a su vendedor y/o cobrador responsable. Una factura puede tener uno, otro o ambos.

Campos:
- `factura_id` (FK).
- `vendedor_id` (FK → `vendedores_cobradores`, nullable).
- `cobrador_id` (FK → `vendedores_cobradores`, nullable).
- `fecha_asignacion`, `fecha_cobro_programada`.
- `estado` VARCHAR(20): `"pendiente" | "en_ruta" | "cobrado" | "reprogramado" | "cancelado"`.
- `notas`, `asignado_por` (FK usuario).

Índices: `(empresa_id)`, `(factura_id)`, `(vendedor_id)`, `(cobrador_id)`, `(estado)`, `(fecha_cobro_programada)`.

#### `rutas_cobranza`
Plan diario de un cobrador.

Campos: `cobrador_id`, `fecha`, `estado` (`"planificada" | "en_curso" | "completada" | "cancelada"`), `observaciones`, `total_facturas`, `total_cobrado`.

#### `ruta_facturas`
Detalle ordenado de la ruta. Cascada con la ruta.

Campos: `ruta_id`, `asignacion_factura_id`, `orden`, `cobrado` BOOLEAN, `monto_cobrado`, `fecha_cobro`, `notas`.

### 2.3 Comisiones

#### `comisiones` (vendedor/cobrador)

Campos:
- `vendedor_cobrador_id` (FK).
- `factura_id` (FK, nullable) — set si `tipo = "venta"`.
- `recibo_id` (FK → `recibos_cobro`, nullable) — set si `tipo = "cobranza"`.
- `tipo` VARCHAR(20): `"venta" | "cobranza"`.
- `monto_base` DECIMAL(18,2) — total factura o monto cobrado del recibo.
- `porcentaje_aplicado` DECIMAL(5,2) nullable.
- `monto_fijo_aplicado` DECIMAL(18,2) default 0.
- `monto_comision` DECIMAL(18,2) — resultado final.
- `estado` VARCHAR(20): `"pendiente" | "aprobada" | "liquidada" | "cancelada"`.
- `liquidacion_id` (FK, nullable) — set al liquidar.
- `fecha_liquidacion`, `observaciones`.

Índices: por `empresa_id`, `vendedor_cobrador_id`, `factura_id`, `recibo_id`, `estado`, `tipo`, `liquidacion_id`.

#### `comisiones_supervisores`

Agregación periódica del equipo. NO referencia comisiones individuales — se calcula sumando los `monto_base` del equipo en el rango.

Campos: `supervisor_id`, `empresa_id`, `periodo_desde`, `periodo_hasta`, `total_ventas`, `total_cobros`, `comision_ventas`, `comision_cobros`, `total_comision` (DECIMAL(19,4)), `estado` (`"pendiente" | "liquidada"`), `observaciones`.

#### `liquidaciones_comisiones`

Un solo modelo cubre ambos tipos vía `tipo`.

Campos:
- `tipo` VARCHAR(20) default `"vendedor"`: `"vendedor" | "supervisor"`.
- `vendedor_cobrador_id` (FK nullable) — set si `tipo = vendedor`.
- `supervisor_id` (FK nullable) — set si `tipo = supervisor`.
- `comision_supervisor_id` (FK → `comisiones_supervisores`, nullable) — solo para supervisor.
- `periodo_desde`, `periodo_hasta` DATE.
- `total_comisiones` DECIMAL(18,2), `cantidad_facturas` INT.
- `estado` VARCHAR(20): `"pendiente" | "pagada" | "anulada"`.
- `fecha_pago`, `observaciones`.

### 2.4 Rendiciones de cobranza

#### `rendiciones_cobranza`

Workflow con seis estados:

```
BORRADOR ─enviar─▶ PENDIENTE ─aprobar─▶ APROBADO  ✅
                       ├─observar─▶ OBSERVADO ─reenviar─▶ PENDIENTE
                       └─rechazar─▶ RECHAZADO  ❌
BORRADOR ─anular─▶ ANULADO
```

Campos relevantes:
- `cobrador_id`, `ruta_id` (nullable), `moneda_id` (única por rendición).
- `fecha_rendicion` (timestamp creación), `fecha_cobranza` (DATE de la operación).
- `numero` INT secuencial (se asigna al enviar a tesorería) + `codigo` VARCHAR(20) único.
- Montos: `monto_esperado`, `monto_cobrado`, `monto_efectivo`, `monto_cheque`, `monto_transferencia`, `monto_tarjeta`, `total_declarado`, `total_verificado`, `diferencia`, `cantidad_recibos`.
- Auditoría de workflow: `supervisor_id`, `fecha_envio_tesoreria`, `fecha_aprobacion`, `aprobado_por`, `motivo_rechazo`, `observaciones_cobrador`, `observaciones_supervisor`.
- Salida: `caja_destino_id`, `asiento_id`.
- Soft delete: `deleted_at`.

#### `rendicion_historial`
Log de transiciones. `(estado_anterior, estado_nuevo, usuario_id, motivo)`. Cascada con la rendición.

### 2.5 Tablas conectadas (relevantes pero fuera de este módulo)

- `recibos_cobro` ya tiene FK `cobrador_id` y `rendicion_id` — los recibos viajan adheridos a una rendición.
- `recibo_cobro_medios_pago` lleva la verificación granular por medio (`verificacion_estado`, `importe_verificado`, `verificado_por`, `verificado_at`).
- `factura_cab.dtotal` y `factura_cab.dfeemide` son las fuentes de `monto_base` y filtro de período para comisión de venta.

---

## 3. Módulos backend (NestJS)

### 3.1 `src/vendedores-cobradores/`

Cinco controladores conviven en este módulo.

#### `vendedores-cobradores.controller.ts`
CRUD de la persona vendedor/cobrador.

| Método | Ruta | Permiso |
|---|---|---|
| POST | `/vendedores-cobradores` | `COB_CBR_COBRADOR_CREAR` |
| GET | `/vendedores-cobradores` | `COB_CBR_COBRADOR_VER` |
| GET | `/vendedores-cobradores/vendedores-activos` | VER |
| GET | `/vendedores-cobradores/cobradores-activos` | VER |
| GET | `/vendedores-cobradores/mi-cobrador` | (usuario logueado) |
| GET | `/vendedores-cobradores/:id` | VER |
| GET | `/vendedores-cobradores/:id/estadisticas?desde&hasta` | VER |
| PATCH | `/vendedores-cobradores/:id` | EDITAR |
| PATCH | `/vendedores-cobradores/:id/toggle-active` | EDITAR |
| DELETE | `/vendedores-cobradores/:id` | ELIMINAR |

#### `asignacion-facturas.controller.ts`
Asignación + comisiones individuales.

| Método | Ruta | Para qué |
|---|---|---|
| GET | `/asignacion-facturas/pendientes` | Facturas sin cobrador, paginadas |
| POST | `/asignacion-facturas` | Asignar 1 factura a 1 cobrador |
| POST | `/asignacion-facturas/masivo` | Bulk assign |
| POST | `/asignacion-facturas/vendedor` | Asignar vendedor a factura |
| GET | `/asignacion-facturas/cobrador/:id` | Cartera de un cobrador |
| GET | `/asignacion-facturas/resumen-cobradores` | KPIs por cobrador |
| PATCH | `/asignacion-facturas/:id/estado` | Cambiar estado asignación |
| GET | `/asignacion-facturas/resumen-comisiones` | Vista de comisiones agrupada (UI principal) |
| GET | `/asignacion-facturas/comisiones-detalle` | Drill-down |
| GET | `/asignacion-facturas/resumen-comisiones/pdf` | PDF |
| GET | `/asignacion-facturas/comisiones-detalle/pdf` | PDF |
| POST | `/asignacion-facturas/generar-comisiones` | Backfill para facturas existentes |
| DELETE | `/asignacion-facturas/comisiones/:id` | Borrar comisión pendiente |

Permisos: `COB_ASG_ASIGNACION_*`, `COB_COM_COMISION_*`.

#### `liquidaciones.controller.ts`

| Método | Ruta | Descripción |
|---|---|---|
| GET | `/liquidaciones/comisiones-pendientes` | Para previsualizar antes de liquidar |
| GET | `/liquidaciones/resumen` | KPIs por persona (lo que ve el panel) |
| GET | `/liquidaciones?vendedor_cobrador_id&estado&tipo` | Listado |
| GET | `/liquidaciones/:id` | Detalle con comisiones incluidas |
| POST | `/liquidaciones` | Crear (agrupa pendientes del rango) |
| PATCH | `/liquidaciones/:id/pagar` | Marcar pagada |
| PATCH | `/liquidaciones/:id/anular` | Anular + devolver comisiones a pendiente |
| GET | `/liquidaciones/lista/pdf` | PDF historial |
| GET | `/liquidaciones/:id/pdf` | PDF comprobante individual |
| POST | `/liquidaciones/generar-comision-venta` | Uso interno (eventos) |
| POST | `/liquidaciones/generar-comision-cobranza` | Uso interno (eventos) |

Permisos: `COB_COM_LIQUIDACION_{VER,GENERAR,PAGAR,ANULAR}`.

#### `rutas-cobranza.controller.ts`

| Método | Ruta | Descripción |
|---|---|---|
| POST | `/rutas-cobranza` | Crear con facturas |
| GET | `/rutas-cobranza` | Listar (cobrador, fecha, estado) |
| GET | `/rutas-cobranza/:id` | Detalle ordenado |
| PATCH | `/rutas-cobranza/:id/estado` | Transición de estado validada |
| POST | `/rutas-cobranza/:id/facturas` | Agregar facturas a ruta `planificada` |
| GET | `/rutas-cobranza/:id/hoja-ruta` | Datos para impresión |

#### `zonas-cobranza.controller.ts`
CRUD plano de zonas, sin lógica especial.

### 3.2 `src/supervisores/`

Un solo controlador `supervisores.controller.ts` con CRUD + gestión de equipo + cálculo/liquidación de comisión por período.

Endpoints destacados:
- `GET /supervisores/:id/equipo` / `POST .../equipo` / `DELETE .../equipo/:vcId` — gestionar miembros.
- `GET /supervisores/:id/comisiones/preview?desde&hasta` — calcular **sin persistir**, con desglose por miembro del equipo.
- `POST /supervisores/:id/comisiones/generar` — crear `comisiones_supervisores` para el período.
- `POST /supervisores/comisiones/:id/recalcular` — recálculo con datos actuales (útil si cambiaron comisiones individuales después).
- `PATCH /supervisores/comisiones/:id/liquidar` — marcar como liquidada (sin crear `liquidaciones_comisiones` — ver §6.1).
- `GET /supervisores/comisiones/:id/detalle` — desglose por miembro.
- `GET /supervisores/reporte-comisiones` + variantes PDF (`/resumen/pdf`, `/detalle/pdf`).

### 3.3 `src/rendiciones/`

`rendiciones.controller.ts`. Endpoints organizados por acción de workflow + reportes.

Acciones de workflow:
- `POST /rendiciones` — crea en `BORRADOR`, agrupa recibos por moneda.
- `POST /rendiciones/:id/enviar-tesoreria` — `BORRADOR|OBSERVADO → PENDIENTE`, asigna `numero` y `codigo`, calcula diferencia.
- `PATCH /rendiciones/:id/medios-pago/:mpId/verificar` — verificación granular por medio.
- `POST /rendiciones/:id/aprobar` — `PENDIENTE → APROBADO` + genera asiento contable.
- `POST /rendiciones/:id/rechazar` — `PENDIENTE → OBSERVADO|RECHAZADO` con motivo.

Reportes:
- `/resumen-diario?fecha`
- `/recibos-disponibles?cobrador_id&fecha&moneda_id` — para armar nueva rendición.
- `/reportes/cobranzas-dia`, `/reportes/pendientes`, `/reportes/diferencias`, `/reportes/comisiones-liquidar`.

Permisos: `COB_RND_RENDICION_{VER,CREAR,APROBAR}`.

---

## 4. Lógica de cálculo de comisiones

### 4.1 Comisión por venta

**Disparador:** `asignacion-facturas.service.ts` → `asignarCobrador()` (también `asignarVendedor()`).

**Fórmula:**
```
monto_base       = factura_cab.dtotal
porcentaje       = vendedores_cobradores.comision_venta
fijo             = vendedores_cobradores.comision_fija
monto_comision   = (monto_base * porcentaje / 100) + fijo

if monto_comision <= 0 → NO se crea registro
```

Se persiste con `tipo = "venta"`, `factura_id` seteada, `recibo_id = NULL`, `estado = "pendiente"`.

### 4.2 Comisión por cobranza

**Disparador:** evento de creación/actualización de `recibos_cobro` → `generarComisionCobranzaPorRecibo(reciboId)`.

**Fórmula:** análoga, usando `recibos_cobro.monto_total` como `monto_base` y `comision_cobranza` como `%`. Persiste con `tipo = "cobranza"`, `recibo_id` seteado.

### 4.3 Backfill histórico

`POST /asignacion-facturas/generar-comisiones` busca facturas que **tienen asignación pero NO tienen comisión** registrada y las genera. Idempotente (no duplica).

### 4.4 Comisión de supervisor

Se calcula sobre **`comisiones` individuales** del equipo en el período (no sobre las facturas/recibos directamente). Esto significa que un supervisor solo cobra por miembros que tienen sus propios % configurados.

**Regla de atribución (en cascada):**

1. **Override por dirección del cliente:** si `factura.cliente_direccion.supervisor_id == supervisor_id`, la comisión cuenta para este supervisor **aunque el vendedor no esté en su equipo**. (Esto permite carteras "VIP" con supervisor dedicado.)
2. Si no, si el `vendedor_cobrador` está en `supervisor_vendedores_cobradores` para ese supervisor → cuenta.
3. Para cobros, solo aplica la regla #2 (cobradores del equipo).

**Cálculo:**
```
total_ventas      = Σ monto_base de comisiones tipo=venta del equipo en el período
total_cobros      = Σ monto_base de comisiones tipo=cobranza del equipo en el período
comision_ventas   = total_ventas * porcentaje_comision_ventas / 100
comision_cobros   = total_cobros * porcentaje_comision_cobros / 100
total_comision    = comision_ventas + comision_cobros
```

`GET /supervisores/:id/comisiones/preview` devuelve este cálculo **con desglose por miembro** sin persistir. `POST .../generar` lo persiste en `comisiones_supervisores`.

---

## 5. Liquidaciones

### 5.1 Crear liquidación de vendedor/cobrador

`POST /liquidaciones` con `{ vendedor_cobrador_id, periodo_desde, periodo_hasta, observaciones? }`.

Pasos del service (`liquidaciones.service.ts`):

1. Buscar `comisiones` con `vendedor_cobrador_id` + `estado="pendiente"` + `created_at` en el rango (inclusive, hasta `23:59:59`).
2. Si no hay → `BadRequestException`.
3. Crear `liquidaciones_comisiones` con `total_comisiones = Σ monto_comision`, `cantidad_facturas = count`, `tipo = "vendedor"`, `estado = "pendiente"`.
4. **Update masivo** de las comisiones: `estado = "liquidada"`, `liquidacion_id`, `fecha_liquidacion = now`.
5. Llamar `ContabilidadIntegracionService.integrarDevengamientoComisiones(liquidacionId)` → genera asiento **Gasto comisiones / Comisiones a pagar**.

### 5.2 Marcar pagada

`PATCH /liquidaciones/:id/pagar` con `{ fecha_pago? }` → `estado = "pagada"`, dispara asiento de **Comisiones a pagar / Caja-Banco** (pago efectivo).

### 5.3 Anular

`PATCH /liquidaciones/:id/anular` → revierte:
- `estado = "anulada"`.
- Comisiones asociadas vuelven a `estado = "pendiente"` + `liquidacion_id = NULL`.
- Asiento contable revertido.

### 5.4 PDFs

- `/liquidaciones/lista/pdf` — historial filtrado.
- `/liquidaciones/:id/pdf` — comprobante con detalle de comisiones incluidas.

---

## 6. Frontend (pos-ventas)

### 6.1 Pantalla **Contactos → Vendedores y Cobradores**

`/src/pages/Contactos.jsx` despacha al tab "Vendedores y Cobradores", que carga `components/organismos/VendedoresCobradoresDesign/VendedoresCobradoresModule.jsx`. Internamente expone dos sub-tabs:

#### Sub-tab "Equipo"
`VendedoresCobradoresList.jsx`. Header con 4 KPIs (Total, Activos, Vendedores, Cobradores). Tabla con columnas: Nombre+Doc, Código, Tipo (chip), Contacto (tel+email), Usuario, Zona, Comisión (chip compacto `V: 5%  C: 3%  +Gs.100000`), Estado, Acciones (editar / toggle active). Filtros: búsqueda, Tipo, Estado.

Dialog "Nuevo / Editar":
- `tipo` (select) → controla qué campos de comisión se muestran.
- Identidad: nombre*, apellido, código, documento, teléfono, email, dirección.
- `usuario_id` (autocomplete con usuarios del sistema, opcional).
- `zona_id` (select, solo si `tipo` incluye cobrador).
- Sección "Comisiones": `% Comisión Venta` (vendedor/ambos), `% Comisión Cobranza` (cobrador/ambos), `Comisión Fija` (siempre).
- `meta_mensual`.

#### Sub-tab "Supervisores"
`SupervisoresList.jsx`. Tabla con Nombre, Código, Documento, **% Com. Ventas**, **% Com. Cobros**, Equipo (chip con count), Estado, Acciones (gestionar equipo / editar / toggle).

Dialog crear/editar: identidad + `porcentaje_comision_ventas`, `porcentaje_comision_cobros`.

**Dialog "Gestionar equipo"** (`EquipoDialog`): autocomplete multi-select con todos los V/C disponibles (excluyendo ya asignados) + lista de miembros actuales con botón "Quitar".

### 6.2 Pantalla **Tesorería / Finanzas**

`/src/pages/Tesoreria.jsx` con tabs (12 tabs filtrados por permisos): Caja del Día, **Comisiones**, **Liquidaciones**, **Asignación**, **Rendiciones**, Recibos, Libro Retenciones, Saldos a Favor, Revisión CxC, Hoja de Ruta, Orden de Pago, Cuentas a Pagar.

#### Tab "Comisiones" — `ComisionesPanel.jsx`
Filtros: Período (`mes_actual | mes_anterior | trimestre | anio`), Vendedor/Cobrador, Moneda.
KPIs: Total Ventas, Total Cobranzas, Comisiones Generadas.
Tabla: Vendedor/Cobrador, Tipo, Ventas, Cobranzas, % Comisión, Comisión Generada, Estado.
Acciones: PDF Resumen, PDF Detallado, **Generar comisiones históricas**.
Sub-tabs internos: "Vendedores/Cobradores" y "Supervisores" — el segundo usa `SupervisoresComisionesTab.jsx` con preview por miembro.

#### Tab "Liquidaciones" — `LiquidacionesPanel.jsx`
Filtro: Moneda.
Sección **"Resumen de comisiones pendientes"** — KPI cards por persona con pendientes + liquidadas.
Sección **"Historial de liquidaciones"** — filtro adicional por vendedor + botones PDF Historial / Nueva Liquidación. Tabla con Beneficiario, Origen (badge Vendedor/Supervisor), Período, Facturas, Total, Estado, Fecha pago, Acciones.

Dialog "Nueva liquidación": autocomplete vendedor/cobrador + DatePickers desde/hasta (default: mes actual) + observaciones.

Dialog "Detalle": tabla de comisiones incluidas + acciones "Comprobante PDF" / "Anular" / "Marcar como pagada".

#### Tab "Asignación" — `AsignacionCobranzaPanel.jsx`
Listado paginado de facturas pendientes con checkboxes para asignar masivamente. Filtros: búsqueda, moneda, rango de fecha, cobrador asignado. Soporta asignar vendedor individual y cobrador (individual o masivo).

#### Tab "Rendiciones" — `components/organismos/RendicionesDesign/RendicionesPanel.jsx`
KPIs diarios + listado con filtros (cobrador, fecha, estado, moneda). Detalle con verificación de medios de pago. Workflow completo (crear → enviar → aprobar/rechazar).

#### Tab "Hoja de Ruta" — `components/tesoreria/HojaRutaTab.jsx`
Mapa visual de asignación cobradores ↔ clientes. Filtra por estado de deuda (vencido / al día / próximos 7/15/30 días / con promesa). Permite arrastrar clientes a cobradores.

#### Otras tabs (resumen 1 línea)
- **Recibos** — listado/auditoría de `recibos_cobro`.
- **Libro Retenciones** — auditoría de retenciones por período.
- **Saldos a Favor** — gestión de notas de crédito y créditos no aplicados.
- **Revisión CxC** — split-screen para reparar CxC cliente por cliente.
- **Orden de Pago** — agrupar facturas de compra para pago a proveedor.
- **Caja del Día** — apertura/cierre de cajas.

### 6.3 Servicios API + tanstack

| Archivo | Endpoints que cubre |
|---|---|
| `src/api/vendedores-cobradores.service.js` | CRUD V/C + zonas + comisiones (resumen/detalle/PDF/generar) + asignación de facturas + liquidaciones (CRUD + pagar/anular + PDF) |
| `src/api/supervisores.service.js` | CRUD supervisores + equipo + comisión supervisor (preview/generar/recalcular/liquidar/eliminar) + reportes PDF |
| `src/api/rendiciones.service.js` | Workflow rendiciones + reportes + recibos-disponibles |
| `src/tanstack/RendicionesStack.jsx` | Hooks tanstack para todo lo anterior |
| `src/tanstack/TesoreriaStack.jsx` | Hooks para caja del día |

Query keys principales:
- `["vendedores-cobradores"]`, `["vendedores-activos"]`, `["cobradores-activos"]`
- `["zonas-cobranza"]`
- `["supervisores", search, active]`, `["supervisor-equipo", id]`
- `["resumen-comisiones", desde, hasta, monedaId]`, `["resumen-liquidaciones", monedaId]`, `["liquidaciones", vcId]`, `["liquidacion-detalle", id]`
- `["empresa-monedas"]`

### 6.4 Enums frontend
`src/components/_standards/enums/estados.js`:
- `ESTADO_COMISION` = `{ PENDIENTE, APROBADA, LIQUIDADA, CANCELADA }`
- `ESTADO_LIQUIDACION` = `{ PENDIENTE, PAGADA, ANULADA }`
- `TIPO_COMISION` = `{ VENTA, COBRANZA }`
- `TIPO_BENEFICIARIO` = `{ VENDEDOR, COBRADOR, SUPERVISOR }`

Con metadata de color para chips MUI (warning/info/success/error/default).

---

## 7. Permisos

Módulo `COBRANZAS`. Permisos chequeados con `hasPermission("COBRANZAS", code)` / `canCobranzas(code)`:

| Sub-área | Códigos |
|---|---|
| Cobradores/Vendedores | `COB_CBR_COBRADOR_{VER,CREAR,EDITAR,ELIMINAR}` |
| Asignación facturas | `COB_ASG_ASIGNACION_{VER,CREAR}` |
| Comisiones | `COB_COM_COMISION_{VER,GENERAR,ELIMINAR}` |
| Liquidaciones | `COB_COM_LIQUIDACION_{VER,GENERAR,PAGAR,ANULAR}` |
| Rendiciones | `COB_RND_RENDICION_{VER,CREAR,APROBAR}` |

Supervisores no tienen códigos explícitos enumerados aquí — el controlador los protege con la guard de empresa estándar.

---

## 8. Eventos y disparadores

| Evento de negocio | Tabla afectada | Quién dispara |
|---|---|---|
| Asignar vendedor o cobrador a factura | `comisiones` (venta) | `asignarVendedor()` / `asignarCobrador()` |
| Emitir recibo de cobro con cobrador | `comisiones` (cobranza) | `generarComisionCobranzaPorRecibo()` (evento al crear/actualizar `recibos_cobro`) |
| Crear liquidación | `liquidaciones_comisiones` + asiento devengamiento | `liquidaciones.service.crearLiquidacion()` |
| Marcar liquidación pagada | Asiento de pago | `marcarComoPagada()` |
| Aprobar rendición | Asiento de ingreso a caja | `rendiciones.service.aprobar()` |
| Generar comisión supervisor | `comisiones_supervisores` | Manual (no automático) |

---

## 9. Cosas NO triviales que conviene saber

1. **El service de supervisor calcula sobre `comisiones` individuales, no sobre facturas.** Si un vendedor tiene `comision_venta = 0`, sus ventas **no entran** en la base del supervisor. Esto es contraintuitivo y vale documentarlo en el código.

2. **Override por dirección del cliente** (`cliente_direccion.supervisor_id`) tiene **prioridad absoluta** sobre el equipo del supervisor. Sirve para cuentas clave.

3. **Una rendición es de una sola moneda.** Si un cobrador tuvo recibos en PYG y USD el mismo día, son dos rendiciones separadas.

4. **`liquidaciones_comisiones` es un solo modelo con dos personalidades** (vendedor vs supervisor). Discriminado por `tipo` y por cuál FK está seteada (`vendedor_cobrador_id` xor `supervisor_id`). Atención al filtrar — siempre poner `tipo` en el where.

5. **`PATCH /supervisores/comisiones/:id/liquidar` cambia el estado de `comisiones_supervisores` directamente.** NO crea un registro en `liquidaciones_comisiones`. Hay endpoints que sí lo hacen (la creación pasa `tipo=supervisor` + `comision_supervisor_id`), pero el "liquidar rápido" del panel de supervisor no integra al historial unificado de liquidaciones. **Esto es un gap.** Ver §10.

6. **El backfill `generar-comisiones`** solo cubre comisiones por **venta** (facturas con asignación sin comisión). No regenera comisiones de cobranza.

7. **`monto_base` se guarda en moneda original** (la de la factura o el recibo). No hay conversión a moneda funcional. El panel de comisiones obliga a filtrar por moneda por eso.

8. **El cálculo combina `%` y fijo aditivamente:** `comision = base × % + fijo`. No es una ni otra. Si querés solo fijo, dejá `%` en 0.

9. **Estado `"aprobada"` de comisión está definido pero no se usa en el flujo actual.** Las comisiones pasan directo de `pendiente → liquidada`. El estado existe para un eventual workflow de aprobación intermedia.

10. **Rendiciones tienen soft delete (`deleted_at`)**, pero el resto del módulo usa `active`. Inconsistente.

---

## 10. Gaps detectados y extensiones naturales

Esto es lo que **falta** o está **a medias** y serían los próximos features lógicos:

### Gaps en la lógica
- **G1.** Liquidaciones unificadas de supervisor: el endpoint "liquidar rápido" cambia estado de `comisiones_supervisores` sin crear `liquidaciones_comisiones` con `tipo=supervisor`. Resultado: el historial unificado se llena solo si el operador usa el flujo largo. **Acción:** que `liquidarComision()` de supervisor cree siempre el registro en `liquidaciones_comisiones`.
- **G2.** Asiento contable de **comisión de supervisor**: no se invoca el devengamiento al liquidar comisión de supervisor (sí al de vendedor). **Acción:** llamar `ContabilidadIntegracionService` desde el flujo de supervisor.
- **G3.** Backfill de comisiones de **cobranza**: solo existe para ventas. Si se cargan recibos con cobrador definido **después** de configurar `comision_cobranza`, no se regeneran. **Acción:** endpoint análogo a `generar-comisiones` pero para recibos.
- **G4.** Recálculo automático de **comisión de supervisor pendiente** cuando cambian comisiones individuales del equipo. Hoy hay que hacer "Recalcular" manual.
- **G5.** Estado `"aprobada"` no usado: o lo implementamos como workflow opcional (aprobación de gerencia antes de liquidar) o lo removemos del enum.
- **G6.** Caps / banding por monto. Hoy es lineal `base × %`. Casos reales suelen tener "primeros 10M al 5%, excedente al 3%", o tope mensual. Modelo necesita una tabla `comision_escala` o JSON config.
- **G7.** Comisiones por **producto/categoría** (a un vendedor le pagan distinto según qué vendió). Hoy es por persona, no por línea.
- **G8.** Reversas automáticas: si una factura se anula después de generar la comisión, la comisión queda viva. Falta listener `factura.anulada → comision.cancelada`.

### Gaps en la UI
- **U1.** El panel "Comisiones" no muestra **a qué supervisor** quedó atribuida cada comisión (útil para auditar reglas de override por dirección).
- **U2.** No hay **dashboard del propio vendedor/cobrador** ("mis comisiones del mes", "mi meta vs avance"). Existen `PanelCobrador.jsx` y `PanelSupervisor.jsx` pero no están conectados al módulo desde la home.
- **U3.** Faltan **alertas** ("tenés N comisiones pendientes hace > X días sin liquidar").
- **U4.** El generador de comisiones históricas es opaco: no muestra preview de cuántas se van a generar antes de ejecutar.
- **U5.** Hoja de Ruta no es imprimible desde el panel de cobranza (el endpoint existe `/rutas-cobranza/:id/hoja-ruta` pero la UI todavía no lo consume).

### Gaps en reportes
- **R1.** Reporte **ranking de vendedores** por período (top N).
- **R2.** Reporte de **cumplimiento de metas** (`meta_mensual` se guarda pero no se reporta).
- **R3.** Exportación a **Excel/CSV** (hoy solo PDF).
- **R4.** Comparativo **mensual / interanual** de comisiones por persona.

### Consistencia
- **C1.** Unificar soft-delete: usar `deleted_at` (estilo rendiciones) o `active` (estilo el resto), no ambos.
- **C2.** Migrar VARCHAR de estados a **enums Prisma** (regla del proyecto en `feedback_enums_indices.md`). Hoy son strings libres con valores convencionados.
- **C3.** Estados de comisión en backend están en **minúsculas** (`pendiente`), pero rendiciones están en **MAYÚSCULAS** (`BORRADOR`). Unificar.

---

## 11. Mapa de archivos (referencia rápida)

```
backend (/var/www/html/proyectos/smartfactvoice-backend/src/)
├── vendedores-cobradores/
│   ├── vendedores-cobradores.controller.ts / .service.ts
│   ├── asignacion-facturas.controller.ts / .service.ts
│   ├── liquidaciones.controller.ts / .service.ts
│   ├── rutas-cobranza.controller.ts / .service.ts
│   └── zonas-cobranza.controller.ts / .service.ts
├── supervisores/
│   └── supervisores.controller.ts / .service.ts
├── rendiciones/
│   └── rendiciones.controller.ts / .service.ts
└── (contabilidad/) ContabilidadIntegracionService

frontend (/var/www/html/proyectos/pos-ventas/src/)
├── pages/
│   ├── Contactos.jsx
│   ├── Tesoreria.jsx
│   ├── PanelCobrador.jsx
│   └── PanelSupervisor.jsx
├── components/organismos/VendedoresCobradoresDesign/
│   ├── VendedoresCobradoresModule.jsx
│   ├── VendedoresCobradoresList.jsx
│   ├── SupervisoresList.jsx
│   ├── ComisionesPanel.jsx + ComisionesDetalleDialog.jsx + ComisionesPdfModal.jsx
│   ├── LiquidacionesPanel.jsx + LiquidacionesPdfModal.jsx
│   ├── SupervisoresComisionesTab.jsx + ComisionSupervisorDetalleDialog.jsx + SupervisoresPdfModal.jsx
│   ├── AsignacionCobranzaPanel.jsx
│   ├── VendedorSelector.jsx
│   └── ZonasCobranzaList.jsx
├── components/organismos/RendicionesDesign/RendicionesPanel.jsx
├── components/tesoreria/ (RevisionCxCTab, HojaRutaTab, ArqueoPdfModal)
├── api/ (vendedores-cobradores, supervisores, rendiciones, asignaciones).service.js
├── tanstack/ (RendicionesStack, TesoreriaStack).jsx
└── components/_standards/enums/estados.js
```

---

## 12. Para el dev que continúe

Si tu próxima tarea es **agregar funcionalidad sobre este módulo**, leé en este orden:

1. §1-2: te da el modelo mental + los nombres exactos de tablas.
2. §9: las trampas no obvias — más que un README, son el tribal knowledge.
3. §10: pickear de acá el feature concreto. Cada bullet es un PR autónomo.
4. §11: dónde tocar los archivos.

Reglas a respetar (del repo):
- Backend en NestJS + Prisma. Enums Prisma para todo dominio cerrado (gap C2 abierto).
- Frontend con UI Standards (`pos-ventas/docs/ui-standards.md`): `MonedaInput` para montos, `EmptyState`, `ScreenGuia`, fechas vía `utils/fecha.js`. Nunca `<TextField type="number">` para dinero ni `new Date().toLocaleDateString()` directo.
- Permisos siempre por módulo `COBRANZAS` con códigos `COB_*`.
- Tanstack Query keys consistentes con los listados en §6.3.
- Tests: unit con Prisma mockeado para services, integration con DB real para flujos contables.
