From 3aeec4ffb27e18be5e518a4732a36a8aa2ed115f Mon Sep 17 00:00:00 2001 From: Scott Pouliot Date: Sun, 3 May 2026 19:49:13 -0400 Subject: [PATCH] Warn on label scan when product already exists in tenant inventory After resolving manufacturer + SKU from the scan, ScanLabel now queries the tenant's InventoryItems: first by ManufacturerPartNumber exact match (most precise), then by ColorName + Manufacturer fuzzy match as fallback. If a match is found, the response includes existingInventoryId and existingInventoryName. The JS fillFromScan() shows a warning banner with a direct link to the existing item instead of the normal success message. Form fields are still pre-filled so the user can proceed to add a new entry (e.g. a different lot or bag size) if that was the intent. Co-Authored-By: Claude Sonnet 4.6 --- .../Controllers/InventoryController.cs | 32 +++++++++++++++++++ .../wwwroot/js/inventory-label-scan.js | 11 ++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/PowderCoating.Web/Controllers/InventoryController.cs b/src/PowderCoating.Web/Controllers/InventoryController.cs index be8426c..a5909c2 100644 --- a/src/PowderCoating.Web/Controllers/InventoryController.cs +++ b/src/PowderCoating.Web/Controllers/InventoryController.cs @@ -834,6 +834,36 @@ public class InventoryController : Controller } } + // Check if this product already exists in the tenant's inventory. + // Match by ManufacturerPartNumber first (most precise); fall back to color name + manufacturer. + // Returns the first active match so the UI can warn the user before they create a duplicate. + int? existingInventoryId = null; + string? existingInventoryName = null; + + if (!string.IsNullOrEmpty(sku)) + { + var skuLower = sku.ToLower(); + var byPart = await _unitOfWork.InventoryItems.FindAsync(i => + i.ManufacturerPartNumber != null && + i.ManufacturerPartNumber.ToLower() == skuLower); + var hit = byPart.FirstOrDefault(); + if (hit != null) { existingInventoryId = hit.Id; existingInventoryName = hit.Name; } + } + + if (existingInventoryId == null && !string.IsNullOrEmpty(colorName)) + { + var nameLower = colorName.ToLower(); + var mfrLower = manufacturer?.ToLower() ?? ""; + var byName = await _unitOfWork.InventoryItems.FindAsync(i => + (i.ColorName != null && i.ColorName.ToLower() == nameLower) || + i.Name.ToLower() == nameLower); + var hit = byName.FirstOrDefault(i => + string.IsNullOrEmpty(mfrLower) || + (i.Manufacturer ?? "").ToLower().Contains(mfrLower) || + mfrLower.Contains((i.Manufacturer ?? "").ToLower().Trim())); + if (hit != null) { existingInventoryId = hit.Id; existingInventoryName = hit.Name; } + } + return Json(new { success = true, @@ -856,6 +886,8 @@ public class InventoryController : Controller vendorName = manufacturer, wasInCatalog = wasInCatalog, addedToCatalog = addedToCatalog, + existingInventoryId = existingInventoryId, + existingInventoryName = existingInventoryName, reasoning = aiResult.Reasoning, }); } diff --git a/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js b/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js index dd9da27..b2f1471 100644 --- a/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js +++ b/src/PowderCoating.Web/wwwroot/js/inventory-label-scan.js @@ -404,7 +404,16 @@ ? ' Added to platform catalog' : ''; - if (filled.length > 0) { + if (data.existingInventoryId) { + const itemName = data.existingInventoryName || data.colorName || 'This product'; + const filledNote = filled.length > 0 ? ` Fields pre-filled from scan.` : ''; + showFormStatus('warning', + `` + + `${itemName} is already in your inventory. ` + + `View existing item` + + ` — or continue below to add a new entry (e.g. a new lot or bag size).${filledNote}${catalogNote}` + ); + } else if (filled.length > 0) { showFormStatus('success', `Filled from label scan: ${filled.join(', ')}.${catalogNote}`); } else { showFormStatus('warning', `Label scanned but no empty fields to fill.${catalogNote}`);