Listen mit eigenem Vorrat und Einkauf, Städte anderer auf der Reise-Karte, Design "Fabrik"

Vorrat & Einkauf:
- Mehrere Listen (z. B. "Zuhause", "Urlaub"), jede mit eigenem Vorrat und
  eigener Einkaufsliste; Auswahl oben auf der Seite, gemerkt pro Browser
- Beim Anlegen festlegen, wer die Liste sieht: alle Konten oder nur
  ausgewählte (Ersteller sieht sie immer); ändern/löschen nur Ersteller
  und Administratoren
- Gekauft, aufgebraucht, Nachkaufen und Rezeptabgleich wirken innerhalb
  der gewählten Liste
- Migration: bisheriger Vorrat und Einkauf werden zur Liste "Zuhause"
  (für alle sichtbar)

Reisen:
- Schalter "Städte anderer zeigen": Städte der übrigen Konten in je einer
  eigenen Farbe, einzelne Personen ein-/ausblendbar, nur lesend
- Info-Karte zeigt, wer noch in einer Stadt bzw. einem Land war

Design "Fabrik" (Industrie-Inventar à la Satisfactory):
- Stahlplatten mit Schrauben, Titelleisten mit Fenstersymbol, gelb
  leuchtende Hauptbuttons, orange Mengen-Zähler, Warnstreifen,
  Riffelblech und drehende Zahnräder im Hintergrund
- Schriften Roboto / Roboto Condensed (OFL), lokal eingebunden

Doku: README verlinkt das zugehörige Repository P42-S.M.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 11:50:46 +02:00
1 parent f16c8c73c6
commit b30acb652a
20 files changed
+1182 -82

No files matched your search

+45 -8
View File
@@ -10,18 +10,20 @@ from .config import DATA_DIR
DB_FILE = DATA_DIR / "p42.db"
# Geteilte Vorrats- und Einkaufsliste; key = Name in casefold() für den Vergleich.
# Pro Produkt, Liste und Einheit eine Zeile ("Mehl 1 kg" und "Mehl 2 Stück" dürfen nebeneinander stehen).
# Vorrat und Einkauf; key = Name in casefold() für den Vergleich.
# Jeder Eintrag gehört zu einer Liste (shopping_lists, z. B. "Zuhause", "Urlaub") mit eigenem Vorrat und Einkauf.
# Pro Liste, Produkt, Bereich und Einheit eine Zeile ("Mehl 1 kg" und "Mehl 2 Stück" dürfen nebeneinander stehen).
HOUSEHOLD_TABLE = """(
id INTEGER PRIMARY KEY,
key TEXT NOT NULL,
name TEXT NOT NULL,
list TEXT NOT NULL CHECK (list IN ('vorrat', 'einkauf')),
list_id INTEGER NOT NULL REFERENCES shopping_lists(id) ON DELETE CASCADE,
quantity REAL NOT NULL CHECK (quantity > 0),
unit TEXT NOT NULL DEFAULT 'stk' CHECK (unit IN ('stk', 'kg', 'l')),
updated_at TEXT NOT NULL,
updated_by INTEGER REFERENCES users(id) ON DELETE SET NULL,
UNIQUE (key, list, unit)
UNIQUE (list_id, key, list, unit)
)"""
# Kanban-Projekte (je ein Board), gemeinsam für alle Konten
@@ -115,6 +117,23 @@ CREATE TABLE IF NOT EXISTS monitor_samples (
PRIMARY KEY (monitor_id, ts)
) WITHOUT ROWID;
-- Listen mit je eigenem Vorrat und Einkauf (z. B. "Zuhause", "Urlaub");
-- everyone = 1: alle Konten sehen sie, sonst nur Ersteller und Mitglieder
CREATE TABLE IF NOT EXISTS shopping_lists (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
everyone INTEGER NOT NULL DEFAULT 1,
position REAL NOT NULL,
created_at TEXT NOT NULL,
created_by INTEGER REFERENCES users(id) ON DELETE SET NULL
);
CREATE TABLE IF NOT EXISTS shopping_list_members (
list_id INTEGER NOT NULL REFERENCES shopping_lists(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
PRIMARY KEY (list_id, user_id)
);
CREATE TABLE IF NOT EXISTS household_items {HOUSEHOLD_TABLE};
-- Produktkatalog für die Vorschläge
@@ -244,15 +263,33 @@ def _import_legacy(conn) -> None:
def _migrate(conn) -> None:
# Vorrat/Einkauf aus der Zeit vor den Einheiten: Tabelle mit Einheit und Kommazahlen neu aufbauen
columns = {r["name"] for r in conn.execute("PRAGMA table_info(household_items)")}
if "unit" in columns:
"""Vorrat/Einkauf aus früheren Versionen (ohne Einheiten, ohne Listen oder Vorrat ohne Liste) neu aufbauen.
Alles, was noch keiner Liste gehört, kommt in die erste Liste bzw. in eine neue Liste "Zuhause",
die alle Konten sehen.
"""
info = {r["name"]: r for r in conn.execute("PRAGMA table_info(household_items)")}
if "list_id" in info and info["list_id"]["notnull"]:
return
row = conn.execute("SELECT id FROM shopping_lists ORDER BY position, id LIMIT 1").fetchone()
if row:
default = row["id"]
# Die Zwischenversion nannte die gemeinsame Liste "Einkaufsliste"; jetzt enthält sie auch den Vorrat
conn.execute("UPDATE shopping_lists SET name = 'Zuhause' WHERE id = ? AND name = 'Einkaufsliste' AND created_by IS NULL", (default,))
else:
default = conn.execute(
"INSERT INTO shopping_lists (name, everyone, position, created_at) VALUES ('Zuhause', 1, 1, ?)", (now_iso(),)
).lastrowid
unit = "unit" if "unit" in info else "'stk'"
list_id = "COALESCE(list_id, ?)" if "list_id" in info else "?"
conn.execute("DROP INDEX IF EXISTS household_vorrat")
conn.execute("DROP INDEX IF EXISTS household_einkauf")
conn.execute("ALTER TABLE household_items RENAME TO household_items_alt")
conn.execute("CREATE TABLE household_items " + HOUSEHOLD_TABLE)
conn.execute(
"""INSERT INTO household_items (id, key, name, list, quantity, unit, updated_at, updated_by)
SELECT id, key, name, list, quantity, 'stk', updated_at, updated_by FROM household_items_alt"""
f"""INSERT INTO household_items (id, key, name, list, list_id, quantity, unit, updated_at, updated_by)
SELECT id, key, name, list, {list_id}, quantity, {unit}, updated_at, updated_by FROM household_items_alt""",
(default,),
)
conn.execute("DROP TABLE household_items_alt")