240 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			240 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 PorcMonCanillaService : IPorcMonCanillaService | ||
|  |     { | ||
|  |         private readonly IPorcMonCanillaRepository _porcMonCanillaRepository; | ||
|  |         private readonly IPublicacionRepository _publicacionRepository; | ||
|  |         private readonly ICanillaRepository _canillaRepository; // Para validar IdCanilla y obtener nombre | ||
|  |         private readonly DbConnectionFactory _connectionFactory; | ||
|  |         private readonly ILogger<PorcMonCanillaService> _logger; | ||
|  | 
 | ||
|  |         public PorcMonCanillaService( | ||
|  |             IPorcMonCanillaRepository porcMonCanillaRepository, | ||
|  |             IPublicacionRepository publicacionRepository, | ||
|  |             ICanillaRepository canillaRepository, | ||
|  |             DbConnectionFactory connectionFactory, | ||
|  |             ILogger<PorcMonCanillaService> logger) | ||
|  |         { | ||
|  |             _porcMonCanillaRepository = porcMonCanillaRepository; | ||
|  |             _publicacionRepository = publicacionRepository; | ||
|  |             _canillaRepository = canillaRepository; | ||
|  |             _connectionFactory = connectionFactory; | ||
|  |             _logger = logger; | ||
|  |         } | ||
|  | 
 | ||
|  |         private PorcMonCanillaDto MapToDto((PorcMonCanilla Item, string NomApeCanilla) data) => new PorcMonCanillaDto | ||
|  |         { | ||
|  |             IdPorcMon = data.Item.IdPorcMon, | ||
|  |             IdPublicacion = data.Item.IdPublicacion, | ||
|  |             IdCanilla = data.Item.IdCanilla, | ||
|  |             NomApeCanilla = data.NomApeCanilla, | ||
|  |             VigenciaD = data.Item.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |             VigenciaH = data.Item.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |             PorcMon = data.Item.PorcMon, | ||
|  |             EsPorcentaje = data.Item.EsPorcentaje | ||
|  |         }; | ||
|  |          | ||
|  |         private async Task<PorcMonCanillaDto?> MapToDtoWithLookup(PorcMonCanilla? item) | ||
|  |         { | ||
|  |             if (item == null) return null; // Si el item es null, devuelve null | ||
|  | 
 | ||
|  |             var canillaData = await _canillaRepository.GetByIdAsync(item.IdCanilla); | ||
|  |             return new PorcMonCanillaDto | ||
|  |             { | ||
|  |                 IdPorcMon = item.IdPorcMon, | ||
|  |                 IdPublicacion = item.IdPublicacion, | ||
|  |                 IdCanilla = item.IdCanilla, | ||
|  |                 NomApeCanilla = canillaData.Canilla?.NomApe ?? "Canillita Desconocido", | ||
|  |                 VigenciaD = item.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                 VigenciaH = item.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |                 PorcMon = item.PorcMon, | ||
|  |                 EsPorcentaje = item.EsPorcentaje | ||
|  |             }; | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<PorcMonCanillaDto>> ObtenerPorPublicacionIdAsync(int idPublicacion) | ||
|  |         { | ||
|  |             var data = await _porcMonCanillaRepository.GetByPublicacionIdAsync(idPublicacion); | ||
|  |             // Filtrar los nulos que MapToDto podría devolver (aunque no debería en este caso si GetAllWithProfileNameAsync no devuelve usuarios nulos en la tupla) | ||
|  |             return data.Select(MapToDto).Where(dto => dto != null).Select(dto => dto!); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<PorcMonCanillaDto?> ObtenerPorIdAsync(int idPorcMon) | ||
|  |         { | ||
|  |             var item = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon); | ||
|  |             return await MapToDtoWithLookup(item); | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(PorcMonCanillaDto? Item, string? Error)> CrearAsync(CreatePorcMonCanillaDto createDto, int idUsuario) | ||
|  |         { | ||
|  |             if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) | ||
|  |                 return (null, "La publicación especificada no existe."); | ||
|  |              | ||
|  |             var canillaData = await _canillaRepository.GetByIdAsync(createDto.IdCanilla); | ||
|  |             if (canillaData.Canilla == null) // GetByIdAsync devuelve una tupla | ||
|  |                 return (null, "El canillita especificado no existe o no está activo."); | ||
|  |             // Validar que solo canillitas accionistas pueden tener porcentaje/monto (o la regla de negocio que aplique) | ||
|  |             // Por ejemplo, si solo los Accionistas pueden tener esta configuración: | ||
|  |             // if (!canillaData.Canilla.Accionista) { | ||
|  |             //     return (null, "Solo los canillitas accionistas pueden tener un porcentaje/monto de pago configurado."); | ||
|  |             // } | ||
|  | 
 | ||
|  | 
 | ||
|  |             var itemActivo = await _porcMonCanillaRepository.GetActiveByPublicacionCanillaAndDateAsync(createDto.IdPublicacion, createDto.IdCanilla, createDto.VigenciaD.Date); | ||
|  |             if (itemActivo != null) | ||
|  |             { | ||
|  |                 return (null, $"Ya existe un porcentaje/monto activo para esta publicación y canillita en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Cierre el período anterior."); | ||
|  |             } | ||
|  | 
 | ||
|  |             var nuevoItem = new PorcMonCanilla | ||
|  |             { | ||
|  |                 IdPublicacion = createDto.IdPublicacion, | ||
|  |                 IdCanilla = createDto.IdCanilla, | ||
|  |                 VigenciaD = createDto.VigenciaD.Date, | ||
|  |                 VigenciaH = null, | ||
|  |                 PorcMon = createDto.PorcMon, | ||
|  |                 EsPorcentaje = createDto.EsPorcentaje | ||
|  |             }; | ||
|  | 
 | ||
|  |             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 itemAnterior = await _porcMonCanillaRepository.GetPreviousActiveAsync(createDto.IdPublicacion, createDto.IdCanilla, nuevoItem.VigenciaD, transaction); | ||
|  |                 if (itemAnterior != null) | ||
|  |                 { | ||
|  |                      if (itemAnterior.VigenciaD.Date >= nuevoItem.VigenciaD.Date) | ||
|  |                     { | ||
|  |                         transaction.Rollback(); | ||
|  |                         return (null, $"La fecha de inicio ({nuevoItem.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último período vigente ({itemAnterior.VigenciaD:dd/MM/yyyy})."); | ||
|  |                     } | ||
|  |                     itemAnterior.VigenciaH = nuevoItem.VigenciaD.AddDays(-1); | ||
|  |                     await _porcMonCanillaRepository.UpdateAsync(itemAnterior, idUsuario, transaction); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var itemCreado = await _porcMonCanillaRepository.CreateAsync(nuevoItem, idUsuario, transaction); | ||
|  |                 if (itemCreado == null) throw new DataException("Error al crear el registro."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcMonCanilla ID {Id} creado por Usuario ID {UserId}.", itemCreado.IdPorcMon, idUsuario); | ||
|  |                 return (await MapToDtoWithLookup(itemCreado), null); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error CrearAsync PorcMonCanilla para Pub ID {IdPub}, Canilla ID {IdCan}", createDto.IdPublicacion, createDto.IdCanilla); | ||
|  |                 return (null, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> ActualizarAsync(int idPorcMon, UpdatePorcMonCanillaDto 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 itemExistente = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon); | ||
|  |                 if (itemExistente == null) return (false, "Registro de porcentaje/monto no encontrado."); | ||
|  | 
 | ||
|  |                 if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < itemExistente.VigenciaD.Date) | ||
|  |                     return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde."); | ||
|  |                  | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                      var itemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemExistente.IdPublicacion); | ||
|  |                      var itemsPosteriores = itemsPubCanillaData | ||
|  |                         .Where(i => i.Item.IdCanilla == itemExistente.IdCanilla && | ||
|  |                                     i.Item.IdPorcMon != idPorcMon && | ||
|  |                                     i.Item.VigenciaD.Date <= updateDto.VigenciaH.Value.Date && | ||
|  |                                     i.Item.VigenciaD.Date > itemExistente.VigenciaD.Date); | ||
|  |                      if(itemsPosteriores.Any()) | ||
|  |                      { | ||
|  |                         return (false, "No se puede cerrar este período porque existen configuraciones posteriores para este canillita que se solaparían."); | ||
|  |                      } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 itemExistente.PorcMon = updateDto.PorcMon; | ||
|  |                 itemExistente.EsPorcentaje = updateDto.EsPorcentaje; | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                     itemExistente.VigenciaH = updateDto.VigenciaH.Value.Date; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var actualizado = await _porcMonCanillaRepository.UpdateAsync(itemExistente, idUsuario, transaction); | ||
|  |                 if (!actualizado) throw new DataException("Error al actualizar registro."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcMonCanilla ID {Id} actualizado por Usuario ID {UserId}.", idPorcMon, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error ActualizarAsync PorcMonCanilla ID: {Id}", idPorcMon); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> EliminarAsync(int idPorcMon, 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 itemAEliminar = await _porcMonCanillaRepository.GetByIdAsync(idPorcMon); | ||
|  |                 if (itemAEliminar == null) return (false, "Registro no encontrado."); | ||
|  | 
 | ||
|  |                 if (itemAEliminar.VigenciaH == null)  | ||
|  |                 { | ||
|  |                     var todosItemsPubCanillaData = await _porcMonCanillaRepository.GetByPublicacionIdAsync(itemAEliminar.IdPublicacion); | ||
|  |                     var todosItemsPubCanilla = todosItemsPubCanillaData | ||
|  |                                                 .Where(i => i.Item.IdCanilla == itemAEliminar.IdCanilla) | ||
|  |                                                 .Select(i => i.Item) | ||
|  |                                                 .OrderByDescending(i => i.VigenciaD).ToList(); | ||
|  |                      | ||
|  |                     var indiceActual = todosItemsPubCanilla.FindIndex(i => i.IdPorcMon == idPorcMon); | ||
|  |                     if(indiceActual != -1 && (indiceActual + 1) < todosItemsPubCanilla.Count) | ||
|  |                     { | ||
|  |                         var itemAnteriorDirecto = todosItemsPubCanilla[indiceActual + 1]; | ||
|  |                         if(itemAnteriorDirecto.VigenciaH.HasValue &&  | ||
|  |                            itemAnteriorDirecto.VigenciaH.Value.Date == itemAEliminar.VigenciaD.AddDays(-1).Date) | ||
|  |                         { | ||
|  |                             itemAnteriorDirecto.VigenciaH = null; | ||
|  |                             await _porcMonCanillaRepository.UpdateAsync(itemAnteriorDirecto, idUsuario, transaction); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var eliminado = await _porcMonCanillaRepository.DeleteAsync(idPorcMon, idUsuario, transaction); | ||
|  |                 if (!eliminado) throw new DataException("Error al eliminar registro."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("PorcMonCanilla ID {Id} eliminado por Usuario ID {UserId}.", idPorcMon, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Registro no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error EliminarAsync PorcMonCanilla ID: {Id}", idPorcMon); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |