feat: simplify receipt import matching logic and enhance trace logging
Test Suite / test (24.15.0) (push) Has been cancelled

This commit is contained in:
Nils-Johan Gynther
2026-05-09 15:17:00 +02:00
parent 1966a92a87
commit 97e7b09bcd
4 changed files with 76 additions and 1 deletions
+6
View File
@@ -25,10 +25,16 @@ All detaljhistorik och djup teknisk bakgrund finns i respektive tekniska dokumen
- **Receptsakerhet och dataintegritet:** aktiv-produktvalidering, transaktion vid update, orphan-fil-cleanup och striktare owner-hantering av legacy-recept.
- **Alternativa ingredienser (Option A):** lagring i `alternativeProductIds` (JSON), matchning mot flera alternativ och lagerkoll som inkluderar alternativ.
- **Flutter felhantering:** kopierbara felmeddelanden i snackbar + global textselektion for enklare support/felsokning.
- **Kvittoimport — Simplified Matching Logic (2026-05-09):** Unified matcher som konsoliderar product matching + categorization i en explicit flödestre. Eliminar split mellan `matchProducts()` och `enrichWithAiCategories()`. Ger tydligare trace-logging och bättre debuggability för varför ett item matchades på ett visst sätt. Systemet är nu mer underhållsbart och framtida förbättringar på matching/kategorisering går snabbare.
## Huvudprioriteringar
1. Aliasstrategi i kvittoimport: user-scope som standard, global fallback via admin.
2. **[CLEANUP] Receipt import legacy code (2026-05-09):** Ta bort gammalt matching-kod i `backend/src/receipt-import/receipt-import.service.ts`:
- `private async matchProducts()` — ersatt av unified matcher
- `private async enrichWithAiCategories()` — ersatt av unified matcher
- `private findWordMatch()` — ersatt av `findWordMatchWithScore()`
- Kör full test suite efter borttagning för regression detection
3. Stabilisera bildimport och diagnostik i alla miljoer.
4. Lokalisera kvarvarande stora Flutter-vyer i import/inventarie.
5. Forbereda avancerad AI-integration med tydlig loggning/audit.
+68
View File
@@ -15,6 +15,74 @@ Se även:
- [TEKNISK_BESKRIVNING.md](TEKNISK_BESKRIVNING.md) för teknisk genomgång.
- [AI-FUNKTIONER.md](_archive/microservice-ai/AI-FUNKTIONER.md) för AI-översikt.
# Session 2026-05-09: Simplified Matching Logic (Consolidation Phase 3)
Denna session har genomfört den tredje omarbetningsfasen som konsoliderar receipt-import matching-flödet:
## Genomförda förbättringar
### 1. Unified Matcher (`matchAndEnrichReceiptItem`)
- **Tidigare:** Matching var splittrad mellan `matchProducts()` (alias + word-match) och `enrichWithAiCategories()` (~850 rader).
- **Nu:** En central metod som gör allt i explicit ordning:
1. **Alias lookup** — certifierad match från ReceiptAlias
2. **Word-match** — fuzzy produktmatchning med scoring
3. **Categorization** — regel-baserad → AI (fallback) → guards → hard overrides
### 2. Improved Context Management (`prepareMatchingContext`)
- Alla data (aliases, produkter, unit mappings, categories) hämtas **en gång** per receipt
- Parallell loading med `Promise.all()` — högre performance än tidigare sekventiell loading
- Context passeras till alla items för effektiv återanvändning
### 3. Better Decision Logging (`enrichCategoryForItem`)
- Structured trace med steg-för-steg-loggning:
- ✓ Rule-based hits with path name
- ✓ AI suggestions
- ⚠️ Guard remaps (contradiction resolution)
- ⚠️ Hard overrides (special cases)
- ✅ Final decision with confidence level
- Debug-träd med alla decision points
- Kan aktiveras per receipt via `RECEIPT_TRACE_DECISIONS` env-var
### 4. Simplified parseReceipt Flow
```
Before:
parseReceipt() → matchProducts() → enrichWithAiCategories()
After:
parseReceipt() → prepareContext() → matchAndEnrichReceiptItem() (per item)
```
## Technical Details
**Nya metoder:**
- `matchAndEnrichReceiptItem()` — unified matching pipeline per item
- `prepareMatchingContext()` — one-time context preparation
- `enrichCategoryForItem()` — consolidated categorization logic
- `findWordMatchWithScore()` — refactored word matching with explicit score return
**Datatyper:**
- `MatchDecision` interface för strukturerad result (i `dto/match-decision.ts`)
**Build Status:**
- ✅ Full TypeScript compilation successful
- ✅ No breaking changes to existing API contracts
## Cleanup Pending
De gamla metoderna är fortfarande i koden men kan nu ta bort:
- `private async matchProducts()` — deprecated by unified matcher
- `private async enrichWithAiCategories()` — deprecated by unified matcher
- `private findWordMatch()` — replaced by findWordMatchWithScore()
**Cleanup tasks:**
1. Ta bort `matchProducts()`
2. Ta bort `enrichWithAiCategories()`
3. Ta bort `findWordMatch()` (gammal version)
4. Uppdatera kommentarer/docstrings
5. Kör full test suite för regression detection
Cleanup bör göras i nästa session för att ge tid för monitoring/QA.
# Plan för omarbetning av receptimport
# 2026-05-07: Säkerhets- och deployförbättringar
+1
View File
@@ -91,6 +91,7 @@ Detta dokument är skrivet för systemadministratörer och programmerare. Fokus
- **PDF-kvittoimport:** `pdf-parse` importeras med `require()` (CJS); `pdfjs-dist/legacy/build/pdf.js` som fallback undviker `DOMMatrix`-fel.
- **Felkods-forwarding:** `receipt-import.service.ts` kastar `ServiceUnavailableException` vid 503/429 från importer-api (tidigare alltid 400).
- **AI-skippning (PDF):** `looksLikeReceiptProductLine()` i importer-api filtrerar rader utan siffra — minskar Mistral-anrop drastiskt för vanliga PDF-kvitton.
- **Simplified Matching Logic (2026-05-09):** Unified matcher konsoliderar receipt-import matching och categorization. Tidigare var logiken splittrad mellan `matchProducts()` och `enrichWithAiCategories()`; nu är allt i `matchAndEnrichReceiptItem()` med explicit steg: Alias lookup → Word-match → Categorization (Rules → AI → Guards → Hard overrides). Bättre trace-logging och debuggability. Context-loading görs en gång per receipt (parallell loading) istället för repeated queries.
### Driftnotering
Verifiera efter deploy att seed-körning inkluderar uppdaterat kategoriträd och att kvittoflödet använder den senaste regelbaserade parserlogiken.
@@ -475,7 +475,7 @@ export class ReceiptImportService {
create: {
receiptName: normalizedReceiptName,
productId,
ownerId: dto.isAdminLearning ? undefined : userId,
ownerId: (dto.isAdminLearning ? null : userId || null) as any,
isGlobal: dto.isAdminLearning ? true : false,
},
});