-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathClientImportIntegrity.cs
More file actions
95 lines (83 loc) · 3.53 KB
/
Copy pathClientImportIntegrity.cs
File metadata and controls
95 lines (83 loc) · 3.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
namespace SalesAPI;
/// <summary>
/// Validates Customer Base import counts across pipeline stages.
/// Detects silent client loss (e.g. wrong Sold-to column mapping).
/// </summary>
public static class ClientImportIntegrity
{
public static ClientImportIntegrityResult Validate(
ClientImportReport report,
int storedCount,
int apiCount,
int repPortalCount)
{
var result = new ClientImportIntegrityResult
{
RawExcelRows = report.RawRowCount,
UniqueSoldToCodes = report.UniqueSoldToCodeCount,
DuplicateSoldToRowCount = report.DuplicateSoldToRowCount,
DuplicateSoldToCodeCount = report.DuplicateSoldToCodes.Count,
InactiveMarkedRows = report.InactiveMarkedRows,
UniqueActiveSoldToCodes = report.UniqueActiveSoldToCodeCount,
ImportedCount = report.ParsedClientCount,
StoredCount = storedCount,
ApiCount = apiCount,
RepPortalCount = repPortalCount,
SkippedBlankSoldTo = report.SkippedBlankSoldTo,
};
if (report.SkippedBlankSoldTo > 0)
{
result.Warnings.Add(
$"{report.SkippedBlankSoldTo} row(s) skipped due to blank Sold-to party code.");
}
if (report.DuplicateSoldToRowCount > 0)
{
result.Warnings.Add(
$"{report.DuplicateSoldToRowCount} duplicate Sold-to row(s) across " +
$"{report.DuplicateSoldToCodes.Count} code(s) — expected when one store appears on multiple rows; import dedupes by Sold-to code.");
}
CompareStage(result, "Unique Sold-to → Imported", report.UniqueSoldToCodeCount, report.ParsedClientCount,
"Incorrect Sold-to party code column mapping, or rows dropped during parse.");
CompareStage(result, "Imported → Database", report.ParsedClientCount, storedCount,
"SQLite upsert failed or UNIQUE constraint collapsed clients onto a non-Sold-to key.");
CompareStage(result, "Database → API", storedCount, apiCount,
"GetAllClients() filter excludes imported rows (check IsActive or query).");
CompareStage(result, "API → Rep Portal", apiCount, repPortalCount,
"Rep Portal does not receive the full GET /api/clients payload.");
return result;
}
private static void CompareStage(
ClientImportIntegrityResult result,
string label,
int from,
int to,
string likelyCause)
{
if (to < from)
{
result.Errors.Add(
$"REGRESSION at {label}: {from:N0} → {to:N0} (lost {from - to:N0}). Likely cause: {likelyCause}");
}
else if (to > from)
{
result.Warnings.Add($"{label}: count increased {from:N0} → {to:N0} (unexpected).");
}
}
}
public class ClientImportIntegrityResult
{
public int RawExcelRows { get; set; }
public int UniqueSoldToCodes { get; set; }
public int DuplicateSoldToRowCount { get; set; }
public int DuplicateSoldToCodeCount { get; set; }
public int InactiveMarkedRows { get; set; }
public int UniqueActiveSoldToCodes { get; set; }
public int ImportedCount { get; set; }
public int StoredCount { get; set; }
public int ApiCount { get; set; }
public int RepPortalCount { get; set; }
public int SkippedBlankSoldTo { get; set; }
public List<string> Warnings { get; set; } = new();
public List<string> Errors { get; set; } = new();
public bool IsValid => Errors.Count == 0;
}