diff --git a/backend/app/routers/dashboard.py b/backend/app/routers/dashboard.py index 3aed98f..2b31690 100644 --- a/backend/app/routers/dashboard.py +++ b/backend/app/routers/dashboard.py @@ -48,6 +48,7 @@ from ..services.conversion import article_unit from ..services.loans import nicht_verliehen from ..services.stock import current_stock from .settings import get_expiry_warning_days +from ..services import einkaufsliste from .views import einkaufsliste_artikel, einkaufsliste_gruppen router = APIRouter(prefix="/dashboard", tags=["dashboard"]) @@ -373,7 +374,16 @@ def stats( # Einkaufsbedarf: so viele Zeilen, wie auf der Einkaufsliste stehen – # inklusive der Verrechnung zwischen „Überall", Lagerorten und Unter- # gruppen. Frueher wurde hier eigenstaendig gezaehlt und wich deshalb ab. - bedarf = len(einkaufsliste_artikel(db)) + len(einkaufsliste_gruppen(db)) + # + # Dazu die selbst draufgesetzten Eintraege, offene wie die aus einem Rezept + # uebernommenen: Sonst meldet die Kennzahl „alles erreicht", waehrend auf der + # Liste fuenf Posten stehen. Abgehakte zaehlen nicht mehr mit - sie sind + # gekauft. + bedarf = ( + len(einkaufsliste_artikel(db)) + + len(einkaufsliste_gruppen(db)) + + len(einkaufsliste.eintraege(db, mit_erledigten=False)) + ) return DashboardStats( products_in_stock=len(bestand_produkte), diff --git a/backend/tests/test_einkaufsliste_eintraege.py b/backend/tests/test_einkaufsliste_eintraege.py index a611f5a..f1d40f9 100644 --- a/backend/tests/test_einkaufsliste_eintraege.py +++ b/backend/tests/test_einkaufsliste_eintraege.py @@ -29,6 +29,7 @@ from app.routers.shopping import ( set_check, update_entry, ) +from app.routers.dashboard import stats as dashboard_stats from app.routers.views import shopping_list_all from app.schemas import ShoppingEntryIn, ShoppingEntryPatch from app.services import einkaufsliste @@ -288,3 +289,15 @@ def test_ein_abgehakter_eintrag_blockiert_die_neue_uebernahme_nicht(db, nutzer): _uebernehmen(db, [_zutat("Weissmehl", 250, "g")], nutzer) assert db.query(ShoppingEntry).count() == 2 + + +def test_die_kennzahl_zaehlt_die_eigenen_eintraege_mit(db, nutzer): + """Sonst meldet das Dashboard „alles erreicht", waehrend die Liste voll ist.""" + vorher = dashboard_stats(db=db, _=nutzer).shopping_items + + eintrag = create_entry(ShoppingEntryIn(free_text="Backpapier"), db=db, user=nutzer) + assert dashboard_stats(db=db, _=nutzer).shopping_items == vorher + 1 + + # Abgehakt heisst gekauft - und zaehlt nicht mehr als Bedarf. + update_entry(eintrag.id, ShoppingEntryPatch(done=True), db=db, _=nutzer) + assert dashboard_stats(db=db, _=nutzer).shopping_items == vorher diff --git a/web/src/dashboard/cards.jsx b/web/src/dashboard/cards.jsx index 119a53c..7dff2fc 100644 --- a/web/src/dashboard/cards.jsx +++ b/web/src/dashboard/cards.jsx @@ -316,23 +316,59 @@ function KarteAblauf({ modus = "alle" }) { */ function KarteEinkaufsliste({ props: karteProps }) { const ortId = karteProps?.location_id || ""; - const [erledigt, setErledigt] = useState({}); // abgehakte Zeilen (nur in dieser Sitzung) + const [erledigt, setErledigt] = useState(null); // null = noch nicht geladen + const [eintraege, setEintraege] = useState([]); + // Ein Aufruf statt drei, und er bringt beides mit: die gerechneten Bedarfe + // UND die selbst draufgesetzten Eintraege samt dauerhaft gesetzter Haken. + // Vorher kannte die Karte nur die gerechnete Seite - was aus einem Rezept + // uebernommen wurde, stand nur im Tab und hier nicht. const { daten, fehler } = useDaten(async () => { - const [produkte, gruppen, orte] = await Promise.all([ - api.shoppingList(), api.groupShoppingList(), api.shoppingByLocation(), - ]); - return { produkte, gruppen, orte }; + const alles = await api.shoppingAll(); + return { + produkte: alles.products || [], gruppen: alles.groups || [], + orte: alles.by_location || [], eintraege: alles.entries || [], + checked: alles.checked || [], + }; }); + useEffect(() => { + if (!daten) return; + setErledigt(Object.fromEntries((daten.checked || []).map((k) => [k, true]))); + setEintraege(daten.eintraege || []); + }, [daten]); + if (fehler) return