Files
GestionIntegralWeb/Backend/GestionIntegral.Api/Services/Distribucion/PublicacionService.cs

313 lines
16 KiB
C#
Raw Normal View History

feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación Backend API: - Canillitas (`dist_dtCanillas`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`. - Auditoría en `dist_dtCanillas_H`. - Validación de legajo único y lógica de empresa vs accionista. - Distribuidores (`dist_dtDistribuidores`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Auditoría en `dist_dtDistribuidores_H`. - Creación de saldos iniciales para el nuevo distribuidor en todas las empresas. - Verificación de NroDoc único y Nombre opcionalmente único. - Precios de Publicación (`dist_Precios`): - Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador). - Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`. - Lógica de negocio para cerrar período de precio anterior al crear uno nuevo. - Lógica de negocio para reabrir período de precio anterior al eliminar el último. - Auditoría en `dist_Precios_H`. - Auditoría en Eliminación de Publicaciones: - Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación. - Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica). - Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada. - Correcciones de Nulabilidad: - Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables. Frontend React: - Canillitas: - `canillaService.ts`. - `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista. - `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja). - Distribuidores: - `distribuidorService.ts`. - `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona. - `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar). - Precios de Publicación: - `precioService.ts`. - `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día). - `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica. - Layout: - Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios. - Navegación: - Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
// src/Services/Distribucion/PublicacionService.cs
using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Distribucion;
1. Funcionalidad Principal: Auditoría General Se creó una nueva sección de "Auditoría" en la aplicación, diseñada para ser accedida por SuperAdmins. Se implementó una página AuditoriaGeneralPage.tsx que actúa como un visor centralizado para el historial de cambios de múltiples entidades del sistema. 2. Backend: Nuevo Controlador (AuditoriaController.cs): Centraliza los endpoints para obtener datos de las tablas de historial (_H). Servicios y Repositorios Extendidos: Se añadieron métodos GetHistorialAsync y ObtenerHistorialAsync a las capas de repositorio y servicio para cada una de las siguientes entidades, permitiendo consultar sus tablas _H con filtros: 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) Tipos de Pago (cue_dtTipopago_H) Canillitas (Maestro) (dist_dtCanillas_H) Distribuidores (Maestro) (dist_dtDistribuidores_H) Empresas (Maestro) (dist_dtEmpresas_H) Zonas (Maestro) (dist_dtZonas_H) Otros Destinos (Maestro) (dist_dtOtrosDestinos_H) Publicaciones (Maestro) (dist_dtPublicaciones_H) Secciones de Publicación (dist_dtPubliSecciones_H) Precios de Publicación (dist_Precios_H) Recargos por Zona (dist_RecargoZona_H) Porcentajes Pago Distribuidores (dist_PorcPago_H) Porcentajes/Montos Canillita (dist_PorcMonPagoCanilla_H) Control de Devoluciones (dist_dtCtrlDevoluciones_H) Tipos de Bobina (bob_dtBobinas_H) Estados de Bobina (bob_dtEstadosBobinas_H) Plantas de Impresión (bob_dtPlantas_H) Stock de Bobinas (bob_StockBobinas_H) Tiradas (Registro Principal) (bob_RegTiradas_H) Secciones de Tirada (bob_RegPublicaciones_H) Cambios de Parada de Canillitas (dist_CambiosParadasCanillas_H) Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial) DTOs de Historial: Se crearon DTOs específicos para cada tabla de historial (ej. UsuarioHistorialDto, PagoDistribuidorHistorialDto, etc.) para transferir los datos al frontend, incluyendo el nombre del usuario que realizó la modificación. Corrección de Lógica de Saldos: Se revisó y corrigió la lógica de afectación de saldos en los servicios PagoDistribuidorService y NotaCreditoDebitoService para asegurar que los débitos y créditos se apliquen correctamente. 3. Frontend: Nuevo Servicio (auditoriaService.ts): Contiene métodos para llamar a cada uno de los nuevos endpoints de auditoría del backend. Nueva Página (AuditoriaGeneralPage.tsx): Permite al SuperAdmin seleccionar el "Tipo de Entidad" a auditar desde un dropdown. Ofrece filtros comunes (rango de fechas, usuario modificador, tipo de acción) y filtros específicos que aparecen dinámicamente según la entidad seleccionada. Utiliza un DataGrid de Material-UI para mostrar el historial, con columnas que se adaptan dinámicamente al tipo de entidad consultada. Nuevos DTOs en TypeScript: Se crearon las interfaces correspondientes a los DTOs de historial del backend. Gestión de Permisos: La sección de Auditoría en MainLayout.tsx y su ruta en AppRoutes.tsx están protegidas para ser visibles y accesibles solo por SuperAdmins. Se añadió un permiso de ejemplo AU_GENERAL_VIEW para ser usado si se decide extender el acceso en el futuro. Corrección de Errores Menores: Se solucionó el problema del "parpadeo" del selector de fecha en GestionarNovedadesCanillaPage al adoptar un patrón de carga de datos más controlado, similar a otras páginas funcionales.
2025-06-12 19:36:21 -03:00
using GestionIntegral.Api.Dtos.Auditoria;
feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación Backend API: - Canillitas (`dist_dtCanillas`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`. - Auditoría en `dist_dtCanillas_H`. - Validación de legajo único y lógica de empresa vs accionista. - Distribuidores (`dist_dtDistribuidores`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Auditoría en `dist_dtDistribuidores_H`. - Creación de saldos iniciales para el nuevo distribuidor en todas las empresas. - Verificación de NroDoc único y Nombre opcionalmente único. - Precios de Publicación (`dist_Precios`): - Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador). - Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`. - Lógica de negocio para cerrar período de precio anterior al crear uno nuevo. - Lógica de negocio para reabrir período de precio anterior al eliminar el último. - Auditoría en `dist_Precios_H`. - Auditoría en Eliminación de Publicaciones: - Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación. - Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica). - Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada. - Correcciones de Nulabilidad: - Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables. Frontend React: - Canillitas: - `canillaService.ts`. - `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista. - `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja). - Distribuidores: - `distribuidorService.ts`. - `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona. - `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar). - Precios de Publicación: - `precioService.ts`. - `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día). - `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica. - Layout: - Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios. - Navegación: - Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
using GestionIntegral.Api.Dtos.Distribucion;
using GestionIntegral.Api.Models.Distribucion;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
namespace GestionIntegral.Api.Services.Distribucion
{
public class PublicacionService : IPublicacionService
{
private readonly IPublicacionRepository _publicacionRepository;
private readonly IEmpresaRepository _empresaRepository;
private readonly DbConnectionFactory _connectionFactory;
private readonly ILogger<PublicacionService> _logger;
private readonly IPrecioRepository _precioRepository;
private readonly IRecargoZonaRepository _recargoZonaRepository;
private readonly IPorcPagoRepository _porcPagoRepository;
private readonly IPorcMonCanillaRepository _porcMonCanillaRepository;
private readonly IPubliSeccionRepository _publiSeccionRepository;
public PublicacionService(
IPublicacionRepository publicacionRepository,
IEmpresaRepository empresaRepository,
DbConnectionFactory connectionFactory,
ILogger<PublicacionService> logger,
IPrecioRepository precioRepository,
IRecargoZonaRepository recargoZonaRepository,
IPorcPagoRepository porcPagoRepository,
IPorcMonCanillaRepository porcMonCanillaRepository,
IPubliSeccionRepository publiSeccionRepository)
{
_publicacionRepository = publicacionRepository;
_empresaRepository = empresaRepository;
_connectionFactory = connectionFactory;
_logger = logger;
_precioRepository = precioRepository;
_recargoZonaRepository = recargoZonaRepository;
_porcPagoRepository = porcPagoRepository;
_porcMonCanillaRepository = porcMonCanillaRepository;
_publiSeccionRepository = publiSeccionRepository;
}
private PublicacionDto? MapToDto((Publicacion? Publicacion, string? NombreEmpresa) data)
{
if (data.Publicacion == null) return null; // Si la publicación es null, no se puede mapear
return new PublicacionDto
{
IdPublicacion = data.Publicacion.IdPublicacion,
Nombre = data.Publicacion.Nombre,
Observacion = data.Publicacion.Observacion,
IdEmpresa = data.Publicacion.IdEmpresa,
NombreEmpresa = data.NombreEmpresa ?? "Empresa Desconocida", // Manejar null para NombreEmpresa
CtrlDevoluciones = data.Publicacion.CtrlDevoluciones,
Habilitada = data.Publicacion.Habilitada ?? true // Asumir true si es null desde BD
};
}
public async Task<IEnumerable<PublicacionDto>> ObtenerTodasAsync(string? nombreFilter, int? idEmpresaFilter, bool? soloHabilitadas)
{
var data = await _publicacionRepository.GetAllAsync(nombreFilter, idEmpresaFilter, soloHabilitadas);
// Filtrar los nulos que MapToDto podría devolver (si alguna tupla tuviera Publicacion null)
return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
}
public async Task<PublicacionDto?> ObtenerPorIdAsync(int id)
{
var data = await _publicacionRepository.GetByIdAsync(id);
// MapToDto ahora devuelve PublicacionDto?
return MapToDto(data);
}
public async Task<IEnumerable<PublicacionDropdownDto>> ObtenerParaDropdownAsync(bool soloHabilitadas = true)
{
var data = await _publicacionRepository.GetAllAsync(null, null, soloHabilitadas ? (bool?)true : null);
return data
.Where(p => p.Publicacion != null) // Asegurar que la publicación no sea null
.Select(d => new PublicacionDropdownDto
{
IdPublicacion = d.Publicacion!.IdPublicacion, // Usar ! si estás seguro que no es null después del Where
Nombre = d.Publicacion!.Nombre,
NombreEmpresa = d.NombreEmpresa ?? "Empresa Desconocida",
Habilitada = d.Publicacion!.Habilitada ?? true // Si necesitas filtrar por esto
})
.OrderBy(p => p.Nombre)
.ToList(); // O ToListAsync si el método del repo es async y devuelve IQueryable
}
feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación Backend API: - Canillitas (`dist_dtCanillas`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`. - Auditoría en `dist_dtCanillas_H`. - Validación de legajo único y lógica de empresa vs accionista. - Distribuidores (`dist_dtDistribuidores`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Auditoría en `dist_dtDistribuidores_H`. - Creación de saldos iniciales para el nuevo distribuidor en todas las empresas. - Verificación de NroDoc único y Nombre opcionalmente único. - Precios de Publicación (`dist_Precios`): - Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador). - Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`. - Lógica de negocio para cerrar período de precio anterior al crear uno nuevo. - Lógica de negocio para reabrir período de precio anterior al eliminar el último. - Auditoría en `dist_Precios_H`. - Auditoría en Eliminación de Publicaciones: - Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación. - Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica). - Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada. - Correcciones de Nulabilidad: - Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables. Frontend React: - Canillitas: - `canillaService.ts`. - `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista. - `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja). - Distribuidores: - `distribuidorService.ts`. - `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona. - `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar). - Precios de Publicación: - `precioService.ts`. - `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día). - `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica. - Layout: - Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios. - Navegación: - Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
public async Task<(PublicacionDto? Publicacion, string? Error)> CrearAsync(CreatePublicacionDto createDto, int idUsuario)
{
if (await _empresaRepository.GetByIdAsync(createDto.IdEmpresa) == null)
return (null, "La empresa seleccionada no es válida.");
if (await _publicacionRepository.ExistsByNameAndEmpresaAsync(createDto.Nombre, createDto.IdEmpresa))
return (null, "Ya existe una publicación con ese nombre para la empresa seleccionada.");
var nuevaPublicacion = new Publicacion
{
Nombre = createDto.Nombre,
Observacion = createDto.Observacion,
IdEmpresa = createDto.IdEmpresa,
CtrlDevoluciones = createDto.CtrlDevoluciones,
Habilitada = createDto.Habilitada
};
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
{
var creada = await _publicacionRepository.CreateAsync(nuevaPublicacion, idUsuario, transaction);
if (creada == null) throw new DataException("Error al crear publicación.");
transaction.Commit();
var dataCompleta = await _publicacionRepository.GetByIdAsync(creada.IdPublicacion); // Para obtener NombreEmpresa
_logger.LogInformation("Publicación ID {Id} creada por Usuario ID {UserId}.", creada.IdPublicacion, idUsuario);
// MapToDto ahora devuelve PublicacionDto?
return (MapToDto(dataCompleta), null);
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error CrearAsync Publicacion: {Nombre}", createDto.Nombre);
return (null, $"Error interno: {ex.Message}");
}
}
public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdatePublicacionDto updateDto, int idUsuario)
{
var existente = await _publicacionRepository.GetByIdSimpleAsync(id);
if (existente == null) return (false, "Publicación no encontrada.");
if (await _empresaRepository.GetByIdAsync(updateDto.IdEmpresa) == null)
return (false, "La empresa seleccionada no es válida.");
if (await _publicacionRepository.ExistsByNameAndEmpresaAsync(updateDto.Nombre, updateDto.IdEmpresa, id))
return (false, "Ya existe otra publicación con ese nombre para la empresa seleccionada.");
existente.Nombre = updateDto.Nombre; existente.Observacion = updateDto.Observacion;
existente.IdEmpresa = updateDto.IdEmpresa; existente.CtrlDevoluciones = updateDto.CtrlDevoluciones;
existente.Habilitada = updateDto.Habilitada;
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
{
var actualizado = await _publicacionRepository.UpdateAsync(existente, idUsuario, transaction);
if (!actualizado) throw new DataException("Error al actualizar.");
transaction.Commit();
_logger.LogInformation("Publicación ID {Id} actualizada por Usuario ID {UserId}.", id, idUsuario);
return (true, null);
}
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Publicación no encontrada."); }
catch (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error ActualizarAsync Publicacion ID: {Id}", id);
return (false, $"Error interno: {ex.Message}");
}
}
public async Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario) // idUsuario es el que realiza la acción
{
var existente = await _publicacionRepository.GetByIdSimpleAsync(id);
if (existente == null) return (false, "Publicación no encontrada.");
if (await _publicacionRepository.IsInUseAsync(id))
{
return (false, "No se puede eliminar. La publicación tiene datos transaccionales o de configuración relacionados.");
}
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
{
_logger.LogInformation("Iniciando eliminación de dependencias para Publicación ID: {IdPublicacion} por Usuario ID: {idUsuario}", id, idUsuario);
// Pasar idUsuario a los métodos de borrado para auditoría
await _precioRepository.DeleteByPublicacionIdAsync(id, idUsuario, transaction);
_logger.LogDebug("Precios eliminados para Publicación ID: {IdPublicacion}", id);
await _recargoZonaRepository.DeleteByPublicacionIdAsync(id, idUsuario, transaction);
_logger.LogDebug("RecargosZona eliminados para Publicación ID: {IdPublicacion}", id);
await _porcPagoRepository.DeleteByPublicacionIdAsync(id, idUsuario, transaction);
_logger.LogDebug("PorcPago eliminados para Publicación ID: {IdPublicacion}", id);
await _porcMonCanillaRepository.DeleteByPublicacionIdAsync(id, idUsuario, transaction);
_logger.LogDebug("PorcMonCanilla eliminados para Publicación ID: {IdPublicacion}", id);
await _publiSeccionRepository.DeleteByPublicacionIdAsync(id, idUsuario, transaction);
_logger.LogDebug("PubliSecciones eliminadas para Publicación ID: {IdPublicacion}", id);
var eliminado = await _publicacionRepository.DeleteAsync(id, idUsuario, transaction);
if (!eliminado)
{
throw new DataException("Error al eliminar la publicación principal después de sus dependencias.");
}
transaction.Commit();
_logger.LogInformation("Publicación ID {Id} y sus dependencias eliminadas por Usuario ID {UserId}.", id, idUsuario);
return (true, null);
}
catch (KeyNotFoundException knfEx)
{
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback tras KeyNotFoundException."); }
_logger.LogWarning(knfEx, "Entidad no encontrada durante la eliminación de Publicación ID {Id}.", id);
return (false, "Una entidad relacionada o la publicación misma no fue encontrada durante la eliminación.");
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback tras excepción general."); }
_logger.LogError(ex, "Error EliminarAsync Publicacion ID: {Id}", id);
return (false, $"Error interno al eliminar la publicación y sus dependencias: {ex.Message}");
}
}
public async Task<IEnumerable<PublicacionDiaSemanaDto>> ObtenerConfiguracionDiasAsync(int idPublicacion)
{
var configs = await _publicacionRepository.GetConfiguracionDiasAsync(idPublicacion);
return configs.Select(c => new PublicacionDiaSemanaDto
{
IdPublicacionDia = c.IdPublicacionDia,
IdPublicacion = c.IdPublicacion,
DiaSemana = c.DiaSemana,
Activo = c.Activo
});
}
public async Task<IEnumerable<PublicacionDto>> ObtenerPublicacionesPorDiaSemanaAsync(byte diaSemana)
{
// Obtener IDs de las publicaciones configuradas para ese día
var idsPublicaciones = await _publicacionRepository.GetPublicacionesIdsPorDiaSemanaAsync(diaSemana);
if (!idsPublicaciones.Any())
{
return Enumerable.Empty<PublicacionDto>();
}
// Obtener los detalles completos de esas publicaciones
// Podríamos optimizar esto si GetByIdAsync es eficiente o crear un GetByIdsAsync
var publicacionesTasks = idsPublicaciones.Select(id => ObtenerPorIdAsync(id));
var publicacionesResult = await Task.WhenAll(publicacionesTasks);
return publicacionesResult.Where(p => p != null).Select(p => p!); // Filtrar nulos y asegurar no nulabilidad
}
public async Task<(bool Exito, string? Error)> ActualizarConfiguracionDiasAsync(int idPublicacion, UpdatePublicacionDiasSemanaRequestDto requestDto, int idUsuario)
{
var publicacionExistente = await _publicacionRepository.GetByIdSimpleAsync(idPublicacion);
if (publicacionExistente == null)
{
return (false, "Publicación no encontrada.");
}
// Validar que los días de la semana estén en el rango correcto (0-6)
if (requestDto.DiasActivos.Any(d => d > 6)) // byte no puede ser < 0
{
return (false, "Día de la semana inválido. Debe estar entre 0 (Domingo) y 6 (Sábado).");
}
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
{
await _publicacionRepository.UpdateConfiguracionDiasAsync(idPublicacion, requestDto.DiasActivos.Distinct(), transaction);
// No se está implementando historial para PublicacionDiaSemana por ahora.
// Si se necesitara, se añadiría aquí una llamada al repositorio para insertar en la tabla _H.
transaction.Commit();
_logger.LogInformation("Configuración de días actualizada para Publicación ID {IdPublicacion} por Usuario ID {UserId}.", idPublicacion, idUsuario);
return (true, null);
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error al actualizar configuración de días para Publicacion ID: {IdPublicacion}", idPublicacion);
return (false, $"Error interno al actualizar la configuración de días: {ex.Message}");
}
}
1. Funcionalidad Principal: Auditoría General Se creó una nueva sección de "Auditoría" en la aplicación, diseñada para ser accedida por SuperAdmins. Se implementó una página AuditoriaGeneralPage.tsx que actúa como un visor centralizado para el historial de cambios de múltiples entidades del sistema. 2. Backend: Nuevo Controlador (AuditoriaController.cs): Centraliza los endpoints para obtener datos de las tablas de historial (_H). Servicios y Repositorios Extendidos: Se añadieron métodos GetHistorialAsync y ObtenerHistorialAsync a las capas de repositorio y servicio para cada una de las siguientes entidades, permitiendo consultar sus tablas _H con filtros: 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) Tipos de Pago (cue_dtTipopago_H) Canillitas (Maestro) (dist_dtCanillas_H) Distribuidores (Maestro) (dist_dtDistribuidores_H) Empresas (Maestro) (dist_dtEmpresas_H) Zonas (Maestro) (dist_dtZonas_H) Otros Destinos (Maestro) (dist_dtOtrosDestinos_H) Publicaciones (Maestro) (dist_dtPublicaciones_H) Secciones de Publicación (dist_dtPubliSecciones_H) Precios de Publicación (dist_Precios_H) Recargos por Zona (dist_RecargoZona_H) Porcentajes Pago Distribuidores (dist_PorcPago_H) Porcentajes/Montos Canillita (dist_PorcMonPagoCanilla_H) Control de Devoluciones (dist_dtCtrlDevoluciones_H) Tipos de Bobina (bob_dtBobinas_H) Estados de Bobina (bob_dtEstadosBobinas_H) Plantas de Impresión (bob_dtPlantas_H) Stock de Bobinas (bob_StockBobinas_H) Tiradas (Registro Principal) (bob_RegTiradas_H) Secciones de Tirada (bob_RegPublicaciones_H) Cambios de Parada de Canillitas (dist_CambiosParadasCanillas_H) Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial) DTOs de Historial: Se crearon DTOs específicos para cada tabla de historial (ej. UsuarioHistorialDto, PagoDistribuidorHistorialDto, etc.) para transferir los datos al frontend, incluyendo el nombre del usuario que realizó la modificación. Corrección de Lógica de Saldos: Se revisó y corrigió la lógica de afectación de saldos en los servicios PagoDistribuidorService y NotaCreditoDebitoService para asegurar que los débitos y créditos se apliquen correctamente. 3. Frontend: Nuevo Servicio (auditoriaService.ts): Contiene métodos para llamar a cada uno de los nuevos endpoints de auditoría del backend. Nueva Página (AuditoriaGeneralPage.tsx): Permite al SuperAdmin seleccionar el "Tipo de Entidad" a auditar desde un dropdown. Ofrece filtros comunes (rango de fechas, usuario modificador, tipo de acción) y filtros específicos que aparecen dinámicamente según la entidad seleccionada. Utiliza un DataGrid de Material-UI para mostrar el historial, con columnas que se adaptan dinámicamente al tipo de entidad consultada. Nuevos DTOs en TypeScript: Se crearon las interfaces correspondientes a los DTOs de historial del backend. Gestión de Permisos: La sección de Auditoría en MainLayout.tsx y su ruta en AppRoutes.tsx están protegidas para ser visibles y accesibles solo por SuperAdmins. Se añadió un permiso de ejemplo AU_GENERAL_VIEW para ser usado si se decide extender el acceso en el futuro. Corrección de Errores Menores: Se solucionó el problema del "parpadeo" del selector de fecha en GestionarNovedadesCanillaPage al adoptar un patrón de carga de datos más controlado, similar a otras páginas funcionales.
2025-06-12 19:36:21 -03:00
public async Task<IEnumerable<PublicacionHistorialDto>> ObtenerHistorialAsync(
DateTime? fechaDesde, DateTime? fechaHasta,
int? idUsuarioModifico, string? tipoModificacion,
int? idPublicacionAfectada)
{
var historialData = await _publicacionRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPublicacionAfectada);
// Si necesitas NombreEmpresa, harías el JOIN en repo o llamada a _empresaRepository aquí.
return historialData.Select(h => new PublicacionHistorialDto
{
Id_Publicacion = h.Historial.Id_Publicacion,
Nombre = h.Historial.Nombre,
Observacion = h.Historial.Observacion,
Id_Empresa = h.Historial.Id_Empresa,
Habilitada = h.Historial.Habilitada,
// CtrlDevoluciones no está en _H, si se añade, mapearla aquí
Id_Usuario = h.Historial.Id_Usuario,
NombreUsuarioModifico = h.NombreUsuarioModifico,
FechaMod = h.Historial.FechaMod,
TipoMod = h.Historial.TipoMod
}).ToList();
}
feat: Implementación CRUD Canillitas, Distribuidores y Precios de Publicación Backend API: - Canillitas (`dist_dtCanillas`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Lógica para manejo de `Accionista`, `Baja`, `FechaBaja`. - Auditoría en `dist_dtCanillas_H`. - Validación de legajo único y lógica de empresa vs accionista. - Distribuidores (`dist_dtDistribuidores`): - Implementado CRUD completo (Modelos, DTOs, Repositorio, Servicio, Controlador). - Auditoría en `dist_dtDistribuidores_H`. - Creación de saldos iniciales para el nuevo distribuidor en todas las empresas. - Verificación de NroDoc único y Nombre opcionalmente único. - Precios de Publicación (`dist_Precios`): - Implementado CRUD básico (Modelos, DTOs, Repositorio, Servicio, Controlador). - Endpoints anidados bajo `/publicaciones/{idPublicacion}/precios`. - Lógica de negocio para cerrar período de precio anterior al crear uno nuevo. - Lógica de negocio para reabrir período de precio anterior al eliminar el último. - Auditoría en `dist_Precios_H`. - Auditoría en Eliminación de Publicaciones: - Extendido `PublicacionService.EliminarAsync` para eliminar en cascada registros de precios, recargos, porcentajes de pago (distribuidores y canillitas) y secciones de publicación. - Repositorios correspondientes (`PrecioRepository`, `RecargoZonaRepository`, `PorcPagoRepository`, `PorcMonCanillaRepository`, `PubliSeccionRepository`) actualizados con métodos `DeleteByPublicacionIdAsync` que registran en sus respectivas tablas `_H` (si existen y se implementó la lógica). - Asegurada la correcta propagación del `idUsuario` para la auditoría en cascada. - Correcciones de Nulabilidad: - Ajustados los métodos `MapToDto` y su uso en `CanillaService` y `PublicacionService` para manejar correctamente tipos anulables. Frontend React: - Canillitas: - `canillaService.ts`. - `CanillaFormModal.tsx` con selectores para Zona y Empresa, y lógica de Accionista. - `GestionarCanillitasPage.tsx` con filtros, paginación, y acciones (editar, toggle baja). - Distribuidores: - `distribuidorService.ts`. - `DistribuidorFormModal.tsx` con múltiples campos y selector de Zona. - `GestionarDistribuidoresPage.tsx` con filtros, paginación, y acciones (editar, eliminar). - Precios de Publicación: - `precioService.ts`. - `PrecioFormModal.tsx` para crear/editar períodos de precios (VigenciaD, VigenciaH opcional, precios por día). - `GestionarPreciosPublicacionPage.tsx` accesible desde la gestión de publicaciones, para listar y gestionar los períodos de precios de una publicación específica. - Layout: - Reemplazado el uso de `Grid` por `Box` con Flexbox en `CanillaFormModal`, `GestionarCanillitasPage` (filtros), `DistribuidorFormModal` y `PrecioFormModal` para resolver problemas de tipos y mejorar la consistencia del layout de formularios. - Navegación: - Actualizadas las rutas y pestañas para los nuevos módulos y sub-módulos.
2025-05-20 12:38:55 -03:00
}
}