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 <noreply@anthropic.com>
This commit is contained in:
2026-05-03 19:49:13 -04:00
parent 28b7b9f86b
commit 3aeec4ffb2
2 changed files with 42 additions and 1 deletions
@@ -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,
});
}
@@ -404,7 +404,16 @@
? ' <span class="badge bg-success ms-1">Added to platform catalog</span>'
: '';
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',
`<i class="bi bi-exclamation-triangle-fill me-1"></i>` +
`<strong>${itemName}</strong> is already in your inventory. ` +
`<a href="/Inventory/Details/${data.existingInventoryId}" class="alert-link fw-semibold">View existing item</a>` +
` &mdash; 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}`);