< Summary

Information
Class: FAU.Logica.Services.FictoPersonaService
Assembly: FAU.Logica
File(s): /home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/FictoPersonaService.cs
Line coverage
87%
Covered lines: 378
Uncovered lines: 52
Coverable lines: 430
Total lines: 619
Line coverage: 87.9%
Branch coverage
71%
Covered branches: 82
Total branches: 114
Branch coverage: 71.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
ObtenerPaginadoAsync()0%2040%
ObtenerPorIdAsync()50%22100%
CrearAsync()62.5%88100%
ActualizarAsync()75%4496.55%
DarBajaAsync()75%4496%
ReactivarAsync()66.66%6693.1%
ValidarImportacionAsync()100%11100%
AplicarImportacionAsync()72.22%191886.88%
ValidarLoteAsync()85.41%574884.33%
ResolverCatalogoAsync()81.25%161688.46%
ResolverMdnAsync()100%22100%
NormalizarTipo(...)50%22100%
NormalizarCedula(...)50%2275%
NormalizarCedulaSinExcepcion(...)50%22100%
RedondearImporte(...)100%11100%
ValidarImporte(...)50%2266.66%
NormalizarObservaciones(...)100%22100%
UsuarioValido(...)50%22100%
MapearDto(...)100%11100%
AgregarError(...)100%11100%
CopiarResultado(...)100%11100%
.ctor(...)100%11100%
get_Tipo()100%11100%
get_NumeroFila()100%11100%
get_Cedula()100%11100%
get_Importe()100%11100%
get_Catalogo()100%11100%
.ctor(...)100%11100%
get_Tipo()100%210%
get_NumeroFila()100%210%
get_Cedula()100%210%
get_Importe()100%11100%
get_Catalogo()100%11100%
get_Persona()100%11100%
get_Existente()100%11100%
get_SinCambios()100%11100%
.ctor(...)100%11100%
get_Resultado()100%11100%
get_Filas()100%11100%
get_AcreedorMdn()100%11100%

File(s)

