Files
recipe-app/.kilo/plans/1779256290774-nimble-lagoon.md
T
Nils-Johan Gynther a1a2c33427
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Successful in 5m8s
Test Suite / flutter-quality (push) Failing after 1m41s
feat(shopping-list): add shopping list feature with flyer integration
This commit introduces a comprehensive shopping list feature with the following key changes:

Backend:
- Added ShoppingListItem model with relations to User, Product, and Category
- Added new fields to FlyerSession for source file metadata
- Added categoryId field to FlyerItem model
- Implemented session source file retrieval endpoint
- Added endpoint for updating flyer session items with category assignment
- Added endpoint for planning flyer selections to shopping list
- Implemented backfillCategoriesMine for AI-assisted category assignment
- Added ShoppingListModule and integrated with FlyerSelectionModule

Frontend:
- Added ShoppingListScreen and navigation route
- Implemented API paths and client methods for shopping list operations
- Added category tree loading for shopping list item creation
- Integrated shopping list functionality in flyer import tab
- Added category backfill trigger in inventory screen
- Updated FlyerImportItem model with categoryId support
- Added methods for updating flyer session items and retrieving source files

Database:
- Added new Prisma migration for flyer source metadata and shopping list items
- Updated schema with new relations and indexes

The shopping list feature allows users to:
1. Plan flyer selections directly to their shopping list
2. View and manage their shopping list items
3. Update flyer session items with proper categorization
4. Retrieve original flyer source files
5. Automatically backfill categories for uncategorized products
2026-05-20 09:07:30 +02:00

153 lines
6.5 KiB
Markdown

