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}"); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |