< Summary

Information
Class: FAU.API.Controllers.CrearForm3100Request
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/Form3100Controller.cs
Line coverage
100%
Covered lines: 15
Uncovered lines: 0
Coverable lines: 15
Total lines: 539
Line coverage: 100%
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_CedulaTitular()100%11100%
get_CappuCategoria()100%11100%
get_CappuImporte()100%11100%
get_ReintegroAporte()100%11100%
get_FondoSolidaridad()100%11100%
get_AdicionalFondoSolidaridad()100%11100%
get_AplicaMinimoNoImponible()100%11100%
get_ConyugeNombre()100%11100%
get_ConyugeApellido()100%11100%
get_ConyugeFechaNacimiento()100%11100%
get_ConyugeCedula()100%11100%
get_ConyugeSexo()100%11100%
get_ConyugeNacionalidad()100%11100%
get_VigenteDesdeMes()100%11100%
get_VigenteDesdeAnio()100%11100%

File(s)

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

#LineLine coverage
 1using FAU.Entidades;
 2using FAU.Logica.DTOs;
 3using FAU.Logica.Exceptions;
 4using FAU.Logica.Services;
 5using Microsoft.AspNetCore.Authorization;
 6using Microsoft.AspNetCore.Http;
 7using Microsoft.AspNetCore.Mvc;
 8using System.ComponentModel.DataAnnotations;
 9
 10namespace FAU.API.Controllers;
 11
 12/// <summary>
 13/// Gestión del Formulario 3100 (deducciones IRPF) y personas a cargo
 14/// </summary>
 15[ApiController]
 16[Route("api/form3100")]
 17[Authorize]
 18[Produces("application/json")]
 19public class Form3100Controller : ControllerBase
 20{
 21    private readonly IForm3100Service _form3100Service;
 22    private readonly ICurrentUserContext _currentUser;
 23    private readonly ILogger<Form3100Controller> _logger;
 24
 25    public Form3100Controller(
 26        IForm3100Service form3100Service,
 27        ICurrentUserContext currentUser,
 28        ILogger<Form3100Controller> logger)
 29    {
 30        _form3100Service = form3100Service;
 31        _currentUser = currentUser;
 32        _logger = logger;
 33    }
 34
 35    /// <summary>
 36    /// Crear una nueva versión del Form 3100
 37    /// POST /api/form3100
 38    /// </summary>
 39    [HttpPost]
 40    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 41    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 42    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 43    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 44    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 45    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status423Locked)]
 46    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 47    public async Task<IActionResult> Crear([FromBody] CrearForm3100Request request)
 48    {
 49        try
 50        {
 51            if (string.IsNullOrWhiteSpace(request.CedulaTitular))
 52                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 53
 54            if (request.VigenteDesdeMes < 1 || request.VigenteDesdeMes > 12)
 55                return BadRequest(ApiResponse<string>.ErrorResult("vigenteDesdeMes debe estar entre 1 y 12"));
 56
 57            if (request.VigenteDesdeAnio < 2000 || request.VigenteDesdeAnio > 2100)
 58                return BadRequest(ApiResponse<string>.ErrorResult("vigenteDesdeAnio inválido"));
 59
 60            if (request.CappuCategoria.HasValue && (request.CappuCategoria < 1 || request.CappuCategoria > 10))
 61                return BadRequest(ApiResponse<string>.ErrorResult("cappuCategoria debe estar entre 1 y 10"));
 62
 63            if (request.CappuImporte < 0)
 64                return BadRequest(ApiResponse<string>.ErrorResult("cappuImporte no puede ser negativo"));
 65
 66            if (request.ReintegroAporte < 0)
 67                return BadRequest(ApiResponse<string>.ErrorResult("reintegroAporte no puede ser negativo"));
 68
 69            if (!string.IsNullOrEmpty(request.FondoSolidaridad) &&
 70                !new[] { "0.5_BPC", "1_BPC", "2_BPC" }.Contains(request.FondoSolidaridad))
 71                return BadRequest(ApiResponse<string>.ErrorResult("fondoSolidaridad debe ser '0.5_BPC', '1_BPC' o '2_BPC
 72
 73            var form = new Form3100
 74            {
 75                CappuCategoria = request.CappuCategoria,
 76                CappuImporte = request.CappuImporte,
 77                ReintegroAporte = request.ReintegroAporte,
 78                FondoSolidaridad = request.FondoSolidaridad,
 79                AdicionalFondoSolidaridad = request.AdicionalFondoSolidaridad,
 80                AplicaMinimoNoImponible = request.AplicaMinimoNoImponible,
 81                ConyugeNombre = request.ConyugeNombre,
 82                ConyugeApellido = request.ConyugeApellido,
 83                ConyugeFechaNacimiento = request.ConyugeFechaNacimiento,
 84                ConyugeCedula = request.ConyugeCedula,
 85                ConyugeSexo = request.ConyugeSexo,
 86                ConyugeNacionalidad = request.ConyugeNacionalidad,
 87                VigenteDesde_Mes = (short)request.VigenteDesdeMes,
 88                VigenteDesde_Anio = (short)request.VigenteDesdeAnio
 89            };
 90
 91            var usuarioId = _currentUser.UserId ?? 0;
 92            var creado = await _form3100Service.CrearAsync(request.CedulaTitular, form, usuarioId);
 93
 94            return CreatedAtAction(
 95                nameof(GetById),
 96                new { id = creado.Id },
 97                ApiResponse<object>.SuccessResult(new { id = creado.Id, creado = true }, "Form 3100 creado exitosamente"
 98        }
 99        catch (ArgumentException ex)
 100        {
 101            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 102        }
 103        catch (PeriodoBloqueadoException ex)
 104        {
 105            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 106        }
 107        catch (InvalidOperationException ex)
 108        {
 109            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 110        }
 111        catch (Exception ex)
 112        {
 113            _logger.LogError(ex, "Error al crear Form 3100");
 114            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 115        }
 116    }
 117
 118    /// <summary>
 119    /// Obtener historial de versiones del Form 3100 por cédula
 120    /// GET /api/form3100?cedulaTitular=...&amp;vigenteDesdeAnio=...&amp;vigenteDesdeMes=...
 121    /// </summary>
 122    [HttpGet]
 123    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 124    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 125    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 126    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 127    public async Task<IActionResult> GetHistorial(
 128        [FromQuery] string cedulaTitular,
 129        [FromQuery] int? vigenteDesdeAnio = null,
 130        [FromQuery] int? vigenteDesdeMes = null)
 131    {
 132        try
 133        {
 134            if (string.IsNullOrWhiteSpace(cedulaTitular))
 135                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 136
 137            var historial = await _form3100Service.GetHistorialAsync(cedulaTitular, vigenteDesdeAnio, vigenteDesdeMes);
 138
 139            var response = historial.Select(MapToDto);
 140            return Ok(ApiResponse<object>.SuccessResult(response));
 141        }
 142        catch (Exception ex)
 143        {
 144            _logger.LogError(ex, "Error al obtener historial Form 3100");
 145            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 146        }
 147    }
 148
 149    /// <summary>
 150    /// Obtener versión específica del Form 3100 por id
 151    /// GET /api/form3100/{id}
 152    /// </summary>
 153    [HttpGet("{id}")]
 154    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 155    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 156    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 157    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 158    public async Task<IActionResult> GetById(long id)
 159    {
 160        try
 161        {
 162            var form = await _form3100Service.GetByIdAsync(id);
 163            if (form == null)
 164                return NotFound(ApiResponse<string>.ErrorResult("Form 3100 no encontrado"));
 165
 166            return Ok(ApiResponse<object>.SuccessResult(MapToDto(form)));
 167        }
 168        catch (Exception ex)
 169        {
 170            _logger.LogError(ex, "Error al obtener Form 3100 {Id}", id);
 171            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 172        }
 173    }
 174
 175    /// <summary>
 176    /// Obtener versión vigente del Form 3100 para un período
 177    /// GET /api/form3100/vigente?cedulaTitular=...&amp;anio=...&amp;mes=...
 178    /// </summary>
 179    [HttpGet("vigente")]
 180    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 181    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 182    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 183    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 184    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 185    public async Task<IActionResult> GetVigente(
 186        [FromQuery] string cedulaTitular,
 187        [FromQuery] int anio,
 188        [FromQuery] int mes)
 189    {
 190        try
 191        {
 192            if (string.IsNullOrWhiteSpace(cedulaTitular))
 193                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 194            if (mes < 1 || mes > 12)
 195                return BadRequest(ApiResponse<string>.ErrorResult("mes debe estar entre 1 y 12"));
 196
 197            var form = await _form3100Service.GetVigenteParaPeriodoAsync(cedulaTitular, anio, mes);
 198            if (form == null)
 199                return NotFound(ApiResponse<string>.ErrorResult("No se encontró Form 3100 vigente para el período indica
 200
 201            return Ok(ApiResponse<object>.SuccessResult(MapToDto(form)));
 202        }
 203        catch (Exception ex)
 204        {
 205            _logger.LogError(ex, "Error al obtener Form 3100 vigente");
 206            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 207        }
 208    }
 209
 210    /// <summary>
 211    /// Listar personas a cargo de una versión del Form 3100
 212    /// GET /api/form3100/{id}/personas-cargo
 213    /// </summary>
 214    [HttpGet("{id}/personas-cargo")]
 215    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 216    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 217    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 218    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 219    public async Task<IActionResult> GetPersonasACargo(long id)
 220    {
 221        try
 222        {
 223            var form = await _form3100Service.GetByIdAsync(id);
 224            if (form == null)
 225                return NotFound(ApiResponse<string>.ErrorResult("Form 3100 no encontrado"));
 226
 227            var personas = form.PersonasACargo?.Select(p => new
 228            {
 229                id = p.Id, nombre = p.Nombre, apellido = p.Apellido,
 230                fechaNacimiento = p.FechaNacimiento, cedula = p.Cedula,
 231                sexo = p.Sexo, porcentajeAtribucion = p.PorcentajeAtribucion,
 232                relacion = p.Relacion, discapacidad = p.Discapacidad
 233            });
 234            return Ok(ApiResponse<object>.SuccessResult(personas ?? Enumerable.Empty<object>()));
 235        }
 236        catch (Exception ex)
 237        {
 238            _logger.LogError(ex, "Error al listar personas a cargo de Form 3100 {Id}", id);
 239            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 240        }
 241    }
 242
 243    /// <summary>
 244    /// Agregar persona a cargo a una versión del Form 3100
 245    /// POST /api/form3100/{id}/personas-cargo
 246    /// </summary>
 247    [HttpPost("{id}/personas-cargo")]
 248    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 249    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status201Created)]
 250    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 251    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 252    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status423Locked)]
 253    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 254    public async Task<IActionResult> AgregarPersonaACargo(long id, [FromBody] AgregarPersonaACargoRequest request)
 255    {
 256        try
 257        {
 258            var persona = new Form3100PersonaACargo
 259            {
 260                Nombre = request.Nombre,
 261                Apellido = request.Apellido,
 262                FechaNacimiento = request.FechaNacimiento.Kind == DateTimeKind.Unspecified
 263                    ? DateTime.SpecifyKind(request.FechaNacimiento.Date, DateTimeKind.Utc)
 264                    : request.FechaNacimiento.Date.ToUniversalTime(),
 265                Cedula = request.Cedula,
 266                Sexo = request.Sexo,
 267                PorcentajeAtribucion = (short)request.PorcentajeAtribucion,
 268                Relacion = request.Relacion,
 269                Discapacidad = request.Discapacidad
 270            };
 271
 272            var creada = await _form3100Service.AgregarPersonaACargoAsync(id, persona);
 273
 274            return StatusCode(201, ApiResponse<object>.SuccessResult(
 275                new { id = creada.Id, creado = true },
 276                "Persona a cargo agregada exitosamente"));
 277        }
 278        catch (PeriodoBloqueadoException ex)
 279        {
 280            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 281        }
 282        catch (ArgumentException ex)
 283        {
 284            return BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 285        }
 286        catch (Exception ex)
 287        {
 288            _logger.LogError(ex, "Error al agregar persona a cargo a Form 3100 {Id}", id);
 289            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 290        }
 291    }
 292
 293    /// <summary>
 294    /// Eliminar persona a cargo de una versión del Form 3100
 295    /// DELETE /api/form3100/{id}/personas-cargo/{personaCargoId}
 296    /// </summary>
 297    [HttpDelete("{id}/personas-cargo/{personaCargoId}")]
 298    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 299    [ProducesResponseType(StatusCodes.Status204NoContent)]
 300    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 301    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 302    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status423Locked)]
 303    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 304    public async Task<IActionResult> EliminarPersonaACargo(long id, long personaCargoId)
 305    {
 306        try
 307        {
 308            await _form3100Service.EliminarPersonaACargoAsync(id, personaCargoId);
 309            return NoContent();
 310        }
 311        catch (PeriodoBloqueadoException ex)
 312        {
 313            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 314        }
 315        catch (ArgumentException ex)
 316        {
 317            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 318        }
 319        catch (Exception ex)
 320        {
 321            _logger.LogError(ex, "Error al eliminar persona a cargo {PersonaCargoId} de Form 3100 {Id}", personaCargoId,
 322            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 323        }
 324    }
 325
 326    /// <summary>
 327    /// Subir archivo escaneado del Form 3100
 328    /// POST /api/form3100/{id}/archivo (multipart/form-data, campo "file")
 329    /// </summary>
 330    [HttpPost("{id}/archivo")]
 331    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 332    [RequestSizeLimit(20_000_000)] // 20 MB
 333    [ProducesResponseType(typeof(ApiResponse<object>), StatusCodes.Status200OK)]
 334    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status400BadRequest)]
 335    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 336    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status409Conflict)]
 337    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status423Locked)]
 338    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 339    public async Task<IActionResult> SubirArchivo(long id, IFormFile file)
 340    {
 341        try
 342        {
 343            if (file == null || file.Length == 0)
 344                return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 345
 346            var usuarioId = _currentUser.UserId ?? 0;
 347            await using var stream = file.OpenReadStream();
 348            var rutaRelativa = await _form3100Service.SubirArchivoAsync(
 349                id,
 350                stream,
 351                file.FileName,
 352                file.ContentType,
 353                usuarioId);
 354
 355            return Ok(ApiResponse<object>.SuccessResult(
 356                new { rutaRelativa, subido = true },
 357                "Archivo subido exitosamente"));
 358        }
 359        catch (PeriodoBloqueadoException ex)
 360        {
 361            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 362        }
 363        catch (ArgumentException ex)
 364        {
 365            if (ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase))
 366                return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 367            return BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 368        }
 369        catch (Exception ex)
 370        {
 371            _logger.LogError(ex, "Error al subir archivo de Form 3100 {Id}", id);
 372            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 373        }
 374    }
 375
 376    /// <summary>
 377    /// Descargar archivo escaneado del Form 3100
 378    /// GET /api/form3100/{id}/archivo
 379    /// </summary>
 380    /// <summary>
 381    /// GET /api/form3100/{id}/archivo?inline=true
 382    /// inline=true  → Content-Disposition: inline  (para mostrar en visor del navegador)
 383    /// inline=false → Content-Disposition: attachment (para forzar descarga, default)
 384    /// </summary>
 385    [HttpGet("{id}/archivo")]
 386    [Authorize(Roles = "Administrador,Supervisor,Usuario")]
 387    [ProducesResponseType(typeof(FileResult), StatusCodes.Status200OK)]
 388    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status404NotFound)]
 389    [ProducesResponseType(typeof(ApiResponse<string>), StatusCodes.Status500InternalServerError)]
 390    public async Task<IActionResult> DescargarArchivo(long id, [FromQuery] bool inline = true)
 391    {
 392        try
 393        {
 394            var (contenido, nombreOriginal, contentType) = await _form3100Service.ObtenerArchivoAsync(id);
 395
 396            if (inline)
 397            {
 398                Response.Headers.ContentDisposition =
 399                    $"inline; filename=\"{nombreOriginal}\"; filename*=UTF-8''{Uri.EscapeDataString(nombreOriginal)}";
 400                return File(contenido, contentType);
 401            }
 402
 403            return File(contenido, contentType, nombreOriginal);
 404        }
 405        catch (ArgumentException ex)
 406        {
 407            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 408        }
 409        catch (KeyNotFoundException ex)
 410        {
 411            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 412        }
 413        catch (StorageFileNotFoundException ex)
 414        {
 415            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 416        }
 417        catch (Exception ex)
 418        {
 419            _logger.LogError(ex, "Error al descargar archivo de Form 3100 {Id}", id);
 420            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 421        }
 422    }
 423
 424    private static object MapToDto(Form3100 f)
 425    {
 426        return new
 427        {
 428            id = f.Id,
 429            personaId = f.PersonaId,
 430            cappuCategoria = f.CappuCategoria,
 431            cappuImporte = f.CappuImporte,
 432            reintegroAporte = f.ReintegroAporte,
 433            fondoSolidaridad = f.FondoSolidaridad,
 434            adicionalFondoSolidaridad = f.AdicionalFondoSolidaridad,
 435            aplicaMinimoNoImponible = f.AplicaMinimoNoImponible,
 436            conyugeNombre = f.ConyugeNombre,
 437            conyugeApellido = f.ConyugeApellido,
 438            conyugeFechaNacimiento = f.ConyugeFechaNacimiento,
 439            conyugeCedula = f.ConyugeCedula,
 440            conyugeSexo = f.ConyugeSexo,
 441            conyugeNacionalidad = f.ConyugeNacionalidad,
 442            vigenteDesdeMes = f.VigenteDesde_Mes,
 443            vigenteDesdeAnio = f.VigenteDesde_Anio,
 444            documentoId = f.DocumentoId,
 445            fechaCreacion = f.FechaCreacion,
 446            usuarioCreacionId = f.UsuarioCreacionId,
 447            personasACargo = f.PersonasACargo?.Select(p => new
 448            {
 449                id = p.Id,
 450                nombre = p.Nombre,
 451                apellido = p.Apellido,
 452                fechaNacimiento = p.FechaNacimiento,
 453                cedula = p.Cedula,
 454                sexo = p.Sexo,
 455                porcentajeAtribucion = p.PorcentajeAtribucion,
 456                relacion = p.Relacion,
 457                discapacidad = p.Discapacidad
 458            })
 459        };
 460    }
 461}
 462
 463public class CrearForm3100Request
 464{
 465    [Required(ErrorMessage = "cedulaTitular es requerida")]
 466    [MaxLength(12)]
 23467    public string CedulaTitular { get; set; } = string.Empty;
 468
 12469    public short? CappuCategoria { get; set; }
 470
 471    [Range(0, double.MaxValue)]
 8472    public decimal CappuImporte { get; set; }
 473
 474    [Range(0, double.MaxValue)]
 8475    public decimal ReintegroAporte { get; set; }
 476
 477    [MaxLength(10)]
 8478    public string? FondoSolidaridad { get; set; }
 479
 4480    public bool AdicionalFondoSolidaridad { get; set; }
 481
 4482    public bool AplicaMinimoNoImponible { get; set; }
 483
 484    [MaxLength(100)]
 4485    public string? ConyugeNombre { get; set; }
 486
 487    [MaxLength(100)]
 4488    public string? ConyugeApellido { get; set; }
 489
 4490    public DateTime? ConyugeFechaNacimiento { get; set; }
 491
 492    [MaxLength(20)]
 4493    public string? ConyugeCedula { get; set; }
 494
 495    [MaxLength(1)]
 4496    public string? ConyugeSexo { get; set; }
 497
 498    [MaxLength(100)]
 4499    public string? ConyugeNacionalidad { get; set; }
 500
 501    [Required]
 502    [Range(1, 12)]
 23503    public int VigenteDesdeMes { get; set; }
 504
 505    [Required]
 506    [Range(2000, 2100)]
 20507    public int VigenteDesdeAnio { get; set; }
 508}
 509
 510public class AgregarPersonaACargoRequest
 511{
 512    [Required]
 513    [MaxLength(100)]
 514    public string Nombre { get; set; } = string.Empty;
 515
 516    [Required]
 517    [MaxLength(100)]
 518    public string Apellido { get; set; } = string.Empty;
 519
 520    [Required]
 521    public DateTime FechaNacimiento { get; set; }
 522
 523    [MaxLength(20)]
 524    public string? Cedula { get; set; }
 525
 526    [Required]
 527    [MaxLength(1)]
 528    public string Sexo { get; set; } = string.Empty;
 529
 530    [Required]
 531    [Range(0, 100)]
 532    public int PorcentajeAtribucion { get; set; }
 533
 534    [Required]
 535    [MaxLength(100)]
 536    public string Relacion { get; set; } = string.Empty;
 537
 538    public bool Discapacidad { get; set; }
 539}