< Summary

Information
Class: FAU.API.Controllers.DescuentosPersonalesController
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/DescuentosPersonalesController.cs
Line coverage
76%
Covered lines: 89
Uncovered lines: 27
Coverable lines: 116
Total lines: 317
Line coverage: 76.7%
Branch coverage
26%
Covered branches: 8
Total branches: 30
Branch coverage: 26.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Crear()62.5%8881.25%
Listar()50%3250%
Editar()50%2257.14%
Eliminar()100%1150%
ConfirmarUno()100%1158.33%
Confirmar()50%2290%

File(s)

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

#LineLine coverage
 1using FAU.Entidades;
 2using FAU.Logica.DTOs;
 3using FAU.Logica.Services;
 4using Microsoft.AspNetCore.Authorization;
 5using Microsoft.AspNetCore.Http;
 6using Microsoft.AspNetCore.Mvc;
 7using System.ComponentModel.DataAnnotations;
 8
 9namespace FAU.API.Controllers;
 10
 11/// <summary>
 12/// Gestión de descuentos personales del período
 13/// </summary>
 14[ApiController]
 15[Route("api/descuentos-personales")]
 16[Authorize]
 17[Produces("application/json")]
 18public class DescuentosPersonalesController : ControllerBase
 19{
 20    private readonly IDescuentoPersonalService _service;
 21    private readonly ICurrentUserContext _currentUser;
 22    private readonly ILogger<DescuentosPersonalesController> _logger;
 23
 1324    public DescuentosPersonalesController(
 1325        IDescuentoPersonalService service,
 1326        ICurrentUserContext currentUser,
 1327        ILogger<DescuentosPersonalesController> logger)
 28    {
 1329        _service = service;
 1330        _currentUser = currentUser;
 1331        _logger = logger;
 1332    }
 33
 34    /// <summary>
 35    /// Crear un descuento personal en borrador
 36    /// POST /api/descuentos-personales
 37    /// </summary>
 38    [HttpPost]
 39    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 40    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 41    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 42    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 43    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 44    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 45    public async Task<IActionResult> Crear([FromBody] CrearDescuentoPersonalRequest request)
 46    {
 47        try
 48        {
 449            if (request.PeriodoId <= 0)
 050                return BadRequest(ApiResponse<string>.ErrorResult("periodoId es requerido"));
 451            if (string.IsNullOrWhiteSpace(request.CedulaTitular))
 052                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 453            if (request.AcreedorId <= 0)
 054                return BadRequest(ApiResponse<string>.ErrorResult("acreedorId es requerido"));
 455            if (request.Importe <= 0)
 156                return BadRequest(ApiResponse<string>.ErrorResult("importe debe ser mayor a cero"));
 57
 358            var usuarioId = _currentUser.UserId ?? 0;
 359            var creado = await _service.CrearAsync(
 360                request.PeriodoId,
 361                request.CedulaTitular,
 362                request.CodigoConcepto,
 363                request.AcreedorId,
 364                request.Importe,
 365                request.Observaciones,
 366                usuarioId,
 367                request.FechaComunicacion);
 68
 169            return CreatedAtAction(
 170                nameof(Listar),
 171                new { periodoId = request.PeriodoId },
 172                ApiResponse<object>.SuccessResult(
 173                    new { id = creado.Id, creado = true },
 174                    "Descuento personal creado exitosamente"));
 75        }
 176        catch (ArgumentException ex)
 77        {
 178            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 79        }
 180        catch (InvalidOperationException ex)
 81        {
 182            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 83        }
 084        catch (Exception ex)
 85        {
 086            _logger.LogError(ex, "Error al crear descuento personal");
 087            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 88        }
 489    }
 90
 91    /// <summary>
 92    /// Listar descuentos personales del período
 93    /// GET /api/descuentos-personales?periodoId=...&amp;cedulaTitular=...&amp;codigoConcepto=...&amp;estado=...
 94    /// </summary>
 95    [HttpGet]
 96    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 97    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 98    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 99    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 100    public async Task<IActionResult> Listar(
 101        [FromQuery] long periodoId,
 102        [FromQuery] string? cedulaTitular = null,
 103        [FromQuery] int? codigoConcepto = null,
 104        [FromQuery] string? estado = null)
 105    {
 106        try
 107        {
 1108            if (periodoId <= 0)
 0109                return BadRequest(ApiResponse<string>.ErrorResult("periodoId es requerido"));
 110
 1111            var items = await _service.ListarAsync(periodoId, cedulaTitular, codigoConcepto, estado);
 112
 1113            var response = items.Select(d => new
 1114            {
 1115                id = d.Id,
 1116                periodoId = d.PeriodoId,
 1117                personaId = d.PersonaId,
 1118                cedula = d.Persona?.Cedula,
 1119                nombreCompleto = d.Persona?.NombreCompleto,
 1120                acreedorId = d.AcreedorId,
 1121                acreedorCodigo = d.Acreedor?.Codigo,
 1122                acreedorNombre = d.Acreedor?.Nombre,
 1123                categoriaDeficit = d.Acreedor?.CategoriaDeficit,
 1124                ordenLegalDeficit = d.Acreedor?.OrdenLegalDeficit,
 1125                catalogoItemId = d.CatalogoItemId,
 1126                codigoConcepto = d.CatalogoItem?.Codigo,
 1127                nombreConcepto = d.CatalogoItem?.Nombre,
 1128                importe = d.Importe,
 1129                estado = d.Estado,
 1130                observaciones = d.Observaciones,
 1131                fechaComunicacion = d.FechaComunicacion,
 1132                fechaCreacion = d.FechaCreacion,
 1133                usuarioCreacionId = d.UsuarioCreacionId
 1134            });
 135
 1136            return Ok(ApiResponse<object>.SuccessResult(response));
 137        }
 0138        catch (Exception ex)
 139        {
 0140            _logger.LogError(ex, "Error al listar descuentos personales");
 0141            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 142        }
 1143    }
 144
 145    /// <summary>
 146    /// Editar un descuento personal en borrador
 147    /// PUT /api/descuentos-personales/{id}
 148    /// </summary>
 149    [HttpPut("{id}")]
 150    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 151    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 152    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 153    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 154    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 155    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 156    public async Task<IActionResult> Editar(long id, [FromBody] EditarDescuentoPersonalRequest request)
 157    {
 158        try
 159        {
 2160            if (request.Importe <= 0)
 0161                return BadRequest(ApiResponse<string>.ErrorResult("importe debe ser mayor a cero"));
 162
 2163            var actualizado = await _service.EditarAsync(id, request.Importe, request.Observaciones, request.FechaComuni
 1164            return Ok(ApiResponse<object>.SuccessResult(
 1165                new { id = actualizado.Id, actualizado = true },
 1166                "Descuento personal actualizado exitosamente"));
 167        }
 0168        catch (ArgumentException ex)
 169        {
 0170            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 171        }
 1172        catch (InvalidOperationException ex)
 173        {
 1174            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 175        }
 0176        catch (Exception ex)
 177        {
 0178            _logger.LogError(ex, "Error al actualizar descuento personal {Id}", id);
 0179            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 180        }
 2181    }
 182
 183    /// <summary>
 184    /// Eliminar un descuento personal en borrador
 185    /// DELETE /api/descuentos-personales/{id}
 186    /// </summary>
 187    [HttpDelete("{id}")]
 188    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 189    [ProducesResponseType(StatusCodes.Status204NoContent)]
 190    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 191    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 192    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 193    public async Task<IActionResult> Eliminar(long id)
 194    {
 195        try
 196        {
 2197            await _service.EliminarAsync(id);
 1198            return NoContent();
 199        }
 0200        catch (ArgumentException ex)
 201        {
 0202            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 203        }
 1204        catch (InvalidOperationException ex)
 205        {
 1206            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 207        }
 0208        catch (Exception ex)
 209        {
 0210            _logger.LogError(ex, "Error al eliminar descuento personal {Id}", id);
 0211            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 212        }
 2213    }
 214
 215    /// <summary>
 216    /// Confirmar un descuento personal en borrador
 217    /// POST /api/descuentos-personales/{id}/confirmar
 218    /// </summary>
 219    [HttpPost("{id}/confirmar")]
 220    [Authorize(Roles = "Administrador,Supervisor")]
 221    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 222    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 223    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 224    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 225    public async Task<IActionResult> ConfirmarUno(long id)
 226    {
 227        try
 228        {
 2229            var confirmado = await _service.ConfirmarAsync(id);
 1230            return Ok(ApiResponse<object>.SuccessResult(
 1231                new { confirmado = true },
 1232                $"Descuento personal {confirmado.Id} confirmado exitosamente"));
 233        }
 0234        catch (ArgumentException ex)
 235        {
 0236            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 237        }
 1238        catch (InvalidOperationException ex)
 239        {
 1240            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 241        }
 0242        catch (Exception ex)
 243        {
 0244            _logger.LogError(ex, "Error al confirmar descuento personal {Id}", id);
 0245            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 246        }
 2247    }
 248
 249    /// <summary>
 250    /// Confirmar todos los borradores de un período
 251    /// POST /api/descuentos-personales/confirmar
 252    /// </summary>
 253    [HttpPost("confirmar")]
 254    [Authorize(Roles = "Administrador,Supervisor")]
 255    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 256    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 257    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 258    public async Task<IActionResult> Confirmar([FromBody] ConfirmarDescuentosRequest request)
 259    {
 260        try
 261        {
 2262            if (request.PeriodoId <= 0)
 0263                return BadRequest(ApiResponse<string>.ErrorResult("periodoId es requerido"));
 264
 2265            var cantidad = await _service.ConfirmarBorradoresAsync(request.PeriodoId);
 1266            return Ok(ApiResponse<object>.SuccessResult(
 1267                new { confirmados = cantidad },
 1268                $"Se confirmaron {cantidad} descuento(s) personal(es)"));
 269        }
 1270        catch (Exception ex)
 271        {
 1272            _logger.LogError(ex, "Error al confirmar descuentos personales del período {PeriodoId}", request.PeriodoId);
 1273            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 274        }
 2275    }
 276}
 277
 278public class CrearDescuentoPersonalRequest
 279{
 280    [Required]
 281    public long PeriodoId { get; set; }
 282
 283    [Required]
 284    [MaxLength(12)]
 285    public string CedulaTitular { get; set; } = string.Empty;
 286
 287    [Required]
 288    public int CodigoConcepto { get; set; }
 289
 290    [Required]
 291    public long AcreedorId { get; set; }
 292
 293    [Required]
 294    public decimal Importe { get; set; }
 295
 296    [MaxLength(500)]
 297    public string? Observaciones { get; set; }
 298
 299    public DateTime? FechaComunicacion { get; set; }
 300}
 301
 302public class EditarDescuentoPersonalRequest
 303{
 304    [Required]
 305    public decimal Importe { get; set; }
 306
 307    [MaxLength(500)]
 308    public string? Observaciones { get; set; }
 309
 310    public DateTime? FechaComunicacion { get; set; }
 311}
 312
 313public class ConfirmarDescuentosRequest
 314{
 315    [Required]
 316    public long PeriodoId { get; set; }
 317}