# Plan: Mejoras Compras / Cuentas por Pagar / Emisión — Jul 2026

## Contexto

Conjunto de 6 mejoras pedidas por usuarios/clientes, agrupadas en un plan por fases (de menor a
mayor complejidad). Tocan emisión (preview antes de registrar), compras (timbrado del proveedor,
NC de compra), y cuentas por pagar (PDF estado de cuenta, orden de pago de contado). Objetivo:
cerrar huecos operativos que hoy obligan a recargar datos, no dan visibilidad de deuda a proveedores,
o impiden circuitos de pago/devolución completos.

Decisiones tomadas con el usuario:
- **Orden de pago contado** → flag **configurable por empresa** (default OFF, no rompe nada).
- **NC de compras** → **registro interno + reversión** (stock + CxP), **sin envío a SIFEN** (la emite el proveedor).
- **Preview** → componente **reutilizable** en POS-Admin (hoy texto plano) y NC (mejora del actual).
- **Entrega** → plan completo, **4 fases**, quick wins primero.

### Hallazgos clave de la exploración (para no re-descubrir)
- Los PDFs (estado de cuenta, orden de pago) los arma el microservicio **`generador-pdf/`** (repo local,
  `API_GENERADOR_PDF`), no pdfmake local. Estado de cuenta CxC: `generador-pdf/src/estado-cuenta/estado_cuenta_a4.js`.
- **El filtro "solo vencidas" del estado de cuenta CxC está en el template**, no en el backend:
  `estado_cuenta_a4.js:73-77` (`todas.filter(c => c.estado === "Vencida")`). El backend
  `cobros.service.ts` `getEstadoCuentaCliente` (línea ~2198) **ya trae todas** las cuotas no pagadas
  (`estado: { notIn: ['pagado','Pagado'] }`, línea 2200).
- Timbrado del proveedor: **no existe** en el modelo `proveedores`; se captura manual por compra en
  `compra_cab.timbrado_proveedor` (`ComprasTemplate.jsx:2391`, `compras.service.ts:186`).
- Orden de pago (módulo real **`pagos-proveedor`**) lee de `cuentas_pagar` filtrando `saldo_pendiente>0`.
  Compras/gastos de **contado** nacen con `saldo_pendiente=0`/`estado='pagada'` → por eso no aparecen.
  Config por empresa vive en tabla **`config_compras`** (schema:933).
- NC de compras: **no existe nada**. Patrón a espejar: NC de ventas en `src/nota-creditos/`.
- Preview: NC de ventas ya tiene resumen (`NuevaNotaCreditoTemplate.jsx` `SummaryRow`:1032, dialog:944-1025);
  POS-Admin tiene confirm de texto plano (`POSAdminTemplate.jsx` dialog:2406-2464). `KudePreviewModal`
  (post-registro, PDF) es aparte y ya es reutilizable.

---

## Fase 1 — Quick wins ✅ COMPLETA (2026-07-24)

Implementado y verificado (TSC + eslint limpios, PDF proveedor renderiza OK):
- **1.1** `proveedores.timbrado` + `fecha_vencimiento_timbrado` (migración aplicada); DTO + service; ABM
  proveedor (inputs) + precarga en `ComprasTemplate.handleSelectProveedor` (no pisa lo tipeado).
- **1.2** check "Todas / Vencidas" en el estado de cuenta CxC: flag `incluir_todas` propagado
  controller→service→generador-pdf (`estado_cuenta_a4.js` parametrizado); toggle en `CuentasCobrar`.
- **1.3** PDF estado de cuenta CxP: `getEstadoCuentaProveedor` + `generateEstadoCuentaProveedorPdf` +
  endpoints en `pagos-proveedor`; template `generador-pdf/src/estado-cuenta-proveedor/` + ruta; botón PDF
  por proveedor + modal con toggle Todas/Vencidas en `CuentasPagar`.

---

## Fase 1 — Detalle (referencia)

### 1.1 Timbrado por proveedor (precarga en compra)
- **Migración**: agregar a `proveedores` los campos `timbrado String? @db.VarChar(8)` y
  `fecha_vencimiento_timbrado DateTime? @db.Date` (+ `schema.prisma`).
- **Backend**: `proveedores` DTO create/update + service — aceptar y persistir los campos. Exponerlos en
  el `findOne`/listado que consume el selector.
