Feat: Implementar modificación de Tiradas y mejorar UX/UI
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				Optimized Build and Deploy / remote-build-and-deploy (push) Successful in 12m54s
				
			
		
		
	
	
				
					
				
			
		
			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.
This commit is contained in:
		| @@ -10,6 +10,7 @@ namespace GestionIntegral.Api.Services.Impresion | ||||
|     { | ||||
|         Task<IEnumerable<TiradaDto>> ObtenerTiradasAsync(DateTime? fecha, int? idPublicacion, int? idPlanta); | ||||
|         Task<(TiradaDto? TiradaCreada, string? Error)> RegistrarTiradaCompletaAsync(CreateTiradaRequestDto createDto, int idUsuario); | ||||
|         Task<(TiradaDto? TiradaActualizada, string? Error)> ModificarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, UpdateTiradaRequestDto updateDto, int idUsuario); // <-- AÑADIDO | ||||
|         Task<(bool Exito, string? Error)> EliminarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario); | ||||
|         Task<IEnumerable<RegTiradaHistorialDto>> ObtenerRegTiradasHistorialAsync( | ||||
|         DateTime? fechaDesde, DateTime? fechaHasta, | ||||
|   | ||||
| @@ -182,6 +182,104 @@ namespace GestionIntegral.Api.Services.Impresion | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public async Task<(TiradaDto? TiradaActualizada, string? Error)> ModificarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, UpdateTiradaRequestDto updateDto, int idUsuario) | ||||
|         { | ||||
|             // 1. Validar que la tirada a modificar exista. | ||||
|             var tiradaExistente = await _regTiradaRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); | ||||
|             if (tiradaExistente == null) | ||||
|             { | ||||
|                 return (null, "No se encontró la tirada que intenta modificar."); | ||||
|             } | ||||
|  | ||||
|             // 2. Validaciones de DTO (secciones válidas, etc.) | ||||
|             var idsSeccionesUnicasDto = updateDto.Secciones.Select(s => s.IdSeccion).Distinct(); | ||||
|             if (idsSeccionesUnicasDto.Any()) // Solo validar si se enviaron secciones | ||||
|             { | ||||
|                 var seccionesValidasDb = await _publiSeccionRepository.GetByIdsAndPublicacionAsync(idsSeccionesUnicasDto, idPublicacion, soloActivas: true); | ||||
|  | ||||
|                 if (seccionesValidasDb.Count() != idsSeccionesUnicasDto.Count()) | ||||
|                 { | ||||
|                     var idsEncontrados = seccionesValidasDb.Select(s => s.IdSeccion).ToHashSet(); | ||||
|                     var idsFaltantes = string.Join(", ", idsSeccionesUnicasDto.Where(id => !idsEncontrados.Contains(id))); | ||||
|                     return (null, $"Las siguientes secciones no son válidas, no pertenecen a la publicación o no están activas: {idsFaltantes}."); | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             using var connection = _connectionFactory.CreateConnection(); | ||||
|             if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); | ||||
|             using var transaction = connection.BeginTransaction(); | ||||
|  | ||||
|             try | ||||
|             { | ||||
|                 // 3. Actualizar registro principal (bob_RegTiradas) si cambió el número de ejemplares | ||||
|                 if (tiradaExistente.Ejemplares != updateDto.Ejemplares) | ||||
|                 { | ||||
|                     tiradaExistente.Ejemplares = updateDto.Ejemplares; | ||||
|                     bool actualizado = await _regTiradaRepository.UpdateAsync(tiradaExistente, idUsuario, transaction); | ||||
|                     if (!actualizado) throw new DataException("No se pudo actualizar el registro principal de la tirada."); | ||||
|                 } | ||||
|  | ||||
|                 // 4. Lógica de reconciliación de secciones | ||||
|                 // 4.1. Obtener estado actual de las secciones en la BBDD | ||||
|                 var seccionesActualesDb = await _regPublicacionSeccionRepository.GetByFechaPublicacionPlantaAsync(fecha.Date, idPublicacion, idPlanta); | ||||
|  | ||||
|                 // 4.2. Procesar Adiciones y Actualizaciones | ||||
|                 foreach (var seccionDto in updateDto.Secciones) | ||||
|                 { | ||||
|                     if (seccionDto.IdRegPublicacionSeccion == 0) // Es una nueva sección | ||||
|                     { | ||||
|                         var nuevaRegPubSeccion = new RegPublicacionSeccion | ||||
|                         { | ||||
|                             IdPublicacion = idPublicacion, | ||||
|                             IdSeccion = seccionDto.IdSeccion, | ||||
|                             CantPag = seccionDto.CantPag, | ||||
|                             Fecha = fecha.Date, | ||||
|                             IdPlanta = idPlanta | ||||
|                         }; | ||||
|                         await _regPublicacionSeccionRepository.CreateAsync(nuevaRegPubSeccion, idUsuario, transaction); | ||||
|                     } | ||||
|                     else // Es una sección existente, verificar si hay cambios | ||||
|                     { | ||||
|                         var seccionDb = seccionesActualesDb.FirstOrDefault(s => s.IdTirada == seccionDto.IdRegPublicacionSeccion); | ||||
|                         if (seccionDb == null) throw new InvalidOperationException($"La sección con ID de registro {seccionDto.IdRegPublicacionSeccion} no pertenece a esta tirada."); | ||||
|  | ||||
|                         if (seccionDb.CantPag != seccionDto.CantPag) // Solo actualizamos si cambió la cantidad de páginas | ||||
|                         { | ||||
|                             seccionDb.CantPag = seccionDto.CantPag; | ||||
|                             await _regPublicacionSeccionRepository.UpdateAsync(seccionDb, idUsuario, transaction); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 // 4.3. Procesar Eliminaciones | ||||
|                 var idsSeccionesEntrantes = updateDto.Secciones | ||||
|                     .Where(s => s.IdRegPublicacionSeccion != 0) | ||||
|                     .Select(s => s.IdRegPublicacionSeccion) | ||||
|                     .ToHashSet(); | ||||
|  | ||||
|                 var seccionesAEliminar = seccionesActualesDb | ||||
|                     .Where(s => !idsSeccionesEntrantes.Contains(s.IdTirada)); | ||||
|  | ||||
|                 foreach (var seccionParaBorrar in seccionesAEliminar) | ||||
|                 { | ||||
|                     await _regPublicacionSeccionRepository.DeleteByIdAsync(seccionParaBorrar.IdTirada, idUsuario, transaction); | ||||
|                 } | ||||
|  | ||||
|                 transaction.Commit(); | ||||
|                 _logger.LogInformation("Tirada completa modificada (granular) para Pub ID {IdPub}, Fecha {Fecha}, por Usuario ID {UserId}.", idPublicacion, fecha.Date, idUsuario); | ||||
|  | ||||
|                 // 5. Devolver el DTO actualizado (tendríamos que volver a consultar o construirlo) | ||||
|                 var tiradaActualizadaResult = await ObtenerTiradasAsync(fecha.Date, idPublicacion, idPlanta); | ||||
|                 return (tiradaActualizadaResult.FirstOrDefault(), null); | ||||
|             } | ||||
|             catch (Exception ex) | ||||
|             { | ||||
|                 try { transaction.Rollback(); } catch { } | ||||
|                 _logger.LogError(ex, "Error en ModificarTiradaCompletaAsync (granular) para Pub ID {IdPub}, Fecha {Fecha}", idPublicacion, fecha); | ||||
|                 return (null, $"Error interno al modificar la tirada: {ex.Message}"); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public async Task<(bool Exito, string? Error)> EliminarTiradaCompletaAsync(DateTime fecha, int idPublicacion, int idPlanta, int idUsuario) | ||||
|         { | ||||
|             // Verificar que la tirada principal exista | ||||
|   | ||||
		Reference in New Issue
	
	Block a user