247 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
		
		
			
		
	
	
			247 lines
		
	
	
		
			13 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 RecargoZonaService : IRecargoZonaService | ||
|  |     { | ||
|  |         private readonly IRecargoZonaRepository _recargoZonaRepository; | ||
|  |         private readonly IPublicacionRepository _publicacionRepository; | ||
|  |         private readonly IZonaRepository _zonaRepository; | ||
|  |         private readonly DbConnectionFactory _connectionFactory; | ||
|  |         private readonly ILogger<RecargoZonaService> _logger; | ||
|  | 
 | ||
|  |         public RecargoZonaService( | ||
|  |             IRecargoZonaRepository recargoZonaRepository, | ||
|  |             IPublicacionRepository publicacionRepository, | ||
|  |             IZonaRepository zonaRepository, | ||
|  |             DbConnectionFactory connectionFactory, | ||
|  |             ILogger<RecargoZonaService> logger) | ||
|  |         { | ||
|  |             _recargoZonaRepository = recargoZonaRepository; | ||
|  |             _publicacionRepository = publicacionRepository; | ||
|  |             _zonaRepository = zonaRepository; | ||
|  |             _connectionFactory = connectionFactory; | ||
|  |             _logger = logger; | ||
|  |         } | ||
|  | 
 | ||
|  |         private RecargoZonaDto MapToDto((RecargoZona Recargo, string NombreZona) data) => new RecargoZonaDto | ||
|  |         { | ||
|  |             IdRecargo = data.Recargo.IdRecargo, | ||
|  |             IdPublicacion = data.Recargo.IdPublicacion, | ||
|  |             IdZona = data.Recargo.IdZona, | ||
|  |             NombreZona = data.NombreZona, | ||
|  |             VigenciaD = data.Recargo.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |             VigenciaH = data.Recargo.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |             Valor = data.Recargo.Valor | ||
|  |         }; | ||
|  |          | ||
|  |         // Helper para mapear cuando solo tenemos el RecargoZona y necesitamos buscar NombreZona | ||
|  |         private async Task<RecargoZonaDto> MapToDtoWithZonaLookup(RecargoZona recargo) | ||
|  |         { | ||
|  |             var zona = await _zonaRepository.GetByIdAsync(recargo.IdZona); // zonaRepository.GetByIdAsync devuelve ZonaDto | ||
|  |             return new RecargoZonaDto | ||
|  |             { | ||
|  |                 IdRecargo = recargo.IdRecargo, | ||
|  |                 IdPublicacion = recargo.IdPublicacion, | ||
|  |                 IdZona = recargo.IdZona, | ||
|  |                 NombreZona = zona?.Nombre ?? "Zona Desconocida/Inactiva", // Usar la propiedad Nombre del ZonaDto | ||
|  |                 VigenciaD = recargo.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                 VigenciaH = recargo.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |                 Valor = recargo.Valor | ||
|  |             }; | ||
|  |         } | ||
|  | 
 | ||
|  | 
 | ||
|  |         public async Task<IEnumerable<RecargoZonaDto>> ObtenerPorPublicacionIdAsync(int idPublicacion) | ||
|  |         { | ||
|  |             var recargosData = await _recargoZonaRepository.GetByPublicacionIdAsync(idPublicacion); | ||
|  |             return recargosData | ||
|  |                 .Select(rd => MapToDto(rd)) // MapToDto ahora devuelve RecargoZonaDto? | ||
|  |                 .Where(dto => dto != null) | ||
|  |                 .Select(dto => dto!); // Cast no nulo | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<RecargoZonaDto?> ObtenerPorIdAsync(int idRecargo) | ||
|  |         { | ||
|  |             var recargo = await _recargoZonaRepository.GetByIdAsync(idRecargo); | ||
|  |             if (recargo == null) return null; | ||
|  |              | ||
|  |             var zona = await _zonaRepository.GetByIdAsync(recargo.IdZona); // Obtiene ZonaDto | ||
|  |             return new RecargoZonaDto { | ||
|  |                  IdRecargo = recargo.IdRecargo, | ||
|  |                 IdPublicacion = recargo.IdPublicacion, | ||
|  |                 IdZona = recargo.IdZona, | ||
|  |                 NombreZona = zona?.Nombre ?? "Zona Desconocida/Inactiva", | ||
|  |                 VigenciaD = recargo.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                 VigenciaH = recargo.VigenciaH?.ToString("yyyy-MM-dd"), | ||
|  |                 Valor = recargo.Valor | ||
|  |             }; | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(RecargoZonaDto? Recargo, string? Error)> CrearAsync(CreateRecargoZonaDto createDto, int idUsuario) | ||
|  |         { | ||
|  |             if (await _publicacionRepository.GetByIdSimpleAsync(createDto.IdPublicacion) == null) | ||
|  |                 return (null, "La publicación especificada no existe."); | ||
|  |             var zona = await _zonaRepository.GetByIdAsync(createDto.IdZona); // Devuelve ZonaDto | ||
|  |             if (zona == null)  | ||
|  |                 return (null, "La zona especificada no existe o no está activa."); | ||
|  | 
 | ||
|  |             // Usar createDto.VigenciaD directamente que ya es DateTime | ||
|  |             var recargoActivo = await _recargoZonaRepository.GetActiveByPublicacionZonaAndDateAsync(createDto.IdPublicacion, createDto.IdZona, createDto.VigenciaD.Date); | ||
|  |             if (recargoActivo != null) | ||
|  |             { | ||
|  |                 return (null, $"Ya existe un recargo activo para esta publicación y zona en la fecha {createDto.VigenciaD:dd/MM/yyyy}. Primero debe cerrar el período anterior."); | ||
|  |             } | ||
|  | 
 | ||
|  |             var nuevoRecargo = new RecargoZona | ||
|  |             { | ||
|  |                 IdPublicacion = createDto.IdPublicacion, | ||
|  |                 IdZona = createDto.IdZona, | ||
|  |                 VigenciaD = createDto.VigenciaD.Date, | ||
|  |                 VigenciaH = null, | ||
|  |                 Valor = createDto.Valor | ||
|  |             }; | ||
|  | 
 | ||
|  |             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 recargoAnterior = await _recargoZonaRepository.GetPreviousActiveRecargoAsync(createDto.IdPublicacion, createDto.IdZona, nuevoRecargo.VigenciaD, transaction); | ||
|  |                 if (recargoAnterior != null) | ||
|  |                 { | ||
|  |                      if (recargoAnterior.VigenciaD.Date >= nuevoRecargo.VigenciaD.Date) // Comparar solo fechas | ||
|  |                     { | ||
|  |                         transaction.Rollback(); | ||
|  |                         return (null, $"La fecha de inicio del nuevo recargo ({nuevoRecargo.VigenciaD:dd/MM/yyyy}) no puede ser anterior o igual a la del último recargo vigente ({recargoAnterior.VigenciaD:dd/MM/yyyy}) para esta zona."); | ||
|  |                     } | ||
|  |                     recargoAnterior.VigenciaH = nuevoRecargo.VigenciaD.AddDays(-1); | ||
|  |                     await _recargoZonaRepository.UpdateAsync(recargoAnterior, idUsuario, transaction); | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var recargoCreado = await _recargoZonaRepository.CreateAsync(nuevoRecargo, idUsuario, transaction); | ||
|  |                 if (recargoCreado == null) throw new DataException("Error al crear el recargo."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Recargo ID {Id} creado por Usuario ID {UserId}.", recargoCreado.IdRecargo, idUsuario); | ||
|  |                 // Pasar el nombre de la zona ya obtenido | ||
|  |                 return (new RecargoZonaDto { | ||
|  |                     IdRecargo = recargoCreado.IdRecargo, IdPublicacion = recargoCreado.IdPublicacion, IdZona = recargoCreado.IdZona, | ||
|  |                     NombreZona = zona.Nombre, VigenciaD = recargoCreado.VigenciaD.ToString("yyyy-MM-dd"), | ||
|  |                     VigenciaH = recargoCreado.VigenciaH?.ToString("yyyy-MM-dd"), Valor = recargoCreado.Valor | ||
|  |                 }, null); | ||
|  |             } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error CrearAsync RecargoZona para Pub ID {IdPub}, Zona ID {IdZona}", createDto.IdPublicacion, createDto.IdZona); | ||
|  |                 return (null, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> ActualizarAsync(int idRecargo, UpdateRecargoZonaDto 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 recargoExistente = await _recargoZonaRepository.GetByIdAsync(idRecargo);  | ||
|  |                 if (recargoExistente == null) return (false, "Recargo por zona no encontrado."); | ||
|  | 
 | ||
|  |                 if (updateDto.VigenciaH.HasValue && updateDto.VigenciaH.Value.Date < recargoExistente.VigenciaD.Date) | ||
|  |                     return (false, "Vigencia Hasta no puede ser anterior a Vigencia Desde."); | ||
|  |                  | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                     var recargosDeLaPublicacion = await _recargoZonaRepository.GetByPublicacionIdAsync(recargoExistente.IdPublicacion); | ||
|  |                     var recargosPosteriores = recargosDeLaPublicacion | ||
|  |                                             .Where(r => r.Recargo.IdZona == recargoExistente.IdZona &&  | ||
|  |                                                         r.Recargo.IdRecargo != idRecargo &&  | ||
|  |                                                         r.Recargo.VigenciaD.Date <= updateDto.VigenciaH.Value.Date &&  | ||
|  |                                                         r.Recargo.VigenciaD.Date > recargoExistente.VigenciaD.Date); | ||
|  |                     if(recargosPosteriores.Any()) | ||
|  |                     { | ||
|  |                         return (false, "No se puede cerrar este período porque existen recargos posteriores para esta zona que se solaparían."); | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 recargoExistente.Valor = updateDto.Valor; | ||
|  |                 if (updateDto.VigenciaH.HasValue) | ||
|  |                 { | ||
|  |                     recargoExistente.VigenciaH = updateDto.VigenciaH.Value.Date; | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var actualizado = await _recargoZonaRepository.UpdateAsync(recargoExistente, idUsuario, transaction); | ||
|  |                 if (!actualizado) throw new DataException("Error al actualizar recargo."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Recargo ID {Id} actualizado por Usuario ID {UserId}.", idRecargo, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Recargo no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error ActualizarAsync RecargoZona ID: {Id}", idRecargo); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         public async Task<(bool Exito, string? Error)> EliminarAsync(int idRecargo, 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 recargoAEliminar = await _recargoZonaRepository.GetByIdAsync(idRecargo); | ||
|  |                 if (recargoAEliminar == null) return (false, "Recargo no encontrado."); | ||
|  | 
 | ||
|  |                 if (recargoAEliminar.VigenciaH == null)  | ||
|  |                 { | ||
|  |                     var todosRecargosPubZonaData = await _recargoZonaRepository.GetByPublicacionIdAsync(recargoAEliminar.IdPublicacion); | ||
|  |                     var todosRecargosPubZona = todosRecargosPubZonaData | ||
|  |                                                 .Where(r => r.Recargo.IdZona == recargoAEliminar.IdZona) | ||
|  |                                                 .Select(r => r.Recargo) | ||
|  |                                                 .OrderByDescending(r => r.VigenciaD).ToList(); | ||
|  |                      | ||
|  |                     var indiceActual = todosRecargosPubZona.FindIndex(r => r.IdRecargo == idRecargo); | ||
|  |                     if(indiceActual != -1 && (indiceActual + 1) < todosRecargosPubZona.Count) | ||
|  |                     { | ||
|  |                         var recargoAnteriorDirecto = todosRecargosPubZona[indiceActual + 1]; | ||
|  |                         if(recargoAnteriorDirecto.VigenciaH.HasValue && recargoAnteriorDirecto.VigenciaH.Value.Date == recargoAEliminar.VigenciaD.AddDays(-1).Date) | ||
|  |                         { | ||
|  |                             recargoAnteriorDirecto.VigenciaH = null; | ||
|  |                             await _recargoZonaRepository.UpdateAsync(recargoAnteriorDirecto, idUsuario, transaction); | ||
|  |                             _logger.LogInformation("Recargo anterior ID {IdRecargoAnterior} reabierto tras eliminación de Recargo ID {IdRecargoEliminado}.", recargoAnteriorDirecto.IdRecargo, idRecargo); | ||
|  |                         } | ||
|  |                     } | ||
|  |                 } | ||
|  | 
 | ||
|  |                 var eliminado = await _recargoZonaRepository.DeleteAsync(idRecargo, idUsuario, transaction); | ||
|  |                 if (!eliminado) throw new DataException("Error al eliminar recargo."); | ||
|  | 
 | ||
|  |                 transaction.Commit(); | ||
|  |                 _logger.LogInformation("Recargo ID {Id} eliminado por Usuario ID {UserId}.", idRecargo, idUsuario); | ||
|  |                 return (true, null); | ||
|  |             } | ||
|  |             catch (KeyNotFoundException) { try { transaction.Rollback(); } catch { } return (false, "Recargo no encontrado."); } | ||
|  |             catch (Exception ex) | ||
|  |             { | ||
|  |                 try { transaction.Rollback(); } catch { } | ||
|  |                 _logger.LogError(ex, "Error EliminarAsync RecargoZona ID: {Id}", idRecargo); | ||
|  |                 return (false, $"Error interno: {ex.Message}"); | ||
|  |             } | ||
|  |         } | ||
|  |     } | ||
|  | } |