< Summary

Information
Class: FAU.Logica.Services.TipoBeneficioService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/TipoBeneficioService.cs
Line coverage
98%
Covered lines: 147
Uncovered lines: 2
Coverable lines: 149
Total lines: 333
Line coverage: 98.6%
Branch coverage
95%
Covered branches: 71
Total branches: 74
Branch coverage: 95.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ObtenerPorIdAsync()100%11100%
ObtenerPaginadoAsync()87.5%88100%
CrearAsync()100%44100%
ActualizarAsync()100%3030100%
ActivarAsync()100%11100%
DesactivarAsync()100%11100%
EliminarAsync()100%66100%
CambiarEstadoAsync()100%66100%
RegistrarAuditoria(...)100%210%
ValidarDatos(...)100%1212100%
EsViolacionCodigoDuplicado(...)75%88100%
MapearDto(...)100%11100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/TipoBeneficioService.cs

#LineLine coverage
 1using FAU.DataAccess.Repositories;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using Microsoft.EntityFrameworkCore;
 5
 6namespace FAU.Logica.Services;
 7
 8public class TipoBeneficioService : ITipoBeneficioService
 9{
 10    private readonly ITipoBeneficioRepository _tipoBeneficioRepository;
 11    private readonly IAuditoriaService _auditoriaService;
 12
 3013    public TipoBeneficioService(
 3014        ITipoBeneficioRepository tipoBeneficioRepository,
 3015        IAuditoriaService auditoriaService)
 16    {
 3017        _tipoBeneficioRepository = tipoBeneficioRepository;
 3018        _auditoriaService = auditoriaService;
 3019    }
 20
 21    public async Task<TipoBeneficio?> ObtenerPorIdAsync(long id)
 22    {
 123        return await _tipoBeneficioRepository.ObtenerPorIdAsync(id);
 124    }
 25
 26    public async Task<PagedResult<TipoBeneficioDto>> ObtenerPaginadoAsync(
 27        int pagina,
 28        int tamano,
 29        string? busqueda = null,
 30        bool? activo = null)
 31    {
 332        if (pagina < 1) pagina = 1;
 233        if (tamano < 1) tamano = 10;
 334        if (tamano > 100) tamano = 100;
 35
 236        var (items, totalCount) = await _tipoBeneficioRepository.ObtenerPaginadoAsync(pagina, tamano, busqueda, activo);
 37
 238        var itemsDto = items.Select(MapearDto).ToList();
 39
 240        return new PagedResult<TipoBeneficioDto>
 241        {
 242            Items = itemsDto,
 243            Page = pagina,
 244            PageSize = tamano,
 245            TotalCount = totalCount
 246        };
 247    }
 48
 49    public async Task<(bool Exito, TipoBeneficio? Resultado, string? Error)> CrearAsync(
 50        string codigo,
 51        string nombre,
 52        bool esMensual,
 53        bool permiteRetroactividad,
 54        short? plazoPresentacionDias,
 55        decimal? montoReferencia,
 56        long? catalogoItemId,
 57        bool activo,
 58        long usuarioId,
 59        string host)
 60    {
 61        try
 62        {
 863            var validacion = ValidarDatos(codigo, nombre, plazoPresentacionDias, montoReferencia);
 864            if (!string.IsNullOrEmpty(validacion))
 65            {
 466                return (false, null, validacion);
 67            }
 68
 469            var codigoNormalizado = codigo.Trim().ToUpperInvariant();
 470            if (await _tipoBeneficioRepository.ExisteCodigoAsync(codigoNormalizado))
 71            {
 172                return (false, null, "Ya existe un tipo de beneficio con ese codigo");
 73            }
 74
 375            var nuevoTipo = new TipoBeneficio
 376            {
 377                Codigo = codigoNormalizado,
 378                Nombre = nombre.Trim(),
 379                EsMensual = esMensual,
 380                PermiteRetroactividad = permiteRetroactividad,
 381                PlazoPresentacionDias = plazoPresentacionDias,
 382                MontoReferencia = montoReferencia,
 383                CatalogoItemId = catalogoItemId,
 384                Activo = activo
 385            };
 86
 387            var creado = await _tipoBeneficioRepository.CrearAsync(nuevoTipo);
 188            await _auditoriaService.LogAuditoriaAsync(usuarioId, AccionEnum.CrearBeneficio, ContextoEnum.Beneficios, hos
 89
 190            return (true, creado, null);
 91        }
 192        catch (DbUpdateException ex) when (EsViolacionCodigoDuplicado(ex))
 93        {
 194            return (false, null, "Ya existe un tipo de beneficio con ese codigo");
 95        }
 196        catch (Exception ex)
 97        {
 198            return (false, null, $"Error al crear tipo de beneficio: {ex.Message}");
 99        }
 8100    }
 101
 102    public async Task<(bool Exito, TipoBeneficio? Resultado, string? Error)> ActualizarAsync(
 103        long id,
 104        string? codigo,
 105        string? nombre,
 106        bool? esMensual,
 107        bool? permiteRetroactividad,
 108        short? plazoPresentacionDias,
 109        decimal? montoReferencia,
 110        long? catalogoItemId,
 111        long usuarioId,
 112        string host)
 113    {
 114        try
 115        {
 9116            var existente = await _tipoBeneficioRepository.ObtenerPorIdAsync(id);
 8117            if (existente == null)
 118            {
 1119                return (false, null, "Tipo de beneficio no encontrado");
 120            }
 121
 7122            if (codigo != null)
 123            {
 4124                if (string.IsNullOrWhiteSpace(codigo))
 125                {
 1126                    return (false, null, "El codigo es requerido");
 127                }
 128
 3129                var codigoNormalizado = codigo.Trim().ToUpperInvariant();
 3130                if (await _tipoBeneficioRepository.ExisteCodigoAsync(codigoNormalizado, id))
 131                {
 1132                    return (false, null, "Ya existe un tipo de beneficio con ese codigo");
 133                }
 134
 2135                existente.Codigo = codigoNormalizado;
 2136            }
 137
 5138            if (nombre != null)
 139            {
 2140                if (string.IsNullOrWhiteSpace(nombre))
 141                {
 1142                    return (false, null, "El nombre es requerido");
 143                }
 144
 1145                existente.Nombre = nombre.Trim();
 146            }
 147
 4148            if (plazoPresentacionDias.HasValue && plazoPresentacionDias.Value < 0)
 149            {
 1150                return (false, null, "El plazo de presentacion no puede ser negativo");
 151            }
 152
 3153            if (montoReferencia.HasValue && montoReferencia.Value < 0)
 154            {
 1155                return (false, null, "El monto de referencia no puede ser negativo");
 156            }
 157
 2158            if (esMensual.HasValue)
 159            {
 1160                existente.EsMensual = esMensual.Value;
 161            }
 162
 2163            if (permiteRetroactividad.HasValue)
 164            {
 1165                existente.PermiteRetroactividad = permiteRetroactividad.Value;
 166            }
 167
 2168            if (plazoPresentacionDias.HasValue)
 169            {
 1170                existente.PlazoPresentacionDias = plazoPresentacionDias;
 171            }
 172
 2173            if (montoReferencia.HasValue)
 174            {
 1175                existente.MontoReferencia = montoReferencia;
 176            }
 177
 2178            if (catalogoItemId.HasValue)
 179            {
 1180                existente.CatalogoItemId = catalogoItemId;
 181            }
 182
 2183            var actualizado = await _tipoBeneficioRepository.ActualizarAsync(existente);
 1184            await _auditoriaService.LogAuditoriaAsync(usuarioId, AccionEnum.ActualizarBeneficio, ContextoEnum.Beneficios
 185
 1186            return (true, actualizado, null);
 187        }
 1188        catch (DbUpdateException ex) when (EsViolacionCodigoDuplicado(ex))
 189        {
 1190            return (false, null, "Ya existe un tipo de beneficio con ese codigo");
 191        }
 1192        catch (Exception ex)
 193        {
 1194            return (false, null, $"Error al actualizar tipo de beneficio: {ex.Message}");
 195        }
 9196    }
 197
 198    public async Task<(bool Exito, TipoBeneficio? Resultado, string? Error)> ActivarAsync(long id, long usuarioId, strin
 199    {
 3200        return await CambiarEstadoAsync(id, true, usuarioId, host);
 3201    }
 202
 203    public async Task<(bool Exito, TipoBeneficio? Resultado, string? Error)> DesactivarAsync(long id, long usuarioId, st
 204    {
 2205        return await CambiarEstadoAsync(id, false, usuarioId, host);
 2206    }
 207
 208    public async Task<(bool Exito, bool BajaLogica, string? Error)> EliminarAsync(long id, long usuarioId, string host)
 209    {
 210        try
 211        {
 5212            var existente = await _tipoBeneficioRepository.ObtenerPorIdAsync(id);
 4213            if (existente == null)
 214            {
 1215                return (false, false, "Tipo de beneficio no encontrado");
 216            }
 217
 3218            if (await _tipoBeneficioRepository.TieneLiquidacionesAbiertasAsync(id))
 219            {
 1220                return (false, false, "No se puede dar de baja porque existen liquidaciones abiertas relacionadas");
 221            }
 222
 2223            var tieneHistorico = await _tipoBeneficioRepository.TieneHistoricoBeneficiosAsync(id);
 2224            if (tieneHistorico)
 225            {
 1226                existente.Activo = false;
 1227                await _tipoBeneficioRepository.ActualizarAsync(existente);
 1228                await _auditoriaService.LogAuditoriaAsync(usuarioId, AccionEnum.DesactivarBeneficio, ContextoEnum.Benefi
 1229                return (true, true, null);
 230            }
 231
 1232            await _tipoBeneficioRepository.EliminarAsync(id);
 1233            await _auditoriaService.LogAuditoriaAsync(usuarioId, AccionEnum.EliminarBeneficio, ContextoEnum.Beneficios, 
 1234            return (true, false, null);
 235        }
 1236        catch (Exception ex)
 237        {
 1238            return (false, false, $"Error al eliminar tipo de beneficio: {ex.Message}");
 239        }
 5240    }
 241
 242    private async Task<(bool Exito, TipoBeneficio? Resultado, string? Error)> CambiarEstadoAsync(
 243        long id,
 244        bool nuevoEstado,
 245        long usuarioId,
 246        string host)
 247    {
 248        try
 249        {
 5250            var existente = await _tipoBeneficioRepository.ObtenerPorIdAsync(id);
 4251            if (existente == null)
 252            {
 1253                return (false, null, "Tipo de beneficio no encontrado");
 254            }
 255
 3256            if (await _tipoBeneficioRepository.TieneLiquidacionesAbiertasAsync(id))
 257            {
 1258                return (false, null, "No se puede cambiar el estado porque existen liquidaciones abiertas relacionadas")
 259            }
 260
 2261            existente.Activo = nuevoEstado;
 2262            var actualizado = await _tipoBeneficioRepository.ActualizarAsync(existente);
 2263            await _auditoriaService.LogAuditoriaAsync(usuarioId,
 2264                nuevoEstado ? AccionEnum.ActivarBeneficio : AccionEnum.DesactivarBeneficio,
 2265                ContextoEnum.Beneficios,
 2266                host,
 2267                entidad: nameof(TipoBeneficio),
 2268                entidadId: actualizado.Id,
 2269                detalle: new { codigo = actualizado.Codigo, activo = actualizado.Activo });
 270
 2271            return (true, actualizado, null);
 272        }
 1273        catch (Exception ex)
 274        {
 1275            return (false, null, $"Error al cambiar estado del tipo de beneficio: {ex.Message}");
 276        }
 5277    }
 278
 279    private void RegistrarAuditoria(long usuarioId, int accionId, string host)
 280    {
 281        // Legacy: conservar firma, pero actualmente no se usa. Mantener para compatibilidad futura.
 0282        _auditoriaService.LogAuditoria(usuarioId, accionId, (int)ContextoEnum.Sistema, host);
 0283    }
 284
 285    private static string? ValidarDatos(string codigo, string nombre, short? plazoPresentacionDias, decimal? montoRefere
 286    {
 8287        if (string.IsNullOrWhiteSpace(codigo))
 288        {
 1289            return "El codigo es requerido";
 290        }
 291
 7292        if (string.IsNullOrWhiteSpace(nombre))
 293        {
 1294            return "El nombre es requerido";
 295        }
 296
 6297        if (plazoPresentacionDias.HasValue && plazoPresentacionDias.Value < 0)
 298        {
 1299            return "El plazo de presentacion no puede ser negativo";
 300        }
 301
 5302        if (montoReferencia.HasValue && montoReferencia.Value < 0)
 303        {
 1304            return "El monto de referencia no puede ser negativo";
 305        }
 306
 4307        return null;
 308    }
 309
 310    private static bool EsViolacionCodigoDuplicado(DbUpdateException ex)
 311    {
 2312        var mensaje = ex.InnerException?.Message ?? ex.Message;
 2313        return mensaje.Contains("ux_tipos_beneficio_codigo_upper", StringComparison.OrdinalIgnoreCase)
 2314               || mensaje.Contains("tipos_beneficio_codigo_key", StringComparison.OrdinalIgnoreCase)
 2315               || mensaje.Contains("duplicate key", StringComparison.OrdinalIgnoreCase);
 316    }
 317
 318    private static TipoBeneficioDto MapearDto(TipoBeneficio item)
 319    {
 1320        return new TipoBeneficioDto
 1321        {
 1322            Id = item.Id,
 1323            Codigo = item.Codigo,
 1324            Nombre = item.Nombre,
 1325            EsMensual = item.EsMensual,
 1326            PermiteRetroactividad = item.PermiteRetroactividad,
 1327            PlazoPresentacionDias = item.PlazoPresentacionDias,
 1328            MontoReferencia = item.MontoReferencia,
 1329            CatalogoItemId = item.CatalogoItemId,
 1330            Activo = item.Activo
 1331        };
 332    }
 333}