using GestionIntegral.Api.Data; using GestionIntegral.Api.Data.Repositories.Distribucion; using GestionIntegral.Api.Dtos.Auditoria; 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 ControlDevolucionesService : IControlDevolucionesService { private readonly IControlDevolucionesRepository _controlDevRepo; private readonly IEmpresaRepository _empresaRepository; // Para validar IdEmpresa y obtener nombre private readonly DbConnectionFactory _connectionFactory; private readonly ILogger _logger; public ControlDevolucionesService( IControlDevolucionesRepository controlDevRepo, IEmpresaRepository empresaRepository, DbConnectionFactory connectionFactory, ILogger logger) { _controlDevRepo = controlDevRepo; _empresaRepository = empresaRepository; _connectionFactory = connectionFactory; _logger = logger; } private async Task MapToDto(ControlDevoluciones control) { if (control == null) return null!; var empresa = await _empresaRepository.GetByIdAsync(control.IdEmpresa); return new ControlDevolucionesDto { IdControl = control.IdControl, IdEmpresa = control.IdEmpresa, NombreEmpresa = empresa?.Nombre ?? "Empresa Desconocida", Fecha = control.Fecha.ToString("yyyy-MM-dd"), Entrada = control.Entrada, Sobrantes = control.Sobrantes, Detalle = control.Detalle, SinCargo = control.SinCargo }; } public async Task> ObtenerTodosAsync(DateTime? fechaDesde, DateTime? fechaHasta, int? idEmpresa) { var controles = await _controlDevRepo.GetAllAsync(fechaDesde, fechaHasta, idEmpresa); var dtos = new List(); foreach (var control in controles) { dtos.Add(await MapToDto(control)); } return dtos; } public async Task ObtenerPorIdAsync(int idControl) { var control = await _controlDevRepo.GetByIdAsync(idControl); return control == null ? null : await MapToDto(control); } public async Task<(ControlDevolucionesDto? Control, string? Error)> CrearAsync(CreateControlDevolucionesDto createDto, int idUsuario) { var empresa = await _empresaRepository.GetByIdAsync(createDto.IdEmpresa); if (empresa == null) return (null, "La empresa especificada no existe."); // Validar que no exista ya un control para esa empresa y fecha if (await _controlDevRepo.GetByEmpresaAndFechaAsync(createDto.IdEmpresa, createDto.Fecha.Date) != null) { return (null, $"Ya existe un control de devoluciones para la empresa '{empresa.Nombre}' en la fecha {createDto.Fecha:dd/MM/yyyy}."); } var nuevoControl = new ControlDevoluciones { IdEmpresa = createDto.IdEmpresa, Fecha = createDto.Fecha.Date, Entrada = createDto.Entrada, Sobrantes = createDto.Sobrantes, Detalle = createDto.Detalle, SinCargo = createDto.SinCargo }; 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 controlCreado = await _controlDevRepo.CreateAsync(nuevoControl, idUsuario, transaction); if (controlCreado == null) throw new DataException("Error al registrar el control de devoluciones."); transaction.Commit(); _logger.LogInformation("ControlDevoluciones ID {Id} creado por Usuario ID {UserId}.", controlCreado.IdControl, idUsuario); return (await MapToDto(controlCreado), null); } catch (Exception ex) { try { transaction.Rollback(); } catch { } _logger.LogError(ex, "Error CrearAsync ControlDevoluciones para Empresa ID {IdEmpresa}, Fecha {Fecha}", createDto.IdEmpresa, createDto.Fecha); return (null, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> ActualizarAsync(int idControl, UpdateControlDevolucionesDto 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 controlExistente = await _controlDevRepo.GetByIdAsync(idControl); // Obtener dentro de TX if (controlExistente == null) return (false, "Control de devoluciones no encontrado."); // IdEmpresa y Fecha no se modifican controlExistente.Entrada = updateDto.Entrada; controlExistente.Sobrantes = updateDto.Sobrantes; controlExistente.Detalle = updateDto.Detalle; controlExistente.SinCargo = updateDto.SinCargo; var actualizado = await _controlDevRepo.UpdateAsync(controlExistente, idUsuario, transaction); if (!actualizado) throw new DataException("Error al actualizar el control de devoluciones."); transaction.Commit(); _logger.LogInformation("ControlDevoluciones ID {Id} actualizado por Usuario ID {UserId}.", idControl, 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 ControlDevoluciones ID: {Id}", idControl); return (false, $"Error interno: {ex.Message}"); } } public async Task<(bool Exito, string? Error)> EliminarAsync(int idControl, 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 controlExistente = await _controlDevRepo.GetByIdAsync(idControl); // Obtener dentro de TX if (controlExistente == null) return (false, "Control de devoluciones no encontrado."); // Aquí no hay dependencias directas que afecten saldos, es un registro informativo. // Se podría verificar si está "en uso" si alguna lógica de reporte o cierre depende de él, // pero por ahora, la eliminación es directa. var eliminado = await _controlDevRepo.DeleteAsync(idControl, idUsuario, transaction); if (!eliminado) throw new DataException("Error al eliminar el control de devoluciones."); transaction.Commit(); _logger.LogInformation("ControlDevoluciones ID {Id} eliminado por Usuario ID {UserId}.", idControl, 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 ControlDevoluciones ID: {Id}", idControl); return (false, $"Error interno: {ex.Message}"); } } public async Task> ObtenerHistorialAsync( DateTime? fechaDesde, DateTime? fechaHasta, int? idUsuarioModifico, string? tipoModificacion, int? idControlAfectado, int? idEmpresaAfectada, DateTime? fechaAfectada) { var historialData = await _controlDevRepo.GetHistorialAsync(fechaDesde, fechaHasta, idUsuarioModifico, tipoModificacion, idControlAfectado, idEmpresaAfectada, fechaAfectada); return historialData.Select(h => new ControlDevolucionesHistorialDto { Id_Control = h.Historial.Id_Control, Id_Empresa = h.Historial.Id_Empresa, Fecha = h.Historial.Fecha, Entrada = h.Historial.Entrada, Sobrantes = h.Historial.Sobrantes, Detalle = h.Historial.Detalle, SinCargo = h.Historial.SinCargo, Id_Usuario = h.Historial.Id_Usuario, NombreUsuarioModifico = h.NombreUsuarioModifico, FechaMod = h.Historial.FechaMod, TipoMod = h.Historial.TipoMod // Mapear NombreEmpresa aquí si lo añades al DTO y lo obtienes }).ToList(); } } }