feat(contables): cierre mensual de cuenta corriente de distribuidor #1

Merged
dmolinari merged 2 commits from feat/cierre-cuenta-corriente-distribuidor into main 2026-05-13 13:21:12 +00:00
Owner

Summary

  • Cierres por (distribuidor + empresa): snapshot del saldo a una fecha de corte calculado por suma de movimientos en rango (no toca cue_Saldos, queda paralela para conciliación).
  • Bloqueo de modificaciones retroactivas sobre fechas en período cerrado para pagos, notas C/D a distribuidores, ajustes manuales y entradas/salidas de distribución → HTTP 409 con código PERIODO_CERRADO_BLOQUEO_OPERACION.
  • Reapertura controlada: exclusiva de SuperAdmin, en cascada manual desde el último cierre vigente.
  • Reporte de cuentas distribuidor arranca con Saldo Inicial real (suma desde último cierre) y termina en Saldo Final (rename de "Total"). Acumulados encadenados, sección Excel de Resumen, atajo "Desde último cierre" en filtros.
  • Auditoría: nuevo endpoint y vista del historial _H con filtros por fecha/usuario/tipo.
  • Permisos: CC001 (crear), CC002 (reabrir — solo SuperAdmin), CC003 (ver). CS002 (ajuste manual) también pasa a exclusivo SuperAdmin. Ambos ocultos en la UI de asignación a perfiles.

Cambios técnicos clave

  • Tabla cue_CierresCuentaCorriente + _H. Entregables SQL en Backend/SQL/cierre-cc-distribuidor/ (fuera del repo por .gitignore).
  • Service con transacción IsolationLevel.Serializable + retry policy ante deadlock 1205 (3 intentos, backoff 50/150/400 ms).
  • IPeriodoCerradoValidator con IMemoryCache Singleton (TTL 10 min) inyectado en 4 services existentes vía IServiceScopeFactory para evitar captive dependency.
  • ExceptionHandlerMiddleware global que mapea BloqueoPorPeriodoCerradoException → 409 con shape {codigo, mensaje, idCierre, fechaCorte}.
  • Reuso del SP existente GetBalanceCuentaDistEntradaSalidaPorEmpresaAsync para el cálculo del saldo (single source of truth de CalcularMontoMovimiento).
  • Helper parseApiError centralizado en frontend para errores semánticos del backend.

Test plan

  • Backend build OK (dotnet build)
  • Frontend build OK (npm run build)
  • S3: crear cierre — saldo calculado por suma de movimientos, NO copiado de cue_Saldos
  • S4: bloqueo POST pago en período cerrado → 409 con shape correcto
  • S5: reporte response incluye saldoInicial, sin saldos[] legacy
  • S6: reapertura solo SuperAdmin, verificación lateral (período se desbloquea correctamente)
  • S7.a: nota crédito a distribuidor en período cerrado → 409
  • S7.c: ajuste manual SuperAdmin en período cerrado → 409
  • S7.d/e: PUT/DELETE pago en período cerrado → 409
  • S7.f: PUT/DELETE entrada/salida en período cerrado → 409
  • S8: PDF muestra "Saldo Inicial" y "Saldo Final" (verificado con pypdf)
  • S11-S15: smokes UI manuales en navegador (a cargo del autor)
  • Aplicar SQLs en TECNICA3 (ya aplicados durante el desarrollo en local)

Plan de smoke tests detallado: Backend/SQL/cierre-cc-distribuidor/SMOKE-TESTS.md (local, no commiteado).

Out of scope

  • Aplicación de los SQLs en producción → manual, cuando el autor lo determine post-revisión
  • Tests automatizados (el proyecto no tiene infra de tests)
  • Notas a Canillas (confirmado contra TECNICA3: 0 filas con destino='Canillas' en cue_Saldos/cue_CreditosDebitos — feature no se ejercita en práctica)
  • Bug preexistente en dist_EntradasSalidas heap sin PK (mitigado parcialmente con índice nuevo, fix completo fuera de scope)

⚠️ NO mergear todavía

PR abierta para revisión. Antes de mergear:

  1. Smoke tests S11-S15 en navegador
  2. Validación contable manual del SaldoCierre con un distribuidor real
  3. Plan de aplicación de SQLs en producción coordinado con ventana de mantenimiento

Recordá: push a main = deploy automático a producción vía CI/CD.

