From 37c95192cae1a53625fbe272ac23376026060f56 Mon Sep 17 00:00:00 2001 From: Scott Pouliot Date: Sat, 25 Apr 2026 22:48:39 -0400 Subject: [PATCH] Enforce quarterly run limit on AI price check - GET: sets ViewBag.NextRunAvailable if last run was within 90 days; view disables the button and shows the next eligible date - POST: returns early with a warning if called before the 90-day window Co-Authored-By: Claude Sonnet 4.6 --- .../Controllers/CatalogItemsController.cs | 21 ++++++++++++++ .../Views/CatalogItems/AiPriceCheck.cshtml | 28 +++++++++++++------ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/PowderCoating.Web/Controllers/CatalogItemsController.cs b/src/PowderCoating.Web/Controllers/CatalogItemsController.cs index f9c7b22..40e0ba3 100644 --- a/src/PowderCoating.Web/Controllers/CatalogItemsController.cs +++ b/src/PowderCoating.Web/Controllers/CatalogItemsController.cs @@ -941,6 +941,13 @@ namespace PowderCoating.Web.Controllers var pricedItems = await _unitOfWork.CatalogItems.FindAsync(ci => ci.IsActive && ci.DefaultPrice > 0); ViewBag.ActiveItemCount = pricedItems.Count(); + if (report != null) + { + var nextRun = report.RunAt.AddDays(90); + if (nextRun > DateTime.UtcNow) + ViewBag.NextRunAvailable = nextRun.ToLocalTime().ToString("MMMM d, yyyy"); + } + CatalogPriceCheckReportDto? dto = null; if (report != null) { @@ -988,6 +995,20 @@ namespace PowderCoating.Web.Controllers var currentUser = await _userManager.GetUserAsync(User); if (currentUser == null) return Forbid(); + // Enforce quarterly run limit — check the most recent report for this company. + var lastReport = (await _unitOfWork.CatalogPriceCheckReports.FindAsync( + r => r.CompanyId == currentUser.CompanyId)) + .OrderByDescending(r => r.RunAt).FirstOrDefault(); + if (lastReport != null) + { + var nextRun = lastReport.RunAt.AddDays(90); + if (nextRun > DateTime.UtcNow) + { + TempData["Warning"] = $"Price check can only be run once per quarter. Next run available: {nextRun.ToLocalTime():MMMM d, yyyy}."; + return RedirectToAction(nameof(AiPriceCheck)); + } + } + try { // Load active catalog items with a real price — skip $0 items (placeholders, diff --git a/src/PowderCoating.Web/Views/CatalogItems/AiPriceCheck.cshtml b/src/PowderCoating.Web/Views/CatalogItems/AiPriceCheck.cshtml index 6857157..3f4f84e 100644 --- a/src/PowderCoating.Web/Views/CatalogItems/AiPriceCheck.cshtml +++ b/src/PowderCoating.Web/Views/CatalogItems/AiPriceCheck.cshtml @@ -94,14 +94,26 @@ Back to Catalog -
- @Html.AntiForgeryToken() - -
+ @if (ViewBag.NextRunAvailable != null) + { +
+ +
Next run available: @ViewBag.NextRunAvailable
+
+ } + else + { +
+ @Html.AntiForgeryToken() + +
+ } @if (TempData["Success"] != null)