226 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			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}");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| } |