Aplicando filtros por operadora e endpoints para filtragem

This commit is contained in:
Leon 2026-03-09 15:17:28 -03:00
parent 64ffb9f2e5
commit 78f403105c
8 changed files with 321 additions and 61 deletions

View File

@ -18,9 +18,9 @@ namespace line_gestao_api.Controllers
} }
[HttpGet("insights")] [HttpGet("insights")]
public async Task<ActionResult<GeralDashboardInsightsDto>> GetInsights() public async Task<ActionResult<GeralDashboardInsightsDto>> GetInsights([FromQuery] string? operadora = null)
{ {
var dto = await _service.GetInsightsAsync(); var dto = await _service.GetInsightsAsync(operadora);
return Ok(dto); return Ok(dto);
} }
} }

View File

@ -33,29 +33,6 @@ namespace line_gestao_api.Controllers
private readonly SpreadsheetImportAuditService _spreadsheetImportAuditService; private readonly SpreadsheetImportAuditService _spreadsheetImportAuditService;
private readonly string _aparelhoAttachmentsRootPath; private readonly string _aparelhoAttachmentsRootPath;
private static readonly FileExtensionContentTypeProvider FileContentTypeProvider = new(); private static readonly FileExtensionContentTypeProvider FileContentTypeProvider = new();
private static readonly List<AccountCompanyDto> AccountCompanies = new()
{
new AccountCompanyDto
{
Empresa = "CLARO LINE MÓVEL",
Contas = new List<string> { "172593311", "172593840" }
},
new AccountCompanyDto
{
Empresa = "VIVO MACROPHONY",
Contas = new List<string> { "0430237019", "0437488125", "0449508564", "0454371844" }
},
new AccountCompanyDto
{
Empresa = "VIVO LINE MÓVEL",
Contas = new List<string> { "0435288088" }
},
new AccountCompanyDto
{
Empresa = "TIM LINE MÓVEL",
Contas = new List<string> { "0072046192" }
}
};
public LinesController( public LinesController(
AppDbContext db, AppDbContext db,
@ -390,31 +367,14 @@ namespace line_gestao_api.Controllers
[HttpGet("account-companies")] [HttpGet("account-companies")]
public ActionResult<List<AccountCompanyDto>> GetAccountCompanies() public ActionResult<List<AccountCompanyDto>> GetAccountCompanies()
{ {
var items = AccountCompanies var items = OperadoraContaResolver.GetAccountCompanies();
.Select(x => new AccountCompanyDto
{
Empresa = x.Empresa,
Contas = x.Contas.ToList()
})
.ToList();
return Ok(items); return Ok(items);
} }
[HttpGet("accounts")] [HttpGet("accounts")]
public ActionResult<List<string>> GetAccounts([FromQuery] string? empresa) public ActionResult<List<string>> GetAccounts([FromQuery] string? empresa)
{ {
if (string.IsNullOrWhiteSpace(empresa)) var contas = OperadoraContaResolver.GetAccountsByEmpresa(empresa);
return Ok(new List<string>());
var target = empresa.Trim();
var contas = AccountCompanies
.FirstOrDefault(x => string.Equals(x.Empresa, target, StringComparison.OrdinalIgnoreCase))
?.Contas
?.ToList()
?? new List<string>();
return Ok(contas); return Ok(contas);
} }
@ -459,6 +419,7 @@ namespace line_gestao_api.Controllers
[FromQuery] string? search, [FromQuery] string? search,
[FromQuery] string? skil, [FromQuery] string? skil,
[FromQuery] string? client, [FromQuery] string? client,
[FromQuery] string? operadora,
[FromQuery] string? additionalMode, [FromQuery] string? additionalMode,
[FromQuery] string? additionalServices, [FromQuery] string? additionalServices,
[FromQuery] int page = 1, [FromQuery] int page = 1,
@ -491,6 +452,7 @@ namespace line_gestao_api.Controllers
q = ExcludeReservaContext(q); q = ExcludeReservaContext(q);
q = ApplyAdditionalFilters(q, additionalMode, additionalServices); q = ApplyAdditionalFilters(q, additionalMode, additionalServices);
q = OperadoraContaResolver.ApplyOperadoraFilter(q, operadora);
var sb = (sortBy ?? "item").Trim().ToLowerInvariant(); var sb = (sortBy ?? "item").Trim().ToLowerInvariant();
var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase); var desc = string.Equals((sortDir ?? "asc").Trim(), "desc", StringComparison.OrdinalIgnoreCase);
@ -545,6 +507,7 @@ namespace line_gestao_api.Controllers
line.Skil, line.Skil,
line.Modalidade, line.Modalidade,
line.VencConta, line.VencConta,
line.FranquiaVivo,
line.FranquiaLine, line.FranquiaLine,
line.GestaoVozDados, line.GestaoVozDados,
line.Skeelo, line.Skeelo,
@ -614,6 +577,7 @@ namespace line_gestao_api.Controllers
Skil = x.Skil, Skil = x.Skil,
Modalidade = x.Modalidade, Modalidade = x.Modalidade,
VencConta = x.VencConta, VencConta = x.VencConta,
FranquiaVivo = x.FranquiaVivo,
FranquiaLine = x.FranquiaLine, FranquiaLine = x.FranquiaLine,
GestaoVozDados = x.GestaoVozDados, GestaoVozDados = x.GestaoVozDados,
Skeelo = x.Skeelo, Skeelo = x.Skeelo,
@ -625,6 +589,8 @@ namespace line_gestao_api.Controllers
}) })
.ToListAsync(); .ToListAsync();
EnrichOperadoraContext(itemsReserva);
return Ok(new PagedResult<MobileLineListDto> return Ok(new PagedResult<MobileLineListDto>
{ {
Page = page, Page = page,
@ -687,6 +653,7 @@ namespace line_gestao_api.Controllers
Skil = x.Skil, Skil = x.Skil,
Modalidade = x.Modalidade, Modalidade = x.Modalidade,
VencConta = x.VencConta, VencConta = x.VencConta,
FranquiaVivo = x.FranquiaVivo,
FranquiaLine = x.FranquiaLine, FranquiaLine = x.FranquiaLine,
GestaoVozDados = x.GestaoVozDados, GestaoVozDados = x.GestaoVozDados,
Skeelo = x.Skeelo, Skeelo = x.Skeelo,
@ -698,6 +665,8 @@ namespace line_gestao_api.Controllers
}) })
.ToListAsync(); .ToListAsync();
EnrichOperadoraContext(items);
return Ok(new PagedResult<MobileLineListDto> return Ok(new PagedResult<MobileLineListDto>
{ {
Page = page, Page = page,
@ -5273,6 +5242,16 @@ namespace line_gestao_api.Controllers
return vigencia; return vigencia;
} }
private static void EnrichOperadoraContext(IEnumerable<MobileLineListDto> items)
{
foreach (var item in items ?? Enumerable.Empty<MobileLineListDto>())
{
var context = OperadoraContaResolver.Resolve(item.Conta);
item.ContaEmpresa = context.Empresa;
item.Operadora = context.Operadora;
}
}
private static MobileLineDetailDto ToDetailDto(MobileLine x, VigenciaLine? vigencia = null) => new() private static MobileLineDetailDto ToDetailDto(MobileLine x, VigenciaLine? vigencia = null) => new()
{ {
Id = x.Id, Id = x.Id,

View File

@ -1,5 +1,6 @@
using line_gestao_api.Data; using line_gestao_api.Data;
using line_gestao_api.Dtos; using line_gestao_api.Dtos;
using line_gestao_api.Services;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
@ -19,7 +20,7 @@ namespace line_gestao_api.Controllers
} }
[HttpGet("dashboard")] [HttpGet("dashboard")]
public async Task<ActionResult<RelatoriosDashboardDto>> GetDashboard() public async Task<ActionResult<RelatoriosDashboardDto>> GetDashboard([FromQuery] string? operadora = null)
{ {
var todayUtcStart = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc); var todayUtcStart = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Utc);
var tomorrowUtcStart = todayUtcStart.AddDays(1); var tomorrowUtcStart = todayUtcStart.AddDays(1);
@ -31,32 +32,40 @@ namespace line_gestao_api.Controllers
// ========================= // =========================
// GERAL (MobileLines) // GERAL (MobileLines)
// ========================= // =========================
var qLines = _db.MobileLines.AsNoTracking(); var qLines = OperadoraContaResolver.ApplyOperadoraFilter(_db.MobileLines.AsNoTracking(), operadora);
var qLinesWithClient = qLines.Where(x => x.Cliente != null && x.Cliente != ""); var qReserva = qLines.Where(x =>
EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") ||
EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") ||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA"));
var qOperacionais = qLines.Where(x =>
!EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") &&
!EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") &&
!EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA"));
var qOperacionaisWithClient = qOperacionais.Where(x => x.Cliente != null && x.Cliente != "");
var totalLinhas = await qLines.CountAsync(); var totalLinhas = await qLines.CountAsync();
var clientesUnicos = await qLines var clientesUnicos = await qOperacionais
.Where(x => x.Cliente != null && x.Cliente != "") .Where(x => x.Cliente != null && x.Cliente != "")
.Select(x => x.Cliente!) .Select(x => x.Cliente!)
.Distinct() .Distinct()
.CountAsync(); .CountAsync();
var ativos = await qLines.CountAsync(x => var ativos = await qOperacionais.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%ativo%")); EF.Functions.ILike((x.Status ?? "").Trim(), "%ativo%"));
var bloqueadosPerdaRoubo = await qLinesWithClient.CountAsync(x => var bloqueadosPerdaRoubo = await qOperacionaisWithClient.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") ||
EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%")); EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"));
var bloqueados120Dias = await qLinesWithClient.CountAsync(x => var bloqueados120Dias = await qOperacionaisWithClient.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") &&
EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") &&
EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%") &&
!(EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || !(EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") ||
EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"))); EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%")));
var bloqueadosOutros = await qLinesWithClient.CountAsync(x => var bloqueadosOutros = await qOperacionaisWithClient.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") &&
!(EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%")) && !(EF.Functions.ILike((x.Status ?? "").Trim(), "%120%") && EF.Functions.ILike((x.Status ?? "").Trim(), "%dia%")) &&
!(EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%")) !(EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"))
@ -64,16 +73,13 @@ namespace line_gestao_api.Controllers
// Regra do KPI "Bloqueadas" alinhada ao critério da página Geral: // Regra do KPI "Bloqueadas" alinhada ao critério da página Geral:
// status contendo "bloque", "perda" ou "roubo". // status contendo "bloque", "perda" ou "roubo".
var bloqueados = await qLinesWithClient.CountAsync(x => var bloqueados = await qOperacionaisWithClient.CountAsync(x =>
EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%bloque%") ||
EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") || EF.Functions.ILike((x.Status ?? "").Trim(), "%perda%") ||
EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%")); EF.Functions.ILike((x.Status ?? "").Trim(), "%roubo%"));
// Regra alinhada ao filtro "Reservas" da página Geral. // Regra alinhada ao filtro "Reservas" da página Geral.
var reservas = await qLines.CountAsync(x => var reservas = await qReserva.CountAsync();
EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") ||
EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") ||
EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA"));
var topClientes = await qLines var topClientes = await qLines
.Where(x => x.Cliente != null && x.Cliente != "") .Where(x => x.Cliente != null && x.Cliente != "")
@ -173,7 +179,16 @@ namespace line_gestao_api.Controllers
// ========================= // =========================
// USER DATA // USER DATA
// ========================= // =========================
var qUserData = _db.UserDatas.AsNoTracking(); var qLineItems = qLines
.Where(x => x.Item > 0)
.Select(x => x.Item);
var qLineNumbers = qLines
.Where(x => x.Linha != null && x.Linha != "")
.Select(x => x.Linha!);
var qUserData = _db.UserDatas.AsNoTracking()
.Where(x =>
(x.Item > 0 && qLineItems.Contains(x.Item)) ||
(x.Linha != null && x.Linha != "" && qLineNumbers.Contains(x.Linha)));
var userDataRegistros = await qUserData.CountAsync(); var userDataRegistros = await qUserData.CountAsync();
var userDataComCpf = await qUserData.CountAsync(x => x.Cpf != null && x.Cpf != ""); var userDataComCpf = await qUserData.CountAsync(x => x.Cpf != null && x.Cpf != "");

View File

@ -31,6 +31,7 @@ namespace line_gestao_api.Dtos
{ {
public int QtdLinhas { get; set; } public int QtdLinhas { get; set; }
public decimal TotalFranquiaGb { get; set; } public decimal TotalFranquiaGb { get; set; }
public decimal TotalFranquiaLine { get; set; }
public decimal TotalBaseMensal { get; set; } public decimal TotalBaseMensal { get; set; }
public decimal TotalAdicionaisMensal { get; set; } public decimal TotalAdicionaisMensal { get; set; }
public decimal TotalGeralMensal { get; set; } public decimal TotalGeralMensal { get; set; }

View File

@ -18,6 +18,9 @@
public string? Skil { get; set; } public string? Skil { get; set; }
public string? Modalidade { get; set; } public string? Modalidade { get; set; }
public string? VencConta { get; set; } public string? VencConta { get; set; }
public string? ContaEmpresa { get; set; }
public string? Operadora { get; set; }
public decimal? FranquiaVivo { get; set; }
public decimal? FranquiaLine { get; set; } public decimal? FranquiaLine { get; set; }
// Campos para filtro deterministico de adicionais no frontend // Campos para filtro deterministico de adicionais no frontend

View File

@ -75,7 +75,6 @@ namespace line_gestao_api.Models
public DateTime CreatedAt { get; set; } = DateTime.UtcNow; public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow; public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
// ✅ Navegação (1 MobileLine -> N Muregs)
public ICollection<MuregLine> Muregs { get; set; } = new List<MuregLine>(); public ICollection<MuregLine> Muregs { get; set; } = new List<MuregLine>();
} }
} }

View File

@ -25,9 +25,9 @@ namespace line_gestao_api.Services
_db = db; _db = db;
} }
public async Task<GeralDashboardInsightsDto> GetInsightsAsync() public async Task<GeralDashboardInsightsDto> GetInsightsAsync(string? operadora = null)
{ {
var qLines = _db.MobileLines.AsNoTracking(); var qLines = OperadoraContaResolver.ApplyOperadoraFilter(_db.MobileLines.AsNoTracking(), operadora);
var totals = await qLines var totals = await qLines
.GroupBy(_ => 1) .GroupBy(_ => 1)
@ -99,6 +99,7 @@ namespace line_gestao_api.Services
x.VivoGestaoDispositivo != null) x.VivoGestaoDispositivo != null)
? (x.FranquiaVivo ?? 0m) ? (x.FranquiaVivo ?? 0m)
: 0m), : 0m),
TotalFranquiaLine = g.Sum(x => x.FranquiaLine ?? 0m),
VivoAdicionaisTotal = g.Sum(x => VivoAdicionaisTotal = g.Sum(x =>
(x.ValorPlanoVivo != null || (x.ValorPlanoVivo != null ||
x.FranquiaVivo != null || x.FranquiaVivo != null ||
@ -653,6 +654,7 @@ namespace line_gestao_api.Services
{ {
QtdLinhas = totals.VivoLinhas, QtdLinhas = totals.VivoLinhas,
TotalFranquiaGb = totals.VivoFranquiaTotalGb, TotalFranquiaGb = totals.VivoFranquiaTotalGb,
TotalFranquiaLine = totals.TotalFranquiaLine,
TotalBaseMensal = totals.VivoBaseTotal, TotalBaseMensal = totals.VivoBaseTotal,
TotalAdicionaisMensal = totals.VivoAdicionaisTotal, TotalAdicionaisMensal = totals.VivoAdicionaisTotal,
TotalGeralMensal = totalGeralMensal, TotalGeralMensal = totalGeralMensal,
@ -1110,6 +1112,7 @@ namespace line_gestao_api.Services
public int TotalBloqueados { get; set; } public int TotalBloqueados { get; set; }
public int VivoLinhas { get; set; } public int VivoLinhas { get; set; }
public decimal VivoFranquiaTotalGb { get; set; } public decimal VivoFranquiaTotalGb { get; set; }
public decimal TotalFranquiaLine { get; set; }
public decimal VivoBaseTotal { get; set; } public decimal VivoBaseTotal { get; set; }
public decimal VivoAdicionaisTotal { get; set; } public decimal VivoAdicionaisTotal { get; set; }
public decimal VivoMinBase { get; set; } public decimal VivoMinBase { get; set; }

View File

@ -0,0 +1,260 @@
using System.Globalization;
using System.Text;
using line_gestao_api.Dtos;
using line_gestao_api.Models;
using Microsoft.EntityFrameworkCore;
namespace line_gestao_api.Services
{
public sealed record OperadoraContaContext(string Operadora, string Empresa, string? VivoEmpresaGrupo = null);
public static class OperadoraContaResolver
{
private const string OperadoraVivo = "VIVO";
private const string OperadoraClaro = "CLARO";
private const string OperadoraTim = "TIM";
private const string OperadoraOutra = "OUTRA";
private const string EmpresaVivoMacrophony = "VIVO MACROPHONY";
private const string EmpresaVivoLineMovel = "VIVO LINE MÓVEL";
private const string EmpresaClaroLineMovel = "CLARO LINE MÓVEL";
private const string EmpresaTimLineMovel = "TIM LINE MÓVEL";
private static readonly IReadOnlyList<AccountCompanyDto> CompanyRules = new List<AccountCompanyDto>
{
new()
{
Empresa = EmpresaClaroLineMovel,
Contas = new List<string> { "172593311", "172593840", "187890982" }
},
new()
{
Empresa = EmpresaVivoMacrophony,
Contas = new List<string>
{
"0430237019",
"0437488125",
"0449508564",
"0454371844",
"455371844",
"460161507"
}
},
new()
{
Empresa = EmpresaVivoLineMovel,
Contas = new List<string> { "0435288088" }
},
new()
{
Empresa = EmpresaTimLineMovel,
Contas = new List<string> { "TIM" }
}
};
private static readonly string[] VivoContaFilters = BuildContaFilterSet(EmpresaVivoMacrophony, EmpresaVivoLineMovel);
private static readonly string[] ClaroContaFilters = BuildContaFilterSet(EmpresaClaroLineMovel);
private static readonly string[] TimContaFilters = BuildContaFilterSet(EmpresaTimLineMovel);
private static readonly Dictionary<string, string> EmpresaByConta = BuildEmpresaByConta();
public static List<AccountCompanyDto> GetAccountCompanies()
{
return CompanyRules
.Select(x => new AccountCompanyDto
{
Empresa = x.Empresa,
Contas = x.Contas.ToList()
})
.ToList();
}
public static List<string> GetAccountsByEmpresa(string? empresa)
{
if (string.IsNullOrWhiteSpace(empresa)) return new List<string>();
var target = NormalizeToken(empresa);
if (string.IsNullOrWhiteSpace(target)) return new List<string>();
return CompanyRules
.FirstOrDefault(x => NormalizeToken(x.Empresa) == target)
?.Contas
?.ToList()
?? new List<string>();
}
public static OperadoraContaContext Resolve(string? conta)
{
var contaRaw = (conta ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(contaRaw))
{
return new OperadoraContaContext(OperadoraOutra, string.Empty, null);
}
var normalizedConta = NormalizeConta(contaRaw);
if (!string.IsNullOrWhiteSpace(normalizedConta) && EmpresaByConta.TryGetValue(normalizedConta, out var empresaDeterministica))
{
return BuildContextByEmpresa(empresaDeterministica);
}
var token = NormalizeToken(contaRaw);
if (token.Contains("TIM", StringComparison.Ordinal))
{
return BuildContextByEmpresa(EmpresaTimLineMovel);
}
if (token.Contains("CLARO", StringComparison.Ordinal))
{
return BuildContextByEmpresa(EmpresaClaroLineMovel);
}
if (token.Contains("MACROPHONY", StringComparison.Ordinal))
{
return BuildContextByEmpresa(EmpresaVivoMacrophony);
}
if (token.Contains("VIVO", StringComparison.Ordinal))
{
return BuildContextByEmpresa(EmpresaVivoLineMovel);
}
return new OperadoraContaContext(OperadoraOutra, string.Empty, null);
}
public static IQueryable<MobileLine> ApplyOperadoraFilter(IQueryable<MobileLine> query, string? operadora)
{
var token = NormalizeToken(operadora);
if (string.IsNullOrWhiteSpace(token) || token == "TODOS" || token == "ALL")
{
return query;
}
return token switch
{
OperadoraVivo => query.Where(x =>
(x.Conta != null && VivoContaFilters.Contains(x.Conta.Trim()))
|| EF.Functions.ILike((x.Conta ?? string.Empty).Trim(), "%VIVO%")
|| EF.Functions.ILike((x.Conta ?? string.Empty).Trim(), "%MACROPHONY%")),
OperadoraClaro => query.Where(x =>
(x.Conta != null && ClaroContaFilters.Contains(x.Conta.Trim()))
|| EF.Functions.ILike((x.Conta ?? string.Empty).Trim(), "%CLARO%")),
OperadoraTim => query.Where(x =>
(x.Conta != null && TimContaFilters.Contains(x.Conta.Trim()))
|| EF.Functions.ILike((x.Conta ?? string.Empty).Trim(), "%TIM%")),
_ => query
};
}
public static string NormalizeConta(string? conta)
{
var raw = (conta ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(raw)) return string.Empty;
if (raw.All(char.IsDigit))
{
var noLeadingZero = raw.TrimStart('0');
return string.IsNullOrWhiteSpace(noLeadingZero) ? "0" : noLeadingZero;
}
return RemoveDiacritics(raw).ToUpperInvariant();
}
private static OperadoraContaContext BuildContextByEmpresa(string empresa)
{
var token = NormalizeToken(empresa);
var operadora = token.Contains("CLARO", StringComparison.Ordinal)
? OperadoraClaro
: token.Contains("TIM", StringComparison.Ordinal)
? OperadoraTim
: token.Contains("VIVO", StringComparison.Ordinal) || token.Contains("MACROPHONY", StringComparison.Ordinal)
? OperadoraVivo
: OperadoraOutra;
var vivoGrupo = operadora == OperadoraVivo
? token.Contains("MACROPHONY", StringComparison.Ordinal)
? "MACROPHONY"
: token.Contains("LINEMOVEL", StringComparison.Ordinal)
? "LINE MOVEL"
: null
: null;
return new OperadoraContaContext(operadora, empresa, vivoGrupo);
}
private static Dictionary<string, string> BuildEmpresaByConta()
{
var map = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
foreach (var group in CompanyRules)
{
foreach (var conta in group.Contas ?? new List<string>())
{
var normalized = NormalizeConta(conta);
if (string.IsNullOrWhiteSpace(normalized)) continue;
map[normalized] = group.Empresa;
}
}
return map;
}
private static string[] BuildContaFilterSet(params string[] empresas)
{
var empresaTokens = new HashSet<string>(empresas.Select(NormalizeToken), StringComparer.OrdinalIgnoreCase);
var set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var group in CompanyRules)
{
if (!empresaTokens.Contains(NormalizeToken(group.Empresa))) continue;
foreach (var conta in group.Contas ?? new List<string>())
{
var trimmed = (conta ?? string.Empty).Trim();
if (string.IsNullOrWhiteSpace(trimmed)) continue;
set.Add(trimmed);
if (trimmed.All(char.IsDigit))
{
var noLeading = trimmed.TrimStart('0');
if (!string.IsNullOrWhiteSpace(noLeading))
{
set.Add(noLeading);
}
}
}
}
return set.ToArray();
}
private static string NormalizeToken(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
var raw = RemoveDiacritics(value);
var sb = new StringBuilder(raw.Length);
foreach (var ch in raw)
{
if (char.IsLetterOrDigit(ch)) sb.Append(char.ToUpperInvariant(ch));
}
return sb.ToString();
}
private static string RemoveDiacritics(string value)
{
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
var normalized = value.Normalize(NormalizationForm.FormD);
var sb = new StringBuilder(normalized.Length);
foreach (var c in normalized)
{
var category = CharUnicodeInfo.GetUnicodeCategory(c);
if (category != UnicodeCategory.NonSpacingMark)
{
sb.Append(c);
}
}
return sb.ToString().Normalize(NormalizationForm.FormC);
}
}
}