- **Frontend**:
  - ABM proveedor: agregar inputs Timbrado + vencimiento (form de `ProveedoresDesign`).
  - `ComprasTemplate.jsx` `handleSelectProveedor` (línea ~1391): al elegir proveedor, **precargar**
    `updateCabecera("timbrado_proveedor", prov.timbrado)` y `fecha_vencimiento_timbrado` si vienen y el
    campo está vacío. Sigue siendo editable (queda guardado en la compra como hoy).
  - `ProveedorSelector.jsx`: incluir `timbrado`/`fecha_vencimiento_timbrado` en el objeto seleccionado.
- **Opcional**: al guardar una compra con timbrado y el proveedor no tiene uno, ofrecer/actualizar el del
  proveedor (o hacerlo silencioso). Decidir en implementación; MVP = solo precarga.

### 1.2 Estado de cuenta CxC: check "incluir todas las cuotas"
Hoy el PDF muestra solo vencidas (recorte en el template). Agregar opción para incluir todas.
- **generador-pdf** `estado_cuenta_a4.js`: parametrizar el filtro (línea 73-89). Si `data.incluir_todas`
  (o `modo === 'todas'`), no filtrar por `estado==="Vencida"`; usar `pendientes` (saldo>0) y ajustar
  título ("ESTADO DE CUENTA — TODAS" vs "— VENCIDOS") y los totales/resumen.
- **Backend** `cobros.service.ts` `generateEstadoCuentaPdf` (línea ~2731): aceptar flag `incluir_todas`
  y pasarlo en el payload al microservicio (líneas 2748-2757). El controller
  `generateEstadoCuentaPdf` (`cobros.controller.ts:505`) agrega el query param.
- **Frontend** `CuentasCobrar.jsx` `EstadoCuentaPdfModal` (línea ~348): checkbox "Incluir cuotas no
  vencidas"; `getEstadoCuentaPdf` (`cobros.service.js:205`) manda el flag.

### 1.3 PDF estado de cuenta de Cuentas por Pagar (CxP)
Análogo al de CxC, pero por proveedor. La data ya existe en `cuentas_pagar` (cada fila es una cuota,
con `numero_cuota`, `fecha_vencimiento`, `saldo_pendiente`, `estado`, `origen_tipo`).
- **Backend** módulo `pagos-proveedor`:
  - `getEstadoCuentaProveedor(empresaId, proveedorId, { incluir_todas })` — arma cabecera empresa +
    proveedor + agrupa `cuentas_pagar` por documento origen (compra/gasto), con cuotas, saldos y
    clasificación vencida/al-día (espejo de `getEstadoCuentaCliente`).
  - `generateEstadoCuentaProveedorPdf(...)` — POST al microservicio.
  - Endpoints en `pagos-proveedor.controller.ts`: `GET .../estado-cuenta/:proveedorId` (JSON) y
    `.../estado-cuenta/:proveedorId/pdf`. Permiso de ver CxP existente.
- **generador-pdf**: nuevo template `src/estado-cuenta-proveedor/estado_cuenta_proveedor_a4.js`
  (clonar el de CxC, cambiar "cliente"→"proveedor", incluir el check "todas/vencidas" desde el inicio).
  Ruta en `src/routes/` (`estado_cuenta_proveedor.js`) registrada como las demás.
- **Frontend** `CuentasPagar.jsx`: botón "Estado de Cuenta" por proveedor + modal visor (reusar el
  patrón de `EstadoCuentaPdfModal` de CxC), con el checkbox "incluir todas". API en
  `pagos-proveedor.service.js`.

---

## Fase 2 — Preview de datos antes de registrar ✅ COMPLETA (2026-07-24)

- Componente reutilizable `src/components/organismos/DocumentoPreviewModal.jsx`: cabecera (campos),
  ítems, totales (por IVA + total), pagos, alerta, multi-moneda vía `fmtMoneda`.
- **POS-Admin** (`POSAdminTemplate.jsx`): reemplazado el confirm de texto plano por el preview
  (cliente, RUC, condición, moneda, depósito, vendedor, pedido/solicitud; ítems; subtotal/descuento/
  IVA10/IVA5/TOTAL; formas de pago). Imports MUI Dialog* eliminados (ya no se usaban).
- **NC** (`NuevaNotaCreditoTemplate.jsx`): reemplazado el dialog con `SummaryRow` por el preview
  (cliente, numeración, tipo doc, factura asociada, saldo; ítems; IVA + TOTAL NC; alerta si hay factura).
  Eliminado `SummaryRow` y imports MUI sin uso.
- Lint limpio (solo quedan `navigate`/`showProductSearch` pre-existentes en NC).

---

## Fase 2 — Detalle (referencia)

- **Nuevo componente reutilizable** `src/components/organismos/DocumentoPreviewModal.jsx`: recibe
  cabecera (cliente, numeración, moneda, condición de pago, fechas), `items[]`, `totales` (subtotales por
  IVA, total), y `pagos[]` opcional; render en secciones (patrón de las secciones del preview de remisión
  y del `SummaryRow` de NC). Multi-moneda: formatear con la moneda del documento (no hardcodear Gs.).
- **POS-Admin** `POSAdminTemplate.jsx`: reemplazar el dialog de confirmación de texto plano
  (líneas 2406-2464 + estado `confirmDialogOpen`) por `DocumentoPreviewModal`. El flujo submit→preview→
  `handleConfirmSave` se mantiene; solo cambia el contenido del modal.
- **NC** `NuevaNotaCreditoTemplate.jsx`: reemplazar el dialog con `SummaryRow` (944-1025) por el mismo
  componente, conservando el `<Alert>` de advertencia y `doSave`.
- No se toca `KudePreviewModal` (es post-registro, PDF).
- POS-Retail queda fuera (contado rápido; el usuario eligió Admin+NC).

---

## Fase 3 — Orden de pago con documentos de CONTADO ✅ COMPLETA (2026-07-24)

- **Migración**: `config_compras.incluir_contado_en_orden_pago BOOLEAN DEFAULT false` (aplicada).
  Agregado al `schema.prisma`, tipo `ConfigComprasAP`, todos los defaults (getDefault + creates en
  compras/gastos service), selects de `getOrCreateConfigCompras`, y DTO `update-config-compras`.
- **Compras** (`compras.service.ts`): `incluirContadoEnOP` en ambos flujos; se **salta**
  `createOrdenPagoAutomatica` cuando es contado y el flag está ON → la CxP queda `pendiente`
  (sin auto-pago). Crédito y contado-sin-flag: intactos.
- **Gastos** (`gastos.service.ts`): `dejarPendiente = esCredito || (flag && !anticipoViatico)`;
  el gasto de contado con flag queda `estado='registrado'` y su CxP `pendiente` (vencimiento = emisión,
  congela cuenta pasivo). Sin `createOrdenPagoAutomatica` en gastos → sin doble registro.
- **UI** (`ComprasConfigTab.jsx`): switch "Incluir contado en orden de pago" en el grupo "CxP y pagos"
  + default. El diff de guardado lo envía automáticamente.
- **Riesgo doble-pago cubierto**: con flag ON el contado no genera orden auto-pagada; el pago ocurre
  una sola vez, vía la OP manual.
- TSC producción limpio (solo un error pre-existente en un `.spec` de recepciones, ajeno) + eslint limpio.

### F3.b — Coherencia validación + contabilidad con flag ON (2026-07-24)
Al probar surgió el conflicto: el sistema exigía "contado debe cargar un pago" y contablemente
acreditaba **Caja** (como si se pagó) aunque la CxP quedaba pendiente → descuadre. Resuelto tratando
el contado-con-flag **igual que un crédito** (patrón espejo de `cobro_diferido` en ventas):
- **Validación de pago**: `validateReglasPagoCompra` recibe `incluirContadoEnOP`; con flag ON el contado
  no exige (ni admite) pagos[] en el alta. Frontend `ComprasTemplate`: no valida pago y **oculta la
  sección Pagos** (nota "se abona vía orden de pago"). Flag `incluir_contado_en_orden_pago` agregado a
  `features/compras/flowConfig.js`.
- **Contable** (`integracion.service.ts` `integrarFacturaCompra`): lee el flag; con flag ON + contado,
  `esContado=false` → acredita **Proveedores** (nace el pasivo), no Caja. El pago real lo asienta la
  orden de pago al ejecutarse (Dr Proveedores / Cr Caja). Asiento cuadrado, pago una sola vez.

---

## Fase 3 — Detalle (referencia)

- **Migración/config**: agregar flag `incluir_contado_en_orden_pago Boolean? @default(false)` a
  `config_compras` (schema:933) + DTO `update-config-compras.dto.ts` + UI `ComprasConfigTab.jsx`.
- **Comportamiento** (cuando el flag está ON):
  - `compras.service.ts`: para compras de **contado**, **no** llamar a `createOrdenPagoAutomatica`
    (línea ~271) ni marcar la CxP como pagada; dejar `cuentas_pagar` con `saldo_pendiente = monto`,
    `estado='pendiente'` (como crédito) para que sea elegible en la OP manual.
  - `gastos.service.ts` `createCuentaPorPagarDesdeGasto` (línea ~1195): para contado, respetar el flag —
    con flag ON, `saldo_pendiente = total`, `estado='pendiente'` (hoy nace en 0/pagada).
  - Con flag OFF: comportamiento actual intacto (contado auto-pagado).
- **`getCuentasPendientes`** (`pagos-proveedor.service.ts:804`) no cambia — al quedar las cuentas de
  contado con `saldo_pendiente>0`, entran solas.
- **Riesgo a cubrir**: no duplicar el pago. Con flag ON el contado se paga **vía la OP** (no auto). El pago
  inicial capturado en la compra/gasto debe reflejarse como pago de esa CxP o no registrarse aparte —
  revisar en implementación que no se registre dos veces (caja + OP).

---

## Fase 4 — Nota de Crédito de Compras ✅ COMPLETA (2026-07-24)

- **Modelos + migración**: `nota_credito_compra_cab` + `nota_credito_compra_det` (aplicadas). Referencian
  `compra_cab`/proveedor/moneda/depósito; sin campos SIFEN. Relaciones inversas en proveedores, compra_cab,
  moneda, depositos, productos, empresas.
- **Backend** módulo `src/nota-credito-compras/` (controller + service + DTO, registrado en app.module):
  - `create`: calcula totales (IVA incluido PY); **decrementa stock** por ítem marcado `afecta_stock`
    (solo productos con `maneja_inventario`, movimiento `nota_credito_compra`); **reduce `cuentas_pagar`**
    del proveedor consumiendo cuotas pendientes FIFO. Auditoría.
  - `findAll`/`findOne`; `anular` (repone stock + restaura saldo CxP + auditoría).
  - Rutas `POST/GET /nota-credito-compras`, `GET /:id`, `PATCH /:id/anular` (401 con guard, TSC limpio).
- **Frontend**: tab **"Notas de crédito"** en `ComprasTemplate`; `NotasCreditoCompraTab` (listado + paginación
  + EmptyState); `NuevaNotaCreditoCompraDialog` (proveedor + compra origen que precarga ítems editables +
  checkbox Stock por ítem + depósito + totales); `NotaCreditoCompraDetalleDialog` (detalle + anular).
  API `nota-credito-compras.service.js`. Multi-moneda con `fmtMoneda`. Lint limpio.

**Limitación MVP**: si la NC supera el saldo pendiente de CxP, el excedente no genera un saldo a favor
formal (se aplica hasta agotar cuotas). Refinable si se requiere nota de débito/saldo a favor del proveedor.

### F4.b — Pantalla completa + parciales + contabilidad (2026-07-24)
Tras revisión con el usuario, se reemplazó el modal por pantalla completa y se agregó control parcial + asiento:
- **NC parciales por ítem**: migración `compra_det.cantidad_acreditada_nc` (aplicada). El `create` valida
  `cantidad <= (cantidad - acreditado)` por ítem con `compra_det_id` e **incrementa** el acumulado; `anular`
  lo **revierte**. Permite varias NC parciales sobre la misma compra sin pasarse. El frontend muestra la
  columna **"Disp."** y precarga la cantidad disponible.
- **Contabilidad** (`integracion.service.ts` `integrarNotaCreditoCompra`): **asiento inverso** a la compra —
  **DEBE Proveedores** / **HABER Inventario-Gasto** (neto) + **HABER IVA Crédito 10/5** (reversa el crédito
  fiscal). Se dispara fire-and-forget al crear; `anular` llama a `revertirDocumento`. Aparece en Libro IVA
  Compras como resta. Módulo importa `ContabilidadModule`.
- **Pantalla completa** `NuevaNotaCreditoCompraTemplate` en ruta `/compras/notas-credito/nueva` (como Factura/
  Remisión, con ScreenGuia + header + volver). El tab "Notas de crédito" **navega** a la pantalla (ya no
  abre modal). `ComprasTemplate` lee `?tab=notas_credito` para volver a la tab tras registrar. Dialog viejo
  eliminado. TSC + eslint limpios.

---

## Fase 4 — Diseño (referencia)

