< Summary

Information
Class: FAU.API.Controllers.PeriodosController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/PeriodosController.cs
Line coverage
62%
Covered lines: 80
Uncovered lines: 48
Coverable lines: 128
Total lines: 282
Line coverage: 62.5%
Branch coverage
70%
Covered branches: 45
Total branches: 64
Branch coverage: 70.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%
AbrirPeriodo()80%131069.23%
GetUltimos()0%620%
GetPeriodoActivo()100%2262.5%
GetPeriodo()100%2262.5%
GetPrecondiciones()0%620%
ValidarInsumos()0%620%
CerrarInsumos()75%211673.33%
ReabrirInsumos()75%12860%
Calcular()66.66%151272.22%
Cerrar()87.5%10870%
MapearPeriodo(...)100%11100%

File(s)

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

#LineLine coverage
 1using FAU.API.Security;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using FAU.Logica.Exceptions;
 5using FAU.Logica.Services;
 6using Microsoft.AspNetCore.Authorization;
 7using Microsoft.AspNetCore.Mvc;
 8
 9namespace FAU.API.Controllers;
 10
 11[ApiController]
 12[Route("api/liquidacion/[controller]")]
 13[Authorize(Roles = "Administrador")]
 14public class PeriodosController : ControllerBase
 15{
 16    private readonly IPeriodoService _periodoService;
 17    private readonly ILiquidacionService _liquidacionService;
 18    private readonly ILogger<PeriodosController> _logger;
 19
 1920    public PeriodosController(
 1921        IPeriodoService periodoService,
 1922        ILiquidacionService liquidacionService,
 1923        ILogger<PeriodosController> logger)
 24    {
 1925        _periodoService       = periodoService;
 1926        _liquidacionService   = liquidacionService;
 1927        _logger               = logger;
 1928    }
 29
 30    [HttpPost]
 31    public async Task<IActionResult> AbrirPeriodo([FromBody] AbrirPeriodoRequest request)
 32    {
 33        try
 34        {
 335            if (!ModelState.IsValid)
 36            {
 037                return BadRequest(ApiResponse<string>.ErrorResult("Solicitud de apertura de período inválida"));
 38            }
 39
 340            var (success, periodo, error) = await _periodoService.AbrirPeriodoAsync(request.Anio, request.Mes);
 41
 342            if (!success)
 43            {
 244                if (error?.Contains("Ya existe un período activo", StringComparison.OrdinalIgnoreCase) == true)
 45                {
 146                    return Conflict(ApiResponse<string>.ErrorResult(error));
 47                }
 48
 149                return BadRequest(ApiResponse<string>.ErrorResult(error ?? "Error al abrir el período"));
 50            }
 51
 152            var dto = MapearPeriodo(periodo!);
 153            return CreatedAtAction(nameof(GetPeriodo), new { id = periodo!.Id }, ApiResponse<PeriodoDto>.SuccessResult(d
 54        }
 055        catch (Exception ex)
 56        {
 057            _logger.LogError(ex, "Error al abrir período");
 058            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 59        }
 360    }
 61
 62    [HttpGet]
 63    public async Task<IActionResult> GetUltimos([FromQuery] int cantidad = 6)
 64    {
 65        try
 66        {
 067            var periodos = await _periodoService.ObtenerUltimosAsync(Math.Min(cantidad, 12));
 068            return Ok(ApiResponse<List<PeriodoDto>>.SuccessResult(periodos.Select(MapearPeriodo).ToList()));
 69        }
 070        catch (Exception ex)
 71        {
 072            _logger.LogError(ex, "Error al obtener últimos períodos");
 073            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 74        }
 075    }
 76
 77    [HttpGet("activo")]
 78    public async Task<IActionResult> GetPeriodoActivo()
 79    {
 80        try
 81        {
 282            var periodo = await _periodoService.ObtenerEnCursoAsync();
 283            if (periodo == null)
 84            {
 185                return NotFound(ApiResponse<string>.ErrorResult("No hay un período activo"));
 86            }
 87
 188            return Ok(ApiResponse<PeriodoDto>.SuccessResult(MapearPeriodo(periodo)));
 89        }
 090        catch (Exception ex)
 91        {
 092            _logger.LogError(ex, "Error al obtener período activo");
 093            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 94        }
 295    }
 96
 97    [HttpGet("{id}")]
 98    public async Task<IActionResult> GetPeriodo(long id)
 99    {
 100        try
 101        {
 2102            var periodo = await _periodoService.ObtenerPorIdAsync(id);
 2103            if (periodo == null)
 104            {
 1105                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 106            }
 107
 1108            return Ok(ApiResponse<PeriodoDto>.SuccessResult(MapearPeriodo(periodo)));
 109        }
 0110        catch (Exception ex)
 111        {
 0112            _logger.LogError(ex, "Error al obtener período {Id}", id);
 0113            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 114        }
 2115    }
 116
 117    [HttpGet("{id}/precondiciones")]
 118    public async Task<IActionResult> GetPrecondiciones(long id)
 119    {
 120        try
 121        {
 0122            var precondiciones = await _periodoService.ObtenerPrecondicionesAsync(id);
 0123            if (precondiciones == null)
 0124                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 0125            return Ok(ApiResponse<PrecondicionesDto>.SuccessResult(precondiciones));
 126        }
 0127        catch (Exception ex)
 128        {
 0129            _logger.LogError(ex, "Error al obtener precondiciones del período {Id}", id);
 0130            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 131        }
 0132    }
 133
 134    [HttpGet("{id}/validar-insumos")]
 135    public async Task<IActionResult> ValidarInsumos(long id)
 136    {
 137        try
 138        {
 0139            var validacion = await _periodoService.ValidarInsumosAsync(id);
 0140            if (validacion == null)
 0141                return NotFound(ApiResponse<string>.ErrorResult("Período no encontrado"));
 0142            return Ok(ApiResponse<ValidacionInsumosDto>.SuccessResult(validacion));
 143        }
 0144        catch (Exception ex)
 145        {
 0146            _logger.LogError(ex, "Error al validar insumos del período {Id}", id);
 0147            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 148        }
 0149    }
 150
 151    [HttpPost("{id}/cerrar-insumos")]
 152    public async Task<IActionResult> CerrarInsumos(long id, [FromBody] CerrarInsumosRequest? request = null)
 153    {
 154        try
 155        {
 4156            var (success, periodo, validacion, error) =
 4157                await _periodoService.CerrarInsumosAsync(id, request?.Confirmaciones);
 158
 4159            if (!success)
 160            {
 3161                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 1162                    return NotFound(ApiResponse<string>.ErrorResult(error));
 2163                if (error?.Contains("Solo se puede", StringComparison.OrdinalIgnoreCase) == true)
 1164                    return Conflict(ApiResponse<string>.ErrorResult(error));
 1165                if (validacion != null)
 1166                    return UnprocessableEntity(validacion);
 0167                return UnprocessableEntity(ApiResponse<string>.ErrorResult(error ?? "Error al cerrar insumos"));
 168            }
 169
 1170            return Ok(ApiResponse<PeriodoDto>.SuccessResult(MapearPeriodo(periodo!), "Insumos cerrados. Período listo pa
 171        }
 0172        catch (Exception ex)
 173        {
 0174            _logger.LogError(ex, "Error al cerrar insumos del período {Id}", id);
 0175            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 176        }
 4177    }
 178
 179    [HttpPost("{id}/reabrir-insumos")]
 180    [RequirePermission(PermisoEnum.ReabrirPeriodo)]
 181    public async Task<IActionResult> ReabrirInsumos(long id)
 182    {
 183        try
 184        {
 2185            var (success, periodo, error) = await _periodoService.ReabrirInsumosAsync(id);
 186
 2187            if (!success)
 188            {
 1189                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 0190                    return NotFound(ApiResponse<string>.ErrorResult(error));
 1191                return Conflict(ApiResponse<string>.ErrorResult(error ?? "Error al reabrir insumos"));
 192            }
 193
 1194            return Ok(ApiResponse<PeriodoDto>.SuccessResult(MapearPeriodo(periodo!), "Insumos reabiertos. Período en est
 195        }
 0196        catch (Exception ex)
 197        {
 0198            _logger.LogError(ex, "Error al reabrir insumos del período {Id}", id);
 0199            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 200        }
 2201    }
 202
 203    [HttpPost("{id}/calcular")]
 204    public async Task<IActionResult> Calcular(long id, [FromQuery] int version = 1)
 205    {
 206        try
 207        {
 208            // 1. Marcar período como EN_CALCULO
 3209            var (success, periodo, error) = await _periodoService.IniciarCalculoAsync(id);
 3210            if (!success)
 211            {
 2212                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 1213                    return NotFound(ApiResponse<string>.ErrorResult(error));
 1214                return Conflict(ApiResponse<string>.ErrorResult(error ?? "Error al iniciar cálculo"));
 215            }
 216
 217            // 2. Ejecutar el cálculo real
 1218            var (exitoCalculo, errorCalculo) = await _liquidacionService.GenerarLiquidacionAsync(id, version);
 219
 220            // 3. Finalizar — éxito o error vuelve a LISTA_CALCULO para reintentar
 1221            await _periodoService.FinalizarCalculoAsync(id, exitoCalculo);
 222
 1223            if (!exitoCalculo)
 0224                return StatusCode(500, ApiResponse<string>.ErrorResult(errorCalculo ?? "Error durante el cálculo"));
 225
 1226            var periodoFinal = await _periodoService.ObtenerPorIdAsync(id);
 1227            return Ok(ApiResponse<PeriodoDto>.SuccessResult(
 1228                MapearPeriodo(periodoFinal!),
 1229                $"Liquidación versión {version} generada correctamente."));
 230        }
 0231        catch (Exception ex)
 232        {
 0233            await _periodoService.FinalizarCalculoAsync(id, exitoso: false).ConfigureAwait(false);
 0234            _logger.LogError(ex, "Error al calcular período {Id}", id);
 0235            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 236        }
 3237    }
 238
 239    [HttpPost("{id}/cerrar")]
 240    [RequirePermission(PermisoEnum.CerrarPeriodo)]
 241    public async Task<IActionResult> Cerrar(long id)
 242    {
 243        try
 244        {
 3245            var (success, periodo, error) = await _periodoService.CerrarAsync(id);
 246
 3247            if (!success)
 248            {
 2249                if (error?.Contains("no encontrado", StringComparison.OrdinalIgnoreCase) == true)
 1250                    return NotFound(ApiResponse<string>.ErrorResult(error));
 1251                return Conflict(ApiResponse<string>.ErrorResult(error ?? "Error al cerrar período"));
 252            }
 253
 1254            return Ok(ApiResponse<PeriodoDto>.SuccessResult(MapearPeriodo(periodo!), "Período cerrado definitivamente.")
 255        }
 0256        catch (Exception ex)
 257        {
 0258            _logger.LogError(ex, "Error al cerrar período {Id}", id);
 0259            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 260        }
 3261    }
 262
 263    private static PeriodoDto MapearPeriodo(Periodo periodo)
 264    {
 7265        return new PeriodoDto
 7266        {
 7267            Id = periodo.Id,
 7268            Anio = periodo.Anio,
 7269            Mes = periodo.Mes,
 7270            Estado = periodo.Estado,
 7271            SnapshotId = periodo.SnapshotId,
 7272            FechaApertura = periodo.FechaApertura,
 7273            UsuarioApertura = periodo.UsuarioApertura,
 7274            HashSnapshot = periodo.HashSnapshot,
 7275            FechaCierreInsumos = periodo.FechaCierreInsumos,
 7276            UsuarioCierreInsumosId = periodo.UsuarioCierreInsumosId,
 7277            FechaCalculo = periodo.FechaCalculo,
 7278            FechaCierre = periodo.FechaCierre,
 7279            UsuarioCierreId = periodo.UsuarioCierreId
 7280        };
 281    }
 282}