< 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
93%
Covered lines: 135
Uncovered lines: 9
Coverable lines: 144
Total lines: 366
Line coverage: 93.7%
Branch coverage
81%
Covered branches: 49
Total branches: 60
Branch coverage: 81.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%
GetBeneficiosSociales()100%11100%
GetBeneficioSocial()100%22100%
CreateBeneficioSocial()100%22100%
UpdateBeneficioSocial()100%22100%
SubirDocumento()100%8893.33%
DescargarDocumento()0%2275%
AgregarDocumento()100%8893.33%
ListarDocumentos()100%11100%
DescargarDocumentoPorId()0%2275%
EliminarDocumento()100%11100%
DeleteBeneficioSocial()100%22100%
MapearError(...)92.85%1414100%
ObtenerUsuarioIdActual()80%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
 4220    public BeneficiosSocialesController(
 4221        IBeneficioSocialService beneficioSocialService,
 4222        ILogger<BeneficiosSocialesController> logger)
 23    {
 4224        _beneficioSocialService = beneficioSocialService;
 4225        _logger = logger;
 4226    }
 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        {
 239            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        }
 151        catch (Exception ex)
 52        {
 153            _logger.LogError(ex, "Error al listar beneficios sociales");
 154            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 55        }
 256    }
 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        {
 366            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        }
 174        catch (Exception ex)
 75        {
 176            _logger.LogError(ex, "Error al obtener beneficio social {Id}", id);
 177            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 78        }
 379    }
 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        {
 691            var (exito, resultado, error) = await _beneficioSocialService.CrearAsync(request, ObtenerUsuarioIdActual(), 
 492            if (!exito)
 93            {
 394                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        }
 1102        catch (PeriodoBloqueadoException ex)
 103        {
 1104            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 105        }
 1106        catch (Exception ex)
 107        {
 1108            _logger.LogError(ex, "Error al crear beneficio social");
 1109            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 110        }
 6111    }
 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        {
 4123            var (exito, resultado, error) = await _beneficioSocialService.ActualizarAsync(id, request, ObtenerUsuarioIdA
 3124            if (!exito)
 125            {
 2126                return MapearError(error, "Error al actualizar beneficio social");
 127            }
 128
 1129            return Ok(ApiResponse<object>.SuccessResult(new { actualizado = true, id = resultado!.Id }, "Beneficio socia
 130        }
 1131        catch (Exception ex)
 132        {
 1133            _logger.LogError(ex, "Error al actualizar beneficio social {Id}", id);
 1134            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 135        }
 4136    }
 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    {
 5146        if (file == null || file.Length == 0)
 1147            return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 148
 149        try
 150        {
 4151            await using var stream = file.OpenReadStream();
 4152            var ruta = await _beneficioSocialService.SubirDocumentoAsync(
 4153                id, stream, file.FileName, file.ContentType, ObtenerUsuarioIdActual());
 1154            return Ok(ApiResponse<object>.SuccessResult(new { rutaRelativa = ruta, subido = true }, "Documento subido ex
 0155        }
 2156        catch (ArgumentException ex)
 157        {
 2158            return ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase)
 2159                ? NotFound(ApiResponse<string>.ErrorResult(ex.Message))
 2160                : BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 161        }
 1162        catch (Exception ex)
 163        {
 1164            _logger.LogError(ex, "Error al subir documento de beneficio {Id}", id);
 1165            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 166        }
 5167    }
 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        {
 3177            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        }
 1182        catch (KeyNotFoundException ex)
 183        {
 1184            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 185        }
 1186        catch (ArgumentException ex)
 187        {
 1188            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 189        }
 1190        catch (Exception ex)
 191        {
 1192            _logger.LogError(ex, "Error al descargar documento del beneficio {Id}", id);
 1193            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 194        }
 3195    }
 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    {
 5207        if (archivo == null || archivo.Length == 0)
 1208            return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 209
 210        try
 211        {
 4212            await using var stream = archivo.OpenReadStream();
 4213            var ruta = await _beneficioSocialService.AgregarDocumentoAsync(
 4214                id, stream, archivo.FileName, archivo.ContentType, ObtenerUsuarioIdActual());
 1215            return Ok(ApiResponse<object>.SuccessResult(new { ruta, subido = true }, "Documento agregado correctamente")
 0216        }
 2217        catch (ArgumentException ex)
 218        {
 2219            return ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase)
 2220                ? NotFound(ApiResponse<string>.ErrorResult(ex.Message))
 2221                : BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 222        }
 1223        catch (Exception ex)
 224        {
 1225            _logger.LogError(ex, "Error al agregar documento al beneficio {Id}", id);
 1226            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 227        }
 5228    }
 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        {
 3238            var docs = await _beneficioSocialService.ObtenerDocumentosAsync(id);
 1239            return Ok(ApiResponse<object>.SuccessResult(docs));
 240        }
 1241        catch (ArgumentException ex)
 242        {
 1243            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 244        }
 1245        catch (Exception ex)
 246        {
 1247            _logger.LogError(ex, "Error al listar documentos del beneficio {Id}", id);
 1248            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 249        }
 3250    }
 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        {
 3260            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        }
 1265        catch (KeyNotFoundException ex)
 266        {
 1267            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 268        }
 1269        catch (ArgumentException ex)
 270        {
 1271            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 272        }
 1273        catch (Exception ex)
 274        {
 1275            _logger.LogError(ex, "Error al descargar documento {DocId} del beneficio {Id}", docId, id);
 1276            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 277        }
 3278    }
 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        {
 4288            await _beneficioSocialService.EliminarDocumentoAsync(id, docId);
 1289            return Ok(ApiResponse<string>.SuccessResult("ok", "Documento eliminado correctamente"));
 290        }
 1291        catch (KeyNotFoundException)
 292        {
 1293            return NotFound(ApiResponse<string>.ErrorResult("Documento no encontrado"));
 294        }
 1295        catch (ArgumentException ex)
 296        {
 1297            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 298        }
 1299        catch (Exception ex)
 300        {
 1301            _logger.LogError(ex, "Error al eliminar documento {DocId} del beneficio {Id}", docId, id);
 1302            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 303        }
 4304    }
 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        {
 4316            var (exito, bajaLogica, error) = await _beneficioSocialService.EliminarAsync(id, ObtenerUsuarioIdActual(), O
 3317            if (!exito)
 318            {
 2319                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        }
 1324        catch (Exception ex)
 325        {
 1326            _logger.LogError(ex, "Error al eliminar beneficio social {Id}", id);
 1327            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 328        }
 4329    }
 330
 331    private IActionResult MapearError(string? error, string errorPorDefecto)
 332    {
 7333        if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 334        {
 3335            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    {
 22349        var usuario = HttpContext?.User;
 22350        if (usuario == null)
 351        {
 0352            return 0;
 353        }
 354
 22355        var claimId = usuario.FindFirstValue(ClaimTypes.NameIdentifier)
 22356                      ?? usuario.FindFirstValue("sub")
 22357                      ?? usuario.FindFirstValue("id");
 358
 22359        return long.TryParse(claimId, out var usuarioId) ? usuarioId : 0;
 360    }
 361
 362    private string ObtenerHost()
 363    {
 14364        return HttpContext?.Connection?.RemoteIpAddress?.ToString() ?? "Desconocido";
 365    }
 366}