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).
299 lines
14 KiB
C#
299 lines
14 KiB
C#
using GestionIntegral.Api.Data;
|
|
using GestionIntegral.Api.Data.Repositories.Contables;
|
|
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
|
using GestionIntegral.Api.Dtos.Auditoria;
|
|
using GestionIntegral.Api.Dtos.Contables;
|
|
using GestionIntegral.Api.Models.Contables;
|
|
using Microsoft.Extensions.Logging;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Data;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace GestionIntegral.Api.Services.Contables
|
|
{
|
|
public class PagoDistribuidorService : IPagoDistribuidorService
|
|
{
|
|
private readonly IPagoDistribuidorRepository _pagoRepo;
|
|
private readonly IDistribuidorRepository _distribuidorRepo;
|
|
private readonly ITipoPagoRepository _tipoPagoRepo;
|
|
private readonly IEmpresaRepository _empresaRepo;
|
|
private readonly ISaldoRepository _saldoRepo;
|
|
private readonly DbConnectionFactory _connectionFactory;
|
|
private readonly ILogger<PagoDistribuidorService> _logger;
|
|
|
|
public PagoDistribuidorService(
|
|
IPagoDistribuidorRepository pagoRepo,
|
|
IDistribuidorRepository distribuidorRepo,
|
|
ITipoPagoRepository tipoPagoRepo,
|
|
IEmpresaRepository empresaRepo,
|
|
ISaldoRepository saldoRepo,
|
|
DbConnectionFactory connectionFactory,
|
|
ILogger<PagoDistribuidorService> logger)
|
|
{
|
|
_pagoRepo = pagoRepo;
|
|
_distribuidorRepo = distribuidorRepo;
|
|
_tipoPagoRepo = tipoPagoRepo;
|
|
_empresaRepo = empresaRepo;
|
|
_saldoRepo = saldoRepo;
|
|
_connectionFactory = connectionFactory;
|
|
_logger = logger;
|
|
}
|
|
|
|
private async Task<PagoDistribuidorDto> MapToDto(PagoDistribuidor pago)
|
|
{
|
|
if (pago == null) return null!;
|
|
|
|
var distribuidorData = await _distribuidorRepo.GetByIdAsync(pago.IdDistribuidor);
|
|
var tipoPago = await _tipoPagoRepo.GetByIdAsync(pago.IdTipoPago);
|
|
var empresa = await _empresaRepo.GetByIdAsync(pago.IdEmpresa);
|
|
|
|
return new PagoDistribuidorDto
|
|
{
|
|
IdPago = pago.IdPago,
|
|
IdDistribuidor = pago.IdDistribuidor,
|
|
NombreDistribuidor = distribuidorData.Distribuidor?.Nombre ?? "N/A",
|
|
Fecha = pago.Fecha.ToString("yyyy-MM-dd"),
|
|
TipoMovimiento = pago.TipoMovimiento,
|
|
Recibo = pago.Recibo,
|
|
Monto = pago.Monto,
|
|
IdTipoPago = pago.IdTipoPago,
|
|
NombreTipoPago = tipoPago?.Nombre ?? "N/A",
|
|
Detalle = pago.Detalle,
|
|
IdEmpresa = pago.IdEmpresa,
|
|
NombreEmpresa = empresa?.Nombre ?? "N/A"
|
|
};
|
|
}
|
|
|
|
public async Task<IEnumerable<PagoDistribuidorDto>> ObtenerTodosAsync(
|
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
|
int? idDistribuidor, int? idEmpresa, string? tipoMovimiento)
|
|
{
|
|
var pagos = await _pagoRepo.GetAllAsync(fechaDesde, fechaHasta, idDistribuidor, idEmpresa, tipoMovimiento);
|
|
var dtos = new List<PagoDistribuidorDto>();
|
|
foreach (var pago in pagos)
|
|
{
|
|
dtos.Add(await MapToDto(pago));
|
|
}
|
|
return dtos;
|
|
}
|
|
|
|
public async Task<PagoDistribuidorDto?> ObtenerPorIdAsync(int idPago)
|
|
{
|
|
var pago = await _pagoRepo.GetByIdAsync(idPago);
|
|
return pago == null ? null : await MapToDto(pago);
|
|
}
|
|
|
|
public async Task<(PagoDistribuidorDto? Pago, string? Error)> CrearAsync(CreatePagoDistribuidorDto createDto, int idUsuario)
|
|
{
|
|
if (await _distribuidorRepo.GetByIdSimpleAsync(createDto.IdDistribuidor) == null)
|
|
return (null, "Distribuidor no válido.");
|
|
if (await _tipoPagoRepo.GetByIdAsync(createDto.IdTipoPago) == null)
|
|
return (null, "Tipo de pago no válido.");
|
|
if (await _empresaRepo.GetByIdAsync(createDto.IdEmpresa) == null)
|
|
return (null, "Empresa no válida.");
|
|
if (await _pagoRepo.ExistsByReciboAndTipoMovimientoAsync(createDto.Recibo, createDto.TipoMovimiento))
|
|
return (null, $"Ya existe un pago '{createDto.TipoMovimiento}' con el número de recibo '{createDto.Recibo}'.");
|
|
|
|
var nuevoPago = new PagoDistribuidor
|
|
{
|
|
IdDistribuidor = createDto.IdDistribuidor,
|
|
Fecha = createDto.Fecha.Date,
|
|
TipoMovimiento = createDto.TipoMovimiento,
|
|
Recibo = createDto.Recibo,
|
|
Monto = createDto.Monto,
|
|
IdTipoPago = createDto.IdTipoPago,
|
|
Detalle = createDto.Detalle,
|
|
IdEmpresa = createDto.IdEmpresa
|
|
};
|
|
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
IDbTransaction? transaction = null; // Declarar fuera para el finally
|
|
try
|
|
{
|
|
if (connection.State != ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
|
}
|
|
transaction = connection.BeginTransaction();
|
|
|
|
var pagoCreado = await _pagoRepo.CreateAsync(nuevoPago, idUsuario, transaction);
|
|
if (pagoCreado == null) throw new DataException("Error al registrar el pago.");
|
|
|
|
decimal montoParaSaldo;
|
|
if (createDto.TipoMovimiento == "Recibido")
|
|
{
|
|
montoParaSaldo = -createDto.Monto;
|
|
}
|
|
else
|
|
{
|
|
montoParaSaldo = createDto.Monto;
|
|
}
|
|
|
|
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoCreado.IdDistribuidor, pagoCreado.IdEmpresa, montoParaSaldo, transaction);
|
|
if (!saldoActualizado) throw new DataException("Error al actualizar el saldo del distribuidor.");
|
|
|
|
transaction.Commit();
|
|
_logger.LogInformation("PagoDistribuidor ID {Id} creado y saldo afectado por Usuario ID {UserId}.", pagoCreado.IdPago, idUsuario);
|
|
return (await MapToDto(pagoCreado), null);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de CrearAsync PagoDistribuidor."); }
|
|
_logger.LogError(ex, "Error CrearAsync PagoDistribuidor.");
|
|
return (null, $"Error interno: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
if (connection.State == ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPago, UpdatePagoDistribuidorDto updateDto, int idUsuario)
|
|
{
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
IDbTransaction? transaction = null;
|
|
try
|
|
{
|
|
if (connection.State != ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
|
}
|
|
transaction = connection.BeginTransaction();
|
|
|
|
var pagoExistente = await _pagoRepo.GetByIdAsync(idPago);
|
|
if (pagoExistente == null)
|
|
{
|
|
transaction.Rollback(); // Rollback si no se encuentra
|
|
return (false, "Pago no encontrado.");
|
|
}
|
|
|
|
if (await _tipoPagoRepo.GetByIdAsync(updateDto.IdTipoPago) == null)
|
|
{
|
|
transaction.Rollback();
|
|
return (false, "Tipo de pago no válido.");
|
|
}
|
|
|
|
decimal impactoOriginalSaldo = pagoExistente.TipoMovimiento == "Recibido" ? -pagoExistente.Monto : pagoExistente.Monto;
|
|
decimal impactoNuevoSaldo = pagoExistente.TipoMovimiento == "Recibido" ? -updateDto.Monto : updateDto.Monto;
|
|
decimal diferenciaAjusteSaldo = impactoNuevoSaldo - impactoOriginalSaldo;
|
|
|
|
var pagoParaActualizarEnRepo = new PagoDistribuidor
|
|
{
|
|
IdPago = pagoExistente.IdPago,
|
|
IdDistribuidor = pagoExistente.IdDistribuidor,
|
|
Fecha = pagoExistente.Fecha,
|
|
TipoMovimiento = pagoExistente.TipoMovimiento,
|
|
Recibo = pagoExistente.Recibo,
|
|
Monto = updateDto.Monto,
|
|
IdTipoPago = updateDto.IdTipoPago,
|
|
Detalle = updateDto.Detalle,
|
|
IdEmpresa = pagoExistente.IdEmpresa
|
|
};
|
|
|
|
var actualizado = await _pagoRepo.UpdateAsync(pagoParaActualizarEnRepo, idUsuario, transaction);
|
|
if (!actualizado) throw new DataException("Error al actualizar el pago en la base de datos.");
|
|
|
|
if (diferenciaAjusteSaldo != 0)
|
|
{
|
|
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoExistente.IdDistribuidor, pagoExistente.IdEmpresa, diferenciaAjusteSaldo, transaction);
|
|
if (!saldoActualizado) throw new DataException("Error al ajustar el saldo del distribuidor tras la actualización del pago.");
|
|
}
|
|
|
|
transaction.Commit();
|
|
_logger.LogInformation("PagoDistribuidor ID {Id} actualizado por Usuario ID {UserId}.", idPago, idUsuario);
|
|
return (true, null);
|
|
}
|
|
catch (KeyNotFoundException) { try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync PagoDistribuidor (KeyNotFound)."); } return (false, "Pago no encontrado."); }
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync PagoDistribuidor."); }
|
|
_logger.LogError(ex, "Error ActualizarAsync PagoDistribuidor ID: {Id}", idPago);
|
|
return (false, $"Error interno: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
if (connection.State == ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<(bool Exito, string? Error)> EliminarAsync(int idPago, int idUsuario)
|
|
{
|
|
using var connection = _connectionFactory.CreateConnection();
|
|
IDbTransaction? transaction = null;
|
|
try
|
|
{
|
|
if (connection.State != ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
|
}
|
|
transaction = connection.BeginTransaction();
|
|
|
|
var pagoExistente = await _pagoRepo.GetByIdAsync(idPago);
|
|
if (pagoExistente == null)
|
|
{
|
|
transaction.Rollback();
|
|
return (false, "Pago no encontrado.");
|
|
}
|
|
|
|
decimal montoReversion = pagoExistente.TipoMovimiento == "Recibido" ? pagoExistente.Monto : -pagoExistente.Monto;
|
|
|
|
var eliminado = await _pagoRepo.DeleteAsync(idPago, idUsuario, transaction);
|
|
if (!eliminado) throw new DataException("Error al eliminar el pago de la base de datos.");
|
|
|
|
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoExistente.IdDistribuidor, pagoExistente.IdEmpresa, montoReversion, transaction);
|
|
if (!saldoActualizado) throw new DataException("Error al revertir el saldo del distribuidor tras la eliminación del pago.");
|
|
|
|
transaction.Commit();
|
|
_logger.LogInformation("PagoDistribuidor ID {Id} eliminado por Usuario ID {UserId}.", idPago, idUsuario);
|
|
return (true, null);
|
|
}
|
|
catch (KeyNotFoundException) { try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync PagoDistribuidor (KeyNotFound)."); } return (false, "Pago no encontrado."); }
|
|
catch (Exception ex)
|
|
{
|
|
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync PagoDistribuidor."); }
|
|
_logger.LogError(ex, "Error EliminarAsync PagoDistribuidor ID: {Id}", idPago);
|
|
return (false, $"Error interno: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
if (connection.State == ConnectionState.Open)
|
|
{
|
|
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Task<IEnumerable<PagoDistribuidorHistorialDto>> ObtenerHistorialAsync(
|
|
DateTime? fechaDesde, DateTime? fechaHasta,
|
|
int? idUsuarioModifico, string? tipoModificacion,
|
|
int? idPagoAfectado)
|
|
{
|
|
var historialData = await _pagoRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idPagoAfectado);
|
|
|
|
return historialData.Select(h => new PagoDistribuidorHistorialDto
|
|
{
|
|
Id_Pago = h.Historial.Id_Pago,
|
|
Id_Distribuidor = h.Historial.Id_Distribuidor,
|
|
Fecha = h.Historial.Fecha,
|
|
TipoMovimiento = h.Historial.TipoMovimiento,
|
|
Recibo = h.Historial.Recibo,
|
|
Monto = h.Historial.Monto,
|
|
Id_TipoPago = h.Historial.Id_TipoPago,
|
|
Detalle = h.Historial.Detalle,
|
|
Id_Empresa = h.Historial.Id_Empresa,
|
|
Id_Usuario = h.Historial.Id_Usuario,
|
|
NombreUsuarioModifico = h.NombreUsuarioModifico,
|
|
FechaMod = h.Historial.FechaMod,
|
|
TipoMod = h.Historial.TipoMod
|
|
}).ToList();
|
|
}
|
|
}
|
|
} |