Módulo de Pagos a Proveedores — Plan de Implementación (referencia)¶
Estado: Plan indicativo — la implementación la realizará otro desarrollador; este documento y los docs 01–04 constituyen el contexto de entrega. El desglose y el orden pueden ajustarse a criterio de quien implemente, respetando las decisiones de diseño de los docs 01–04. Base:
04-diseno-tecnico.md(en_borradores/, no publicado) (arquitectura y fases). Modelo de confirmación: automática como flujo principal, manual como fallback (doc 03 §G4c / doc 04 §6). Supuesto vigente: relación pago↔extracto anclada en ticket → comprobante →BankPages.Reference; pendiente de comprobación G5.
Principios de ejecución¶
- El código de cobros no se modifica en las fases 1–2, salvo los puntos explícitos del §Fase 2 (reconciliación), cada uno con su prueba de regresión.
- Cada fase termina desplegable y verificable por sí misma contra el SL de dev (los escenarios de prueba ya están validados a mano en el doc 02 — sirven de guion).
- Una rama / PR por etapa numerada (1.1, 1.2, …) para revisión corta.
Fase 1 — Circuito SAP completo (sin banco)¶
Entregable: solicitudes de pago a proveedores operando de punta a punta contra SAP: crear (facturas o anticipo) → notificar → listar/consultar → confirmar (manual) → borrar; más pago directo. Transferencia sin carga bancaria todavía (el ticket llega en Fase 2).
| # | Etapa | Contenido | Archivos (nuevos ✚ / modificados ✎) |
|---|---|---|---|
| 1.1 | Utilidades compartidas | Extraer helpers puros a utils/ para uso de outgoing (cobros mantiene sus copias privadas; deduplicación se decide después): toSapDate, mergePaymentInvoices, armado de changeset $batch |
✚ payments/utils/payment-shared.util.ts |
| 1.2 | DTOs | CreateOutgoingPaymentDto (multipart: supplierCode, paymentAmount, currency, date, paymentType, PaymentInvoices[] con it_PurchaseInvoice/anticipo, supplierBankAccountKey?, remarks, referenceNumber?); respuesta con bankTicket?; reutilizar ConfirmPaymentWithBankPageDto |
✚ payments/dto/create-outgoing-payment.dto.ts, outgoing-payment-response.dto.ts |
| 1.3 | Repositorio SAP | Extender SapPaymentRepository: createVendorPayment, getVendorPayments (paginado + filtro CardCode), getVendorPaymentByDocEntry, findVendorPaymentByReference (Reference2/TransferReference), createPurchaseDownPaymentWithDraftBatch ($1), cancelDraftAndPurchaseDownPaymentBatch |
✎ payments/repositories/sap-payment.repository.ts |
| 1.4 | Resolución de cuenta origen | Servicio pequeño que resuelve moneda → HouseBankAccounts (U_MonedaPago) → GLAccount + AccNo + U_CuentaHash, con caché en memoria (los datos casi no cambian). Falla explícita si no hay cuenta para la moneda |
✚ payments/services/house-bank-account.resolver.ts |
| 1.5 | Servicio outgoing | OutgoingPaymentsService: validaciones (≥1 PaymentInvoice o anticipo; SumApplied ≤ DocTotal − PaidToDate por factura, en creación y re-validación en confirmación), crear solicitud (draft directo o batch anticipo), listar/detalle, confirmar (batch PATCH + SaveDraftToDocument + BankPages; recuperar el pago creado por referencia), borrar (con reversa de anticipo), pago directo, U_SalesPersonID |
✚ payments/outgoing-payments.service.ts |
| 1.6 | Controller | Reemplazar la fachada: endpoints del contrato REST (doc 04 §3, sin los de banco), roles Compras/Tesoreria, Swagger completo, FileInterceptor para adjunto |
✎ payments/outgoing-payments.controller.ts |
| 1.7 | Notificaciones | Eventos WS: new_outgoing_payment_draft (a rol Tesorería), outgoing_payment_confirmed y OUTGOING_CANCELLED (al creador vía U_SalesPersonID) |
✎ (dentro de 1.5) |
| 1.8 | Wiring y limpieza | Registrar servicio en PaymentsModule; eliminar del controller los endpoints legacy outgoing/drafts de payments.controller.ts (duplicados) o marcarlos deprecados — decidir en revisión |
✎ payments/payments.module.ts, payments/payments.controller.ts |
Verificación Fase 1 (guion = pruebas del doc 02, ahora vía API del OMS):
- Crear solicitud contra factura (parcial) → draft en SAP con serie 24 + notificación.
- Intentar sobrepago → 422 con detalle por factura (la validación que SAP no hace).
- Crear solicitud anticipo → batch
PurchaseDownPayments+ draft$1. - Confirmar → VendorPayment + asiento (
OriginalJournal = ttVendorPayment), respuesta condocEntryreal (no null), factura cerrada/parcial según monto. - Borrar solicitud (simple y con anticipo) → draft eliminado / anticipo cancelado.
- Pago directo → VendorPayment sin draft.
- Regresión de cobros: humo sobre
incoming-payments(crear/confirmar/cancelar).
Pendiente funcional que NO bloquea la fase: alcance final de medios de pago (Def. 3);
la fase se implementa con transfer + cash y la estructura queda lista para check.
Fase 2 — Integración bancaria (carga + sincronización)¶
Entregable: la solicitud tipo transferencia carga la operación en el banco y guarda el ticket; BankPages se mantiene fresco mientras haya solicitudes pendientes; Tesorería confirma seleccionando el egreso (con preselección si ya hay comprobante).
| # | Etapa | Contenido |
|---|---|---|
| 2.1 | Token por producto | Caché Redis por subscription-key (continental:api_token:<key>), sin tocar el flujo de extractos |
| 2.2 | ContinentalPaymentsService |
crearPago (body doc 03 §6b) y consultarOperacion(ticket); DTOs de ambos contratos |
| 2.3 | Carga en la solicitud | Orden draft-primero, el draft sobrevive al fallo (doc 04 §5b D1 — ya no se borra); PATCH Reference2 = ticket; numeroFactura como clave de idempotencia (D3); resolución de cuenta destino del proveedor (regla doc 03 §6b — requiere UDF U_Moneda en OCRB, prerequisito de datos) y su persistencia en el draft (D2, según resultado de doc 02 B7) |
| 2.4 | Scheduler condicional | OutgoingBankSyncScheduler (intervalo config, kill-switch): drafts pendientes → monedas → sincronizar rango |
| 2.5 | Refactor reconciliación (acotado) | reconcileAccountForDate → rango de fechas; payload de notificación con accountCode/currency; selección de cuenta vía DSC1 (elimina find(moneda)) — con regresión del flujo de cobros |
| 2.6 | Endpoints de apoyo | GET bank-movements (egresos filtrados, con preselección por comprobante si la operación está FINALIZADO), POST bank-sync manual |
| 2.7 | Reintento de carga bancaria (doc 04 §5b) | consultarOperaciones (masiva) como guard anti-doble-carga; endpoint POST /request/:id/retry-bank-load; estado derivado "Carga bancaria fallida" en el listado. Requiere G6 verificado (doc 03): sin consulta masiva filtrable, el endpoint se implementa devolviendo siempre el bloqueo de la rama 3 de D4 — nunca reintentando a ciegas |
Prerequisitos externos: catálogos motivo/procedencia del portal; UDF U_Moneda en
OCRB + carga de cuentas de proveedores piloto; credenciales sandbox vigentes; doc 02 B7
(persistencia de la cuenta destino) antes de 2.3 y doc 03 G6 antes de 2.7.
Verificación: carga real en sandbox (ticket persistido en Reference2), scheduler
sincronizando solo con pendientes, regresión completa de la reconciliación de cobros, y
para 2.7 — con fallo simulado de carga: el draft sobrevive y aparece como "Carga bancaria
fallida"; el reintento no duplica cuando la operación ya existe en el banco (guard);
el reintento reconstruye el body completo desde el draft, incluida la cuenta destino.
Fase 3 — Estado bancario y auto-confirmación (completa el modelo objetivo)¶
consultarOperacionintegrado al detalle de la solicitud (estado, carga/autoriza, motivoRechazo).- Detección de anulación/rechazo en el ciclo del scheduler → notificación a Tesorería y marca visual en el listado (la solicitud sigue viva: decisión humana).
- G5 se ejecuta aquí (primera operación autorizada real): confirmar cadena
ticket→comprobante→Reference e hipótesis
codigoTibWeb. - Auto-confirmación (flujo principal, doc 03 §G4c / doc 04 §6a): extensión del
scheduler — operación
FINALIZADO+ matchcomprobantey monto iguales en BankPages + cross-checks → batch de confirmación automático + notificación "confirmado automáticamente". La confirmación manual (§6b) queda como fallback cuando el match no es inequívoco. - Feature flag (
OUTGOING_AUTO_CONFIRM_ENABLED) para activación gradual; requiere G5 comprobado antes de encender en producción. - Salida a producción en dos etapas (decisión 2026-07-08): primera subida con confirmación manual (fases 1–2 + estado bancario, flag apagado); la automatización se enciende en una segunda etapa, después de validar con operaciones reales de producción que el matching determinista dispone de todos los datos (comprobante, monto, atributos de refuerzo) de forma consistente.
Transversales (paralelizables)¶
| Ítem | Cuándo |
|---|---|
~~Alta del rol Tesorería~~ ✅ creado en prod (2026-08-06, TypeID 13); Compras ya existía (-4). Resolver por nombre, no por ID (doc 04 §3) |
antes de probar Fase 1 con usuarios reales |
~~Creación de UDFs (OCRB + DSC1)~~ ✅ completa en dev y prod (2026-08-06) — script backend/scripts/create-udfs-pagos-proveedores.ts |
— |
Carga de valores en DSC1 de prod (moneda + hash por cuenta) — mapeo listo en doc 04 §11b; validar hashes contra la API productiva antes de copiarlos de sandbox |
antes del despliegue de Fase 2 |
Carga de OCRB.U_Moneda por cuenta de proveedor — hoy producción no tiene ninguna cuenta bancaria de proveedor cargada: es el prerequisito operativo más grande del módulo |
antes de Fase 2.3 |
| Definir mecanismo "grupo de Google" (Def. 8) | puede entrar en cualquier fase; hoy WS |
| Frontend (pantallas Compras/Tesorería) | en paralelo desde Fase 1 (contrato REST estable) |
Historial¶
| Fecha | Cambio |
|---|---|
| 2026-07-08 | Plan inicial: desglose por etapas de Fase 1 (1.1–1.8) con verificación, fases 2–4 y transversales. |
| 2026-07-08 | Reencuadre como plan indicativo (implementa otro dev). Fase 4 fusionada en Fase 3 (auto-confirmación como flujo principal); salida a producción en dos etapas. |
| 2026-08-06 | Etapa 2.7 nueva (reintento de carga bancaria con guard anti-doble-carga); 2.3 actualizada (el draft sobrevive al fallo, cuenta destino persistida, clave de idempotencia); prerequisitos y verificación de Fase 2 ampliados. Decisiones en doc 04 §5b. |