/home/runner/work/Sistema.Liquidacion.BE/Sistema.Liquidacion.BE/FAU.Logica/Services/FictoPersonaService.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.RegularExpressions;
 3using FAU.DataAccess;
 4using FAU.DataAccess.Repositories;
 5using FAU.Entidades;
 6using FAU.Logica.DTOs;
 7using FAU.Logica.Validadores;
 8using Microsoft.EntityFrameworkCore;
 9using Microsoft.EntityFrameworkCore.Storage;
 10
 11namespace FAU.Logica.Services;
 12
 13public class FictoPersonaService : IFictoPersonaService
 14{
 15    private const string CodigoAcreedorMdn = "MDN";
 116    private static readonly IReadOnlyDictionary<string, int> CodigoPorTipo =
 117        new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
 118        {
 119            [TiposFicto.Comedor] = 608,
 120            [TiposFicto.Guarderia] = 614,
 121            [TiposFicto.Ticket] = 618
 122        };
 23
 124    private static readonly IReadOnlyDictionary<string, int> CodigoEspejoPorTipo =
 125        new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase)
 126        {
 127            [TiposFicto.Comedor] = 372,
 128            [TiposFicto.Guarderia] = 375,
 129            [TiposFicto.Ticket] = 376
 130        };
 31
 132    private static readonly IReadOnlyDictionary<int, string> TipoPorCodigo =
 733        CodigoPorTipo.ToDictionary(par => par.Value, par => par.Key);
 34
 35    private readonly IFictoPersonaRepository _repositorio;
 36    private readonly IPersonalRepository _repositorioPersonal;
 37    private readonly ApplicationDbContext _contexto;
 38    private readonly IAuditoriaService _auditoria;
 39
 1440    public FictoPersonaService(
 1441        IFictoPersonaRepository repositorio,
 1442        IPersonalRepository repositorioPersonal,
 1443        ApplicationDbContext contexto,
 1444        IAuditoriaService auditoria)
 45    {
 1446        _repositorio = repositorio;
 1447        _repositorioPersonal = repositorioPersonal;
 1448        _contexto = contexto;
 1449        _auditoria = auditoria;
 1450    }
 51
 52    public async Task<PagedResult<FictoPersonaDto>> ObtenerPaginadoAsync(
 53        int pagina,
 54        int tamano,
 55        string? busqueda,
 56        string? tipo,
 57        bool? activo)
 58    {
 059        pagina = Math.Max(1, pagina);
 060        tamano = Math.Clamp(tamano, 1, 100);
 61
 062        long? catalogoItemId = null;
 063        if (!string.IsNullOrWhiteSpace(tipo))
 064            catalogoItemId = (await ResolverCatalogoAsync(tipo)).Id;
 65
 066        var (items, total) = await _repositorio.ObtenerPaginadoAsync(
 067            pagina,
 068            tamano,
 069            busqueda,
 070            catalogoItemId,
 071            activo);
 72
 073        return new PagedResult<FictoPersonaDto>
 074        {
 075            Items = items.Select(MapearDto),
 076            Page = pagina,
 077            PageSize = tamano,
 078            TotalCount = total
 079        };
 080    }
 81
 82    public async Task<FictoPersonaDto?> ObtenerPorIdAsync(long id)
 83    {
 184        var ficto = await _repositorio.ObtenerPorIdAsync(id);
 185        return ficto is null ? null : MapearDto(ficto);
 186    }
 87
 88    public async Task<FictoPersonaDto> CrearAsync(
 89        CrearFictoPersonaRequest request,
 90        long usuarioId,
 91        string? host)
 92    {
 1093        ValidarImporte(request.Importe);
 1094        var cedula = NormalizarCedula(request.CedulaTitular);
 1095        var relacion = await _repositorioPersonal.GetByCedulaAsync(cedula)
 1096            ?? throw new ArgumentException($"No se encontró personal activo con cédula {cedula}.");
 1097        var catalogo = await ResolverCatalogoAsync(request.Tipo);
 998        var acreedor = await ResolverMdnAsync();
 99
 8100        var existente = await _repositorio.ObtenerPorPersonaYCatalogoAsync(
 8101            relacion.PersonaId,
 8102            catalogo.Id);
 8103        if (existente is not null)
 104        {
 1105            var estado = existente.Activo ? "activo" : "inactivo";
 1106            throw new InvalidOperationException(
 1107                $"La persona ya tiene un ficto de tipo {NormalizarTipo(request.Tipo)} {estado}.");
 108        }
 109
 7110        var ficto = new FictoPersona
 7111        {
 7112            PersonaId = relacion.PersonaId,
 7113            AcreedorId = acreedor.Id,
 7114            CatalogoItemId = catalogo.Id,
 7115            Importe = RedondearImporte(request.Importe),
 7116            Activo = true,
 7117            Observaciones = NormalizarObservaciones(request.Observaciones),
 7118            FechaCreacion = DateTime.UtcNow,
 7119            UsuarioCreacionId = UsuarioValido(usuarioId)
 7120        };
 121
 7122        await _repositorio.CrearAsync(ficto);
 7123        await _auditoria.LogAuditoriaAsync(
 7124            usuarioId,
 7125            AccionEnum.CrearFicto,
 7126            ContextoEnum.Descuentos,
 7127            host,
 7128            nameof(FictoPersona),
 7129            ficto.Id,
 7130            new
 7131            {
 7132                ficto.PersonaId,
 7133                Cedula = cedula,
 7134                Tipo = NormalizarTipo(request.Tipo),
 7135                ficto.Importe,
 7136                Acreedor = CodigoAcreedorMdn
 7137            });
 138
 7139        var fictoCompleto = await _repositorio.ObtenerPorIdAsync(ficto.Id)
 7140            ?? throw new InvalidOperationException("No fue posible recuperar el ficto creado.");
 7141        return MapearDto(fictoCompleto);
 7142    }
 143
 144    public async Task<FictoPersonaDto> ActualizarAsync(
 145        long id,
 146        ActualizarFictoPersonaRequest request,
 147        long usuarioId,
 148        string? host)
 149    {
 2150        ValidarImporte(request.Importe);
 2151        var ficto = await _repositorio.ObtenerPorIdAsync(id)
 2152            ?? throw new ArgumentException($"Ficto {id} no encontrado.");
 1153        if (!ficto.Activo)
 0154            throw new InvalidOperationException("No se puede editar un ficto inactivo. Debe reactivarlo primero.");
 155
 1156        var anterior = new
 1157        {
 1158            ficto.Importe,
 1159            ficto.Observaciones
 1160        };
 161
 1162        ficto.Importe = RedondearImporte(request.Importe);
 1163        ficto.Observaciones = NormalizarObservaciones(request.Observaciones);
 1164        ficto.FechaModificacion = DateTime.UtcNow;
 1165        ficto.UsuarioModificacionId = UsuarioValido(usuarioId);
 1166        await _repositorio.ActualizarAsync(ficto);
 167
 1168        await _auditoria.LogAuditoriaAsync(
 1169            usuarioId,
 1170            AccionEnum.ActualizarFicto,
 1171            ContextoEnum.Descuentos,
 1172            host,
 1173            nameof(FictoPersona),
 1174            ficto.Id,
 1175            new
 1176            {
 1177                Antes = anterior,
 1178                Despues = new { ficto.Importe, ficto.Observaciones }
 1179            });
 180
 1181        return MapearDto(ficto);
 1182    }
 183
 184    public async Task DarBajaAsync(long id, long usuarioId, string? host)
 185    {
 2186        var ficto = await _repositorio.ObtenerPorIdAsync(id)
 2187            ?? throw new ArgumentException($"Ficto {id} no encontrado.");
 1188        if (!ficto.Activo)
 0189            throw new InvalidOperationException("El ficto ya está inactivo.");
 190
 1191        ficto.Activo = false;
 1192        ficto.FechaBaja = DateTime.UtcNow;
 1193        ficto.FechaModificacion = ficto.FechaBaja;
 1194        ficto.UsuarioBajaId = UsuarioValido(usuarioId);
 1195        ficto.UsuarioModificacionId = UsuarioValido(usuarioId);
 1196        await _repositorio.ActualizarAsync(ficto);
 197
 1198        await _auditoria.LogAuditoriaAsync(
 1199            usuarioId,
 1200            AccionEnum.DarBajaFicto,
 1201            ContextoEnum.Descuentos,
 1202            host,
 1203            nameof(FictoPersona),
 1204            ficto.Id,
 1205            new
 1206            {
 1207                ficto.PersonaId,
 1208                ficto.CatalogoItemId,
 1209                ficto.Importe,
 1210                ficto.FechaBaja
 1211            });
 1212    }
 213
 214    public async Task<FictoPersonaDto> ReactivarAsync(long id, long usuarioId, string? host)
 215    {
 2216        var ficto = await _repositorio.ObtenerPorIdAsync(id)
 2217            ?? throw new ArgumentException($"Ficto {id} no encontrado.");
 1218        if (ficto.Activo)
 0219            throw new InvalidOperationException("El ficto ya está activo.");
 220
 1221        var personalActivo = await _contexto.RelacionesLaborales
 1222            .AnyAsync(r => r.PersonaId == ficto.PersonaId && r.Estado == EstadoRelacion.Activo);
 1223        if (!personalActivo)
 0224            throw new InvalidOperationException("No se puede reactivar el ficto porque la persona no tiene una relación 
 225
 1226        ficto.Activo = true;
 1227        ficto.FechaBaja = null;
 1228        ficto.UsuarioBajaId = null;
 1229        ficto.FechaModificacion = DateTime.UtcNow;
 1230        ficto.UsuarioModificacionId = UsuarioValido(usuarioId);
 1231        await _repositorio.ActualizarAsync(ficto);
 232
 1233        await _auditoria.LogAuditoriaAsync(
 1234            usuarioId,
 1235            AccionEnum.ReactivarFicto,
 1236            ContextoEnum.Descuentos,
 1237            host,
 1238            nameof(FictoPersona),
 1239            ficto.Id,
 1240            new
 1241            {
 1242                ficto.PersonaId,
 1243                ficto.CatalogoItemId,
 1244                ficto.Importe
 1245            });
 246
 1247        return MapearDto(ficto);
 1248    }
 249
 250    public async Task<ResultadoValidacionImportacionFictosDto> ValidarImportacionAsync(
 251        ImportarFictosRequest request)
 252    {
 2253        var validacion = await ValidarLoteAsync(request);
 2254        return validacion.Resultado;
 2255    }
 256
 257    public async Task<ResultadoImportacionFictosDto> AplicarImportacionAsync(
 258        ImportarFictosRequest request,
 259        long usuarioId,
 260        string? host)
 261    {
 2262        IDbContextTransaction? transaccion = null;
 2263        if (_contexto.Database.IsRelational())
 0264            transaccion = await _contexto.Database.BeginTransactionAsync();
 265
 266        try
 267        {
 2268            var validacion = await ValidarLoteAsync(request);
 2269            var resultado = CopiarResultado(validacion.Resultado);
 2270            if (!validacion.Resultado.EsValido)
 271            {
 1272                if (transaccion is not null)
 0273                    await transaccion.RollbackAsync();
 1274                return resultado;
 275            }
 276
 1277            var ahora = DateTime.UtcNow;
 1278            var usuarioValido = UsuarioValido(usuarioId);
 10279            foreach (var fila in validacion.Filas)
 280            {
 4281                if (fila.Existente is null)
 282                {
 1283                    _contexto.FictosPersona.Add(new FictoPersona
 1284                    {
 1285                        PersonaId = fila.Persona.Id,
 1286                        AcreedorId = validacion.AcreedorMdn!.Id,
 1287                        CatalogoItemId = fila.Catalogo.Id,
 1288                        Importe = fila.Importe,
 1289                        Activo = true,
 1290                        FechaCreacion = ahora,
 1291                        UsuarioCreacionId = usuarioValido
 1292                    });
 1293                    continue;
 294                }
 295
 3296                if (fila.SinCambios)
 297                    continue;
 298
 2299                fila.Existente.Importe = fila.Importe;
 2300                fila.Existente.AcreedorId = validacion.AcreedorMdn!.Id;
 2301                fila.Existente.Activo = true;
 2302                fila.Existente.FechaBaja = null;
 2303                fila.Existente.UsuarioBajaId = null;
 2304                fila.Existente.FechaModificacion = ahora;
 2305                fila.Existente.UsuarioModificacionId = usuarioValido;
 306            }
 307
 1308            await _contexto.SaveChangesAsync();
 1309            await _auditoria.LogAuditoriaAsync(
 1310                usuarioId,
 1311                AccionEnum.ImportarFictos,
 1312                ContextoEnum.Descuentos,
 1313                host,
 1314                nameof(FictoPersona),
 1315                null,
 1316                new
 1317                {
 1318                    request.NombreArchivo,
 1319                    request.HashArchivo,
 1320                    resultado.TotalFilas,
 1321                    resultado.Nuevos,
 1322                    resultado.Actualizados,
 1323                    resultado.Reactivados,
 1324                    resultado.SinCambios
 1325                });
 326
 1327            if (transaccion is not null)
 0328                await transaccion.CommitAsync();
 1329            resultado.Aplicado = true;
 1330            return resultado;
 331        }
 0332        catch
 333        {
 0334            if (transaccion is not null)
 0335                await transaccion.RollbackAsync();
 0336            throw;
 337        }
 338        finally
 339        {
 2340            if (transaccion is not null)
 0341                await transaccion.DisposeAsync();
 342        }
 2343    }
 344
 345    private async Task<ValidacionLote> ValidarLoteAsync(ImportarFictosRequest request)
 346    {
 4347        var resultado = new ResultadoValidacionImportacionFictosDto
 4348        {
 4349            TotalFilas = request.Filas?.Count ?? 0
 4350        };
 351
 4352        if (request.Filas is null || request.Filas.Count == 0)
 353        {
 0354            AgregarError(resultado, string.Empty, 0, string.Empty, "El archivo no contiene filas para importar.");
 0355            return new ValidacionLote(resultado, [], null);
 356        }
 357
 4358        if (request.Filas.Count > 20000)
 359        {
 0360            AgregarError(resultado, string.Empty, 0, string.Empty, "La importación no puede superar 10.000 filas por hoj
 0361            return new ValidacionLote(resultado, [], null);
 362        }
 363
 4364        var acreedor = await _contexto.Acreedores
 4365            .FirstOrDefaultAsync(a => a.Codigo == CodigoAcreedorMdn && a.Activo);
 4366        if (acreedor is null)
 367        {
 0368            AgregarError(resultado, string.Empty, 0, string.Empty, "El acreedor MDN no existe o no está activo.");
 0369            return new ValidacionLote(resultado, [], null);
 370        }
 371
 4372        var catalogos = new Dictionary<string, CatalogoItem>(StringComparer.OrdinalIgnoreCase);
 25373        foreach (var tipo in request.Filas.Select(f => NormalizarTipo(f.Tipo)).Distinct(StringComparer.OrdinalIgnoreCase
 374        {
 4375            if (!TiposFicto.Importables.Contains(tipo))
 376            {
 5377                foreach (var fila in request.Filas.Where(f => NormalizarTipo(f.Tipo) == tipo))
 1378                    AgregarError(resultado, tipo, fila.NumeroFila, fila.Cedula, "Solo se pueden importar comedor y guard
 379                continue;
 380            }
 381
 382            try
 383            {
 3384                catalogos[tipo] = await ResolverCatalogoAsync(tipo);
 3385            }
 0386            catch (Exception ex) when (ex is ArgumentException or InvalidOperationException)
 387            {
 0388                AgregarError(resultado, tipo, 0, string.Empty, ex.Message);
 0389            }
 3390        }
 391
 4392        var filasNormalizadas = new List<FilaNormalizada>();
 4393        var claves = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
 26394        foreach (var fila in request.Filas)
 395        {
 9396            var tipo = NormalizarTipo(fila.Tipo);
 9397            var cedula = NormalizarCedulaSinExcepcion(fila.Cedula);
 9398            if (!TiposFicto.Importables.Contains(tipo) || !catalogos.ContainsKey(tipo))
 399                continue;
 8400            if (!Regex.IsMatch(cedula, @"^\d{6,7}$"))
 401            {
 0402                AgregarError(resultado, tipo, fila.NumeroFila, cedula, "La cédula debe contener 6 o 7 dígitos (sin dígit
 0403                continue;
 404            }
 8405            cedula = CedulaValidator.CompletarCedula(cedula);
 8406            if (fila.Importe <= 0)
 407            {
 0408                AgregarError(resultado, tipo, fila.NumeroFila, cedula, "El importe debe ser mayor a cero.");
 0409                continue;
 410            }
 411
 8412            var clave = $"{tipo}|{cedula}";
 8413            if (!claves.Add(clave))
 414            {
 1415                AgregarError(resultado, tipo, fila.NumeroFila, cedula, "La cédula está duplicada para el mismo tipo de f
 1416                continue;
 417            }
 418
 7419            filasNormalizadas.Add(new FilaNormalizada(
 7420                tipo,
 7421                fila.NumeroFila,
 7422                cedula,
 7423                RedondearImporte(fila.Importe),
 7424                catalogos[tipo]));
 425        }
 426
 11427        var cedulas = filasNormalizadas.Select(f => f.Cedula).Distinct().ToList();
 4428        var relaciones = await _contexto.RelacionesLaborales
 4429            .AsNoTracking()
 4430            .Include(r => r.Persona)
 4431            .Where(r => r.Estado == EstadoRelacion.Activo && cedulas.Contains(r.Persona.Cedula))
 4432            .ToListAsync();
 4433        var personasPorCedula = relaciones
 6434            .GroupBy(r => r.Persona.Cedula)
 16435            .ToDictionary(g => g.Key, g => g.First().Persona);
 436
 17437        foreach (var fila in filasNormalizadas.Where(f => !personasPorCedula.ContainsKey(f.Cedula)))
 1438            AgregarError(resultado, fila.Tipo, fila.NumeroFila, fila.Cedula, "No se encontró personal activo con esa céd
 439
 4440        var filasConPersona = filasNormalizadas
 7441            .Where(f => personasPorCedula.ContainsKey(f.Cedula))
 4442            .ToList();
 10443        var personaIds = filasConPersona.Select(f => personasPorCedula[f.Cedula].Id).Distinct().ToArray();
 10444        var catalogoIds = filasConPersona.Select(f => f.Catalogo.Id).Distinct().ToArray();
 4445        var existentes = await _repositorio.ObtenerPorPersonasYCatalogosAsync(personaIds, catalogoIds);
 7446        var existentesPorClave = existentes.ToDictionary(f => (f.PersonaId, f.CatalogoItemId));
 447
 4448        var filasResueltas = new List<FilaResuelta>();
 20449        foreach (var fila in filasConPersona)
 450        {
 6451            var persona = personasPorCedula[fila.Cedula];
 6452            existentesPorClave.TryGetValue((persona.Id, fila.Catalogo.Id), out var existente);
 6453            var sinCambios = existente is not null &&
 6454                             existente.Activo &&
 6455                             existente.Importe == fila.Importe &&
 6456                             existente.AcreedorId == acreedor.Id;
 457
 6458            if (existente is null)
 3459                resultado.Nuevos++;
 3460            else if (!existente.Activo)
 1461                resultado.Reactivados++;
 2462            else if (sinCambios)
 1463                resultado.SinCambios++;
 464            else
 1465                resultado.Actualizados++;
 466
 6467            filasResueltas.Add(new FilaResuelta(
 6468                fila.Tipo,
 6469                fila.NumeroFila,
 6470                fila.Cedula,
 6471                fila.Importe,
 6472                fila.Catalogo,
 6473                persona,
 6474                existente,
 6475                sinCambios));
 476        }
 477
 4478        return new ValidacionLote(resultado, filasResueltas, acreedor);
 4479    }
 480
 481    private async Task<CatalogoItem> ResolverCatalogoAsync(string tipo)
 482    {
 13483        tipo = NormalizarTipo(tipo);
 13484        if (!CodigoPorTipo.TryGetValue(tipo, out var codigo))
 0485            throw new ArgumentException($"El tipo de ficto '{tipo}' no es válido.");
 486
 13487        var catalogo = await _contexto.CatalogoItems
 13488            .AsNoTracking()
 13489            .FirstOrDefaultAsync(c =>
 13490                c.Codigo == codigo &&
 13491                c.Tipo == "descuento_personal" &&
 13492                c.Vigente);
 13493        if (catalogo is null)
 0494            throw new InvalidOperationException($"El concepto de {tipo} no existe o no está vigente.");
 13495        if (catalogo.ItemParFictoId is null)
 0496            throw new InvalidOperationException($"El concepto de {tipo} no tiene configurado su par ficto.");
 497
 13498        var codigoEspejoEsperado = CodigoEspejoPorTipo[tipo];
 13499        var conceptoEspejo = await _contexto.CatalogoItems
 13500            .AsNoTracking()
 13501            .FirstOrDefaultAsync(c => c.Id == catalogo.ItemParFictoId.Value);
 13502        if (conceptoEspejo is null ||
 13503            !conceptoEspejo.Vigente ||
 13504            conceptoEspejo.Codigo != codigoEspejoEsperado ||
 13505            !string.Equals(conceptoEspejo.Tipo, "ficto_descuento", StringComparison.OrdinalIgnoreCase) ||
 13506            conceptoEspejo.ItemParFictoId != catalogo.Id)
 507        {
 1508            throw new InvalidOperationException(
 1509                $"El concepto de {tipo} no tiene configurado correctamente el concepto espejo {codigoEspejoEsperado}.");
 510        }
 511
 12512        return catalogo;
 12513    }
 514
 515    private async Task<Acreedor> ResolverMdnAsync() =>
 9516        await _contexto.Acreedores
 9517            .AsNoTracking()
 9518            .FirstOrDefaultAsync(a => a.Codigo == CodigoAcreedorMdn && a.Activo)
 9519        ?? throw new InvalidOperationException("El acreedor MDN no existe o no está activo.");
 520
 521    private static string NormalizarTipo(string? tipo)
 522    {
 40523        var texto = (tipo ?? string.Empty).Trim().ToLowerInvariant().Normalize(NormalizationForm.FormD);
 40524        var caracteres = texto.Where(c =>
 331525            System.Globalization.CharUnicodeInfo.GetUnicodeCategory(c) !=
 331526            System.Globalization.UnicodeCategory.NonSpacingMark);
 40527        return new string(caracteres.ToArray()).Normalize(NormalizationForm.FormC);
 528    }
 529
 530    private static string NormalizarCedula(string cedula)
 531    {
 10532        var normalizada = NormalizarCedulaSinExcepcion(cedula);
 10533        if (!Regex.IsMatch(normalizada, @"^\d{7,8}$"))
 0534            throw new ArgumentException("La cédula debe contener 7 u 8 dígitos.");
 10535        return normalizada;
 536    }
 537
 538    private static string NormalizarCedulaSinExcepcion(string? cedula) =>
 19539        CedulaValidator.Limpiar(cedula ?? string.Empty);
 540
 541    private static decimal RedondearImporte(decimal importe) =>
 15542        Math.Round(importe, 2, MidpointRounding.AwayFromZero);
 543
 544    private static void ValidarImporte(decimal importe)
 545    {
 12546        if (importe <= 0)
 0547            throw new ArgumentException("El importe debe ser mayor a cero.");
 12548    }
 549
 550    private static string? NormalizarObservaciones(string? observaciones) =>
 8551        string.IsNullOrWhiteSpace(observaciones) ? null : observaciones.Trim();
 552
 12553    private static long? UsuarioValido(long usuarioId) => usuarioId > 0 ? usuarioId : null;
 554
 9555    private static FictoPersonaDto MapearDto(FictoPersona ficto) => new()
 9556    {
 9557        Id = ficto.Id,
 9558        PersonaId = ficto.PersonaId,
 9559        Cedula = ficto.Persona.Cedula,
 9560        NombreCompleto = ficto.Persona.NombreCompleto,
 9561        Tipo = TipoPorCodigo.GetValueOrDefault(ficto.CatalogoItem.Codigo, ficto.CatalogoItem.Nombre),
 9562        Importe = ficto.Importe,
 9563        Activo = ficto.Activo,
 9564        AcreedorCodigo = ficto.Acreedor.Codigo,
 9565        AcreedorNombre = ficto.Acreedor.Nombre,
 9566        Observaciones = ficto.Observaciones,
 9567        FechaCreacion = ficto.FechaCreacion,
 9568        FechaModificacion = ficto.FechaModificacion,
 9569        FechaBaja = ficto.FechaBaja
 9570    };
 571
 572    private static void AgregarError(
 573        ResultadoValidacionImportacionFictosDto resultado,
 574        string tipo,
 575        int numeroFila,
 576        string cedula,
 577        string mensaje) =>
 3578        resultado.Errores.Add(new ErrorImportacionFictoDto
 3579        {
 3580            Tipo = tipo,
 3581            NumeroFila = numeroFila,
 3582            Cedula = cedula,
 3583            Mensaje = mensaje
 3584        });
 585
 586    private static ResultadoImportacionFictosDto CopiarResultado(
 2587        ResultadoValidacionImportacionFictosDto origen) => new()
 2588    {
 2589        TotalFilas = origen.TotalFilas,
 2590        Nuevos = origen.Nuevos,
 2591        Actualizados = origen.Actualizados,
 2592        Reactivados = origen.Reactivados,
 2593        SinCambios = origen.SinCambios,
 2594        Errores = origen.Errores.ToList(),
 2595        Aplicado = false
 2596    };
 597
 7598    private sealed record FilaNormalizada(
 7599        string Tipo,
 7600        int NumeroFila,
 40601        string Cedula,
 8602        decimal Importe,
 25603        CatalogoItem Catalogo);
 604
 6605    private sealed record FilaResuelta(
 0606        string Tipo,
 0607        int NumeroFila,
 0608        string Cedula,
 3609        decimal Importe,
 1610        CatalogoItem Catalogo,
 1611        Persona Persona,
 18612        FictoPersona? Existente,
 9613        bool SinCambios);
 614
 4615    private sealed record ValidacionLote(
 6616        ResultadoValidacionImportacionFictosDto Resultado,
 1617        IReadOnlyList<FilaResuelta> Filas,
 7618        Acreedor? AcreedorMdn);
 619}

Methods/Properties

.cctor()
.ctor(FAU.DataAccess.Repositories.IFictoPersonaRepository,FAU.DataAccess.Repositories.IPersonalRepository,FAU.DataAccess.ApplicationDbContext,FAU.Logica.Services.IAuditoriaService)
ObtenerPaginadoAsync()
ObtenerPorIdAsync()
CrearAsync()
ActualizarAsync()
DarBajaAsync()
ReactivarAsync()
ValidarImportacionAsync()
AplicarImportacionAsync()
ValidarLoteAsync()
ResolverCatalogoAsync()
ResolverMdnAsync()
NormalizarTipo(System.String)
NormalizarCedula(System.String)
NormalizarCedulaSinExcepcion(System.String)
RedondearImporte(System.Decimal)
ValidarImporte(System.Decimal)
NormalizarObservaciones(System.String)
UsuarioValido(System.Int64)
MapearDto(FAU.Entidades.FictoPersona)
AgregarError(FAU.Logica.DTOs.ResultadoValidacionImportacionFictosDto,System.String,System.Int32,System.String,System.String)
CopiarResultado(FAU.Logica.DTOs.ResultadoValidacionImportacionFictosDto)
.ctor(System.String,System.Int32,System.String,System.Decimal,FAU.Entidades.CatalogoItem)
get_Tipo()
get_NumeroFila()
get_Cedula()
get_Importe()
get_Catalogo()
.ctor(System.String,System.Int32,System.String,System.Decimal,FAU.Entidades.CatalogoItem,FAU.Entidades.Persona,FAU.Entidades.FictoPersona,System.Boolean)
get_Tipo()
get_NumeroFila()
get_Cedula()
get_Importe()
get_Catalogo()
get_Persona()
get_Existente()
get_SinCambios()
.ctor(FAU.Logica.DTOs.ResultadoValidacionImportacionFictosDto,System.Collections.Generic.IReadOnlyList`1<FAU.Logica.Services.FictoPersonaService/FilaResuelta>,FAU.Entidades.Acreedor)
get_Resultado()
get_Filas()
get_AcreedorMdn()