Link inventory to powder catalog and flag discontinued items
Phase 5 (part): the inventory tie-in. - Set InventoryItem.PowderCatalogItemId on the catalog-sourced create paths: directly in CreateIncomingFromCatalog, and via a new FindCatalogMatchAsync (Manufacturer + ManufacturerPartNumber) helper in Create. - Inventory Details loads the linked catalog row (falling back to an identity match for items created before linking) and shows a "Discontinued by manufacturer — cannot reorder" badge + banner when it's discontinued. Deliberately distinct from the shop's own Active/Inactive status: existing stock can still be used and quoted, it just can't be reordered. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -240,6 +240,17 @@ public class InventoryController : Controller
|
||||
var useMetric = await _tenantContext.UseMetricSystemAsync();
|
||||
ViewBag.CoverageUnit = _measurementService.GetCoverageUnitLabel(useMetric);
|
||||
|
||||
// Manufacturer-level catalog status: prefer the linked catalog row, fall back to an
|
||||
// identity match for items added before they were linked. Drives the "discontinued by
|
||||
// manufacturer — cannot reorder" warning. This is distinct from the shop's own
|
||||
// IsActive/DiscontinuedDate (whether the shop still stocks it).
|
||||
var catalogItem = item.PowderCatalogItemId.HasValue
|
||||
? await _unitOfWork.PowderCatalog.GetByIdAsync(item.PowderCatalogItemId.Value)
|
||||
: await FindCatalogMatchAsync(item.Manufacturer, item.ManufacturerPartNumber);
|
||||
ViewBag.CatalogDiscontinued = catalogItem?.IsDiscontinued ?? false;
|
||||
ViewBag.CatalogVendorName = catalogItem?.VendorName;
|
||||
ViewBag.CatalogProductUrl = catalogItem?.ProductUrl;
|
||||
|
||||
return View(itemDto);
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -302,6 +313,12 @@ public class InventoryController : Controller
|
||||
item.Category = category.DisplayName;
|
||||
}
|
||||
|
||||
// Link to the platform catalog row when this item's identity matches one, so the detail
|
||||
// screen can show manufacturer-level status (discontinued / cannot reorder).
|
||||
var catalogMatch = await FindCatalogMatchAsync(item.Manufacturer, item.ManufacturerPartNumber);
|
||||
if (catalogMatch != null)
|
||||
item.PowderCatalogItemId = catalogMatch.Id;
|
||||
|
||||
await _unitOfWork.InventoryItems.AddAsync(item);
|
||||
await _unitOfWork.SaveChangesAsync();
|
||||
|
||||
@@ -763,6 +780,24 @@ public class InventoryController : Controller
|
||||
/// Returns (wasInCatalog, addedToCatalog) so callers can surface UI badges.
|
||||
/// Mutates <paramref name="result"/> in place.
|
||||
/// </summary>
|
||||
/// <summary>
|
||||
/// Finds the platform powder catalog row matching an inventory item's identity
|
||||
/// (Manufacturer + ManufacturerPartNumber), or null. Used to set
|
||||
/// <see cref="InventoryItem.PowderCatalogItemId"/> and to surface manufacturer-level status
|
||||
/// (e.g. discontinued / cannot reorder) on the detail screen.
|
||||
/// </summary>
|
||||
private async Task<PowderCatalogItem?> FindCatalogMatchAsync(string? manufacturer, string? sku)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(manufacturer) || string.IsNullOrWhiteSpace(sku))
|
||||
return null;
|
||||
|
||||
var skuLower = sku.Trim().ToLower();
|
||||
var mfrLower = manufacturer.Trim().ToLower();
|
||||
var hits = await _unitOfWork.PowderCatalog.FindAsync(p =>
|
||||
p.Sku.ToLower() == skuLower && p.VendorName.ToLower().Contains(mfrLower));
|
||||
return hits.FirstOrDefault();
|
||||
}
|
||||
|
||||
private async Task<(bool wasInCatalog, bool addedToCatalog)> EnrichFromCatalogAsync(
|
||||
InventoryAiLookupResult result, bool autoContribute)
|
||||
{
|
||||
@@ -1257,6 +1292,7 @@ public class InventoryController : Controller
|
||||
ColorName = catalogItem.ColorName,
|
||||
Manufacturer = catalogItem.VendorName,
|
||||
ManufacturerPartNumber= catalogItem.Sku,
|
||||
PowderCatalogItemId = catalogItem.Id,
|
||||
Finish = catalogItem.Finish,
|
||||
ColorFamilies = catalogItem.ColorFamilies,
|
||||
RequiresClearCoat = catalogItem.RequiresClearCoat ?? false,
|
||||
|
||||
@@ -69,6 +69,12 @@
|
||||
{
|
||||
<span class="badge bg-danger"><i class="bi bi-x-circle me-1"></i>Inactive</span>
|
||||
}
|
||||
@if ((bool?)ViewBag.CatalogDiscontinued == true)
|
||||
{
|
||||
<span class="badge bg-warning text-dark" title="Discontinued by the manufacturer — cannot reorder">
|
||||
<i class="bi bi-slash-circle me-1"></i>Discontinued by manufacturer
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
@@ -103,6 +109,20 @@
|
||||
<div><strong>Status:</strong> This item is inactive</div>
|
||||
</div>
|
||||
}
|
||||
@if ((bool?)ViewBag.CatalogDiscontinued == true)
|
||||
{
|
||||
<div class="alert alert-warning alert-permanent d-flex align-items-center mb-3">
|
||||
<i class="bi bi-slash-circle me-2"></i>
|
||||
<div>
|
||||
<strong>Discontinued by @(ViewBag.CatalogVendorName ?? "manufacturer"):</strong>
|
||||
this powder has been discontinued and cannot be reordered. Existing stock can still be used and quoted.
|
||||
@if (!string.IsNullOrEmpty(ViewBag.CatalogProductUrl as string))
|
||||
{
|
||||
<a href="@ViewBag.CatalogProductUrl" target="_blank" rel="noopener" class="alert-link ms-1">View product page</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Left Column -->
|
||||
|
||||
Reference in New Issue
Block a user