feat: deprecate matchProducts and enrichWithAiCategories methods, update categorization logic with unified matcher
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -541,6 +541,14 @@ export class ReceiptImportService {
|
||||
return items.filter((item) => !isIgnoredReceiptName(item.rawName));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated CLEANUP PENDING (Session 2026-05-09)
|
||||
*
|
||||
* Ersatt av unified matcher (matchAndEnrichReceiptItem).
|
||||
* Denna metod körde alias-lookup + word-match separat.
|
||||
*
|
||||
* Cleanup: Se enrichWithAiCategories checklist ovan.
|
||||
*/
|
||||
private async matchProducts(
|
||||
items: ParsedReceiptItem[],
|
||||
userId?: number,
|
||||
@@ -723,6 +731,14 @@ export class ReceiptImportService {
|
||||
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
// UNIFIED MATCHER: Kombinerar product matching + categorization
|
||||
//
|
||||
// KATEGORI-HIERARKI (fallback-first):
|
||||
// 1. Alias-kopplad produkt (om fanns)
|
||||
// 2. Word-match-kopplad produkt (om fanns)
|
||||
// 3. Regel-baserad (deterministisk, alltid försökt)
|
||||
// 4. AI-kategorisering (BARA som fallback när allt annat misslyckades, och om aktiverat)
|
||||
//
|
||||
// AI kallas ALDRIG om regel-baserad eller produkt-koppling redan satte kategori.
|
||||
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
private async matchAndEnrichReceiptItem(
|
||||
@@ -766,7 +782,7 @@ export class ReceiptImportService {
|
||||
(um) => um.productId === aliasMatch.product.id && um.originalUnit === (item.unit ?? '').trim().toLowerCase(),
|
||||
)?.preferredUnit;
|
||||
|
||||
return {
|
||||
const aliasResult: ParsedReceiptItem = {
|
||||
...item,
|
||||
matchedProductId: aliasMatch.product.id,
|
||||
matchedProductName: aliasMatch.product.canonicalName ?? aliasMatch.product.name,
|
||||
@@ -784,6 +800,9 @@ export class ReceiptImportService {
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
// Kör alltid enrichCategoryForItem för guard-funktioner och hard overrides
|
||||
return await this.enrichCategoryForItem(aliasResult, context, debug);
|
||||
}
|
||||
debug.steps.push(` ✗ No alias match`);
|
||||
debug.tree.alias = { found: false };
|
||||
@@ -846,6 +865,12 @@ export class ReceiptImportService {
|
||||
},
|
||||
debug: any,
|
||||
): Promise<ParsedReceiptItem> {
|
||||
// Kategori-hierarki:
|
||||
// 1. Om produktmatchning redan satte kategori, börjar vi med den
|
||||
// 2. Försöker regel-baserad kategorisering (HIGH confidence: ersätt, fallback: använd om tom)
|
||||
// 3. AI kallas ENDAST om ingen kategori satts än (nextCategory === null)
|
||||
// 4. Guards och hard overrides tillämpas på slutresultatet
|
||||
|
||||
debug.steps.push('Step 3: Categorization');
|
||||
|
||||
const signalText = [item.rawName, item.matchedProductName, item.suggestedProductName]
|
||||
@@ -854,7 +879,7 @@ export class ReceiptImportService {
|
||||
|
||||
let nextCategory = item.categorySuggestion ?? null;
|
||||
|
||||
// ┌─ Försök regel-baserad kategorisering ─────────────────────────────┐
|
||||
// ┌─ STEG 3A: Försök regel-baserad kategorisering ─────────────────────┐
|
||||
debug.steps.push(' Trying rule-based categorization');
|
||||
const ruleResult = this.ruleBasedCategorySuggestion(signalText || item.rawName, context.categories);
|
||||
debug.tree.rule = { found: !!ruleResult, path: ruleResult?.path };
|
||||
@@ -874,9 +899,13 @@ export class ReceiptImportService {
|
||||
debug.steps.push(` ✗ Rule-based miss or lower priority`);
|
||||
}
|
||||
|
||||
// ┌─ AI-kategorisering som fallback ──────────────────────────────────┐
|
||||
// ┌─ STEG 3B: AI-kategorisering ENDAST som fallback ────────────────────┐
|
||||
// AI kallas bara om:
|
||||
// 1) nextCategory är fortfarande NULL (regel-baserad misslyckades/fanns inte)
|
||||
// 2) User har AI aktiverat (context.aiEnabled === true)
|
||||
// AI ersätter ALDRIG redan satta kategorier.
|
||||
if (!nextCategory) {
|
||||
debug.steps.push(' Trying AI categorization');
|
||||
debug.steps.push(' Trying AI categorization (fallback: no category set yet)');
|
||||
if (context.aiEnabled) {
|
||||
debug.tree.ai = { called: true };
|
||||
try {
|
||||
@@ -890,6 +919,9 @@ export class ReceiptImportService {
|
||||
debug.steps.push(` ✗ AI disabled for user`);
|
||||
debug.tree.ai = { called: false };
|
||||
}
|
||||
} else {
|
||||
debug.steps.push(` ⊘ AI skipped (category already set: ${nextCategory.path})`);
|
||||
debug.tree.ai = { called: false, reason: 'category_already_set' };
|
||||
}
|
||||
|
||||
// ┌─ Contradiction guard (final sanity check) ────────────────────────┐
|
||||
@@ -1008,6 +1040,19 @@ export class ReceiptImportService {
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated CLEANUP PENDING (Session 2026-05-09)
|
||||
*
|
||||
* Denna metod är ersatt av unified matcher (matchAndEnrichReceiptItem + enrichCategoryForItem).
|
||||
* Den användes för att köra regel-baserad och AI-kategorisering separat från product-matching.
|
||||
*
|
||||
* Cleanup checklist:
|
||||
* - [ ] Ta bort denna metod
|
||||
* - [ ] Ta bort matchProducts() metod
|
||||
* - [ ] Ta bort findWordMatch() (gammal version)
|
||||
* - [ ] Uppdatera kommentarer
|
||||
* - [ ] Kör full test suite för regression detection
|
||||
*/
|
||||
private async enrichWithAiCategories(items: ParsedReceiptItem[], userId?: number): Promise<ParsedReceiptItem[]> {
|
||||
let categories: Awaited<ReturnType<CategoriesService['findFlattened']>>;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user