Files
GestionIntegralWeb/Backend/GestionIntegral.Api/Services/Distribucion/EmpresaService.cs
eldiadmolinari 437b1e8864 Backend:
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).
2025-06-09 19:37:07 -03:00

286 lines
14 KiB
C#

using GestionIntegral.Api.Dtos.Empresas; // Para los DTOs (EmpresaDto, CreateEmpresaDto, UpdateEmpresaDto)
using Microsoft.Extensions.Logging; // Para ILogger
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Data;
using GestionIntegral.Api.Data;
using GestionIntegral.Api.Models.Distribucion;
using GestionIntegral.Api.Data.Repositories.Distribucion;
using GestionIntegral.Api.Data.Repositories.Contables;
using GestionIntegral.Api.Dtos.Auditoria; // Para IDbTransaction, ConnectionState
namespace GestionIntegral.Api.Services.Distribucion
{
public class EmpresaService : IEmpresaService
{
private readonly IEmpresaRepository _empresaRepository;
private readonly ISaldoRepository _saldoRepository;
private readonly DbConnectionFactory _connectionFactory; // Para manejar la transacción
private readonly ILogger<EmpresaService> _logger;
public EmpresaService(
IEmpresaRepository empresaRepository,
ISaldoRepository saldoRepository,
DbConnectionFactory connectionFactory,
ILogger<EmpresaService> logger)
{
_empresaRepository = empresaRepository;
_saldoRepository = saldoRepository;
_connectionFactory = connectionFactory;
_logger = logger;
}
public async Task<IEnumerable<EmpresaDto>> ObtenerTodasAsync(string? nombreFilter, string? detalleFilter)
{
// El repositorio ya devuelve solo las activas si es necesario
var empresas = await _empresaRepository.GetAllAsync(nombreFilter, detalleFilter);
// Mapeo Entidad -> DTO
return empresas.Select(e => new EmpresaDto
{
IdEmpresa = e.IdEmpresa,
Nombre = e.Nombre,
Detalle = e.Detalle
});
}
public async Task<IEnumerable<EmpresaDropdownDto>> ObtenerParaDropdown()
{
// El repositorio ya devuelve solo las activas si es necesario
var empresas = await _empresaRepository.GetAllDropdownAsync();
// Mapeo Entidad -> DTO
return empresas.Select(e => new EmpresaDropdownDto
{
IdEmpresa = e.IdEmpresa,
Nombre = e.Nombre
});
}
public async Task<EmpresaDto?> ObtenerPorIdAsync(int id)
{
// El repositorio ya devuelve solo las activas si es necesario
var empresa = await _empresaRepository.GetByIdAsync(id);
if (empresa == null) return null;
// Mapeo Entidad -> DTO
return new EmpresaDto
{
IdEmpresa = empresa.IdEmpresa,
Nombre = empresa.Nombre,
Detalle = empresa.Detalle
};
}
public async Task<EmpresaLookupDto?> ObtenerLookupPorIdAsync(int id)
{
// El repositorio ya devuelve solo las activas si es necesario
var empresa = await _empresaRepository.ObtenerLookupPorIdAsync(id);
if (empresa == null) return null;
// Mapeo Entidad -> DTO
return new EmpresaLookupDto
{
IdEmpresa = empresa.IdEmpresa,
Nombre = empresa.Nombre
};
}
public async Task<(EmpresaDto? Empresa, string? Error)> CrearAsync(CreateEmpresaDto createDto, int idUsuario)
{
// Validación de negocio: Nombre duplicado
if (await _empresaRepository.ExistsByNameAsync(createDto.Nombre))
{
return (null, "El nombre de la empresa ya existe.");
}
var nuevaEmpresa = new Empresa
{
Nombre = createDto.Nombre,
Detalle = createDto.Detalle
};
// --- Transacción ---
using (var connection = _connectionFactory.CreateConnection())
{
// Abrir conexión de forma asíncrona
if (connection is System.Data.Common.DbConnection dbConnection)
{
await dbConnection.OpenAsync();
}
else
{
connection.Open(); // Fallback síncrono
}
using (var transaction = connection.BeginTransaction())
{
try
{
// 1. Crear Empresa (Repo maneja su historial dentro de esta transacción)
var empresaCreada = await _empresaRepository.CreateAsync(nuevaEmpresa, idUsuario, transaction);
if (empresaCreada == null)
{
throw new InvalidOperationException("No se pudo crear la empresa en el repositorio.");
}
// 2. Obtener IDs de Distribuidores
var distribuidoresIds = await _saldoRepository.GetAllDistribuidorIdsAsync(); // No necesita transacción si solo lee
// 3. Crear Saldos Iniciales (CERO) para cada distribuidor en esta nueva empresa
foreach (var idDistribuidor in distribuidoresIds)
{
bool saldoCreado = await _saldoRepository.CreateSaldoInicialAsync("Distribuidores", idDistribuidor, empresaCreada.IdEmpresa, transaction);
if (!saldoCreado)
{
throw new InvalidOperationException($"Falló al crear saldo inicial para distribuidor {idDistribuidor} y nueva empresa {empresaCreada.IdEmpresa}.");
}
_logger.LogInformation("Saldo inicial creado para Distribuidor ID {IdDistribuidor}, Empresa ID {IdEmpresa}", idDistribuidor, empresaCreada.IdEmpresa);
}
transaction.Commit();
var empresaDto = new EmpresaDto
{
IdEmpresa = empresaCreada.IdEmpresa,
Nombre = empresaCreada.Nombre,
Detalle = empresaCreada.Detalle
};
_logger.LogInformation("Empresa ID {IdEmpresa} creada exitosamente por Usuario ID {IdUsuario}.", empresaCreada.IdEmpresa, idUsuario);
return (empresaDto, null); // Éxito
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en CrearAsync Empresa."); }
_logger.LogError(ex, "Error en transacción CrearAsync para Empresa. Nombre: {Nombre}", createDto.Nombre);
return (null, "Error interno al procesar la creación de la empresa.");
}
} // La transacción se dispone aquí (y cierra la conexión si no hubo commit/rollback explícito, aunque ya lo hacemos)
} // La conexión se cierra/dispone aquí
// --- Fin Transacción ---
}
public async Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateEmpresaDto updateDto, int idUsuario)
{
var empresaExistente = await _empresaRepository.GetByIdAsync(id);
if (empresaExistente == null)
{
return (false, "Empresa no encontrada.");
}
if (await _empresaRepository.ExistsByNameAsync(updateDto.Nombre, id))
{
return (false, "El nombre de la empresa ya existe para otro registro.");
}
empresaExistente.Nombre = updateDto.Nombre;
empresaExistente.Detalle = updateDto.Detalle;
// --- Transacción ---
using (var connection = _connectionFactory.CreateConnection())
{
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
using (var transaction = connection.BeginTransaction())
{
try
{
var actualizado = await _empresaRepository.UpdateAsync(empresaExistente, idUsuario, transaction);
if (!actualizado)
{
throw new InvalidOperationException("La actualización en el repositorio de empresas devolvió false.");
}
transaction.Commit();
_logger.LogInformation("Empresa ID {IdEmpresa} actualizada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
return (true, null); // Éxito
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en ActualizarAsync Empresa."); }
_logger.LogError(ex, "Error en transacción ActualizarAsync para Empresa ID: {Id}", id);
return (false, "Error interno al actualizar la empresa.");
}
}
}
// --- Fin Transacción ---
}
public async Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario)
{
// Primero verificamos si existe, incluso inactiva, para evitar errores 404 si no existe
var empresaExistente = await _empresaRepository.GetByIdAsync(id);
if (empresaExistente == null)
{
return (false, "Empresa no encontrada.");
}
// Validación: ¿Está en uso?
if (await _empresaRepository.IsInUseAsync(id))
{
return (false, "No se puede eliminar. Existen publicaciones relacionadas a la empresa.");
}
// --- Transacción ---
using (var connection = _connectionFactory.CreateConnection())
{
if (connection is System.Data.Common.DbConnection dbConnection) { await dbConnection.OpenAsync(); } else { connection.Open(); }
using (var transaction = connection.BeginTransaction())
{
try
{
// 1. Eliminar Saldos asociados
bool saldosEliminados = await _saldoRepository.DeleteSaldosByEmpresaAsync(id, transaction);
// No lanzamos error si saldosEliminados es false, podría no haber tenido saldos. Loggeamos si es necesario.
if (!saldosEliminados && await _saldoRepository.CheckIfSaldosExistForEmpresaAsync(id)) // Necesitarías este método en ISaldoRepository
{
_logger.LogWarning("Se intentó eliminar Empresa ID {IdEmpresa} pero falló la eliminación de saldos asociados.", id);
// Decidir si continuar o fallar. Por ahora, continuamos pero loggeamos.
// throw new InvalidOperationException("Error al intentar eliminar los saldos asociados a la empresa.");
}
else if (!saldosEliminados)
{
_logger.LogInformation("No se encontraron saldos para eliminar de la Empresa ID {IdEmpresa}.", id);
}
else
{
_logger.LogInformation("Saldos eliminados para Empresa ID {IdEmpresa}.", id);
}
// 2. Eliminar Empresa (Repo maneja historial)
var eliminado = await _empresaRepository.DeleteAsync(id, idUsuario, transaction);
if (!eliminado)
{
throw new InvalidOperationException("La eliminación en el repositorio de empresas devolvió false.");
}
transaction.Commit();
_logger.LogInformation("Empresa ID {IdEmpresa} eliminada exitosamente por Usuario ID {IdUsuario}.", id, idUsuario);
return (true, null); // Éxito
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error al intentar hacer rollback en EliminarAsync Empresa."); }
_logger.LogError(ex, "Error en transacción EliminarAsync para Empresa ID: {Id}", id);
return (false, "Error interno al eliminar la empresa.");
}
}
}
}
public async Task<IEnumerable<EmpresaHistorialDto>> ObtenerHistorialAsync(
DateTime? fechaDesde, DateTime? fechaHasta,
int? idUsuarioModifico, string? tipoModificacion,
int? idEmpresaAfectada)
{
var historialData = await _empresaRepository.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idEmpresaAfectada);
return historialData.Select(h => new EmpresaHistorialDto
{
Id_Empresa = h.Historial.Id_Empresa,
Nombre = h.Historial.Nombre,
Detalle = h.Historial.Detalle,
Id_Usuario = h.Historial.Id_Usuario,
NombreUsuarioModifico = h.NombreUsuarioModifico,
FechaMod = h.Historial.FechaMod,
TipoMod = h.Historial.TipoMod
}).ToList();
}
}
}