< Summary

Information
Class: FAU.API.Controllers.TiposBeneficioController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/TiposBeneficioController.cs
Line coverage
100%
Covered lines: 156
Uncovered lines: 0
Coverable lines: 156
Total lines: 344
Line coverage: 100%
Branch coverage
88%
Covered branches: 78
Total branches: 88
Branch coverage: 88.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetTiposBeneficio()100%11100%
GetTipoBeneficio()100%22100%
CreateTipoBeneficio()91.66%1212100%
UpdateTipoBeneficio()96.15%2626100%
ActivarTipoBeneficio()100%11100%
DesactivarTipoBeneficio()100%11100%
DeleteTipoBeneficio()85.71%1414100%
CambiarEstadoAsync()93.75%1616100%
ObtenerUsuarioIdActual()70%1010100%
ObtenerHost()75%88100%
MapearRespuesta(...)100%11100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/TiposBeneficioController.cs

#LineLine coverage
 1using System.Security.Claims;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using FAU.Logica.Services;
 5using Microsoft.AspNetCore.Authorization;
 6using Microsoft.AspNetCore.Http;
 7using Microsoft.AspNetCore.Mvc;
 8
 9namespace FAU.API.Controllers;
 10
 11[ApiController]
 12[Route("api/tipos-beneficio")]
 13[Produces("application/json")]
 14public class TiposBeneficioController : ControllerBase
 15{
 16    private readonly ITipoBeneficioService _tipoBeneficioService;
 17    private readonly ILogger<TiposBeneficioController> _logger;
 18
 3019    public TiposBeneficioController(
 3020        ITipoBeneficioService tipoBeneficioService,
 3021        ILogger<TiposBeneficioController> logger)
 22    {
 3023        _tipoBeneficioService = tipoBeneficioService;
 3024        _logger = logger;
 3025    }
 26
 27    [HttpGet]
 28    [FAU.API.Security.RequirePermission(PermisoEnum.VerBeneficios)]
 29    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 30    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 31    public async Task<IActionResult> GetTiposBeneficio(
 32        [FromQuery] int pagina = 1,
 33        [FromQuery] int tamano = 10,
 34        [FromQuery] string? busqueda = null,
 35        [FromQuery] bool? activo = null)
 36    {
 37        try
 38        {
 239            var resultado = await _tipoBeneficioService.ObtenerPaginadoAsync(pagina, tamano, busqueda, activo);
 40
 141            var response = new
 142            {
 143                items = resultado.Items,
 144                page = resultado.Page,
 145                pageSize = resultado.PageSize,
 146                totalCount = resultado.TotalCount,
 147                totalPages = resultado.TotalPages,
 148                hasPreviousPage = resultado.HasPreviousPage,
 149                hasNextPage = resultado.HasNextPage
 150            };
 51
 152            return Ok(ApiResponse<object>.SuccessResult(response));
 53        }
 154        catch (Exception ex)
 55        {
 156            _logger.LogError(ex, "Error al listar tipos de beneficio");
 157            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 58        }
 259    }
 60
 61    [HttpGet("{id:long}")]
 62    [FAU.API.Security.RequirePermission(PermisoEnum.VerBeneficios)]
 63    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 64    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 65    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 66    public async Task<IActionResult> GetTipoBeneficio(long id)
 67    {
 68        try
 69        {
 370            var tipo = await _tipoBeneficioService.ObtenerPorIdAsync(id);
 271            if (tipo == null)
 72            {
 173                return NotFound(ApiResponse<string>.ErrorResult("Tipo de beneficio no encontrado"));
 74            }
 75
 176            return Ok(ApiResponse<object>.SuccessResult(MapearRespuesta(tipo)));
 77        }
 178        catch (Exception ex)
 79        {
 180            _logger.LogError(ex, "Error al obtener tipo de beneficio {Id}", id);
 181            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 82        }
 383    }
 84
 85    [HttpPost]
 86    [FAU.API.Security.RequirePermission(PermisoEnum.CrearBeneficios)]
 87    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 88    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 89    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 90    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 91    public async Task<IActionResult> CreateTipoBeneficio([FromBody] CrearTipoBeneficioRequest request)
 92    {
 93        try
 94        {
 795            if (string.IsNullOrWhiteSpace(request.Codigo))
 96            {
 197                return BadRequest(ApiResponse<string>.ErrorResult("El codigo es requerido"));
 98            }
 99
 6100            if (string.IsNullOrWhiteSpace(request.Nombre))
 101            {
 1102                return BadRequest(ApiResponse<string>.ErrorResult("El nombre es requerido"));
 103            }
 104
 5105            var (exito, resultado, error) = await _tipoBeneficioService.CrearAsync(
 5106                request.Codigo,
 5107                request.Nombre,
 5108                request.EsMensual,
 5109                request.PermiteRetroactividad,
 5110                request.PlazoPresentacionDias,
 5111                request.MontoReferencia,
 5112                request.CatalogoItemId,
 5113                request.Activo,
 5114                ObtenerUsuarioIdActual(),
 5115                ObtenerHost());
 116
 4117            if (!exito)
 118            {
 2119                if (error?.Contains("ya existe", StringComparison.OrdinalIgnoreCase) == true)
 120                {
 1121                    return Conflict(ApiResponse<string>.ErrorResult(error));
 122                }
 123
 1124                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al crear tipo de beneficio"));
 125            }
 126
 2127            return CreatedAtAction(
 2128                nameof(GetTipoBeneficio),
 2129                new { id = resultado!.Id },
 2130                ApiResponse<object>.SuccessResult(new { id = resultado.Id, creado = true }, "Tipo de beneficio creado ex
 131        }
 1132        catch (Exception ex)
 133        {
 1134            _logger.LogError(ex, "Error al crear tipo de beneficio");
 1135            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 136        }
 7137    }
 138
 139    [HttpPut("{id:long}")]
 140    [FAU.API.Security.RequirePermission(PermisoEnum.ActualizarBeneficios)]
 141    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 142    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 143    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 144    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 145    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 146    public async Task<IActionResult> UpdateTipoBeneficio(long id, [FromBody] ActualizarTipoBeneficioRequest request)
 147    {
 148        try
 149        {
 6150            if (request.Codigo == null &&
 6151                request.Nombre == null &&
 6152                !request.EsMensual.HasValue &&
 6153                !request.PermiteRetroactividad.HasValue &&
 6154                !request.PlazoPresentacionDias.HasValue &&
 6155                !request.MontoReferencia.HasValue &&
 6156                !request.CatalogoItemId.HasValue)
 157            {
 1158                return BadRequest(ApiResponse<string>.ErrorResult("Debe proporcionar al menos un campo para actualizar")
 159            }
 160
 5161            var (exito, resultado, error) = await _tipoBeneficioService.ActualizarAsync(
 5162                id,
 5163                request.Codigo,
 5164                request.Nombre,
 5165                request.EsMensual,
 5166                request.PermiteRetroactividad,
 5167                request.PlazoPresentacionDias,
 5168                request.MontoReferencia,
 5169                request.CatalogoItemId,
 5170                ObtenerUsuarioIdActual(),
 5171                ObtenerHost());
 172
 4173            if (!exito)
 174            {
 3175                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 176                {
 1177                    return NotFound(ApiResponse<string>.ErrorResult(error));
 178                }
 179
 2180                if (error?.Contains("ya existe", StringComparison.OrdinalIgnoreCase) == true)
 181                {
 1182                    return Conflict(ApiResponse<string>.ErrorResult(error));
 183                }
 184
 1185                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al actualizar tipo de beneficio"));
 186            }
 187
 1188            return Ok(ApiResponse<object>.SuccessResult(new
 1189            {
 1190                actualizado = true,
 1191                id = resultado!.Id
 1192            }, "Tipo de beneficio actualizado exitosamente"));
 193        }
 1194        catch (Exception ex)
 195        {
 1196            _logger.LogError(ex, "Error al actualizar tipo de beneficio {Id}", id);
 1197            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 198        }
 6199    }
 200
 201    [HttpPatch("{id:long}/activar")]
 202    [FAU.API.Security.RequirePermission(PermisoEnum.ActivarBeneficios)]
 203    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 204    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 205    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 206    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 207    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 208    public async Task<IActionResult> ActivarTipoBeneficio(long id)
 209    {
 3210        return await CambiarEstadoAsync(id, activar: true);
 3211    }
 212
 213    [HttpPatch("{id:long}/desactivar")]
 214    [FAU.API.Security.RequirePermission(PermisoEnum.DesactivarBeneficios)]
 215    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 216    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 217    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 218    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 219    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 220    public async Task<IActionResult> DesactivarTipoBeneficio(long id)
 221    {
 4222        return await CambiarEstadoAsync(id, activar: false);
 4223    }
 224
 225    [HttpDelete("{id:long}")]
 226    [FAU.API.Security.RequirePermission(PermisoEnum.EliminarBeneficios)]
 227    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 228    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 229    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 230    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 231    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 232    public async Task<IActionResult> DeleteTipoBeneficio(long id)
 233    {
 234        try
 235        {
 5236            var (exito, bajaLogica, error) = await _tipoBeneficioService.EliminarAsync(
 5237                id,
 5238                ObtenerUsuarioIdActual(),
 5239                ObtenerHost());
 240
 4241            if (!exito)
 242            {
 3243                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 244                {
 1245                    return NotFound(ApiResponse<string>.ErrorResult(error));
 246                }
 247
 2248                if (error?.Contains("liquidaciones abiertas", StringComparison.OrdinalIgnoreCase) == true)
 249                {
 1250                    return Conflict(ApiResponse<string>.ErrorResult(error));
 251                }
 252
 1253                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al eliminar tipo de beneficio"));
 254            }
 255
 1256            return Ok(ApiResponse<object>.SuccessResult(new
 1257            {
 1258                eliminado = true,
 1259                bajaLogica
 1260            }, bajaLogica
 1261                ? "Tipo de beneficio dado de baja logicamente"
 1262                : "Tipo de beneficio eliminado exitosamente"));
 263        }
 1264        catch (Exception ex)
 265        {
 1266            _logger.LogError(ex, "Error al eliminar tipo de beneficio {Id}", id);
 1267            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 268        }
 5269    }
 270
 271    private async Task<IActionResult> CambiarEstadoAsync(long id, bool activar)
 272    {
 273        try
 274        {
 7275            var (exito, resultado, error) = activar
 7276                ? await _tipoBeneficioService.ActivarAsync(id, ObtenerUsuarioIdActual(), ObtenerHost())
 7277                : await _tipoBeneficioService.DesactivarAsync(id, ObtenerUsuarioIdActual(), ObtenerHost());
 278
 6279            if (!exito)
 280            {
 4281                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 282                {
 2283                    return NotFound(ApiResponse<string>.ErrorResult(error));
 284                }
 285
 2286                if (error?.Contains("liquidaciones abiertas", StringComparison.OrdinalIgnoreCase) == true)
 287                {
 1288                    return Conflict(ApiResponse<string>.ErrorResult(error));
 289                }
 290
 1291                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al cambiar estado"));
 292            }
 293
 2294            return Ok(ApiResponse<object>.SuccessResult(new
 2295            {
 2296                id = resultado!.Id,
 2297                activo = resultado.Activo
 2298            }, activar
 2299                ? "Tipo de beneficio activado exitosamente"
 2300                : "Tipo de beneficio desactivado exitosamente"));
 301        }
 1302        catch (Exception ex)
 303        {
 1304            _logger.LogError(ex, "Error al cambiar estado del tipo de beneficio {Id}", id);
 1305            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 306        }
 7307    }
 308
 309    private long ObtenerUsuarioIdActual()
 310    {
 22311        var usuario = HttpContext?.User;
 22312        if (usuario == null)
 313        {
 1314            return 0;
 315        }
 316
 21317        var claimId = usuario.FindFirstValue(ClaimTypes.NameIdentifier)
 21318                      ?? usuario.FindFirstValue("sub")
 21319                      ?? usuario.FindFirstValue("id");
 320
 21321        return long.TryParse(claimId, out var usuarioId) ? usuarioId : 0;
 322    }
 323
 324    private string ObtenerHost()
 325    {
 22326        return HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "Desconocido";
 327    }
 328
 329    private static object MapearRespuesta(TipoBeneficio tipo)
 330    {
 1331        return new
 1332        {
 1333            id = tipo.Id,
 1334            codigo = tipo.Codigo,
 1335            nombre = tipo.Nombre,
 1336            esMensual = tipo.EsMensual,
 1337            permiteRetroactividad = tipo.PermiteRetroactividad,
 1338            plazoPresentacionDias = tipo.PlazoPresentacionDias,
 1339            montoReferencia = tipo.MontoReferencia,
 1340            catalogoItemId = tipo.CatalogoItemId,
 1341            activo = tipo.Activo
 1342        };
 343    }
 344}