< Summary

Information
Class: FAU.API.Controllers.AguinaldosController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/AguinaldosController.cs
Line coverage
70%
Covered lines: 87
Uncovered lines: 37
Coverable lines: 124
Total lines: 346
Line coverage: 70.1%
Branch coverage
96%
Covered branches: 54
Total branches: 56
Branch coverage: 96.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Validar()100%11100%
Calcular()75%44100%
ValidarBaja()100%1150%
CalcularBaja()75%5462.5%
CompletarHistorico()100%5462.5%
ObtenerPorPeriodoBaja()100%8662.5%
GenerarRecibosSemestral()100%151481.25%
GenerarReciboPdfSemestral()100%1162.5%
GenerarRecibosBaja()100%8661.53%
GenerarPadronSemestral()100%8662.5%
GenerarReporteControl()100%151272.72%
GenerarReciboPdfBaja()100%210%

File(s)

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

#LineLine coverage
 1using FAU.Logica.DTOs;
 2using FAU.Logica.Services;
 3using Microsoft.AspNetCore.Authorization;
 4using Microsoft.AspNetCore.Http;
 5using Microsoft.AspNetCore.Mvc;
 6
 7namespace FAU.API.Controllers;
 8
 9[ApiController]
 10[Route("api/[controller]")]
 11[Authorize]
 12public class AguinaldosController : ControllerBase
 13{
 14    private readonly IAguinaldoSemestralService _service;
 15    private readonly IAguinaldoBajaService _bajaService;
 16    private readonly IAguinaldoHistoricoService _historicoService;
 17    private readonly IAguinaldoReciboService _reciboService;
 18    private readonly IAguinaldoPadronService _padronService;
 19    private readonly IAguinaldoReporteControlService _reporteControlService;
 20    private readonly ILogger<AguinaldosController> _logger;
 21
 2622    public AguinaldosController(
 2623        IAguinaldoSemestralService service,
 2624        IAguinaldoBajaService bajaService,
 2625        IAguinaldoHistoricoService historicoService,
 2626        IAguinaldoReciboService reciboService,
 2627        IAguinaldoPadronService padronService,
 2628        IAguinaldoReporteControlService reporteControlService,
 2629        ILogger<AguinaldosController> logger)
 30    {
 2631        _service = service;
 2632        _bajaService = bajaService;
 2633        _historicoService = historicoService;
 2634        _reciboService = reciboService;
 2635        _padronService = padronService;
 2636        _reporteControlService = reporteControlService;
 2637        _logger = logger;
 2638    }
 39
 40    [HttpPost("validar")]
 41    public async Task<IActionResult> Validar([FromBody] CalcularAguinaldoSemestralRequest request)
 42    {
 43        try
 44        {
 245            var resultado = await _service.ValidarHistoricoAsync(request);
 146            return Ok(ApiResponse<AguinaldoValidacionDto>.SuccessResult(resultado));
 47        }
 148        catch (Exception ex)
 49        {
 150            _logger.LogError(ex, "Error validando histórico de aguinaldo");
 151            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 52        }
 253    }
 54
 55    [HttpPost("calcular")]
 56    public async Task<IActionResult> Calcular([FromBody] CalcularAguinaldoSemestralRequest request)
 57    {
 58        try
 59        {
 360            var (success, resultados, error) = await _service.CalcularAguinaldoSemestralAsync(request);
 261            if (!success)
 62            {
 163                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error en cálculo"));
 64            }
 65
 166            return Ok(ApiResponse<IEnumerable<AguinaldoSemestralDto>>.SuccessResult(resultados));
 67        }
 168        catch (Exception ex)
 69        {
 170            _logger.LogError(ex, "Error calculando aguinaldo semestral");
 171            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 72        }
 373    }
 74
 75    [HttpPost("validar/baja")]
 76    public async Task<IActionResult> ValidarBaja([FromBody] CalcularAguinaldoBajaRequest request)
 77    {
 78        try
 79        {
 180            var resultado = await _bajaService.ValidarHistoricoAsync(request);
 181            return Ok(ApiResponse<AguinaldoValidacionDto>.SuccessResult(resultado));
 82        }
 083        catch (Exception ex)
 84        {
 085            _logger.LogError(ex, "Error validando histórico de aguinaldo por baja");
 086            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 87        }
 188    }
 89
 90    [HttpPost("calcular/baja")]
 91    public async Task<IActionResult> CalcularBaja([FromBody] CalcularAguinaldoBajaRequest request)
 92    {
 93        try
 94        {
 295            var (success, resultados, error) = await _bajaService.CalcularAguinaldoBajaAsync(request);
 296            if (!success)
 97            {
 198                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error en cálculo de aguinaldo por baja"));
 99            }
 100
 1101            return Ok(ApiResponse<IEnumerable<AguinaldoBajaDto>>.SuccessResult(resultados));
 102        }
 0103        catch (Exception ex)
 104        {
 0105            _logger.LogError(ex, "Error calculando aguinaldo por baja");
 0106            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 107        }
 2108    }
 109
 110    [HttpPost("completar-historico")]
 111    public async Task<IActionResult> CompletarHistorico([FromBody] CompletarHistoricoAguinaldoRequest request)
 112    {
 113        try
 114        {
 2115            if (request.Funcionarios == null || !request.Funcionarios.Any())
 1116                return BadRequest(ApiResponse<string>.ErrorResult("La lista de funcionarios no puede estar vacía."));
 117
 1118            var resultado = await _historicoService.CompletarHistoricoAsync(request);
 1119            return Ok(ApiResponse<CompletarHistoricoAguinaldoResultadoDto>.SuccessResult(resultado));
 120        }
 0121        catch (Exception ex)
 122        {
 0123            _logger.LogError(ex, "Error completando histórico de aguinaldo");
 0124            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 125        }
 2126    }
 127
 128    [HttpGet("periodo/baja")]
 129    public async Task<IActionResult> ObtenerPorPeriodoBaja([FromQuery] int anio, [FromQuery] int mes)
 130    {
 131        try
 132        {
 2133            if (anio <= 0 || (mes != 6 && mes != 12))
 134            {
 1135                return BadRequest(ApiResponse<string>.ErrorResult("El período de consulta debe usar mes 6 o 12 y un año 
 136            }
 137
 1138            var resultados = await _bajaService.ObtenerPorPeriodoAsync(anio, mes);
 1139            return Ok(ApiResponse<IEnumerable<AguinaldoBajaDto>>.SuccessResult(resultados));
 140        }
 0141        catch (Exception ex)
 142        {
 0143            _logger.LogError(ex, "Error consultando aguinaldo por baja");
 0144            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 145        }
 2146    }
 147
 148    /// <summary>
 149    /// Genera un ZIP con los recibos PDF del aguinaldo semestral para los funcionarios indicados.
 150    /// Si PersonaIds es null o vacío, genera para todos los del período.
 151    /// POST /api/aguinaldos/recibos/semestral
 152    /// </summary>
 153    [HttpPost("recibos/semestral")]
 154    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 155    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 156    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 157    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 158    public async Task<IActionResult> GenerarRecibosSemestral([FromBody] GenerarRecibosAguinaldoSemestralRequest request)
 159    {
 160        try
 161        {
 4162            if (request.Anio <= 0 || (request.Semestre != 1 && request.Semestre != 2))
 1163                return BadRequest(ApiResponse<string>.ErrorResult("Año y semestre inválidos. El semestre debe ser 1 o 2.
 164
 3165            var personaIds = request.PersonaIds?.Count > 0 ? request.PersonaIds : null;
 166
 3167            if (personaIds?.Count == 1)
 168            {
 1169                var (pdfBytes, fileName) = await _reciboService.GenerarPdfSemestralAsync(
 1170                    personaIds[0], request.Anio, request.Semestre);
 1171                return File(pdfBytes, "application/pdf", fileName);
 172            }
 173
 2174            var (zipStream, zipName) = await _reciboService.GenerarLoteSemestralZipAsync(
 2175                request.Anio, request.Semestre, personaIds);
 1176            return File(zipStream, "application/zip", zipName);
 177        }
 1178        catch (KeyNotFoundException ex)
 179        {
 1180            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 181        }
 0182        catch (Exception ex)
 183        {
 0184            _logger.LogError(ex, "Error generando recibos de aguinaldo semestral");
 0185            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 186        }
 4187    }
 188
 189    /// <summary>
 190    /// Genera el PDF del recibo de aguinaldo semestral de un funcionario individual.
 191    /// GET /api/aguinaldos/recibos/semestral/{personaId}?anio=2026&amp;semestre=1
 192    /// </summary>
 193    [HttpGet("recibos/semestral/{personaId}")]
 194    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 195    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 196    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 197    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 198    public async Task<IActionResult> GenerarReciboPdfSemestral(
 199        long personaId,
 200        [FromQuery] int anio,
 201        [FromQuery] short semestre)
 202    {
 203        try
 204        {
 2205            var (pdfBytes, fileName) = await _reciboService.GenerarPdfSemestralAsync(personaId, anio, semestre);
 1206            return File(pdfBytes, "application/pdf", fileName);
 207        }
 1208        catch (KeyNotFoundException ex)
 209        {
 1210            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 211        }
 0212        catch (Exception ex)
 213        {
 0214            _logger.LogError(ex, "Error generando PDF de aguinaldo semestral para persona {PersonaId}", personaId);
 0215            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 216        }
 2217    }
 218
 219    /// <summary>
 220    /// Genera un ZIP con los recibos PDF de aguinaldo por baja para los ids indicados.
 221    /// POST /api/aguinaldos/recibos/baja
 222    /// </summary>
 223    [HttpPost("recibos/baja")]
 224    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 225    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 226    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 227    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 228    public async Task<IActionResult> GenerarRecibosBaja([FromBody] GenerarRecibosAguinaldoBajaRequest request)
 229    {
 230        try
 231        {
 3232            if (request.AguinaldoBajaIds == null || !request.AguinaldoBajaIds.Any())
 1233                return BadRequest(ApiResponse<string>.ErrorResult("Debe indicar al menos un id de aguinaldo por baja."))
 234
 2235            if (request.AguinaldoBajaIds.Count == 1)
 236            {
 1237                var (pdfBytes, fileName) = await _reciboService.GenerarPdfBajaAsync(request.AguinaldoBajaIds[0]);
 1238                return File(pdfBytes, "application/pdf", fileName);
 239            }
 240
 1241            var (zipStream, zipName) = await _reciboService.GenerarLoteBajaZipAsync(request.AguinaldoBajaIds);
 1242            return File(zipStream, "application/zip", zipName);
 243        }
 0244        catch (KeyNotFoundException ex)
 245        {
 0246            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 247        }
 0248        catch (Exception ex)
 249        {
 0250            _logger.LogError(ex, "Error generando recibos de aguinaldo por baja");
 0251            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 252        }
 3253    }
 254
 255    /// <summary>
 256    /// Genera el padrón LIQUIDOS de aguinaldo semestral en formato Excel.
 257    /// Incluye duodécimo, sueldo retiro, montepíos, sanidad y líquido a cobrar.
 258    /// GET /api/aguinaldos/padron/semestral?anio=2026&amp;mes=6&amp;esLeyVieja=false
 259    /// </summary>
 260    [HttpGet("padron/semestral")]
 261    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 262    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 263    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 264    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 265    public async Task<IActionResult> GenerarPadronSemestral(
 266        [FromQuery] int anio,
 267        [FromQuery] int mes,
 268        [FromQuery] bool esLeyVieja)
 269    {
 270        try
 271        {
 2272            if (anio <= 0 || (mes != 6 && mes != 12))
 1273                return BadRequest(ApiResponse<string>.ErrorResult("El año debe ser válido y el mes debe ser 6 o 12."));
 274
 1275            var (bytes, fileName) = await _padronService.GenerarPadronExcelAsync(anio, mes, esLeyVieja);
 1276            return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
 277        }
 0278        catch (Exception ex)
 279        {
 0280            _logger.LogError(ex, "Error generando padrón de aguinaldo semestral {Anio}/{Mes}", anio, mes);
 0281            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 282        }
 2283    }
 284
 285    /// <summary>
 286    /// Genera el listado calculado de control de aguinaldo en formato Excel.
 287    /// Variantes: TODOS, REVISTAN, BAJAS.
 288    /// GET /api/aguinaldos/reporte-control?anio=2026&amp;mes=6&amp;esLeyVieja=false&amp;variante=TODOS
 289    /// </summary>
 290    [HttpGet("reporte-control")]
 291    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 292    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 293    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 294    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 295    public async Task<IActionResult> GenerarReporteControl(
 296        [FromQuery] int anio,
 297        [FromQuery] int mes,
 298        [FromQuery] bool esLeyVieja,
 299        [FromQuery] string variante = "TODOS")
 300    {
 301        try
 302        {
 3303            if (anio <= 0 || (mes != 6 && mes != 12))
 1304                return BadRequest(ApiResponse<string>.ErrorResult("El año debe ser válido y el mes debe ser 6 o 12."));
 305
 2306            var v = variante.ToUpper();
 2307            if (v != "TODOS" && v != "REVISTAN" && v != "BAJAS")
 1308                return BadRequest(ApiResponse<string>.ErrorResult("La variante debe ser TODOS, REVISTAN o BAJAS."));
 309
 1310            var (bytes, fileName) = await _reporteControlService.GenerarExcelAsync(anio, mes, v, esLeyVieja);
 1311            return File(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName);
 312        }
 0313        catch (Exception ex)
 314        {
 0315            _logger.LogError(ex, "Error generando reporte de control de aguinaldo {Anio}/{Mes}/{Variante}", anio, mes, v
 0316            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 317        }
 3318    }
 319
 320    /// <summary>
 321    /// Genera el PDF del recibo de aguinaldo por baja individual.
 322    /// GET /api/aguinaldos/recibos/baja/{aguinaldoBajaId}
 323    /// </summary>
 324    [HttpGet("recibos/baja/{aguinaldoBajaId}")]
 325    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 326    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 327    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 328    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 329    public async Task<IActionResult> GenerarReciboPdfBaja(long aguinaldoBajaId)
 330    {
 331        try
 332        {
 0333            var (pdfBytes, fileName) = await _reciboService.GenerarPdfBajaAsync(aguinaldoBajaId);
 0334            return File(pdfBytes, "application/pdf", fileName);
 335        }
 0336        catch (KeyNotFoundException ex)
 337        {
 0338            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 339        }
 0340        catch (Exception ex)
 341        {
 0342            _logger.LogError(ex, "Error generando PDF de aguinaldo por baja {AguinaldoBajaId}", aguinaldoBajaId);
 0343            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 344        }
 0345    }
 346}