chore(import): improve error handling and add flyer integration
- Replace BadRequestException with UnauthorizedException for authentication failures in flyer-import and flyer-selection controllers - Add bulk selection endpoint in FlyerSelectionController for creating multiple selections in one request - Update FlyerSelectionModule to include new FlyerSelectionMatcherService and FlyerSelectionSyncController - Extend FlyerSelectionService with createMany method for bulk operations - Add new DTOs for bulk selection and receipt matching functionality - Update ReceiptImportService to accept FlyerSelectionService dependency and track successful rows - Extend SaveReceiptResponse with flyerAutoSync field for receipt-to-flyer matching results - Add new API paths for flyer import and selection endpoints - Update Flutter UI to include Flyer import tab and adjust tab controller length - Add new domain models and repository methods for flyer import functionality - Update test files to include new FlyerSelectionService dependency - Modify .kilo plan documentation to reflect current system architecture
This commit is contained in:
@@ -1,185 +1,225 @@
|
||||
# Analys av `plan importer willys.md.txt` i projektkontext
|
||||
# Plan: Fortsatt implementation av FlyerImport (autoflöde + UX i `/import` Flutter)
|
||||
|
||||
## Kontext och utgångsläge
|
||||
## Målbild
|
||||
Implementera ett komplett flyer-flöde med så få klick som möjligt där:
|
||||
- användaren markerar planerade inköp i `FlyerImportTab`
|
||||
- kvittoimporten matchar automatiskt mot öppna `FlyerSelection` och sätter `status=bought`
|
||||
- användaren bara behöver ingripa vid osäker matchning
|
||||
|
||||
Den analyserade planen finns i `microservice-importer/plan importer willys.md.txt` och beskriver ett förslag för:
|
||||
- premium-gating av AI
|
||||
- PDF-import av Willys-underlag
|
||||
- produktmatchning mot inventory
|
||||
- receptgenerering med AI (Mistral) eller mallar
|
||||
Detta följer båda underlagen:
|
||||
1) ingen befintlig flyer-UX i `/import` ännu (behöver byggas)
|
||||
2) maximal automation i punkt 3 (sync med kvittoimport)
|
||||
|
||||
Jämfört med faktisk arkitektur i era tre repos:
|
||||
- `recipe-app`: äger användare, premium/AI-flaggor, inventory, recept, auth, adminflöden och databas (Prisma + MariaDB).
|
||||
- `microservice-importer`: stateless import/parsing utan databas; ansvarar för quick-import, markdown-parse och receipt-parse.
|
||||
- `recipe-gitea-runner`: CI-exekvering med labels `backend-node24` och `flutter-3-41`.
|
||||
---
|
||||
|
||||
## Sammanfattande bedömning
|
||||
## Principer för “så få klick som möjligt”
|
||||
- Default är automation: matcha och uppdatera utan extra dialoger.
|
||||
- UI ska vara “review first, edit only when needed”.
|
||||
- Endast två manuella åtgärder i happy path:
|
||||
1. markera planerade flyer-varor
|
||||
2. importera kvitto
|
||||
- Manuell override ska finnas men inte blockera flödet.
|
||||
|
||||
Planen är ambitiös men **inte direkt kompatibel** med nuvarande systemdesign. Den blandar ansvar mellan tjänster, använder kodmönster som avviker från era etablerade stackval och återinför funktioner ni redan implementerat i annan form.
|
||||
---
|
||||
|
||||
Hög nivå:
|
||||
- Bra: tydlig stegstruktur, fallback-idé (AI -> templates), fokus på robust importflöde.
|
||||
- Problem: arkitekturdrift (DB i importer), felaktig domänmodell för era nuvarande User/Product-fält, Express-exempel i NestJS-miljö, svag validering/säkerhet i flera snippets.
|
||||
## Fas 1: Backend-kontrakt för auto-sync
|
||||
|
||||
## Styrkor att återanvända
|
||||
### 1.1 Nya/utökade endpoints
|
||||
Implementera i `backend/src/flyer-selection`:
|
||||
|
||||
- Tydlig domänuppdelning i delsteg: extrahering -> parsing -> matchning -> generering.
|
||||
- Fallback-first-princip vid AI-fel (ligger i linje med era befintliga principer).
|
||||
- Intention att normalisera och strukturera råtext innan beslut i senare led.
|
||||
- Fokus på svenska enheter/uttryck som passar era kvittoflöden.
|
||||
- `POST /flyer-selections/receipt-match-preview`
|
||||
- Input: kvittorader (normaliserad struktur), `weekKey` (optional), `sessionId` (optional)
|
||||
- Output: matchförslag per kvittorad + confidence + reasonCodes + kandidat-selection
|
||||
- Används för transparent UI-annotering före commit
|
||||
|
||||
## Kritiska gap mot befintlig arkitektur
|
||||
- `POST /flyer-selections/receipt-match-commit`
|
||||
- Input: samma payload + optional overrides
|
||||
- Output: antal uppdaterade selections, listor över `bought`, `unmatched`, `ambiguous`
|
||||
- Utför transaktionell statusuppdatering (`planned -> bought`)
|
||||
|
||||
1. **Fel placerat ansvar (största gapet)**
|
||||
- Planen föreslår Prisma/DB i importer-flödet (`prisma.user`, `encryptedData.create`, premiumfält m.m.).
|
||||
- Er importer är dokumenterat stateless och DB-lös.
|
||||
- Rekommendation: all user/premium/inventory/recipe persistence ska ligga i `recipe-app` backend, inte i `microservice-importer`.
|
||||
- `GET /flyer-selections/open`
|
||||
- Query: `weekKey`, `retailer`, pagination
|
||||
- Returnerar öppna selections (`status=planned`) för snabb klienthämtning
|
||||
|
||||
2. **Premium-modell avviker från faktisk datamodell**
|
||||
- Planen använder `is_premium` och `premium_expiry_date`.
|
||||
- I `recipe-app` används `isPremium` + `aiEngineEnabled` redan, med admin-styrning och JWT-scope.
|
||||
- Rekommendation: återanvänd befintliga fält/guards; undvik parallel premiummodell.
|
||||
Notering: Behåll befintliga CRUD-rutter under `/flyer-sessions/:sessionId/selections`.
|
||||
|
||||
3. **Framework mismatch (Express vs NestJS)**
|
||||
- Planen innehåller Express-routerexempel medan repos är NestJS-moduler/controllers/services.
|
||||
- Rekommendation: all ny implementation bör följa NestJS module/service/controller + DTO + class-validator.
|
||||
### 1.2 Matchningsmotor (service-nivå)
|
||||
I `FlyerSelectionService` lägg till en intern matcher med prioriterad strategi:
|
||||
1. `productId` exact (högsta prio)
|
||||
2. normalized name exact
|
||||
3. alias/ordöverlap (token)
|
||||
4. quantity/unit-stöd som förstärkning (inte ensam källa)
|
||||
|
||||
4. **Datamodell-krockar**
|
||||
- Planen antar tabeller/fält som inte finns i nuvarande schema (`encryptedData`, snake_case-kolumner osv).
|
||||
- Rekommendation: mappa mot faktiska modeller (`User`, `Product`, `InventoryItem`, `Recipe*`) eller skapa tydlig migrationplan med namngiven adapter.
|
||||
Regler:
|
||||
- En `FlyerSelection` kan bara konsumeras en gång per commit.
|
||||
- Confidence-trösklar:
|
||||
- `>=0.90`: auto-commit-kandidat
|
||||
- `0.70-0.89`: ambiguous (kräver override för commit)
|
||||
- `<0.70`: unmatched
|
||||
- Returnera alltid `matchedVia`, `confidence`, `reasonCodes`.
|
||||
|
||||
5. **Överlapp med befintlig funktionalitet**
|
||||
- Ni har redan importerad kvittoparsning med regelmotor + AI-fallback och premium/ai-scope.
|
||||
- Rekommendation: undvik ”nytt parallellt flöde”; bygg som utökning av befintliga receipt/import pipelines.
|
||||
### 1.3 Datamodell-justeringar (om behövs)
|
||||
Nuvarande schema räcker i stort, men planera följande icke-blockerande förbättringar:
|
||||
- `FlyerSelection`:
|
||||
- `boughtAt DateTime?`
|
||||
- `boughtSource String?` (t.ex. `receipt_auto`, `receipt_manual`)
|
||||
- `receiptImportBatchId String?` för spårbarhet
|
||||
- Index:
|
||||
- `(userId, status, updatedAt)` för snabb hämtning av öppna poster
|
||||
|
||||
## Tekniska förbättringar av själva planen
|
||||
Migration i separat steg efter kod.
|
||||
|
||||
### A. Arkitekturförbättringar (måste prioriteras)
|
||||
### 1.4 Säkerhet och robusthet
|
||||
- Validera alltid user-scope i alla nya endpoints.
|
||||
- Rate-limit på match-endpoints (liknande befintlig throttle-nivå).
|
||||
- Transaktion (`prisma.$transaction`) för commit så statusuppdatering blir atomisk.
|
||||
- Idempotens: commit med samma `receiptImportBatchId` ska inte dubbeluppdatera.
|
||||
|
||||
- **Inför tydligt kontrakt mellan tjänsterna**
|
||||
- `microservice-importer`: parse/normalize only (ingen userstate).
|
||||
- `recipe-app`: auth, premium-gating, produktmatchning, persistence.
|
||||
- **Skapa explicit API-kontrakt för kampanjblad/Willys**
|
||||
- ny endpoint i importer, exempel: `POST /api/flyer/parse`.
|
||||
- svar med strikt schema: produkter, erbjudandeflaggor, normaliserade mått/enheter, confidence.
|
||||
- **Beslut i recipe-app**
|
||||
- besluta AI/template på basis av `isPremium && aiEngineEnabled`.
|
||||
- lagra endast i recipe-app DB.
|
||||
---
|
||||
|
||||
### B. Datakontrakt och validering
|
||||
## Fas 2: Flutter UX i `/import` (ny flyer-tab)
|
||||
|
||||
- Ersätt `any` med typed DTO/interfaces i båda repos.
|
||||
- Lägg till strikt validering (zod eller class-validator) för:
|
||||
- parsed flyer rows
|
||||
- matched product payload
|
||||
- generated recipe payload
|
||||
- Definiera versionerat kontrakt (`v1`) så importer och app kan deployas oberoende.
|
||||
### 2.1 Lägg till `FlyerImportTab`
|
||||
Uppdatera `ImportScreen` så tabbar blir:
|
||||
1. Recept
|
||||
2. Kvitto
|
||||
3. Flyer
|
||||
|
||||
### C. Parser-kvalitet och robusthet (Willys-specifikt)
|
||||
Skapa:
|
||||
- `flutter/lib/features/import/presentation/flyer_import_tab.dart`
|
||||
- `flutter/lib/features/import/data/flyer_import_repository.dart`
|
||||
- `flutter/lib/features/import/data/flyer_import_providers.dart`
|
||||
- `flutter/lib/features/import/domain/flyer_item.dart`
|
||||
- `flutter/lib/features/import/domain/flyer_selection.dart`
|
||||
|
||||
- Nuvarande regex-idé är för skör för verkliga PDF-varianter.
|
||||
- Förbättra med pipeline:
|
||||
1) textblock-normalisering
|
||||
2) radklassificering (kategori, produkt, prisrad, metadata)
|
||||
3) enhetsnormalisering (`förp`, `st`, `kg`)
|
||||
4) probabilistisk matchscore per fält
|
||||
- Lägg in rule priority + fallback AI bara för osäkra rader (som ni redan gör i receipt flow).
|
||||
### 2.2 Flyer-tabens minimalklick-flöde
|
||||
Steg i UI:
|
||||
1. Upload flyerfil
|
||||
2. Visa parserader med förvald checkbox för matchade varor
|
||||
3. Primär CTA: `Planera markerade` (bulk-create/upsert)
|
||||
4. Direkt visning av statuschips (`planned/bought/skipped`)
|
||||
|
||||
### D. Matchning mot inventory
|
||||
Klickoptimering:
|
||||
- Förifyll `plannedQuantity/plannedUnit` från flyerdata.
|
||||
- Batch-upsert selections i ett enda API-anrop.
|
||||
- Visa inline varningar istället för modaler där möjligt.
|
||||
|
||||
- Planens fuzzy-match på 0.6 riskerar falska positiva.
|
||||
- Förslag:
|
||||
- kombinera alias > exact-normalized > token-similarity > levenshtein.
|
||||
- category-guardrails (finns redan i receipt-flöde, återanvänd).
|
||||
- trösklar per kategori (mejeri/kött behöver striktare gränser än exotiska varor).
|
||||
- returnera `matchedVia`, `confidence`, `reasonCodes` för UI-debugg och lärande.
|
||||
### 2.3 Statusöversikt
|
||||
I flyer-tabben visa sektioner:
|
||||
- `Planerade` (öppna)
|
||||
- `Nyligen köpta` (autouppdaterade från kvitto)
|
||||
- `Ej matchade vid senaste kvitto` (för snabb manuell hantering)
|
||||
|
||||
### E. AI-generering
|
||||
---
|
||||
|
||||
- Planen gör fri JSON-parsning från modelltext; hög risk för parse-fel.
|
||||
- Förslag:
|
||||
- använd strikt output schema + reparationssteg vid JSON-avvikelse.
|
||||
- lägg budget/timeouts/retries per request.
|
||||
- prompta på begränsad produktmängd (top-N relevanta) för lägre kostnad/latens.
|
||||
- logga token-användning och feltyper för cost observability.
|
||||
## Fas 3: Integrera auto-sync i `ReceiptImportTab`
|
||||
|
||||
### F. Säkerhet
|
||||
### 3.1 Hook efter receipt parse
|
||||
I befintlig `_submit()` i `receipt_import_tab.dart`:
|
||||
1. importera kvitto som idag
|
||||
2. anropa `receipt-match-preview`
|
||||
3. annotera UI-rader med matchstatus (icon/chip)
|
||||
4. vid `Lägg till markerade`: anropa `receipt-match-commit` parallellt/sekventiellt med saveReceipt
|
||||
|
||||
- Behåll premium-kontroll i backend (ej klient).
|
||||
- Lägg rate limiting även på nya flyer-endpoints.
|
||||
- Undvik filsystemberoende (`multer dest + fs.unlinkSync`) om möjligt; använd bufferbaserad pipeline.
|
||||
- Lägg explicit content-type + storleksgräns + filsignature-validering.
|
||||
### 3.2 Zero-click commit i happy path
|
||||
Defaultbeteende vid `Lägg till markerade`:
|
||||
- auto-committa alla matcher med confidence `>=0.90`
|
||||
- lämna ambiguous som `planned`
|
||||
- visa en kompakt snackbar:
|
||||
- `2 planerade markerades som köpta`
|
||||
- `1 kräver manuell kontroll`
|
||||
|
||||
### G. Drift och CI (recipe-gitea-runner)
|
||||
Ingen extra dialog i standardfall.
|
||||
|
||||
- Lägg nya testjobb med labels ni redan har:
|
||||
- `backend-node24`: contract tests mellan app/importer.
|
||||
- `backend-node24`: parser regression suite med fixtures från riktiga Willys-underlag.
|
||||
- Lägg minimikrav i CI:
|
||||
- typecheck
|
||||
- unit tests parser + matcher
|
||||
- contract tests importer <-> recipe-app
|
||||
- build
|
||||
- Publicera artifact med parser-rapport (precision/recall på fixture-set) för varje PR.
|
||||
### 3.3 Manuell override (endast vid behov)
|
||||
Lägg till valfri expandrad i resultatlistan:
|
||||
- “Föreslagen flyer-match” + knapp `Bekräfta ändå`
|
||||
- används endast för ambiguous fall
|
||||
|
||||
## Konkreta optimeringar per repo
|
||||
---
|
||||
|
||||
### 1) `microservice-importer`
|
||||
## Fas 4: API- och UI-detaljer för låg friktion
|
||||
|
||||
- Lägg till separat modul `flyer-parsing` istället för att återanvända receipt rakt av.
|
||||
- Returnera endast normaliserad och validerad struktur, aldrig användarspecifika beslut.
|
||||
- Återanvänd befintlig robusthet:
|
||||
- fallback parsing
|
||||
- timeout/retry-mönster
|
||||
- global exception shape.
|
||||
- Bygg fixture-driven tester för Willys-format (varianter med OCR-brus, multipack, kampanjtext).
|
||||
### 4.1 Payload-standard (receipt -> matcher)
|
||||
Standardisera kvittorad till:
|
||||
- `rowId` (lokalt index eller UUID)
|
||||
- `rawName`
|
||||
- `normalizedName`
|
||||
- `productId` (om redan mappad)
|
||||
- `quantity`
|
||||
- `unit`
|
||||
- `price`
|
||||
|
||||
### 2) `recipe-app`
|
||||
### 4.2 UX-copy
|
||||
Konsekventa texter i UI:
|
||||
- `Automatchad mot flyer`
|
||||
- `Osäker matchning`
|
||||
- `Ej matchad`
|
||||
|
||||
- Implementera orkestreringstjänst för flyerimport:
|
||||
- anropa importer
|
||||
- matcha mot user-scopade produkter
|
||||
- premium-gata AI-recept (isPremium + aiEngineEnabled)
|
||||
- spara recept via befintliga modeller
|
||||
- Exponera adminfeature toggle för ”flyer-recipe-generation” (separation från övrig AI om ni vill kontrollera rollout).
|
||||
- Lägg telemetri per steg: parse time, match confidence distribution, AI fallback rate.
|
||||
### 4.3 WeekKey-hantering
|
||||
Fallback-ordning vid matchning:
|
||||
1. explicit `sessionId`
|
||||
2. explicit `weekKey`
|
||||
3. server beräknar aktuell `weekKey`
|
||||
|
||||
### 3) `recipe-gitea-runner`
|
||||
Detta minimerar klientlogik och fel.
|
||||
|
||||
- Säkra att workflow i `recipe-app` inkluderar integrationstest mot importer (mockad eller ephemeral service).
|
||||
- Lägg nattlig regressionkörning för parser-fixtures för att fånga drift i regex/regler.
|
||||
- Behåll labels som idag; komplettera med tydligare jobbseparation i CI (quick PR vs full push).
|
||||
---
|
||||
|
||||
## Prioriterad implementeringsplan (reviderad)
|
||||
## Fas 5: Test och kvalitetssäkring
|
||||
|
||||
1. **Målbild/ansvar (P0)**
|
||||
- Fastställ och dokumentera kontrakt: importer parser-only, app stateful orchestration.
|
||||
### 5.1 Backend
|
||||
- Unit-tester för matcher-regler och confidence-nivåer
|
||||
- Service-tester för commit-idempotens
|
||||
- Controller-e2e för user-scope + throttling + felkoder
|
||||
- Prisma-transaktionsscenarion (dubbelklick/duplicerat commit)
|
||||
|
||||
2. **Kontrakt + DTO (P0)**
|
||||
- Definiera `FlyerParseResponse v1` och valideringsregler.
|
||||
### 5.2 Flutter
|
||||
- Widget-tester för:
|
||||
- `FlyerImportTab` listning/bulk-planering
|
||||
- kvitto-rad med automatch-chip
|
||||
- Integrationstester för `ReceiptImportTab` + auto-sync callback
|
||||
|
||||
3. **Importer-modul (P1)**
|
||||
- Implementera Willys/flyer parser i `microservice-importer` med tester + fixtures.
|
||||
### 5.3 Acceptanskriterier (måste uppfyllas)
|
||||
- Happy path kräver max 2 aktiva klick från planering till auto-bought.
|
||||
- Minst 90% av high-confidence-matchningar autouppdateras korrekt i test-fixtures.
|
||||
- Inga writes till `Product/Inventory` sker i flyer-planeringsfas.
|
||||
|
||||
4. **Recipe-app orkestrering (P1)**
|
||||
- Ny service i backend som mappar parse-resultat till matchning + recipe generation.
|
||||
---
|
||||
|
||||
5. **Premium-gating harmonisering (P1)**
|
||||
- Använd enbart `isPremium` + `aiEngineEnabled`; ta bort/undvik expiry-logik om den inte behövs produktmässigt.
|
||||
## Fas 6: Gradvis lansering
|
||||
|
||||
6. **Observability + säkerhet (P1)**
|
||||
- Metrics, structured logs, rate limits, upload guards.
|
||||
1. Backend-endpoints bakom feature flag: `flyerReceiptAutoSyncEnabled`
|
||||
2. Aktivera för intern/test-användare först
|
||||
3. Mät:
|
||||
- andel auto-match
|
||||
- andel ambiguous
|
||||
- manuell override-frekvens
|
||||
4. Finjustera thresholds innan full rollout
|
||||
|
||||
7. **CI-utbyggnad (P2)**
|
||||
- Contract tests + parser regression suite i Gitea workflows.
|
||||
---
|
||||
|
||||
## Risker om planen implementeras oförändrad
|
||||
## Implementeringsordning (konkret)
|
||||
1. Backend: matcher + preview/commit endpoints
|
||||
2. Backend: idempotens + spårbarhetsfält + migration
|
||||
3. Flutter: ny `FlyerImportTab` + repository/providers
|
||||
4. Flutter: integrera preview/commit i `ReceiptImportTab`
|
||||
5. Tester backend + Flutter
|
||||
6. Feature flag rollout
|
||||
|
||||
- Arkitekturspret: dubbla sanningskällor för premium och recipes.
|
||||
- Ökad driftkomplexitet: importer blir stateful och svårare att skala/deploya.
|
||||
- Regressionsrisk: ny kod duplicerar befintlig receipt/importlogik.
|
||||
- Säkerhets- och datakvalitetsrisk: svag typing/validering + osäker JSON-parsning från AI-svar.
|
||||
---
|
||||
|
||||
## Rekommenderad riktning (kort)
|
||||
## Risker och mitigering
|
||||
- Felmatchningar: håll konservativ tröskel och auto-commit bara vid hög confidence.
|
||||
- Dubbla commits: idempotensnyckel + transaktion.
|
||||
- UX-brus: visa detaljer först vid ambiguous, inte i happy path.
|
||||
- Prestanda: batcha matchning och undvik N+1-frågor via prefetch av öppna selections.
|
||||
|
||||
Använd planen som **idékatalog**, inte som direkt implementation. Behåll er nuvarande ansvarsfördelning mellan repos, bygg Willys-stödet som en ny parserdomän i `microservice-importer`, och låt `recipe-app` fortsätta vara enda platsen för användarlogik, premiumbeslut och datalagring. Detta ger lägst risk och bäst kompatibilitet med er nuvarande kodbas, driftmodell och CI-upplägg.
|
||||
---
|
||||
|
||||
## Definition of Done
|
||||
- `/import` har en fungerande `FlyerImportTab`.
|
||||
- Kvittoimport auto-synkar mot `FlyerSelection` med minimal friktion.
|
||||
- `planned -> bought` uppdateras automatiskt för high-confidence.
|
||||
- Ambiguous fall kan hanteras manuellt utan att blockera flödet.
|
||||
- Tester gröna och feature flag klar för kontrollerad utrullning.
|
||||
|
||||
Reference in New Issue
Block a user