< Summary

Information
Class: FAU.API.Controllers.Form3100Controller
Assembly: FAU.API
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.API/Controllers/Form3100Controller.cs
Line coverage
90%
Covered lines: 190
Uncovered lines: 19
Coverable lines: 209
Total lines: 539
Line coverage: 90.9%
Branch coverage
88%
Covered branches: 53
Total branches: 60
Branch coverage: 88.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%
Crear()92.85%2828100%
GetHistorial()100%44100%
GetById()100%22100%
GetVigente()87.5%8891.66%
GetPersonasACargo()66.66%66100%
AgregarPersonaACargo()100%22100%
EliminarPersonaACargo()100%11100%
SubirArchivo()100%8895.83%
DescargarArchivo()0%2268.75%
MapToDto(...)50%2264.7%

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
 4325    public Form3100Controller(
 4326        IForm3100Service form3100Service,
 4327        ICurrentUserContext currentUser,
 4328        ILogger<Form3100Controller> logger)
 29    {
 4330        _form3100Service = form3100Service;
 4331        _currentUser = currentUser;
 4332        _logger = logger;
 4333    }
 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        {
 1251            if (string.IsNullOrWhiteSpace(request.CedulaTitular))
 152                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 53
 1154            if (request.VigenteDesdeMes < 1 || request.VigenteDesdeMes > 12)
 155                return BadRequest(ApiResponse<string>.ErrorResult("vigenteDesdeMes debe estar entre 1 y 12"));
 56
 1057            if (request.VigenteDesdeAnio < 2000 || request.VigenteDesdeAnio > 2100)
 158                return BadRequest(ApiResponse<string>.ErrorResult("vigenteDesdeAnio inválido"));
 59
 960            if (request.CappuCategoria.HasValue && (request.CappuCategoria < 1 || request.CappuCategoria > 10))
 161                return BadRequest(ApiResponse<string>.ErrorResult("cappuCategoria debe estar entre 1 y 10"));
 62
 863            if (request.CappuImporte < 0)
 164                return BadRequest(ApiResponse<string>.ErrorResult("cappuImporte no puede ser negativo"));
 65
 766            if (request.ReintegroAporte < 0)
 167                return BadRequest(ApiResponse<string>.ErrorResult("reintegroAporte no puede ser negativo"));
 68
 669            if (!string.IsNullOrEmpty(request.FondoSolidaridad) &&
 670                !new[] { "0.5_BPC", "1_BPC", "2_BPC" }.Contains(request.FondoSolidaridad))
 171                return BadRequest(ApiResponse<string>.ErrorResult("fondoSolidaridad debe ser '0.5_BPC', '1_BPC' o '2_BPC
 72
 573            var form = new Form3100
 574            {
 575                CappuCategoria = request.CappuCategoria,
 576                CappuImporte = request.CappuImporte,
 577                ReintegroAporte = request.ReintegroAporte,
 578                FondoSolidaridad = request.FondoSolidaridad,
 579                AdicionalFondoSolidaridad = request.AdicionalFondoSolidaridad,
 580                AplicaMinimoNoImponible = request.AplicaMinimoNoImponible,
 581                ConyugeNombre = request.ConyugeNombre,
 582                ConyugeApellido = request.ConyugeApellido,
 583                ConyugeFechaNacimiento = request.ConyugeFechaNacimiento,
 584                ConyugeCedula = request.ConyugeCedula,
 585                ConyugeSexo = request.ConyugeSexo,
 586                ConyugeNacionalidad = request.ConyugeNacionalidad,
 587                VigenteDesde_Mes = (short)request.VigenteDesdeMes,
 588                VigenteDesde_Anio = (short)request.VigenteDesdeAnio
 589            };
 90
 591            var usuarioId = _currentUser.UserId ?? 0;
 592            var creado = await _form3100Service.CrearAsync(request.CedulaTitular, form, usuarioId);
 93
 194            return CreatedAtAction(
 195                nameof(GetById),
 196                new { id = creado.Id },
 197                ApiResponse<object>.SuccessResult(new { id = creado.Id, creado = true }, "Form 3100 creado exitosamente"
 98        }
 199        catch (ArgumentException ex)
 100        {
 1101            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 102        }
 1103        catch (PeriodoBloqueadoException ex)
 104        {
 1105            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 106        }
 1107        catch (InvalidOperationException ex)
 108        {
 1109            return Conflict(ApiResponse<string>.ErrorResult(ex.Message));
 110        }
 1111        catch (Exception ex)
 112        {
 1113            _logger.LogError(ex, "Error al crear Form 3100");
 1114            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 115        }
 12116    }
 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        {
 3134            if (string.IsNullOrWhiteSpace(cedulaTitular))
 1135                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 136
 2137            var historial = await _form3100Service.GetHistorialAsync(cedulaTitular, vigenteDesdeAnio, vigenteDesdeMes);
 138
 1139            var response = historial.Select(MapToDto);
 1140            return Ok(ApiResponse<object>.SuccessResult(response));
 141        }
 1142        catch (Exception ex)
 143        {
 1144            _logger.LogError(ex, "Error al obtener historial Form 3100");
 1145            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 146        }
 3147    }
 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        {
 3162            var form = await _form3100Service.GetByIdAsync(id);
 2163            if (form == null)
 1164                return NotFound(ApiResponse<string>.ErrorResult("Form 3100 no encontrado"));
 165
 1166            return Ok(ApiResponse<object>.SuccessResult(MapToDto(form)));
 167        }
 1168        catch (Exception ex)
 169        {
 1170            _logger.LogError(ex, "Error al obtener Form 3100 {Id}", id);
 1171            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 172        }
 3173    }
 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        {
 4192            if (string.IsNullOrWhiteSpace(cedulaTitular))
 0193                return BadRequest(ApiResponse<string>.ErrorResult("cedulaTitular es requerida"));
 4194            if (mes < 1 || mes > 12)
 1195                return BadRequest(ApiResponse<string>.ErrorResult("mes debe estar entre 1 y 12"));
 196
 3197            var form = await _form3100Service.GetVigenteParaPeriodoAsync(cedulaTitular, anio, mes);
 2198            if (form == null)
 1199                return NotFound(ApiResponse<string>.ErrorResult("No se encontró Form 3100 vigente para el período indica
 200
 1201            return Ok(ApiResponse<object>.SuccessResult(MapToDto(form)));
 202        }
 1203        catch (Exception ex)
 204        {
 1205            _logger.LogError(ex, "Error al obtener Form 3100 vigente");
 1206            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 207        }
 4208    }
 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        {
 3223            var form = await _form3100Service.GetByIdAsync(id);
 2224            if (form == null)
 1225                return NotFound(ApiResponse<string>.ErrorResult("Form 3100 no encontrado"));
 226
 1227            var personas = form.PersonasACargo?.Select(p => new
 1228            {
 1229                id = p.Id, nombre = p.Nombre, apellido = p.Apellido,
 1230                fechaNacimiento = p.FechaNacimiento, cedula = p.Cedula,
 1231                sexo = p.Sexo, porcentajeAtribucion = p.PorcentajeAtribucion,
 1232                relacion = p.Relacion, discapacidad = p.Discapacidad
 1233            });
 1234            return Ok(ApiResponse<object>.SuccessResult(personas ?? Enumerable.Empty<object>()));
 235        }
 1236        catch (Exception ex)
 237        {
 1238            _logger.LogError(ex, "Error al listar personas a cargo de Form 3100 {Id}", id);
 1239            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 240        }
 3241    }
 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        {
 4258            var persona = new Form3100PersonaACargo
 4259            {
 4260                Nombre = request.Nombre,
 4261                Apellido = request.Apellido,
 4262                FechaNacimiento = request.FechaNacimiento.Kind == DateTimeKind.Unspecified
 4263                    ? DateTime.SpecifyKind(request.FechaNacimiento.Date, DateTimeKind.Utc)
 4264                    : request.FechaNacimiento.Date.ToUniversalTime(),
 4265                Cedula = request.Cedula,
 4266                Sexo = request.Sexo,
 4267                PorcentajeAtribucion = (short)request.PorcentajeAtribucion,
 4268                Relacion = request.Relacion,
 4269                Discapacidad = request.Discapacidad
 4270            };
 271
 4272            var creada = await _form3100Service.AgregarPersonaACargoAsync(id, persona);
 273
 1274            return StatusCode(201, ApiResponse<object>.SuccessResult(
 1275                new { id = creada.Id, creado = true },
 1276                "Persona a cargo agregada exitosamente"));
 277        }
 1278        catch (PeriodoBloqueadoException ex)
 279        {
 1280            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 281        }
 1282        catch (ArgumentException ex)
 283        {
 1284            return BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 285        }
 1286        catch (Exception ex)
 287        {
 1288            _logger.LogError(ex, "Error al agregar persona a cargo a Form 3100 {Id}", id);
 1289            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 290        }
 4291    }
 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        {
 4308            await _form3100Service.EliminarPersonaACargoAsync(id, personaCargoId);
 1309            return NoContent();
 310        }
 1311        catch (PeriodoBloqueadoException ex)
 312        {
 1313            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 314        }
 1315        catch (ArgumentException ex)
 316        {
 1317            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 318        }
 1319        catch (Exception ex)
 320        {
 1321            _logger.LogError(ex, "Error al eliminar persona a cargo {PersonaCargoId} de Form 3100 {Id}", personaCargoId,
 1322            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 323        }
 4324    }
 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        {
 6343            if (file == null || file.Length == 0)
 1344                return BadRequest(ApiResponse<string>.ErrorResult("Archivo requerido"));
 345
 5346            var usuarioId = _currentUser.UserId ?? 0;
 5347            await using var stream = file.OpenReadStream();
 5348            var rutaRelativa = await _form3100Service.SubirArchivoAsync(
 5349                id,
 5350                stream,
 5351                file.FileName,
 5352                file.ContentType,
 5353                usuarioId);
 354
 1355            return Ok(ApiResponse<object>.SuccessResult(
 1356                new { rutaRelativa, subido = true },
 1357                "Archivo subido exitosamente"));
 0358        }
 1359        catch (PeriodoBloqueadoException ex)
 360        {
 1361            return StatusCode(StatusCodes.Status423Locked, ApiResponse<string>.ErrorResult(ex.Message));
 362        }
 2363        catch (ArgumentException ex)
 364        {
 2365            if (ex.Message.Contains("no encontrado", StringComparison.OrdinalIgnoreCase))
 1366                return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 1367            return BadRequest(ApiResponse<string>.ErrorResult(ex.Message));
 368        }
 1369        catch (Exception ex)
 370        {
 1371            _logger.LogError(ex, "Error al subir archivo de Form 3100 {Id}", id);
 1372            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 373        }
 6374    }
 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        {
 4394            var (contenido, nombreOriginal, contentType) = await _form3100Service.ObtenerArchivoAsync(id);
 395
 0396            if (inline)
 397            {
 0398                Response.Headers.ContentDisposition =
 0399                    $"inline; filename=\"{nombreOriginal}\"; filename*=UTF-8''{Uri.EscapeDataString(nombreOriginal)}";
 0400                return File(contenido, contentType);
 401            }
 402
 0403            return File(contenido, contentType, nombreOriginal);
 404        }
 1405        catch (ArgumentException ex)
 406        {
 1407            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 408        }
 1409        catch (KeyNotFoundException ex)
 410        {
 1411            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 412        }
 1413        catch (StorageFileNotFoundException ex)
 414        {
 1415            return NotFound(ApiResponse<string>.ErrorResult(ex.Message));
 416        }
 1417        catch (Exception ex)
 418        {
 1419            _logger.LogError(ex, "Error al descargar archivo de Form 3100 {Id}", id);
 1420            return StatusCode(500, ApiResponse<string>.ErrorResult("Error interno del servidor"));
 421        }
 4422    }
 423
 424    private static object MapToDto(Form3100 f)
 425    {
 2426        return new
 2427        {
 2428            id = f.Id,
 2429            personaId = f.PersonaId,
 2430            cappuCategoria = f.CappuCategoria,
 2431            cappuImporte = f.CappuImporte,
 2432            reintegroAporte = f.ReintegroAporte,
 2433            fondoSolidaridad = f.FondoSolidaridad,
 2434            adicionalFondoSolidaridad = f.AdicionalFondoSolidaridad,
 2435            aplicaMinimoNoImponible = f.AplicaMinimoNoImponible,
 2436            conyugeNombre = f.ConyugeNombre,
 2437            conyugeApellido = f.ConyugeApellido,
 2438            conyugeFechaNacimiento = f.ConyugeFechaNacimiento,
 2439            conyugeCedula = f.ConyugeCedula,
 2440            conyugeSexo = f.ConyugeSexo,
 2441            conyugeNacionalidad = f.ConyugeNacionalidad,
 2442            vigenteDesdeMes = f.VigenteDesde_Mes,
 2443            vigenteDesdeAnio = f.VigenteDesde_Anio,
 2444            documentoId = f.DocumentoId,
 2445            fechaCreacion = f.FechaCreacion,
 2446            usuarioCreacionId = f.UsuarioCreacionId,
 0447            personasACargo = f.PersonasACargo?.Select(p => new
 0448            {
 0449                id = p.Id,
 0450                nombre = p.Nombre,
 0451                apellido = p.Apellido,
 0452                fechaNacimiento = p.FechaNacimiento,
 0453                cedula = p.Cedula,
 0454                sexo = p.Sexo,
 0455                porcentajeAtribucion = p.PorcentajeAtribucion,
 0456                relacion = p.Relacion,
 0457                discapacidad = p.Discapacidad
 0458            })
 2459        };
 460    }
 461}
 462
 463public class CrearForm3100Request
 464{
 465    [Required(ErrorMessage = "cedulaTitular es requerida")]
 466    [MaxLength(12)]
 467    public string CedulaTitular { get; set; } = string.Empty;
 468
 469    public short? CappuCategoria { get; set; }
 470
 471    [Range(0, double.MaxValue)]
 472    public decimal CappuImporte { get; set; }
 473
 474    [Range(0, double.MaxValue)]
 475    public decimal ReintegroAporte { get; set; }
 476
 477    [MaxLength(10)]
 478    public string? FondoSolidaridad { get; set; }
 479
 480    public bool AdicionalFondoSolidaridad { get; set; }
 481
 482    public bool AplicaMinimoNoImponible { get; set; }
 483
 484    [MaxLength(100)]
 485    public string? ConyugeNombre { get; set; }
 486
 487    [MaxLength(100)]
 488    public string? ConyugeApellido { get; set; }
 489
 490    public DateTime? ConyugeFechaNacimiento { get; set; }
 491
 492    [MaxLength(20)]
 493    public string? ConyugeCedula { get; set; }
 494
 495    [MaxLength(1)]
 496    public string? ConyugeSexo { get; set; }
 497
 498    [MaxLength(100)]
 499    public string? ConyugeNacionalidad { get; set; }
 500
 501    [Required]
 502    [Range(1, 12)]
 503    public int VigenteDesdeMes { get; set; }
 504
 505    [Required]
 506    [Range(2000, 2100)]
 507    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}