< Summary

Information
Class: FAU.API.Controllers.BeneficiosSocialesController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/BeneficiosSocialesController.cs
Line coverage
36%
Covered lines: 53
Uncovered lines: 91
Coverable lines: 144
Total lines: 366
Line coverage: 36.8%
Branch coverage
48%
Covered branches: 29
Total branches: 60
Branch coverage: 48.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetBeneficiosSociales()100%1180%
GetBeneficioSocial()100%2262.5%
CreateBeneficioSocial()100%2261.53%
UpdateBeneficioSocial()50%3250%
SubirDocumento()0%7280%
DescargarDocumento()0%620%
AgregarDocumento()0%7280%
ListarDocumentos()100%210%
DescargarDocumentoPorId()0%620%
EliminarDocumento()100%210%
DeleteBeneficioSocial()100%2262.5%
MapearError(...)92.85%1414100%
ObtenerUsuarioIdActual()50%101085.71%
ObtenerHost()50%88100%

File(s)

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

#LineLine coverage
 1using System.Security.Claims;
 2using FAU.Logica.DTOs;
 3using FAU.Logica.Exceptions;
 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/beneficios-sociales")]
 13[Authorize(Roles = "Administrador")]
 14[Produces("application/json")]
 15public class BeneficiosSocialesController : ControllerBase
 16{
 17    private readonly IBeneficioSocialService _beneficioSocialService;
 18    private readonly ILogger<BeneficiosSocialesController> _logger;
 19
 1020    public BeneficiosSocialesController(
 1021        IBeneficioSocialService beneficioSocialService,
 1022        ILogger<BeneficiosSocialesController> logger)
 23    {
 1024        _beneficioSocialService = beneficioSocialService;
 1025        _logger = logger;
 1026    }
 27
 28    [HttpGet]
 29    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 30    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 31    public async Task<IActionResult> GetBeneficiosSociales(
 32        [FromQuery] int pagina = 1,
 33        [FromQuery] int tamano = 10,
 34        [FromQuery] long? personaId = null,
 35        [FromQuery] string? estado = null)
 36    {
 37        try
 38        {
 139            var resultado = await _beneficioSocialService.ObtenerPaginadoAsync(pagina, tamano, personaId, estado);
 140            return Ok(ApiResponse<object>.SuccessResult(new
 141            {
 142                items = resultado.Items,
 143                page = resultado.Page,
 144                pageSize = resultado.PageSize,
 145                totalCount = resultado.TotalCount,
 146                totalPages = resultado.TotalPages,
 147                hasPreviousPage = resultado.HasPreviousPage,
 148                hasNextPage = resultado.HasNextPage
 149            }));
 50        }
 051        catch (Exception ex)
 52        {
 053            _logger.LogError(ex, "Error al listar beneficios sociales");
 054            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 55        }
 156    }
 57
 58    [HttpGet("{id:long}")]
 59    [ProducesResponseType(typeof(ApiResponse<BeneficioSocialDto>), StatusCodes.Status200OK)]
 60    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 61    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 62    public async Task<IActionResult> GetBeneficioSocial(long id)
 63    {
 64        try
 65        {
 266            var beneficio = await _beneficioSocialService.ObtenerPorIdAsync(id);
 267            if (beneficio == null)
 68            {
 169                return NotFound(ApiResponse<string>.ErrorResult("Beneficio social no encontrado"));
 70            }
 71
 172            return Ok(ApiResponse<BeneficioSocialDto>.SuccessResult(beneficio));
 73        }
 074        catch (Exception ex)
 75        {
 076            _logger.LogError(ex, "Error al obtener beneficio social {Id}", id);
 077            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 78        }
 279    }
 80
 81    [HttpPost]
 82    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 83    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 84    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 85    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 86    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 87    public async Task<IActionResult> CreateBeneficioSocial([FromBody] CrearBeneficioSocialRequest request)
 88    {
 89        try
 90        {
 391            var (exito, resultado, error) = await _beneficioSocialService.CrearAsync(request, ObtenerUsuarioIdActual(), 
 392            if (!exito)
 93            {
 294                return MapearError(error, "Error al crear beneficio social");
 95            }
 96
 197            return CreatedAtAction(
 198                nameof(GetBeneficioSocial),
 199                new { id = resultado!.Id },
 1100                ApiResponse<object>.SuccessResult(new { id = resultado.Id, creado = true }, "Beneficio social creado exi
 101        }
 0102        catch (PeriodoBloqueadoException ex)
 103        {
 0104            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 105        }
 0106        catch (Exception ex)
 107        {
 0108            _logger.LogError(ex, "Error al crear beneficio social");
 0109            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 110        }
 3111    }
 112
 113    [HttpPut("{id:long}")]
 114    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 115    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 116    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 117    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 118    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 119    public async Task<IActionResult> UpdateBeneficioSocial(long id, [FromBody] ActualizarBeneficioSocialRequest request)
 120    {
 121        try
 122        {
 2123            var (exito, resultado, error) = await _beneficioSocialService.ActualizarAsync(id, request, ObtenerUsuarioIdA
 2124            if (!exito)
 125            {
 2126                return MapearError(error, "Error al actualizar beneficio social");
 127            }
 128
 0129            return Ok(ApiResponse<object>.SuccessResult(new { actualizado = true, id = resultado!.Id }, "Beneficio socia
 130        }
 0131        catch (Exception ex)
 132        {
 0133            _logger.LogError(ex, "Error al actualizar beneficio social {Id}", id);
 0134            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 135        }
 2136    }
 137
 138    [HttpPost("{id:long}/documento")]
 139    [RequestSizeLimit(20_000_000)]
 140    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 141    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 142    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 143    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 144    public async Task<IActionResult> SubirDocumento(long id, IFormFile file)
 145    {
 0146        if (file == null || file.Length == 0)
 0147            return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 148
 149        try
 150        {
 0151            await using var stream = file.OpenReadStream();
 0152            var ruta = await _beneficioSocialService.SubirDocumentoAsync(
 0153                id, stream, file.FileName, file.ContentType, ObtenerUsuarioIdActual());
 0154            return Ok(ApiResponse<object>.SuccessResult(new { rutaRelativa = ruta, subido = true }, "Documento subido ex
 0155        }
 0156        catch (ArgumentException ex)
 157        {
 0158            return ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase)
 0159                ? NotFound(ApiResponse<string>.ErrorResult(ex.Message))
 0160                : BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 161        }
 0162        catch (Exception ex)
 163        {
 0164            _logger.LogError(ex, "Error al subir documento de beneficio {Id}", id);
 0165            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 166        }
 0167    }
 168
 169    [HttpGet("{id:long}/documento")]
 170    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 171    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 172    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 173    public async Task<IActionResult> DescargarDocumento(long id, [FromQuery] bool inline = false)
 174    {
 175        try
 176        {
 0177            var (stream, nombre, contentType) = await _beneficioSocialService.DescargarDocumentoAsync(id);
 0178            var disposition = inline ? "inline" : "attachment";
 0179            Response.Headers.Append("Content-Disposition", $"{disposition}; filename=\"{nombre}\"");
 0180            return File(stream, contentType);
 181        }
 0182        catch (KeyNotFoundException ex)
 183        {
 0184            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 185        }
 0186        catch (ArgumentException ex)
 187        {
 0188            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 189        }
 0190        catch (Exception ex)
 191        {
 0192            _logger.LogError(ex, "Error al descargar documento del beneficio {Id}", id);
 0193            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 194        }
 0195    }
 196
 197    // â”€â”€ Múltiples documentos â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€â”€
 198
 199    [HttpPost("{id:long}/documentos")]
 200    [RequestSizeLimit(20_000_000)]
 201    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 202    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 203    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 204    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 205    public async Task<IActionResult> AgregarDocumento(long id, IFormFile archivo)
 206    {
 0207        if (archivo == null || archivo.Length == 0)
 0208            return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 209
 210        try
 211        {
 0212            await using var stream = archivo.OpenReadStream();
 0213            var ruta = await _beneficioSocialService.AgregarDocumentoAsync(
 0214                id, stream, archivo.FileName, archivo.ContentType, ObtenerUsuarioIdActual());
 0215            return Ok(ApiResponse<object>.SuccessResult(new { ruta, subido = true }, "Documento agregado correctamente")
 0216        }
 0217        catch (ArgumentException ex)
 218        {
 0219            return ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase)
 0220                ? NotFound(ApiResponse<string>.ErrorResult(ex.Message))
 0221                : BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 222        }
 0223        catch (Exception ex)
 224        {
 0225            _logger.LogError(ex, "Error al agregar documento al beneficio {Id}", id);
 0226            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 227        }
 0228    }
 229
 230    [HttpGet("{id:long}/documentos")]
 231    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 232    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 233    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 234    public async Task<IActionResult> ListarDocumentos(long id)
 235    {
 236        try
 237        {
 0238            var docs = await _beneficioSocialService.ObtenerDocumentosAsync(id);
 0239            return Ok(ApiResponse<object>.SuccessResult(docs));
 240        }
 0241        catch (ArgumentException ex)
 242        {
 0243            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 244        }
 0245        catch (Exception ex)
 246        {
 0247            _logger.LogError(ex, "Error al listar documentos del beneficio {Id}", id);
 0248            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 249        }
 0250    }
 251
 252    [HttpGet("{id:long}/documentos/{docId:long}")]
 253    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 254    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 255    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 256    public async Task<IActionResult> DescargarDocumentoPorId(long id, long docId, [FromQuery] bool inline = false)
 257    {
 258        try
 259        {
 0260            var (stream, nombre, contentType) = await _beneficioSocialService.DescargarDocumentoPorIdAsync(id, docId);
 0261            var disposition = inline ? "inline" : "attachment";
 0262            Response.Headers.Append("Content-Disposition", $"{disposition}; filename=\"{nombre}\"");
 0263            return File(stream, contentType);
 264        }
 0265        catch (KeyNotFoundException ex)
 266        {
 0267            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 268        }
 0269        catch (ArgumentException ex)
 270        {
 0271            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 272        }
 0273        catch (Exception ex)
 274        {
 0275            _logger.LogError(ex, "Error al descargar documento {DocId} del beneficio {Id}", docId, id);
 0276            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 277        }
 0278    }
 279
 280    [HttpDelete("{id:long}/documentos/{docId:long}")]
 281    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status200OK)]
 282    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 283    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 284    public async Task<IActionResult> EliminarDocumento(long id, long docId)
 285    {
 286        try
 287        {
 0288            await _beneficioSocialService.EliminarDocumentoAsync(id, docId);
 0289            return Ok(ApiResponse<string>.SuccessResult("ok", "Documento eliminado correctamente"));
 290        }
 0291        catch (KeyNotFoundException)
 292        {
 0293            return NotFound(ApiResponse<string>.ErrorResult("Documento no encontrado"));
 294        }
 0295        catch (ArgumentException ex)
 296        {
 0297            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 298        }
 0299        catch (Exception ex)
 300        {
 0301            _logger.LogError(ex, "Error al eliminar documento {DocId} del beneficio {Id}", docId, id);
 0302            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 303        }
 0304    }
 305
 306    [HttpDelete("{id:long}")]
 307    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 308    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 309    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 310    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 311    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 312    public async Task<IActionResult> DeleteBeneficioSocial(long id)
 313    {
 314        try
 315        {
 2316            var (exito, bajaLogica, error) = await _beneficioSocialService.EliminarAsync(id, ObtenerUsuarioIdActual(), O
 2317            if (!exito)
 318            {
 1319                return MapearError(error, "Error al eliminar beneficio social");
 320            }
 321
 1322            return Ok(ApiResponse<object>.SuccessResult(new { eliminado = true, bajaLogica }, "Beneficio social dado de 
 323        }
 0324        catch (Exception ex)
 325        {
 0326            _logger.LogError(ex, "Error al eliminar beneficio social {Id}", id);
 0327            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 328        }
 2329    }
 330
 331    private IActionResult MapearError(string? error, string errorPorDefecto)
 332    {
 5333        if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 334        {
 1335            return NotFound(ApiResponse<string>.ErrorResult(error));
 336        }
 337
 4338        if (error?.Contains("duplicado", StringComparison.OrdinalIgnoreCase) == true
 4339            || error?.Contains("ya existe", StringComparison.OrdinalIgnoreCase) == true)
 340        {
 2341            return Conflict(ApiResponse<string>.ErrorResult(error));
 342        }
 343
 2344        return BadRequest(ApiResponse<string>.ErrorResult(error ?? errorPorDefecto));
 345    }
 346
 347    private long ObtenerUsuarioIdActual()
 348    {
 7349        var usuario = HttpContext?.User;
 7350        if (usuario == null)
 351        {
 0352            return 0;
 353        }
 354
 7355        var claimId = usuario.FindFirstValue(ClaimTypes.NameIdentifier)
 7356                      ?? usuario.FindFirstValue("sub")
 7357                      ?? usuario.FindFirstValue("id");
 358
 7359        return long.TryParse(claimId, out var usuarioId) ? usuarioId : 0;
 360    }
 361
 362    private string ObtenerHost()
 363    {
 7364        return HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "Desconocido";
 365    }
 366}