# Åtgärdsplan utifrån E2E-fynd (Flyer + Inventory)
## Mål
- Göra flyerflödet praktiskt användbart för jämförelse, redigering och planering till inköpslista.
- Säkerställa att inventory/pantry visar korrekta kategorier i stället för att allt hamnar i `Övrigt`.
## Scope (utifrån dina punkter)
1. Flyer-vy:
- Visa importerad flyer-PDF för jämförelse mot extraherade rader.
- Redigera poster (namn + kategori) med samma kategorikälla som products/pantry.
- `Planera X markerade` ska skapa en faktisk inköpslista i en egen flik.
2. Inventory:
- Felsöka och åtgärda varför poster visas under `Övrigt`.
---
## Nulägesbild (från kodbasen)
- Flutter har redan `FlyerImportTab` med:
- filval/import,
- checkboxar,
- `Planera X markerade` -> `POST /flyer-sessions/:id/selections/bulk`.
- PDF-visning finns idag bara för aktuell uppladdad fil i minnet (`_pickedFile.bytes`) och kan inte återöppnas säkert vid återställd session/app-omstart.
- Flyer-rader kan ännu inte redigeras i UI (ingen inline edit-dialog för flyer-item).
- Meal Plan har en shopping-sektion, men ingen dedikerad flik för flyer-planerade köp.
- Kategori-träd finns redan och används i flera vyer via `/categories/tree`.
- Inventory läser kategori via `product.categoryRef`; null blir `Övrigt` i UI.
---
## Genomförandeplan
### 1) Flyer-PDF: beständig förhandsvisning per session
**Backend**
- Lägg till lagring av originalfil för flyer-session (MVP: lokal filstore eller DB blob beroende på befintligt mönster i projektet).
- Utöka `flyer_session` metadata med filreferens (filnamn, mime, storlek, storageKey).
- Ny endpoint: `GET /flyer-import/sessions/:sessionId/source` (auth + ägarskap) som streamar PDF/bild.
**Flutter**
- I `FlyerImportTab`:
- använd befintlig local preview direkt efter uppladdning,
- när session återställs: hämta source-endpoint och visa `Visa flyer` även då.
- Behåll fallback-meddelande för plattformar som inte kan öppna PDF direkt.
**Klart-kriterium**
- Samma importerade flyer kan öppnas efter tab-byte och app-omstart för samma användare.
### 2) Redigering av flyer-poster (namn + kategori)
**Backend**
- Lägg till endpoint för uppdatering av flyer-item i session, t.ex.
- `PATCH /flyer-import/sessions/:sessionId/items/:itemId`
- fält: `rawName` (eller `displayName`) och `categoryId` (ev. `categoryHintPath` för visning).
- Validera att kategori finns i samma kategoriträd (`categories`).
- Ägarskapskontroll via sessionens `userId`.
**Flutter**
- I listan i `FlyerImportTab`: lägg till `Redigera`-action per rad.
- Edit-dialog:
- textfält för namn,
- kategori-väljare baserad på samma träd/komponentmönster som inventory/pantry/admin.
- Spara uppdatering till backend och uppdatera lokal/session state.
**Klart-kriterium**
- Användaren kan ändra namn och kategori på en flyer-rad och ser ändringen direkt i listan.
### 3) Flyer -> Inköpslista i egen flik
**Backend**
- Definiera enkel shopping-list-resurs för MVP (user-scoped):
- tabell t.ex. `shopping_list_item` (name/productId/categoryId/quantity/unit/source/status/userId).
- Ny endpoint för att skapa inköpsrader från flyer-selections:
- `POST /flyer-sessions/:sessionId/selections/plan-to-shopping-list`
- alternativt återanvänd bulk-create i shopping-modul.
- Deduplicering/regler:
- om samma `productId+unit` finns öppet: summera eller hoppa över (bestäms i implementation; rekommenderat: summera).
**Flutter**
- Lägg till ny flik/skärm `Inköpslista` i app-shell.
- `Planera X markerade` i flyer-vyn ska:
1) skapa/uppdatera flyer selections,
2) trigga backend-mappning till shopping-list,
3) visa snackbar med antal tillagda/uppdaterade rader.
- Inköpslista-vyn (MVP): lista rader + enkel check/avprickning.
**Klart-kriterium**
- Klick på `Planera X markerade` flyttar markerade flyer-produkter till Inköpslista-fliken.
### 4) Inventory-fel: allt i `Övrigt`
**Felsökning**
- Verifiera varför `product.categoryId/categoryRef` blir null i aktuella poster:
- skapade produkter utan kategori,
- importflöden som inte persistar vald kategori,
- äldre data utan backfill.
**Åtgärd**
- Säkerställ att produktskapande från import/edit alltid skickar/sätter kategori när sådan är vald.
- Lägg skydd i backend så kategori inte tappas vid update-flöden.
- Engångs-backfill för befintliga produkter utan kategori:
- använd befintlig kategoriseringslogik (regel/AI) + fallback till rimlig underkategori.
- Kör re-fetch/invalidations i Flutter inventory/pantry efter backfill.
**Klart-kriterium**
- Inventory/pantry visar blandade korrekta kategorier; endast okända poster ligger kvar i `Övrigt`.
---
## Testplan
### Backend
- Nya tester för:
- auth/ownership på source-endpoint och item-edit endpoint,
- validering av kategori-id,
- plan-to-shopping-list (antal skapade, dedupe, idempotens).
### Flutter widget/integration
- Flyer:
- render av `Visa flyer` efter restore,
- edit-dialog uppdaterar rad,
- `Planera X markerade` ger förväntad feedback.
- Inköpslista:
- ny flik syns,
- mottar rader från flyer.
- Inventory regression:
- kategori visas från product category path när satt,
- `Övrigt` endast fallback.
### E2E-checklista
- Importera flyer PDF -> öppna PDF -> redigera 2 rader -> planera markerade -> verifiera Inköpslista-fliken.
- Starta om app -> återöppna samma flyer-PDF -> verifiera ändringar kvar.
- Kontrollera inventory/pantry-kategorier efter backfill.
---
## Leveransordning (rekommenderad)
1. Inventory-kategori bugfix + backfill (snabbt värde, hög påverkan).
2. Flyer item-redigering (namn/kategori).
3. Inköpslista-flik + backend-mappning från flyer.
4. Beständig flyer-PDF source-visning (kan byggas parallellt med 2/3 om backendkapacitet finns).
---
## Risker och mitigering
- **Datamigrering/backfill-risk**: kör först mot staging + logga träffsäkerhet och antal fallback till `Övrigt`.
- **Dubbelposter i inköpslista**: inför tydlig dedupe-regel och testfall.
- **PDF-hantering per plattform**: behåll web-first öppning och tydligt fallback-meddelande där native viewer saknas.
- **Prestanda vid stora flyers**: paginera/virtuallista om UI blir tungt.
---
## Definition of Done
- Flyer-vyn har fungerande: PDF-visning, redigering av namn/kategori, planering till inköpslista.
- Inköpslista finns som separat flik och visar planerade flyer-rader.
- Inventory/pantry kategoriserar korrekt och `Övrigt` används endast som verklig fallback.
- Nya backend- och Flutter-tester gröna.