using Dapper; using GestionIntegral.Api.Models.Impresion; 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 StockBobinaRepository : IStockBobinaRepository { private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public StockBobinaRepository(DbConnectionFactory connectionFactory, ILogger logger) { _connectionFactory = connectionFactory; _logger = logger; } public async Task> GetAllAsync( int? idTipoBobina, string? nroBobinaFilter, int? idPlanta, int? idEstadoBobina, string? remitoFilter, DateTime? fechaDesde, DateTime? fechaHasta) { var sqlBuilder = new StringBuilder(@" SELECT sb.Id_Bobina AS IdBobina, sb.Id_TipoBobina AS IdTipoBobina, sb.NroBobina, sb.Peso, sb.Id_Planta AS IdPlanta, sb.Id_EstadoBobina AS IdEstadoBobina, sb.Remito, sb.FechaRemito, sb.FechaEstado, sb.Id_Publicacion AS IdPublicacion, sb.Id_Seccion AS IdSeccion, sb.Obs FROM dbo.bob_StockBobinas sb WHERE 1=1"); var parameters = new DynamicParameters(); if (idTipoBobina.HasValue) { sqlBuilder.Append(" AND sb.Id_TipoBobina = @IdTipoBobinaParam"); parameters.Add("IdTipoBobinaParam", idTipoBobina.Value); } if (!string.IsNullOrWhiteSpace(nroBobinaFilter)) { sqlBuilder.Append(" AND sb.NroBobina LIKE @NroBobinaParam"); parameters.Add("NroBobinaParam", $"%{nroBobinaFilter}%"); } if (idPlanta.HasValue) { sqlBuilder.Append(" AND sb.Id_Planta = @IdPlantaParam"); parameters.Add("IdPlantaParam", idPlanta.Value); } if (idEstadoBobina.HasValue) { sqlBuilder.Append(" AND sb.Id_EstadoBobina = @IdEstadoBobinaParam"); parameters.Add("IdEstadoBobinaParam", idEstadoBobina.Value); } if (!string.IsNullOrWhiteSpace(remitoFilter)) { sqlBuilder.Append(" AND sb.Remito LIKE @RemitoParam"); parameters.Add("RemitoParam", $"%{remitoFilter}%"); } if (fechaDesde.HasValue) { sqlBuilder.Append(" AND sb.FechaRemito >= @FechaDesdeParam"); // O FechaEstado según el contexto del filtro parameters.Add("FechaDesdeParam", fechaDesde.Value.Date); } if (fechaHasta.HasValue) { sqlBuilder.Append(" AND sb.FechaRemito <= @FechaHastaParam"); // O FechaEstado parameters.Add("FechaHastaParam", fechaHasta.Value.Date.AddDays(1).AddTicks(-1)); // Hasta el final del día } sqlBuilder.Append(" ORDER BY sb.FechaRemito DESC, sb.NroBobina;"); try { using var connection = _connectionFactory.CreateConnection(); return await connection.QueryAsync(sqlBuilder.ToString(), parameters); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener Stock de Bobinas con filtros."); return Enumerable.Empty(); } } public async Task GetByIdAsync(int idBobina) { const string sql = @" SELECT Id_Bobina AS IdBobina, Id_TipoBobina AS IdTipoBobina, NroBobina, Peso, Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { IdBobinaParam = idBobina }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener StockBobina por ID: {IdBobina}", idBobina); return null; } } public async Task GetByNroBobinaAsync(string nroBobina) { const string sql = @" SELECT Id_Bobina AS IdBobina, Id_TipoBobina AS IdTipoBobina, NroBobina, Peso, Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs FROM dbo.bob_StockBobinas WHERE NroBobina = @NroBobinaParam"; try { using var connection = _connectionFactory.CreateConnection(); return await connection.QuerySingleOrDefaultAsync(sql, new { NroBobinaParam = nroBobina }); } catch (Exception ex) { _logger.LogError(ex, "Error al obtener StockBobina por NroBobina: {NroBobina}", nroBobina); return null; } } public async Task CreateAsync(StockBobina nuevaBobina, int idUsuario, IDbTransaction transaction) { const string sqlInsert = @" INSERT INTO dbo.bob_StockBobinas (Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs) OUTPUT INSERTED.Id_Bobina AS IdBobina, INSERTED.Id_TipoBobina AS IdTipoBobina, INSERTED.NroBobina, INSERTED.Peso, INSERTED.Id_Planta AS IdPlanta, INSERTED.Id_EstadoBobina AS IdEstadoBobina, INSERTED.Remito, INSERTED.FechaRemito, INSERTED.FechaEstado, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Seccion AS IdSeccion, INSERTED.Obs VALUES (@IdTipoBobina, @NroBobina, @Peso, @IdPlanta, @IdEstadoBobina, @Remito, @FechaRemito, @FechaEstado, @IdPublicacion, @IdSeccion, @Obs);"; var inserted = await transaction.Connection!.QuerySingleAsync(sqlInsert, nuevaBobina, transaction); if (inserted == null || inserted.IdBobina == 0) throw new DataException("Error al ingresar bobina o ID no generado."); const string sqlInsertHistorico = @" INSERT INTO dbo.bob_StockBobinas_H (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) VALUES (@IdBobinaHist, @IdTipoBobinaHist, @NroBobinaHist, @PesoHist, @IdPlantaHist, @IdEstadoBobinaHist, @RemitoHist, @FechaRemitoHist, @FechaEstadoHist, @IdPublicacionHist, @IdSeccionHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdBobinaHist = inserted.IdBobina, IdTipoBobinaHist = inserted.IdTipoBobina, NroBobinaHist = inserted.NroBobina, PesoHist = inserted.Peso, IdPlantaHist = inserted.IdPlanta, IdEstadoBobinaHist = inserted.IdEstadoBobina, RemitoHist = inserted.Remito, FechaRemitoHist = inserted.FechaRemito, FechaEstadoHist = inserted.FechaEstado, IdPublicacionHist = inserted.IdPublicacion, IdSeccionHist = inserted.IdSeccion, ObsHist = inserted.Obs, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Ingreso" }, transaction); return inserted; } public async Task UpdateAsync(StockBobina bobinaAActualizar, int idUsuario, IDbTransaction transaction, string tipoMod = "Actualizada") { // Obtener estado actual para el historial var actual = await transaction.Connection!.QuerySingleOrDefaultAsync( @"SELECT Id_Bobina AS IdBobina, Id_TipoBobina AS IdTipoBobina, NroBobina, Peso, Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam", new { IdBobinaParam = bobinaAActualizar.IdBobina }, transaction); if (actual == null) throw new KeyNotFoundException("Bobina no encontrada para actualizar."); const string sqlUpdate = @" UPDATE dbo.bob_StockBobinas SET Id_TipoBobina = @IdTipoBobina, NroBobina = @NroBobina, Peso = @Peso, Id_Planta = @IdPlanta, Id_EstadoBobina = @IdEstadoBobina, Remito = @Remito, FechaRemito = @FechaRemito, FechaEstado = @FechaEstado, Id_Publicacion = @IdPublicacion, Id_Seccion = @IdSeccion, Obs = @Obs WHERE Id_Bobina = @IdBobina;"; const string sqlInsertHistorico = @" INSERT INTO dbo.bob_StockBobinas_H (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) VALUES (@IdBobinaHist, @IdTipoBobinaHist, @NroBobinaHist, @PesoHist, @IdPlantaHist, @IdEstadoBobinaHist, @RemitoHist, @FechaRemitoHist, @FechaEstadoHist, @IdPublicacionHist, @IdSeccionHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await transaction.Connection!.ExecuteAsync(sqlInsertHistorico, new { IdBobinaHist = actual.IdBobina, IdTipoBobinaHist = actual.IdTipoBobina, NroBobinaHist = actual.NroBobina, PesoHist = actual.Peso, IdPlantaHist = actual.IdPlanta, IdEstadoBobinaHist = actual.IdEstadoBobina, RemitoHist = actual.Remito, FechaRemitoHist = actual.FechaRemito, FechaEstadoHist = actual.FechaEstado, IdPublicacionHist = actual.IdPublicacion, IdSeccionHist = actual.IdSeccion, ObsHist = actual.Obs, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = tipoMod // "Actualizada" o "Estado Cambiado" }, transaction); var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, bobinaAActualizar, transaction); return rowsAffected == 1; } public async Task DeleteAsync(int idBobina, int idUsuario, IDbTransaction transaction) { var connection = transaction.Connection!; // Asegurar que la conexión no es null var actual = await connection.QuerySingleOrDefaultAsync( @"SELECT Id_Bobina AS IdBobina, Id_TipoBobina AS IdTipoBobina, NroBobina, Peso, Id_Planta AS IdPlanta, Id_EstadoBobina AS IdEstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, Obs FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam", new { IdBobinaParam = idBobina }, transaction); if (actual == null) throw new KeyNotFoundException("Bobina no encontrada para eliminar."); // --- INICIO DE CAMBIO EN VALIDACIÓN --- // Permitir eliminar si está Disponible (1) o Dañada (3) if (actual.IdEstadoBobina != 1 && actual.IdEstadoBobina != 3) { _logger.LogWarning("Intento de eliminar bobina {IdBobina} que no está en estado 'Disponible' o 'Dañada'. Estado actual: {EstadoActual}", idBobina, actual.IdEstadoBobina); return false; // Devolver false si no cumple la condición para ser eliminada } // --- FIN DE CAMBIO EN VALIDACIÓN --- const string sqlDelete = "DELETE FROM dbo.bob_StockBobinas WHERE Id_Bobina = @IdBobinaParam"; const string sqlInsertHistorico = @" INSERT INTO dbo.bob_StockBobinas_H (Id_Bobina, Id_TipoBobina, NroBobina, Peso, Id_Planta, Id_EstadoBobina, Remito, FechaRemito, FechaEstado, Id_Publicacion, Id_Seccion, Obs, Id_Usuario, FechaMod, TipoMod) VALUES (@IdBobinaHist, @IdTipoBobinaHist, @NroBobinaHist, @PesoHist, @IdPlantaHist, @IdEstadoBobinaHist, @RemitoHist, @FechaRemitoHist, @FechaEstadoHist, @IdPublicacionHist, @IdSeccionHist, @ObsHist, @IdUsuarioHist, @FechaModHist, @TipoModHist);"; await connection.ExecuteAsync(sqlInsertHistorico, new { IdBobinaHist = actual.IdBobina, IdTipoBobinaHist = actual.IdTipoBobina, NroBobinaHist = actual.NroBobina, PesoHist = actual.Peso, IdPlantaHist = actual.IdPlanta, IdEstadoBobinaHist = actual.IdEstadoBobina, RemitoHist = actual.Remito, FechaRemitoHist = actual.FechaRemito, FechaEstadoHist = actual.FechaEstado, IdPublicacionHist = actual.IdPublicacion, IdSeccionHist = actual.IdSeccion, ObsHist = actual.Obs, IdUsuarioHist = idUsuario, FechaModHist = DateTime.Now, TipoModHist = "Eliminada" }, transaction); var rowsAffected = await connection.ExecuteAsync(sqlDelete, new { IdBobinaParam = idBobina }, transaction); return rowsAffected == 1; } public async Task> GetHistorialDetalladoAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idBobinaOriginal, int? idTipoBobinaFiltro, int? idPlantaFiltro, int? idEstadoBobinaFiltro) { using var connection = _connectionFactory.CreateConnection(); var sqlBuilder = new StringBuilder(@" SELECT h.Id_Bobina, h.Id_TipoBobina, h.NroBobina, h.Peso, h.Id_Planta, h.Id_EstadoBobina, h.Remito, h.FechaRemito, h.FechaEstado, h.Id_Publicacion, h.Id_Seccion, h.Obs, h.Id_Usuario, h.FechaMod, h.TipoMod, u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico, tb.Denominacion AS NombreTipoBobina, p.Nombre AS NombrePlanta, eb.Denominacion AS NombreEstadoBobina, pub.Nombre AS NombrePublicacion, sec.Nombre AS NombreSeccion FROM dbo.bob_StockBobinas_H h JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id LEFT JOIN dbo.bob_dtBobinas tb ON h.Id_TipoBobina = tb.Id_TipoBobina LEFT JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta LEFT JOIN dbo.bob_dtEstadosBobinas eb ON h.Id_EstadoBobina = eb.Id_EstadoBobina LEFT JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion LEFT JOIN dbo.dist_dtPubliSecciones sec ON h.Id_Seccion = sec.Id_Seccion AND h.Id_Publicacion = sec.Id_Publicacion -- Asegurar que la sección pertenezca a la publicación 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 (idBobinaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Bobina = @IdBobinaOriginalParam"); parameters.Add("IdBobinaOriginalParam", idBobinaOriginal.Value); } if (idTipoBobinaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_TipoBobina = @IdTipoBobinaFiltroParam"); parameters.Add("IdTipoBobinaFiltroParam", idTipoBobinaFiltro.Value); } if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); } if (idEstadoBobinaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_EstadoBobina = @IdEstadoBobinaFiltroParam"); parameters.Add("IdEstadoBobinaFiltroParam", idEstadoBobinaFiltro.Value); } sqlBuilder.Append(" ORDER BY h.FechaMod DESC;"); try { var result = await connection.QueryAsync( sqlBuilder.ToString(), (hist, userMod, tipoBob, planta, estadoBob, pub, secc) => (hist, userMod, tipoBob, planta, estadoBob, pub, secc), parameters, splitOn: "NombreUsuarioModifico,NombreTipoBobina,NombrePlanta,NombreEstadoBobina,NombrePublicacion,NombreSeccion" ); return result; } catch (Exception ex) { _logger.LogError(ex, "Error al obtener historial detallado de Stock de Bobinas."); return Enumerable.Empty<(StockBobinaHistorico, string, string?, string?, string?, string?, string?)>(); } } } }