diff --git a/src/PowderCoating.Web/Controllers/PowderCatalogController.cs b/src/PowderCoating.Web/Controllers/PowderCatalogController.cs
index 25329c3..32f2148 100644
--- a/src/PowderCoating.Web/Controllers/PowderCatalogController.cs
+++ b/src/PowderCoating.Web/Controllers/PowderCatalogController.cs
@@ -452,6 +452,60 @@ public class PowderCatalogController : Controller
return RedirectToAction(nameof(Index));
}
+ ///
+ /// Right-to-delete: removes every catalog record sourced from the Columbia Coatings API
+ /// (regardless of derived manufacturer, since PPG/KP products were served through that feed)
+ /// and nulls any inventory links to them across all tenants. The shops' own inventory stock
+ /// records survive — only the catalog link and discontinued badge are lost. SuperAdmin only.
+ ///
+ [HttpPost]
+ [ValidateAntiForgeryToken]
+ public async Task PurgeColumbiaData()
+ {
+ var sourced = (await _unitOfWork.PowderCatalog.FindAsync(
+ p => p.Source == ColumbiaIntegrationConstants.SourceName)).ToList();
+
+ if (sourced.Count == 0)
+ {
+ TempData["Error"] = "There is no Columbia Coatings API data to remove.";
+ return RedirectToAction(nameof(Index));
+ }
+
+ var ids = sourced.Select(p => p.Id).ToList();
+
+ // Null the inventory links across ALL tenants (platform-level purge). A tenant's stock
+ // record is their data and must survive — it keeps its add-time snapshot, losing only the
+ // live catalog link.
+ var linked = (await _unitOfWork.InventoryItems.FindAsync(
+ i => i.PowderCatalogItemId.HasValue && ids.Contains(i.PowderCatalogItemId.Value),
+ ignoreQueryFilters: true)).ToList();
+ foreach (var inv in linked)
+ {
+ inv.PowderCatalogItemId = null;
+ await _unitOfWork.InventoryItems.UpdateAsync(inv);
+ }
+
+ foreach (var p in sourced)
+ await _unitOfWork.PowderCatalog.DeleteAsync(p);
+ await _unitOfWork.CompleteAsync();
+
+ // Reset sync tracking so the admin panel reflects the purge.
+ await _platformSettings.SetAsync(ColumbiaIntegrationConstants.SettingLastSyncedAt, null, "Columbia Purge");
+ await _platformSettings.SetAsync(
+ ColumbiaIntegrationConstants.SettingLastResult,
+ $"Purged {sourced.Count:N0} records on {DateTime.UtcNow:yyyy-MM-dd}",
+ "Columbia Purge");
+
+ _logger.LogWarning(
+ "Columbia data purge: deleted {Count} catalog records, unlinked {Linked} inventory items.",
+ sourced.Count, linked.Count);
+
+ TempData["Success"] =
+ $"Removed {sourced.Count:N0} Columbia Coatings catalog record(s) and unlinked " +
+ $"{linked.Count:N0} inventory item(s). Inventory stock was preserved.";
+ return RedirectToAction(nameof(Index));
+ }
+
// Private helpers
private async Task ApplyTdsCureFallbackAsync(InventoryAiLookupResult result, string? colorName)
diff --git a/src/PowderCoating.Web/Views/PowderCatalog/Index.cshtml b/src/PowderCoating.Web/Views/PowderCatalog/Index.cshtml
index 4ceb16d..7a71a0a 100644
--- a/src/PowderCoating.Web/Views/PowderCatalog/Index.cshtml
+++ b/src/PowderCoating.Web/Views/PowderCatalog/Index.cshtml
@@ -144,6 +144,13 @@
{
— @columbiaLastResult
}
+