Barcode-Scan — geteilte, lernende Wissensbasis für Produktname + Kategorie über alle Listen #190

Closed
opened 2026-09-12 00:02:25 +02:00 by lena · 3 comments
Collaborator

Story: Barcode-Scan — geteilte, lernende Wissensbasis für Produktname + Kategorie über alle Listen

As a Nutzer mit mehreren Listen (z. B. Einkaufsliste und Vorratsschrank),
I want to dass ein einmal gescannter und benannter/kategorisierter Barcode auf JEDER Liste wiedererkannt wird - inklusive vorgeschlagenem Namen und Kategorie,
so that ich ein Produkt nicht auf jeder Liste erneut manuell benennen und einsortieren muss.

Kontext (verifiziert im Code):

  • Aktuell gibt es KEINE geteilte Barcode-Wissensbasis. Barcode existiert nur als Spalte direkt auf ShoppingProductEntity/PantryProductEntity und wird beim Scan ausschließlich INNERHALB derselben Liste abgeglichen (ScanShoppingProductBarcodeCommandHandler: x.ShoppingListId == ... && x.Barcode == ...; ScanPantryProductBarcodeCommandHandler: x.PantryId == ... && x.Barcode == ...). Ein auf der Einkaufsliste bereits bekannter/umbenannter Barcode wird beim Scan auf dem Vorratsschrank (oder einer anderen Einkaufsliste) nicht wiedererkannt - der Nutzer startet dort bei Null.
  • Bei einem unbekannten Barcode wird der Name aktuell NUR über die externe Open-Food-Facts-Abfrage vorgeschlagen (IOpenFoodFactsClient.TryLookupProductName) - eine rein online-basierte Quelle ohne Gedächtnis für zuvor selbst vergebene Namen.
  • Kategorie beim Scan: Auf der Einkaufsliste wird die Kategorie über die bestehende ProductSectionKnowledgeEntity-Wissensbasis automatisch zugeordnet, mit Nachfrage nur falls das fehlschlägt (ShoppingBarcodeScanDialog.tsxs pendingCategoryProduct-Flow). Auf dem Vorratsschrank passiert das nicht: ScanPantryProductBarcodeCommandHandler setzt die Kategorie eines neu über Scan angelegten Produkts IMMER hart auf die Standard-Kategorie der verknüpften Einkaufsliste - es gibt dort überhaupt keinen Auflösungs- oder Abfrage-Schritt für die Kategorie, nur für den Namen. Das deckt sich mit der Beobachtung: auf dem Vorratsschrank wird aktuell wirklich nur nach dem Namen gefragt, nie nach der Kategorie.
  • Das bereits bestehende ProductSectionKnowledgeEntity-System (aus #90, Scope kürzlich in #178 von app-weit auf pro-Einkaufsliste umgestellt) ist NICHT barcode-basiert, sondern rein namensbasiert (Produktname → Sektion/Kategorie) und wird nur für Texteingabe-Flows (Smart-Add etc.) genutzt, nicht für den Barcode-Scan-Flow.

Acceptance criteria:

  • Ein neuer, geteilter Datensatz koppelt Barcode ↔ zuletzt verwendeter Produktname ↔ zuletzt verwendete Kategorie, gespeist aus jedem Scan/jeder manuellen Umbenennung auf JEDER Liste (Einkaufsliste wie Vorratsschrank).
  • Wird ein bereits bekannter Barcode auf einer beliebigen anderen Liste (auch anderem Listentyp) gescannt, werden Name und Kategorie aus dieser Wissensbasis vorgeschlagen/vorausgefüllt, statt dass der Nutzer bei Null anfängt.
  • Der Vorratsschrank-Scan-Flow zeigt beim Anlegen eines neuen Produkts (egal ob aus der Wissensbasis oder komplett neu) genauso wie die Einkaufsliste ein Kategorie-Feld/eine Kategorie-Auswahl, nicht nur den Namen.
  • Die externe Open-Food-Facts-Abfrage bleibt als Fallback bestehen, wenn weder die eigene Wissensbasis noch (für die Kategorie) ProductSectionKnowledgeEntity etwas liefert.

Out of scope:

  • Änderungen am bestehenden, rein namensbasierten ProductSectionKnowledgeEntity-System selbst (#178 behandelt dessen Scope bereits separat).
  • Die rein clientseitige Embedding-Ähnlichkeits-Vorschlags-Badge (useCategorySuggestions).

Open questions (escalate to human if unanswered):

  • Soll die neue Wissensbasis app-weit (alle Nutzer/Haushalte teilen sich gelernte Barcode→Name/Kategorie-Zuordnungen) oder pro Haushalt/Listengruppe gescopt sein? #178 hat für das bestehende namensbasierte System "pro Einkaufsliste, nicht global" entschieden - soll dieselbe Regel hier gelten, oder ist ein Barcode (anders als frei getippte Kategorienamen) eindeutig genug, um app-weit sinnvoll geteilt zu werden?
  • Wessen Zuordnung gewinnt, wenn zwei Nutzer denselben Barcode unterschiedlich benennen/kategorisieren (zuletzt gewinnt / erster gewinnt / pro Scope getrennt)?
## Story: Barcode-Scan — geteilte, lernende Wissensbasis für Produktname + Kategorie über alle Listen **As a** Nutzer mit mehreren Listen (z. B. Einkaufsliste und Vorratsschrank), **I want to** dass ein einmal gescannter und benannter/kategorisierter Barcode auf JEDER Liste wiedererkannt wird - inklusive vorgeschlagenem Namen und Kategorie, **so that** ich ein Produkt nicht auf jeder Liste erneut manuell benennen und einsortieren muss. **Kontext (verifiziert im Code):** - Aktuell gibt es KEINE geteilte Barcode-Wissensbasis. `Barcode` existiert nur als Spalte direkt auf `ShoppingProductEntity`/`PantryProductEntity` und wird beim Scan ausschließlich INNERHALB derselben Liste abgeglichen (`ScanShoppingProductBarcodeCommandHandler`: `x.ShoppingListId == ... && x.Barcode == ...`; `ScanPantryProductBarcodeCommandHandler`: `x.PantryId == ... && x.Barcode == ...`). Ein auf der Einkaufsliste bereits bekannter/umbenannter Barcode wird beim Scan auf dem Vorratsschrank (oder einer anderen Einkaufsliste) nicht wiedererkannt - der Nutzer startet dort bei Null. - Bei einem unbekannten Barcode wird der Name aktuell NUR über die externe Open-Food-Facts-Abfrage vorgeschlagen (`IOpenFoodFactsClient.TryLookupProductName`) - eine rein online-basierte Quelle ohne Gedächtnis für zuvor selbst vergebene Namen. - Kategorie beim Scan: Auf der Einkaufsliste wird die Kategorie über die bestehende `ProductSectionKnowledgeEntity`-Wissensbasis automatisch zugeordnet, mit Nachfrage nur falls das fehlschlägt (`ShoppingBarcodeScanDialog.tsx`s `pendingCategoryProduct`-Flow). Auf dem Vorratsschrank passiert das nicht: `ScanPantryProductBarcodeCommandHandler` setzt die Kategorie eines neu über Scan angelegten Produkts IMMER hart auf die Standard-Kategorie der verknüpften Einkaufsliste - es gibt dort überhaupt keinen Auflösungs- oder Abfrage-Schritt für die Kategorie, nur für den Namen. Das deckt sich mit der Beobachtung: auf dem Vorratsschrank wird aktuell wirklich nur nach dem Namen gefragt, nie nach der Kategorie. - Das bereits bestehende `ProductSectionKnowledgeEntity`-System (aus #90, Scope kürzlich in #178 von app-weit auf pro-Einkaufsliste umgestellt) ist NICHT barcode-basiert, sondern rein namensbasiert (Produktname → Sektion/Kategorie) und wird nur für Texteingabe-Flows (Smart-Add etc.) genutzt, nicht für den Barcode-Scan-Flow. **Acceptance criteria:** - [ ] Ein neuer, geteilter Datensatz koppelt Barcode ↔ zuletzt verwendeter Produktname ↔ zuletzt verwendete Kategorie, gespeist aus jedem Scan/jeder manuellen Umbenennung auf JEDER Liste (Einkaufsliste wie Vorratsschrank). - [ ] Wird ein bereits bekannter Barcode auf einer beliebigen anderen Liste (auch anderem Listentyp) gescannt, werden Name und Kategorie aus dieser Wissensbasis vorgeschlagen/vorausgefüllt, statt dass der Nutzer bei Null anfängt. - [ ] Der Vorratsschrank-Scan-Flow zeigt beim Anlegen eines neuen Produkts (egal ob aus der Wissensbasis oder komplett neu) genauso wie die Einkaufsliste ein Kategorie-Feld/eine Kategorie-Auswahl, nicht nur den Namen. - [ ] Die externe Open-Food-Facts-Abfrage bleibt als Fallback bestehen, wenn weder die eigene Wissensbasis noch (für die Kategorie) `ProductSectionKnowledgeEntity` etwas liefert. **Out of scope:** - Änderungen am bestehenden, rein namensbasierten `ProductSectionKnowledgeEntity`-System selbst (#178 behandelt dessen Scope bereits separat). - Die rein clientseitige Embedding-Ähnlichkeits-Vorschlags-Badge (`useCategorySuggestions`). **Open questions (escalate to human if unanswered):** - Soll die neue Wissensbasis app-weit (alle Nutzer/Haushalte teilen sich gelernte Barcode→Name/Kategorie-Zuordnungen) oder pro Haushalt/Listengruppe gescopt sein? #178 hat für das bestehende namensbasierte System "pro Einkaufsliste, nicht global" entschieden - soll dieselbe Regel hier gelten, oder ist ein Barcode (anders als frei getippte Kategorienamen) eindeutig genug, um app-weit sinnvoll geteilt zu werden? - Wessen Zuordnung gewinnt, wenn zwei Nutzer denselben Barcode unterschiedlich benennen/kategorisieren (zuletzt gewinnt / erster gewinnt / pro Scope getrennt)?
Author
Collaborator

Entscheidung (2026-09-12, mit dem Menschen geklärt):

  • Scope: pro Einkaufsliste/Haushalt getrennt (nicht app-weit) - konsistent mit #178s bereits getroffener Entscheidung für die ähnliche, namensbasierte Kategorie-Wissensbasis. Ein neuer Haushalt startet bei einem neuen Produkt wieder bei Null, dafür keine Vermischung mit fremden Haushalten.
  • Konfliktregel: zuletzt gespeichert gewinnt - benennt/kategorisiert ein zweites Haushaltsmitglied denselben Barcode später anders, ersetzt das die vorherige Zuordnung.

Damit sind beide offenen Fragen der Story geklärt - Issue ist bereit für die Umsetzung.

**Entscheidung (2026-09-12, mit dem Menschen geklärt):** - **Scope:** pro Einkaufsliste/Haushalt getrennt (nicht app-weit) - konsistent mit #178s bereits getroffener Entscheidung für die ähnliche, namensbasierte Kategorie-Wissensbasis. Ein neuer Haushalt startet bei einem neuen Produkt wieder bei Null, dafür keine Vermischung mit fremden Haushalten. - **Konfliktregel:** zuletzt gespeichert gewinnt - benennt/kategorisiert ein zweites Haushaltsmitglied denselben Barcode später anders, ersetzt das die vorherige Zuordnung. Damit sind beide offenen Fragen der Story geklärt - Issue ist bereit für die Umsetzung.
lena self-assigned this 2026-09-12 00:29:26 +02:00
Author
Collaborator

Claimed - Umsetzung beginnt. Architektur (mit dem Menschen vorab nicht-technisch abgestimmt): neue Tabelle ProductBarcodeKnowledgeEntity, gescoped pro ShoppingListId (eindeutiger Index auf (ShoppingListId, Barcode)), gespeist/gelesen sowohl vom Einkaufslisten- als auch vom Vorratsschrank-Scan-Flow (Vorratsschrank löst über sein bestehendes TargetShoppingListId auf dieselbe Liste auf, analog zu #174s bereits geteilten Kategorien). Konfliktregel: zuletzt gespeichert gewinnt (Upsert). Vorratsschrank-Scan fragt künftig zusätzlich nach der Kategorie statt sie stillschweigend auf die Standard-Kategorie zu setzen.

Claimed - Umsetzung beginnt. Architektur (mit dem Menschen vorab nicht-technisch abgestimmt): neue Tabelle `ProductBarcodeKnowledgeEntity`, gescoped pro `ShoppingListId` (eindeutiger Index auf `(ShoppingListId, Barcode)`), gespeist/gelesen sowohl vom Einkaufslisten- als auch vom Vorratsschrank-Scan-Flow (Vorratsschrank löst über sein bestehendes `TargetShoppingListId` auf dieselbe Liste auf, analog zu #174s bereits geteilten Kategorien). Konfliktregel: zuletzt gespeichert gewinnt (Upsert). Vorratsschrank-Scan fragt künftig zusätzlich nach der Kategorie statt sie stillschweigend auf die Standard-Kategorie zu setzen.
Author
Collaborator

Implemented and merged (commits 9cf7f088 backend, 156b5050 frontend).

Scope (final, nach Rücksprache mit dem Menschen):

  • Neue ProductBarcodeKnowledgeEntity: Barcode -> zuletzt verwendeter Name + Kategorie, gescoped pro ShoppingListId, geteilt mit der verknüpften Vorratsschrank-Liste (via #174s bestehender TargetShoppingListId-Verknüpfung), nie über unabhängige Listen hinweg.
  • Gespeist aus jedem Scan (Einkaufsliste wie Vorratsschrank) und jeder manuellen Umbenennung eines Barcode-tragenden Produkts. Konfliktregel: zuletzt gespeichert gewinnt.
  • Der Vorratsschrank-Scan-Flow fragt jetzt ebenfalls nach der Kategorie (vorher: stillschweigend immer Standard-Kategorie, kein Auflösungsversuch) - Priorität: explizite Auswahl > Wissensbasis-Vorschlag > Standard-Kategorie als letzter Fallback.
  • CSV-Export/-Import für die Wissensbasis (GetProductBarcodeKnowledgeForListQuery / ImportProductBarcodeKnowledgeFromCsvCommand), nutzt die bestehende CSV-Dialog-Infrastruktur - erlaubt, eine bereits gelernte Zuordnung von einer Liste in eine andere zu übertragen, statt bei 0 anzufangen.
  • #178 im selben Zug erneut aufgegriffen (mit frischem, explizitem menschlichem Feedback, keine Neu-Verhandlung der damaligen Entscheidung): die bestehende namensbasierte Kategorie-Wissensbasis bekommt dieselbe Einkaufsliste+Vorratsschrank-Scope wie die neue barcode-basierte - Details in #178s eigenem, wiedereröffneten und erneut geschlossenen Thread.

Sicherheits-Fix während der Umsetzung gefunden: ScanPantryProductBarcodeCommandHandler validierte eine vom Client mitgegebene Kategorie-Id nicht gegen die tatsächliche Ziel-Liste (Einkaufslistes eigener Scan-Pfad hatte diese Prüfung bereits über CreateShoppingProductCommandHandler). Nachgezogen, mit eigenem Test (Rejects_a_fallback_category_id_that_belongs_to_a_different_list).

Tests: 16 neue/erweiterte Backend-Tests (Scan-Handler beider Listentypen, Rename-Hooks, CSV-Import/-Export, #178s Re-Scoping inkl. eines Cross-List-Isolation-Tests), Frontend-Tests aktualisiert (neue suggestedCategoryId/fallbackCategoryId-Felder, Kategorie-Picker-Props). Vollständig grün: 1136 Backend-Tests (965+52+119), 1324 Frontend-Tests (136 Dateien).

Implemented and merged (commits 9cf7f088 backend, 156b5050 frontend). **Scope (final, nach Rücksprache mit dem Menschen):** - Neue `ProductBarcodeKnowledgeEntity`: Barcode -> zuletzt verwendeter Name + Kategorie, gescoped pro `ShoppingListId`, geteilt mit der verknüpften Vorratsschrank-Liste (via #174s bestehender `TargetShoppingListId`-Verknüpfung), nie über unabhängige Listen hinweg. - Gespeist aus jedem Scan (Einkaufsliste wie Vorratsschrank) und jeder manuellen Umbenennung eines Barcode-tragenden Produkts. Konfliktregel: zuletzt gespeichert gewinnt. - Der Vorratsschrank-Scan-Flow fragt jetzt ebenfalls nach der Kategorie (vorher: stillschweigend immer Standard-Kategorie, kein Auflösungsversuch) - Priorität: explizite Auswahl > Wissensbasis-Vorschlag > Standard-Kategorie als letzter Fallback. - CSV-Export/-Import für die Wissensbasis (`GetProductBarcodeKnowledgeForListQuery` / `ImportProductBarcodeKnowledgeFromCsvCommand`), nutzt die bestehende CSV-Dialog-Infrastruktur - erlaubt, eine bereits gelernte Zuordnung von einer Liste in eine andere zu übertragen, statt bei 0 anzufangen. - **#178 im selben Zug erneut aufgegriffen** (mit frischem, explizitem menschlichem Feedback, keine Neu-Verhandlung der damaligen Entscheidung): die bestehende namensbasierte Kategorie-Wissensbasis bekommt dieselbe Einkaufsliste+Vorratsschrank-Scope wie die neue barcode-basierte - Details in #178s eigenem, wiedereröffneten und erneut geschlossenen Thread. **Sicherheits-Fix während der Umsetzung gefunden:** `ScanPantryProductBarcodeCommandHandler` validierte eine vom Client mitgegebene Kategorie-Id nicht gegen die tatsächliche Ziel-Liste (Einkaufslistes eigener Scan-Pfad hatte diese Prüfung bereits über `CreateShoppingProductCommandHandler`). Nachgezogen, mit eigenem Test (`Rejects_a_fallback_category_id_that_belongs_to_a_different_list`). **Tests:** 16 neue/erweiterte Backend-Tests (Scan-Handler beider Listentypen, Rename-Hooks, CSV-Import/-Export, #178s Re-Scoping inkl. eines Cross-List-Isolation-Tests), Frontend-Tests aktualisiert (neue `suggestedCategoryId`/`fallbackCategoryId`-Felder, Kategorie-Picker-Props). Vollständig grün: 1136 Backend-Tests (965+52+119), 1324 Frontend-Tests (136 Dateien).
lena closed this issue 2026-09-12 09:27:45 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
robert/todo#190
No description provided.