Refinamiento de permisos y ajustes en controles. Añade gestión sobre saldos y visualización. Entre otros..
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
using GestionIntegral.Api.Dtos.Contables;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Contables
|
||||
{
|
||||
public interface ISaldoService
|
||||
{
|
||||
Task<IEnumerable<SaldoGestionDto>> ObtenerSaldosParaGestionAsync(string? destinoFilter, int? idDestinoFilter, int? idEmpresaFilter);
|
||||
Task<(bool Exito, string? Error, SaldoGestionDto? SaldoActualizado)> RealizarAjusteManualSaldoAsync(AjusteSaldoRequestDto ajusteDto, int idUsuarioAjuste);
|
||||
}
|
||||
}
|
||||
@@ -52,7 +52,7 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
}
|
||||
else if (nota.Destino == "Canillas")
|
||||
{
|
||||
var canData = await _canillaRepo.GetByIdAsync(nota.IdDestino); // Asumiendo que GetByIdAsync devuelve una tupla
|
||||
var canData = await _canillaRepo.GetByIdAsync(nota.IdDestino);
|
||||
nombreDestinatario = canData.Canilla?.NomApe ?? "Canillita Desconocido";
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
|
||||
public async Task<(NotaCreditoDebitoDto? Nota, string? Error)> CrearAsync(CreateNotaDto createDto, int idUsuario)
|
||||
{
|
||||
// Validar Destinatario
|
||||
if (createDto.Destino == "Distribuidores")
|
||||
{
|
||||
if (await _distribuidorRepo.GetByIdSimpleAsync(createDto.IdDestino) == null)
|
||||
@@ -103,7 +102,7 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
}
|
||||
else if (createDto.Destino == "Canillas")
|
||||
{
|
||||
if (await _canillaRepo.GetByIdSimpleAsync(createDto.IdDestino) == null) // Asumiendo GetByIdSimpleAsync en ICanillaRepository
|
||||
if (await _canillaRepo.GetByIdSimpleAsync(createDto.IdDestino) == null)
|
||||
return (null, "El canillita especificado no existe.");
|
||||
}
|
||||
else { return (null, "Tipo de destino inválido."); }
|
||||
@@ -124,19 +123,29 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
};
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
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 notaCreada = await _notaRepo.CreateAsync(nuevaNota, idUsuario, transaction);
|
||||
if (notaCreada == null) throw new DataException("Error al registrar la nota.");
|
||||
|
||||
// Afectar Saldo
|
||||
// Nota de Crédito: Disminuye la deuda del destinatario (monto positivo para el servicio de saldo)
|
||||
// Nota de Débito: Aumenta la deuda del destinatario (monto negativo para el servicio de saldo)
|
||||
decimal montoAjusteSaldo = createDto.Tipo == "Credito" ? createDto.Monto : -createDto.Monto;
|
||||
decimal montoParaSaldo;
|
||||
if (createDto.Tipo == "Credito")
|
||||
{
|
||||
montoParaSaldo = -createDto.Monto;
|
||||
}
|
||||
else
|
||||
{
|
||||
montoParaSaldo = createDto.Monto;
|
||||
}
|
||||
|
||||
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync(notaCreada.Destino, notaCreada.IdDestino, notaCreada.IdEmpresa, montoAjusteSaldo, transaction);
|
||||
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync(notaCreada.Destino, notaCreada.IdDestino, notaCreada.IdEmpresa, montoParaSaldo, transaction);
|
||||
if (!saldoActualizado) throw new DataException($"Error al actualizar el saldo para {notaCreada.Destino} ID {notaCreada.IdDestino}.");
|
||||
|
||||
transaction.Commit();
|
||||
@@ -145,32 +154,57 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de CrearAsync NotaCreditoDebito."); }
|
||||
_logger.LogError(ex, "Error CrearAsync NotaCreditoDebito.");
|
||||
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 idNota, UpdateNotaDto updateDto, int idUsuario)
|
||||
{
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
IDbTransaction? transaction = null;
|
||||
try
|
||||
{
|
||||
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
|
||||
if (notaExistente == null) return (false, "Nota no encontrada.");
|
||||
if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
}
|
||||
transaction = connection.BeginTransaction();
|
||||
|
||||
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
|
||||
if (notaExistente == null)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return (false, "Nota no encontrada.");
|
||||
}
|
||||
|
||||
decimal impactoOriginalSaldo = notaExistente.Tipo == "Credito" ? -notaExistente.Monto : notaExistente.Monto;
|
||||
decimal impactoNuevoSaldo = notaExistente.Tipo == "Credito" ? -updateDto.Monto : updateDto.Monto;
|
||||
decimal diferenciaAjusteSaldo = impactoNuevoSaldo - impactoOriginalSaldo;
|
||||
|
||||
// Calcular diferencia de monto para ajustar saldo
|
||||
decimal montoOriginal = notaExistente.Tipo == "Credito" ? notaExistente.Monto : -notaExistente.Monto;
|
||||
decimal montoNuevo = notaExistente.Tipo == "Credito" ? updateDto.Monto : -updateDto.Monto; // Tipo no cambia
|
||||
decimal diferenciaAjusteSaldo = montoNuevo - montoOriginal;
|
||||
var notaParaActualizarEnRepo = new NotaCreditoDebito
|
||||
{
|
||||
IdNota = notaExistente.IdNota,
|
||||
Destino = notaExistente.Destino,
|
||||
IdDestino = notaExistente.IdDestino,
|
||||
Referencia = notaExistente.Referencia,
|
||||
Tipo = notaExistente.Tipo,
|
||||
Fecha = notaExistente.Fecha,
|
||||
Monto = updateDto.Monto,
|
||||
Observaciones = updateDto.Observaciones,
|
||||
IdEmpresa = notaExistente.IdEmpresa
|
||||
};
|
||||
|
||||
notaExistente.Monto = updateDto.Monto;
|
||||
notaExistente.Observaciones = updateDto.Observaciones;
|
||||
|
||||
var actualizado = await _notaRepo.UpdateAsync(notaExistente, idUsuario, transaction);
|
||||
if (!actualizado) throw new DataException("Error al actualizar la nota.");
|
||||
var actualizado = await _notaRepo.UpdateAsync(notaParaActualizarEnRepo, idUsuario, transaction);
|
||||
if (!actualizado) throw new DataException("Error al actualizar la nota en la base de datos.");
|
||||
|
||||
if (diferenciaAjusteSaldo != 0)
|
||||
{
|
||||
@@ -182,30 +216,45 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
_logger.LogInformation("NotaC/D ID {Id} actualizada por Usuario ID {UserId}.", idNota, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Nota no encontrada."); }
|
||||
catch (KeyNotFoundException) { try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync NotaCreditoDebito (KeyNotFound)."); } return (false, "Nota no encontrada."); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error ActualizarAsync NotaC/D ID: {Id}", idNota);
|
||||
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de ActualizarAsync NotaCreditoDebito."); }
|
||||
_logger.LogError(ex, "Error ActualizarAsync Nota C/D ID: {Id}", idNota);
|
||||
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 idNota, int idUsuario)
|
||||
{
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
IDbTransaction? transaction = null;
|
||||
try
|
||||
{
|
||||
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
|
||||
if (notaExistente == null) return (false, "Nota no encontrada.");
|
||||
if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
}
|
||||
transaction = connection.BeginTransaction();
|
||||
|
||||
// Revertir el efecto en el saldo
|
||||
decimal montoReversion = notaExistente.Tipo == "Credito" ? -notaExistente.Monto : notaExistente.Monto;
|
||||
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
|
||||
if (notaExistente == null)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return (false, "Nota no encontrada.");
|
||||
}
|
||||
|
||||
decimal montoReversion = notaExistente.Tipo == "Credito" ? notaExistente.Monto : -notaExistente.Monto;
|
||||
|
||||
var eliminado = await _notaRepo.DeleteAsync(idNota, idUsuario, transaction);
|
||||
if (!eliminado) throw new DataException("Error al eliminar la nota.");
|
||||
if (!eliminado) throw new DataException("Error al eliminar la nota de la base de datos.");
|
||||
|
||||
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync(notaExistente.Destino, notaExistente.IdDestino, notaExistente.IdEmpresa, montoReversion, transaction);
|
||||
if (!saldoActualizado) throw new DataException("Error al revertir el saldo tras la eliminación de la nota.");
|
||||
@@ -214,13 +263,20 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
_logger.LogInformation("NotaC/D ID {Id} eliminada y saldo revertido por Usuario ID {UserId}.", idNota, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Nota no encontrada."); }
|
||||
catch (KeyNotFoundException) { try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync NotaCreditoDebito (KeyNotFound)."); } return (false, "Nota no encontrada."); }
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
try { transaction?.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error en Rollback de EliminarAsync NotaCreditoDebito."); }
|
||||
_logger.LogError(ex, "Error EliminarAsync NotaC/D ID: {Id}", idNota);
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -95,7 +95,6 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
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,
|
||||
@@ -109,19 +108,29 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
};
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
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.");
|
||||
|
||||
// Afectar Saldo
|
||||
// Si TipoMovimiento es "Recibido", el monto DISMINUYE la deuda del distribuidor (monto positivo para el servicio de saldo).
|
||||
// Si TipoMovimiento es "Realizado" (empresa paga a distribuidor), el monto AUMENTA la deuda (monto negativo para el servicio de saldo).
|
||||
decimal montoAjusteSaldo = createDto.TipoMovimiento == "Recibido" ? createDto.Monto : -createDto.Monto;
|
||||
decimal montoParaSaldo;
|
||||
if (createDto.TipoMovimiento == "Recibido")
|
||||
{
|
||||
montoParaSaldo = -createDto.Monto;
|
||||
}
|
||||
else
|
||||
{
|
||||
montoParaSaldo = createDto.Monto;
|
||||
}
|
||||
|
||||
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync("Distribuidores", pagoCreado.IdDistribuidor, pagoCreado.IdEmpresa, montoAjusteSaldo, transaction);
|
||||
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();
|
||||
@@ -130,37 +139,63 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
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();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
IDbTransaction? transaction = null;
|
||||
try
|
||||
{
|
||||
var pagoExistente = await _pagoRepo.GetByIdAsync(idPago);
|
||||
if (pagoExistente == null) return (false, "Pago no encontrado.");
|
||||
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;
|
||||
|
||||
// Calcular la diferencia de monto para ajustar el saldo
|
||||
decimal montoOriginal = pagoExistente.TipoMovimiento == "Recibido" ? pagoExistente.Monto : -pagoExistente.Monto;
|
||||
decimal montoNuevo = pagoExistente.TipoMovimiento == "Recibido" ? updateDto.Monto : -updateDto.Monto;
|
||||
decimal diferenciaAjusteSaldo = montoNuevo - montoOriginal;
|
||||
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
|
||||
};
|
||||
|
||||
// Actualizar campos permitidos
|
||||
pagoExistente.Monto = updateDto.Monto;
|
||||
pagoExistente.IdTipoPago = updateDto.IdTipoPago;
|
||||
pagoExistente.Detalle = updateDto.Detalle;
|
||||
|
||||
var actualizado = await _pagoRepo.UpdateAsync(pagoExistente, idUsuario, transaction);
|
||||
if (!actualizado) throw new DataException("Error al actualizar el pago.");
|
||||
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)
|
||||
{
|
||||
@@ -172,32 +207,45 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
_logger.LogInformation("PagoDistribuidor ID {Id} actualizado por Usuario ID {UserId}.", idPago, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Pago no encontrado."); }
|
||||
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 { }
|
||||
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();
|
||||
if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open();
|
||||
using var transaction = connection.BeginTransaction();
|
||||
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) return (false, "Pago no encontrado.");
|
||||
|
||||
// Revertir el efecto en el saldo
|
||||
// Si fue "Recibido", el saldo disminuyó (montoAjusteSaldo fue +Monto). Al eliminar, revertimos sumando -Monto (o restando +Monto).
|
||||
// Si fue "Realizado", el saldo aumentó (montoAjusteSaldo fue -Monto). Al eliminar, revertimos sumando +Monto (o restando -Monto).
|
||||
decimal montoReversion = pagoExistente.TipoMovimiento == "Recibido" ? -pagoExistente.Monto : pagoExistente.Monto;
|
||||
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.");
|
||||
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.");
|
||||
@@ -206,13 +254,20 @@ namespace GestionIntegral.Api.Services.Contables
|
||||
_logger.LogInformation("PagoDistribuidor ID {Id} eliminado por Usuario ID {UserId}.", idPago, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Pago no encontrado."); }
|
||||
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 { }
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
164
Backend/GestionIntegral.Api/Services/Contables/SaldoService.cs
Normal file
164
Backend/GestionIntegral.Api/Services/Contables/SaldoService.cs
Normal file
@@ -0,0 +1,164 @@
|
||||
using GestionIntegral.Api.Data;
|
||||
using GestionIntegral.Api.Data.Repositories.Contables;
|
||||
using GestionIntegral.Api.Data.Repositories.Distribucion; // Para IDistribuidorRepository, ICanillaRepository
|
||||
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 SaldoService : ISaldoService
|
||||
{
|
||||
private readonly ISaldoRepository _saldoRepo;
|
||||
private readonly IDistribuidorRepository _distribuidorRepo; // Para nombres
|
||||
private readonly ICanillaRepository _canillaRepo; // Para nombres
|
||||
private readonly IEmpresaRepository _empresaRepo; // Para nombres
|
||||
private readonly DbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<SaldoService> _logger;
|
||||
|
||||
public SaldoService(
|
||||
ISaldoRepository saldoRepo,
|
||||
IDistribuidorRepository distribuidorRepo,
|
||||
ICanillaRepository canillaRepo,
|
||||
IEmpresaRepository empresaRepo,
|
||||
DbConnectionFactory connectionFactory,
|
||||
ILogger<SaldoService> logger)
|
||||
{
|
||||
_saldoRepo = saldoRepo;
|
||||
_distribuidorRepo = distribuidorRepo;
|
||||
_canillaRepo = canillaRepo;
|
||||
_empresaRepo = empresaRepo;
|
||||
_connectionFactory = connectionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private async Task<SaldoGestionDto> MapToGestionDto(Saldo saldo)
|
||||
{
|
||||
if (saldo == null) return null!;
|
||||
|
||||
string nombreDestinatario = "N/A";
|
||||
if (saldo.Destino == "Distribuidores")
|
||||
{
|
||||
var distData = await _distribuidorRepo.GetByIdAsync(saldo.IdDestino);
|
||||
nombreDestinatario = distData.Distribuidor?.Nombre ?? $"Dist. ID {saldo.IdDestino}";
|
||||
}
|
||||
else if (saldo.Destino == "Canillas")
|
||||
{
|
||||
var canData = await _canillaRepo.GetByIdAsync(saldo.IdDestino);
|
||||
nombreDestinatario = canData.Canilla?.NomApe ?? $"Can. ID {saldo.IdDestino}";
|
||||
}
|
||||
|
||||
var empresa = await _empresaRepo.GetByIdAsync(saldo.IdEmpresa);
|
||||
|
||||
return new SaldoGestionDto
|
||||
{
|
||||
IdSaldo = saldo.IdSaldo,
|
||||
Destino = saldo.Destino,
|
||||
IdDestino = saldo.IdDestino,
|
||||
NombreDestinatario = nombreDestinatario,
|
||||
IdEmpresa = saldo.IdEmpresa,
|
||||
NombreEmpresa = empresa?.Nombre ?? $"Emp. ID {saldo.IdEmpresa}",
|
||||
Monto = saldo.Monto,
|
||||
FechaUltimaModificacion = saldo.FechaUltimaModificacion
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<SaldoGestionDto>> ObtenerSaldosParaGestionAsync(string? destinoFilter, int? idDestinoFilter, int? idEmpresaFilter)
|
||||
{
|
||||
var saldos = await _saldoRepo.GetSaldosParaGestionAsync(destinoFilter, idDestinoFilter, idEmpresaFilter);
|
||||
var dtos = new List<SaldoGestionDto>();
|
||||
foreach (var saldo in saldos)
|
||||
{
|
||||
dtos.Add(await MapToGestionDto(saldo));
|
||||
}
|
||||
return dtos;
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error, SaldoGestionDto? SaldoActualizado)> RealizarAjusteManualSaldoAsync(AjusteSaldoRequestDto ajusteDto, int idUsuarioAjuste)
|
||||
{
|
||||
if (ajusteDto.MontoAjuste == 0)
|
||||
return (false, "El monto de ajuste no puede ser cero.", null);
|
||||
|
||||
// Validar existencia de Destino y Empresa
|
||||
if (ajusteDto.Destino == "Distribuidores")
|
||||
{
|
||||
if (await _distribuidorRepo.GetByIdSimpleAsync(ajusteDto.IdDestino) == null)
|
||||
return (false, "El distribuidor especificado no existe.", null);
|
||||
}
|
||||
else if (ajusteDto.Destino == "Canillas")
|
||||
{
|
||||
if (await _canillaRepo.GetByIdSimpleAsync(ajusteDto.IdDestino) == null)
|
||||
return (false, "El canillita especificado no existe.", null);
|
||||
} else {
|
||||
return (false, "Tipo de destino inválido.", null);
|
||||
}
|
||||
if (await _empresaRepo.GetByIdAsync(ajusteDto.IdEmpresa) == null)
|
||||
return (false, "La empresa especificada no existe.", null);
|
||||
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection.State != ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.OpenAsync(); else connection.Open(); }
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var saldoActual = await _saldoRepo.GetSaldoAsync(ajusteDto.Destino, ajusteDto.IdDestino, ajusteDto.IdEmpresa, transaction);
|
||||
if (saldoActual == null)
|
||||
{
|
||||
// Podríamos crear el saldo aquí si no existe y se quiere permitir un ajuste sobre un saldo nuevo.
|
||||
// O devolver error. Por ahora, error.
|
||||
transaction.Rollback();
|
||||
return (false, "No se encontró un saldo existente para el destinatario y empresa especificados.", null);
|
||||
}
|
||||
|
||||
decimal saldoAnterior = saldoActual.Monto;
|
||||
|
||||
bool modificado = await _saldoRepo.ModificarSaldoAsync(ajusteDto.Destino, ajusteDto.IdDestino, ajusteDto.IdEmpresa, ajusteDto.MontoAjuste, transaction);
|
||||
if (!modificado)
|
||||
{
|
||||
throw new DataException("No se pudo modificar el saldo principal.");
|
||||
}
|
||||
|
||||
// Obtener el saldo después de la modificación para el historial
|
||||
var saldoDespuesDeModificacion = await _saldoRepo.GetSaldoAsync(ajusteDto.Destino, ajusteDto.IdDestino, ajusteDto.IdEmpresa, transaction);
|
||||
if(saldoDespuesDeModificacion == null) throw new DataException("No se pudo obtener el saldo después de la modificación.");
|
||||
|
||||
|
||||
var historial = new SaldoAjusteHistorial
|
||||
{
|
||||
Destino = ajusteDto.Destino,
|
||||
IdDestino = ajusteDto.IdDestino,
|
||||
IdEmpresa = ajusteDto.IdEmpresa,
|
||||
MontoAjuste = ajusteDto.MontoAjuste,
|
||||
SaldoAnterior = saldoAnterior,
|
||||
SaldoNuevo = saldoDespuesDeModificacion.Monto, // saldoActual.Monto + ajusteDto.MontoAjuste,
|
||||
Justificacion = ajusteDto.Justificacion,
|
||||
FechaAjuste = DateTime.Now, // O UtcNow
|
||||
IdUsuarioAjuste = idUsuarioAjuste
|
||||
};
|
||||
await _saldoRepo.CreateSaldoAjusteHistorialAsync(historial, transaction);
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Ajuste manual de saldo realizado para {Destino} ID {IdDestino}, Empresa ID {IdEmpresa} por Usuario ID {IdUsuarioAjuste}. Monto: {MontoAjuste}",
|
||||
ajusteDto.Destino, ajusteDto.IdDestino, ajusteDto.IdEmpresa, idUsuarioAjuste, ajusteDto.MontoAjuste);
|
||||
|
||||
var saldoDtoActualizado = await MapToGestionDto(saldoDespuesDeModificacion);
|
||||
return (true, null, saldoDtoActualizado);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch (Exception rbEx){ _logger.LogError(rbEx, "Error en Rollback de RealizarAjusteManualSaldoAsync."); }
|
||||
_logger.LogError(ex, "Error en RealizarAjusteManualSaldoAsync.");
|
||||
return (false, $"Error interno al realizar el ajuste: {ex.Message}", null);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open) { if (connection is System.Data.Common.DbConnection dbConn) await dbConn.CloseAsync(); else connection.Close(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -54,11 +54,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CanillaDto>> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? soloActivos)
|
||||
public async Task<IEnumerable<CanillaDto>> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? esAccionista, bool? soloActivos)
|
||||
{
|
||||
var canillasData = await _canillaRepository.GetAllAsync(nomApeFilter, legajoFilter, 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 canillasData.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
|
||||
return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
|
||||
}
|
||||
|
||||
public async Task<CanillaDto?> ObtenerPorIdAsync(int id)
|
||||
@@ -81,11 +81,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
}
|
||||
if (createDto.Empresa != 0) // Solo validar empresa si no es 0
|
||||
{
|
||||
var empresa = await _empresaRepository.GetByIdAsync(createDto.Empresa);
|
||||
if(empresa == null)
|
||||
{
|
||||
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
|
||||
@@ -122,7 +122,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
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)
|
||||
@@ -131,12 +131,20 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
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
|
||||
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);
|
||||
@@ -144,7 +152,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch {}
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error CrearAsync Canilla: {NomApe}", createDto.NomApe);
|
||||
return (null, $"Error interno al crear el canillita: {ex.Message}");
|
||||
}
|
||||
@@ -165,11 +173,11 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
}
|
||||
if (updateDto.Empresa != 0) // Solo validar empresa si no es 0
|
||||
{
|
||||
var empresa = await _empresaRepository.GetByIdAsync(updateDto.Empresa);
|
||||
if(empresa == null)
|
||||
{
|
||||
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
|
||||
@@ -200,18 +208,19 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
try
|
||||
{
|
||||
var actualizado = await _canillaRepository.UpdateAsync(canillaExistente, idUsuario, transaction);
|
||||
if (!actualizado) throw new DataException("Error al actualizar el canillita.");
|
||||
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 {}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
try { transaction.Rollback(); } catch { }
|
||||
return (false, "Canillita no encontrado durante la actualización.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch {}
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error ActualizarAsync Canilla ID: {IdCanilla}", id);
|
||||
return (false, $"Error interno al actualizar el canillita: {ex.Message}");
|
||||
}
|
||||
@@ -240,13 +249,14 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
_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 {}
|
||||
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 {}
|
||||
try { transaction.Rollback(); } catch { }
|
||||
_logger.LogError(ex, "Error ToggleBajaAsync Canilla ID: {IdCanilla}", id);
|
||||
return (false, $"Error interno al cambiar estado de baja: {ex.Message}");
|
||||
}
|
||||
|
||||
@@ -66,11 +66,31 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!);
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<DistribuidorDropdownDto>> GetAllDropdownAsync()
|
||||
{
|
||||
var data = await _distribuidorRepository.GetAllDropdownAsync();
|
||||
// Asegurar que el resultado no sea nulo y no contiene elementos nulos
|
||||
if (data == null)
|
||||
{
|
||||
return new List<DistribuidorDropdownDto>
|
||||
{
|
||||
new DistribuidorDropdownDto { IdDistribuidor = 0, Nombre = "No hay distribuidores disponibles" }
|
||||
};
|
||||
}
|
||||
return data.Where(x => x != null)!;
|
||||
}
|
||||
|
||||
public async Task<DistribuidorDto?> ObtenerPorIdAsync(int id)
|
||||
{
|
||||
var data = await _distribuidorRepository.GetByIdAsync(id);
|
||||
// MapToDto ahora devuelve DistribuidorDto?
|
||||
return MapToDto(data);
|
||||
}
|
||||
|
||||
public async Task<DistribuidorLookupDto?> ObtenerLookupPorIdAsync(int id)
|
||||
{
|
||||
var data = await _distribuidorRepository.ObtenerLookupPorIdAsync(id);
|
||||
return data;
|
||||
}
|
||||
|
||||
public async Task<(DistribuidorDto? Distribuidor, string? Error)> CrearAsync(CreateDistribuidorDto createDto, int idUsuario)
|
||||
|
||||
@@ -42,10 +42,22 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
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
|
||||
// El repositorio ya devuelve solo las activas si es necesario
|
||||
var empresa = await _empresaRepository.GetByIdAsync(id);
|
||||
if (empresa == null) return null;
|
||||
// Mapeo Entidad -> DTO
|
||||
@@ -57,6 +69,19 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
@@ -234,5 +259,5 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
}
|
||||
// --- Fin Transacción ---
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
{
|
||||
public interface ICanillaService
|
||||
{
|
||||
Task<IEnumerable<CanillaDto>> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? soloActivos);
|
||||
Task<IEnumerable<CanillaDto>> ObtenerTodosAsync(string? nomApeFilter, int? legajoFilter, bool? esAccionista, bool? soloActivos);
|
||||
Task<CanillaDto?> ObtenerPorIdAsync(int id);
|
||||
Task<(CanillaDto? Canilla, string? Error)> CrearAsync(CreateCanillaDto createDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateCanillaDto updateDto, int idUsuario);
|
||||
|
||||
@@ -11,5 +11,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
Task<(DistribuidorDto? Distribuidor, string? Error)> CrearAsync(CreateDistribuidorDto createDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateDistribuidorDto updateDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario);
|
||||
Task<IEnumerable<DistribuidorDropdownDto>> GetAllDropdownAsync();
|
||||
Task<DistribuidorLookupDto?> ObtenerLookupPorIdAsync(int id);
|
||||
}
|
||||
}
|
||||
@@ -11,5 +11,7 @@ namespace GestionIntegral.Api.Services.Distribucion
|
||||
Task<(EmpresaDto? Empresa, string? Error)> CrearAsync(CreateEmpresaDto createDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> ActualizarAsync(int id, UpdateEmpresaDto updateDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> EliminarAsync(int id, int idUsuario);
|
||||
Task<IEnumerable<EmpresaDropdownDto>> ObtenerParaDropdown();
|
||||
Task<EmpresaLookupDto?> ObtenerLookupPorIdAsync(int id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using GestionIntegral.Api.Dtos.Distribucion;
|
||||
using GestionIntegral.Api.Dtos.Reportes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Distribucion
|
||||
{
|
||||
public interface INovedadCanillaService
|
||||
{
|
||||
Task<IEnumerable<NovedadCanillaDto>> ObtenerPorCanillaAsync(int idCanilla, DateTime? fechaDesde, DateTime? fechaHasta);
|
||||
Task<NovedadCanillaDto?> ObtenerPorIdAsync(int idNovedad);
|
||||
Task<(NovedadCanillaDto? Novedad, string? Error)> CrearAsync(CreateNovedadCanillaDto createDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> ActualizarAsync(int idNovedad, UpdateNovedadCanillaDto updateDto, int idUsuario);
|
||||
Task<(bool Exito, string? Error)> EliminarAsync(int idNovedad, int idUsuario);
|
||||
Task<IEnumerable<NovedadesCanillasReporteDto>> ObtenerReporteNovedadesAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta);
|
||||
Task<IEnumerable<CanillaGananciaReporteDto>> ObtenerReporteGananciasAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
// En Services/Distribucion (o donde corresponda)
|
||||
using GestionIntegral.Api.Data; // Para DbConnectionFactory
|
||||
using GestionIntegral.Api.Data.Repositories.Distribucion;
|
||||
using GestionIntegral.Api.Dtos.Distribucion;
|
||||
using GestionIntegral.Api.Dtos.Reportes;
|
||||
using GestionIntegral.Api.Models.Distribucion; // Asegúrate que el modelo Canilla tenga NomApe
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Data; // Para IDbTransaction
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace GestionIntegral.Api.Services.Distribucion
|
||||
{
|
||||
public class NovedadCanillaService : INovedadCanillaService
|
||||
{
|
||||
private readonly INovedadCanillaRepository _novedadRepository;
|
||||
private readonly ICanillaRepository _canillaRepository;
|
||||
private readonly DbConnectionFactory _connectionFactory;
|
||||
private readonly ILogger<NovedadCanillaService> _logger;
|
||||
|
||||
public NovedadCanillaService(
|
||||
INovedadCanillaRepository novedadRepository,
|
||||
ICanillaRepository canillaRepository,
|
||||
DbConnectionFactory connectionFactory,
|
||||
ILogger<NovedadCanillaService> logger)
|
||||
{
|
||||
_novedadRepository = novedadRepository;
|
||||
_canillaRepository = canillaRepository;
|
||||
_connectionFactory = connectionFactory;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private NovedadCanillaDto MapToDto((NovedadCanilla Novedad, string NombreCanilla) data)
|
||||
{
|
||||
return new NovedadCanillaDto
|
||||
{
|
||||
IdNovedad = data.Novedad.IdNovedad,
|
||||
IdCanilla = data.Novedad.IdCanilla,
|
||||
NombreCanilla = data.NombreCanilla, // Viene de la tupla en GetByCanillaAsync
|
||||
Fecha = data.Novedad.Fecha,
|
||||
Detalle = data.Novedad.Detalle
|
||||
};
|
||||
}
|
||||
private NovedadCanillaDto MapToDto(NovedadCanilla data, string nombreCanilla)
|
||||
{
|
||||
return new NovedadCanillaDto
|
||||
{
|
||||
IdNovedad = data.IdNovedad,
|
||||
IdCanilla = data.IdCanilla,
|
||||
NombreCanilla = nombreCanilla,
|
||||
Fecha = data.Fecha,
|
||||
Detalle = data.Detalle
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NovedadCanillaDto>> ObtenerPorCanillaAsync(int idCanilla, DateTime? fechaDesde, DateTime? fechaHasta)
|
||||
{
|
||||
var data = await _novedadRepository.GetByCanillaAsync(idCanilla, fechaDesde, fechaHasta);
|
||||
return data.Select(MapToDto);
|
||||
}
|
||||
|
||||
public async Task<NovedadCanillaDto?> ObtenerPorIdAsync(int idNovedad)
|
||||
{
|
||||
var novedad = await _novedadRepository.GetByIdAsync(idNovedad);
|
||||
if (novedad == null) return null;
|
||||
|
||||
// Asumiendo que _canillaRepository.GetByIdAsync devuelve una tupla (Canilla? Canilla, ...)
|
||||
// O un DTO CanillaDto que tiene NomApe
|
||||
var canillaDataResult = await _canillaRepository.GetByIdAsync(novedad.IdCanilla);
|
||||
|
||||
// Ajusta esto según lo que realmente devuelva GetByIdAsync
|
||||
// Si devuelve CanillaDto:
|
||||
// string nombreCanilla = canillaDataResult?.NomApe ?? "Desconocido";
|
||||
// Si devuelve la tupla (Canilla? Canilla, string? NombreZona, string? NombreEmpresa):
|
||||
string nombreCanilla = canillaDataResult.Canilla?.NomApe ?? "Desconocido";
|
||||
|
||||
return MapToDto(novedad, nombreCanilla);
|
||||
}
|
||||
|
||||
public async Task<(NovedadCanillaDto? Novedad, string? Error)> CrearAsync(CreateNovedadCanillaDto createDto, int idUsuario)
|
||||
{
|
||||
// Asegúrate que GetByIdSimpleAsync devuelva un objeto Canilla o algo con NomApe
|
||||
var canilla = await _canillaRepository.GetByIdSimpleAsync(createDto.IdCanilla);
|
||||
if (canilla == null)
|
||||
{
|
||||
return (null, "El canillita especificado no existe.");
|
||||
}
|
||||
|
||||
var nuevaNovedad = new NovedadCanilla
|
||||
{
|
||||
IdCanilla = createDto.IdCanilla,
|
||||
Fecha = createDto.Fecha.Date,
|
||||
Detalle = createDto.Detalle
|
||||
};
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
// Abre la conexión explícitamente si no se usa una transacción externa
|
||||
if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
|
||||
{
|
||||
await dbConn.OpenAsync();
|
||||
}
|
||||
else if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var creada = await _novedadRepository.CreateAsync(nuevaNovedad, idUsuario, transaction);
|
||||
if (creada == null)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return (null, "Error al guardar la novedad en la base de datos.");
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Novedad ID {IdNovedad} para Canilla ID {IdCanilla} creada por Usuario ID {UserId}.", creada.IdNovedad, creada.IdCanilla, idUsuario);
|
||||
// Asegúrate que 'canilla.NomApe' sea accesible. Si GetByIdSimpleAsync devuelve la entidad Canilla, esto está bien.
|
||||
return (MapToDto(creada, canilla.NomApe ?? "Canilla sin nombre"), null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en CrearAsync NovedadCanilla."); }
|
||||
_logger.LogError(ex, "Error CrearAsync NovedadCanilla para Canilla ID: {IdCanilla}", createDto.IdCanilla);
|
||||
return (null, $"Error interno al crear la novedad: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open) connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error)> ActualizarAsync(int idNovedad, UpdateNovedadCanillaDto updateDto, int idUsuario)
|
||||
{
|
||||
var existente = await _novedadRepository.GetByIdAsync(idNovedad);
|
||||
if (existente == null)
|
||||
{
|
||||
return (false, "Novedad no encontrada.");
|
||||
}
|
||||
|
||||
existente.Detalle = updateDto.Detalle;
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
|
||||
{
|
||||
await dbConn.OpenAsync();
|
||||
}
|
||||
else if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var actualizado = await _novedadRepository.UpdateAsync(existente, idUsuario, transaction);
|
||||
if (!actualizado)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return (false, "Error al actualizar la novedad en la base de datos.");
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Novedad ID {IdNovedad} actualizada por Usuario ID {UserId}.", idNovedad, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en ActualizarAsync NovedadCanilla."); }
|
||||
_logger.LogError(ex, "Error ActualizarAsync NovedadCanilla ID: {IdNovedad}", idNovedad);
|
||||
return (false, $"Error interno al actualizar la novedad: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open) connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(bool Exito, string? Error)> EliminarAsync(int idNovedad, int idUsuario)
|
||||
{
|
||||
var existente = await _novedadRepository.GetByIdAsync(idNovedad);
|
||||
if (existente == null)
|
||||
{
|
||||
return (false, "Novedad no encontrada.");
|
||||
}
|
||||
|
||||
using var connection = _connectionFactory.CreateConnection();
|
||||
if (connection is System.Data.Common.DbConnection dbConn && connection.State != ConnectionState.Open)
|
||||
{
|
||||
await dbConn.OpenAsync();
|
||||
}
|
||||
else if (connection.State != ConnectionState.Open)
|
||||
{
|
||||
connection.Open();
|
||||
}
|
||||
using var transaction = connection.BeginTransaction();
|
||||
try
|
||||
{
|
||||
var eliminado = await _novedadRepository.DeleteAsync(idNovedad, idUsuario, transaction);
|
||||
if (!eliminado)
|
||||
{
|
||||
transaction.Rollback();
|
||||
return (false, "Error al eliminar la novedad de la base de datos.");
|
||||
}
|
||||
transaction.Commit();
|
||||
_logger.LogInformation("Novedad ID {IdNovedad} eliminada por Usuario ID {UserId}.", idNovedad, idUsuario);
|
||||
return (true, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
try { transaction.Rollback(); } catch (Exception rbEx) { _logger.LogError(rbEx, "Error durante Rollback en EliminarAsync NovedadCanilla."); }
|
||||
_logger.LogError(ex, "Error EliminarAsync NovedadCanilla ID: {IdNovedad}", idNovedad);
|
||||
return (false, $"Error interno al eliminar la novedad: {ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (connection.State == ConnectionState.Open) connection.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NovedadesCanillasReporteDto>> ObtenerReporteNovedadesAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
|
||||
{
|
||||
// Podría añadir validaciones o lógica de negocio adicional si fuera necesario
|
||||
// antes de llamar al repositorio. Por ahora, es una llamada directa.
|
||||
try
|
||||
{
|
||||
return await _novedadRepository.GetReporteNovedadesAsync(idEmpresa, fechaDesde, fechaHasta);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener datos para el reporte de novedades de canillitas. Empresa: {IdEmpresa}, Desde: {FechaDesde}, Hasta: {FechaHasta}", idEmpresa, fechaDesde, fechaHasta);
|
||||
// Podría relanzar o devolver una lista vacía con un mensaje de error,
|
||||
// dependiendo de cómo quiera manejar los errores en la capa de servicio.
|
||||
// Por simplicidad, relanzamos para que el controlador lo maneje.
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<CanillaGananciaReporteDto>> ObtenerReporteGananciasAsync(int idEmpresa, DateTime fechaDesde, DateTime fechaHasta)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _novedadRepository.GetReporteGananciasAsync(idEmpresa, fechaDesde, fechaHasta);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener datos para el reporte de ganancias de canillitas. Empresa: {IdEmpresa}, Desde: {FechaDesde}, Hasta: {FechaHasta}", idEmpresa, fechaDesde, fechaHasta);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,5 +69,8 @@ namespace GestionIntegral.Api.Services.Reportes
|
||||
IEnumerable<LiquidacionCanillaGananciaDto> Ganancias,
|
||||
string? Error
|
||||
)> ObtenerDatosTicketLiquidacionAsync(DateTime fecha, int idCanilla);
|
||||
|
||||
Task<(IEnumerable<ListadoDistCanMensualDiariosDto> Data, string? Error)> ObtenerReporteMensualDiariosAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
||||
Task<(IEnumerable<ListadoDistCanMensualPubDto> Data, string? Error)> ObtenerReporteMensualPorPublicacionAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista);
|
||||
}
|
||||
}
|
||||
@@ -490,5 +490,33 @@ namespace GestionIntegral.Api.Services.Reportes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<ListadoDistCanMensualDiariosDto> Data, string? Error)> ObtenerReporteMensualDiariosAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await _reportesRepository.GetReporteMensualDiariosAsync(fechaDesde, fechaHasta, esAccionista);
|
||||
return (data, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener reporte mensual canillitas (diarios).");
|
||||
return (Enumerable.Empty<ListadoDistCanMensualDiariosDto>(), "Error al obtener datos del reporte (diarios).");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<(IEnumerable<ListadoDistCanMensualPubDto> Data, string? Error)> ObtenerReporteMensualPorPublicacionAsync(DateTime fechaDesde, DateTime fechaHasta, bool esAccionista)
|
||||
{
|
||||
try
|
||||
{
|
||||
var data = await _reportesRepository.GetReporteMensualPorPublicacionAsync(fechaDesde, fechaHasta, esAccionista);
|
||||
return (data, null);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error al obtener reporte mensual canillitas (por publicación).");
|
||||
return (Enumerable.Empty<ListadoDistCanMensualPubDto>(), "Error al obtener datos del reporte (por publicación).");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user