Files
GestionIntegralWeb/Backend/GestionIntegral.Api/Services/Contables/NotaCreditoDebitoService.cs

226 lines
11 KiB
C#

using GestionIntegral.Api.Data;
using GestionIntegral.Api.Data.Repositories.Contables;
using GestionIntegral.Api.Data.Repositories.Distribucion;
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 NotaCreditoDebitoService : INotaCreditoDebitoService
{
private readonly INotaCreditoDebitoRepository _notaRepo;
private readonly IDistribuidorRepository _distribuidorRepo;
private readonly ICanillaRepository _canillaRepo;
private readonly IEmpresaRepository _empresaRepo;
private readonly ISaldoRepository _saldoRepo;
private readonly DbConnectionFactory _connectionFactory;
private readonly ILogger<NotaCreditoDebitoService> _logger;
public NotaCreditoDebitoService(
INotaCreditoDebitoRepository notaRepo,
IDistribuidorRepository distribuidorRepo,
ICanillaRepository canillaRepo,
IEmpresaRepository empresaRepo,
ISaldoRepository saldoRepo,
DbConnectionFactory connectionFactory,
ILogger<NotaCreditoDebitoService> logger)
{
_notaRepo = notaRepo;
_distribuidorRepo = distribuidorRepo;
_canillaRepo = canillaRepo;
_empresaRepo = empresaRepo;
_saldoRepo = saldoRepo;
_connectionFactory = connectionFactory;
_logger = logger;
}
private async Task<NotaCreditoDebitoDto> MapToDto(NotaCreditoDebito nota)
{
if (nota == null) return null!;
string nombreDestinatario = "N/A";
if (nota.Destino == "Distribuidores")
{
var distData = await _distribuidorRepo.GetByIdAsync(nota.IdDestino);
nombreDestinatario = distData.Distribuidor?.Nombre ?? "Distribuidor Desconocido";
}
else if (nota.Destino == "Canillas")
{
var canData = await _canillaRepo.GetByIdAsync(nota.IdDestino); // Asumiendo que GetByIdAsync devuelve una tupla
nombreDestinatario = canData.Canilla?.NomApe ?? "Canillita Desconocido";
}
var empresa = await _empresaRepo.GetByIdAsync(nota.IdEmpresa);
return new NotaCreditoDebitoDto
{
IdNota = nota.IdNota,
Destino = nota.Destino,
IdDestino = nota.IdDestino,
NombreDestinatario = nombreDestinatario,
Referencia = nota.Referencia,
Tipo = nota.Tipo,
Fecha = nota.Fecha.ToString("yyyy-MM-dd"),
Monto = nota.Monto,
Observaciones = nota.Observaciones,
IdEmpresa = nota.IdEmpresa,
NombreEmpresa = empresa?.Nombre ?? "Empresa Desconocida"
};
}
public async Task<IEnumerable<NotaCreditoDebitoDto>> ObtenerTodosAsync(
DateTime? fechaDesde, DateTime? fechaHasta,
string? destino, int? idDestino, int? idEmpresa, string? tipoNota)
{
var notas = await _notaRepo.GetAllAsync(fechaDesde, fechaHasta, destino, idDestino, idEmpresa, tipoNota);
var dtos = new List<NotaCreditoDebitoDto>();
foreach (var nota in notas)
{
dtos.Add(await MapToDto(nota));
}
return dtos;
}
public async Task<NotaCreditoDebitoDto?> ObtenerPorIdAsync(int idNota)
{
var nota = await _notaRepo.GetByIdAsync(idNota);
return nota == null ? null : await MapToDto(nota);
}
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)
return (null, "El distribuidor especificado no existe.");
}
else if (createDto.Destino == "Canillas")
{
if (await _canillaRepo.GetByIdSimpleAsync(createDto.IdDestino) == null) // Asumiendo GetByIdSimpleAsync en ICanillaRepository
return (null, "El canillita especificado no existe.");
}
else { return (null, "Tipo de destino inválido."); }
if (await _empresaRepo.GetByIdAsync(createDto.IdEmpresa) == null)
return (null, "La empresa especificada no existe.");
var nuevaNota = new NotaCreditoDebito
{
Destino = createDto.Destino,
IdDestino = createDto.IdDestino,
Referencia = createDto.Referencia,
Tipo = createDto.Tipo,
Fecha = createDto.Fecha.Date,
Monto = createDto.Monto,
Observaciones = createDto.Observaciones,
IdEmpresa = createDto.IdEmpresa
};
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 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;
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync(notaCreada.Destino, notaCreada.IdDestino, notaCreada.IdEmpresa, montoAjusteSaldo, transaction);
if (!saldoActualizado) throw new DataException($"Error al actualizar el saldo para {notaCreada.Destino} ID {notaCreada.IdDestino}.");
transaction.Commit();
_logger.LogInformation("NotaC/D ID {Id} creada y saldo afectado por Usuario ID {UserId}.", notaCreada.IdNota, idUsuario);
return (await MapToDto(notaCreada), null);
}
catch (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error CrearAsync NotaCreditoDebito.");
return (null, $"Error interno: {ex.Message}");
}
}
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();
try
{
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
if (notaExistente == null) return (false, "Nota no encontrada.");
// 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;
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.");
if (diferenciaAjusteSaldo != 0)
{
bool saldoActualizado = await _saldoRepo.ModificarSaldoAsync(notaExistente.Destino, notaExistente.IdDestino, notaExistente.IdEmpresa, diferenciaAjusteSaldo, transaction);
if (!saldoActualizado) throw new DataException("Error al ajustar el saldo tras la actualización de la nota.");
}
transaction.Commit();
_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 (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error ActualizarAsync NotaC/D ID: {Id}", idNota);
return (false, $"Error interno: {ex.Message}");
}
}
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();
try
{
var notaExistente = await _notaRepo.GetByIdAsync(idNota);
if (notaExistente == null) return (false, "Nota no encontrada.");
// Revertir el efecto en el saldo
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.");
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.");
transaction.Commit();
_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 (Exception ex)
{
try { transaction.Rollback(); } catch { }
_logger.LogError(ex, "Error EliminarAsync NotaC/D ID: {Id}", idNota);
return (false, $"Error interno: {ex.Message}");
}
}
}
}