Einkauf: Abhaken legt Produkte in den Wagen, "Einkauf beenden" übernimmt sie

- Abgehakte Produkte bleiben auf der Einkaufsliste, rutschen durchgestrichen
  nach unten ("Im Wagen") und lassen sich zurücknehmen
- Button "Einkauf beenden (n)" verschiebt alle abgehakten Produkte auf einmal
  in den Vorrat derselben Liste (Mengen werden zusammengeführt)
- Haken wird gespeichert (Spalte checked, Migration ergänzt sie) und ist für
  alle sichtbar, die die Liste sehen; erneut hinzugefügte Produkte verlieren
  den Haken
- Einträge gleiten per View Transition an ihren neuen Platz (aus bei
  "Animation: aus")

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
guyg1474andClaude Opus 5.5 committed 2026-10-15 12:11:13 +02:00
1 parent b30acb652a
commit f0e17b06b5
6 files changed
+109 -13

No files matched your search

+34 -2
View File
@@ -34,6 +34,7 @@ def _item(row) -> dict:
"listId": row["list_id"],
"quantity": _out_quantity(row["quantity"], row["unit"]),
"unit": row["unit"],
"checked": bool(row["checked"]),
"updatedAt": row["updated_at"],
"updatedBy": {"id": row["updated_by"], "name": row["updated_by_name"]} if row["updated_by"] else None,
}
@@ -102,12 +103,16 @@ def _canonical_name(conn, name: str) -> str:
def _add(conn, name: str, list_: str, list_id: int, quantity, unit: str, user_id: int) -> None:
"""Produkt in den Vorrat bzw. Einkauf einer Liste setzen; steht es dort schon in derselben Einheit, wird die Menge erhöht."""
"""Produkt in den Vorrat bzw. Einkauf einer Liste setzen; steht es dort schon in derselben Einheit, wird die Menge erhöht.
Kommt ein schon abgehaktes Produkt erneut auf die Einkaufsliste, ist der Haken wieder weg (es fehlt ja noch etwas).
"""
conn.execute(
"""INSERT INTO household_items (key, name, list, list_id, quantity, unit, updated_at, updated_by)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
ON CONFLICT (list_id, key, list, unit) DO UPDATE SET
quantity = MIN(ROUND(quantity + excluded.quantity, 2), ?),
checked = 0,
updated_at = excluded.updated_at,
updated_by = excluded.updated_by""",
(_key(name), name, list_, list_id, quantity, unit, now_iso(), user_id, MAX_QUANTITY),
@@ -237,9 +242,20 @@ def add_item(data: dict = Body(...), user: dict = Depends(current_user)):
@router.patch("/api/haushalt/items/{item_id}")
def update_item(item_id: int, data: dict = Body(...), user: dict = Depends(current_user)):
"""Menge und/oder Einheit ändern. Beim Einheitenwechsel wird mit einer bestehenden Zeile zusammengeführt."""
"""Menge und/oder Einheit ändern bzw. abhaken. Beim Einheitenwechsel wird mit einer bestehenden Zeile zusammengeführt."""
with transaction() as conn:
row = _get_item(conn, item_id, user)
if "checked" in data:
# Abhaken: liegt im Wagen, bleibt aber bis "Einkauf beenden" auf der Einkaufsliste
if not isinstance(data["checked"], bool) or row["list"] != "einkauf":
raise ValidationError("Nur Einträge auf der Einkaufsliste lassen sich abhaken.")
conn.execute(
"UPDATE household_items SET checked = ?, updated_at = ?, updated_by = ? WHERE id = ?",
(int(data["checked"]), now_iso(), user["id"], item_id),
)
if len(data) == 1:
return _state(conn, user)
row = _get_item(conn, item_id, user)
unit = _unit(data.get("unit", row["unit"]))
if "quantity" in data:
quantity = _quantity(data["quantity"], unit)
@@ -274,6 +290,22 @@ def move_item(item_id: int, user: dict = Depends(current_user)):
return _state(conn, user)
@router.post("/api/haushalt/listen/{list_id}/einkauf-beenden")
def finish_shopping(list_id: int, user: dict = Depends(current_user)):
"""Alle abgehakten Einträge der Einkaufsliste auf einmal in den Vorrat dieser Liste übernehmen."""
with transaction() as conn:
_get_list(conn, list_id, user)
rows = conn.execute(
"SELECT * FROM household_items WHERE list_id = ? AND list = 'einkauf' AND checked = 1", (list_id,)
).fetchall()
if not rows:
raise ValidationError("Nichts abgehakt – vielleicht hat gerade jemand anderes den Einkauf beendet.")
for row in rows:
conn.execute("DELETE FROM household_items WHERE id = ?", (row["id"],))
_add(conn, row["name"], "vorrat", list_id, row["quantity"], row["unit"], user["id"])
return {**_state(conn, user), "moved": len(rows)}
@router.delete("/api/haushalt/items/{item_id}")
def delete_item(item_id: int, user: dict = Depends(current_user)):
with transaction() as conn: