Add comprehensive documentation for Flutter frontend migration and backend review
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
- Introduced user guide for Flutter frontend in README.md, detailing user flows and recent improvements. - Created next steps roadmap for Flutter migration in next_steps_flutter.md, outlining current tasks and priorities. - Developed technical description for Flutter frontend in teknisk_beskrivning_flutter.md, covering architecture and security status. - Removed outdated migration documentation for Prisma P3009 and added recovery steps for failed migrations in migrering-MSI.md. - Established a release checklist for product launches in produktlansering.md, ensuring security and stability measures are met. - Formulated a systematic backend review and optimization plan in review_backend.md, focusing on reducing complexity and improving performance.
This commit is contained in:
@@ -0,0 +1,820 @@
|
||||
# Session 2026-05-06: Refaktor och user-scoped AI
|
||||
|
||||
Denna session har genomfört:
|
||||
- **User-scoped AI-fallback:** AI-förslag för ingrediens- och kategorimatchning är nu individuellt aktiverbara per användare (premium).
|
||||
- **Admin-toggles:** Backend och UI har stöd för att admin kan slå på/av AI per användare.
|
||||
- **Premium-scope:** Flutter och backend respekterar premium-flagga och AI-tillgång i alla flöden.
|
||||
- **Rematch och manuell produkt:** Flutter har stöd för ommatchning och manuell produkt vid import.
|
||||
- **Lessons learned:**
|
||||
- Nullable propagation i Prisma och DTO:er kräver noggrannhet.
|
||||
- Fallback-first AI och tydlig separation av analyskontrakt ger robustare flöden.
|
||||
- User-scoped features kräver ownerId/userId-filter i all logik.
|
||||
- Manuella migrationer kan krävas vid DB-problem (se migrering-MSI.md).
|
||||
|
||||
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
|
||||
|
||||
- **Inventory är nu user-scopad:** Alla inventory-operationer kräver och filtrerar på userId i backend (schema, migration, service, controller, tester).
|
||||
- **IDOR-skydd för inventory:** Det är nu omöjligt för användare att läsa eller ändra andras inventarieposter. Tester verifierar att åtkomst nekas vid försök till IDOR.
|
||||
- **.gitignore och deploy-hygien:** backend/dist och backend/tsconfig.tsbuildinfo ignoreras och är ej längre spårade i git. .env och .env.* ignoreras, men .env.example finns och är uppdaterad.
|
||||
- **CI/CD-härdning:** npm audit och prisma validate körs i pipeline. Alla tester och byggen måste passera.
|
||||
|
||||
## Bakgrund
|
||||
|
||||
Nuvarande importflöde blandar ihop två olika ansvar:
|
||||
|
||||
1. Att importera och spara receptet så troget källan som möjligt.
|
||||
2. Att matcha receptets ingredienser mot interna produkter.
|
||||
|
||||
Det som fungerar bra i dag är:
|
||||
- webskrapning
|
||||
- import med text och bild
|
||||
- presentation av receptbild och text när receptet väl är sparat
|
||||
|
||||
Det som fungerar sämre är:
|
||||
- ingrediensimporten
|
||||
- tidig produktmatchning i importsteget
|
||||
- att receptets innehåll förvrängs eller tappar ingredienser om matchning saknas
|
||||
|
||||
## Målbild
|
||||
|
||||
Importsteget ska bara göra detta:
|
||||
- hämta receptets titel, bild, beskrivning, instruktioner och ingrediensrader
|
||||
- strukturera ingrediensraderna så gott det går
|
||||
- spara receptet även när ingen produktmatchning finns
|
||||
- låta användaren granska receptet utan att behöva välja produkter
|
||||
|
||||
Ett senare analyssteg ska göra detta:
|
||||
- jämföra receptets ingredienser med inventory och pantry
|
||||
- avgöra exakt träff, trolig ersättningsvara eller saknad vara
|
||||
- ge underlag för shoppinglista
|
||||
- ge underlag för AI-förslag och substitutionsförslag
|
||||
|
||||
## Arkitekturprincip
|
||||
|
||||
Receptet ska vara källtroget.
|
||||
Produktmatchning ska vara ett separat lager ovanpå receptet.
|
||||
|
||||
Det innebär att vi behöver separera:
|
||||
- receptets råa ingredienser
|
||||
- användarens lager och skafferi
|
||||
- matchnings- och analyslogik
|
||||
|
||||
## Föreslagen datamodell
|
||||
|
||||
### Nuvarande problem
|
||||
|
||||
I dag kräver `RecipeIngredient` i Prisma:
|
||||
- `productId`
|
||||
- `quantity`
|
||||
- `unit`
|
||||
|
||||
Det gör att en ingrediens inte kan sparas om vi inte redan vet vilken intern produkt den motsvarar.
|
||||
|
||||
### Ny målmodell
|
||||
|
||||
Inför två nivåer för ingredienser:
|
||||
|
||||
1. Receptets egen ingrediensrad
|
||||
2. Matchning/analys mot interna produkter
|
||||
|
||||
### Förslag A: utöka befintlig `RecipeIngredient`
|
||||
|
||||
Det enklaste spåret är att behålla `RecipeIngredient`, men göra den receptcentrisk i stället för produktcentrisk.
|
||||
|
||||
Nya eller ändrade fält:
|
||||
- `rawLine String?`
|
||||
- `rawName String`
|
||||
- `productId Int?` i stället för required
|
||||
- `quantity Decimal?`
|
||||
- `unit String?`
|
||||
- `note String?`
|
||||
- `matchConfidence Float?`
|
||||
- `matchSource String?` (`heuristic`, `ai`, `manual`)
|
||||
- `analysisStatus String?` (`unmatched`, `exact`, `substitutable`, `missing`)
|
||||
|
||||
Behåll:
|
||||
- `alternativeProductIds Json?`
|
||||
- `recipeId`
|
||||
|
||||
### Förslag B: dela upp i två tabeller
|
||||
|
||||
Mer robust på sikt men större ombyggnad:
|
||||
- `RecipeIngredient` blir rå ingrediensrad
|
||||
- `RecipeIngredientMatch` blir separat matchningslager
|
||||
|
||||
Första implementationen bör använda Förslag A för lägre risk.
|
||||
|
||||
## Rekommenderad genomförandeordning
|
||||
|
||||
1. Gör datamodellen tolerant för omatchade ingredienser.
|
||||
2. Ändra backend så att import och sparning fungerar utan produktmatchning.
|
||||
3. Förenkla frontendens importgranskning.
|
||||
4. Lägg till separat analyssteg mot inventory och pantry.
|
||||
5. Lägg till AI-stöd först när grundflödet är stabilt.
|
||||
|
||||
---
|
||||
|
||||
# Fil-för-fil-plan
|
||||
|
||||
## 1. Databas och Prisma
|
||||
|
||||
### Fil: `backend/prisma/schema.prisma`
|
||||
|
||||
### Ändringar
|
||||
- Gör `RecipeIngredient.productId` optional.
|
||||
- Gör `RecipeIngredient.product` optional relation.
|
||||
- Gör `quantity` optional.
|
||||
- Gör `unit` optional.
|
||||
- Lägg till `rawName`.
|
||||
- Lägg till `rawLine`.
|
||||
- Lägg till `matchConfidence`.
|
||||
- Lägg till `matchSource`.
|
||||
- Lägg till `analysisStatus`.
|
||||
|
||||
### Exempel på målmodell
|
||||
|
||||
```prisma
|
||||
model RecipeIngredient {
|
||||
id Int @id @default(autoincrement())
|
||||
recipe Recipe @relation(fields: [recipeId], references: [id])
|
||||
recipeId Int
|
||||
product Product? @relation(fields: [productId], references: [id])
|
||||
productId Int?
|
||||
rawName String
|
||||
rawLine String? @db.Text
|
||||
quantity Decimal? @db.Decimal(10, 2)
|
||||
unit String?
|
||||
note String?
|
||||
alternativeProductIds Json?
|
||||
matchConfidence Float?
|
||||
matchSource String?
|
||||
analysisStatus String?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
}
|
||||
```
|
||||
|
||||
### Leverabler
|
||||
- Prisma-schema uppdaterat
|
||||
- ny migration skapad
|
||||
- Prisma Client regenererad
|
||||
|
||||
### Risk
|
||||
- All kod som i dag antar att `productId`, `quantity` och `unit` alltid finns måste uppdateras
|
||||
|
||||
---
|
||||
|
||||
## 2. DTO:er för receptskapande
|
||||
|
||||
### Fil: `backend/src/recipes/dto/create-recipe.dto.ts`
|
||||
|
||||
### Nuvarande problem
|
||||
DTO:n kräver att varje ingrediens har:
|
||||
- `productId`
|
||||
- `quantity`
|
||||
- `unit`
|
||||
|
||||
Det blockerar trogen import.
|
||||
|
||||
### Ändringar
|
||||
- Gör `productId` optional.
|
||||
- Gör `quantity` optional.
|
||||
- Gör `unit` optional.
|
||||
- Lägg till `rawName` som required.
|
||||
- Lägg till `rawLine` som optional.
|
||||
- Lägg till `matchConfidence` och `matchSource` som optional om vi vill bevara preliminär heuristik.
|
||||
|
||||
### Mål
|
||||
Backend ska kunna acceptera ett recept där ingrediensen bara är:
|
||||
- `rawName`
|
||||
- eventuellt `rawLine`
|
||||
- eventuellt `quantity`, `unit`, `note`
|
||||
|
||||
### Konsekvens
|
||||
`ArrayMinSize(1)` kan sannolikt vara kvar, men valideringen måste vara råingrediens-baserad i stället för produkt-baserad.
|
||||
|
||||
---
|
||||
|
||||
## 3. DTO för parse-resultat
|
||||
|
||||
### Fil: `backend/src/recipes/dto/parse-markdown.dto.ts`
|
||||
|
||||
### Ändringar
|
||||
Ingen stor strukturändring behövs här om endpointen fortsatt bara tar emot markdown.
|
||||
|
||||
### Eventuell komplettering
|
||||
Om importen senare ska stödja flera källor mer explicit kan ett `sourceType` läggas till:
|
||||
- `markdown`
|
||||
- `web`
|
||||
- `ocr`
|
||||
|
||||
Detta är inte nödvändigt i första omgången.
|
||||
|
||||
---
|
||||
|
||||
## 4. Receptservice: parse ska sluta göra för mycket
|
||||
|
||||
### Fil: `backend/src/recipes/recipes.service.ts`
|
||||
|
||||
### Nuvarande problem
|
||||
`parseMarkdown()` gör i dag flera saker:
|
||||
- anropar importer-api eller lokal parser
|
||||
- hämtar alla produkter
|
||||
- kör heuristisk matchning med normalisering och Levenshtein
|
||||
- returnerar suggestions
|
||||
|
||||
Det gör att importsteget implicit blir ett matchningssteg.
|
||||
|
||||
### Rekommenderad ändring i etapp 1
|
||||
Behåll parse men ändra dess ansvar:
|
||||
- parse ska alltid returnera råingredienser
|
||||
- suggestions får vara optional och sekundära
|
||||
- frontend ska inte vara beroende av suggestions för att kunna spara
|
||||
|
||||
### Konkret refaktorering
|
||||
Dela upp i privata metoder:
|
||||
- `parseRecipeContent(markdown)`
|
||||
- `buildIngredientSuggestions(parsedIngredients)`
|
||||
- `createRecipeFromImport(dto, userId)`
|
||||
|
||||
### Ändringar i `create()`
|
||||
- sluta kräva att alla ingredienser har `productId`
|
||||
- kör `assertProductsActive()` bara för ingredienser som faktiskt har `productId`
|
||||
- spara `rawName` även när produktmatchning finns
|
||||
- spara `rawLine` när det finns
|
||||
|
||||
### Ny metod att lägga till
|
||||
- `analyzeRecipeIngredients(recipeId, userId)`
|
||||
|
||||
Ansvar för `analyzeRecipeIngredients`:
|
||||
- ladda receptets råingredienser
|
||||
- jämför mot inventory
|
||||
- jämför mot pantry
|
||||
- returnera analysstatus per ingrediens
|
||||
- returnera förslag på ersättningsvaror och inköp
|
||||
|
||||
### Ny privat logik i service
|
||||
- `findExactInventoryMatches(...)`
|
||||
- `findPantryMatches(...)`
|
||||
- `findSubstituteCandidates(...)`
|
||||
- `buildShoppingNeeds(...)`
|
||||
|
||||
### Viktig designregel
|
||||
`parseMarkdown()` får gärna returnera suggestions, men `create()` får inte vara beroende av att de finns.
|
||||
|
||||
---
|
||||
|
||||
## 5. Receptcontroller: separera import från analys
|
||||
|
||||
### Fil: `backend/src/recipes/recipes.controller.ts`
|
||||
|
||||
### Ändringar
|
||||
Behåll:
|
||||
- `POST /recipes/parse-markdown`
|
||||
- `POST /recipes`
|
||||
|
||||
Lägg till:
|
||||
- `GET /recipes/:id/analysis`
|
||||
- eventuellt `POST /recipes/:id/rematch`
|
||||
|
||||
### Förslag på ansvar
|
||||
`GET /recipes/:id/analysis`
|
||||
- returnerar en användarspecifik analys av receptet mot inventory och pantry
|
||||
|
||||
`POST /recipes/:id/rematch`
|
||||
- kör ny matchning om användaren har ändrat inventory/pantry eller om heuristiken förbättrats
|
||||
|
||||
### Mål
|
||||
Importen blir en egen sak.
|
||||
Analysen blir en egen endpoint.
|
||||
|
||||
---
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 7. Alias-strategi och matchedVia (2026-05-07)
|
||||
|
||||
- Alias-strategin är nu fullt implementerad:
|
||||
- Backend och Flutter stödjer user-alias och globala alias.
|
||||
- matchedVia-badge visas i UI (Alias/Ordmatch/AI).
|
||||
- Användare kan spara egna alias, admins kan spara globala.
|
||||
- Profilsidan har alias-lista för användare, admin-panelen visar produkt-ID och radstruktur.
|
||||
- Backend har 3 nya tester för matchedVia, totalt 66 tester gröna.
|
||||
|
||||
### Fil: `backend/src/recipes/recipes.module.ts`
|
||||
|
||||
### Ändringar
|
||||
Lägg till eventuella nya providers när analyslogik bryts ut:
|
||||
- `RecipeAnalysisService`
|
||||
- eventuellt `RecipeMatchingService`
|
||||
|
||||
### Rekommendation
|
||||
Om analysen växer, bryt ut den från `RecipesService` för att undvika att samma service blir för stor.
|
||||
|
||||
---
|
||||
|
||||
## 7. Ny analysservice
|
||||
|
||||
### Ny fil: `backend/src/recipes/recipe-analysis.service.ts`
|
||||
|
||||
### Ansvar
|
||||
- ta ett sparat recept som indata
|
||||
- analysera varje ingrediens mot inventory och pantry
|
||||
- ta fram:
|
||||
- exakt träff
|
||||
- täcks av pantry
|
||||
- möjlig ersättning
|
||||
- saknas
|
||||
|
||||
### Förslag på output
|
||||
|
||||
```ts
|
||||
{
|
||||
recipeId: 123,
|
||||
ingredients: [
|
||||
{
|
||||
ingredientId: 1,
|
||||
rawName: "gul lök",
|
||||
quantity: 1,
|
||||
unit: "st",
|
||||
status: "exact_match",
|
||||
matchedProductId: 456,
|
||||
matchedProductName: "Gul lök",
|
||||
source: "inventory"
|
||||
}
|
||||
],
|
||||
summary: {
|
||||
exactCount: 3,
|
||||
pantryCount: 2,
|
||||
substituteCount: 1,
|
||||
missingCount: 4
|
||||
},
|
||||
shoppingListCandidates: []
|
||||
}
|
||||
```
|
||||
|
||||
### Nytta
|
||||
Detta blir grunden för:
|
||||
- "kan jag laga detta?"
|
||||
- shoppinglista
|
||||
- AI-förslag
|
||||
|
||||
---
|
||||
|
||||
## 8. Ny matchningstjänst
|
||||
|
||||
### Ny fil: `backend/src/recipes/recipe-matching.service.ts`
|
||||
|
||||
### Ansvar
|
||||
- matcha rå ingrediens mot produktkatalog
|
||||
- inte spara något själv
|
||||
- kunna användas av både import och analys
|
||||
|
||||
### Matchningsnivåer
|
||||
1. Exakt match på normaliserat namn
|
||||
2. Synonym/alias-match
|
||||
3. Kategori- eller taggmatchning
|
||||
4. AI-fallback
|
||||
|
||||
### Varför separat tjänst
|
||||
Det gör att heuristik, aliaslogik och AI-stöd inte behöver ligga inbyggt i `parseMarkdown()`.
|
||||
|
||||
---
|
||||
|
||||
## 9. AI-integration för ingredienstolkning
|
||||
|
||||
### Fil: `backend/src/ai/ai.service.ts`
|
||||
|
||||
### Nuvarande läge
|
||||
AI används redan för kategorisering.
|
||||
|
||||
### Nytt användningsområde
|
||||
Lägg till en separat metod, till exempel:
|
||||
- `suggestIngredientMatches(rawIngredient, candidates)`
|
||||
- `suggestSubstitutions(rawIngredient, availableProducts)`
|
||||
|
||||
### Viktig regel
|
||||
AI ska inte styra om receptet går att spara.
|
||||
AI ska bara förbättra analys och förslag efteråt.
|
||||
|
||||
### Mål
|
||||
AI används där osäkerhet är acceptabel:
|
||||
- substitutionsförslag
|
||||
- kompletterande matchning
|
||||
- shoppingförslag
|
||||
|
||||
---
|
||||
|
||||
## 10. Importskärm i Flutter
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
|
||||
|
||||
### Nuvarande problem
|
||||
Skärmen gör i dag detta i review-steget:
|
||||
- checkbox per ingrediens
|
||||
- produktval via suggestions
|
||||
- manuellt tillagda ingredienser med produktdropdown
|
||||
- vid sparning ignoreras ingredienser utan `productId`
|
||||
|
||||
Det gör att användaren i praktiken förväntas produktmatcha importen.
|
||||
|
||||
### Målbild för skärmen
|
||||
Importgranskningen ska fokusera på receptet, inte lagerkoppling.
|
||||
|
||||
### Ändringar
|
||||
Ta bort eller dölja i importläget:
|
||||
- produktdropdownar
|
||||
- krav på produktmatchning
|
||||
- manuell ingrediensskapning via produktlista
|
||||
|
||||
Behåll eller lägg till:
|
||||
- justera titel
|
||||
- justera beskrivning vid behov
|
||||
- justera servings
|
||||
- visa importerade ingrediensrader som text
|
||||
- tillåt enklare rättningar av mängd, enhet, notering och namn
|
||||
- inkludera/uteslut ingrediensrad
|
||||
|
||||
### Ny save-logik
|
||||
Vid `_save()` ska frontend skicka:
|
||||
- `rawName`
|
||||
- `rawLine`
|
||||
- `quantity`
|
||||
- `unit`
|
||||
- `note`
|
||||
- `productId` bara om en automatisk eller manuell matchning redan finns
|
||||
|
||||
### Rekommenderad UI-förändring
|
||||
Dela upp skärmen i två modes:
|
||||
- `import review`
|
||||
- `manual recipe editing`
|
||||
|
||||
Import review ska vara avsevärt enklare än manuell receptredigering.
|
||||
|
||||
---
|
||||
|
||||
## 11. Parsed recipe-domain i Flutter
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/domain/parsed_recipe.dart`
|
||||
|
||||
### Ändringar
|
||||
Lägg till fält som tydligare representerar rå importdata:
|
||||
- `rawLine`
|
||||
- `matchState`
|
||||
- `isParsedSafely`
|
||||
|
||||
### Rekommendation
|
||||
`ParsedIngredient` ska behandlas som importdata, inte som nästan färdig `RecipeIngredient`.
|
||||
|
||||
### Ny riktning
|
||||
`suggestions` ska vara optional hjälpdata i UI, inte en förutsättning för att spara.
|
||||
|
||||
---
|
||||
|
||||
## 12. Flutter-repository för recept
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/data/recipe_repository.dart`
|
||||
|
||||
### Ändringar
|
||||
- uppdatera `createRecipe()` så att request body tillåter råingredienser utan `productId`
|
||||
- lägg till metod:
|
||||
- `fetchRecipeAnalysis(int id)`
|
||||
- lägg till eventuell metod:
|
||||
- `rematchRecipeIngredients(int id)`
|
||||
|
||||
### Mål
|
||||
Frontend ska kunna:
|
||||
- spara troget importerat recept
|
||||
- senare hämta analys mot inventory/pantry
|
||||
|
||||
---
|
||||
|
||||
## 13. API-paths i Flutter
|
||||
|
||||
### Fil: `flutter/lib/core/api/api_paths.dart`
|
||||
|
||||
### Ändringar
|
||||
Lägg till:
|
||||
- `static String analysis(int id) => '/recipes/$id/analysis';`
|
||||
- eventuellt `static String rematch(int id) => '/recipes/$id/rematch';`
|
||||
|
||||
---
|
||||
|
||||
## 14. Receptdomän i Flutter
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/domain/recipe.dart`
|
||||
|
||||
### Nuvarande problem
|
||||
Nuvarande domän ser sannolikt ingredienser som produktkopplade objekt.
|
||||
|
||||
### Ändringar
|
||||
Gör att `RecipeIngredient` klarar:
|
||||
- `productId == null`
|
||||
- `productName == null`
|
||||
- `rawName` finns alltid
|
||||
- `quantity` och `unit` kan vara null eller tomma
|
||||
|
||||
### Viktigt
|
||||
UI för receptdetalj ska kunna visa rå ingredienstext även när ingen produktmatchning finns.
|
||||
|
||||
---
|
||||
|
||||
## 15. Receptdetaljskärm i Flutter
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
|
||||
|
||||
### Ändringar
|
||||
Ingredienslistan ska rendera enligt följande prioritet:
|
||||
1. `rawName` och råa fält
|
||||
2. matchat produktnamn om det finns
|
||||
|
||||
### Exempel
|
||||
Om produktmatchning saknas ska användaren ändå se:
|
||||
- `2 msk olivolja`
|
||||
- `1 gul lök`
|
||||
- `400 g krossade tomater`
|
||||
|
||||
### Lägg till ny sektion
|
||||
- "Har du hemma?"
|
||||
|
||||
Den sektionen ska bygga på `GET /recipes/:id/analysis` i stället för att anta att receptingredienser redan är perfekt produktmatchade.
|
||||
|
||||
---
|
||||
|
||||
## 16. Ny analysdomän i Flutter
|
||||
|
||||
### Ny fil: `flutter/lib/features/recipes/domain/recipe_analysis.dart`
|
||||
|
||||
### Ansvar
|
||||
Representera analysresultat från backend.
|
||||
|
||||
### Förslag på typer
|
||||
- `RecipeAnalysis`
|
||||
- `RecipeIngredientAnalysis`
|
||||
- `RecipeIngredientAvailabilityStatus`
|
||||
|
||||
### Statusvärden
|
||||
- `exactMatch`
|
||||
- `coveredByPantry`
|
||||
- `substitutable`
|
||||
- `missing`
|
||||
|
||||
---
|
||||
|
||||
## 17. Ny provider för analys
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/data/recipe_providers.dart`
|
||||
|
||||
### Ändringar
|
||||
Lägg till provider:
|
||||
- `recipeAnalysisProvider`
|
||||
|
||||
### Ansvar
|
||||
- ladda analys för ett recept
|
||||
- kunna invalidateras när inventory eller pantry ändras
|
||||
|
||||
---
|
||||
|
||||
## 18. Manual recipe editing ska vara separat
|
||||
|
||||
### Fil: `flutter/lib/features/recipes/presentation/recipe_edit_screen.dart`
|
||||
|
||||
### Rekommendation
|
||||
Behåll denna skärm för riktig redigering av receptets struktur.
|
||||
|
||||
### Viktig skillnad mot import
|
||||
- import review = bevara originalet
|
||||
- recipe edit = medveten redigering av receptet
|
||||
|
||||
### Praktisk åtgärd
|
||||
Undvik att återanvända fullständig edit-UI direkt i importflödet.
|
||||
|
||||
---
|
||||
|
||||
## 19. Alias- och produktmatchning
|
||||
|
||||
### Relevanta filer
|
||||
- `backend/src/receipt-import/...`
|
||||
- `backend/src/products/...`
|
||||
- `backend/src/recipes/recipes.service.ts`
|
||||
|
||||
### Åtgärd
|
||||
Flytta all generell ingrediensmatchning till en central tjänst i stället för att ha den dold i `parseMarkdown()`.
|
||||
|
||||
### Mål
|
||||
Samma logik ska kunna användas av:
|
||||
- receptimport
|
||||
- analys mot inventory/pantry
|
||||
- AI-förslag
|
||||
- eventuellt framtida shoppinglista
|
||||
|
||||
---
|
||||
|
||||
## 20. Migration av befintliga data
|
||||
|
||||
### Vad behöver hanteras
|
||||
Befintliga recept har ingredienser som redan är produktkopplade.
|
||||
|
||||
### Strategi
|
||||
- behåll gamla värden
|
||||
- fyll `rawName` med bästa tillgängliga namn från produkt eller befintlig text
|
||||
- sätt `matchSource = 'legacy'` för migrerade rader
|
||||
- sätt `analysisStatus = null` initialt
|
||||
|
||||
### Mål
|
||||
Gamla recept fortsätter fungera utan att behöva byggas om manuellt.
|
||||
|
||||
---
|
||||
|
||||
# Implementationsfaser
|
||||
|
||||
## Fas 1: Gör receptingredienser omatchningsbara
|
||||
|
||||
### Filer
|
||||
- `backend/prisma/schema.prisma`
|
||||
- `backend/src/recipes/dto/create-recipe.dto.ts`
|
||||
- `backend/src/recipes/recipes.service.ts`
|
||||
- `flutter/lib/features/recipes/data/recipe_repository.dart`
|
||||
- `flutter/lib/features/recipes/domain/recipe.dart`
|
||||
|
||||
### Resultat
|
||||
Ett recept kan sparas även när inga ingredienser är produktmatchade.
|
||||
|
||||
---
|
||||
|
||||
## Fas 2: Förenkla importgranskningen
|
||||
|
||||
### Filer
|
||||
- `flutter/lib/features/recipes/presentation/create_recipe_screen.dart`
|
||||
- `flutter/lib/features/recipes/domain/parsed_recipe.dart`
|
||||
|
||||
### Resultat
|
||||
Användaren granskar receptet som recept, inte som lagerobjekt.
|
||||
|
||||
---
|
||||
|
||||
## Fas 3: Lägg till analys mot inventory/pantry
|
||||
|
||||
### Filer
|
||||
- `backend/src/recipes/recipe-analysis.service.ts`
|
||||
- `backend/src/recipes/recipes.controller.ts`
|
||||
- `backend/src/recipes/recipes.module.ts`
|
||||
- `flutter/lib/core/api/api_paths.dart`
|
||||
- `flutter/lib/features/recipes/domain/recipe_analysis.dart`
|
||||
- `flutter/lib/features/recipes/data/recipe_repository.dart`
|
||||
- `flutter/lib/features/recipes/data/recipe_providers.dart`
|
||||
- `flutter/lib/features/recipes/presentation/recipe_detail_screen.dart`
|
||||
|
||||
### Resultat
|
||||
Appen kan svara på:
|
||||
- vad har jag exakt
|
||||
- vad täcks av pantry
|
||||
- vad kan ersättas
|
||||
- vad saknas
|
||||
|
||||
---
|
||||
|
||||
## Fas 4: Förbättra matchning och AI
|
||||
|
||||
### Filer
|
||||
- `backend/src/recipes/recipe-matching.service.ts`
|
||||
- `backend/src/ai/ai.service.ts`
|
||||
- eventuellt fler UI-filer för ersättningsförslag och shoppinglista
|
||||
|
||||
### Resultat
|
||||
AI blir en förbättrare, inte ett krav för importen.
|
||||
|
||||
---
|
||||
|
||||
# Beslut som bör tas innan implementation
|
||||
|
||||
## 1. Ska `RecipeIngredient` byggas om eller delas upp?
|
||||
|
||||
Rekommendation:
|
||||
- börja med att bygga om `RecipeIngredient`
|
||||
- dela upp i fler tabeller först om behovet blir tydligt senare
|
||||
|
||||
## 2. Ska suggestions visas under import?
|
||||
|
||||
Rekommendation:
|
||||
- ja, men bara som passiv hjälp
|
||||
- inte som något användaren måste välja för att kunna spara
|
||||
|
||||
## 3. Ska användaren kunna redigera ingrediensrader under import?
|
||||
|
||||
Rekommendation:
|
||||
- ja, men bara lätt redigering
|
||||
- ingen tung produktkoppling i detta steg
|
||||
|
||||
## 4. När ska AI användas?
|
||||
|
||||
Rekommendation:
|
||||
- efter att receptet är sparat
|
||||
- för matchning, substitutioner och shoppingstöd
|
||||
- inte för att avgöra om receptet får sparas
|
||||
|
||||
---
|
||||
|
||||
# Definition av klart
|
||||
|
||||
Denna omarbetning är klar när följande gäller:
|
||||
|
||||
1. Ett importerat recept kan sparas utan att någon ingrediens är kopplad till en intern produkt.
|
||||
2. Receptets ingredienslista visas troget även utan produktmatchning.
|
||||
3. Importskärmen kräver inte produktval.
|
||||
4. Receptdetaljen kan analysera receptet mot inventory och pantry i ett separat steg.
|
||||
5. Analysen kan särskilja exakt träff, pantry-träff, ersättningsbar och saknad ingrediens.
|
||||
6. AI används bara som hjälplager ovanpå detta.
|
||||
|
||||
---
|
||||
|
||||
# Rekommenderat första arbetsblock
|
||||
|
||||
Om implementationen ska påbörjas direkt är detta bästa första block:
|
||||
|
||||
1. Uppdatera `schema.prisma` för råingrediensstöd.
|
||||
2. Skapa migration.
|
||||
3. Uppdatera `create-recipe.dto.ts`.
|
||||
4. Uppdatera `recipes.service.ts#create()` så att recept kan sparas utan `productId`.
|
||||
5. Uppdatera `create_recipe_screen.dart` så att `_save()` skickar råingredienser i stället för att kasta bort omatchade rader.
|
||||
|
||||
Det blocket ger störst effekt med lägst risk, eftersom det löser kärnproblemet: att importen inte längre förstör receptets innehåll.
|
||||
|
||||
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
|
||||
|
||||
## 2026-05-10: Admin-inventarie (CRUD, merge, filter, sortering, preview, säkerhet), user-scope, IDOR-skydd, säkerhetshärdning, optimeringar och utökad testtäckning är nu genomförda och dokumenterade i README, TEKNISK_BESKRIVNING, SÄKERHETSHÄRDNINGSPLAN och SESSIONLOGGAR.
|
||||
Reference in New Issue
Block a user