Diseño de un AuditoriaController con un patrón para añadir endpoints de historial para diferentes entidades. Implementación de la lógica de servicio y repositorio para obtener datos de las tablas _H para: 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) Ajustes Manuales de Saldo (cue_SaldoAjustesHistorial) Tipos de Pago (cue_dtTipopago_H) Canillitas (Maestro) (dist_dtCanillas_H) Distribuidores (Maestro) (dist_dtDistribuidores_H) Empresas (Maestro) (dist_dtEmpresas_H) DTOs específicos para cada tipo de historial, incluyendo NombreUsuarioModifico. Frontend: Servicio auditoriaService.ts con métodos para llamar a cada endpoint de historial. Página AuditoriaGeneralPage.tsx con: Selector de "Tipo de Entidad a Auditar". Filtros comunes (Fechas, Usuario Modificador, Tipo de Modificación, ID Entidad). Un DataGrid que muestra las columnas dinámicamente según el tipo de entidad seleccionada. Lógica para cargar los datos correspondientes. DTOs de historial en TypeScript. Actualizaciones en AppRoutes.tsx y MainLayout.tsx para la nueva sección de Auditoría (restringida a SuperAdmin).
292 lines
14 KiB
C#
292 lines
14 KiB
C#
using GestionIntegral.Api.Data;
|
|
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
|
using GestionIntegral.Api.Dtos.Auditoria;
|
|
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 CanillaService : ICanillaService
|
|
{
|
|
private readonly ICanillaRepository _canillaRepository;
|
|
private readonly IZonaRepository _zonaRepository;
|
|
private readonly IEmpresaRepository _empresaRepository;
|
|
private readonly DbConnectionFactory _connectionFactory;
|
|
private readonly ILogger<CanillaService> _logger;
|
|
|
|
public CanillaService(
|
|
ICanillaRepository canillaRepository,
|
|
IZonaRepository zonaRepository,
|
|
IEmpresaRepository empresaRepository,
|
|
DbConnectionFactory connectionFactory,
|
|
ILogger<CanillaService> logger)
|
|
{
|
|
_canillaRepository = canillaRepository;
|
|
_zonaRepository = zonaRepository;
|
|
_empresaRepository = empresaRepository;
|
|
_connectionFactory = connectionFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
// CORREGIDO: MapToDto ahora acepta una tupla con tipos anulables
|
|
private CanillaDto? MapToDto((Canilla? Canilla, string? NombreZona, string? NombreEmpresa) data)
|
|
{
|
|
if (data.Canilla == null) return null;
|
|
|
|
return new CanillaDto
|
|
{
|
|
IdCanilla = data.Canilla.IdCanilla,
|
|
Legajo = data.Canilla.Legajo,
|
|
NomApe = data.Canilla.NomApe,
|
|
Parada = data.Canilla.Parada,
|
|
IdZona = data.Canilla.IdZona,
|
|
NombreZona = data.NombreZona ?? "N/A", // Manejar null
|
|
Accionista = data.Canilla.Accionista,
|
|
Obs = data.Canilla.Obs,
|
|
Empresa = data.Canilla.Empresa,
|
|
NombreEmpresa = data.NombreEmpresa ?? "N/A (Accionista)", // Manejar null
|
|
Baja = data.Canilla.Baja,
|
|
FechaBaja = data.Canilla.FechaBaja?.ToString("dd/MM/yyyy")
|
|
};
|
|
}
|
|
|
|
public async Task<IEnumerable<CanillaDto>> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? esAccionista, bool? soloActivos)
|
|
{
|
|
var data = await _canillaRepository.GetAllAsync(nomApeFilter, legajoFilter, soloActivos, esAccionista);
|
|
// Filtrar nulos y asegurar al compilador que no hay nulos en la lista final
|
|
return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
|
|
}
|
|
|
|
public async Task<CanillaDto?> ObtenerPorIdAsync(int id)
|
|
{
|
|
var data = await _canillaRepository.GetByIdAsync(id);
|
|
// MapToDto ahora devuelve CanillaDto? así que esto es correcto
|
|
return MapToDto(data);
|
|
}
|
|
|
|
public async Task<(CanillaDto? Canilla, string? Error)> CrearAsync(CreateCanillaDto createDto, int idUsuario)
|
|
{
|
|
if (createDto.Legajo.HasValue && createDto.Legajo != 0 && await _canillaRepository.ExistsByLegajoAsync(createDto.Legajo.Value))
|
|
{
|
|
return (null, "El legajo ingresado ya existe para otro canillita.");
|
|
}
|
|
var zona = await _zonaRepository.GetByIdAsync(createDto.IdZona); // GetByIdAsync de Zona ya considera solo activas
|
|
if (zona == null)
|
|
{
|
|
return (null, "La zona seleccionada no es válida o no está activa.");
|
|
}
|
|
if (createDto.Empresa != 0) // Solo validar empresa si no es 0
|
|
{
|
|
var empresa = await _empresaRepository.GetByIdAsync(createDto.Empresa);
|
|
if (empresa == null)
|
|
{
|
|
return (null, "La empresa seleccionada no es válida.");
|
|
}
|
|
}
|
|
|
|
// CORREGIDO: Usar directamente el valor booleano
|
|
if (createDto.Accionista == true && createDto.Empresa != 0)
|
|
{
|
|
return (null, "Un canillita accionista no debe tener una empresa asignada (Empresa debe ser 0).");
|
|
}
|
|
if (createDto.Accionista == false && createDto.Empresa == 0)
|
|
{
|
|
return (null, "Un canillita no accionista debe tener una empresa asignada (Empresa no puede ser 0).");
|
|
}
|
|
|
|
|
|
var nuevoCanilla = new Canilla
|
|
{
|
|
Legajo = createDto.Legajo == 0 ? null : createDto.Legajo,
|
|
NomApe = createDto.NomApe,
|
|
Parada = createDto.Parada,
|
|
IdZona = createDto.IdZona,
|
|
Accionista = createDto.Accionista,
|
|
Obs = createDto.Obs,
|
|
Empresa = createDto.Empresa,
|
|
Baja = false,
|
|
FechaBaja = null
|
|
};
|
|
|
|
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 canillaCreado = await _canillaRepository.CreateAsync(nuevoCanilla, idUsuario, transaction);
|
|
if (canillaCreado == null) throw new DataException("Error al crear el canillita.");
|
|
|
|
transaction.Commit();
|
|
|
|
// Para el DTO de respuesta, necesitamos NombreZona y NombreEmpresa
|
|
string nombreEmpresaParaDto = "N/A (Accionista)";
|
|
if (canillaCreado.Empresa != 0)
|
|
{
|
|
var empresaData = await _empresaRepository.GetByIdAsync(canillaCreado.Empresa);
|
|
nombreEmpresaParaDto = empresaData?.Nombre ?? "Empresa Desconocida";
|
|
}
|
|
|
|
var dtoCreado = new CanillaDto
|
|
{
|
|
IdCanilla = canillaCreado.IdCanilla,
|
|
Legajo = canillaCreado.Legajo,
|
|
NomApe = canillaCreado.NomApe,
|
|
Parada = canillaCreado.Parada,
|
|
IdZona = canillaCreado.IdZona,
|
|
NombreZona = zona.Nombre, // Usar nombre de zona ya obtenido
|
|
Accionista = canillaCreado.Accionista,
|
|
Obs = canillaCreado.Obs,
|
|
Empresa = canillaCreado.Empresa,
|
|
NombreEmpresa = nombreEmpresaParaDto,
|
|
Baja = canillaCreado.Baja,
|
|
FechaBaja = null
|
|
};
|
|
|
|
_logger.LogInformation("Canilla ID {IdCanilla} creado por Usuario ID {IdUsuario}.", canillaCreado.IdCanilla, idUsuario);
|
|
return (dtoCreado, null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction.Rollback(); } catch { }
|
|
_logger.LogError(ex, "Error CrearAsync Canilla: {NomApe}", createDto.NomApe);
|
|
return (null, $"Error interno al crear el canillita: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateCanillaDto updateDto, int idUsuario)
|
|
{
|
|
var canillaExistente = await _canillaRepository.GetByIdSimpleAsync(id);
|
|
if (canillaExistente == null) return (false, "Canillita no encontrado.");
|
|
|
|
if (updateDto.Legajo.HasValue && updateDto.Legajo != 0 && await _canillaRepository.ExistsByLegajoAsync(updateDto.Legajo.Value, id))
|
|
{
|
|
return (false, "El legajo ingresado ya existe para otro canillita.");
|
|
}
|
|
if (await _zonaRepository.GetByIdAsync(updateDto.IdZona) == null) // GetByIdAsync de Zona ya considera solo activas
|
|
{
|
|
return (false, "La zona seleccionada no es válida o no está activa.");
|
|
}
|
|
if (updateDto.Empresa != 0) // Solo validar empresa si no es 0
|
|
{
|
|
var empresa = await _empresaRepository.GetByIdAsync(updateDto.Empresa);
|
|
if (empresa == null)
|
|
{
|
|
return (false, "La empresa seleccionada no es válida.");
|
|
}
|
|
}
|
|
|
|
// Usar directamente el valor booleano para Accionista
|
|
if (updateDto.Accionista == true && updateDto.Empresa != 0)
|
|
{
|
|
// Al ser 'bool', no puede ser null. La comparación explícita con 'true'/'false' es para claridad.
|
|
return (false, "Un canillita accionista no debe tener una empresa asignada (Empresa debe ser 0).");
|
|
}
|
|
if (updateDto.Accionista == false && updateDto.Empresa == 0)
|
|
{
|
|
return (false, "Un canillita no accionista debe tener una empresa asignada (Empresa no puede ser 0).");
|
|
}
|
|
|
|
// Mapear DTO a entidad existente
|
|
canillaExistente.Legajo = updateDto.Legajo == 0 ? null : updateDto.Legajo;
|
|
canillaExistente.NomApe = updateDto.NomApe;
|
|
canillaExistente.Parada = updateDto.Parada;
|
|
canillaExistente.IdZona = updateDto.IdZona;
|
|
canillaExistente.Accionista = updateDto.Accionista; // Aquí Accionista ya es bool
|
|
canillaExistente.Obs = updateDto.Obs;
|
|
canillaExistente.Empresa = updateDto.Empresa;
|
|
// Baja y FechaBaja se manejan por ToggleBajaAsync
|
|
|
|
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 _canillaRepository.UpdateAsync(canillaExistente, idUsuario, transaction);
|
|
if (!actualizado) throw new DataException("Error al actualizar el canillita.");
|
|
transaction.Commit();
|
|
_logger.LogInformation("Canilla ID {IdCanilla} actualizado por Usuario ID {IdUsuario}.", id, idUsuario);
|
|
return (true, null);
|
|
}
|
|
catch (KeyNotFoundException)
|
|
{
|
|
try { transaction.Rollback(); } catch { }
|
|
return (false, "Canillita no encontrado durante la actualización.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction.Rollback(); } catch { }
|
|
_logger.LogError(ex, "Error ActualizarAsync Canilla ID: {IdCanilla}", id);
|
|
return (false, $"Error interno al actualizar el canillita: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Exito, string? Error)> ToggleBajaAsync(int id, bool darDeBaja, int idUsuario)
|
|
{
|
|
var canilla = await _canillaRepository.GetByIdSimpleAsync(id);
|
|
if (canilla == null) return (false, "Canillita no encontrado.");
|
|
|
|
if (canilla.Baja == darDeBaja)
|
|
{
|
|
return (false, darDeBaja ? "El canillita ya está dado de baja." : "El canillita ya está activo.");
|
|
}
|
|
|
|
DateTime? fechaBaja = darDeBaja ? DateTime.Now : (DateTime?)null;
|
|
|
|
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 success = await _canillaRepository.ToggleBajaAsync(id, darDeBaja, fechaBaja, idUsuario, transaction);
|
|
if (!success) throw new DataException("Error al cambiar estado de baja del canillita.");
|
|
transaction.Commit();
|
|
_logger.LogInformation("Estado de baja cambiado a {EstadoBaja} para Canilla ID {IdCanilla} por Usuario ID {IdUsuario}.", darDeBaja, id, idUsuario);
|
|
return (true, null);
|
|
}
|
|
catch (KeyNotFoundException)
|
|
{
|
|
try { transaction.Rollback(); } catch { }
|
|
return (false, "Canillita no encontrado durante el cambio de estado de baja.");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction.Rollback(); } catch { }
|
|
_logger.LogError(ex, "Error ToggleBajaAsync Canilla ID: {IdCanilla}", id);
|
|
return (false, $"Error interno al cambiar estado de baja: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
public async Task<IEnumerable<CanillaHistorialDto>> ObtenerHistorialAsync(
|
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
|
int? idUsuarioModifico, string? tipoModificacion,
|
|
int? idCanillaAfectado)
|
|
{
|
|
var historialData = await _canillaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idCanillaAfectado);
|
|
|
|
return historialData.Select(h => new CanillaHistorialDto
|
|
{
|
|
Id_Canilla = h.Historial.Id_Canilla,
|
|
Legajo = h.Historial.Legajo,
|
|
NomApe = h.Historial.NomApe,
|
|
Parada = h.Historial.Parada,
|
|
Id_Zona = h.Historial.Id_Zona,
|
|
Accionista = h.Historial.Accionista,
|
|
Obs = h.Historial.Obs,
|
|
Empresa = h.Historial.Empresa,
|
|
Baja = h.Historial.Baja,
|
|
FechaBaja = h.Historial.FechaBaja,
|
|
Id_Usuario = h.Historial.Id_Usuario,
|
|
NombreUsuarioModifico = h.NombreUsuarioModifico,
|
|
FechaMod = h.Historial.FechaMod,
|
|
TipoMod = h.Historial.TipoMod
|
|
}).ToList();
|
|
}
|
|
}
|
|
} |