< Summary

Information
Class: FAU.API.Controllers.ReporteControlController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/ReporteControlController.cs
Line coverage
79%
Covered lines: 92
Uncovered lines: 24
Coverable lines: 116
Total lines: 270
Line coverage: 79.3%
Branch coverage
100%
Covered branches: 40
Total branches: 40
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetEfectivosSuperior()100%4472.72%
GetEfectivosSuperiorCsv()100%4475%
GetEfectivosSubalterno()100%4472.72%
GetEfectivosSubalternoCsv()100%4475%
GetListin()100%4472.72%
GetListinCsv()100%4475%
PostComparativoSgh()100%9876.92%
PostComparativoSghCsv()100%9878.57%
RegistrarAuditoriaAsync(...)100%11100%

File(s)

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

#LineLine coverage
 1using FAU.API.Security;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using FAU.Logica.Services;
 5using Microsoft.AspNetCore.Authorization;
 6using Microsoft.AspNetCore.Mvc;
 7
 8namespace FAU.API.Controllers;
 9
 10[ApiController]
 11[Route("api/liquidacion/reportes-control")]
 12[Authorize]
 13public class ReporteControlController : ControllerBase
 14{
 15    private readonly IReporteControlService _service;
 16    private readonly IPeriodoService _periodoService;
 17    private readonly IAuditoriaService _auditoriaService;
 18    private readonly ICurrentUserContext _currentUser;
 19    private readonly ILogger<ReporteControlController> _logger;
 20
 2721    public ReporteControlController(
 2722        IReporteControlService service,
 2723        IPeriodoService periodoService,
 2724        IAuditoriaService auditoriaService,
 2725        ICurrentUserContext currentUser,
 2726        ILogger<ReporteControlController> logger)
 27    {
 2728        _service          = service;
 2729        _periodoService   = periodoService;
 2730        _auditoriaService = auditoriaService;
 2731        _currentUser      = currentUser;
 2732        _logger           = logger;
 2733    }
 34
 35    // ─── Cuadro de Efectivos Superior ───────────────────────────────────────
 36
 37    [HttpGet("{periodoId}/efectivos-superior")]
 38    [RequirePermission(PermisoEnum.VerReportesControl)]
 39    public async Task<IActionResult> GetEfectivosSuperior(
 40        long periodoId,
 41        [FromQuery] string? programa,
 42        [FromQuery] string? regimen)
 43    {
 44        try
 45        {
 346            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 347            if (periodo == null)
 148                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 249            if (periodo.Estado != Periodo.EstadoCerrada)
 150                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 51
 152            var result = await _service.ObtenerEfectivosSuperiorAsync(periodoId, programa, regimen);
 153            return Ok(ApiResponse<CuadroEfectivosDto>.SuccessResult(result));
 54        }
 055        catch (Exception ex)
 56        {
 057            _logger.LogError(ex, "Error al obtener efectivos superior del período {PeriodoId}", periodoId);
 058            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 59        }
 360    }
 61
 62    [HttpGet("{periodoId}/efectivos-superior/csv")]
 63    [RequirePermission(PermisoEnum.VerReportesControl)]
 64    public async Task<IActionResult> GetEfectivosSuperiorCsv(
 65        long periodoId,
 66        [FromQuery] string? programa,
 67        [FromQuery] string? regimen)
 68    {
 69        try
 70        {
 371            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 372            if (periodo == null)
 173                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 274            if (periodo.Estado != Periodo.EstadoCerrada)
 175                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 76
 177            var csv = await _service.GenerarCsvEfectivosSuperiorAsync(periodoId, programa, regimen);
 178            await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteEfectivosSuperior, periodoId);
 179            return File(csv, "text/csv", $"efectivos_superior_{periodo.Anio}_{periodo.Mes:D2}.csv");
 80        }
 081        catch (Exception ex)
 82        {
 083            _logger.LogError(ex, "Error al generar CSV efectivos superior del período {PeriodoId}", periodoId);
 084            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 85        }
 386    }
 87
 88    // ─── Cuadro de Efectivos Subalterno ─────────────────────────────────────
 89
 90    [HttpGet("{periodoId}/efectivos-subalterno")]
 91    [RequirePermission(PermisoEnum.VerReportesControl)]
 92    public async Task<IActionResult> GetEfectivosSubalterno(
 93        long periodoId,
 94        [FromQuery] string? programa,
 95        [FromQuery] string? regimen)
 96    {
 97        try
 98        {
 399            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3100            if (periodo == null)
 1101                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2102            if (periodo.Estado != Periodo.EstadoCerrada)
 1103                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 104
 1105            var result = await _service.ObtenerEfectivosSubalternoAsync(periodoId, programa, regimen);
 1106            return Ok(ApiResponse<CuadroEfectivosSubalternoDto>.SuccessResult(result));
 107        }
 0108        catch (Exception ex)
 109        {
 0110            _logger.LogError(ex, "Error al obtener efectivos subalterno del período {PeriodoId}", periodoId);
 0111            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 112        }
 3113    }
 114
 115    [HttpGet("{periodoId}/efectivos-subalterno/csv")]
 116    [RequirePermission(PermisoEnum.VerReportesControl)]
 117    public async Task<IActionResult> GetEfectivosSubalternoCsv(
 118        long periodoId,
 119        [FromQuery] string? programa,
 120        [FromQuery] string? regimen)
 121    {
 122        try
 123        {
 3124            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3125            if (periodo == null)
 1126                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2127            if (periodo.Estado != Periodo.EstadoCerrada)
 1128                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 129
 1130            var csv = await _service.GenerarCsvEfectivosSubalternoAsync(periodoId, programa, regimen);
 1131            await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteEfectivosSubalterno, periodoId);
 1132            return File(csv, "text/csv", $"efectivos_subalterno_{periodo.Anio}_{periodo.Mes:D2}.csv");
 133        }
 0134        catch (Exception ex)
 135        {
 0136            _logger.LogError(ex, "Error al generar CSV efectivos subalterno del período {PeriodoId}", periodoId);
 0137            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 138        }
 3139    }
 140
 141    // ─── Listín de Contralor ─────────────────────────────────────────────────
 142
 143    [HttpGet("{periodoId}/listin")]
 144    [RequirePermission(PermisoEnum.VerReportesControl)]
 145    public async Task<IActionResult> GetListin(
 146        long periodoId,
 147        [FromQuery] int? situacionDesde,
 148        [FromQuery] int? situacionHasta)
 149    {
 150        try
 151        {
 3152            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3153            if (periodo == null)
 1154                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2155            if (periodo.Estado != Periodo.EstadoCerrada)
 1156                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 157
 1158            var result = await _service.ObtenerListinAsync(periodoId, situacionDesde, situacionHasta);
 1159            return Ok(ApiResponse<ListinContralorDto>.SuccessResult(result));
 160        }
 0161        catch (Exception ex)
 162        {
 0163            _logger.LogError(ex, "Error al obtener listín del período {PeriodoId}", periodoId);
 0164            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 165        }
 3166    }
 167
 168    [HttpGet("{periodoId}/listin/csv")]
 169    [RequirePermission(PermisoEnum.VerReportesControl)]
 170    public async Task<IActionResult> GetListinCsv(
 171        long periodoId,
 172        [FromQuery] int? situacionDesde,
 173        [FromQuery] int? situacionHasta)
 174    {
 175        try
 176        {
 3177            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3178            if (periodo == null)
 1179                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2180            if (periodo.Estado != Periodo.EstadoCerrada)
 1181                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 182
 1183            var csv = await _service.GenerarCsvListinAsync(periodoId, situacionDesde, situacionHasta);
 1184            await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteListin, periodoId);
 1185            return File(csv, "text/csv", $"listin_{periodo.Anio}_{periodo.Mes:D2}.csv");
 186        }
 0187        catch (Exception ex)
 188        {
 0189            _logger.LogError(ex, "Error al generar CSV listín del período {PeriodoId}", periodoId);
 0190            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 191        }
 3192    }
 193
 194    // ─── Comparativo SGH ─────────────────────────────────────────────────────
 195
 196    /// <summary>
 197    /// Compara el padrón SGH enviado en el body contra los funcionarios activos del sistema.
 198    /// POST /api/liquidacion/reportes-control/{periodoId}/sgh/comparativo
 199    /// </summary>
 200    [HttpPost("{periodoId}/sgh/comparativo")]
 201    [RequirePermission(PermisoEnum.VerReportesControl)]
 202    public async Task<IActionResult> PostComparativoSgh(
 203        long periodoId,
 204        [FromBody] List<PadronSghEntradaDto> filas)
 205    {
 206        try
 207        {
 4208            if (filas == null || filas.Count == 0)
 1209                return BadRequest(ApiResponse<string>.ErrorResult("El padrón SGH no puede estar vacío"));
 210
 3211            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3212            if (periodo == null)
 1213                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2214            if (periodo.Estado != Periodo.EstadoCerrada)
 1215                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 216
 1217            var result = await _service.ObtenerComparativoSghAsync(periodoId, filas);
 1218            return Ok(ApiResponse<ComparativoSghDto>.SuccessResult(result));
 219        }
 0220        catch (Exception ex)
 221        {
 0222            _logger.LogError(ex, "Error al obtener comparativo SGH del período {PeriodoId}", periodoId);
 0223            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 224        }
 4225    }
 226
 227    /// <summary>
 228    /// Descarga el comparativo SGH como CSV.
 229    /// POST /api/liquidacion/reportes-control/{periodoId}/sgh/comparativo/csv
 230    /// </summary>
 231    [HttpPost("{periodoId}/sgh/comparativo/csv")]
 232    [RequirePermission(PermisoEnum.VerReportesControl)]
 233    public async Task<IActionResult> PostComparativoSghCsv(
 234        long periodoId,
 235        [FromBody] List<PadronSghEntradaDto> filas)
 236    {
 237        try
 238        {
 4239            if (filas == null || filas.Count == 0)
 1240                return BadRequest(ApiResponse<string>.ErrorResult("El padrón SGH no puede estar vacío"));
 241
 3242            var periodo = await _periodoService.ObtenerPorIdAsync(periodoId);
 3243            if (periodo == null)
 1244                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 2245            if (periodo.Estado != Periodo.EstadoCerrada)
 1246                return Conflict(ApiResponse<string>.ErrorResult("El período no está cerrado"));
 247
 1248            var csv = await _service.GenerarCsvComparativoSghAsync(periodoId, filas);
 1249            await RegistrarAuditoriaAsync(AccionEnum.GenerarReporteComparativoSgh, periodoId);
 1250            return File(csv, "text/csv", $"comparativo_sgh_{periodo.Anio}_{periodo.Mes:D2}.csv");
 251        }
 0252        catch (Exception ex)
 253        {
 0254            _logger.LogError(ex, "Error al generar CSV comparativo SGH del período {PeriodoId}", periodoId);
 0255            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 256        }
 4257    }
 258
 259    // ─── Helper ──────────────────────────────────────────────────────────────
 260
 261    private Task RegistrarAuditoriaAsync(AccionEnum accion, long periodoId) =>
 4262        _auditoriaService.LogAuditoriaAsync(
 4263            _currentUser.UserId ?? 0,
 4264            accion,
 4265            ContextoEnum.Liquidaciones,
 4266            _currentUser.Host,
 4267            entidad: "ReporteControl",
 4268            entidadId: periodoId,
 4269            detalle: new { periodoId });
 270}