Documento que **emite el proveedor** y la empresa **registra** (no se emite a SIFEN). Espeja el modelo de
NC de ventas (`src/nota-creditos/`) pero apuntando a `compra_cab`/`gasto_cab` y `proveedor_id`.
- **Migración/modelos Prisma**: `nota_credito_compra_cab` (empresa_id, proveedor_id, compra_id?/origen,
  numero+timbrado del documento del proveedor, motivo, fecha, moneda, totales, estado, `saldo_disponible`)
  + `nota_credito_compra_det` (producto_id, cantidad, precio, IVA, deposito_id) + subtotales. Sin campos
  SIFEN (cdc/enlace_qr/estado_sifen).
- **Backend** nuevo módulo `src/nota-credito-compras/` (controller + service + DTO):
  - `create`: valida total NC ≤ saldo de la compra/CxP; **decrementa stock** (patrón `revertInventario`
    de `compras.service.ts:2337` / `resolveTipoMovimientoSalidaId`) para ítems con `maneja_inventario`;
    **reduce `cuentas_pagar.saldo_pendiente`** del proveedor (o genera saldo a favor si no hay CxP con
    saldo). Auditoría.
  - `findAll`/`findOne`/`anular` (revierte: repone stock + restaura saldo CxP).
  - Solo afecta stock si la compra manejaba inventario; NC sobre gasto = solo ajuste CxP.
- **Frontend**: tab "Notas de Crédito" en Compras + `NuevaNotaCreditoCompraTemplate.jsx` (elige compra/
  proveedor origen, precarga ítems, permite parcial) + detalle. Reusar `ProveedorSelector`,
  patrón de `NuevaNotaCreditoTemplate` de ventas.
- **PDF**: interno (registro), opcional en esta fase.

---

## Archivos principales por fase

- **F1.1**: `prisma/schema.prisma` (proveedores) + migración; `src/proveedores/*`;
  `ComprasTemplate.jsx`, `ProveedorSelector.jsx`, ABM proveedor.
- **F1.2**: `generador-pdf/src/estado-cuenta/estado_cuenta_a4.js`; `cobros.service.ts` (~2731),
  `cobros.controller.ts` (~505); `CuentasCobrar.jsx`, `cobros.service.js`.
- **F1.3**: `pagos-proveedor.{service,controller}.ts` + `pagos-proveedor.service.js`;
  `generador-pdf/src/estado-cuenta-proveedor/*` + ruta; `CuentasPagar.jsx`.
- **F2**: nuevo `DocumentoPreviewModal.jsx`; `POSAdminTemplate.jsx`, `NuevaNotaCreditoTemplate.jsx`.
- **F3**: `config_compras` (schema) + migración; `update-config-compras.dto.ts`, `ComprasConfigTab.jsx`;
  `compras.service.ts`, `gastos.service.ts`.
- **F4**: nuevos modelos Prisma + migración; nuevo módulo `src/nota-credito-compras/`;
  frontend tab + template NC compra en `ComprasDesign`.

## Verificación (por fase)
- **F1.1**: cargar timbrado en un proveedor → nueva compra → al seleccionarlo, el timbrado se precarga; editable; se guarda en la compra.
- **F1.2**: PDF estado cuenta CxC con y sin el check → sin check solo vencidas (como hoy), con check todas las cuotas con saldo.
- **F1.3**: proveedor con varias compras a crédito → PDF estado de cuenta CxP muestra cuotas, saldos y total adeudado; check todas/vencidas.
- **F2**: en POS-Admin y NC, al confirmar aparece el preview con ítems, totales, IVA y (POS) pagos, en la moneda del documento; confirmar registra igual que hoy.
- **F3**: con flag OFF, compra/gasto de contado no aparece en OP (igual que hoy). Con flag ON, sí aparece como pendiente y se paga vía OP sin doble registro.
- **F4**: registrar NC de compra sobre una compra con stock → decrementa stock y reduce saldo CxP; anular la NC repone ambos. NC sobre gasto solo ajusta CxP.
- `npx tsc --noEmit` backend limpio y `eslint` de archivos frontend sin errores nuevos, por fase.

## Notas / riesgos
- **F3** es la más delicada por el doble registro de pago de contado — validar el circuito caja↔OP.
- **F4** es la más grande (modelos + módulo + UI). Podría subdividirse (primero solo ajuste CxP, luego stock) si se quiere entregar antes.
- Multi-moneda: en preview (F2) y estados de cuenta, formatear con la moneda del documento; no hardcodear Gs.
