< Summary

Information
Class: FAU.Logica.Services.DependienteService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/DependienteService.cs
Line coverage
88%
Covered lines: 119
Uncovered lines: 15
Coverable lines: 134
Total lines: 225
Line coverage: 88.8%
Branch coverage
77%
Covered branches: 77
Total branches: 100
Branch coverage: 77%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
GetDependientesPagedAsync()33.33%7673.33%
GetDependientesByPersonaAsync()75%44100%
GetDependienteByIdAsync()100%22100%
CreateDependienteAsync()92.1%383893.75%
UpdateDependienteAsync()71.87%703266.66%
MapToDto(...)68.75%1616100%
CalcularEdad(...)50%22100%

File(s)

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

#LineLine coverage
 1using FAU.DataAccess.Repositories;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using FAU.Logica.Validadores;
 5using Microsoft.Extensions.Logging;
 6
 7namespace FAU.Logica.Services;
 8
 9public class DependienteService : IDependienteService
 10{
 111    private static readonly HashSet<string> TiposValidos = new(StringComparer.Ordinal)
 112    {
 113        "hijo", "conyuge_sin_ingresos"
 114    };
 15
 116    private static readonly HashSet<short> PorcentajesValidos = new() { 50, 100 };
 17
 18    private readonly IDependienteRepository _dependienteRepository;
 19    private readonly IPersonalRepository _personalRepository;
 20    private readonly ILogger<DependienteService> _logger;
 21
 2122    public DependienteService(
 2123        IDependienteRepository dependienteRepository,
 2124        IPersonalRepository personalRepository,
 2125        ILogger<DependienteService> logger)
 26    {
 2127        _dependienteRepository = dependienteRepository;
 2128        _personalRepository = personalRepository;
 2129        _logger = logger;
 2130    }
 31
 32    public async Task<PagedResult<DependienteDto>> GetDependientesPagedAsync(
 33        int page, int pageSize, string? cedulaTitular = null, string? tipo = null, bool? activo = null)
 34    {
 135        long? personaId = null;
 136        if (!string.IsNullOrWhiteSpace(cedulaTitular))
 37        {
 038            var relacion = await _personalRepository.GetByCedulaAsync(cedulaTitular);
 039            if (relacion == null)
 040                return new PagedResult<DependienteDto> { Items = [], Page = page, PageSize = pageSize, TotalCount = 0 };
 041            personaId = relacion.PersonaId;
 42        }
 43
 144        var (items, totalCount) = await _dependienteRepository.GetDependientesPagedAsync(page, pageSize, personaId, tipo
 145        return new PagedResult<DependienteDto>
 146        {
 147            Items = items.Select(MapToDto),
 148            Page = page,
 149            PageSize = pageSize,
 150            TotalCount = totalCount
 151        };
 152    }
 53
 54    public async Task<IEnumerable<DependienteDto>> GetDependientesByPersonaAsync(string cedula)
 55    {
 156        var relacion = await _personalRepository.GetByCedulaAsync(cedula);
 157        if (relacion == null) return [];
 158        var dependientes = await _dependienteRepository.GetDependientesByPersonaAsync(relacion.PersonaId);
 159        return dependientes.Select(MapToDto);
 160    }
 61
 62    public async Task<DependienteDto?> GetDependienteByIdAsync(long id)
 63    {
 464        var dependiente = await _dependienteRepository.GetDependienteByIdAsync(id);
 465        return dependiente == null ? null : MapToDto(dependiente);
 466    }
 67
 68    public async Task<(bool Success, Dependiente? Dependiente, string? Error)> CreateDependienteAsync(CreateDependienteR
 69    {
 1270        if (!TiposValidos.Contains(request.Tipo))
 171            return (false, null, $"Tipo inválido. Valores aceptados: {string.Join(", ", TiposValidos)}");
 72
 73        // Validaciones específicas por tipo
 1174        if (request.Tipo == "hijo")
 75        {
 876            if (!request.FechaNacimiento.HasValue)
 177                return (false, null, "fecha_nacimiento es obligatoria para dependientes de tipo hijo");
 78
 779            if (request.FechaNacimiento.Value.Date > DateTime.Today)
 180                return (false, null, "fecha_nacimiento no puede ser una fecha futura");
 81
 682            if (request.PorcentajeDeduccion.HasValue && !PorcentajesValidos.Contains(request.PorcentajeDeduccion.Value))
 183                return (false, null, "porcentaje_deduccion debe ser 50 o 100");
 84        }
 385        else if (request.Tipo == "conyuge_sin_ingresos")
 86        {
 387            if (request.PorcentajeDeduccion.HasValue)
 188                return (false, null, "porcentaje_deduccion no aplica para tipo conyuge_sin_ingresos");
 89        }
 90
 791        if (!string.IsNullOrWhiteSpace(request.Cedula) && !CedulaValidator.Validar(request.Cedula))
 192            return (false, null, "La cédula ingresada no es válida");
 93
 694        var vigenteDesde = DateTime.Today;
 95
 696        if (request.VigenteHasta.HasValue && request.VigenteHasta.Value < vigenteDesde)
 197            return (false, null, "vigente_hasta debe ser mayor o igual a la fecha actual");
 98
 99        // Resolver personaId vía PersonaId o CedulaTitular
 100        long personaId;
 5101        if (request.PersonaId > 0)
 102        {
 0103            personaId = request.PersonaId;
 104        }
 5105        else if (!string.IsNullOrWhiteSpace(request.CedulaTitular))
 106        {
 5107            var relacionTitular = await _personalRepository.GetByCedulaAsync(request.CedulaTitular.Trim());
 5108            if (relacionTitular == null)
 1109                return (false, null, $"No se encontró personal activo con cédula {request.CedulaTitular}");
 4110            personaId = relacionTitular.PersonaId;
 111        }
 112        else
 113        {
 0114            return (false, null, "Debe proporcionar PersonaId o CedulaTitular para el dependiente");
 115        }
 116
 4117        var relacionActiva = await _personalRepository.GetActivoByPersonaIdAsync(personaId);
 4118        if (relacionActiva == null)
 0119            return (false, null, $"No se encontró personal activo con PersonaId {personaId}");
 120
 121        // Restricción: solo un cónyuge activo por persona
 4122        if (request.Tipo == "conyuge_sin_ingresos")
 123        {
 2124            if (await _dependienteRepository.ExisteConyugeActivoAsync(personaId))
 1125                return (false, null, "Ya existe un dependiente activo de tipo conyuge_sin_ingresos para este funcionario
 126        }
 127
 3128        var dependiente = new Dependiente
 3129        {
 3130            PersonaId = personaId,
 3131            Tipo = request.Tipo,
 3132            Nombre = request.Nombre,
 3133            Cedula = request.Cedula,
 3134            FechaNacimiento = request.FechaNacimiento,
 3135            Discapacitado = request.Discapacitado,
 3136            PorcentajeDeduccion = request.Tipo == "hijo" ? request.PorcentajeDeduccion : null,
 3137            VigenteDesde = vigenteDesde,
 3138            VigenteHasta = request.VigenteHasta,
 3139            Activo = true
 3140        };
 141
 3142        var creado = await _dependienteRepository.CreateDependienteAsync(dependiente);
 3143        _logger.LogInformation("Dependiente tipo {Tipo} creado para persona {PersonaId}", dependiente.Tipo, dependiente.
 3144        return (true, creado, null);
 12145    }
 146
 147    public async Task<(bool Success, Dependiente? Dependiente, string? Error)> UpdateDependienteAsync(long id, UpdateDep
 148    {
 3149        var dependiente = await _dependienteRepository.GetDependienteByIdAsync(id);
 4150        if (dependiente == null) return (false, null, "Dependiente no encontrado");
 151
 2152        if (request.VigenteHasta.HasValue && request.VigenteHasta.Value < dependiente.VigenteDesde)
 0153            return (false, null, "vigente_hasta debe ser mayor o igual a vigente_desde");
 154
 2155        if (request.PorcentajeDeduccion.HasValue)
 156        {
 0157            if (dependiente.Tipo != "hijo")
 0158                return (false, null, "porcentaje_deduccion no aplica para tipo conyuge_sin_ingresos");
 0159            if (!PorcentajesValidos.Contains(request.PorcentajeDeduccion.Value))
 0160                return (false, null, "porcentaje_deduccion debe ser 50 o 100");
 0161            dependiente.PorcentajeDeduccion = request.PorcentajeDeduccion;
 162        }
 163
 2164        if (request.Discapacitado.HasValue)
 0165            dependiente.Discapacitado = request.Discapacitado.Value;
 166
 2167        if (request.VigenteHasta.HasValue)
 0168            dependiente.VigenteHasta = request.VigenteHasta;
 169
 2170        if (request.Activo.HasValue)
 171        {
 172            // Si se reactiva un cónyuge, verificar que no haya otro activo
 2173            if (request.Activo.Value && dependiente.Tipo == "conyuge_sin_ingresos" && !dependiente.Activo)
 174            {
 1175                if (await _dependienteRepository.ExisteConyugeActivoAsync(dependiente.PersonaId))
 1176                    return (false, null, "Ya existe un dependiente activo de tipo conyuge_sin_ingresos para este funcion
 177            }
 178
 1179            dependiente.Activo = request.Activo.Value;
 180            // Al desactivar sin fecha de cierre, setear vigente_hasta = hoy
 1181            if (!request.Activo.Value && !dependiente.VigenteHasta.HasValue && !request.VigenteHasta.HasValue)
 1182                dependiente.VigenteHasta = DateTime.UtcNow.Date;
 183        }
 184
 1185        var actualizado = await _dependienteRepository.UpdateDependienteAsync(dependiente);
 1186        return (true, actualizado, null);
 3187    }
 188
 189    // ── Helper ──────────────────────────────────────────────────────────────
 190
 191    private static DependienteDto MapToDto(Dependiente d)
 192    {
 5193        var aplicaDeduccion = d.Activo && (
 5194            d.Tipo == "conyuge_sin_ingresos"
 5195            || d.Discapacitado
 5196            || (d.FechaNacimiento.HasValue && CalcularEdad(d.FechaNacimiento.Value) < 18)
 5197        );
 198
 5199        return new DependienteDto
 5200        {
 5201            Id = d.Id,
 5202            PersonaId = d.PersonaId,
 5203            CedulaTitular = d.Persona?.Cedula ?? string.Empty,
 5204            NombreTitular = d.Persona?.NombreCompleto ?? string.Empty,
 5205            Tipo = d.Tipo,
 5206            Nombre = d.Nombre,
 5207            Cedula = d.Cedula,
 5208            FechaNacimiento = d.FechaNacimiento,
 5209            Discapacitado = d.Discapacitado,
 5210            PorcentajeDeduccion = d.PorcentajeDeduccion,
 5211            VigenteDesde = d.VigenteDesde,
 5212            VigenteHasta = d.VigenteHasta,
 5213            Activo = d.Activo,
 5214            AplicaDeduccion = aplicaDeduccion
 5215        };
 216    }
 217
 218    private static int CalcularEdad(DateTime fechaNacimiento)
 219    {
 2220        var hoy = DateTime.Today;
 2221        var edad = hoy.Year - fechaNacimiento.Year;
 2222        if (fechaNacimiento.Date > hoy.AddYears(-edad)) edad--;
 2223        return edad;
 224    }
 225}