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:
@@ -0,0 +1,435 @@
|
||||
using Dapper;
|
||||
using GestionIntegral.Api.Dtos.Auditoria;
|
||||
using GestionIntegral.Api.Dtos.Contables;
|
||||
using GestionIntegral.Api.Models.Contables;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Data.Repositories.Contables
|
||||
{
|
||||
public class CierreCuentaCorrienteRepository : ICierreCuentaCorrienteRepository
|
||||
{
|
||||
private readonly DbConnectionFactory _cf;
|
||||
private readonly ILogger<CierreCuentaCorrienteRepository> _log;
|
||||
|
||||
public CierreCuentaCorrienteRepository(DbConnectionFactory cf, ILogger<CierreCuentaCorrienteRepository> log)
|
||||
{
|
||||
_cf = cf;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
// Aliases SELECT — mapean Id_X (SQL) → IdX (modelo C#).
|
||||
private const string SelectModelBase = @"
|
||||
SELECT
|
||||
Id_Cierre AS IdCierre,
|
||||
Id_Distribuidor AS IdDistribuidor,
|
||||
Id_Empresa AS IdEmpresa,
|
||||
FechaCorte,
|
||||
FechaCierre,
|
||||
SaldoCierre,
|
||||
Estado,
|
||||
Justificacion,
|
||||
Id_Usuario_Cierre AS IdUsuarioCierre,
|
||||
Id_Usuario_Anula AS IdUsuarioAnula,
|
||||
FechaAnulacion,
|
||||
Justificacion_Anulacion AS JustificacionAnulacion
|
||||
FROM dbo.cue_CierresCuentaCorriente";
|
||||
|
||||
public async Task<CierreCuentaCorriente?> GetByIdAsync(int idCierre)
|
||||
{
|
||||
var sql = SelectModelBase + " WHERE Id_Cierre = @IdCierreParam;";
|
||||
try
|
||||
{
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<CierreCuentaCorriente>(sql, new { IdCierreParam = idCierre });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error al obtener Cierre por ID: {IdCierre}", idCierre);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CierreCuentaCorriente?> GetUltimoCierreVigenteAsync(int idDistribuidor, int idEmpresa, IDbTransaction? transaction = null)
|
||||
{
|
||||
var sql = @"
|
||||
SELECT TOP 1
|
||||
Id_Cierre AS IdCierre,
|
||||
Id_Distribuidor AS IdDistribuidor,
|
||||
Id_Empresa AS IdEmpresa,
|
||||
FechaCorte,
|
||||
FechaCierre,
|
||||
SaldoCierre,
|
||||
Estado,
|
||||
Justificacion,
|
||||
Id_Usuario_Cierre AS IdUsuarioCierre,
|
||||
Id_Usuario_Anula AS IdUsuarioAnula,
|
||||
FechaAnulacion,
|
||||
Justificacion_Anulacion AS JustificacionAnulacion
|
||||
FROM dbo.cue_CierresCuentaCorriente
|
||||
WHERE Id_Distribuidor = @IdDist
|
||||
AND Id_Empresa = @IdEmp
|
||||
AND Estado = 'Activo'
|
||||
ORDER BY FechaCorte DESC, Id_Cierre DESC;";
|
||||
|
||||
var parameters = new { IdDist = idDistribuidor, IdEmp = idEmpresa };
|
||||
|
||||
try
|
||||
{
|
||||
if (transaction != null)
|
||||
{
|
||||
return await transaction.Connection!.QuerySingleOrDefaultAsync<CierreCuentaCorriente>(sql, parameters, transaction);
|
||||
}
|
||||
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<CierreCuentaCorriente>(sql, parameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error al obtener último cierre vigente Dist={IdDist} Emp={IdEmp}", idDistribuidor, idEmpresa);
|
||||
if (transaction != null) throw;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<CierreCuentaCorriente?> GetCierreVigenteParaFechaAsync(int idDistribuidor, int idEmpresa, DateTime fechaOperacion)
|
||||
{
|
||||
// Cae en período cerrado si existe un cierre Activo cuya FechaCorte sea >= fechaOperacion (la fecha está dentro del período cerrado).
|
||||
// Se devuelve el más reciente (TOP 1 ORDER BY FechaCorte DESC) — el más restrictivo desde la perspectiva de la fecha consultada.
|
||||
const string sql = @"
|
||||
SELECT TOP 1
|
||||
Id_Cierre AS IdCierre,
|
||||
Id_Distribuidor AS IdDistribuidor,
|
||||
Id_Empresa AS IdEmpresa,
|
||||
FechaCorte,
|
||||
FechaCierre,
|
||||
SaldoCierre,
|
||||
Estado,
|
||||
Justificacion,
|
||||
Id_Usuario_Cierre AS IdUsuarioCierre,
|
||||
Id_Usuario_Anula AS IdUsuarioAnula,
|
||||
FechaAnulacion,
|
||||
Justificacion_Anulacion AS JustificacionAnulacion
|
||||
FROM dbo.cue_CierresCuentaCorriente
|
||||
WHERE Id_Distribuidor = @IdDist
|
||||
AND Id_Empresa = @IdEmp
|
||||
AND Estado = 'Activo'
|
||||
AND FechaCorte >= @FechaOp
|
||||
ORDER BY FechaCorte DESC, Id_Cierre DESC;";
|
||||
try
|
||||
{
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<CierreCuentaCorriente>(sql, new
|
||||
{
|
||||
IdDist = idDistribuidor,
|
||||
IdEmp = idEmpresa,
|
||||
FechaOp = fechaOperacion.Date
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error en GetCierreVigenteParaFechaAsync Dist={IdDist} Emp={IdEmp} Fecha={Fecha}", idDistribuidor, idEmpresa, fechaOperacion);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> ExisteCierrePosteriorVigenteAsync(int idDistribuidor, int idEmpresa, DateTime fechaCorte, int? excluirIdCierre = null, IDbTransaction? transaction = null)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1 FROM dbo.cue_CierresCuentaCorriente
|
||||
WHERE Id_Distribuidor = @IdDist
|
||||
AND Id_Empresa = @IdEmp
|
||||
AND Estado = 'Activo'
|
||||
AND FechaCorte > @FechaCorte
|
||||
AND (@Excluir IS NULL OR Id_Cierre <> @Excluir)
|
||||
) THEN 1 ELSE 0 END;";
|
||||
|
||||
var parameters = new
|
||||
{
|
||||
IdDist = idDistribuidor,
|
||||
IdEmp = idEmpresa,
|
||||
FechaCorte = fechaCorte.Date,
|
||||
Excluir = excluirIdCierre
|
||||
};
|
||||
|
||||
if (transaction != null)
|
||||
{
|
||||
return await transaction.Connection!.ExecuteScalarAsync<bool>(sql, parameters, transaction);
|
||||
}
|
||||
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.ExecuteScalarAsync<bool>(sql, parameters);
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(CierreCuentaCorriente cierre, int idUsuarioMod, IDbTransaction transaction)
|
||||
{
|
||||
const string sqlInsertMaster = @"
|
||||
INSERT INTO dbo.cue_CierresCuentaCorriente
|
||||
(Id_Distribuidor, Id_Empresa, FechaCorte, FechaCierre, SaldoCierre, Estado, Justificacion, Id_Usuario_Cierre)
|
||||
VALUES
|
||||
(@IdDistribuidor, @IdEmpresa, @FechaCorte, @FechaCierre, @SaldoCierre, @Estado, @Justificacion, @IdUsuarioCierre);
|
||||
SELECT CAST(SCOPE_IDENTITY() AS INT);";
|
||||
|
||||
var idCierre = await transaction.Connection!.ExecuteScalarAsync<int>(sqlInsertMaster, new
|
||||
{
|
||||
cierre.IdDistribuidor,
|
||||
cierre.IdEmpresa,
|
||||
FechaCorte = cierre.FechaCorte.Date,
|
||||
cierre.FechaCierre,
|
||||
cierre.SaldoCierre,
|
||||
cierre.Estado,
|
||||
cierre.Justificacion,
|
||||
cierre.IdUsuarioCierre
|
||||
}, transaction);
|
||||
|
||||
if (idCierre <= 0) throw new DataException("No se pudo crear el cierre — SCOPE_IDENTITY no devolvió valor.");
|
||||
|
||||
const string sqlInsertHist = @"
|
||||
INSERT INTO dbo.cue_CierresCuentaCorriente_H
|
||||
(Id_Cierre, Id_Distribuidor, Id_Empresa, FechaCorte, FechaCierre, SaldoCierre,
|
||||
Estado, Justificacion, Id_Usuario_Cierre,
|
||||
Id_Usuario_Anula, FechaAnulacion, Justificacion_Anulacion,
|
||||
TipoMod, Id_Usuario_Mod, FechaMod)
|
||||
VALUES
|
||||
(@IdCierre, @IdDistribuidor, @IdEmpresa, @FechaCorte, @FechaCierre, @SaldoCierre,
|
||||
@Estado, @Justificacion, @IdUsuarioCierre,
|
||||
NULL, NULL, NULL,
|
||||
@TipoMod, @IdUsuarioMod, @FechaMod);";
|
||||
|
||||
await transaction.Connection!.ExecuteAsync(sqlInsertHist, new
|
||||
{
|
||||
IdCierre = idCierre,
|
||||
cierre.IdDistribuidor,
|
||||
cierre.IdEmpresa,
|
||||
FechaCorte = cierre.FechaCorte.Date,
|
||||
cierre.FechaCierre,
|
||||
cierre.SaldoCierre,
|
||||
cierre.Estado,
|
||||
cierre.Justificacion,
|
||||
cierre.IdUsuarioCierre,
|
||||
TipoMod = "Creacion",
|
||||
IdUsuarioMod = idUsuarioMod,
|
||||
FechaMod = DateTime.Now
|
||||
}, transaction);
|
||||
|
||||
return idCierre;
|
||||
}
|
||||
|
||||
public async Task<bool> AnularAsync(int idCierre, int idUsuarioAnula, string justificacionAnulacion, int idUsuarioMod, IDbTransaction transaction)
|
||||
{
|
||||
// UPDATE atómico: solo cambia Estado si está actualmente Activo. Evita doble anulación concurrente.
|
||||
const string sqlUpdate = @"
|
||||
UPDATE dbo.cue_CierresCuentaCorriente
|
||||
SET Estado = 'Anulado',
|
||||
Id_Usuario_Anula = @IdUsuarioAnula,
|
||||
FechaAnulacion = @FechaAnulacion,
|
||||
Justificacion_Anulacion = @JustificacionAnulacion
|
||||
WHERE Id_Cierre = @IdCierre
|
||||
AND Estado = 'Activo';";
|
||||
|
||||
var fechaAnulacion = DateTime.Now;
|
||||
|
||||
int affected = await transaction.Connection!.ExecuteAsync(sqlUpdate, new
|
||||
{
|
||||
IdCierre = idCierre,
|
||||
IdUsuarioAnula = idUsuarioAnula,
|
||||
FechaAnulacion = fechaAnulacion,
|
||||
JustificacionAnulacion = justificacionAnulacion
|
||||
}, transaction);
|
||||
|
||||
if (affected != 1) return false;
|
||||
|
||||
// Snapshot post-update para el _H. Trae los valores ya actualizados.
|
||||
const string sqlSnapshot = SelectModelBase + " WHERE Id_Cierre = @IdCierre;";
|
||||
var actualizado = await transaction.Connection!.QuerySingleAsync<CierreCuentaCorriente>(sqlSnapshot, new { IdCierre = idCierre }, transaction);
|
||||
|
||||
const string sqlInsertHist = @"
|
||||
INSERT INTO dbo.cue_CierresCuentaCorriente_H
|
||||
(Id_Cierre, Id_Distribuidor, Id_Empresa, FechaCorte, FechaCierre, SaldoCierre,
|
||||
Estado, Justificacion, Id_Usuario_Cierre,
|
||||
Id_Usuario_Anula, FechaAnulacion, Justificacion_Anulacion,
|
||||
TipoMod, Id_Usuario_Mod, FechaMod)
|
||||
VALUES
|
||||
(@IdCierre, @IdDistribuidor, @IdEmpresa, @FechaCorte, @FechaCierre, @SaldoCierre,
|
||||
@Estado, @Justificacion, @IdUsuarioCierre,
|
||||
@IdUsuarioAnula, @FechaAnulacion, @JustificacionAnulacion,
|
||||
@TipoMod, @IdUsuarioMod, @FechaMod);";
|
||||
|
||||
await transaction.Connection!.ExecuteAsync(sqlInsertHist, new
|
||||
{
|
||||
IdCierre = actualizado.IdCierre,
|
||||
actualizado.IdDistribuidor,
|
||||
actualizado.IdEmpresa,
|
||||
FechaCorte = actualizado.FechaCorte.Date,
|
||||
actualizado.FechaCierre,
|
||||
actualizado.SaldoCierre,
|
||||
actualizado.Estado,
|
||||
actualizado.Justificacion,
|
||||
actualizado.IdUsuarioCierre,
|
||||
actualizado.IdUsuarioAnula,
|
||||
actualizado.FechaAnulacion,
|
||||
JustificacionAnulacion = actualizado.JustificacionAnulacion,
|
||||
TipoMod = "Reapertura",
|
||||
IdUsuarioMod = idUsuarioMod,
|
||||
FechaMod = DateTime.Now
|
||||
}, transaction);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CierreCuentaCorrienteDto>> GetAllAsync(
|
||||
int? idDistribuidor, int? idEmpresa, string? estado,
|
||||
DateTime? fechaCorteDesde, DateTime? fechaCorteHasta)
|
||||
{
|
||||
var sqlBuilder = new StringBuilder(@"
|
||||
SELECT
|
||||
c.Id_Cierre AS IdCierre,
|
||||
c.Id_Distribuidor AS IdDistribuidor,
|
||||
d.Nombre AS NombreDistribuidor,
|
||||
c.Id_Empresa AS IdEmpresa,
|
||||
e.Nombre AS NombreEmpresa,
|
||||
CONVERT(varchar(10), c.FechaCorte, 23) AS FechaCorte,
|
||||
c.FechaCierre,
|
||||
c.SaldoCierre,
|
||||
c.Estado,
|
||||
c.Justificacion,
|
||||
c.Id_Usuario_Cierre AS IdUsuarioCierre,
|
||||
(uc.Nombre + ' ' + uc.Apellido) AS NombreUsuarioCierre,
|
||||
c.Id_Usuario_Anula AS IdUsuarioAnula,
|
||||
CASE WHEN ua.Id IS NULL THEN NULL ELSE (ua.Nombre + ' ' + ua.Apellido) END AS NombreUsuarioAnula,
|
||||
c.FechaAnulacion,
|
||||
c.Justificacion_Anulacion AS JustificacionAnulacion,
|
||||
CAST(CASE WHEN c.Estado = 'Activo'
|
||||
AND c.Id_Cierre = (
|
||||
SELECT TOP 1 c2.Id_Cierre FROM dbo.cue_CierresCuentaCorriente c2
|
||||
WHERE c2.Id_Distribuidor = c.Id_Distribuidor
|
||||
AND c2.Id_Empresa = c.Id_Empresa
|
||||
AND c2.Estado = 'Activo'
|
||||
ORDER BY c2.FechaCorte DESC, c2.Id_Cierre DESC)
|
||||
THEN 1 ELSE 0 END AS bit) AS EsUltimoVigente
|
||||
FROM dbo.cue_CierresCuentaCorriente c
|
||||
JOIN dbo.dist_dtDistribuidores d ON d.Id_Distribuidor = c.Id_Distribuidor
|
||||
JOIN dbo.dist_dtEmpresas e ON e.Id_Empresa = c.Id_Empresa
|
||||
JOIN dbo.gral_Usuarios uc ON uc.Id = c.Id_Usuario_Cierre
|
||||
LEFT JOIN dbo.gral_Usuarios ua ON ua.Id = c.Id_Usuario_Anula
|
||||
WHERE 1=1");
|
||||
|
||||
var parameters = new DynamicParameters();
|
||||
if (idDistribuidor.HasValue) { sqlBuilder.Append(" AND c.Id_Distribuidor = @IdDist"); parameters.Add("IdDist", idDistribuidor.Value); }
|
||||
if (idEmpresa.HasValue) { sqlBuilder.Append(" AND c.Id_Empresa = @IdEmp"); parameters.Add("IdEmp", idEmpresa.Value); }
|
||||
if (!string.IsNullOrWhiteSpace(estado)) { sqlBuilder.Append(" AND c.Estado = @Estado"); parameters.Add("Estado", estado); }
|
||||
if (fechaCorteDesde.HasValue) { sqlBuilder.Append(" AND c.FechaCorte >= @FechaDesde"); parameters.Add("FechaDesde", fechaCorteDesde.Value.Date); }
|
||||
if (fechaCorteHasta.HasValue) { sqlBuilder.Append(" AND c.FechaCorte <= @FechaHasta"); parameters.Add("FechaHasta", fechaCorteHasta.Value.Date); }
|
||||
sqlBuilder.Append(" ORDER BY c.FechaCorte DESC, c.Id_Cierre DESC;");
|
||||
|
||||
try
|
||||
{
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QueryAsync<CierreCuentaCorrienteDto>(sqlBuilder.ToString(), parameters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error en GetAllAsync de CierresCuentaCorriente.");
|
||||
return Enumerable.Empty<CierreCuentaCorrienteDto>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CierreCuentaCorrienteHistorialDto>> GetHistorialAsync(int idCierre)
|
||||
{
|
||||
const string sql = @"
|
||||
SELECT
|
||||
h.Id_Historial,
|
||||
h.Id_Cierre,
|
||||
h.Id_Distribuidor,
|
||||
h.Id_Empresa,
|
||||
h.FechaCorte,
|
||||
h.FechaCierre,
|
||||
h.SaldoCierre,
|
||||
h.Estado,
|
||||
h.Justificacion,
|
||||
h.Id_Usuario_Cierre,
|
||||
h.Id_Usuario_Anula,
|
||||
h.FechaAnulacion,
|
||||
h.Justificacion_Anulacion,
|
||||
h.TipoMod,
|
||||
h.Id_Usuario_Mod,
|
||||
(u.Nombre + ' ' + u.Apellido) AS NombreUsuarioModifico,
|
||||
h.FechaMod
|
||||
FROM dbo.cue_CierresCuentaCorriente_H h
|
||||
JOIN dbo.gral_Usuarios u ON u.Id = h.Id_Usuario_Mod
|
||||
WHERE h.Id_Cierre = @IdCierre
|
||||
ORDER BY h.FechaMod ASC, h.Id_Historial ASC;";
|
||||
|
||||
try
|
||||
{
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QueryAsync<CierreCuentaCorrienteHistorialDto>(sql, new { IdCierre = idCierre });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error en GetHistorialAsync para Cierre ID {IdCierre}", idCierre);
|
||||
return Enumerable.Empty<CierreCuentaCorrienteHistorialDto>();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CierreCuentaCorrienteHistorialDto>> ObtenerHistorialAsync(
|
||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||
int? idUsuarioModifico, string? tipoModificacion,
|
||||
int? idCierreAfectado)
|
||||
{
|
||||
// FechaMod cubre el rango inclusive: [fechaDesde 00:00, fechaHasta+1día). Patrón consistente con otros ObtenerHistorial del proyecto.
|
||||
var sql = @"
|
||||
SELECT
|
||||
h.Id_Historial,
|
||||
h.Id_Cierre,
|
||||
h.Id_Distribuidor,
|
||||
h.Id_Empresa,
|
||||
h.FechaCorte,
|
||||
h.FechaCierre,
|
||||
h.SaldoCierre,
|
||||
h.Estado,
|
||||
h.Justificacion,
|
||||
h.Id_Usuario_Cierre,
|
||||
h.Id_Usuario_Anula,
|
||||
h.FechaAnulacion,
|
||||
h.Justificacion_Anulacion,
|
||||
h.TipoMod,
|
||||
h.Id_Usuario_Mod,
|
||||
(u.Nombre + ' ' + u.Apellido) AS NombreUsuarioModifico,
|
||||
h.FechaMod
|
||||
FROM dbo.cue_CierresCuentaCorriente_H h
|
||||
JOIN dbo.gral_Usuarios u ON u.Id = h.Id_Usuario_Mod
|
||||
WHERE 1 = 1
|
||||
AND (@FechaDesde IS NULL OR h.FechaMod >= @FechaDesde)
|
||||
AND (@FechaHasta IS NULL OR h.FechaMod < DATEADD(DAY, 1, @FechaHasta))
|
||||
AND (@IdUsuarioMod IS NULL OR h.Id_Usuario_Mod = @IdUsuarioMod)
|
||||
AND (@TipoMod IS NULL OR h.TipoMod = @TipoMod)
|
||||
AND (@IdCierre IS NULL OR h.Id_Cierre = @IdCierre)
|
||||
ORDER BY h.FechaMod DESC, h.Id_Historial DESC;";
|
||||
|
||||
try
|
||||
{
|
||||
using var connection = _cf.CreateConnection();
|
||||
return await connection.QueryAsync<CierreCuentaCorrienteHistorialDto>(sql, new
|
||||
{
|
||||
FechaDesde = fechaDesde?.Date,
|
||||
FechaHasta = fechaHasta?.Date,
|
||||
IdUsuarioMod = idUsuarioModifico,
|
||||
TipoMod = tipoModificacion,
|
||||
IdCierre = idCierreAfectado
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.LogError(ex, "Error en ObtenerHistorialAsync (auditoría) de Cierres CC.");
|
||||
return Enumerable.Empty<CierreCuentaCorrienteHistorialDto>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
using GestionIntegral.Api.Dtos.Auditoria;
|
||||
using GestionIntegral.Api.Dtos.Contables;
|
||||
using GestionIntegral.Api.Models.Contables;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Data.Repositories.Contables
|
||||
{
|
||||
public interface ICierreCuentaCorrienteRepository
|
||||
{
|
||||
Task<CierreCuentaCorriente?> GetByIdAsync(int idCierre);
|
||||
|
||||
// Devuelve el último cierre con Estado = 'Activo' para el par (Distribuidor + Empresa).
|
||||
// Acepta una transacción opcional para usarse dentro del flujo Crear/Reabrir.
|
||||
Task<CierreCuentaCorriente?> GetUltimoCierreVigenteAsync(int idDistribuidor, int idEmpresa, IDbTransaction? transaction = null);
|
||||
|
||||
// Verifica si la fecha cae dentro de un período cerrado: existe un cierre Activo con FechaCorte >= fechaOperacion.
|
||||
Task<CierreCuentaCorriente?> GetCierreVigenteParaFechaAsync(int idDistribuidor, int idEmpresa, DateTime fechaOperacion);
|
||||
|
||||
// True si existe otro cierre Activo con FechaCorte > fechaCorte (excluyendo opcionalmente un cierre puntual).
|
||||
// Se usa al reabrir para forzar la cascada manual.
|
||||
Task<bool> ExisteCierrePosteriorVigenteAsync(int idDistribuidor, int idEmpresa, DateTime fechaCorte, int? excluirIdCierre = null, IDbTransaction? transaction = null);
|
||||
|
||||
// Crea la fila maestra y registra la entrada inicial en _H con TipoMod='Creacion'. Devuelve el Id_Cierre generado.
|
||||
Task<int> CreateAsync(CierreCuentaCorriente cierre, int idUsuarioMod, IDbTransaction transaction);
|
||||
|
||||
// Marca un cierre como Anulado (UPDATE atómico solo si Estado='Activo') y registra entrada en _H con TipoMod='Reapertura'.
|
||||
// Devuelve true si se anuló (Estado pasó de Activo a Anulado), false si ya estaba anulado o no existía.
|
||||
Task<bool> AnularAsync(int idCierre, int idUsuarioAnula, string justificacionAnulacion, int idUsuarioMod, IDbTransaction transaction);
|
||||
|
||||
Task<IEnumerable<CierreCuentaCorrienteDto>> GetAllAsync(
|
||||
int? idDistribuidor, int? idEmpresa, string? estado,
|
||||
DateTime? fechaCorteDesde, DateTime? fechaCorteHasta);
|
||||
|
||||
Task<IEnumerable<CierreCuentaCorrienteHistorialDto>> GetHistorialAsync(int idCierre);
|
||||
|
||||
// Auditoría general: filtra el historial cruzado de todos los cierres por rango de FechaMod, usuario, tipo, y opcional Id_Cierre.
|
||||
Task<IEnumerable<CierreCuentaCorrienteHistorialDto>> ObtenerHistorialAsync(
|
||||
DateTime? fechaDesde, DateTime? fechaHasta,
|
||||
int? idUsuarioModifico, string? tipoModificacion,
|
||||
int? idCierreAfectado);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user