2025-05-23 15:47:39 -03:00
using GestionIntegral.Api.Data ;
using GestionIntegral.Api.Data.Repositories.Contables ;
using GestionIntegral.Api.Data.Repositories.Distribucion ;
2025-06-09 19:37:07 -03:00
using GestionIntegral.Api.Dtos.Auditoria ;
2025-05-23 15:47:39 -03:00
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" )
{
2025-06-06 18:33:09 -03:00
var canData = await _canillaRepo . GetByIdAsync ( nota . IdDestino ) ;
2025-05-23 15:47:39 -03:00
nombreDestinatario = canData . Canilla ? . NomApe ? ? "Canillita Desconocido" ;
}
2025-06-09 19:37:07 -03:00
2025-05-23 15:47:39 -03:00
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 )
{
if ( createDto . Destino = = "Distribuidores" )
{
if ( await _distribuidorRepo . GetByIdSimpleAsync ( createDto . IdDestino ) = = null )
return ( null , "El distribuidor especificado no existe." ) ;
}
else if ( createDto . Destino = = "Canillas" )
{
2025-06-09 19:37:07 -03:00
if ( await _canillaRepo . GetByIdSimpleAsync ( createDto . IdDestino ) = = null )
2025-05-23 15:47:39 -03:00
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 ( ) ;
2025-06-06 18:33:09 -03:00
IDbTransaction ? transaction = null ;
2025-05-23 15:47:39 -03:00
try
{
2025-06-06 18:33:09 -03:00
if ( connection . State ! = ConnectionState . Open )
{
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . OpenAsync ( ) ; else connection . Open ( ) ;
}
transaction = connection . BeginTransaction ( ) ;
2025-06-09 19:37:07 -03:00
2025-05-23 15:47:39 -03:00
var notaCreada = await _notaRepo . CreateAsync ( nuevaNota , idUsuario , transaction ) ;
if ( notaCreada = = null ) throw new DataException ( "Error al registrar la nota." ) ;
2025-06-06 18:33:09 -03:00
decimal montoParaSaldo ;
2025-06-09 19:37:07 -03:00
if ( createDto . Tipo = = "Credito" )
2025-06-06 18:33:09 -03:00
{
montoParaSaldo = - createDto . Monto ;
}
2025-06-09 19:37:07 -03:00
else
2025-06-06 18:33:09 -03:00
{
montoParaSaldo = createDto . Monto ;
}
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
bool saldoActualizado = await _saldoRepo . ModificarSaldoAsync ( notaCreada . Destino , notaCreada . IdDestino , notaCreada . IdEmpresa , montoParaSaldo , transaction ) ;
2025-05-23 15:47:39 -03:00
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 )
{
2025-06-06 18:33:09 -03:00
try { transaction ? . Rollback ( ) ; } catch ( Exception rbEx ) { _logger . LogError ( rbEx , "Error en Rollback de CrearAsync NotaCreditoDebito." ) ; }
2025-05-23 15:47:39 -03:00
_logger . LogError ( ex , "Error CrearAsync NotaCreditoDebito." ) ;
return ( null , $"Error interno: {ex.Message}" ) ;
}
2025-06-06 18:33:09 -03:00
finally
{
if ( connection . State = = ConnectionState . Open )
{
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . CloseAsync ( ) ; else connection . Close ( ) ;
}
}
2025-05-23 15:47:39 -03:00
}
public async Task < ( bool Exito , string? Error ) > ActualizarAsync ( int idNota , UpdateNotaDto updateDto , int idUsuario )
{
using var connection = _connectionFactory . CreateConnection ( ) ;
2025-06-06 18:33:09 -03:00
IDbTransaction ? transaction = null ;
2025-05-23 15:47:39 -03:00
try
{
2025-06-06 18:33:09 -03:00
if ( connection . State ! = ConnectionState . Open )
{
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . OpenAsync ( ) ; else connection . Open ( ) ;
}
transaction = connection . BeginTransaction ( ) ;
2025-06-09 19:37:07 -03:00
var notaExistente = await _notaRepo . GetByIdAsync ( idNota ) ;
if ( notaExistente = = null )
2025-06-06 18:33:09 -03:00
{
transaction . Rollback ( ) ;
return ( false , "Nota no encontrada." ) ;
}
2025-06-09 19:37:07 -03:00
2025-06-06 18:33:09 -03:00
decimal impactoOriginalSaldo = notaExistente . Tipo = = "Credito" ? - notaExistente . Monto : notaExistente . Monto ;
decimal impactoNuevoSaldo = notaExistente . Tipo = = "Credito" ? - updateDto . Monto : updateDto . Monto ;
decimal diferenciaAjusteSaldo = impactoNuevoSaldo - impactoOriginalSaldo ;
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
var notaParaActualizarEnRepo = new NotaCreditoDebito
{
IdNota = notaExistente . IdNota ,
2025-06-09 19:37:07 -03:00
Destino = notaExistente . Destino ,
IdDestino = notaExistente . IdDestino ,
Referencia = notaExistente . Referencia ,
Tipo = notaExistente . Tipo ,
Fecha = notaExistente . Fecha ,
Monto = updateDto . Monto ,
Observaciones = updateDto . Observaciones ,
IdEmpresa = notaExistente . IdEmpresa
2025-06-06 18:33:09 -03:00
} ;
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
var actualizado = await _notaRepo . UpdateAsync ( notaParaActualizarEnRepo , idUsuario , transaction ) ;
if ( ! actualizado ) throw new DataException ( "Error al actualizar la nota en la base de datos." ) ;
2025-05-23 15:47:39 -03:00
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 ) ;
}
2025-06-06 18:33:09 -03:00
catch ( KeyNotFoundException ) { try { transaction ? . Rollback ( ) ; } catch ( Exception rbEx ) { _logger . LogError ( rbEx , "Error en Rollback de ActualizarAsync NotaCreditoDebito (KeyNotFound)." ) ; } return ( false , "Nota no encontrada." ) ; }
2025-05-23 15:47:39 -03:00
catch ( Exception ex )
{
2025-06-06 18:33:09 -03:00
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 ) ;
2025-05-23 15:47:39 -03:00
return ( false , $"Error interno: {ex.Message}" ) ;
}
2025-06-06 18:33:09 -03:00
finally
{
if ( connection . State = = ConnectionState . Open )
{
2025-06-09 19:37:07 -03:00
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . CloseAsync ( ) ; else connection . Close ( ) ;
2025-06-06 18:33:09 -03:00
}
}
2025-05-23 15:47:39 -03:00
}
public async Task < ( bool Exito , string? Error ) > EliminarAsync ( int idNota , int idUsuario )
{
using var connection = _connectionFactory . CreateConnection ( ) ;
2025-06-06 18:33:09 -03:00
IDbTransaction ? transaction = null ;
2025-05-23 15:47:39 -03:00
try
{
2025-06-06 18:33:09 -03:00
if ( connection . State ! = ConnectionState . Open )
{
2025-06-09 19:37:07 -03:00
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . OpenAsync ( ) ; else connection . Open ( ) ;
2025-06-06 18:33:09 -03:00
}
transaction = connection . BeginTransaction ( ) ;
2025-05-23 15:47:39 -03:00
2025-06-06 18:33:09 -03:00
var notaExistente = await _notaRepo . GetByIdAsync ( idNota ) ;
2025-06-09 19:37:07 -03:00
if ( notaExistente = = null )
2025-06-06 18:33:09 -03:00
{
transaction . Rollback ( ) ;
return ( false , "Nota no encontrada." ) ;
}
2025-06-09 19:37:07 -03:00
2025-06-06 18:33:09 -03:00
decimal montoReversion = notaExistente . Tipo = = "Credito" ? notaExistente . Monto : - notaExistente . Monto ;
2025-05-23 15:47:39 -03:00
var eliminado = await _notaRepo . DeleteAsync ( idNota , idUsuario , transaction ) ;
2025-06-06 18:33:09 -03:00
if ( ! eliminado ) throw new DataException ( "Error al eliminar la nota de la base de datos." ) ;
2025-05-23 15:47:39 -03:00
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 ) ;
}
2025-06-06 18:33:09 -03:00
catch ( KeyNotFoundException ) { try { transaction ? . Rollback ( ) ; } catch ( Exception rbEx ) { _logger . LogError ( rbEx , "Error en Rollback de EliminarAsync NotaCreditoDebito (KeyNotFound)." ) ; } return ( false , "Nota no encontrada." ) ; }
2025-05-23 15:47:39 -03:00
catch ( Exception ex )
{
2025-06-06 18:33:09 -03:00
try { transaction ? . Rollback ( ) ; } catch ( Exception rbEx ) { _logger . LogError ( rbEx , "Error en Rollback de EliminarAsync NotaCreditoDebito." ) ; }
2025-05-23 15:47:39 -03:00
_logger . LogError ( ex , "Error EliminarAsync NotaC/D ID: {Id}" , idNota ) ;
return ( false , $"Error interno: {ex.Message}" ) ;
}
2025-06-06 18:33:09 -03:00
finally
{
if ( connection . State = = ConnectionState . Open )
{
if ( connection is System . Data . Common . DbConnection dbConn ) await dbConn . CloseAsync ( ) ; else connection . Close ( ) ;
}
}
2025-05-23 15:47:39 -03:00
}
2025-06-09 19:37:07 -03:00
public async Task < IEnumerable < NotaCreditoDebitoHistorialDto > > ObtenerHistorialAsync (
DateTime ? fechaDesde , DateTime ? fechaHasta ,
int? idUsuarioModifico , string? tipoModificacion ,
int? idNotaAfectada )
{
var historialData = await _notaRepo . GetHistorialAsync ( fechaDesde , fechaHasta , idUsuarioModifico , tipoModificacion , idNotaAfectada ) ;
return historialData . Select ( h = > new NotaCreditoDebitoHistorialDto
{
Id_Nota = h . Historial . Id_Nota ,
Destino = h . Historial . Destino ,
Id_Destino = h . Historial . Id_Destino ,
Referencia = h . Historial . Referencia ,
Tipo = h . Historial . Tipo ,
Fecha = h . Historial . Fecha , // Fecha original de la nota
Monto = h . Historial . Monto ,
Observaciones = h . Historial . Observaciones ,
Id_Empresa = h . Historial . Id_Empresa ,
Id_Usuario = h . Historial . Id_Usuario ,
NombreUsuarioModifico = h . NombreUsuarioModifico ,
FechaMod = h . Historial . FechaMod , // Fecha de la auditoría
TipoMod = h . Historial . TipoMod
} ) . ToList ( ) ;
}
2025-05-23 15:47:39 -03:00
}
}