234 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			234 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
|  | using GestionIntegral.Api.Data; | ||
|  | using GestionIntegral.Api.Data.Repositories.Distribucion; | ||
|  | using GestionIntegral.Api.Dtos.Distribucion; | ||
|  | using GestionIntegral.Api.Models.Distribucion; | ||
|  | using Microsoft.Extensions.Logging; | ||
|  | using System; | ||
|  | using System.Collections.Generic; | ||
|  | using System.Data; | ||
|  | using System.Linq; | ||
|  | using System.Threading.Tasks; | ||
|  | 
 | ||
|  | namespace GestionIntegral.Api.Services.Distribucion | ||
|  | { | ||
|  |     public class PorcPagoService : IPorcPagoService | ||
|  |     { | ||
|  |         private readonly IPorcPagoRepository _porcPagoRepository; | ||
|  |         private readonly IPublicacionRepository _publicacionRepository; | ||
|  |         private readonly IDistribuidorRepository _distribuidorRepository; | ||
|  |         private readonly DbConnectionFactory _connectionFactory; | ||
|  |         private readonly ILogger<PorcPagoService> _logger; | ||
|  | 
 | ||
|  |         public PorcPagoService( | ||
|  |             IPorcPagoRepository porcPagoRepository, | ||
|  |             IPublicacionRepository publicacionRepository, | ||
|  |             IDistribuidorRepository distribuidorRepository, | ||
|  |             DbConnectionFactory connectionFactory, | ||
|  |             ILogger<PorcPagoService> logger) | ||
|  |         { | ||
|  |             _porcPagoRepository = porcPagoRepository; | ||
|  |             _publicacionRepository = publicacionRepository; | ||
|  |             _distribuidorRepository = distribuidorRepository; | ||
|  |             _connectionFactory = connectionFactory; | ||
|  |             _logger = logger; | ||
|  |         } | ||
|  | 
 | ||
|  |         private PorcPagoDto MapToDto((PorcPago PorcPago, string NombreDistribuidor) data) => new PorcPagoDto | ||
|  |         { | ||
|  |             IdPorcentaje = data.PorcPago.IdPorcentaje, | ||
|  |             IdPublicacion = data.PorcPago.IdPublicacion, | ||
|  |             IdDistribuidor = data.PorcPago.IdDistribuidor, | ||
|  |             NombreDistribuidor = data.NombreDistribuidor, | ||
|  |             VigenciaD = data.PorcPago.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |             VigenciaH = data.PorcPago.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |             Porcentaje = data.PorcPago.Porcentaje | ||
|  |         }; | ||
|  |          | ||
|  |         private async Task<PorcPagoDto> MapToDtoWithLookup(PorcPago porcPago) | ||
|  |         { | ||
|  |             var distribuidorData = await _distribuidorRepository.GetByIdAsync(porcPago.IdDistribuidor); | ||
|  |             return new PorcPagoDto | ||
|  |             { | ||
|  |                 IdPorcentaje = porcPago.IdPorcentaje, | ||
|  |                 IdPublicacion = porcPago.IdPublicacion, | ||
|  |                 IdDistribuidor = porcPago.IdDistribuidor, | ||
|  |                 NombreDistribuidor = distribuidorData.Distribuidor?.Nombre ?? "Distribuidor Desconocido", | ||
|  |                 VigenciaD = porcPago.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                 VigenciaH = porcPago.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |                 Porcentaje = porcPago.Porcentaje | ||
|  |             }; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<PorcPagoDto>> ObtenerPorPublicacionIdAsync(int idPublicacion) | ||
|  |         { | ||
|  |             var data = await _porcPagoRepository.GetByPublicacionIdAsync(idPublicacion); | ||
|  |             return data.Select(MapToDto); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<PorcPagoDto?> ObtenerPorIdAsync(int idPorcentaje) | ||
|  |         { | ||
|  |             var porcPago = await _porcPagoRepository.GetByIdAsync(idPorcentaje); | ||
|  |             if (porcPago == null) return null; | ||
|  |             return await MapToDtoWithLookup(porcPago); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(PorcPagoDto? PorcPago, string? Error)> CrearAsync(CreatePorcPagoDto createDto, int idUsuario) | ||
|  |         { | ||
|  |             if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) | ||
|  |                 return (null, "La publicación especificada no existe."); | ||
|  |             var distribuidorData = await _distribuidorRepository.GetByIdAsync(createDto.IdDistribuidor); | ||
|  |             if (distribuidorData.Distribuidor == null) | ||
|  |                 return (null, "El distribuidor especificado no existe."); | ||
|  | 
 | ||
|  |             var porcPagoActivo = await _porcPagoRepository.GetActiveByPublicacionDistribuidorAndDateAsync(createDto.IdPublicacion, createDto.IdDistribuidor, createDto.VigenciaD.Date); | ||
|  |             if (porcPagoActivo != null) | ||
|  |             { | ||
|  |                 return (null, $"Ya existe un porcentaje de pago activo para esta publicación y distribuidor en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Cierre el período anterior primero."); | ||
|  |             } | ||
|  | 
 | ||
|  |             var nuevoPorcPago = new PorcPago | ||
|  |             { | ||
|  |                 IdPublicacion = createDto.IdPublicacion, | ||
|  |                 IdDistribuidor = createDto.IdDistribuidor, | ||
|  |                 VigenciaD = createDto.VigenciaD.Date, | ||
|  |                 VigenciaH = null, | ||
|  |                 Porcentaje = createDto.Porcentaje | ||
|  |             }; | ||
|  | 
 | ||
|  |             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 porcPagoAnterior = await _porcPagoRepository.GetPreviousActivePorcPagoAsync(createDto.IdPublicacion, createDto.IdDistribuidor, nuevoPorcPago.VigenciaD, transaction); | ||
|  |                 if (porcPagoAnterior != null) | ||
|  |                 { | ||
|  |                     if (porcPagoAnterior.VigenciaD.Date >= nuevoPorcPago.VigenciaD.Date) | ||
|  |                     { | ||
|  |                         transaction.Rollback(); | ||
|  |                         return (null, $"La fecha de inicio del nuevo porcentaje ({nuevoPorcPago.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último porcentaje vigente ({porcPagoAnterior.VigenciaD:dd/MM/yyyy}) para este distribuidor."); | ||
|  |                     } | ||
|  |                     porcPagoAnterior.VigenciaH = nuevoPorcPago.VigenciaD.AddDays(-1); | ||
|  |                     await _porcPagoRepository.UpdateAsync(porcPagoAnterior, idUsuario, transaction); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var porcPagoCreado = await _porcPagoRepository.CreateAsync(nuevoPorcPago, idUsuario, transaction); | ||
|  |                 if (porcPagoCreado == null) throw new DataException("Error al crear el porcentaje de pago."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcPago ID {Id} creado por Usuario ID {UserId}.", porcPagoCreado.IdPorcentaje, idUsuario); | ||
|  |                 return (new PorcPagoDto { // Construir DTO manualmente ya que tenemos NombreDistribuidor | ||
|  |                     IdPorcentaje = porcPagoCreado.IdPorcentaje, | ||
|  |                     IdPublicacion = porcPagoCreado.IdPublicacion, | ||
|  |                     IdDistribuidor = porcPagoCreado.IdDistribuidor, | ||
|  |                     NombreDistribuidor = distribuidorData.Distribuidor.Nombre, | ||
|  |                     VigenciaD = porcPagoCreado.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                     VigenciaH = porcPagoCreado.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |                     Porcentaje = porcPagoCreado.Porcentaje | ||
|  |                 }, null); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error CrearAsync PorcPago para Pub ID {IdPub}, Dist ID {IdDist}", createDto.IdPublicacion, createDto.IdDistribuidor); | ||
|  |                 return (null, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcentaje, UpdatePorcPagoDto 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 porcPagoExistente = await _porcPagoRepository.GetByIdAsync(idPorcentaje); | ||
|  |                 if (porcPagoExistente == null) return (false, "Porcentaje de pago no encontrado."); | ||
|  | 
 | ||
|  |                 if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < porcPagoExistente.VigenciaD.Date) | ||
|  |                     return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde."); | ||
|  | 
 | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                     var porcentajesPubDist = await _porcPagoRepository.GetByPublicacionIdAsync(porcPagoExistente.IdPublicacion); | ||
|  |                     var porcentajesPosteriores = porcentajesPubDist | ||
|  |                         .Where(p => p.PorcPago.IdDistribuidor == porcPagoExistente.IdDistribuidor && | ||
|  |                                     p.PorcPago.IdPorcentaje != idPorcentaje && | ||
|  |                                     p.PorcPago.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && | ||
|  |                                     p.PorcPago.VigenciaD.Date > porcPagoExistente.VigenciaD.Date); | ||
|  |                     if(porcentajesPosteriores.Any()) | ||
|  |                     { | ||
|  |                          return (false, "No se puede cerrar este período porque existen porcentajes posteriores para este distribuidor que se solaparían."); | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 porcPagoExistente.Porcentaje = updateDto.Porcentaje; | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                     porcPagoExistente.VigenciaH = updateDto.VigenciaH.Value.Date; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var actualizado = await _porcPagoRepository.UpdateAsync(porcPagoExistente, idUsuario, transaction); | ||
|  |                 if (!actualizado) throw new DataException("Error al actualizar porcentaje de pago."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcPago ID {Id} actualizado por Usuario ID {UserId}.", idPorcentaje, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Porcentaje no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error ActualizarAsync PorcPago ID: {Id}", idPorcentaje); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> EliminarAsync(int idPorcentaje, 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 porcPagoAEliminar = await _porcPagoRepository.GetByIdAsync(idPorcentaje); | ||
|  |                 if (porcPagoAEliminar == null) return (false, "Porcentaje de pago no encontrado."); | ||
|  | 
 | ||
|  |                 if (porcPagoAEliminar.VigenciaH == null)  | ||
|  |                 { | ||
|  |                     var todosPorcPubDistData = await _porcPagoRepository.GetByPublicacionIdAsync(porcPagoAEliminar.IdPublicacion); | ||
|  |                     var todosPorcPubDist = todosPorcPubDistData | ||
|  |                                                 .Where(p => p.PorcPago.IdDistribuidor == porcPagoAEliminar.IdDistribuidor) | ||
|  |                                                 .Select(p => p.PorcPago) | ||
|  |                                                 .OrderByDescending(p => p.VigenciaD).ToList(); | ||
|  |                      | ||
|  |                     var indiceActual = todosPorcPubDist.FindIndex(p => p.IdPorcentaje == idPorcentaje); | ||
|  |                     if(indiceActual != -1 && (indiceActual + 1) < todosPorcPubDist.Count) | ||
|  |                     { | ||
|  |                         var porcPagoAnteriorDirecto = todosPorcPubDist[indiceActual + 1]; | ||
|  |                         if(porcPagoAnteriorDirecto.VigenciaH.HasValue &&  | ||
|  |                            porcPagoAnteriorDirecto.VigenciaH.Value.Date == porcPagoAEliminar.VigenciaD.AddDays(-1).Date) | ||
|  |                         { | ||
|  |                             porcPagoAnteriorDirecto.VigenciaH = null; | ||
|  |                             await _porcPagoRepository.UpdateAsync(porcPagoAnteriorDirecto, idUsuario, transaction); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var eliminado = await _porcPagoRepository.DeleteAsync(idPorcentaje, idUsuario, transaction); | ||
|  |                 if (!eliminado) throw new DataException("Error al eliminar porcentaje de pago."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcPago ID {Id} eliminado por Usuario ID {UserId}.", idPorcentaje, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Porcentaje no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error EliminarAsync PorcPago ID: {Id}", idPorcentaje); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |