< Summary

Information
Line coverage
0%
Covered lines: 0
Uncovered lines: 8
Coverable lines: 8
Total lines: 234
Line coverage: 0%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_Id()100%210%
get_GradoId()100%210%
get_GradoCodigo()100%210%
get_GradoNombre()100%210%
get_ClasificacionSiif()100%210%
get_Monto()100%210%
get_VigenteDesde()100%210%
get_VigenteHasta()100%210%

File(s)

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

#LineLine coverage
 1using FAU.DataAccess;
 2using FAU.Entidades;
 3using FAU.Logica.DTOs;
 4using Microsoft.AspNetCore.Authorization;
 5using Microsoft.AspNetCore.Mvc;
 6using Microsoft.EntityFrameworkCore;
 7
 8namespace FAU.API.Controllers;
 9
 10[ApiController]
 11[Route("api/remuneraciones-grado")]
 12[Authorize(Roles = "Administrador")]
 13public class RemuneracionesGradoController : ControllerBase
 14{
 15    private readonly ApplicationDbContext _context;
 16    private readonly ILogger<RemuneracionesGradoController> _logger;
 17
 18    public RemuneracionesGradoController(
 19        ApplicationDbContext context,
 20        ILogger<RemuneracionesGradoController> logger)
 21    {
 22        _context = context;
 23        _logger = logger;
 24    }
 25
 26    // GET /api/remuneraciones-grado/tabla-salarial
 27    // Returns ALL remuneraciones_grado entries for 'por_grado' catalog items,
 28    // including the catalog item name and SIIF code for each.
 29    // Used by the admin UI to display the full salary instructivo table.
 30    [HttpGet("tabla-salarial")]
 31    public async Task<IActionResult> GetTablaSalarial()
 32    {
 33        try
 34        {
 35            var items = await _context.RemuneracionesGrado
 36                .AsNoTracking()
 37                .Include(r => r.Item)
 38                .Include(r => r.Grado)
 39                .Where(r => r.Item.FormulaCalculo == "por_grado" && r.Item.Vigente)
 40                .OrderBy(r => r.Item.OrdenCalculo)
 41                .ThenBy(r => r.Grado.Orden)
 42                .ThenBy(r => r.VigenteDesde)
 43                .Select(r => new TablaSalarialItemDto
 44                {
 45                    Id = r.Id,
 46                    GradoId = r.GradoId,
 47                    GradoCodigo = r.Grado.Codigo,
 48                    GradoNombre = r.Grado.Denominacion,
 49                    ClasificacionSiif = r.Item.ClasificacionSiif!,
 50                    NombreOg = r.Item.Nombre,
 51                    OrdenOg = r.Item.OrdenCalculo,
 52                    Monto = r.Monto,
 53                    VigenteDesde = r.VigenteDesde,
 54                    VigenteHasta = r.VigenteHasta
 55                })
 56                .ToListAsync();
 57
 58            return Ok(ApiResponse<IEnumerable<TablaSalarialItemDto>>.SuccessResult(items));
 59        }
 60        catch (Exception ex)
 61        {
 62            _logger.LogError(ex, "Error al obtener tabla salarial completa");
 63            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 64        }
 65    }
 66
 67    // GET /api/remuneraciones-grado?siif=015.000
 68    [HttpGet]
 69    public async Task<IActionResult> Get([FromQuery] string siif)
 70    {
 71        if (string.IsNullOrWhiteSpace(siif))
 72            return BadRequest(ApiResponse<string>.ErrorResult("El parámetro 'siif' es obligatorio."));
 73
 74        try
 75        {
 76            var items = await _context.RemuneracionesGrado
 77                .AsNoTracking()
 78                .Include(r => r.Item)
 79                .Include(r => r.Grado)
 80                .Where(r => r.Item.ClasificacionSiif == siif)
 81                .OrderBy(r => r.Grado.Orden)
 82                .ThenBy(r => r.VigenteDesde)
 83                .Select(r => new RemuneracionGradoDto
 84                {
 85                    Id = r.Id,
 86                    GradoId = r.GradoId,
 87                    GradoCodigo = r.Grado.Codigo,
 88                    GradoNombre = r.Grado.Denominacion,
 89                    ClasificacionSiif = r.Item.ClasificacionSiif!,
 90                    Monto = r.Monto,
 91                    VigenteDesde = r.VigenteDesde,
 92                    VigenteHasta = r.VigenteHasta
 93                })
 94                .ToListAsync();
 95
 96            return Ok(ApiResponse<IEnumerable<RemuneracionGradoDto>>.SuccessResult(items));
 97        }
 98        catch (Exception ex)
 99        {
 100            _logger.LogError(ex, "Error al obtener remuneraciones por grado para SIIF {Siif}", siif);
 101            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 102        }
 103    }
 104
 105    // PUT /api/remuneraciones-grado/{id}
 106    [HttpPut("{id:long}")]
 107    public async Task<IActionResult> Put(long id, [FromBody] ActualizarRemuneracionGradoRequest request)
 108    {
 109        try
 110        {
 111            var entidad = await _context.RemuneracionesGrado.FindAsync(id);
 112            if (entidad is null)
 113                return NotFound(ApiResponse<string>.ErrorResult("Remuneración no encontrada."));
 114
 115            static DateTime ToUtcDate(DateTime d) =>
 116                DateTime.SpecifyKind(d.Date, DateTimeKind.Utc);
 117
 118            entidad.Monto = request.Monto;
 119            entidad.VigenteDesde = ToUtcDate(request.VigenteDesde);
 120            entidad.VigenteHasta = request.VigenteHasta.HasValue ? ToUtcDate(request.VigenteHasta.Value) : null;
 121
 122            await _context.SaveChangesAsync();
 123
 124            return Ok(ApiResponse<string>.SuccessResult("ok", "Remuneración actualizada correctamente."));
 125        }
 126        catch (Exception ex)
 127        {
 128            _logger.LogError(ex, "Error al actualizar remuneración {Id}", id);
 129            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 130        }
 131    }
 132
 133    // POST /api/remuneraciones-grado
 134    [HttpPost]
 135    public async Task<IActionResult> Post([FromBody] CrearRemuneracionGradoRequest request)
 136    {
 137        if (string.IsNullOrWhiteSpace(request.ClasificacionSiif))
 138            return BadRequest(ApiResponse<string>.ErrorResult("ClasificacionSiif es obligatorio."));
 139
 140        try
 141        {
 142            var item = await _context.CatalogoItems
 143                .AsNoTracking()
 144                .Where(c => c.ClasificacionSiif == request.ClasificacionSiif && c.Vigente)
 145                .OrderBy(c => c.OrdenCalculo)
 146                .FirstOrDefaultAsync();
 147
 148            if (item is null)
 149                return BadRequest(ApiResponse<string>.ErrorResult(
 150                    $"No existe ítem de catálogo vigente con clasificación SIIF '{request.ClasificacionSiif}'."));
 151
 152            var gradoExiste = await _context.Grados.AnyAsync(g => g.Id == request.GradoId);
 153            if (!gradoExiste)
 154                return BadRequest(ApiResponse<string>.ErrorResult(
 155                    $"No existe el grado {request.GradoId}."));
 156
 157            static DateTime ToUtcDate(DateTime d) =>
 158                DateTime.SpecifyKind(d.Date, DateTimeKind.Utc);
 159
 160            var entidad = new RemuneracionGrado
 161            {
 162                GradoId = request.GradoId,
 163                ItemId = item.Id,
 164                Monto = request.Monto,
 165                VigenteDesde = ToUtcDate(request.VigenteDesde),
 166                VigenteHasta = request.VigenteHasta.HasValue ? ToUtcDate(request.VigenteHasta.Value) : null
 167            };
 168
 169            _context.RemuneracionesGrado.Add(entidad);
 170            await _context.SaveChangesAsync();
 171
 172            var dto = new RemuneracionGradoDto
 173            {
 174                Id = entidad.Id,
 175                GradoId = entidad.GradoId,
 176                ClasificacionSiif = request.ClasificacionSiif,
 177                Monto = entidad.Monto,
 178                VigenteDesde = entidad.VigenteDesde,
 179                VigenteHasta = entidad.VigenteHasta
 180            };
 181
 182            return Ok(ApiResponse<RemuneracionGradoDto>.SuccessResult(dto, "Remuneración por grado registrada correctame
 183        }
 184        catch (Exception ex)
 185        {
 186            _logger.LogError(ex, "Error al registrar remuneración por grado");
 187            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 188        }
 189    }
 190}
 191
 192// ── DTOs ────────────────────────────────────────────────────────────────────
 193
 194public class ActualizarRemuneracionGradoRequest
 195{
 196    public decimal Monto { get; set; }
 197    public DateTime VigenteDesde { get; set; }
 198    public DateTime? VigenteHasta { get; set; }
 199}
 200
 201public class CrearRemuneracionGradoRequest
 202{
 203    public string ClasificacionSiif { get; set; } = string.Empty; // ej. "015.000"
 204    public long GradoId { get; set; }
 205    public decimal Monto { get; set; }
 206    public DateTime VigenteDesde { get; set; }
 207    public DateTime? VigenteHasta { get; set; }
 208}
 209
 210public class RemuneracionGradoDto
 211{
 0212    public long Id { get; set; }
 0213    public long GradoId { get; set; }
 0214    public string GradoCodigo { get; set; } = string.Empty;
 0215    public string GradoNombre { get; set; } = string.Empty;
 0216    public string ClasificacionSiif { get; set; } = string.Empty;
 0217    public decimal Monto { get; set; }
 0218    public DateTime VigenteDesde { get; set; }
 0219    public DateTime? VigenteHasta { get; set; }
 220}
 221
 222public class TablaSalarialItemDto
 223{
 224    public long Id { get; set; }
 225    public long GradoId { get; set; }
 226    public string GradoCodigo { get; set; } = string.Empty;
 227    public string GradoNombre { get; set; } = string.Empty;
 228    public string ClasificacionSiif { get; set; } = string.Empty;
 229    public string NombreOg { get; set; } = string.Empty;
 230    public int OrdenOg { get; set; }
 231    public decimal Monto { get; set; }
 232    public DateTime VigenteDesde { get; set; }
 233    public DateTime? VigenteHasta { get; set; }
 234}