Vorrat: Mindestbestand, Sortierung nach Laden, Rezeptsammlung mit Vorschlägen

- Mindestbestand pro Liste/Produkt/Einheit (Tabelle stock_minimums): fällt
  der Vorrat darunter, füllt der Server die Einkaufsliste genau bis zur
  fehlenden Menge auf (nach Ändern, Aufgebraucht, Löschen, Setzen)
- Laden-Kategorien (kategorien.py): automatische Zuordnung über Stichwörter
  (längstes Stichwort gewinnt), änderbar in der Zeile und im
  Vorschläge-Dialog; Einkaufsliste "A–Z" oder "Nach Laden" gruppiert;
  Reihenfolge der Abteilungen pro Liste
- Rezeptsammlung (Tabelle recipes): "☆ Merken" beim Abgleich, "Was kann ich
  kochen?" sortiert gemerkte Rezepte nach vorhandenen Zutaten (Salz & Co.
  zählen nicht, nur sichtbare Produkte), vollständiger Abgleich per Klick
- Migration Schritt 3 (Kategorien für bestehende Produkte); _add_column für
  Tabellen, die SCHEMA in alten Datenbanken schon neu angelegt hat
- Tests in test_vorrat_extras.py

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 19:17:52 +02:00
1 parent 9df0e38f52
commit 9835f5e69e
15 files changed
+879 -40

No files matched your search

+21
View File
@@ -4,6 +4,8 @@ SCHEMA in db.py beschreibt immer den neuesten Stand. Bei einer Änderung am Sche
1. SCHEMA in db.py anpassen – neue Datenbanken entstehen direkt so und bekommen die höchste Nummer.
2. Unten an STEPS eine Funktion anhängen, die eine bestehende Datenbank vom vorigen Stand dorthin bringt.
Jeder Schritt läuft genau einmal, in einer Transaktion mit dem Hochzählen der Nummer.
Achtung: Eine Tabelle, die es in einer alten Datenbank noch gar nicht gab, legt SCHEMA schon in der
neuesten Form an. Spalten deshalb mit _add_column() ergänzen (fügt nur hinzu, was fehlt).
Tests: tests/test_migrationen.py (alte Stände nachbauen und umstellen).
"""
import logging
@@ -14,6 +16,7 @@ from .common import ValidationError, now_iso
from .config import DATA_DIR
from .db import HOUSEHOLD_TABLE, KANBAN_PROJECTS_TABLE, KANBAN_TASKS_TABLE
from .htmlclean import sanitize
from .kategorien import guess
log = logging.getLogger(__name__)
@@ -32,6 +35,12 @@ def upgrade(conn) -> None:
conn.execute(f"PRAGMA user_version = {number}")
def _add_column(conn, table: str, column: str, definition: str) -> None:
"""Spalte ergänzen, falls sie fehlt (table/column/definition stammen nur aus diesem Modul)."""
if column not in {r["name"] for r in conn.execute(f"PRAGMA table_info({table})")}:
conn.execute(f"ALTER TABLE {table} ADD COLUMN {column} {definition}")
def _rebuild(conn, table: str, definition: str, columns: str, select: str, params=()) -> None:
"""Tabelle neu aufbauen: neue Tabelle unter Zwischennamen anlegen, Daten kopieren, alte löschen, umbenennen.
@@ -229,8 +238,20 @@ def _v2_datenschutz(conn) -> None:
log.warning("Bild %s konnte nicht bereinigt werden: %s", f.name, e)
# ---------- Schritt 3: Laden-Kategorien ----------
def _v3_kategorien(conn) -> None:
"""Produkte bekommen eine Laden-Kategorie (geraten), Listen eine eigene Kategorie-Reihenfolge."""
_add_column(conn, "products", "category", "TEXT")
_add_column(conn, "shopping_lists", "category_order", "TEXT")
for row in conn.execute("SELECT key, name FROM products").fetchall():
conn.execute("UPDATE products SET category = ? WHERE key = ?", (guess(row["name"]), row["key"]))
# Reihenfolge = Versionsnummer (Index + 1). Nur anhängen, nie umsortieren oder entfernen.
STEPS = [
_v1_altbestand,
_v2_datenschutz,
_v3_kategorien,
]