## Summary - **Cierres por (distribuidor + empresa)**: snapshot del saldo a una fecha de corte calculado por suma de movimientos en rango (no toca `cue_Saldos`, queda paralela para conciliación). - **Bloqueo de modificaciones retroactivas** sobre fechas en período cerrado para pagos, notas C/D a distribuidores, ajustes manuales y entradas/salidas de distribución → HTTP 409 con código `PERIODO_CERRADO_BLOQUEO_OPERACION`. - **Reapertura controlada**: exclusiva de SuperAdmin, en cascada manual desde el último cierre vigente. - **Reporte de cuentas distribuidor** arranca con `Saldo Inicial` real (suma desde último cierre) y termina en `Saldo Final` (rename de "Total"). Acumulados encadenados, sección Excel de Resumen, atajo "Desde último cierre" en filtros. - **Auditoría**: nuevo endpoint y vista del historial `_H` con filtros por fecha/usuario/tipo. - **Permisos**: `CC001` (crear), `CC002` (reabrir — solo SuperAdmin), `CC003` (ver). `CS002` (ajuste manual) también pasa a exclusivo SuperAdmin. Ambos ocultos en la UI de asignación a perfiles. ## Cambios técnicos clave - Tabla `cue_CierresCuentaCorriente` + `_H`. Entregables SQL en `Backend/SQL/cierre-cc-distribuidor/` (fuera del repo por `.gitignore`). - Service con transacción `IsolationLevel.Serializable` + retry policy ante deadlock 1205 (3 intentos, backoff 50/150/400 ms). - `IPeriodoCerradoValidator` con `IMemoryCache` Singleton (TTL 10 min) inyectado en 4 services existentes vía `IServiceScopeFactory` para evitar captive dependency. - `ExceptionHandlerMiddleware` global que mapea `BloqueoPorPeriodoCerradoException` → 409 con shape `{codigo, mensaje, idCierre, fechaCorte}`. - Reuso del SP existente `GetBalanceCuentaDistEntradaSalidaPorEmpresaAsync` para el cálculo del saldo (single source of truth de `CalcularMontoMovimiento`). - Helper `parseApiError` centralizado en frontend para errores semánticos del backend. ## Test plan - [x] Backend build OK (`dotnet build`) - [x] Frontend build OK (`npm run build`) - [x] **S3**: crear cierre — saldo calculado por suma de movimientos, NO copiado de `cue_Saldos` - [x] **S4**: bloqueo POST pago en período cerrado → 409 con shape correcto - [x] **S5**: reporte response incluye `saldoInicial`, sin `saldos[]` legacy - [x] **S6**: reapertura solo SuperAdmin, verificación lateral (período se desbloquea correctamente) - [x] **S7.a**: nota crédito a distribuidor en período cerrado → 409 - [x] **S7.c**: ajuste manual SuperAdmin en período cerrado → 409 - [x] **S7.d/e**: PUT/DELETE pago en período cerrado → 409 - [x] **S7.f**: PUT/DELETE entrada/salida en período cerrado → 409 - [x] **S8**: PDF muestra "Saldo Inicial" y "Saldo Final" (verificado con pypdf) - [x] **S11-S15**: smokes UI manuales en navegador (a cargo del autor) - [x] Aplicar SQLs en TECNICA3 (ya aplicados durante el desarrollo en local) Plan de smoke tests detallado: `Backend/SQL/cierre-cc-distribuidor/SMOKE-TESTS.md` (local, no commiteado). ## Out of scope - Aplicación de los SQLs en producción → manual, cuando el autor lo determine post-revisión - Tests automatizados (el proyecto no tiene infra de tests) - Notas a Canillas (confirmado contra TECNICA3: 0 filas con `destino='Canillas'` en `cue_Saldos`/`cue_CreditosDebitos` — feature no se ejercita en práctica) - Bug preexistente en `dist_EntradasSalidas` heap sin PK (mitigado parcialmente con índice nuevo, fix completo fuera de scope) ## ⚠️ NO mergear todavía PR abierta para revisión. Antes de mergear: 1. Smoke tests S11-S15 en navegador 2. Validación contable manual del SaldoCierre con un distribuidor real 3. Plan de aplicación de SQLs en producción coordinado con ventana de mantenimiento Recordá: push a `main` = deploy automático a producción vía CI/CD.
dmolinari added 1 commit 2026-05-07 15:04:36 +00:00
Permite congelar el saldo de un distribuidor por empresa a una fecha de
corte y bloquear modificaciones retroactivas sobre el período cerrado.
El saldo se calcula sumando movimientos en rango (sin tocar cue_Saldos).
Incluye reapertura controlada exclusivamente por SuperAdmin, reporte con
saldo inicial, atajo "Desde último cierre", y auditoría del ciclo de
vida _H. Permisos CC001/CC002/CC003. Middleware global mapea bloqueos
por período cerrado a HTTP 409.
dmolinari added 1 commit 2026-05-13 13:18:42 +00:00
La reapertura de cierres ya se validaba con User.IsInRole('SuperAdmin') sin
chequear CC002. Mantenerlo en gral_Permisos solo agregaba ruido — un permiso
filtrado intencionalmente de la UI de asignación. Se quita del catálogo y se
simplifica el set PERMISOS_SOLO_SUPERADMIN del frontend a {CS002} (legacy).

CS002 se mantiene en el filtro porque ya existía en producción antes del feature.
dmolinari merged commit 4358700cfd into main 2026-05-13 13:21:12 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dmolinari/GestionIntegralWeb#1