From 2213bd36dd0cc881523213b90b39feaad177e8bb Mon Sep 17 00:00:00 2001 From: Dave Date: Sat, 24 Oct 2026 23:07:20 +0200 Subject: [PATCH] feat(quote-assistant): the quote line's intel records the AI's pick MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quote assistant anchored the line's intel on the POS row it picked, but did not record the pick itself — so the price check could not tell an exact pick from a fuzzy match. It now stores the pick and match_kind becomes "pos_pick". Co-Authored-By: Claude Opus 5.5 --- .../server/quote-assistant-service.test.ts | 7 ++++++ src/lib/server/quote-assistant-service.ts | 24 +++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/lib/server/quote-assistant-service.test.ts b/src/lib/server/quote-assistant-service.test.ts index b5ff37b..d779f8b 100644 --- a/src/lib/server/quote-assistant-service.test.ts +++ b/src/lib/server/quote-assistant-service.test.ts @@ -290,6 +290,13 @@ describe("fillQuote", () => { expect(populateIntel).toHaveBeenCalledWith("a", "quote_items", { write: true, anchor: { stockItemId: "stk1", sku: "HB-1050" }, + posPick: { + stock_item_id: "stk1", + match: "exact", + differs: [], + reason: "same bolt", + model: "MiniMax-M3", + }, }); }); diff --git a/src/lib/server/quote-assistant-service.ts b/src/lib/server/quote-assistant-service.ts index 32b2720..0c1e2b3 100644 --- a/src/lib/server/quote-assistant-service.ts +++ b/src/lib/server/quote-assistant-service.ts @@ -145,12 +145,28 @@ async function populateIntelAfterFill( nowFn: () => Date, ): Promise { const aiFill = patch.ai_fill as - | { stock_item_id?: string | null; stock_item_sku?: string | null } + | { + stock_item_id?: string | null; + stock_item_sku?: string | null; + reason?: string; + model?: string; + } | undefined; const anchor = { stockItemId: aiFill?.stock_item_id || undefined, sku: aiFill?.stock_item_sku || undefined, }; + // The line was filled from a POS row the AI picked — record it, so the + // price check says "this is the item the AI picked" and not "similar". + const posPick = aiFill?.stock_item_id + ? { + stock_item_id: aiFill.stock_item_id, + match: "exact" as const, + differs: [] as string[], + reason: aiFill.reason || "", + model: aiFill.model || "", + } + : undefined; if (nowFn().getTime() >= deadlineMs) { console.warn( `[quote-assistant] time budget spent — skipping item intel for line ${lineId}`, @@ -158,7 +174,11 @@ async function populateIntelAfterFill( return; } try { - await populateItemIntel(lineId, "quote_items", { write: true, anchor }); + await populateItemIntel(lineId, "quote_items", { + write: true, + anchor, + ...(posPick ? { posPick } : {}), + }); } catch (e) { console.error(`[quote-assistant] item intel failed for line ${lineId}:`, e); }