744 lines
27 KiB
C#
744 lines
27 KiB
C#
using line_gestao_api.Data;
|
|
using line_gestao_api.Dtos;
|
|
using line_gestao_api.Models;
|
|
using Microsoft.EntityFrameworkCore;
|
|
|
|
namespace line_gestao_api.Services;
|
|
|
|
public sealed class MveReconciliationService
|
|
{
|
|
private readonly AppDbContext _db;
|
|
|
|
public MveReconciliationService(AppDbContext db)
|
|
{
|
|
_db = db;
|
|
}
|
|
|
|
internal async Task<MveReconciliationResult> BuildAsync(
|
|
MveParsedFileResult parsedFile,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(parsedFile);
|
|
|
|
var mobileLines = await _db.MobileLines
|
|
.AsNoTracking()
|
|
.Include(x => x.Aparelho)
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var vigencias = await _db.VigenciaLines
|
|
.AsNoTracking()
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var userDatas = await _db.UserDatas
|
|
.AsNoTracking()
|
|
.ToListAsync(cancellationToken);
|
|
|
|
var systemAggregates = BuildSystemAggregates(mobileLines, vigencias, userDatas);
|
|
var systemByNumber = systemAggregates
|
|
.Where(x => !string.IsNullOrWhiteSpace(x.NumeroNormalizado))
|
|
.GroupBy(x => x.NumeroNormalizado, StringComparer.Ordinal)
|
|
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.Ordinal);
|
|
|
|
var reportByNumber = parsedFile.Lines
|
|
.GroupBy(x => x.NumeroNormalizado, StringComparer.Ordinal)
|
|
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.Ordinal);
|
|
|
|
var result = new MveReconciliationResult
|
|
{
|
|
TotalSystemLines = mobileLines.Count,
|
|
TotalReportLines = parsedFile.Lines.Count,
|
|
TotalInvalidRows = parsedFile.Issues.Count(x => x.IssueType == "INVALID_ROW"),
|
|
TotalUnknownStatuses = parsedFile.Lines.Count(x => !x.StatusLinhaRecognized)
|
|
};
|
|
|
|
foreach (var parserIssue in parsedFile.Issues)
|
|
{
|
|
result.Issues.Add(new MveReconciliationIssueResult
|
|
{
|
|
SourceRowNumber = parserIssue.SourceRowNumber,
|
|
NumeroLinha = parserIssue.NumeroLinha,
|
|
IssueType = parserIssue.IssueType,
|
|
Situation = "linha inválida no relatório",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Corrigir o arquivo MVE e refazer a auditoria",
|
|
Notes = parserIssue.Message
|
|
});
|
|
}
|
|
|
|
var duplicateReportKeys = reportByNumber
|
|
.Where(entry => entry.Value.Count > 1)
|
|
.Select(entry => entry.Key)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
|
|
foreach (var duplicateKey in duplicateReportKeys)
|
|
{
|
|
var duplicates = reportByNumber[duplicateKey];
|
|
var first = duplicates[0];
|
|
result.TotalDuplicateReportLines++;
|
|
result.Issues.Add(new MveReconciliationIssueResult
|
|
{
|
|
SourceRowNumber = first.SourceRowNumber,
|
|
NumeroLinha = duplicateKey,
|
|
IssueType = "DUPLICATE_REPORT",
|
|
Situation = "duplicidade no relatório",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Corrigir a duplicidade no relatório MVE",
|
|
Notes = $"A linha {duplicateKey} apareceu {duplicates.Count} vezes no arquivo MVE.",
|
|
ReportStatus = first.StatusLinha,
|
|
ReportPlan = first.PlanoLinha,
|
|
ReportSnapshot = BuildReportSnapshot(first)
|
|
});
|
|
}
|
|
|
|
var duplicateSystemKeys = systemByNumber
|
|
.Where(entry => entry.Value.Count > 1)
|
|
.Select(entry => entry.Key)
|
|
.ToHashSet(StringComparer.Ordinal);
|
|
|
|
foreach (var duplicateKey in duplicateSystemKeys)
|
|
{
|
|
var duplicates = systemByNumber[duplicateKey];
|
|
var first = duplicates[0];
|
|
result.TotalDuplicateSystemLines++;
|
|
result.Issues.Add(new MveReconciliationIssueResult
|
|
{
|
|
NumeroLinha = duplicateKey,
|
|
MobileLineId = first.MobileLine.Id,
|
|
SystemItem = first.MobileLine.Item,
|
|
IssueType = "DUPLICATE_SYSTEM",
|
|
Situation = "duplicidade no sistema",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Corrigir a duplicidade interna antes de sincronizar",
|
|
Notes = $"A linha {duplicateKey} possui {duplicates.Count} registros no sistema.",
|
|
SystemStatus = first.MobileLine.Status,
|
|
SystemPlan = first.MobileLine.PlanoContrato,
|
|
SystemSnapshot = BuildSystemSnapshot(first)
|
|
});
|
|
}
|
|
|
|
var blockedSystemKeys = new HashSet<string>(duplicateSystemKeys, StringComparer.Ordinal);
|
|
var blockedReportKeys = new HashSet<string>(duplicateReportKeys, StringComparer.Ordinal);
|
|
var matchedSystemLineIds = new HashSet<Guid>();
|
|
var matchedReportRows = new HashSet<int>();
|
|
|
|
var allKeys = reportByNumber.Keys
|
|
.Concat(systemByNumber.Keys)
|
|
.Where(key => !string.IsNullOrWhiteSpace(key))
|
|
.Distinct(StringComparer.Ordinal)
|
|
.OrderBy(key => key, StringComparer.Ordinal)
|
|
.ToList();
|
|
|
|
foreach (var key in allKeys)
|
|
{
|
|
if (blockedSystemKeys.Contains(key) || blockedReportKeys.Contains(key))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var hasReport = reportByNumber.TryGetValue(key, out var reportLines);
|
|
var hasSystem = systemByNumber.TryGetValue(key, out var systemLines);
|
|
var reportLine = hasReport ? reportLines![0] : null;
|
|
var systemLine = hasSystem ? systemLines![0] : null;
|
|
|
|
if (reportLine == null || systemLine == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
matchedSystemLineIds.Add(systemLine.MobileLine.Id);
|
|
matchedReportRows.Add(reportLine.SourceRowNumber);
|
|
|
|
var comparison = CompareMatchedLine(systemLine, reportLine);
|
|
RegisterComparisonResult(result, comparison);
|
|
}
|
|
|
|
var unmatchedSystemLines = systemAggregates
|
|
.Where(x => !blockedSystemKeys.Contains(x.NumeroNormalizado))
|
|
.Where(x => !matchedSystemLineIds.Contains(x.MobileLine.Id))
|
|
.ToList();
|
|
|
|
var unmatchedReportLines = parsedFile.Lines
|
|
.Where(x => !blockedReportKeys.Contains(x.NumeroNormalizado))
|
|
.Where(x => !matchedReportRows.Contains(x.SourceRowNumber))
|
|
.ToList();
|
|
|
|
var chipMatchedSystemLineIds = new HashSet<Guid>();
|
|
var chipMatchedReportRows = new HashSet<int>();
|
|
|
|
var systemByChip = unmatchedSystemLines
|
|
.Where(x => !string.IsNullOrWhiteSpace(MveAuditNormalization.NullIfEmptyDigits(x.MobileLine.Chip)))
|
|
.GroupBy(x => MveAuditNormalization.OnlyDigits(x.MobileLine.Chip), StringComparer.Ordinal)
|
|
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.Ordinal);
|
|
|
|
var reportByChip = unmatchedReportLines
|
|
.Where(x => !string.IsNullOrWhiteSpace(MveAuditNormalization.NullIfEmptyDigits(x.Chip)))
|
|
.GroupBy(x => MveAuditNormalization.OnlyDigits(x.Chip), StringComparer.Ordinal)
|
|
.ToDictionary(g => g.Key, g => g.ToList(), StringComparer.Ordinal);
|
|
|
|
var chipKeys = systemByChip.Keys
|
|
.Intersect(reportByChip.Keys, StringComparer.Ordinal)
|
|
.OrderBy(key => key, StringComparer.Ordinal)
|
|
.ToList();
|
|
|
|
foreach (var chipKey in chipKeys)
|
|
{
|
|
var systemCandidates = systemByChip[chipKey];
|
|
var reportCandidates = reportByChip[chipKey];
|
|
if (systemCandidates.Count != 1 || reportCandidates.Count != 1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var systemLine = systemCandidates[0];
|
|
var reportLine = reportCandidates[0];
|
|
|
|
chipMatchedSystemLineIds.Add(systemLine.MobileLine.Id);
|
|
chipMatchedReportRows.Add(reportLine.SourceRowNumber);
|
|
|
|
var comparison = CompareMatchedByChip(systemLine, reportLine);
|
|
RegisterComparisonResult(result, comparison);
|
|
}
|
|
|
|
foreach (var systemLine in unmatchedSystemLines.Where(x => !chipMatchedSystemLineIds.Contains(x.MobileLine.Id)))
|
|
{
|
|
result.TotalOnlyInSystem++;
|
|
result.Issues.Add(new MveReconciliationIssueResult
|
|
{
|
|
NumeroLinha = systemLine.NumeroNormalizado,
|
|
MobileLineId = systemLine.MobileLine.Id,
|
|
SystemItem = systemLine.MobileLine.Item,
|
|
IssueType = "ONLY_IN_SYSTEM",
|
|
Situation = "ausente no relatório",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Validar com a Vivo antes de alterar o cadastro",
|
|
Notes = "A linha existe no sistema, mas não foi encontrada no relatório MVE.",
|
|
SystemStatus = systemLine.MobileLine.Status,
|
|
SystemPlan = systemLine.MobileLine.PlanoContrato,
|
|
SystemSnapshot = BuildSystemSnapshot(systemLine)
|
|
});
|
|
}
|
|
|
|
foreach (var reportLine in unmatchedReportLines.Where(x => !chipMatchedReportRows.Contains(x.SourceRowNumber)))
|
|
{
|
|
result.TotalOnlyInReport++;
|
|
result.Issues.Add(new MveReconciliationIssueResult
|
|
{
|
|
SourceRowNumber = reportLine.SourceRowNumber,
|
|
NumeroLinha = reportLine.NumeroNormalizado,
|
|
IssueType = "ONLY_IN_REPORT",
|
|
Situation = "ausente no sistema",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Avaliar cadastro manual dessa linha",
|
|
Notes = "A linha existe no relatório MVE, mas não foi encontrada na página Geral.",
|
|
ReportStatus = reportLine.StatusLinha,
|
|
ReportPlan = reportLine.PlanoLinha,
|
|
ReportSnapshot = BuildReportSnapshot(reportLine)
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static List<MveSystemLineAggregate> BuildSystemAggregates(
|
|
IReadOnlyCollection<MobileLine> mobileLines,
|
|
IReadOnlyCollection<VigenciaLine> vigencias,
|
|
IReadOnlyCollection<UserData> userDatas)
|
|
{
|
|
var vigenciaByLine = vigencias
|
|
.Where(x => !string.IsNullOrWhiteSpace(x.Linha))
|
|
.GroupBy(x => MveAuditNormalization.OnlyDigits(x.Linha), StringComparer.Ordinal)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.OrderByDescending(x => x.UpdatedAt).ThenByDescending(x => x.CreatedAt).First(),
|
|
StringComparer.Ordinal);
|
|
|
|
var vigenciaByItem = vigencias
|
|
.Where(x => x.Item > 0)
|
|
.GroupBy(x => x.Item)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.OrderByDescending(x => x.UpdatedAt).ThenByDescending(x => x.CreatedAt).First());
|
|
|
|
var userDataByLine = userDatas
|
|
.Where(x => !string.IsNullOrWhiteSpace(x.Linha))
|
|
.GroupBy(x => MveAuditNormalization.OnlyDigits(x.Linha), StringComparer.Ordinal)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.OrderByDescending(x => x.UpdatedAt).ThenByDescending(x => x.CreatedAt).First(),
|
|
StringComparer.Ordinal);
|
|
|
|
var userDataByItem = userDatas
|
|
.Where(x => x.Item > 0)
|
|
.GroupBy(x => x.Item)
|
|
.ToDictionary(
|
|
g => g.Key,
|
|
g => g.OrderByDescending(x => x.UpdatedAt).ThenByDescending(x => x.CreatedAt).First());
|
|
|
|
return mobileLines
|
|
.Select(line =>
|
|
{
|
|
var numeroNormalizado = MveAuditNormalization.OnlyDigits(line.Linha);
|
|
vigenciaByLine.TryGetValue(numeroNormalizado, out var vigencia);
|
|
if (vigencia == null && line.Item > 0)
|
|
{
|
|
vigenciaByItem.TryGetValue(line.Item, out vigencia);
|
|
}
|
|
|
|
userDataByLine.TryGetValue(numeroNormalizado, out var userData);
|
|
if (userData == null && line.Item > 0)
|
|
{
|
|
userDataByItem.TryGetValue(line.Item, out userData);
|
|
}
|
|
|
|
return new MveSystemLineAggregate(line, vigencia, userData, numeroNormalizado);
|
|
})
|
|
.ToList();
|
|
}
|
|
|
|
private static MveReconciliationIssueResult? CompareMatchedLine(
|
|
MveSystemLineAggregate systemLine,
|
|
MveParsedLine reportLine)
|
|
{
|
|
var systemSnapshot = BuildSystemSnapshot(systemLine);
|
|
var reportSnapshot = BuildReportSnapshot(reportLine);
|
|
var differences = new List<MveAuditDifferenceDto>();
|
|
|
|
var systemStatus = MveAuditNormalization.NormalizeSystemStatus(systemSnapshot.StatusLinha);
|
|
if (!string.Equals(systemStatus.Key, reportLine.StatusLinhaKey, StringComparison.Ordinal))
|
|
{
|
|
differences.Add(new MveAuditDifferenceDto
|
|
{
|
|
FieldKey = "status",
|
|
Label = "Status da linha",
|
|
SystemValue = NullIfEmpty(systemSnapshot.StatusLinha),
|
|
ReportValue = NullIfEmpty(reportSnapshot.StatusLinha),
|
|
Syncable = true
|
|
});
|
|
}
|
|
|
|
AddDifference(
|
|
differences,
|
|
"chip",
|
|
"Chip da linha",
|
|
systemSnapshot.Chip,
|
|
reportSnapshot.Chip,
|
|
syncable: true,
|
|
comparer: MveAuditNormalization.OnlyDigits);
|
|
|
|
var hasUnknownStatus = !reportLine.StatusLinhaRecognized;
|
|
if (differences.Count == 0 && !hasUnknownStatus)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return BuildIssue(systemLine, reportLine, systemSnapshot, reportSnapshot, differences, hasUnknownStatus);
|
|
}
|
|
|
|
private static MveReconciliationIssueResult? CompareMatchedByChip(
|
|
MveSystemLineAggregate systemLine,
|
|
MveParsedLine reportLine)
|
|
{
|
|
var systemSnapshot = BuildSystemSnapshot(systemLine);
|
|
var reportSnapshot = BuildReportSnapshot(reportLine);
|
|
var systemLocalNumber = MveAuditNormalization.ExtractPhoneLocalNumber(systemSnapshot.NumeroLinha);
|
|
var reportLocalNumber = MveAuditNormalization.ExtractPhoneLocalNumber(reportSnapshot.NumeroLinha);
|
|
|
|
if (!string.IsNullOrWhiteSpace(systemLocalNumber) &&
|
|
string.Equals(systemLocalNumber, reportLocalNumber, StringComparison.Ordinal))
|
|
{
|
|
return BuildDddReviewIssue(systemLine, reportLine, systemSnapshot, reportSnapshot);
|
|
}
|
|
|
|
var differences = new List<MveAuditDifferenceDto>();
|
|
AddDifference(
|
|
differences,
|
|
"line",
|
|
"Número da linha",
|
|
systemSnapshot.NumeroLinha,
|
|
reportSnapshot.NumeroLinha,
|
|
syncable: true,
|
|
comparer: MveAuditNormalization.OnlyDigits);
|
|
|
|
var systemStatus = MveAuditNormalization.NormalizeSystemStatus(systemSnapshot.StatusLinha);
|
|
if (!string.Equals(systemStatus.Key, reportLine.StatusLinhaKey, StringComparison.Ordinal))
|
|
{
|
|
differences.Add(new MveAuditDifferenceDto
|
|
{
|
|
FieldKey = "status",
|
|
Label = "Status da linha",
|
|
SystemValue = NullIfEmpty(systemSnapshot.StatusLinha),
|
|
ReportValue = NullIfEmpty(reportSnapshot.StatusLinha),
|
|
Syncable = true
|
|
});
|
|
}
|
|
|
|
var hasUnknownStatus = !reportLine.StatusLinhaRecognized;
|
|
if (differences.Count == 0 && !hasUnknownStatus)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return BuildIssue(systemLine, reportLine, systemSnapshot, reportSnapshot, differences, hasUnknownStatus);
|
|
}
|
|
|
|
private static void RegisterComparisonResult(MveReconciliationResult result, MveReconciliationIssueResult? comparison)
|
|
{
|
|
if (comparison == null)
|
|
{
|
|
result.TotalConciliated++;
|
|
return;
|
|
}
|
|
|
|
result.Issues.Add(comparison);
|
|
if (comparison.Differences.Any(x => x.FieldKey == "status"))
|
|
{
|
|
result.TotalStatusDivergences++;
|
|
}
|
|
|
|
if (comparison.Differences.Any(x => x.FieldKey != "status" && x.Syncable))
|
|
{
|
|
result.TotalDataDivergences++;
|
|
}
|
|
|
|
if (comparison.Syncable)
|
|
{
|
|
result.TotalSyncableIssues++;
|
|
}
|
|
}
|
|
|
|
private static MveReconciliationIssueResult BuildIssue(
|
|
MveSystemLineAggregate systemLine,
|
|
MveParsedLine reportLine,
|
|
MveAuditSnapshotDto systemSnapshot,
|
|
MveAuditSnapshotDto reportSnapshot,
|
|
List<MveAuditDifferenceDto> differences,
|
|
bool hasUnknownStatus)
|
|
{
|
|
var notes = new List<string>();
|
|
if (hasUnknownStatus)
|
|
{
|
|
notes.Add("O STATUS_LINHA do relatório MVE não foi reconhecido pelo mapa de normalização.");
|
|
}
|
|
|
|
var hasStatusDifference = differences.Any(x => x.FieldKey == "status");
|
|
var hasLineDifference = differences.Any(x => x.FieldKey == "line");
|
|
var hasChipDifference = differences.Any(x => x.FieldKey == "chip");
|
|
var hasDataDifference = differences.Any(x => x.FieldKey != "status" && x.Syncable);
|
|
|
|
return new MveReconciliationIssueResult
|
|
{
|
|
SourceRowNumber = reportLine.SourceRowNumber,
|
|
NumeroLinha = reportLine.NumeroNormalizado,
|
|
MobileLineId = systemLine.MobileLine.Id,
|
|
SystemItem = systemLine.MobileLine.Item,
|
|
IssueType = ResolveIssueType(hasStatusDifference, hasLineDifference, hasChipDifference, hasUnknownStatus),
|
|
Situation = ResolveSituation(hasStatusDifference, hasLineDifference, hasChipDifference, hasUnknownStatus),
|
|
Severity = ResolveSeverity(hasStatusDifference, hasDataDifference, hasUnknownStatus),
|
|
Syncable = differences.Any(x => x.Syncable),
|
|
ActionSuggestion = ResolveActionSuggestion(hasStatusDifference, hasLineDifference, hasChipDifference, hasUnknownStatus),
|
|
Notes = notes.Count == 0 ? null : string.Join(" ", notes),
|
|
SystemStatus = systemSnapshot.StatusLinha,
|
|
ReportStatus = reportSnapshot.StatusLinha,
|
|
SystemPlan = systemSnapshot.PlanoLinha,
|
|
ReportPlan = reportSnapshot.PlanoLinha,
|
|
SystemSnapshot = systemSnapshot,
|
|
ReportSnapshot = reportSnapshot,
|
|
Differences = differences
|
|
};
|
|
}
|
|
|
|
private static MveReconciliationIssueResult BuildDddReviewIssue(
|
|
MveSystemLineAggregate systemLine,
|
|
MveParsedLine reportLine,
|
|
MveAuditSnapshotDto systemSnapshot,
|
|
MveAuditSnapshotDto reportSnapshot)
|
|
{
|
|
return new MveReconciliationIssueResult
|
|
{
|
|
SourceRowNumber = reportLine.SourceRowNumber,
|
|
NumeroLinha = reportLine.NumeroNormalizado,
|
|
MobileLineId = systemLine.MobileLine.Id,
|
|
SystemItem = systemLine.MobileLine.Item,
|
|
IssueType = "DDD_CHANGE_REVIEW",
|
|
Situation = "mudança de DDD detectada",
|
|
Severity = "WARNING",
|
|
Syncable = false,
|
|
ActionSuggestion = "Revisar manualmente na página Mureg antes de aplicar alterações",
|
|
Notes = "O mesmo chip foi encontrado com o mesmo número base, mas com DDD diferente. Esse cenário ainda não é atualizado automaticamente pelo MVE.",
|
|
SystemStatus = systemSnapshot.StatusLinha,
|
|
ReportStatus = reportSnapshot.StatusLinha,
|
|
SystemPlan = systemSnapshot.PlanoLinha,
|
|
ReportPlan = reportSnapshot.PlanoLinha,
|
|
SystemSnapshot = systemSnapshot,
|
|
ReportSnapshot = reportSnapshot,
|
|
Differences = new List<MveAuditDifferenceDto>
|
|
{
|
|
new()
|
|
{
|
|
FieldKey = "ddd",
|
|
Label = "DDD da linha",
|
|
SystemValue = NullIfEmpty(MveAuditNormalization.ExtractPhoneDdd(systemSnapshot.NumeroLinha)),
|
|
ReportValue = NullIfEmpty(MveAuditNormalization.ExtractPhoneDdd(reportSnapshot.NumeroLinha)),
|
|
Syncable = false
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
private static string ResolveIssueType(
|
|
bool hasStatusDifference,
|
|
bool hasLineDifference,
|
|
bool hasChipDifference,
|
|
bool hasUnknownStatus)
|
|
{
|
|
if (hasLineDifference)
|
|
{
|
|
return "LINE_CHANGE_DETECTED";
|
|
}
|
|
|
|
if (hasChipDifference)
|
|
{
|
|
return "CHIP_CHANGE_DETECTED";
|
|
}
|
|
|
|
if (hasStatusDifference)
|
|
{
|
|
return "STATUS_DIVERGENCE";
|
|
}
|
|
|
|
return hasUnknownStatus ? "UNKNOWN_STATUS" : "ALIGNED";
|
|
}
|
|
|
|
private static string ResolveSituation(
|
|
bool hasStatusDifference,
|
|
bool hasLineDifference,
|
|
bool hasChipDifference,
|
|
bool hasUnknownStatus)
|
|
{
|
|
if (hasLineDifference && hasStatusDifference)
|
|
{
|
|
return "troca de número e status diferente";
|
|
}
|
|
|
|
if (hasLineDifference)
|
|
{
|
|
return "troca de número detectada";
|
|
}
|
|
|
|
if (hasChipDifference && hasStatusDifference)
|
|
{
|
|
return "troca de chip e status diferente";
|
|
}
|
|
|
|
if (hasChipDifference)
|
|
{
|
|
return "troca de chip detectada";
|
|
}
|
|
|
|
if (hasStatusDifference)
|
|
{
|
|
return "divergência de status";
|
|
}
|
|
|
|
return hasUnknownStatus ? "status desconhecido no relatório" : "alinhada";
|
|
}
|
|
|
|
private static string ResolveSeverity(bool hasStatusDifference, bool hasDataDifference, bool hasUnknownStatus)
|
|
{
|
|
if (hasStatusDifference)
|
|
{
|
|
return "HIGH";
|
|
}
|
|
|
|
if (hasDataDifference)
|
|
{
|
|
return "MEDIUM";
|
|
}
|
|
|
|
return hasUnknownStatus ? "WARNING" : "INFO";
|
|
}
|
|
|
|
private static string ResolveActionSuggestion(
|
|
bool hasStatusDifference,
|
|
bool hasLineDifference,
|
|
bool hasChipDifference,
|
|
bool hasUnknownStatus)
|
|
{
|
|
if (hasLineDifference && hasStatusDifference)
|
|
{
|
|
return "Atualizar linha e status da linha com base no MVE";
|
|
}
|
|
|
|
if (hasLineDifference)
|
|
{
|
|
return "Atualizar a linha cadastrada com base no chip informado no MVE";
|
|
}
|
|
|
|
if (hasChipDifference && hasStatusDifference)
|
|
{
|
|
return "Atualizar chip e status da linha com base no MVE";
|
|
}
|
|
|
|
if (hasChipDifference)
|
|
{
|
|
return "Atualizar o chip da linha com base no MVE";
|
|
}
|
|
|
|
if (hasStatusDifference)
|
|
{
|
|
return "Atualizar status da linha com base no MVE";
|
|
}
|
|
|
|
return hasUnknownStatus
|
|
? "Revisar o status recebido e ajustar o mapa de normalização se necessário"
|
|
: "Nenhuma ação";
|
|
}
|
|
|
|
private static MveAuditSnapshotDto BuildSystemSnapshot(MveSystemLineAggregate systemLine)
|
|
{
|
|
return new MveAuditSnapshotDto
|
|
{
|
|
NumeroLinha = NullIfEmpty(systemLine.MobileLine.Linha),
|
|
StatusLinha = NullIfEmpty(systemLine.MobileLine.Status),
|
|
PlanoLinha = NullIfEmpty(systemLine.MobileLine.PlanoContrato),
|
|
DataAtivacao = systemLine.Vigencia?.DtEfetivacaoServico,
|
|
TerminoContrato = systemLine.Vigencia?.DtTerminoFidelizacao,
|
|
Chip = NullIfEmpty(systemLine.MobileLine.Chip),
|
|
Conta = NullIfEmpty(systemLine.MobileLine.Conta),
|
|
Cnpj = NullIfEmpty(systemLine.UserData?.Cnpj),
|
|
ModeloAparelho = NullIfEmpty(systemLine.MobileLine.Aparelho?.Nome),
|
|
Fabricante = NullIfEmpty(systemLine.MobileLine.Aparelho?.Fabricante)
|
|
};
|
|
}
|
|
|
|
private static MveAuditSnapshotDto BuildReportSnapshot(MveParsedLine reportLine)
|
|
{
|
|
return new MveAuditSnapshotDto
|
|
{
|
|
NumeroLinha = NullIfEmpty(reportLine.NumeroNormalizado),
|
|
StatusLinha = NullIfEmpty(reportLine.StatusLinha),
|
|
StatusConta = NullIfEmpty(reportLine.StatusConta),
|
|
PlanoLinha = NullIfEmpty(reportLine.PlanoLinha),
|
|
DataAtivacao = reportLine.DataAtivacao,
|
|
TerminoContrato = reportLine.TerminoContrato,
|
|
Chip = NullIfEmpty(reportLine.Chip),
|
|
Conta = NullIfEmpty(reportLine.Conta),
|
|
Cnpj = NullIfEmpty(reportLine.Cnpj),
|
|
ModeloAparelho = NullIfEmpty(reportLine.ModeloAparelho),
|
|
Fabricante = NullIfEmpty(reportLine.Fabricante),
|
|
ServicosAtivos = reportLine.ServicosAtivos
|
|
.Where(x => !string.IsNullOrWhiteSpace(x))
|
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
|
.ToList()
|
|
};
|
|
}
|
|
|
|
private static void AddDifference(
|
|
ICollection<MveAuditDifferenceDto> differences,
|
|
string fieldKey,
|
|
string label,
|
|
string? systemValue,
|
|
string? reportValue,
|
|
bool syncable,
|
|
Func<string?, string> comparer)
|
|
{
|
|
var normalizedSystem = comparer(systemValue);
|
|
var normalizedReport = comparer(reportValue);
|
|
if (string.Equals(normalizedSystem, normalizedReport, StringComparison.Ordinal))
|
|
{
|
|
return;
|
|
}
|
|
|
|
differences.Add(new MveAuditDifferenceDto
|
|
{
|
|
FieldKey = fieldKey,
|
|
Label = label,
|
|
SystemValue = NullIfEmpty(systemValue),
|
|
ReportValue = NullIfEmpty(reportValue),
|
|
Syncable = syncable
|
|
});
|
|
}
|
|
|
|
private static string? NullIfEmpty(string? value)
|
|
{
|
|
return string.IsNullOrWhiteSpace(value) ? null : value.Trim();
|
|
}
|
|
}
|
|
|
|
public sealed class MveReconciliationResult
|
|
{
|
|
public int TotalSystemLines { get; init; }
|
|
|
|
public int TotalReportLines { get; init; }
|
|
|
|
public int TotalConciliated { get; set; }
|
|
|
|
public int TotalStatusDivergences { get; set; }
|
|
|
|
public int TotalDataDivergences { get; set; }
|
|
|
|
public int TotalOnlyInSystem { get; set; }
|
|
|
|
public int TotalOnlyInReport { get; set; }
|
|
|
|
public int TotalDuplicateReportLines { get; set; }
|
|
|
|
public int TotalDuplicateSystemLines { get; set; }
|
|
|
|
public int TotalInvalidRows { get; init; }
|
|
|
|
public int TotalUnknownStatuses { get; init; }
|
|
|
|
public int TotalSyncableIssues { get; set; }
|
|
|
|
public List<MveReconciliationIssueResult> Issues { get; } = new();
|
|
}
|
|
|
|
public sealed class MveReconciliationIssueResult
|
|
{
|
|
public int? SourceRowNumber { get; init; }
|
|
|
|
public string NumeroLinha { get; init; } = string.Empty;
|
|
|
|
public Guid? MobileLineId { get; init; }
|
|
|
|
public int? SystemItem { get; init; }
|
|
|
|
public string IssueType { get; init; } = string.Empty;
|
|
|
|
public string Situation { get; init; } = string.Empty;
|
|
|
|
public string Severity { get; init; } = "INFO";
|
|
|
|
public bool Syncable { get; init; }
|
|
|
|
public string? ActionSuggestion { get; init; }
|
|
|
|
public string? Notes { get; init; }
|
|
|
|
public string? SystemStatus { get; init; }
|
|
|
|
public string? ReportStatus { get; init; }
|
|
|
|
public string? SystemPlan { get; init; }
|
|
|
|
public string? ReportPlan { get; init; }
|
|
|
|
public MveAuditSnapshotDto? SystemSnapshot { get; init; }
|
|
|
|
public MveAuditSnapshotDto? ReportSnapshot { get; init; }
|
|
|
|
public List<MveAuditDifferenceDto> Differences { get; init; } = new();
|
|
}
|
|
|
|
internal sealed record MveSystemLineAggregate(
|
|
MobileLine MobileLine,
|
|
VigenciaLine? Vigencia,
|
|
UserData? UserData,
|
|
string NumeroNormalizado);
|