Diseño de un AuditoriaController con un patrón para añadir endpoints de historial para diferentes entidades. Implementación de la lógica de servicio y repositorio para obtener datos de las tablas _H para: Usuarios (gral_Usuarios_H) Pagos de Distribuidores (cue_PagosDistribuidor_H) Notas de Crédito/Débito (cue_CreditosDebitos_H) Entradas/Salidas de Distribuidores (dist_EntradasSalidas_H) Entradas/Salidas de Canillitas (dist_EntradasSalidasCanillas_H) Novedades de Canillitas (dist_dtNovedadesCanillas_H) Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial) Tipos de Pago (cue_dtTipopago_H) Canillitas (Maestro) (dist_dtCanillas_H) Distribuidores (Maestro) (dist_dtDistribuidores_H) Empresas (Maestro) (dist_dtEmpresas_H) DTOs específicos para cada tipo de historial, incluyendo NombreUsuarioModifico. Frontend: Servicio auditoriaService.ts con métodos para llamar a cada endpoint de historial. Página AuditoriaGeneralPage.tsx con: Selector de "Tipo de Entidad a Auditar". Filtros comunes (Fechas, Usuario Modificador, Tipo de Modificación, ID Entidad). Un DataGrid que muestra las columnas dinámicamente según el tipo de entidad seleccionada. Lógica para cargar los datos correspondientes. DTOs de historial en TypeScript. Actualizaciones en AppRoutes.tsx y MainLayout.tsx para la nueva sección de Auditoría (restringida a SuperAdmin).
236 lines
11 KiB
C#
236 lines
11 KiB
C#
using Dapper;
|
|
using GestionIntegral.Api.Data.Repositories;
|
|
using GestionIntegral.Api.Models;
|
|
using Microsoft.Extensions.Logging;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Threading.Tasks;
|
|
using GestionIntegral.Api.Models.Contables;
|
|
using System.Text;
|
|
|
|
namespace GestionIntegral.Api.Data.Repositories.Contables
|
|
{
|
|
public class SaldoRepository : ISaldoRepository
|
|
{
|
|
private readonly DbConnectionFactory _connectionFactory;
|
|
private readonly ILogger<SaldoRepository> _logger;
|
|
|
|
public SaldoRepository(DbConnectionFactory connectionFactory, ILogger<SaldoRepository> logger)
|
|
{
|
|
_connectionFactory = connectionFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<IEnumerable<int>> GetAllDistribuidorIdsAsync()
|
|
{
|
|
var sql = "SELECT Id_Distribuidor FROM dbo.dist_dtDistribuidores";
|
|
try
|
|
{
|
|
using (var connection = _connectionFactory.CreateConnection())
|
|
{
|
|
return await connection.QueryAsync<int>(sql);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error al obtener IDs de Distribuidores.");
|
|
return Enumerable.Empty<int>();
|
|
}
|
|
}
|
|
|
|
public async Task<bool> CreateSaldoInicialAsync(string destino, int idDestino, int idEmpresa, IDbTransaction transaction)
|
|
{
|
|
var sql = @"
|
|
INSERT INTO dbo.cue_Saldos (Destino, Id_Destino, Monto, Id_Empresa)
|
|
VALUES (@Destino, @IdDestino, 0.00, @IdEmpresa);";
|
|
try
|
|
{
|
|
int rowsAffected = await transaction.Connection!.ExecuteAsync(sql, // Añadir !
|
|
new { Destino = destino, IdDestino = idDestino, IdEmpresa = idEmpresa },
|
|
transaction: transaction);
|
|
return rowsAffected == 1;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error al insertar saldo inicial para {Destino} ID {IdDestino}, Empresa ID {IdEmpresa}.", destino, idDestino, idEmpresa);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<bool> DeleteSaldosByEmpresaAsync(int idEmpresa, IDbTransaction transaction)
|
|
{
|
|
var sql = "DELETE FROM dbo.cue_Saldos WHERE Id_Empresa = @IdEmpresa";
|
|
try
|
|
{
|
|
await transaction.Connection!.ExecuteAsync(sql, new { IdEmpresa = idEmpresa }, transaction: transaction);
|
|
return true; // Asumir éxito si no hay excepción
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error al eliminar saldos para Empresa ID {IdEmpresa}.", idEmpresa);
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public async Task<bool> ModificarSaldoAsync(string destino, int idDestino, int idEmpresa, decimal montoAAgregar, IDbTransaction? transaction = null)
|
|
{
|
|
var sql = @"UPDATE dbo.cue_Saldos
|
|
SET Monto = Monto + @MontoAAgregar,
|
|
FechaUltimaModificacion = @FechaActualizacion -- << AÑADIR
|
|
WHERE Destino = @Destino AND Id_Destino = @IdDestino AND Id_Empresa = @IdEmpresa;";
|
|
|
|
IDbConnection connection = transaction?.Connection ?? _connectionFactory.CreateConnection();
|
|
bool ownConnection = transaction == null;
|
|
|
|
try
|
|
{
|
|
if (ownConnection && connection.State != ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
|
}
|
|
|
|
var parameters = new
|
|
{
|
|
MontoAAgregar = montoAAgregar,
|
|
Destino = destino,
|
|
IdDestino = idDestino,
|
|
IdEmpresa = idEmpresa,
|
|
FechaActualizacion = DateTime.Now // O DateTime.UtcNow si prefieres
|
|
};
|
|
int rowsAffected = await connection.ExecuteAsync(sql, parameters, transaction: transaction);
|
|
return rowsAffected == 1;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error al modificar saldo para {Destino} ID {IdDestino}, Empresa ID {IdEmpresa}.", destino, idDestino, idEmpresa);
|
|
if (transaction != null) throw;
|
|
return false;
|
|
}
|
|
finally
|
|
{
|
|
if (ownConnection && connection.State == ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
|
|
}
|
|
if (ownConnection && connection is IDisposable d) d.Dispose(); // Mejorar dispose
|
|
}
|
|
}
|
|
|
|
public async Task<bool> CheckIfSaldosExistForEmpresaAsync(int idEmpresa)
|
|
{
|
|
var sql = "SELECT COUNT(1) FROM dbo.cue_Saldos WHERE Id_Empresa = @IdEmpresa";
|
|
try
|
|
{
|
|
// Este método es de solo lectura, no necesita transacción externa normalmente
|
|
using (var connection = _connectionFactory.CreateConnection())
|
|
{
|
|
var count = await connection.ExecuteScalarAsync<int>(sql, new { IdEmpresa = idEmpresa });
|
|
return count > 0; // Devuelve true si hay al menos un saldo
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error en CheckIfSaldosExistForEmpresaAsync para Empresa ID {IdEmpresa}", idEmpresa);
|
|
return false; // Asumir que no existen si hay error, para no bloquear la eliminación innecesariamente
|
|
// O podrías devolver true para ser más conservador si la verificación es crítica.
|
|
}
|
|
}
|
|
|
|
public async Task<IEnumerable<Saldo>> GetSaldosParaGestionAsync(string? destinoFilter, int? idDestinoFilter, int? idEmpresaFilter)
|
|
{
|
|
var sqlBuilder = new StringBuilder("SELECT Id_Saldo AS IdSaldo, Destino, Id_Destino AS IdDestino, Monto, Id_Empresa AS IdEmpresa, FechaUltimaModificacion FROM dbo.cue_Saldos WHERE 1=1");
|
|
var parameters = new DynamicParameters();
|
|
|
|
if (!string.IsNullOrWhiteSpace(destinoFilter))
|
|
{
|
|
sqlBuilder.Append(" AND Destino = @Destino");
|
|
parameters.Add("Destino", destinoFilter);
|
|
}
|
|
if (idDestinoFilter.HasValue)
|
|
{
|
|
sqlBuilder.Append(" AND Id_Destino = @IdDestino");
|
|
parameters.Add("IdDestino", idDestinoFilter.Value);
|
|
}
|
|
if (idEmpresaFilter.HasValue)
|
|
{
|
|
sqlBuilder.Append(" AND Id_Empresa = @IdEmpresa");
|
|
parameters.Add("IdEmpresa", idEmpresaFilter.Value);
|
|
}
|
|
sqlBuilder.Append(" ORDER BY Destino, Id_Empresa, Id_Destino;");
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
return await connection.QueryAsync<Saldo>(sqlBuilder.ToString(), parameters);
|
|
}
|
|
|
|
public async Task<Saldo?> GetSaldoAsync(string destino, int idDestino, int idEmpresa, IDbTransaction? transaction = null)
|
|
{
|
|
const string sql = "SELECT Id_Saldo AS IdSaldo, Destino, Id_Destino AS IdDestino, Monto, Id_Empresa AS IdEmpresa, FechaUltimaModificacion FROM dbo.cue_Saldos WHERE Destino = @Destino AND Id_Destino = @IdDestino AND Id_Empresa = @IdEmpresa;";
|
|
var conn = transaction?.Connection ?? _connectionFactory.CreateConnection();
|
|
if (transaction == null && conn.State != ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else conn.Open(); }
|
|
|
|
try
|
|
{
|
|
return await conn.QuerySingleOrDefaultAsync<Saldo>(sql, new { Destino = destino, IdDestino = idDestino, IdEmpresa = idEmpresa }, transaction);
|
|
}
|
|
finally
|
|
{
|
|
if (transaction == null && conn.State == ConnectionState.Open) { if (conn is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else conn.Close(); }
|
|
}
|
|
}
|
|
|
|
public async Task CreateSaldoAjusteHistorialAsync(SaldoAjusteHistorial historialEntry, IDbTransaction transaction)
|
|
{
|
|
const string sql = @"
|
|
INSERT INTO dbo.cue_SaldoAjustesHistorial
|
|
(Destino, Id_Destino, Id_Empresa, MontoAjuste, SaldoAnterior, SaldoNuevo, Justificacion, FechaAjuste, Id_UsuarioAjuste)
|
|
VALUES
|
|
(@Destino, @IdDestino, @IdEmpresa, @MontoAjuste, @SaldoAnterior, @SaldoNuevo, @Justificacion, @FechaAjuste, @IdUsuarioAjuste);";
|
|
|
|
await transaction.Connection!.ExecuteAsync(sql, historialEntry, transaction);
|
|
}
|
|
|
|
public async Task<IEnumerable<(SaldoAjusteHistorial Historial, string NombreUsuarioModifico)>> GetHistorialAjustesAsync(
|
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
|
int? idUsuarioModifico,
|
|
string? destino, int? idDestino, int? idEmpresa)
|
|
{
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
var sqlBuilder = new StringBuilder(@"
|
|
SELECT
|
|
h.IdSaldoAjusteHist, h.Destino, h.Id_Destino, h.Id_Empresa,
|
|
h.MontoAjuste, h.SaldoAnterior, h.SaldoNuevo, h.Justificacion,
|
|
h.FechaAjuste, h.Id_UsuarioAjuste,
|
|
u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
|
|
FROM dbo.cue_SaldoAjustesHistorial h
|
|
JOIN dbo.gral_Usuarios u ON h.Id_UsuarioAjuste = u.Id
|
|
WHERE 1=1");
|
|
|
|
var parameters = new DynamicParameters();
|
|
|
|
if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaAjuste >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
|
|
if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaAjuste <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); }
|
|
if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_UsuarioAjuste = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); }
|
|
if (!string.IsNullOrWhiteSpace(destino)) { sqlBuilder.Append(" AND h.Destino = @DestinoParam"); parameters.Add("DestinoParam", destino); }
|
|
if (idDestino.HasValue) { sqlBuilder.Append(" AND h.Id_Destino = @IdDestinoParam"); parameters.Add("IdDestinoParam", idDestino.Value); }
|
|
if (idEmpresa.HasValue) { sqlBuilder.Append(" AND h.Id_Empresa = @IdEmpresaParam"); parameters.Add("IdEmpresaParam", idEmpresa.Value); }
|
|
|
|
sqlBuilder.Append(" ORDER BY h.FechaAjuste DESC;");
|
|
|
|
try
|
|
{
|
|
var result = await connection.QueryAsync<SaldoAjusteHistorial, string, (SaldoAjusteHistorial, string)>(
|
|
sqlBuilder.ToString(),
|
|
(hist, userName) => (hist, userName),
|
|
parameters,
|
|
splitOn: "NombreUsuarioModifico"
|
|
);
|
|
return result;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Error al obtener historial de Ajustes de Saldo.");
|
|
return Enumerable.Empty<(SaldoAjusteHistorial, string)>();
|
|
}
|
|
}
|
|
}
|
|
} |