feat(contables): cierre mensual de cuenta corriente de distribuidor
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.
This commit is contained in:
@@ -4,6 +4,7 @@ using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||||
using GestionIntegral.Api.Dtos.Auditoria;
|
||||
using GestionIntegral.Api.Dtos.Distribucion;
|
||||
using GestionIntegral.Api.Models.Distribucion;
|
||||
using GestionIntegral.Api.Services.Contables;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -24,6 +25,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
private readonly IPorcPagoRepository _porcPagoRepository;
|
||||
private readonly ISaldoRepository _saldoRepository;
|
||||
private readonly IEmpresaRepository _empresaRepository; // Para obtener IdEmpresa de la publicación
|
||||
private readonly IPeriodoCerradoValidator _periodoCerrado;
|
||||
private readonly DbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<EntradaSalidaDistService> _logger;
|
||||
|
||||
@@ -36,6 +38,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
IPorcPagoRepository porcPagoRepository,
|
||||
ISaldoRepository saldoRepository,
|
||||
IEmpresaRepository empresaRepository,
|
||||
IPeriodoCerradoValidator periodoCerrado,
|
||||
DbConnectionFactory connectionFactory,
|
||||
ILogger<EntradaSalidaDistService> logger)
|
||||
{
|
||||
@@ -47,6 +50,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
_porcPagoRepository = porcPagoRepository;
|
||||
_saldoRepository = saldoRepository;
|
||||
_empresaRepository = empresaRepository;
|
||||
_periodoCerrado = periodoCerrado;
|
||||
_connectionFactory = connectionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
@@ -167,6 +171,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(createDto.IdDistribuidor);
|
||||
if (distribuidor == null) return (null, "Distribuidor no válido.");
|
||||
|
||||
// Bloqueo por período cerrado: la fecha del movimiento no puede caer dentro de un cierre vigente del distribuidor en la empresa de la publicación.
|
||||
var bloqueoCrear = await _periodoCerrado.EstaCerradoAsync("Distribuidores", createDto.IdDistribuidor, publicacion.IdEmpresa, createDto.Fecha);
|
||||
if (bloqueoCrear.EstaCerrado)
|
||||
throw new BloqueoPorPeriodoCerradoException(bloqueoCrear.IdCierre!.Value, bloqueoCrear.FechaCorte!.Value);
|
||||
|
||||
/*
|
||||
if (await _esRepository.ExistsByRemitoAndTipoForPublicacionAsync(createDto.Remito, createDto.TipoMovimiento, createDto.IdPublicacion))
|
||||
{
|
||||
@@ -262,6 +271,14 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(esExistente.IdDistribuidor);
|
||||
if (distribuidor == null) return (false, "Distribuidor asociado no encontrado.");
|
||||
|
||||
// Bloqueo por período cerrado sobre la fecha original del movimiento (el DTO de update no permite cambiar Fecha).
|
||||
var bloqueoUpd = await _periodoCerrado.EstaCerradoAsync("Distribuidores", esExistente.IdDistribuidor, publicacion.IdEmpresa, esExistente.Fecha);
|
||||
if (bloqueoUpd.EstaCerrado)
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw new BloqueoPorPeriodoCerradoException(bloqueoUpd.IdCierre!.Value, bloqueoUpd.FechaCorte!.Value);
|
||||
}
|
||||
|
||||
|
||||
// 1. Calcular monto del movimiento original (antes de la actualización)
|
||||
decimal montoOriginal = await CalcularMontoMovimiento(
|
||||
@@ -307,6 +324,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Movimiento no encontrado."); }
|
||||
catch (BloqueoPorPeriodoCerradoException)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
@@ -330,6 +352,14 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
var distribuidor = await _distribuidorRepository.GetByIdSimpleAsync(esExistente.IdDistribuidor);
|
||||
if (distribuidor == null) return (false, "Distribuidor asociado no encontrado.");
|
||||
|
||||
// Bloqueo por período cerrado: no se puede eliminar un movimiento cuya fecha cae en un cierre vigente.
|
||||
var bloqueoDel = await _periodoCerrado.EstaCerradoAsync("Distribuidores", esExistente.IdDistribuidor, publicacion.IdEmpresa, esExistente.Fecha);
|
||||
if (bloqueoDel.EstaCerrado)
|
||||
{
|
||||
transaction.Rollback();
|
||||
throw new BloqueoPorPeriodoCerradoException(bloqueoDel.IdCierre!.Value, bloqueoDel.FechaCorte!.Value);
|
||||
}
|
||||
|
||||
// 1. Calcular el monto del movimiento a eliminar para revertir el saldo
|
||||
decimal montoReversion = await CalcularMontoMovimiento(
|
||||
esExistente.IdPublicacion, esExistente.IdDistribuidor, esExistente.Fecha, esExistente.Cantidad, esExistente.TipoMovimiento,
|
||||
@@ -364,6 +394,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Movimiento no encontrado."); }
|
||||
catch (BloqueoPorPeriodoCerradoException)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
|
||||
Reference in New Issue
Block a user