"""Die gespeicherte Seite der Einkaufsliste: eigene Einträge und Haken. Die gerechnete Liste steht in ``routers/views.py`` und bleibt dort. Dieser Router verwaltet nur, was jemand selbst draufgesetzt hat — und das Abhaken, das bisher im Browser verfiel und nach dem Neuladen mitten im Supermarkt weg war. """ # ACHTUNG: Kein ``from __future__ import annotations`` – siehe package_types.py. from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from ..database import get_db from ..deps import get_current_user from ..models import ShoppingEntry, User from ..schemas import ShoppingEntryIn, ShoppingEntryOut, ShoppingEntryPatch from ..services import einkaufsliste router = APIRouter(prefix="/shopping-list", tags=["shopping"]) def eintrag_out(db: Session, eintrag: ShoppingEntry) -> ShoppingEntryOut: art = ( "product" if eintrag.product_id is not None else "group" if eintrag.group_id is not None else "text" ) return ShoppingEntryOut( id=eintrag.id, art=art, product_id=eintrag.product_id, group_id=eintrag.group_id, free_text=eintrag.free_text, name=einkaufsliste.bezeichnung(db, eintrag), quantity_base=eintrag.quantity_base, base=eintrag.base, menge_text=einkaufsliste.mengen_text(db, eintrag), source=eintrag.source, recipe_slug=eintrag.recipe_slug, recipe_name=eintrag.recipe_name, done=eintrag.done, created_at=eintrag.created_at, ) @router.get("/entries", response_model=list[ShoppingEntryOut]) def list_entries( db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> list[ShoppingEntryOut]: """Die eigenen Einträge, offene zuerst.""" return [eintrag_out(db, e) for e in einkaufsliste.eintraege(db)] @router.post("/entries", response_model=ShoppingEntryOut, status_code=status.HTTP_201_CREATED) def create_entry( payload: ShoppingEntryIn, db: Session = Depends(get_db), user: User = Depends(get_current_user), ) -> ShoppingEntryOut: """Etwas auf die Liste setzen – Artikel, Gruppe oder einfach Text.""" try: eintrag = einkaufsliste.anlegen( db, product_id=payload.product_id, group_id=payload.group_id, free_text=payload.free_text, quantity_base=payload.quantity_base, base=(payload.base.value if payload.base is not None else None), user=user, ) except einkaufsliste.EintragFehler as fehler: raise HTTPException(status.HTTP_400_BAD_REQUEST, str(fehler)) from fehler return eintrag_out(db, eintrag) @router.patch("/entries/{entry_id}", response_model=ShoppingEntryOut) def update_entry( entry_id: int, payload: ShoppingEntryPatch, db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> ShoppingEntryOut: """Abhaken oder die Menge korrigieren.""" eintrag = db.get(ShoppingEntry, entry_id) if eintrag is None: raise HTTPException(status.HTTP_404_NOT_FOUND, "Eintrag nicht gefunden") if payload.quantity_base is not None: eintrag.quantity_base = payload.quantity_base if payload.free_text is not None: if eintrag.free_text is None: raise HTTPException( status.HTTP_400_BAD_REQUEST, "Dieser Eintrag zeigt auf einen Artikel – sein Text lässt sich " "nicht ändern.", ) eintrag.free_text = payload.free_text.strip()[:120] if payload.done is not None: einkaufsliste.abhaken(db, eintrag, payload.done) else: db.commit() db.refresh(eintrag) return eintrag_out(db, eintrag) @router.delete("/entries/{entry_id}", status_code=status.HTTP_204_NO_CONTENT) def delete_entry( entry_id: int, db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> None: eintrag = db.get(ShoppingEntry, entry_id) if eintrag is None: raise HTTPException(status.HTTP_404_NOT_FOUND, "Eintrag nicht gefunden") einkaufsliste.loeschen(db, eintrag) @router.post("/clear-done", response_model=int) def clear_done( db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> int: """Nach dem Einkauf aufräumen: alles Abgehakte weg. Bewusst ein POST und kein DELETE auf einer Sammlung: Es löscht zwei verschiedene Dinge (eigene Einträge und Haken an gerechneten Posten), und das ist eine Handlung, keine Ressource. """ return einkaufsliste.erledigte_entfernen(db) @router.put("/checks/{key}", status_code=status.HTTP_204_NO_CONTENT) def set_check( key: str, db: Session = Depends(get_db), user: User = Depends(get_current_user), ) -> None: """Einen gerechneten Posten abhaken – dauerhaft. Der Schlüssel kommt aus der Oberfläche („p12", „g3", „lKUECHEp12"). Der Server prüft ihn nicht gegen die gerechnete Liste: Sie ändert sich mit jedem Einlagern, und ein Haken auf einen Posten, der gerade nicht mehr fehlt, schadet nichts — er verschwindet mit „Erledigte entfernen". """ try: einkaufsliste.haken_setzen(db, key, user) except einkaufsliste.EintragFehler as fehler: raise HTTPException(status.HTTP_400_BAD_REQUEST, str(fehler)) from fehler @router.delete("/checks/{key}", status_code=status.HTTP_204_NO_CONTENT) def clear_check( key: str, db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> None: einkaufsliste.haken_loesen(db, key)