517e452c64
Introduce a shared InventoryDuplicateMatcher (SKU, manufacturer part number, manufacturer color) used by both manual inventory creation and powder-label scanning, so the two paths flag duplicates consistently. Surfaces a duplicate warning in the Create/Edit forms via inventory-duplicate-check.js and the catalog-lookup / label-scan flows. Callers pass tenant-restricted inventory; the matcher re-checks CompanyId as defense in depth. Adds InventoryDuplicateMatcherTests covering the match precedence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
168 lines
4.6 KiB
C#
168 lines
4.6 KiB
C#
using PowderCoating.Application.Services;
|
|
using PowderCoating.Core.Entities;
|
|
|
|
namespace PowderCoating.UnitTests;
|
|
|
|
public class InventoryDuplicateMatcherTests
|
|
{
|
|
[Fact]
|
|
public void Find_SkuMatch_IsRestrictedToRequestedCompany()
|
|
{
|
|
var otherTenantItem = Item(
|
|
id: 1,
|
|
companyId: 22,
|
|
sku: "POW-001",
|
|
manufacturer: "Prismatic Powders",
|
|
colorName: "Illusion Malbec");
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { otherTenantItem },
|
|
companyId: 11,
|
|
sku: "POW-001",
|
|
manufacturer: null,
|
|
manufacturerPartNumber: null,
|
|
colorName: null,
|
|
isCoating: false);
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Find_SkuMatch_IsCaseAndWhitespaceInsensitive()
|
|
{
|
|
var item = Item(1, 11, "POW-001", null, null);
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { item },
|
|
companyId: 11,
|
|
sku: " pow-001 ",
|
|
manufacturer: null,
|
|
manufacturerPartNumber: null,
|
|
colorName: null,
|
|
isCoating: false);
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(InventoryDuplicateMatchType.Sku, result!.MatchType);
|
|
Assert.Same(item, result.Item);
|
|
}
|
|
|
|
[Fact]
|
|
public void Find_PowderManufacturerAndColor_NormalizesSpacingAndCase()
|
|
{
|
|
var item = Item(
|
|
id: 1,
|
|
companyId: 11,
|
|
sku: "POW-001",
|
|
manufacturer: "Prismatic Powders",
|
|
colorName: "Illusion Malbec");
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { item },
|
|
companyId: 11,
|
|
sku: null,
|
|
manufacturer: " prismatic powders ",
|
|
manufacturerPartNumber: null,
|
|
colorName: "illusion malbec",
|
|
isCoating: true);
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(InventoryDuplicateMatchType.ManufacturerColor, result!.MatchType);
|
|
}
|
|
|
|
[Fact]
|
|
public void Find_SameColorFromDifferentManufacturer_IsNotDuplicate()
|
|
{
|
|
var item = Item(
|
|
id: 1,
|
|
companyId: 11,
|
|
sku: "POW-001",
|
|
manufacturer: "Prismatic Powders",
|
|
colorName: "Gloss Black");
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { item },
|
|
companyId: 11,
|
|
sku: null,
|
|
manufacturer: "Columbia Coatings",
|
|
manufacturerPartNumber: null,
|
|
colorName: "Gloss Black",
|
|
isCoating: true);
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
[Fact]
|
|
public void Find_ManufacturerPartNumber_UsesSharedPowderRule()
|
|
{
|
|
var item = Item(
|
|
id: 1,
|
|
companyId: 11,
|
|
sku: "POW-001",
|
|
manufacturer: "Columbia Coatings",
|
|
colorName: "Smokey Blue",
|
|
manufacturerPartNumber: "S5704126");
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { item },
|
|
companyId: 11,
|
|
sku: null,
|
|
manufacturer: "columbia coatings",
|
|
manufacturerPartNumber: " s5704126 ",
|
|
colorName: null,
|
|
isCoating: true);
|
|
|
|
Assert.NotNull(result);
|
|
Assert.Equal(InventoryDuplicateMatchType.ManufacturerPartNumber, result!.MatchType);
|
|
}
|
|
|
|
[Fact]
|
|
public void Find_NonCoating_DoesNotUsePowderIdentityRules()
|
|
{
|
|
var item = Item(
|
|
id: 1,
|
|
companyId: 11,
|
|
sku: "SUP-001",
|
|
manufacturer: "Acme",
|
|
colorName: "Blue");
|
|
|
|
var result = InventoryDuplicateMatcher.Find(
|
|
new[] { item },
|
|
companyId: 11,
|
|
sku: null,
|
|
manufacturer: "Acme",
|
|
manufacturerPartNumber: null,
|
|
colorName: "Blue",
|
|
isCoating: false);
|
|
|
|
Assert.Null(result);
|
|
}
|
|
|
|
private static InventoryItem Item(
|
|
int id,
|
|
int companyId,
|
|
string sku,
|
|
string? manufacturer,
|
|
string? colorName,
|
|
string? manufacturerPartNumber = null)
|
|
{
|
|
return new InventoryItem
|
|
{
|
|
Id = id,
|
|
CompanyId = companyId,
|
|
SKU = sku,
|
|
Name = colorName ?? sku,
|
|
Manufacturer = manufacturer,
|
|
ManufacturerPartNumber = manufacturerPartNumber,
|
|
ColorName = colorName,
|
|
InventoryCategory = new InventoryCategoryLookup
|
|
{
|
|
Id = id,
|
|
CompanyId = companyId,
|
|
DisplayName = "Powder",
|
|
CategoryCode = "POWDER",
|
|
IsCoating = true
|
|
}
|
|
};
|
|
}
|
|
}
|