298 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			298 lines
		
	
	
		
			16 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 EntradaSalidaCanillaRepository : IEntradaSalidaCanillaRepository | ||
|  |     { | ||
|  |         private readonly DbConnectionFactory _cf; | ||
|  |         private readonly ILogger<EntradaSalidaCanillaRepository> _log; | ||
|  | 
 | ||
|  |         public EntradaSalidaCanillaRepository(DbConnectionFactory cf, ILogger<EntradaSalidaCanillaRepository> log) | ||
|  |         { | ||
|  |             _cf = cf; | ||
|  |             _log = log; | ||
|  |         } | ||
|  | 
 | ||
|  |         private string SelectQueryBase() => @"
 | ||
|  |             SELECT  | ||
|  |                 Id_Parte AS IdParte, Id_Publicacion AS IdPublicacion, Id_Canilla AS IdCanilla, | ||
|  |                 Fecha, CantSalida, CantEntrada, Id_Precio AS IdPrecio, Id_Recargo AS IdRecargo, | ||
|  |                 Id_PorcMon AS IdPorcMon, Observacion, Liquidado, FechaLiquidado, UserLiq | ||
|  |             FROM dbo.dist_EntradasSalidasCanillas";
 | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<EntradaSalidaCanilla>> GetAllAsync( | ||
|  |             DateTime? fechaDesde, DateTime? fechaHasta, | ||
|  |             int? idPublicacion, int? idCanilla, bool? liquidados) | ||
|  |         { | ||
|  |             var sqlBuilder = new StringBuilder(SelectQueryBase()); | ||
|  |             sqlBuilder.Append(" 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 (idCanilla.HasValue) { sqlBuilder.Append(" AND Id_Canilla = @IdCanillaParam"); parameters.Add("IdCanillaParam", idCanilla.Value); } | ||
|  |             if (liquidados.HasValue) { sqlBuilder.Append(" AND Liquidado = @LiquidadoParam"); parameters.Add("LiquidadoParam", liquidados.Value); } | ||
|  | 
 | ||
|  |             sqlBuilder.Append(" ORDER BY Fecha DESC, Id_Canilla, Id_Publicacion, Id_Parte DESC;"); | ||
|  | 
 | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _cf.CreateConnection(); | ||
|  |                 return await connection.QueryAsync<EntradaSalidaCanilla>(sqlBuilder.ToString(), parameters); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _log.LogError(ex, "Error al obtener Entradas/Salidas de Canillitas."); | ||
|  |                 return Enumerable.Empty<EntradaSalidaCanilla>(); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<EntradaSalidaCanilla?> GetByIdAsync(int idParte) | ||
|  |         { | ||
|  |             var sql = SelectQueryBase() + " WHERE Id_Parte = @IdParteParam"; | ||
|  |             try | ||
|  |             { | ||
|  |                 using var connection = _cf.CreateConnection(); | ||
|  |                 return await connection.QuerySingleOrDefaultAsync<EntradaSalidaCanilla>(sql, new { IdParteParam = idParte }); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _log.LogError(ex, "Error al obtener EntradaSalidaCanilla por ID: {IdParte}", idParte); | ||
|  |                 return null; | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<EntradaSalidaCanilla>> GetByIdsAsync(IEnumerable<int> idsPartes, IDbTransaction? transaction = null) | ||
|  |         { | ||
|  |             if (idsPartes == null || !idsPartes.Any()) return Enumerable.Empty<EntradaSalidaCanilla>(); | ||
|  |             var sql = SelectQueryBase() + " WHERE Id_Parte IN @IdsPartesParam"; | ||
|  | 
 | ||
|  |             var cn = transaction?.Connection ?? _cf.CreateConnection(); | ||
|  |             bool ownConnection = transaction == null; | ||
|  |             IEnumerable<EntradaSalidaCanilla> result = Enumerable.Empty<EntradaSalidaCanilla>(); | ||
|  |             try | ||
|  |             { | ||
|  |                 if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); | ||
|  |                 result = await cn.QueryAsync<EntradaSalidaCanilla>(sql, new { IdsPartesParam = idsPartes }, transaction); | ||
|  |             } | ||
|  |             finally | ||
|  |             { | ||
|  |                 if (ownConnection && cn.State == ConnectionState.Open && cn is System.Data.Common.DbConnection dbConnClose) await dbConnClose.CloseAsync(); | ||
|  |                 if (ownConnection) (cn as IDisposable)?.Dispose(); | ||
|  |             } | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> ExistsByPublicacionCanillaFechaAsync(int idPublicacion, int idCanilla, DateTime fecha, IDbTransaction? transaction = null, int? excludeIdParte = null) | ||
|  |         { | ||
|  |             var sqlBuilder = new StringBuilder("SELECT COUNT(1) FROM dbo.dist_EntradasSalidasCanillas WHERE Id_Publicacion = @IdPubParam AND Id_Canilla = @IdCanParam AND Fecha = @FechaParam"); | ||
|  |             var parameters = new DynamicParameters(); | ||
|  |             parameters.Add("IdPubParam", idPublicacion); | ||
|  |             parameters.Add("IdCanParam", idCanilla); | ||
|  |             parameters.Add("FechaParam", fecha.Date); | ||
|  | 
 | ||
|  |             if (excludeIdParte.HasValue) | ||
|  |             { | ||
|  |                 sqlBuilder.Append(" AND Id_Parte != @ExcludeIdParteParam"); | ||
|  |                 parameters.Add("ExcludeIdParteParam", excludeIdParte.Value); | ||
|  |             } | ||
|  | 
 | ||
|  |             var connection = transaction?.Connection ?? _cf.CreateConnection(); | ||
|  |             bool ownConnection = transaction == null; | ||
|  |             bool result = false; | ||
|  |             try | ||
|  |             { | ||
|  |                 if (ownConnection && connection.State == ConnectionState.Closed && connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); | ||
|  | 
 | ||
|  |                 result = await connection.ExecuteScalarAsync<bool>(sqlBuilder.ToString(), parameters, transaction); | ||
|  | 
 | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 _log.LogError(ex, "Error en ExistsByPublicacionCanillaFechaAsync."); | ||
|  |                 return true; // Asumir que existe en caso de error | ||
|  |             } | ||
|  |             finally | ||
|  |             { | ||
|  |                 if (ownConnection && connection.State == ConnectionState.Open && connection is System.Data.Common.DbConnection dbConnClose) await dbConnClose.CloseAsync(); | ||
|  |                 if (ownConnection) (connection as IDisposable)?.Dispose(); | ||
|  |             } | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         public async Task<EntradaSalidaCanilla?> CreateAsync(EntradaSalidaCanilla nuevoES, int idUsuario, IDbTransaction transaction) | ||
|  |         { | ||
|  |             const string sqlInsert = @"
 | ||
|  |                 INSERT INTO dbo.dist_EntradasSalidasCanillas  | ||
|  |                     (Id_Publicacion, Id_Canilla, Fecha, CantSalida, CantEntrada, Id_Precio, Id_Recargo, Id_PorcMon, Observacion, Liquidado, FechaLiquidado, UserLiq) | ||
|  |                 OUTPUT INSERTED.Id_Parte AS IdParte, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Canilla AS IdCanilla, INSERTED.Fecha,  | ||
|  |                        INSERTED.CantSalida, INSERTED.CantEntrada, INSERTED.Id_Precio AS IdPrecio, INSERTED.Id_Recargo AS IdRecargo,  | ||
|  |                        INSERTED.Id_PorcMon AS IdPorcMon, INSERTED.Observacion, INSERTED.Liquidado, INSERTED.FechaLiquidado, INSERTED.UserLiq | ||
|  |                 VALUES (@IdPublicacion, @IdCanilla, @Fecha, @CantSalida, @CantEntrada, @IdPrecio, @IdRecargo, @IdPorcMon, @Observacion, @Liquidado, @FechaLiquidado, @UserLiq);";
 | ||
|  | 
 | ||
|  |             var inserted = await transaction.Connection!.QuerySingleAsync<EntradaSalidaCanilla>(sqlInsert, nuevoES, transaction); | ||
|  |             if (inserted == null || inserted.IdParte == 0) throw new DataException("Error al crear E/S Canilla o ID no generado."); | ||
|  | 
 | ||
|  |             const string sqlHistorico = @"
 | ||
|  |                 INSERT INTO dbo.dist_EntradasSalidasCanillas_H  | ||
|  |                     (Id_Parte, Id_Publicacion, Id_Canilla, Fecha, CantSalida, CantEntrada, Id_Precio, Id_Recargo, Id_PorcMon, Observacion, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdParteHist, @IdPubHist, @IdCanillaHist, @FechaHist, @CantSalidaHist, @CantEntradaHist, @IdPrecioHist, @IdRecargoHist, @IdPorcMonHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  |             // Liquidado, FechaLiquidado, UserLiq no van al historial de creación, sino al de liquidación. | ||
|  | 
 | ||
|  |             await transaction.Connection!.ExecuteAsync(sqlHistorico, new | ||
|  |             { | ||
|  |                 IdParteHist = inserted.IdParte, | ||
|  |                 IdPubHist = inserted.IdPublicacion, | ||
|  |                 IdCanillaHist = inserted.IdCanilla, | ||
|  |                 FechaHist = inserted.Fecha, | ||
|  |                 CantSalidaHist = inserted.CantSalida, | ||
|  |                 CantEntradaHist = inserted.CantEntrada, | ||
|  |                 IdPrecioHist = inserted.IdPrecio, | ||
|  |                 IdRecargoHist = inserted.IdRecargo, | ||
|  |                 IdPorcMonHist = inserted.IdPorcMon, | ||
|  |                 ObsHist = inserted.Observacion, | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = "Creada" | ||
|  |             }, transaction); | ||
|  |             return inserted; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> UpdateAsync(EntradaSalidaCanilla esAActualizar, int idUsuario, IDbTransaction transaction, string tipoMod = "Actualizada") | ||
|  |         { | ||
|  |             var actual = await transaction.Connection!.QuerySingleOrDefaultAsync<EntradaSalidaCanilla>(SelectQueryBase() + " WHERE Id_Parte = @IdParteParam", | ||
|  |                 new { IdParteParam = esAActualizar.IdParte }, transaction); | ||
|  |             if (actual == null) throw new KeyNotFoundException("Registro E/S Canilla no encontrado."); | ||
|  | 
 | ||
|  |             const string sqlUpdate = @"
 | ||
|  |                 UPDATE dbo.dist_EntradasSalidasCanillas SET | ||
|  |                     CantSalida = @CantSalida, CantEntrada = @CantEntrada, Observacion = @Observacion, | ||
|  |                     Liquidado = @Liquidado, FechaLiquidado = @FechaLiquidado, UserLiq = @UserLiq, | ||
|  |                     Id_Precio = @IdPrecio, Id_Recargo = @IdRecargo, Id_PorcMon = @IdPorcMon  | ||
|  |                     -- No se permite cambiar Publicacion, Canilla, Fecha directamente aquí. | ||
|  |                 WHERE Id_Parte = @IdParte;";
 | ||
|  | 
 | ||
|  |             const string sqlHistorico = @"
 | ||
|  |                 INSERT INTO dbo.dist_EntradasSalidasCanillas_H  | ||
|  |                     (Id_Parte, Id_Publicacion, Id_Canilla, Fecha, CantSalida, CantEntrada, Id_Precio, Id_Recargo, Id_PorcMon, Observacion, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdParteHist, @IdPubHist, @IdCanillaHist, @FechaHist, @CantSalidaHist, @CantEntradaHist, @IdPrecioHist, @IdRecargoHist, @IdPorcMonHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             await transaction.Connection!.ExecuteAsync(sqlHistorico, new | ||
|  |             { | ||
|  |                 IdParteHist = actual.IdParte, | ||
|  |                 IdPubHist = actual.IdPublicacion, | ||
|  |                 IdCanillaHist = actual.IdCanilla, | ||
|  |                 FechaHist = actual.Fecha, | ||
|  |                 CantSalidaHist = actual.CantSalida, | ||
|  |                 CantEntradaHist = actual.CantEntrada, | ||
|  |                 IdPrecioHist = actual.IdPrecio, | ||
|  |                 IdRecargoHist = actual.IdRecargo, | ||
|  |                 IdPorcMonHist = actual.IdPorcMon, | ||
|  |                 ObsHist = actual.Observacion, // Valores ANTERIORES | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = tipoMod | ||
|  |             }, transaction); | ||
|  | 
 | ||
|  |             var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, esAActualizar, transaction); | ||
|  |             return rowsAffected == 1; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<bool> LiquidarAsync(IEnumerable<int> idsPartes, DateTime fechaLiquidacion, int idUsuarioLiquidador, IDbTransaction transaction) | ||
|  |         { | ||
|  |             // Primero, obtener los registros actuales para el historial | ||
|  |             var movimientosALiquidar = await GetByIdsAsync(idsPartes, transaction); | ||
|  |             if (!movimientosALiquidar.Any() || movimientosALiquidar.Count() != idsPartes.Distinct().Count()) | ||
|  |             { | ||
|  |                 _log.LogWarning("Intento de liquidar IdsPartes no encontrados o inconsistentes."); | ||
|  |                 return false; // O lanzar excepción | ||
|  |             } | ||
|  | 
 | ||
|  |             const string sqlUpdate = @"
 | ||
|  |                 UPDATE dbo.dist_EntradasSalidasCanillas SET | ||
|  |                     Liquidado = 1, FechaLiquidado = @FechaLiquidacionParam, UserLiq = @UserLiqParam | ||
|  |                 WHERE Id_Parte = @IdParteParam AND Liquidado = 0;"; // Solo liquidar los no liquidados
 | ||
|  | 
 | ||
|  |             const string sqlHistorico = @"
 | ||
|  |                  INSERT INTO dbo.dist_EntradasSalidasCanillas_H  | ||
|  |                     (Id_Parte, Id_Publicacion, Id_Canilla, Fecha, CantSalida, CantEntrada, Id_Precio, Id_Recargo, Id_PorcMon, Observacion, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdParteHist, @IdPubHist, @IdCanillaHist, @FechaHist, @CantSalidaHist, @CantEntradaHist, @IdPrecioHist, @IdRecargoHist, @IdPorcMonHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             int totalRowsAffected = 0; | ||
|  |             foreach (var mov in movimientosALiquidar) | ||
|  |             { | ||
|  |                 if (mov.Liquidado) continue; // Ya estaba liquidado, no hacer nada ni registrar historial | ||
|  | 
 | ||
|  |                 await transaction.Connection!.ExecuteAsync(sqlHistorico, new | ||
|  |                 { | ||
|  |                     IdParteHist = mov.IdParte, | ||
|  |                     IdPubHist = mov.IdPublicacion, | ||
|  |                     IdCanillaHist = mov.IdCanilla, | ||
|  |                     FechaHist = mov.Fecha, | ||
|  |                     CantSalidaHist = mov.CantSalida, | ||
|  |                     CantEntradaHist = mov.CantEntrada, | ||
|  |                     IdPrecioHist = mov.IdPrecio, | ||
|  |                     IdRecargoHist = mov.IdRecargo, | ||
|  |                     IdPorcMonHist = mov.IdPorcMon, | ||
|  |                     ObsHist = mov.Observacion, | ||
|  |                     IdUsuarioHist = idUsuarioLiquidador, | ||
|  |                     FechaModHist = DateTime.Now, | ||
|  |                     TipoModHist = "Liquidada" | ||
|  |                 }, transaction); | ||
|  | 
 | ||
|  |                 var rows = await transaction.Connection!.ExecuteAsync(sqlUpdate, | ||
|  |                     new { FechaLiquidacionParam = fechaLiquidacion.Date, UserLiqParam = idUsuarioLiquidador, IdParteParam = mov.IdParte }, | ||
|  |                     transaction); | ||
|  |                 totalRowsAffected += rows; | ||
|  |             } | ||
|  |             // Se considera éxito si al menos una fila fue afectada (o si todas ya estaban liquidadas y no hubo errores) | ||
|  |             // Si se pasaron IDs que no existen, GetByIdsAsync ya devolvería menos elementos. | ||
|  |             return totalRowsAffected >= 0; | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         public async Task<bool> DeleteAsync(int idParte, int idUsuario, IDbTransaction transaction) | ||
|  |         { | ||
|  |             var actual = await GetByIdAsync(idParte); // No necesita TX, solo para el historial | ||
|  |             if (actual == null) throw new KeyNotFoundException("Registro E/S Canilla no encontrado para eliminar."); | ||
|  |             if (actual.Liquidado) throw new InvalidOperationException("No se puede eliminar un movimiento liquidado."); | ||
|  | 
 | ||
|  | 
 | ||
|  |             const string sqlDelete = "DELETE FROM dbo.dist_EntradasSalidasCanillas WHERE Id_Parte = @IdParteParam"; | ||
|  |             const string sqlHistorico = @"
 | ||
|  |                  INSERT INTO dbo.dist_EntradasSalidasCanillas_H  | ||
|  |                     (Id_Parte, Id_Publicacion, Id_Canilla, Fecha, CantSalida, CantEntrada, Id_Precio, Id_Recargo, Id_PorcMon, Observacion, Id_Usuario, FechaMod, TipoMod) | ||
|  |                 VALUES (@IdParteHist, @IdPubHist, @IdCanillaHist, @FechaHist, @CantSalidaHist, @CantEntradaHist, @IdPrecioHist, @IdRecargoHist, @IdPorcMonHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);";
 | ||
|  | 
 | ||
|  |             await transaction.Connection!.ExecuteAsync(sqlHistorico, new | ||
|  |             { | ||
|  |                 IdParteHist = actual.IdParte, | ||
|  |                 IdPubHist = actual.IdPublicacion, | ||
|  |                 IdCanillaHist = actual.IdCanilla, | ||
|  |                 FechaHist = actual.Fecha, | ||
|  |                 CantSalidaHist = actual.CantSalida, | ||
|  |                 CantEntradaHist = actual.CantEntrada, | ||
|  |                 IdPrecioHist = actual.IdPrecio, | ||
|  |                 IdRecargoHist = actual.IdRecargo, | ||
|  |                 IdPorcMonHist = actual.IdPorcMon, | ||
|  |                 ObsHist = actual.Observacion, | ||
|  |                 IdUsuarioHist = idUsuario, | ||
|  |                 FechaModHist = DateTime.Now, | ||
|  |                 TipoModHist = "Eliminada" | ||
|  |             }, transaction); | ||
|  | 
 | ||
|  |             var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdParteParam = idParte }, transaction); | ||
|  |             return rowsAffected == 1; | ||
|  |         } | ||
|  |     } | ||
|  | } |