Files
GestionIntegralWeb/Backend/GestionIntegral.Api/Data/Repositories/Distribucion/EntradaSalidaDistRepository.cs
eldiadmolinari 437b1e8864 Backend:
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).
2025-06-09 19:37:07 -03:00

256 lines
15 KiB
C#

using Dapper;
using GestionIntegral.Api.Models.Distribucion;
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.Distribucion
{
public class EntradaSalidaDistRepository : IEntradaSalidaDistRepository
{
private readonly DbConnectionFactory _cf;
private readonly ILogger<EntradaSalidaDistRepository> _log;
public EntradaSalidaDistRepository(DbConnectionFactory cf, ILogger<EntradaSalidaDistRepository> log)
{
_cf = cf;
_log = log;
}
public async Task<IEnumerable<EntradaSalidaDist>> GetAllAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idPublicacion, int? idDistribuidor, string? tipoMovimiento)
{
var sqlBuilder = new StringBuilder(@"
SELECT
Id_Parte AS IdParte, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor,
Fecha, TipoMovimiento, Cantidad, Remito, Observacion,
Id_Precio AS IdPrecio, Id_Recargo AS IdRecargo, Id_Porcentaje AS IdPorcentaje
FROM dbo.dist_EntradasSalidas
WHERE 1=1");
var parameters = new DynamicParameters();
if (fechaDesde.HasValue) { sqlBuilder.Append(" AND Fecha >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
if (fechaHasta.HasValue) { sqlBuilder.Append(" AND Fecha <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date); }
if (idPublicacion.HasValue) { sqlBuilder.Append(" AND Id_Publicacion = @IdPublicacionParam"); parameters.Add("IdPublicacionParam", idPublicacion.Value); }
if (idDistribuidor.HasValue) { sqlBuilder.Append(" AND Id_Distribuidor = @IdDistribuidorParam"); parameters.Add("IdDistribuidorParam", idDistribuidor.Value); }
if (!string.IsNullOrWhiteSpace(tipoMovimiento)) { sqlBuilder.Append(" AND TipoMovimiento = @TipoMovimientoParam"); parameters.Add("TipoMovimientoParam", tipoMovimiento); }
sqlBuilder.Append(" ORDER BY Fecha DESC, Id_Parte DESC;");
try
{
using var connection = _cf.CreateConnection();
return await connection.QueryAsync<EntradaSalidaDist>(sqlBuilder.ToString(), parameters);
}
catch (Exception ex)
{
_log.LogError(ex, "Error al obtener Entradas/Salidas de Distribuidores.");
return Enumerable.Empty<EntradaSalidaDist>();
}
}
public async Task<EntradaSalidaDist?> GetByIdAsync(int idParte)
{
const string sql = @"
SELECT
Id_Parte AS IdParte, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor,
Fecha, TipoMovimiento, Cantidad, Remito, Observacion,
Id_Precio AS IdPrecio, Id_Recargo AS IdRecargo, Id_Porcentaje AS IdPorcentaje
FROM dbo.dist_EntradasSalidas
WHERE Id_Parte = @IdParteParam";
try
{
using var connection = _cf.CreateConnection();
return await connection.QuerySingleOrDefaultAsync<EntradaSalidaDist>(sql, new { IdParteParam = idParte });
}
catch (Exception ex)
{
_log.LogError(ex, "Error al obtener EntradaSalidaDist por ID: {IdParte}", idParte);
return null;
}
}
public async Task<bool> ExistsByRemitoAndTipoForPublicacionAsync(int remito, string tipoMovimiento, int idPublicacion, int? excludeIdParte = null)
{
var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_EntradasSalidas WHERE Remito = @RemitoParam AND TipoMovimiento = @TipoMovimientoParam AND Id_Publicacion = @IdPublicacionParam");
var parameters = new DynamicParameters();
parameters.Add("RemitoParam", remito);
parameters.Add("TipoMovimientoParam", tipoMovimiento);
parameters.Add("IdPublicacionParam", idPublicacion);
if (excludeIdParte.HasValue)
{
sqlBuilder.Append(" AND Id_Parte != @ExcludeIdParteParam");
parameters.Add("ExcludeIdParteParam", excludeIdParte.Value);
}
try
{
using var connection = _cf.CreateConnection();
return await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters);
}
catch (Exception ex)
{
_log.LogError(ex, "Error en ExistsByRemitoAndTipoForPublicacionAsync. Remito: {Remito}", remito);
return true; // Asumir que existe en caso de error para prevenir duplicados
}
}
public async Task<EntradaSalidaDist?> CreateAsync(EntradaSalidaDist nuevaES, int idUsuario, IDbTransaction transaction)
{
const string sqlInsert = @"
INSERT INTO dbo.dist_EntradasSalidas (Id_Publicacion, Id_Distribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio, Id_Recargo, Id_Porcentaje)
OUTPUT INSERTED.Id_Parte AS IdParte, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Distribuidor AS IdDistribuidor,
INSERTED.Fecha, INSERTED.TipoMovimiento, INSERTED.Cantidad, INSERTED.Remito, INSERTED.Observacion,
INSERTED.Id_Precio AS IdPrecio, INSERTED.Id_Recargo AS IdRecargo, INSERTED.Id_Porcentaje AS IdPorcentaje
VALUES (@IdPublicacion, @IdDistribuidor, @Fecha, @TipoMovimiento, @Cantidad, @Remito, @Observacion, @IdPrecio, @IdRecargo, @IdPorcentaje);";
const string sqlHistorico = @"
INSERT INTO dbo.dist_EntradasSalidas_H
(Id_Parte, Id_Publicacion, Id_Distribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio, Id_Recargo, Id_Porcentaje, Id_Usuario, FechaMod, TipoMod)
VALUES (@IdParteHist, @IdPublicacionHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @CantidadHist, @RemitoHist, @ObservacionHist, @IdPrecioHist, @IdRecargoHist, @IdPorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
var inserted = await transaction.Connection!.QuerySingleAsync<EntradaSalidaDist>(sqlInsert, nuevaES, transaction);
if (inserted == null || inserted.IdParte == 0) throw new DataException("Error al crear Entrada/Salida o ID no generado.");
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
{
IdParteHist = inserted.IdParte,
IdPublicacionHist = inserted.IdPublicacion,
IdDistribuidorHist = inserted.IdDistribuidor,
FechaHist = inserted.Fecha,
TipoMovimientoHist = inserted.TipoMovimiento,
CantidadHist = inserted.Cantidad,
RemitoHist = inserted.Remito,
ObservacionHist = inserted.Observacion,
IdPrecioHist = inserted.IdPrecio,
IdRecargoHist = inserted.IdRecargo,
IdPorcentajeHist = inserted.IdPorcentaje,
IdUsuarioHist = idUsuario,
FechaModHist = DateTime.Now,
TipoModHist = "Creada"
}, transaction);
return inserted;
}
public async Task<bool> UpdateAsync(EntradaSalidaDist esAActualizar, int idUsuario, IDbTransaction transaction)
{
var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<EntradaSalidaDist>(
@"SELECT Id_Parte AS IdParte, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio AS IdPrecio, Id_Recargo AS IdRecargo, Id_Porcentaje AS IdPorcentaje
FROM dbo.dist_EntradasSalidas WHERE Id_Parte = @IdParteParam",
new { IdParteParam = esAActualizar.IdParte }, transaction);
if (actual == null) throw new KeyNotFoundException("Registro de Entrada/Salida no encontrado.");
const string sqlUpdate = @"
UPDATE dbo.dist_EntradasSalidas SET
Cantidad = @Cantidad, Observacion = @Observacion
-- Publicacion, Distribuidor, Fecha, TipoMovimiento, Remito, Ids de precio/recargo/porc no se modifican aquí
WHERE Id_Parte = @IdParte;";
const string sqlHistorico = @"
INSERT INTO dbo.dist_EntradasSalidas_H
(Id_Parte, Id_Publicacion, Id_Distribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio, Id_Recargo, Id_Porcentaje, Id_Usuario, FechaMod, TipoMod)
VALUES (@IdParteHist, @IdPublicacionHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @CantidadHist, @RemitoHist, @ObservacionHist, @IdPrecioHist, @IdRecargoHist, @IdPorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
{
IdParteHist = actual.IdParte,
IdPublicacionHist = actual.IdPublicacion,
IdDistribuidorHist = actual.IdDistribuidor,
FechaHist = actual.Fecha,
TipoMovimientoHist = actual.TipoMovimiento,
CantidadHist = actual.Cantidad,
RemitoHist = actual.Remito,
ObservacionHist = actual.Observacion,
IdPrecioHist = actual.IdPrecio,
IdRecargoHist = actual.IdRecargo,
IdPorcentajeHist = actual.IdPorcentaje, // Valores ANTERIORES
IdUsuarioHist = idUsuario,
FechaModHist = DateTime.Now,
TipoModHist = "Actualizada"
}, transaction);
var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, esAActualizar, transaction);
return rowsAffected == 1;
}
public async Task<bool> DeleteAsync(int idParte, int idUsuario, IDbTransaction transaction)
{
var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<EntradaSalidaDist>(
@"SELECT Id_Parte AS IdParte, Id_Publicacion AS IdPublicacion, Id_Distribuidor AS IdDistribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio AS IdPrecio, Id_Recargo AS IdRecargo, Id_Porcentaje AS IdPorcentaje
FROM dbo.dist_EntradasSalidas WHERE Id_Parte = @IdParteParam",
new { IdParteParam = idParte }, transaction);
if (actual == null) throw new KeyNotFoundException("Registro de Entrada/Salida no encontrado para eliminar.");
const string sqlDelete = "DELETE FROM dbo.dist_EntradasSalidas WHERE Id_Parte = @IdParteParam";
const string sqlHistorico = @"
INSERT INTO dbo.dist_EntradasSalidas_H
(Id_Parte, Id_Publicacion, Id_Distribuidor, Fecha, TipoMovimiento, Cantidad, Remito, Observacion, Id_Precio, Id_Recargo, Id_Porcentaje, Id_Usuario, FechaMod, TipoMod)
VALUES (@IdParteHist, @IdPublicacionHist, @IdDistribuidorHist, @FechaHist, @TipoMovimientoHist, @CantidadHist, @RemitoHist, @ObservacionHist, @IdPrecioHist, @IdRecargoHist, @IdPorcentajeHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
await transaction.Connection!.ExecuteAsync(sqlHistorico, new
{
IdParteHist = actual.IdParte,
IdPublicacionHist = actual.IdPublicacion,
IdDistribuidorHist = actual.IdDistribuidor,
FechaHist = actual.Fecha,
TipoMovimientoHist = actual.TipoMovimiento,
CantidadHist = actual.Cantidad,
RemitoHist = actual.Remito,
ObservacionHist = actual.Observacion,
IdPrecioHist = actual.IdPrecio,
IdRecargoHist = actual.IdRecargo,
IdPorcentajeHist = actual.IdPorcentaje,
IdUsuarioHist = idUsuario,
FechaModHist = DateTime.Now,
TipoModHist = "Eliminada"
}, transaction);
var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdParteParam = idParte }, transaction);
return rowsAffected == 1;
}
public async Task<IEnumerable<(EntradaSalidaDistHistorico Historial, string NombreUsuarioModifico)>> GetHistorialAsync(
DateTime? fechaDesde, DateTime? fechaHasta,
int? idUsuarioModifico, string? tipoModificacion,
int? idParteOriginal)
{
using var connection = _cf.CreateConnection();
var sqlBuilder = new StringBuilder(@"
SELECT
h.Id_Parte, h.Id_Publicacion, h.Id_Distribuidor, h.Fecha, h.TipoMovimiento,
h.Cantidad, h.Remito, h.Observacion, h.Id_Precio, h.Id_Recargo, h.Id_Porcentaje,
h.Id_Usuario, h.FechaMod, h.TipoMod,
u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico
FROM dbo.dist_EntradasSalidas_H h
JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
WHERE 1=1");
var parameters = new DynamicParameters();
if (fechaDesde.HasValue) { sqlBuilder.Append(" AND h.FechaMod >= @FechaDesdeParam"); parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); }
if (fechaHasta.HasValue) { sqlBuilder.Append(" AND h.FechaMod <= @FechaHastaParam"); parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); }
if (idUsuarioModifico.HasValue) { sqlBuilder.Append(" AND h.Id_Usuario = @IdUsuarioModificoParam"); parameters.Add("IdUsuarioModificoParam", idUsuarioModifico.Value); }
if (!string.IsNullOrWhiteSpace(tipoModificacion)) { sqlBuilder.Append(" AND h.TipoMod = @TipoModParam"); parameters.Add("TipoModParam", tipoModificacion); }
if (idParteOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Parte = @IdParteOriginalParam"); parameters.Add("IdParteOriginalParam", idParteOriginal.Value); }
sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");
try
{
var result = await connection.QueryAsync<EntradaSalidaDistHistorico, string, (EntradaSalidaDistHistorico, string)>(
sqlBuilder.ToString(),
(hist, userName) => (hist, userName),
parameters,
splitOn: "NombreUsuarioModifico"
);
return result;
}
catch (Exception ex)
{
_log.LogError(ex, "Error al obtener historial de Entradas/Salidas Distribuidores.");
return Enumerable.Empty<(EntradaSalidaDistHistorico, string)>();
}
}
}
}