All checks were successful
		
		
	
	Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 12m54s
				
			**Backend:** - Se ha añadido el endpoint `PUT /api/tiradas` para manejar la modificación de una Tirada, identificada por su clave única (fecha, idPublicacion, idPlanta). - Se implementó un mecanismo de actualización granular para las secciones de la tirada (`bob_RegPublicaciones`), reemplazando la estrategia anterior de "eliminar todo y recrear". - La nueva lógica reconcilia el estado entrante con el de la base de datos, realizando operaciones individuales de `INSERT`, `UPDATE` y `DELETE` para cada sección. - Esto mejora significativamente el rendimiento y proporciona un historial de auditoría mucho más preciso. - Se añadieron los DTOs `UpdateTiradaRequestDto` y `UpdateDetalleSeccionTiradaDto` para soportar el nuevo payload de modificación. - Se expandieron los repositorios `IRegPublicacionSeccionRepository` y `IPubliSeccionRepository` con métodos para operaciones granulares (`UpdateAsync`, `DeleteByIdAsync`, `GetByIdsAndPublicacionAsync`). **Frontend:** - El componente `TiradaFormModal` ha sido refactorizado para funcionar tanto en modo "Crear" como en modo "Editar", recibiendo una prop `tiradaToEdit`. - Se implementó una lógica de carga asíncrona robusta que obtiene los datos completos de una tirada antes de abrir el modal en modo edición. **Mejoras de UI/UX:** - Se ha rediseñado el layout de la lista de tiradas en `GestionarTiradasPage`: - Los botones de acción (Editar, Borrar) y los datos clave (chips de ejemplares y páginas) ahora se encuentran en una cabecera estática. - Estos elementos permanecen fijos en la parte superior y no se desplazan al expandir el acordeón, mejorando la consistencia visual. - Se ha mejorado la tabla de secciones dentro del `TiradaFormModal`: - El botón "+ AGREGAR SECCIÓN" ahora está fijo en la parte inferior de la tabla, permaneciendo siempre visible incluso cuando la lista de secciones tiene scroll. - Al agregar una nueva sección, la lista se desplaza automáticamente hacia abajo para mostrar la nueva fila.
		
			
				
	
	
		
			403 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using Dapper;
 | 
						|
using GestionIntegral.Api.Models.Impresion;
 | 
						|
using Microsoft.Extensions.Logging;
 | 
						|
using System;
 | 
						|
using System.Collections.Generic;
 | 
						|
using System.Data;
 | 
						|
using System.Text;
 | 
						|
using System.Threading.Tasks;
 | 
						|
 | 
						|
