513 lines
20 KiB
C#
513 lines
20 KiB
C#
using System.Text.RegularExpressions;
|
|
using System.Linq;
|
|
using line_gestao_api.Data;
|
|
using line_gestao_api.Dtos;
|
|
using line_gestao_api.Services;
|
|
using Microsoft.AspNetCore.Authorization;
|
|
using Microsoft.AspNetCore.Mvc;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace line_gestao_api.Controllers;
|
|
|
|
[ApiController]
|
|
[Route("api/resumo")]
|
|
[Authorize]
|
|
public class ResumoController : ControllerBase
|
|
{
|
|
private static readonly Regex PlanGbRegex = new(@"(\d+(?:[.,]\d+)?)\s*(GB|MB)\b", RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
|
|
|
private readonly AppDbContext _db;
|
|
private readonly SpreadsheetImportAuditService _spreadsheetImportAuditService;
|
|
|
|
public ResumoController(AppDbContext db, SpreadsheetImportAuditService spreadsheetImportAuditService)
|
|
{
|
|
_db = db;
|
|
_spreadsheetImportAuditService = spreadsheetImportAuditService;
|
|
}
|
|
|
|
[HttpGet]
|
|
public async Task<ActionResult<ResumoResponseDto>> GetResumo()
|
|
{
|
|
var spreadsheetResponse = await BuildSpreadsheetResumoAsync();
|
|
var hasLiveLines = await _db.MobileLines.AsNoTracking().AnyAsync();
|
|
|
|
if (hasLiveLines)
|
|
{
|
|
var live = await BuildLiveResumoAsync();
|
|
|
|
spreadsheetResponse.MacrophonyPlans = live.MacrophonyPlans;
|
|
spreadsheetResponse.MacrophonyTotals = live.MacrophonyTotals;
|
|
spreadsheetResponse.VivoLineResumos = live.VivoLineResumos;
|
|
spreadsheetResponse.VivoLineTotals = live.VivoLineTotals;
|
|
spreadsheetResponse.PlanoContratoResumos = live.PlanoContratoResumos;
|
|
spreadsheetResponse.PlanoContratoTotal = live.PlanoContratoTotal;
|
|
spreadsheetResponse.LineTotais = live.LineTotais;
|
|
spreadsheetResponse.GbDistribuicao = live.GbDistribuicao;
|
|
spreadsheetResponse.GbDistribuicaoTotal = live.GbDistribuicaoTotal;
|
|
spreadsheetResponse.ReservaLines = live.ReservaLines;
|
|
spreadsheetResponse.ReservaPorDdd = live.ReservaPorDdd;
|
|
spreadsheetResponse.TotalGeralLinhasReserva = live.TotalGeralLinhasReserva;
|
|
spreadsheetResponse.ReservaTotal = live.ReservaTotal;
|
|
}
|
|
|
|
spreadsheetResponse.MacrophonyTotals ??= new ResumoMacrophonyTotalDto();
|
|
spreadsheetResponse.VivoLineTotals ??= new ResumoVivoLineTotalDto();
|
|
|
|
return Ok(spreadsheetResponse);
|
|
}
|
|
|
|
private async Task<ResumoResponseDto> BuildSpreadsheetResumoAsync()
|
|
{
|
|
var reservaLines = await _db.ResumoReservaLines.AsNoTracking()
|
|
.OrderBy(x => x.Ddd)
|
|
.ToListAsync();
|
|
|
|
var reservaPorDdd = reservaLines
|
|
.Where(x => !string.IsNullOrWhiteSpace(x.Ddd))
|
|
.GroupBy(x => x.Ddd!.Trim())
|
|
.Select(g => new ResumoReservaPorDddDto
|
|
{
|
|
Ddd = g.Key,
|
|
TotalLinhas = g.Sum(x => x.QtdLinhas ?? 0),
|
|
PorFranquia = g.GroupBy(x => x.FranquiaGb)
|
|
.Select(fg => new ResumoReservaPorFranquiaDto
|
|
{
|
|
FranquiaGb = fg.Key,
|
|
TotalLinhas = fg.Sum(x => x.QtdLinhas ?? 0)
|
|
})
|
|
.OrderBy(x => x.FranquiaGb)
|
|
.ToList()
|
|
})
|
|
.OrderBy(x => x.Ddd)
|
|
.ToList();
|
|
|
|
var reservaTotalEntity = await _db.ResumoReservaTotals.AsNoTracking()
|
|
.FirstOrDefaultAsync();
|
|
var gbDistribuicaoTotalEntity = await _db.ResumoGbDistribuicaoTotais.AsNoTracking()
|
|
.FirstOrDefaultAsync();
|
|
var canonicalTotalLinhas = await _spreadsheetImportAuditService.GetCanonicalTotalLinhasForReadAsync();
|
|
|
|
var response = new ResumoResponseDto
|
|
{
|
|
MacrophonyPlans = await _db.ResumoMacrophonyPlans.AsNoTracking()
|
|
.OrderBy(x => x.PlanoContrato)
|
|
.Select(x => new ResumoMacrophonyPlanDto
|
|
{
|
|
PlanoContrato = x.PlanoContrato,
|
|
Gb = x.Gb,
|
|
ValorIndividualComSvas = x.ValorIndividualComSvas,
|
|
FranquiaGb = x.FranquiaGb,
|
|
TotalLinhas = x.TotalLinhas,
|
|
ValorTotal = x.ValorTotal,
|
|
VivoTravel = x.VivoTravel
|
|
})
|
|
.ToListAsync(),
|
|
MacrophonyTotals = await _db.ResumoMacrophonyTotals.AsNoTracking()
|
|
.Select(x => new ResumoMacrophonyTotalDto
|
|
{
|
|
FranquiaGbTotal = x.FranquiaGbTotal,
|
|
TotalLinhasTotal = canonicalTotalLinhas,
|
|
ValorTotal = x.ValorTotal
|
|
})
|
|
.FirstOrDefaultAsync(),
|
|
VivoLineResumos = await _db.ResumoVivoLineResumos.AsNoTracking()
|
|
.OrderBy(x => x.Cliente)
|
|
.Select(x => new ResumoVivoLineResumoDto
|
|
{
|
|
Skil = x.Skil,
|
|
Cliente = x.Cliente,
|
|
QtdLinhas = x.QtdLinhas,
|
|
FranquiaTotal = x.FranquiaTotal,
|
|
ValorContratoVivo = x.ValorContratoVivo,
|
|
FranquiaLine = x.FranquiaLine,
|
|
ValorContratoLine = x.ValorContratoLine,
|
|
Lucro = x.Lucro
|
|
})
|
|
.ToListAsync(),
|
|
VivoLineTotals = await _db.ResumoVivoLineTotals.AsNoTracking()
|
|
.Select(x => new ResumoVivoLineTotalDto
|
|
{
|
|
QtdLinhasTotal = canonicalTotalLinhas,
|
|
FranquiaTotal = x.FranquiaTotal,
|
|
ValorContratoVivo = x.ValorContratoVivo,
|
|
FranquiaLine = x.FranquiaLine,
|
|
ValorContratoLine = x.ValorContratoLine,
|
|
Lucro = x.Lucro
|
|
})
|
|
.FirstOrDefaultAsync(),
|
|
ClienteEspeciais = await _db.ResumoClienteEspeciais.AsNoTracking()
|
|
.OrderBy(x => x.Nome)
|
|
.Select(x => new ResumoClienteEspecialDto
|
|
{
|
|
Nome = x.Nome,
|
|
Valor = x.Valor
|
|
})
|
|
.ToListAsync(),
|
|
PlanoContratoResumos = await _db.ResumoPlanoContratoResumos.AsNoTracking()
|
|
.OrderBy(x => x.PlanoContrato)
|
|
.Select(x => new ResumoPlanoContratoResumoDto
|
|
{
|
|
PlanoContrato = x.PlanoContrato,
|
|
Gb = x.Gb,
|
|
ValorIndividualComSvas = x.ValorIndividualComSvas,
|
|
FranquiaGb = x.FranquiaGb,
|
|
TotalLinhas = x.TotalLinhas,
|
|
ValorTotal = x.ValorTotal
|
|
})
|
|
.ToListAsync(),
|
|
PlanoContratoTotal = await _db.ResumoPlanoContratoTotals.AsNoTracking()
|
|
.Select(x => new ResumoPlanoContratoTotalDto
|
|
{
|
|
ValorTotal = x.ValorTotal
|
|
})
|
|
.FirstOrDefaultAsync(),
|
|
LineTotais = await _db.ResumoLineTotais.AsNoTracking()
|
|
.OrderBy(x => x.Tipo)
|
|
.Select(x => new ResumoLineTotaisDto
|
|
{
|
|
Tipo = x.Tipo,
|
|
ValorTotalLine = x.ValorTotalLine,
|
|
LucroTotalLine = x.LucroTotalLine,
|
|
QtdLinhas = x.QtdLinhas
|
|
})
|
|
.ToListAsync(),
|
|
GbDistribuicao = await _db.ResumoGbDistribuicoes.AsNoTracking()
|
|
.OrderBy(x => x.Gb)
|
|
.Select(x => new ResumoGbDistribuicaoDto
|
|
{
|
|
Gb = x.Gb,
|
|
Qtd = x.Qtd,
|
|
Soma = x.Soma
|
|
})
|
|
.ToListAsync(),
|
|
GbDistribuicaoTotal = gbDistribuicaoTotalEntity == null ? null : new ResumoGbDistribuicaoTotalDto
|
|
{
|
|
TotalLinhas = gbDistribuicaoTotalEntity.TotalLinhas,
|
|
SomaTotal = gbDistribuicaoTotalEntity.SomaTotal
|
|
},
|
|
ReservaLines = reservaLines
|
|
.Select(x => new ResumoReservaLineDto
|
|
{
|
|
Ddd = x.Ddd,
|
|
FranquiaGb = x.FranquiaGb,
|
|
QtdLinhas = x.QtdLinhas,
|
|
Total = x.Total
|
|
})
|
|
.ToList(),
|
|
ReservaPorDdd = reservaPorDdd,
|
|
TotalGeralLinhasReserva = reservaTotalEntity?.TotalGeralLinhasReserva
|
|
?? reservaTotalEntity?.QtdLinhasTotal
|
|
?? reservaPorDdd.Sum(x => x.TotalLinhas),
|
|
ReservaTotal = reservaTotalEntity == null ? null : new ResumoReservaTotalDto
|
|
{
|
|
QtdLinhasTotal = reservaTotalEntity.QtdLinhasTotal,
|
|
Total = reservaTotalEntity.Total
|
|
}
|
|
};
|
|
|
|
response.MacrophonyTotals ??= new ResumoMacrophonyTotalDto();
|
|
response.MacrophonyTotals.TotalLinhasTotal = canonicalTotalLinhas;
|
|
|
|
response.VivoLineTotals ??= new ResumoVivoLineTotalDto();
|
|
response.VivoLineTotals.QtdLinhasTotal = canonicalTotalLinhas;
|
|
|
|
return response;
|
|
}
|
|
|
|
private async Task<ResumoResponseDto> BuildLiveResumoAsync()
|
|
{
|
|
var allLines = _db.MobileLines.AsNoTracking();
|
|
var nonReservaLines = allLines.Where(x =>
|
|
!EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") &&
|
|
!EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") &&
|
|
!EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA"));
|
|
var reservaLinesQuery = allLines.Where(x =>
|
|
EF.Functions.ILike((x.Usuario ?? "").Trim(), "RESERVA") ||
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "RESERVA") ||
|
|
EF.Functions.ILike((x.Cliente ?? "").Trim(), "RESERVA"));
|
|
|
|
var planosAgg = await nonReservaLines
|
|
.GroupBy(x => (x.PlanoContrato ?? "").Trim())
|
|
.Select(g => new
|
|
{
|
|
Plano = g.Key,
|
|
TotalLinhas = g.Count(),
|
|
FranquiaMedia = g.Average(x => (decimal?)x.FranquiaVivo),
|
|
ValorTotal = g.Sum(x => x.ValorContratoVivo ?? 0m),
|
|
TravelCount = g.Count(x => (x.VivoTravelMundo ?? 0m) > 0m)
|
|
})
|
|
.ToListAsync();
|
|
|
|
var planoRows = planosAgg
|
|
.Select(row =>
|
|
{
|
|
var planoLabel = string.IsNullOrWhiteSpace(row.Plano) ? "SEM PLANO" : row.Plano;
|
|
var franquiaGb = (row.FranquiaMedia ?? 0m) > 0m
|
|
? row.FranquiaMedia
|
|
: ExtractGbFromPlanName(planoLabel);
|
|
var valorIndividual = row.TotalLinhas > 0 ? row.ValorTotal / row.TotalLinhas : (decimal?)null;
|
|
|
|
return new ResumoPlanoContratoResumoDto
|
|
{
|
|
PlanoContrato = planoLabel,
|
|
Gb = franquiaGb,
|
|
FranquiaGb = franquiaGb,
|
|
TotalLinhas = row.TotalLinhas,
|
|
ValorTotal = row.ValorTotal,
|
|
ValorIndividualComSvas = valorIndividual
|
|
};
|
|
})
|
|
.OrderByDescending(x => x.TotalLinhas ?? 0)
|
|
.ThenBy(x => x.PlanoContrato)
|
|
.ToList();
|
|
|
|
var macrophonyRows = planosAgg
|
|
.Select(row =>
|
|
{
|
|
var planoLabel = string.IsNullOrWhiteSpace(row.Plano) ? "SEM PLANO" : row.Plano;
|
|
var franquiaGb = (row.FranquiaMedia ?? 0m) > 0m
|
|
? row.FranquiaMedia
|
|
: ExtractGbFromPlanName(planoLabel);
|
|
var valorIndividual = row.TotalLinhas > 0 ? row.ValorTotal / row.TotalLinhas : (decimal?)null;
|
|
|
|
return new ResumoMacrophonyPlanDto
|
|
{
|
|
PlanoContrato = planoLabel,
|
|
Gb = franquiaGb,
|
|
FranquiaGb = franquiaGb,
|
|
TotalLinhas = row.TotalLinhas,
|
|
ValorTotal = row.ValorTotal,
|
|
ValorIndividualComSvas = valorIndividual,
|
|
VivoTravel = row.TravelCount > 0
|
|
};
|
|
})
|
|
.OrderByDescending(x => x.TotalLinhas ?? 0)
|
|
.ThenBy(x => x.PlanoContrato)
|
|
.ToList();
|
|
|
|
var clientesAgg = await nonReservaLines
|
|
.GroupBy(x => (x.Cliente ?? "").Trim())
|
|
.Select(g => new
|
|
{
|
|
Cliente = g.Key,
|
|
QtdLinhas = g.Count(),
|
|
FranquiaTotal = g.Sum(x => x.FranquiaVivo ?? 0m),
|
|
ValorContratoVivo = g.Sum(x => x.ValorContratoVivo ?? 0m),
|
|
FranquiaLine = g.Sum(x => x.FranquiaLine ?? 0m),
|
|
ValorContratoLine = g.Sum(x => x.ValorContratoLine ?? 0m),
|
|
Lucro = g.Sum(x => x.Lucro ?? 0m)
|
|
})
|
|
.ToListAsync();
|
|
|
|
var clientesRows = clientesAgg
|
|
.Select(row => new ResumoVivoLineResumoDto
|
|
{
|
|
Cliente = string.IsNullOrWhiteSpace(row.Cliente) ? "SEM CLIENTE" : row.Cliente,
|
|
QtdLinhas = row.QtdLinhas,
|
|
FranquiaTotal = row.FranquiaTotal,
|
|
ValorContratoVivo = row.ValorContratoVivo,
|
|
FranquiaLine = row.FranquiaLine,
|
|
ValorContratoLine = row.ValorContratoLine,
|
|
Lucro = row.Lucro
|
|
})
|
|
.OrderByDescending(x => x.QtdLinhas ?? 0)
|
|
.ThenBy(x => x.Cliente)
|
|
.ToList();
|
|
|
|
var totalLinhasNaoReserva = clientesRows.Sum(x => x.QtdLinhas ?? 0);
|
|
var totalFranquiaNaoReserva = clientesRows.Sum(x => x.FranquiaTotal ?? 0m);
|
|
var totalValorContratoVivo = clientesRows.Sum(x => x.ValorContratoVivo ?? 0m);
|
|
var totalFranquiaLine = clientesRows.Sum(x => x.FranquiaLine ?? 0m);
|
|
var totalValorContratoLine = clientesRows.Sum(x => x.ValorContratoLine ?? 0m);
|
|
var totalLucro = clientesRows.Sum(x => x.Lucro ?? 0m);
|
|
|
|
var pfAtivas = await nonReservaLines
|
|
.Where(x => EF.Functions.ILike((x.Status ?? "").Trim(), "%ativo%"))
|
|
.Where(x =>
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%fís%") ||
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%fis%") ||
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%pf%"))
|
|
.GroupBy(_ => 1)
|
|
.Select(g => new
|
|
{
|
|
QtdLinhas = g.Count(),
|
|
ValorTotal = g.Sum(x => x.ValorContratoLine ?? 0m),
|
|
LucroTotal = g.Sum(x => x.Lucro ?? 0m)
|
|
})
|
|
.FirstOrDefaultAsync();
|
|
|
|
var pjAtivas = await nonReservaLines
|
|
.Where(x => EF.Functions.ILike((x.Status ?? "").Trim(), "%ativo%"))
|
|
.Where(x =>
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%jur%") ||
|
|
EF.Functions.ILike((x.Skil ?? "").Trim(), "%pj%"))
|
|
.GroupBy(_ => 1)
|
|
.Select(g => new
|
|
{
|
|
QtdLinhas = g.Count(),
|
|
ValorTotal = g.Sum(x => x.ValorContratoLine ?? 0m),
|
|
LucroTotal = g.Sum(x => x.Lucro ?? 0m)
|
|
})
|
|
.FirstOrDefaultAsync();
|
|
|
|
var totaisLine = new List<ResumoLineTotaisDto>
|
|
{
|
|
new()
|
|
{
|
|
Tipo = "PF",
|
|
QtdLinhas = pfAtivas?.QtdLinhas ?? 0,
|
|
ValorTotalLine = pfAtivas?.ValorTotal ?? 0m,
|
|
LucroTotalLine = pfAtivas?.LucroTotal ?? 0m
|
|
},
|
|
new()
|
|
{
|
|
Tipo = "PJ",
|
|
QtdLinhas = pjAtivas?.QtdLinhas ?? 0,
|
|
ValorTotalLine = pjAtivas?.ValorTotal ?? 0m,
|
|
LucroTotalLine = pjAtivas?.LucroTotal ?? 0m
|
|
},
|
|
new()
|
|
{
|
|
Tipo = "TOTAL",
|
|
QtdLinhas = totalLinhasNaoReserva,
|
|
ValorTotalLine = totalValorContratoLine,
|
|
LucroTotalLine = totalLucro
|
|
}
|
|
};
|
|
|
|
var gbDistribuicao = await nonReservaLines
|
|
.Where(x => (x.FranquiaVivo ?? 0m) > 0m)
|
|
.GroupBy(x => x.FranquiaVivo ?? 0m)
|
|
.Select(g => new ResumoGbDistribuicaoDto
|
|
{
|
|
Gb = g.Key,
|
|
Qtd = g.Count(),
|
|
Soma = g.Sum(x => x.FranquiaVivo ?? 0m)
|
|
})
|
|
.OrderBy(x => x.Gb)
|
|
.ToListAsync();
|
|
|
|
var gbDistribuicaoTotal = new ResumoGbDistribuicaoTotalDto
|
|
{
|
|
TotalLinhas = gbDistribuicao.Sum(x => x.Qtd ?? 0),
|
|
SomaTotal = gbDistribuicao.Sum(x => x.Soma ?? 0m)
|
|
};
|
|
|
|
var reservaSnapshot = await reservaLinesQuery
|
|
.Select(x => new
|
|
{
|
|
x.Linha,
|
|
x.FranquiaVivo,
|
|
x.ValorContratoVivo
|
|
})
|
|
.ToListAsync();
|
|
|
|
var reservaGroupRaw = reservaSnapshot
|
|
.Select(row => new
|
|
{
|
|
Ddd = ExtractDddFromLine(row.Linha) ?? "-",
|
|
FranquiaGb = row.FranquiaVivo
|
|
})
|
|
.GroupBy(x => new { x.Ddd, x.FranquiaGb })
|
|
.Select(g => new ResumoReservaLineDto
|
|
{
|
|
Ddd = g.Key.Ddd,
|
|
FranquiaGb = g.Key.FranquiaGb,
|
|
QtdLinhas = g.Count(),
|
|
Total = null
|
|
})
|
|
.OrderBy(x => x.Ddd)
|
|
.ThenBy(x => x.FranquiaGb ?? 0m)
|
|
.ToList();
|
|
|
|
var reservaPorDdd = reservaGroupRaw
|
|
.GroupBy(x => x.Ddd ?? "-")
|
|
.Select(g => new ResumoReservaPorDddDto
|
|
{
|
|
Ddd = g.Key,
|
|
TotalLinhas = g.Sum(x => x.QtdLinhas ?? 0),
|
|
PorFranquia = g
|
|
.GroupBy(x => x.FranquiaGb)
|
|
.Select(fg => new ResumoReservaPorFranquiaDto
|
|
{
|
|
FranquiaGb = fg.Key,
|
|
TotalLinhas = fg.Sum(x => x.QtdLinhas ?? 0)
|
|
})
|
|
.OrderBy(x => x.FranquiaGb ?? 0m)
|
|
.ToList()
|
|
})
|
|
.OrderBy(x => x.Ddd)
|
|
.ToList();
|
|
|
|
var reservaTotalLinhas = reservaSnapshot.Count;
|
|
var reservaTotalValor = reservaSnapshot.Sum(x => x.ValorContratoVivo ?? 0m);
|
|
|
|
return new ResumoResponseDto
|
|
{
|
|
MacrophonyPlans = macrophonyRows,
|
|
MacrophonyTotals = new ResumoMacrophonyTotalDto
|
|
{
|
|
TotalLinhasTotal = totalLinhasNaoReserva,
|
|
FranquiaGbTotal = totalFranquiaNaoReserva,
|
|
ValorTotal = totalValorContratoVivo
|
|
},
|
|
VivoLineResumos = clientesRows,
|
|
VivoLineTotals = new ResumoVivoLineTotalDto
|
|
{
|
|
QtdLinhasTotal = totalLinhasNaoReserva,
|
|
FranquiaTotal = totalFranquiaNaoReserva,
|
|
ValorContratoVivo = totalValorContratoVivo,
|
|
FranquiaLine = totalFranquiaLine,
|
|
ValorContratoLine = totalValorContratoLine,
|
|
Lucro = totalLucro
|
|
},
|
|
PlanoContratoResumos = planoRows,
|
|
PlanoContratoTotal = new ResumoPlanoContratoTotalDto
|
|
{
|
|
ValorTotal = planoRows.Sum(x => x.ValorTotal ?? 0m)
|
|
},
|
|
LineTotais = totaisLine,
|
|
GbDistribuicao = gbDistribuicao,
|
|
GbDistribuicaoTotal = gbDistribuicaoTotal,
|
|
ReservaLines = reservaGroupRaw,
|
|
ReservaPorDdd = reservaPorDdd,
|
|
TotalGeralLinhasReserva = reservaTotalLinhas,
|
|
ReservaTotal = new ResumoReservaTotalDto
|
|
{
|
|
QtdLinhasTotal = reservaTotalLinhas,
|
|
Total = reservaTotalValor
|
|
}
|
|
};
|
|
}
|
|
|
|
private static decimal? ExtractGbFromPlanName(string? planoContrato)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(planoContrato))
|
|
return null;
|
|
|
|
var match = PlanGbRegex.Match(planoContrato);
|
|
if (!match.Success)
|
|
return null;
|
|
|
|
var normalized = match.Groups[1].Value.Replace(',', '.');
|
|
if (!decimal.TryParse(normalized, out var rawValue))
|
|
return null;
|
|
|
|
var unit = match.Groups[2].Value.ToUpperInvariant();
|
|
return unit == "MB" ? rawValue / 1000m : rawValue;
|
|
}
|
|
|
|
private static string? ExtractDddFromLine(string? linha)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(linha))
|
|
return null;
|
|
|
|
var digits = new string(linha.Where(char.IsDigit).ToArray());
|
|
if (digits.Length >= 12 && digits.StartsWith("55"))
|
|
return digits.Substring(2, 2);
|
|
if (digits.Length >= 10)
|
|
return digits.Substring(0, 2);
|
|
return null;
|
|
}
|
|
}
|