< 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
0%
Covered lines: 0
Uncovered lines: 124
Coverable lines: 124
Total lines: 346
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 56
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
Validar()100%210%
Calcular()0%2040%
ValidarBaja()100%210%
CalcularBaja()0%2040%
CompletarHistorico()0%2040%
ObtenerPorPeriodoBaja()0%4260%
GenerarRecibosSemestral()0%210140%
GenerarReciboPdfSemestral()100%210%
GenerarRecibosBaja()0%4260%
GenerarPadronSemestral()0%4260%
GenerarReporteControl()0%156120%
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
 022    public AguinaldosController(
 023        IAguinaldoSemestralService service,
 024        IAguinaldoBajaService bajaService,
 025        IAguinaldoHistoricoService historicoService,
 026        IAguinaldoReciboService reciboService,
 027        IAguinaldoPadronService padronService,
 028        IAguinaldoReporteControlService reporteControlService,
 029        ILogger<AguinaldosController> logger)
 30    {
 031        _service = service;
 032        _bajaService = bajaService;
 033        _historicoService = historicoService;
 034        _reciboService = reciboService;
 035        _padronService = padronService;
 036        _reporteControlService = reporteControlService;
 037        _logger = logger;
 038    }
 39
 40    [HttpPost("validar")]
 41    public async Task<IActionResult> Validar([FromBody] CalcularAguinaldoSemestralRequest request)
 42    {
 43        try
 44        {
 045            var resultado = await _service.ValidarHistoricoAsync(request);
 046            return Ok(ApiResponse<AguinaldoValidacionDto>.SuccessResult(resultado));
 47        }
 048        catch (Exception ex)
 49        {
 050            _logger.LogError(ex, "Error validando histórico de aguinaldo");
 051            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 52        }
 053    }
 54
 55    [HttpPost("calcular")]
 56    public async Task<IActionResult> Calcular([FromBody] CalcularAguinaldoSemestralRequest request)
 57    {
 58        try
 59        {
 060            var (success, resultados, error) = await _service.CalcularAguinaldoSemestralAsync(request);
 061            if (!success)
 62            {
 063                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error en cálculo"));
 64            }
 65
 066            return Ok(ApiResponse<IEnumerable<AguinaldoSemestralDto>>.SuccessResult(resultados));
 67        }
 068        catch (Exception ex)
 69        {
 070            _logger.LogError(ex, "Error calculando aguinaldo semestral");
 071            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 72        }
 073    }
 74
 75    [HttpPost("validar/baja")]
 76    public async Task<IActionResult> ValidarBaja([FromBody] CalcularAguinaldoBajaRequest request)
 77    {
 78        try
 79        {
 080            var resultado = await _bajaService.ValidarHistoricoAsync(request);
 081            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        }
 088    }
 89
 90    [HttpPost("calcular/baja")]
 91    public async Task<IActionResult> CalcularBaja([FromBody] CalcularAguinaldoBajaRequest request)
 92    {
 93        try
 94        {
 095            var (success, resultados, error) = await _bajaService.CalcularAguinaldoBajaAsync(request);
 096            if (!success)
 97            {
 098                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error en cálculo de aguinaldo por baja"));
 99            }
 100
 0101            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        }
 0108    }
 109
 110    [HttpPost("completar-historico")]
 111    public async Task<IActionResult> CompletarHistorico([FromBody] CompletarHistoricoAguinaldoRequest request)
 112    {
 113        try
 114        {
 0115            if (request.Funcionarios == null || !request.Funcionarios.Any())
 0116                return BadRequest(ApiResponse<string>.ErrorResult("La lista de funcionarios no puede estar vacía."));
 117
 0118            var resultado = await _historicoService.CompletarHistoricoAsync(request);
 0119            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        }
 0126    }
 127
 128    [HttpGet("periodo/baja")]
 129    public async Task<IActionResult> ObtenerPorPeriodoBaja([FromQuery] int anio, [FromQuery] int mes)
 130    {
 131        try
 132        {
 0133            if (anio <= 0 || (mes != 6 && mes != 12))
 134            {
 0135                return BadRequest(ApiResponse<string>.ErrorResult("El período de consulta debe usar mes 6 o 12 y un año 
 136            }
 137
 0138            var resultados = await _bajaService.ObtenerPorPeriodoAsync(anio, mes);
 0139            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        }
 0146    }
 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        {
 0162            if (request.Anio <= 0 || (request.Semestre != 1 && request.Semestre != 2))
 0163                return BadRequest(ApiResponse<string>.ErrorResult("Año y semestre inválidos. El semestre debe ser 1 o 2.
 164
 0165            var personaIds = request.PersonaIds?.Count > 0 ? request.PersonaIds : null;
 166
 0167            if (personaIds?.Count == 1)
 168            {
 0169                var (pdfBytes, fileName) = await _reciboService.GenerarPdfSemestralAsync(
 0170                    personaIds[0], request.Anio, request.Semestre);
 0171                return File(pdfBytes, "application/pdf", fileName);
 172            }
 173
 0174            var (zipStream, zipName) = await _reciboService.GenerarLoteSemestralZipAsync(
 0175                request.Anio, request.Semestre, personaIds);
 0176            return File(zipStream, "application/zip", zipName);
 177        }
 0178        catch (KeyNotFoundException ex)
 179        {
 0180            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        }
 0187    }
 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        {
 0205            var (pdfBytes, fileName) = await _reciboService.GenerarPdfSemestralAsync(personaId, anio, semestre);
 0206            return File(pdfBytes, "application/pdf", fileName);
 207        }
 0208        catch (KeyNotFoundException ex)
 209        {
 0210            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        }
 0217    }
 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        {
 0232            if (request.AguinaldoBajaIds == null || !request.AguinaldoBajaIds.Any())
 0233                return BadRequest(ApiResponse<string>.ErrorResult("Debe indicar al menos un id de aguinaldo por baja."))
 234
 0235            if (request.AguinaldoBajaIds.Count == 1)
 236            {
 0237                var (pdfBytes, fileName) = await _reciboService.GenerarPdfBajaAsync(request.AguinaldoBajaIds[0]);
 0238                return File(pdfBytes, "application/pdf", fileName);
 239            }
 240
 0241            var (zipStream, zipName) = await _reciboService.GenerarLoteBajaZipAsync(request.AguinaldoBajaIds);
 0242            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        }
 0253    }
 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        {
 0272            if (anio <= 0 || (mes != 6 && mes != 12))
 0273                return BadRequest(ApiResponse<string>.ErrorResult("El año debe ser válido y el mes debe ser 6 o 12."));
 274
 0275            var (bytes, fileName) = await _padronService.GenerarPadronExcelAsync(anio, mes, esLeyVieja);
 0276            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        }
 0283    }
 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        {
 0303            if (anio <= 0 || (mes != 6 && mes != 12))
 0304                return BadRequest(ApiResponse<string>.ErrorResult("El año debe ser válido y el mes debe ser 6 o 12."));
 305
 0306            var v = variante.ToUpper();
 0307            if (v != "TODOS" && v != "REVISTAN" && v != "BAJAS")
 0308                return BadRequest(ApiResponse<string>.ErrorResult("La variante debe ser TODOS, REVISTAN o BAJAS."));
 309
 0310            var (bytes, fileName) = await _reporteControlService.GenerarExcelAsync(anio, mes, v, esLeyVieja);
 0311            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        }
 0318    }
 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}