namespace GestionIntegral.Api.Data.Repositories.Impresion
 | 
						|
{
 | 
						|
    public class RegTiradaRepository : IRegTiradaRepository
 | 
						|
    {
 | 
						|
        private readonly DbConnectionFactory _cf;
 | 
						|
        private readonly ILogger<RegTiradaRepository> _log;
 | 
						|
 | 
						|
        public RegTiradaRepository(DbConnectionFactory cf, ILogger<RegTiradaRepository> log)
 | 
						|
        {
 | 
						|
            _cf = cf;
 | 
						|
            _log = log;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<RegTirada?> GetByIdAsync(int idRegistro)
 | 
						|
        {
 | 
						|
            const string sql = "SELECT Id_Registro AS IdRegistro, Ejemplares, Id_Publicacion AS IdPublicacion, Fecha, Id_Planta AS IdPlanta FROM dbo.bob_RegTiradas WHERE Id_Registro = @IdRegistroParam";
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            return await connection.QuerySingleOrDefaultAsync<RegTirada>(sql, new { IdRegistroParam = idRegistro });
 | 
						|
        }
 | 
						|
        public async Task<RegTirada?> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, IDbTransaction? transaction = null)
 | 
						|
        {
 | 
						|
            const string sql = "SELECT Id_Registro AS IdRegistro, Ejemplares, Id_Publicacion AS IdPublicacion, Fecha, Id_Planta AS IdPlanta FROM dbo.bob_RegTiradas WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam";
 | 
						|
            var cn = transaction?.Connection ?? _cf.CreateConnection();
 | 
						|
            bool ownConnection = transaction == null;
 | 
						|
            RegTirada? result = null;
 | 
						|
            try
 | 
						|
            {
 | 
						|
                if (ownConnection && cn.State == ConnectionState.Closed && cn is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync();
 | 
						|
                result = await cn.QuerySingleOrDefaultAsync<RegTirada>(sql, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, 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<IEnumerable<RegTirada>> GetByCriteriaAsync(DateTime? fecha, int? idPublicacion, int? idPlanta)
 | 
						|
        {
 | 
						|
            var sqlBuilder = new StringBuilder("SELECT Id_Registro AS IdRegistro, Ejemplares, Id_Publicacion AS IdPublicacion, Fecha, Id_Planta AS IdPlanta FROM dbo.bob_RegTiradas WHERE 1=1 ");
 | 
						|
            var parameters = new DynamicParameters();
 | 
						|
            if (fecha.HasValue) { sqlBuilder.Append("AND Fecha = @FechaParam "); parameters.Add("FechaParam", fecha.Value.Date); }
 | 
						|
            if (idPublicacion.HasValue) { sqlBuilder.Append("AND Id_Publicacion = @IdPublicacionParam "); parameters.Add("IdPublicacionParam", idPublicacion.Value); }
 | 
						|
            if (idPlanta.HasValue) { sqlBuilder.Append("AND Id_Planta = @IdPlantaParam "); parameters.Add("IdPlantaParam", idPlanta.Value); }
 | 
						|
            sqlBuilder.Append("ORDER BY Fecha DESC, Id_Publicacion;");
 | 
						|
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            return await connection.QueryAsync<RegTirada>(sqlBuilder.ToString(), parameters);
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<RegTirada?> CreateAsync(RegTirada nuevaTirada, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            const string sqlInsert = @"
 | 
						|
                INSERT INTO dbo.bob_RegTiradas (Ejemplares, Id_Publicacion, Fecha, Id_Planta)
 | 
						|
                OUTPUT INSERTED.Id_Registro AS IdRegistro, INSERTED.Ejemplares, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Fecha, INSERTED.Id_Planta AS IdPlanta
 | 
						|
                VALUES (@Ejemplares, @IdPublicacion, @Fecha, @IdPlanta);";
 | 
						|
            var inserted = await transaction.Connection!.QuerySingleAsync<RegTirada>(sqlInsert, nuevaTirada, transaction);
 | 
						|
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                IdRegistroParam = inserted.IdRegistro,
 | 
						|
                EjemplaresParam = inserted.Ejemplares,
 | 
						|
                IdPublicacionParam = inserted.IdPublicacion,
 | 
						|
                FechaParam = inserted.Fecha,
 | 
						|
                IdPlantaParam = inserted.IdPlanta,
 | 
						|
                IdUsuarioParam = idUsuario,
 | 
						|
                FechaModParam = DateTime.Now,
 | 
						|
                TipoModParam = "Creado"
 | 
						|
            }, transaction);
 | 
						|
            return inserted;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<bool> UpdateAsync(RegTirada tiradaAActualizar, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            // 1. Obtener el estado actual para guardarlo en el historial
 | 
						|
            const string sqlSelectActual = "SELECT * FROM dbo.bob_RegTiradas WHERE Id_Registro = @IdRegistro";
 | 
						|
            var estadoActual = await transaction.Connection!.QuerySingleOrDefaultAsync<RegTirada>(sqlSelectActual, new { IdRegistro = tiradaAActualizar.IdRegistro }, transaction);
 | 
						|
 | 
						|
            if (estadoActual == null)
 | 
						|
            {
 | 
						|
                throw new KeyNotFoundException("No se encontró el registro de tirada a actualizar para generar el historial.");
 | 
						|
            }
 | 
						|
 | 
						|
            // 2. Guardar el estado PREVIO en el historial
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                IdRegistroParam = estadoActual.IdRegistro,
 | 
						|
                EjemplaresParam = estadoActual.Ejemplares,
 | 
						|
                IdPublicacionParam = estadoActual.IdPublicacion,
 | 
						|
                FechaParam = estadoActual.Fecha,
 | 
						|
                IdPlantaParam = estadoActual.IdPlanta,
 | 
						|
                IdUsuarioParam = idUsuario,
 | 
						|
                FechaModParam = DateTime.Now,
 | 
						|
                TipoModParam = "Modificado"
 | 
						|
            }, transaction);
 | 
						|
 | 
						|
            // 3. Actualizar el registro principal
 | 
						|
            const string sqlUpdate = @"
 | 
						|
                UPDATE dbo.bob_RegTiradas 
 | 
						|
                SET Ejemplares = @Ejemplares
 | 
						|
                WHERE Id_Registro = @IdRegistro;";
 | 
						|
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, tiradaAActualizar, transaction);
 | 
						|
            return rowsAffected == 1;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<bool> DeleteAsync(int idRegistro, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            var actual = await GetByIdAsync(idRegistro); // No necesita TX aquí ya que es solo para historial
 | 
						|
            if (actual == null) throw new KeyNotFoundException("Registro de tirada no encontrado.");
 | 
						|
 | 
						|
            const string sqlDelete = "DELETE FROM dbo.bob_RegTiradas WHERE Id_Registro = @IdRegistroParam";
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                IdRegistroParam = actual.IdRegistro,
 | 
						|
                EjemplaresParam = actual.Ejemplares,
 | 
						|
                IdPublicacionParam = actual.IdPublicacion,
 | 
						|
                FechaParam = actual.Fecha,
 | 
						|
                IdPlantaParam = actual.IdPlanta,
 | 
						|
                IdUsuarioParam = idUsuario,
 | 
						|
                FechaModParam = DateTime.Now,
 | 
						|
                TipoModParam = "Eliminado"
 | 
						|
            }, transaction);
 | 
						|
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdRegistroParam = idRegistro }, transaction);
 | 
						|
            return rowsAffected == 1;
 | 
						|
        }
 | 
						|
        public async Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            var tiradasAEliminar = await GetByCriteriaAsync(fecha.Date, idPublicacion, idPlanta); // Obtener antes de borrar para historial
 | 
						|
 | 
						|
            foreach (var actual in tiradasAEliminar)
 | 
						|
            {
 | 
						|
                const string sqlHistorico = @"INSERT INTO dbo.bob_RegTiradas_H (Id_Registro, Ejemplares, Id_Publicacion, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdRegistroParam, @EjemplaresParam, @IdPublicacionParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
                await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
                {
 | 
						|
                    IdRegistroParam = actual.IdRegistro,
 | 
						|
                    EjemplaresParam = actual.Ejemplares,
 | 
						|
                    IdPublicacionParam = actual.IdPublicacion,
 | 
						|
                    FechaParam = actual.Fecha,
 | 
						|
                    IdPlantaParam = actual.IdPlanta,
 | 
						|
                    IdUsuarioParam = idUsuario,
 | 
						|
                    FechaModParam = DateTime.Now,
 | 
						|
                    TipoModParam = "Eliminado (Por Fecha/Pub/Planta)"
 | 
						|
                }, transaction);
 | 
						|
            }
 | 
						|
 | 
						|
            const string sqlDelete = "DELETE FROM dbo.bob_RegTiradas WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam";
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete,
 | 
						|
                new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction);
 | 
						|
            return rowsAffected > 0; // Devuelve true si se borró al menos una fila
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<IEnumerable<(RegTiradaHistorico Historial, string NombreUsuarioModifico, string NombrePublicacion, string NombrePlanta)>> GetRegTiradasHistorialAsync(
 | 
						|
            DateTime? fechaDesde, DateTime? fechaHasta,
 | 
						|
            int? idUsuarioModifico, string? tipoModificacion,
 | 
						|
            int? idRegistroOriginal, int? idPublicacionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro)
 | 
						|
        {
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            var sqlBuilder = new StringBuilder(@"
 | 
						|
                SELECT 
 | 
						|
                    h.Id_Registro, h.Ejemplares, h.Id_Publicacion, h.Fecha, h.Id_Planta,
 | 
						|
                    h.Id_Usuario, h.FechaMod, h.TipoMod,
 | 
						|
                    u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico,
 | 
						|
                    pub.Nombre AS NombrePublicacion,
 | 
						|
                    p.Nombre AS NombrePlanta
 | 
						|
                FROM dbo.bob_RegTiradas_H h
 | 
						|
                JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
 | 
						|
                JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion
 | 
						|
                JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta
 | 
						|
                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 (idRegistroOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Registro = @IdRegistroOriginalParam"); parameters.Add("IdRegistroOriginalParam", idRegistroOriginal.Value); }
 | 
						|
            if (idPublicacionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionFiltroParam"); parameters.Add("IdPublicacionFiltroParam", idPublicacionFiltro.Value); }
 | 
						|
            if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); }
 | 
						|
            if (fechaTiradaFiltro.HasValue) { sqlBuilder.Append(" AND h.Fecha = @FechaTiradaFiltroParam"); parameters.Add("FechaTiradaFiltroParam", fechaTiradaFiltro.Value.Date); }
 | 
						|
 | 
						|
            sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var result = await connection.QueryAsync<RegTiradaHistorico, string, string, string, (RegTiradaHistorico, string, string, string)>(
 | 
						|
                    sqlBuilder.ToString(),
 | 
						|
                    (hist, userMod, pubNombre, plantaNombre) => (hist, userMod, pubNombre, plantaNombre),
 | 
						|
                    parameters,
 | 
						|
                    splitOn: "NombreUsuarioModifico,NombrePublicacion,NombrePlanta"
 | 
						|
                );
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _log.LogError(ex, "Error al obtener historial de Registro de Tiradas.");
 | 
						|
                return Enumerable.Empty<(RegTiradaHistorico, string, string, string)>();
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<IEnumerable<(RegPublicacionSeccionHistorico Historial, string NombreUsuarioModifico, string NombrePublicacion, string NombreSeccion, string NombrePlanta)>> GetRegSeccionesTiradaHistorialAsync(
 | 
						|
        DateTime? fechaDesde, DateTime? fechaHasta,
 | 
						|
        int? idUsuarioModifico, string? tipoModificacion,
 | 
						|
        int? idTiradaOriginal, int? idPublicacionFiltro, int? idSeccionFiltro, int? idPlantaFiltro, DateTime? fechaTiradaFiltro)
 | 
						|
        {
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            var sqlBuilder = new StringBuilder(@"
 | 
						|
            SELECT 
 | 
						|
                h.Id_Tirada, h.Id_Publicacion, h.Id_Seccion, h.CantPag, h.Fecha, h.Id_Planta,
 | 
						|
                h.Id_Usuario, h.FechaMod, h.TipoMod,
 | 
						|
                u.Nombre + ' ' + u.Apellido AS NombreUsuarioModifico,
 | 
						|
                pub.Nombre AS NombrePublicacion,
 | 
						|
                sec.Nombre AS NombreSeccion,
 | 
						|
                p.Nombre AS NombrePlanta
 | 
						|
            FROM dbo.bob_RegPublicaciones_H h
 | 
						|
            JOIN dbo.gral_Usuarios u ON h.Id_Usuario = u.Id
 | 
						|
            JOIN dbo.dist_dtPublicaciones pub ON h.Id_Publicacion = pub.Id_Publicacion
 | 
						|
            JOIN dbo.dist_dtPubliSecciones sec ON h.Id_Seccion = sec.Id_Seccion 
 | 
						|
                AND h.Id_Publicacion = sec.Id_Publicacion -- Crucial para el JOIN correcto de sección
 | 
						|
            JOIN dbo.bob_dtPlantas p ON h.Id_Planta = p.Id_Planta
 | 
						|
            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 (idTiradaOriginal.HasValue) { sqlBuilder.Append(" AND h.Id_Tirada = @IdTiradaOriginalParam"); parameters.Add("IdTiradaOriginalParam", idTiradaOriginal.Value); }
 | 
						|
            if (idPublicacionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Publicacion = @IdPublicacionFiltroParam"); parameters.Add("IdPublicacionFiltroParam", idPublicacionFiltro.Value); }
 | 
						|
            if (idSeccionFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Seccion = @IdSeccionFiltroParam"); parameters.Add("IdSeccionFiltroParam", idSeccionFiltro.Value); }
 | 
						|
            if (idPlantaFiltro.HasValue) { sqlBuilder.Append(" AND h.Id_Planta = @IdPlantaFiltroParam"); parameters.Add("IdPlantaFiltroParam", idPlantaFiltro.Value); }
 | 
						|
            if (fechaTiradaFiltro.HasValue) { sqlBuilder.Append(" AND h.Fecha = @FechaTiradaFiltroParam"); parameters.Add("FechaTiradaFiltroParam", fechaTiradaFiltro.Value.Date); }
 | 
						|
 | 
						|
            sqlBuilder.Append(" ORDER BY h.FechaMod DESC;");
 | 
						|
 | 
						|
            try
 | 
						|
            {
 | 
						|
                var result = await connection.QueryAsync<RegPublicacionSeccionHistorico, string, string, string, string, (RegPublicacionSeccionHistorico, string, string, string, string)>(
 | 
						|
                    sqlBuilder.ToString(),
 | 
						|
                    (hist, userMod, pubNombre, secNombre, plantaNombre) => (hist, userMod, pubNombre, secNombre, plantaNombre),
 | 
						|
                    parameters,
 | 
						|
                    splitOn: "NombreUsuarioModifico,NombrePublicacion,NombreSeccion,NombrePlanta"
 | 
						|
                );
 | 
						|
                return result;
 | 
						|
            }
 | 
						|
            catch (Exception ex)
 | 
						|
            {
 | 
						|
                _log.LogError(ex, "Error al obtener historial de Secciones de Tirada.");
 | 
						|
                return Enumerable.Empty<(RegPublicacionSeccionHistorico, string, string, string, string)>();
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    public class RegPublicacionSeccionRepository : IRegPublicacionSeccionRepository
 | 
						|
    {
 | 
						|
        private readonly DbConnectionFactory _cf;
 | 
						|
        private readonly ILogger<RegPublicacionSeccionRepository> _log;
 | 
						|
        public RegPublicacionSeccionRepository(DbConnectionFactory cf, ILogger<RegPublicacionSeccionRepository> log) { _cf = cf; _log = log; }
 | 
						|
 | 
						|
        public async Task<IEnumerable<RegPublicacionSeccion>> GetByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta)
 | 
						|
        {
 | 
						|
            const string sql = @"SELECT Id_Tirada AS IdTirada, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, CantPag, Fecha, Id_Planta AS IdPlanta 
 | 
						|
                                 FROM dbo.bob_RegPublicaciones 
 | 
						|
                                 WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam";
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            return await connection.QueryAsync<RegPublicacionSeccion>(sql, new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta });
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<RegPublicacionSeccion?> CreateAsync(RegPublicacionSeccion nuevaSeccionTirada, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            const string sqlInsert = @"
 | 
						|
                INSERT INTO dbo.bob_RegPublicaciones (Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta)
 | 
						|
                OUTPUT INSERTED.Id_Tirada AS IdTirada, INSERTED.Id_Publicacion AS IdPublicacion, INSERTED.Id_Seccion AS IdSeccion, INSERTED.CantPag, INSERTED.Fecha, INSERTED.Id_Planta AS IdPlanta
 | 
						|
                VALUES (@IdPublicacion, @IdSeccion, @CantPag, @Fecha, @IdPlanta);";
 | 
						|
            var inserted = await transaction.Connection!.QuerySingleAsync<RegPublicacionSeccion>(sqlInsert, nuevaSeccionTirada, transaction);
 | 
						|
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdTiradaParam, @IdPublicacionParam, @IdSeccionParam, @CantPagParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                IdTiradaParam = inserted.IdTirada,
 | 
						|
                IdPublicacionParam = inserted.IdPublicacion,
 | 
						|
                IdSeccionParam = inserted.IdSeccion,
 | 
						|
                CantPagParam = inserted.CantPag,
 | 
						|
                FechaParam = inserted.Fecha,
 | 
						|
                IdPlantaParam = inserted.IdPlanta,
 | 
						|
                IdUsuarioParam = idUsuario,
 | 
						|
                FechaModParam = DateTime.Now,
 | 
						|
                TipoModParam = "Creado"
 | 
						|
            }, transaction);
 | 
						|
            return inserted;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<RegPublicacionSeccion?> GetByIdAsync(int idTirada)
 | 
						|
        {
 | 
						|
            const string sql = @"SELECT Id_Tirada AS IdTirada, Id_Publicacion AS IdPublicacion, Id_Seccion AS IdSeccion, CantPag, Fecha, Id_Planta AS IdPlanta 
 | 
						|
                             FROM dbo.bob_RegPublicaciones WHERE Id_Tirada = @IdTiradaParam";
 | 
						|
            using var connection = _cf.CreateConnection();
 | 
						|
            return await connection.QuerySingleOrDefaultAsync<RegPublicacionSeccion>(sql, new { IdTiradaParam = idTirada });
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<bool> UpdateAsync(RegPublicacionSeccion seccionAActualizar, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            // Obtener estado PREVIO para historial
 | 
						|
            var actual = await GetByIdAsync(seccionAActualizar.IdTirada);
 | 
						|
            if (actual == null) throw new KeyNotFoundException("No se encontró la sección de tirada a actualizar.");
 | 
						|
 | 
						|
            // Insertar en historial con tipo "Modificado"
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                      VALUES (@IdTirada, @IdPublicacion, @IdSeccion, @CantPag, @Fecha, @IdPlanta, @IdUsuario, GETDATE(), 'Modificado');";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                actual.IdTirada,
 | 
						|
                actual.IdPublicacion,
 | 
						|
                actual.IdSeccion,
 | 
						|
                actual.CantPag,
 | 
						|
                actual.Fecha,
 | 
						|
                actual.IdPlanta,
 | 
						|
                IdUsuario = idUsuario
 | 
						|
            }, transaction);
 | 
						|
 | 
						|
            // Actualizar el registro
 | 
						|
            const string sqlUpdate = "UPDATE dbo.bob_RegPublicaciones SET CantPag = @CantPag WHERE Id_Tirada = @IdTirada";
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlUpdate, seccionAActualizar, transaction);
 | 
						|
            return rowsAffected == 1;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<bool> DeleteByIdAsync(int idTirada, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            // Obtener estado PREVIO para historial
 | 
						|
            var actual = await GetByIdAsync(idTirada);
 | 
						|
            if (actual == null) return false; // Ya no existe, no hacemos nada.
 | 
						|
 | 
						|
            // Insertar en historial con tipo "Eliminado"
 | 
						|
            const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                      VALUES (@IdTirada, @IdPublicacion, @IdSeccion, @CantPag, @Fecha, @IdPlanta, @IdUsuario, GETDATE(), 'Eliminado');";
 | 
						|
            await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
            {
 | 
						|
                actual.IdTirada,
 | 
						|
                actual.IdPublicacion,
 | 
						|
                actual.IdSeccion,
 | 
						|
                actual.CantPag,
 | 
						|
                actual.Fecha,
 | 
						|
                actual.IdPlanta,
 | 
						|
                IdUsuario = idUsuario
 | 
						|
            }, transaction);
 | 
						|
 | 
						|
            // Eliminar el registro
 | 
						|
            const string sqlDelete = "DELETE FROM dbo.bob_RegPublicaciones WHERE Id_Tirada = @IdTirada";
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete, new { IdTirada = idTirada }, transaction);
 | 
						|
            return rowsAffected == 1;
 | 
						|
        }
 | 
						|
 | 
						|
        public async Task<bool> DeleteByFechaPublicacionPlantaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario, IDbTransaction transaction)
 | 
						|
        {
 | 
						|
            var seccionesAEliminar = await GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); // No necesita TX, es SELECT
 | 
						|
 | 
						|
            foreach (var actual in seccionesAEliminar)
 | 
						|
            {
 | 
						|
                const string sqlHistorico = @"INSERT INTO dbo.bob_RegPublicaciones_H (Id_Tirada, Id_Publicacion, Id_Seccion, CantPag, Fecha, Id_Planta, Id_Usuario, FechaMod, TipoMod)
 | 
						|
                                          VALUES (@IdTiradaParam, @IdPublicacionParam, @IdSeccionParam, @CantPagParam, @FechaParam, @IdPlantaParam, @IdUsuarioParam, @FechaModParam, @TipoModParam);";
 | 
						|
                await transaction.Connection!.ExecuteAsync(sqlHistorico, new
 | 
						|
                {
 | 
						|
                    IdTiradaParam = actual.IdTirada,
 | 
						|
                    IdPublicacionParam = actual.IdPublicacion,
 | 
						|
                    IdSeccionParam = actual.IdSeccion,
 | 
						|
                    CantPagParam = actual.CantPag,
 | 
						|
                    FechaParam = actual.Fecha,
 | 
						|
                    IdPlantaParam = actual.IdPlanta,
 | 
						|
                    IdUsuarioParam = idUsuario,
 | 
						|
                    FechaModParam = DateTime.Now,
 | 
						|
                    TipoModParam = "Eliminado (Por Fecha/Pub/Planta)"
 | 
						|
                }, transaction);
 | 
						|
            }
 | 
						|
 | 
						|
            const string sqlDelete = "DELETE FROM dbo.bob_RegPublicaciones WHERE Fecha = @FechaParam AND Id_Publicacion = @IdPublicacionParam AND Id_Planta = @IdPlantaParam";
 | 
						|
            var rowsAffected = await transaction.Connection!.ExecuteAsync(sqlDelete,
 | 
						|
                new { FechaParam = fecha.Date, IdPublicacionParam = idPublicacion, IdPlantaParam = idPlanta }, transaction);
 | 
						|
            return rowsAffected >= 0; // Devuelve true incluso si no había nada que borrar (para que no falle la TX si es parte de una cadena)
 | 
						|
        }
 | 
						|
    }
 | 
						|
} |