line-gestao-api/Services/GeralDashboardInsightsServi...

1172 lines
54 KiB
C#

using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using line_gestao_api.Data;
using line_gestao_api.Dtos;
using Microsoft.EntityFrameworkCore;
namespace line_gestao_api.Services
{
public class GeralDashboardInsightsService
{
private static readonly CultureInfo PtBr = new("pt-BR");
private const string ServiceGestaoVozDados = "GESTÃO VOZ E DADOS";
private const string ServiceSkeelo = "SKEELO";
private const string ServiceVivoNewsPlus = "VIVO NEWS PLUS";
private const string ServiceVivoTravelMundo = "VIVO TRAVEL MUNDO";
private const string ServiceVivoSync = "VIVO SYNC";
private const string ServiceVivoGestaoDispositivo = "VIVO GESTÃO DISPOSITIVO";
private readonly AppDbContext _db;
public GeralDashboardInsightsService(AppDbContext db)
{
_db = db;
}
public async Task<GeralDashboardInsightsDto> GetInsightsAsync()
{
var qLines = _db.MobileLines.AsNoTracking();
var totals = await qLines
.GroupBy(_ => 1)
.Select(g => new TotalsProjection
{
TotalLinhas = g.Count(),
TotalAtivas = g.Count(x => (x.Status ?? "").ToLower().Contains("ativo")),
PfAtivas = g.Count(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("fís") || (x.Skil ?? "").ToLower().Contains("fis") || (x.Skil ?? "").ToLower().Contains("pf"))),
PjAtivas = g.Count(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("jur") || (x.Skil ?? "").ToLower().Contains("pj"))),
PfValorTotalLineAtivas = g.Sum(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("fís") || (x.Skil ?? "").ToLower().Contains("fis") || (x.Skil ?? "").ToLower().Contains("pf"))
? (x.ValorContratoLine ?? 0m)
: 0m),
PjValorTotalLineAtivas = g.Sum(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("jur") || (x.Skil ?? "").ToLower().Contains("pj"))
? (x.ValorContratoLine ?? 0m)
: 0m),
PfLucroTotalLineAtivas = g.Sum(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("fís") || (x.Skil ?? "").ToLower().Contains("fis") || (x.Skil ?? "").ToLower().Contains("pf"))
? (x.Lucro ?? 0m)
: 0m),
PjLucroTotalLineAtivas = g.Sum(x =>
(x.Status ?? "").ToLower().Contains("ativo") &&
((x.Skil ?? "").ToLower().Contains("jur") || (x.Skil ?? "").ToLower().Contains("pj"))
? (x.Lucro ?? 0m)
: 0m),
TotalBloqueados = g.Count(x =>
(x.Status ?? "").ToLower().Contains("bloque") ||
(x.Status ?? "").ToLower().Contains("perda") ||
(x.Status ?? "").ToLower().Contains("roubo")),
VivoLinhas = g.Count(x =>
x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null),
VivoBaseTotal = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.ValorPlanoVivo ?? 0m)
: 0m),
VivoFranquiaTotalGb = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.FranquiaVivo ?? 0m)
: 0m),
VivoAdicionaisTotal = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.GestaoVozDados ?? 0m) + (x.Skeelo ?? 0m) + (x.VivoNewsPlus ?? 0m) +
(x.VivoTravelMundo ?? 0m) + (x.VivoSync ?? 0m) + (x.VivoGestaoDispositivo ?? 0m)
: 0m),
VivoMinBase = g.Where(x =>
x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
.Select(x => x.ValorPlanoVivo ?? 0m)
.DefaultIfEmpty()
.Min(),
VivoMaxBase = g.Where(x =>
x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
.Select(x => x.ValorPlanoVivo ?? 0m)
.DefaultIfEmpty()
.Max(),
TravelCom = g.Count(x => (x.VivoTravelMundo ?? 0m) > 0m),
TravelSem = g.Count(x => (x.VivoTravelMundo ?? 0m) <= 0m),
TravelTotal = g.Sum(x => x.VivoTravelMundo ?? 0m),
PaidGestaoVozDados = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.GestaoVozDados ?? 0m) > 0m),
PaidSkeelo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.Skeelo ?? 0m) > 0m),
PaidNews = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoNewsPlus ?? 0m) > 0m),
PaidTravel = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoTravelMundo ?? 0m) > 0m),
PaidGestaoDispositivo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoGestaoDispositivo ?? 0m) > 0m),
PaidSync = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoSync ?? 0m) > 0m),
NotPaidGestaoVozDados = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.GestaoVozDados ?? 0m) <= 0m),
NotPaidSkeelo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.Skeelo ?? 0m) <= 0m),
NotPaidNews = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoNewsPlus ?? 0m) <= 0m),
NotPaidTravel = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoTravelMundo ?? 0m) <= 0m),
NotPaidGestaoDispositivo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
NotPaidSync = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoSync ?? 0m) <= 0m),
TotalLinesWithAnyPaidAdditional = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
((x.GestaoVozDados ?? 0m) > 0m ||
(x.Skeelo ?? 0m) > 0m ||
(x.VivoNewsPlus ?? 0m) > 0m ||
(x.VivoTravelMundo ?? 0m) > 0m ||
(x.VivoSync ?? 0m) > 0m ||
(x.VivoGestaoDispositivo ?? 0m) > 0m)),
TotalLinesWithNoPaidAdditional = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.GestaoVozDados ?? 0m) <= 0m &&
(x.Skeelo ?? 0m) <= 0m &&
(x.VivoNewsPlus ?? 0m) <= 0m &&
(x.VivoTravelMundo ?? 0m) <= 0m &&
(x.VivoSync ?? 0m) <= 0m &&
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
TotalGestaoVozDados = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.GestaoVozDados ?? 0m)
: 0m),
TotalSkeelo = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.Skeelo ?? 0m)
: 0m),
TotalNews = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoNewsPlus ?? 0m)
: 0m),
TotalTravel = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoTravelMundo ?? 0m)
: 0m),
TotalGestaoDispositivo = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoGestaoDispositivo ?? 0m)
: 0m),
TotalSync = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoSync ?? 0m)
: 0m)
})
.FirstOrDefaultAsync();
var franquiasRaw = await qLines
.Select(x => new FranquiaProjection { FranquiaVivo = x.FranquiaVivo, PlanoContrato = x.PlanoContrato })
.ToListAsync();
var linhasPorFranquia = BuildFranquiaBuckets(franquiasRaw);
var tipoChip = await qLines.Select(x => x.TipoDeChip).ToListAsync();
var tipoChipBuckets = BuildTipoChipBuckets(tipoChip);
var clientGroupsRaw = await qLines
.Where(x => x.Cliente != null && x.Cliente != "")
.GroupBy(x => x.Cliente!)
.Select(g => new ClientGroupProjection
{
Cliente = g.Key,
TotalLinhas = g.Count(),
Ativos = g.Count(x => (x.Status ?? "").ToLower().Contains("ativo")),
Bloqueados = g.Count(x =>
(x.Status ?? "").ToLower().Contains("bloque") ||
(x.Status ?? "").ToLower().Contains("perda") ||
(x.Status ?? "").ToLower().Contains("roubo")),
LinhasVivo = g.Count(x =>
x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null),
TravelCom = g.Count(x => (x.VivoTravelMundo ?? 0m) > 0m),
TravelSem = g.Count(x => (x.VivoTravelMundo ?? 0m) <= 0m),
PaidGestaoVozDados = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.GestaoVozDados ?? 0m) > 0m),
PaidSkeelo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.Skeelo ?? 0m) > 0m),
PaidNews = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoNewsPlus ?? 0m) > 0m),
PaidTravel = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoTravelMundo ?? 0m) > 0m),
PaidGestaoDispositivo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoGestaoDispositivo ?? 0m) > 0m),
PaidSync = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoSync ?? 0m) > 0m),
NotPaidGestaoVozDados = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.GestaoVozDados ?? 0m) <= 0m),
NotPaidSkeelo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.Skeelo ?? 0m) <= 0m),
NotPaidNews = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoNewsPlus ?? 0m) <= 0m),
NotPaidTravel = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoTravelMundo ?? 0m) <= 0m),
NotPaidGestaoDispositivo = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoGestaoDispositivo ?? 0m) <= 0m),
NotPaidSync = g.Count(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null) &&
(x.VivoSync ?? 0m) <= 0m),
TotalGestaoVozDados = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.GestaoVozDados ?? 0m)
: 0m),
TotalSkeelo = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.Skeelo ?? 0m)
: 0m),
TotalNews = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoNewsPlus ?? 0m)
: 0m),
TotalTravel = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoTravelMundo ?? 0m)
: 0m),
TotalGestaoDispositivo = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoGestaoDispositivo ?? 0m)
: 0m),
TotalSync = g.Sum(x =>
(x.ValorPlanoVivo != null ||
x.FranquiaVivo != null ||
x.ValorContratoVivo != null ||
x.GestaoVozDados != null ||
x.Skeelo != null ||
x.VivoNewsPlus != null ||
x.VivoTravelMundo != null ||
x.VivoSync != null ||
x.VivoGestaoDispositivo != null)
? (x.VivoSync ?? 0m)
: 0m)
})
.OrderBy(x => x.Cliente)
.ToListAsync();
var dto = new GeralDashboardInsightsDto
{
Kpis = BuildKpis(totals),
Charts = BuildCharts(totals, linhasPorFranquia, tipoChipBuckets),
ClientGroups = BuildClientGroups(clientGroupsRaw)
};
return dto;
}
private static GeralDashboardKpisDto BuildKpis(TotalsProjection? totals)
{
if (totals == null)
{
return new GeralDashboardKpisDto
{
Vivo = new GeralDashboardVivoKpiDto(),
TravelMundo = new GeralDashboardTravelKpiDto(),
Adicionais = new GeralDashboardAdditionalKpiDto()
};
}
var totalGeralMensal = totals.VivoBaseTotal + totals.VivoAdicionaisTotal;
var media = totals.VivoLinhas > 0 ? totalGeralMensal / totals.VivoLinhas : 0m;
return new GeralDashboardKpisDto
{
TotalLinhas = totals.TotalLinhas,
TotalAtivas = totals.TotalAtivas,
Vivo = new GeralDashboardVivoKpiDto
{
QtdLinhas = totals.VivoLinhas,
TotalFranquiaGb = totals.VivoFranquiaTotalGb,
TotalBaseMensal = totals.VivoBaseTotal,
TotalAdicionaisMensal = totals.VivoAdicionaisTotal,
TotalGeralMensal = totalGeralMensal,
MediaPorLinha = media,
MinPorLinha = totals.VivoLinhas > 0 ? totals.VivoMinBase : null,
MaxPorLinha = totals.VivoLinhas > 0 ? totals.VivoMaxBase : null
},
TravelMundo = new GeralDashboardTravelKpiDto
{
ComTravel = totals.TravelCom,
SemTravel = totals.TravelSem,
TotalValue = totals.TravelTotal
},
Adicionais = new GeralDashboardAdditionalKpiDto
{
TotalLinesWithAnyPaidAdditional = totals.TotalLinesWithAnyPaidAdditional,
TotalLinesWithNoPaidAdditional = totals.TotalLinesWithNoPaidAdditional,
ServicesPaid = new List<GeralDashboardServiceKpiDto>
{
new() { ServiceName = ServiceGestaoVozDados, CountLines = totals.PaidGestaoVozDados, TotalValue = totals.TotalGestaoVozDados },
new() { ServiceName = ServiceSkeelo, CountLines = totals.PaidSkeelo, TotalValue = totals.TotalSkeelo },
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.PaidNews, TotalValue = totals.TotalNews },
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.PaidTravel, TotalValue = totals.TotalTravel },
new() { ServiceName = ServiceVivoSync, CountLines = totals.PaidSync, TotalValue = totals.TotalSync },
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.PaidGestaoDispositivo, TotalValue = totals.TotalGestaoDispositivo }
},
ServicesNotPaid = new List<GeralDashboardServiceKpiDto>
{
new() { ServiceName = ServiceGestaoVozDados, CountLines = totals.NotPaidGestaoVozDados, TotalValue = 0m },
new() { ServiceName = ServiceSkeelo, CountLines = totals.NotPaidSkeelo, TotalValue = 0m },
new() { ServiceName = ServiceVivoNewsPlus, CountLines = totals.NotPaidNews, TotalValue = 0m },
new() { ServiceName = ServiceVivoTravelMundo, CountLines = totals.NotPaidTravel, TotalValue = 0m },
new() { ServiceName = ServiceVivoSync, CountLines = totals.NotPaidSync, TotalValue = 0m },
new() { ServiceName = ServiceVivoGestaoDispositivo, CountLines = totals.NotPaidGestaoDispositivo, TotalValue = 0m }
}
},
TotaisLine = BuildTotaisLineRows(totals)
};
}
private static GeralDashboardChartsDto BuildCharts(TotalsProjection? totals, FranquiaBuckets franquias, TipoChipBuckets tipoChip)
{
var adicionaisLabels = new List<string>
{
ServiceGestaoVozDados,
ServiceSkeelo,
ServiceVivoNewsPlus,
ServiceVivoTravelMundo,
ServiceVivoSync,
ServiceVivoGestaoDispositivo
};
var adicionaisValues = totals == null
? new List<int> { 0, 0, 0, 0, 0, 0 }
: new List<int>
{
totals.PaidGestaoVozDados,
totals.PaidSkeelo,
totals.PaidNews,
totals.PaidTravel,
totals.PaidSync,
totals.PaidGestaoDispositivo
};
var adicionaisTotals = totals == null
? new List<decimal> { 0m, 0m, 0m, 0m, 0m, 0m }
: new List<decimal>
{
totals.TotalGestaoVozDados,
totals.TotalSkeelo,
totals.TotalNews,
totals.TotalTravel,
totals.TotalSync,
totals.TotalGestaoDispositivo
};
return new GeralDashboardChartsDto
{
LinhasPorFranquia = new GeralDashboardChartDto
{
Labels = franquias.Labels,
Values = franquias.Values
},
AdicionaisPagosPorServico = new GeralDashboardChartDto
{
Labels = adicionaisLabels,
Values = adicionaisValues,
Totals = adicionaisTotals
},
TravelMundo = new GeralDashboardChartDto
{
Labels = new List<string> { "Com", "Sem" },
Values = totals == null
? new List<int> { 0, 0 }
: new List<int> { totals.TravelCom, totals.TravelSem }
},
TipoChip = new GeralDashboardChartDto
{
Labels = tipoChip.Labels,
Values = tipoChip.Values
}
};
}
private static List<GeralDashboardLineTotalDto> BuildTotaisLineRows(TotalsProjection? totals)
{
if (totals == null)
{
return new List<GeralDashboardLineTotalDto>();
}
var diffQtd = totals.PjAtivas - totals.PfAtivas;
var diffValor = totals.PjValorTotalLineAtivas - totals.PfValorTotalLineAtivas;
var diffLucro = totals.PjLucroTotalLineAtivas - totals.PfLucroTotalLineAtivas;
return new List<GeralDashboardLineTotalDto>
{
new()
{
Tipo = "PESSOA FISICA",
QtdLinhas = totals.PfAtivas,
ValorTotalLine = totals.PfValorTotalLineAtivas,
LucroTotalLine = totals.PfLucroTotalLineAtivas
},
new()
{
Tipo = "PESSOA JURIDICA",
QtdLinhas = totals.PjAtivas,
ValorTotalLine = totals.PjValorTotalLineAtivas,
LucroTotalLine = totals.PjLucroTotalLineAtivas
},
new()
{
Tipo = "DIFERENCA PJ X PF",
QtdLinhas = diffQtd,
ValorTotalLine = diffValor,
LucroTotalLine = diffLucro
}
};
}
private static List<GeralDashboardClientGroupDto> BuildClientGroups(IEnumerable<ClientGroupProjection> rows)
{
var list = new List<GeralDashboardClientGroupDto>();
foreach (var row in rows)
{
var baseTags = new List<GeralDashboardTagDto>
{
new() { Label = "LINHAS", Value = row.TotalLinhas.ToString(PtBr) },
new() { Label = "ATIVAS", Value = row.Ativos.ToString(PtBr) },
new() { Label = "BLOQUEADAS", Value = row.Bloqueados.ToString(PtBr) },
new() { Label = "LINHAS VIVO", Value = row.LinhasVivo.ToString(PtBr) }
};
var extras = new List<GeralDashboardTagDto>
{
new() { Label = "TRAVEL MUNDO", Value = row.TravelCom.ToString(PtBr) },
new() { Label = "SEM TRAVEL", Value = row.TravelSem.ToString(PtBr) },
new() { Label = ServiceGestaoVozDados, Value = row.PaidGestaoVozDados.ToString(PtBr) },
new() { Label = $"R$ {ServiceGestaoVozDados}", Value = FormatCurrency(row.TotalGestaoVozDados) },
new() { Label = ServiceSkeelo, Value = row.PaidSkeelo.ToString(PtBr) },
new() { Label = $"R$ {ServiceSkeelo}", Value = FormatCurrency(row.TotalSkeelo) },
new() { Label = ServiceVivoNewsPlus, Value = row.PaidNews.ToString(PtBr) },
new() { Label = $"R$ {ServiceVivoNewsPlus}", Value = FormatCurrency(row.TotalNews) },
new() { Label = ServiceVivoTravelMundo, Value = row.PaidTravel.ToString(PtBr) },
new() { Label = $"R$ {ServiceVivoTravelMundo}", Value = FormatCurrency(row.TotalTravel) },
new() { Label = ServiceVivoSync, Value = row.PaidSync.ToString(PtBr) },
new() { Label = $"R$ {ServiceVivoSync}", Value = FormatCurrency(row.TotalSync) },
new() { Label = ServiceVivoGestaoDispositivo, Value = row.PaidGestaoDispositivo.ToString(PtBr) },
new() { Label = $"R$ {ServiceVivoGestaoDispositivo}", Value = FormatCurrency(row.TotalGestaoDispositivo) }
};
var serviceDetails = new List<GeralDashboardClientServiceDto>
{
new()
{
ServiceName = ServiceGestaoVozDados,
PaidCount = row.PaidGestaoVozDados,
NotPaidCount = row.NotPaidGestaoVozDados,
TotalValue = row.TotalGestaoVozDados
},
new()
{
ServiceName = ServiceSkeelo,
PaidCount = row.PaidSkeelo,
NotPaidCount = row.NotPaidSkeelo,
TotalValue = row.TotalSkeelo
},
new()
{
ServiceName = ServiceVivoNewsPlus,
PaidCount = row.PaidNews,
NotPaidCount = row.NotPaidNews,
TotalValue = row.TotalNews
},
new()
{
ServiceName = ServiceVivoTravelMundo,
PaidCount = row.PaidTravel,
NotPaidCount = row.NotPaidTravel,
TotalValue = row.TotalTravel
},
new()
{
ServiceName = ServiceVivoSync,
PaidCount = row.PaidSync,
NotPaidCount = row.NotPaidSync,
TotalValue = row.TotalSync
},
new()
{
ServiceName = ServiceVivoGestaoDispositivo,
PaidCount = row.PaidGestaoDispositivo,
NotPaidCount = row.NotPaidGestaoDispositivo,
TotalValue = row.TotalGestaoDispositivo
}
};
list.Add(new GeralDashboardClientGroupDto
{
Cliente = row.Cliente,
TotalLinhas = row.TotalLinhas,
Ativos = row.Ativos,
Bloqueados = row.Bloqueados,
LinhasVivo = row.LinhasVivo,
TagsBase = baseTags,
TagsExtras = extras,
AdicionaisPorServico = serviceDetails
});
}
return list;
}
private static TipoChipBuckets BuildTipoChipBuckets(IEnumerable<string?> chipTypes)
{
var esim = 0;
var simcard = 0;
foreach (var raw in chipTypes)
{
var normalized = NormalizeChipType(raw);
if (normalized == "ESIM")
{
esim++;
continue;
}
if (normalized == "SIMCARD")
{
simcard++;
}
}
return new TipoChipBuckets
{
Labels = new List<string> { "e-SIM", "SIMCARD" },
Values = new List<int> { esim, simcard }
};
}
private static string NormalizeChipType(string? value)
{
var normalized = NormalizeText(value);
if (string.IsNullOrWhiteSpace(normalized))
{
return string.Empty;
}
if (normalized.Contains("ESIM"))
{
return "ESIM";
}
if (normalized.Contains("SIM") ||
normalized.Contains("CHIP") ||
normalized.Contains("CARD") ||
normalized.Contains("FISIC"))
{
return "SIMCARD";
}
return string.Empty;
}
private static string NormalizeText(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return string.Empty;
var decomposed = value
.Trim()
.ToUpperInvariant()
.Normalize(NormalizationForm.FormD);
var builder = new StringBuilder(decomposed.Length);
foreach (var ch in decomposed)
{
if (CharUnicodeInfo.GetUnicodeCategory(ch) == UnicodeCategory.NonSpacingMark)
continue;
if (char.IsLetterOrDigit(ch))
builder.Append(ch);
}
return builder.ToString();
}
private static FranquiaBuckets BuildFranquiaBuckets(IEnumerable<FranquiaProjection> rows)
{
var map = new Dictionary<string, FranquiaBucket>();
foreach (var row in rows)
{
var label = NormalizeFranquiaLabel(row.FranquiaVivo, row.PlanoContrato);
var key = label;
if (!map.TryGetValue(key, out var bucket))
{
bucket = new FranquiaBucket { Label = label, SortKey = BuildFranquiaSortKey(label) };
map[key] = bucket;
}
bucket.Count++;
}
var ordered = map.Values
.OrderBy(x => x.SortKey)
.ThenBy(x => x.Label)
.ToList();
return new FranquiaBuckets
{
Labels = ordered.Select(x => x.Label).ToList(),
Values = ordered.Select(x => x.Count).ToList()
};
}
private static string NormalizeFranquiaLabel(decimal? value, string? planoContrato)
{
if (value.HasValue && value.Value > 0)
{
return NormalizeNumericFranquia(value.Value);
}
var parsed = ParseFranquiaFromPlano(planoContrato);
return string.IsNullOrWhiteSpace(parsed) ? "Sem Franquia" : parsed;
}
private static string? ParseFranquiaFromPlano(string? planoContrato)
{
if (string.IsNullOrWhiteSpace(planoContrato)) return null;
var match = Regex.Match(planoContrato, @"(?<val>\d+(?:[.,]\d+)?)\s*(?<unit>GB|MB)", RegexOptions.IgnoreCase);
if (!match.Success) return null;
var valueRaw = match.Groups["val"].Value.Replace(",", ".");
if (!decimal.TryParse(valueRaw, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) return null;
var unit = match.Groups["unit"].Value.ToUpperInvariant();
if (unit == "GB")
{
return $"{TrimDecimal(value)}GB";
}
return $"{Math.Round(value):0}MB";
}
private static string NormalizeNumericFranquia(decimal value)
{
if (value < 1m)
{
var mb = Math.Round(value * 1000m);
return $"{mb:0}MB";
}
if (value >= 100m && value < 1024m)
{
return $"{Math.Round(value):0}MB";
}
if (value >= 1024m)
{
var gb = value / 1024m;
return $"{TrimDecimal(gb)}GB";
}
return $"{TrimDecimal(value)}GB";
}
private static decimal BuildFranquiaSortKey(string label)
{
if (label.Equals("Sem Franquia", StringComparison.OrdinalIgnoreCase))
{
return decimal.MaxValue;
}
var match = Regex.Match(label, @"(?<val>\d+(?:[.,]\d+)?)\s*(?<unit>GB|MB)", RegexOptions.IgnoreCase);
if (!match.Success) return decimal.MaxValue - 1;
var valueRaw = match.Groups["val"].Value.Replace(",", ".");
if (!decimal.TryParse(valueRaw, NumberStyles.Any, CultureInfo.InvariantCulture, out var value))
{
return decimal.MaxValue - 1;
}
var unit = match.Groups["unit"].Value.ToUpperInvariant();
return unit == "MB" ? value / 1000m : value;
}
private static string TrimDecimal(decimal value)
{
return value % 1 == 0 ? value.ToString("0", CultureInfo.InvariantCulture) : value.ToString("0.#", CultureInfo.InvariantCulture);
}
private static string FormatCurrency(decimal value)
{
return value.ToString("C", PtBr);
}
private sealed class FranquiaBucket
{
public string Label { get; set; } = string.Empty;
public int Count { get; set; }
public decimal SortKey { get; set; }
}
private sealed class FranquiaBuckets
{
public List<string> Labels { get; set; } = new();
public List<int> Values { get; set; } = new();
}
private sealed class TipoChipBuckets
{
public List<string> Labels { get; set; } = new();
public List<int> Values { get; set; } = new();
}
private sealed class FranquiaProjection
{
public decimal? FranquiaVivo { get; set; }
public string? PlanoContrato { get; set; }
}
private sealed class TotalsProjection
{
public int TotalLinhas { get; set; }
public int TotalAtivas { get; set; }
public int PfAtivas { get; set; }
public int PjAtivas { get; set; }
public decimal PfValorTotalLineAtivas { get; set; }
public decimal PjValorTotalLineAtivas { get; set; }
public decimal PfLucroTotalLineAtivas { get; set; }
public decimal PjLucroTotalLineAtivas { get; set; }
public int TotalBloqueados { get; set; }
public int VivoLinhas { get; set; }
public decimal VivoFranquiaTotalGb { get; set; }
public decimal VivoBaseTotal { get; set; }
public decimal VivoAdicionaisTotal { get; set; }
public decimal VivoMinBase { get; set; }
public decimal VivoMaxBase { get; set; }
public int TravelCom { get; set; }
public int TravelSem { get; set; }
public decimal TravelTotal { get; set; }
public int PaidGestaoVozDados { get; set; }
public int PaidSkeelo { get; set; }
public int PaidNews { get; set; }
public int PaidTravel { get; set; }
public int PaidGestaoDispositivo { get; set; }
public int PaidSync { get; set; }
public int NotPaidGestaoVozDados { get; set; }
public int NotPaidSkeelo { get; set; }
public int NotPaidNews { get; set; }
public int NotPaidTravel { get; set; }
public int NotPaidGestaoDispositivo { get; set; }
public int NotPaidSync { get; set; }
public int TotalLinesWithAnyPaidAdditional { get; set; }
public int TotalLinesWithNoPaidAdditional { get; set; }
public decimal TotalGestaoVozDados { get; set; }
public decimal TotalSkeelo { get; set; }
public decimal TotalNews { get; set; }
public decimal TotalTravel { get; set; }
public decimal TotalGestaoDispositivo { get; set; }
public decimal TotalSync { get; set; }
}
private sealed class ClientGroupProjection
{
public string Cliente { get; set; } = string.Empty;
public int TotalLinhas { get; set; }
public int Ativos { get; set; }
public int Bloqueados { get; set; }
public int LinhasVivo { get; set; }
public int TravelCom { get; set; }
public int TravelSem { get; set; }
public int PaidGestaoVozDados { get; set; }
public int PaidSkeelo { get; set; }
public int PaidNews { get; set; }
public int PaidTravel { get; set; }
public int PaidGestaoDispositivo { get; set; }
public int PaidSync { get; set; }
public int NotPaidGestaoVozDados { get; set; }
public int NotPaidSkeelo { get; set; }
public int NotPaidNews { get; set; }
public int NotPaidTravel { get; set; }
public int NotPaidGestaoDispositivo { get; set; }
public int NotPaidSync { get; set; }
public decimal TotalGestaoVozDados { get; set; }
public decimal TotalSkeelo { get; set; }
public decimal TotalNews { get; set; }
public decimal TotalTravel { get; set; }
public decimal TotalGestaoDispositivo { get; set; }
public decimal TotalSync { get; set; }
}
}
}