fix(inventory): robustare dedup vid skann + borttag pa startsidan

Dedup vid /v1/scans/:id/confirm matchade bara pa exakt (skiftlageskansligt)
namn eller identiskt katalog-id, sa omfotografering av samma hylla skapade
dubbletter. Ny normalizeItemName (gemener, accenter, storlek bort; behaller
fetthalt-siffror) + matchning pa katalog-id ELLER normaliserat namn mot
aktivt hushallslager.

Mobil: 'Ta bort' fanns bara i Ditt kok. Lade borttag pa startsidans
lager-rader (befintlig DELETE /v1/inventory/items/:id).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WZdew6cWn1MeHqoYWFfxzo
This commit is contained in:
Claude
2026-08-18 14:44:03 +00:00
parent 4ca567bba3
commit 684743ae95
4 changed files with 86 additions and 23 deletions
+21
View File
@@ -21,6 +21,27 @@ export interface DedupCandidate {
const WINDOW_MS = 3 * 86_400_000; // 3 dygn
/**
* Normaliserar ett produktnamn för dubblett-matchning: gemener, bort med
* accenter/å/ä/ö, storleksangivelser (500 g, 1 l) och skiljetecken — men
* BEHÅLL sifferskillnader som skiljer varianter (mjölk 3 % vs 1,5 %).
* "Mjölk", "mjölk", "MJÖLK 1L" → "mjolk"; "Mjölk 3%" → "mjolk 3".
*/
export function normalizeItemName(name: string): string {
return name
.normalize("NFC")
.toLowerCase()
.replace(/[àáâãäå]/g, "a")
.replace(/[èéêë]/g, "e")
.replace(/[ìíîï]/g, "i")
.replace(/[òóôõö]/g, "o")
.replace(/[ùúûü]/g, "u")
.replace(/\b\d+(?:[.,]\d+)?\s*(?:kg|g|ml|cl|dl|l|st|pack)\b/g, " ")
.replace(/[^a-z0-9 ]+/g, " ")
.replace(/\s+/g, " ")
.trim();
}
/**
* Dubblettkandidater mellan kvitto, streckkod och bilder (spec §9).
* Ren heuristik användaren fattar alltid beslutet. AI-baserad
@@ -6,6 +6,7 @@ import {
findDuplicateCandidates,
forecastDepletion,
normalizeDelta,
normalizeItemName,
} from "../src/index.js";
const TODAY = new Date("2026-08-02T00:00:00Z"); // UTC testet ska vara sant i alla tidszoner
@@ -217,3 +218,14 @@ describe("pantry forecast (spec §40)", () => {
);
});
});
describe("normalizeItemName (dubblett-matchning vid omfoto)", () => {
it("slår ihop skiftläge, accenter och storlek", () => {
expect(normalizeItemName("Mjölk")).toBe(normalizeItemName("mjölk"));
expect(normalizeItemName("MJÖLK 1L")).toBe(normalizeItemName("Mjölk"));
expect(normalizeItemName("Vispgrädde 5 dl")).toBe(normalizeItemName("vispgrädde"));
});
it("håller isär olika fetthalt/varianter", () => {
expect(normalizeItemName("Mjölk 3%")).not.toBe(normalizeItemName("Mjölk 1,5%"));
});
});