Einkaufsliste: Fehlmenge als Gebinde (Glaeser/Dosen), Basiseinheit klein

Statt 'fehlt 500 Gramm' jetzt die Produktgroesse als Leitangabe:
'fehlt 3 Glaeser (500 g)'. Auf ganze Gebinde aufgerundet, weil man nur
ganze Glaeser/Dosen kauft; Basiseinheit als kleiner Hinweis.

Backend: neues ShoppingNeed-Objekt an jeder Einkaufslisten-Zeile (Produkte
+ Gruppen, gesamt + je Ort) - damit die Angabe auch direkt ueber die API
kommt ('x Glaeser (y g)'), inkl. Pluralisierung aus der Gebinde-Tabelle.
Gruppen leiten das Gebinde ab, wenn alle passenden Produkte dasselbe haben,
sonst bleibt es bei der Basiseinheit. Bestehende Felder unveraendert.

Frontend: gemeinsame Komponente ShoppingNeedText fuer Karte und volle Seite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-07 18:03:37 +02:00
parent e214bb76a9
commit f2c657ff31
6 changed files with 294 additions and 29 deletions

View File

@@ -0,0 +1,66 @@
"""Einkaufsliste: Fehlmenge als Gebinde-Leitangabe (auf ganze Packungen
aufgerundet) mit der Basiseinheit als kleinem Hinweis „3 Gläser (500 g)".
"""
import pytest
from app.models import BaseUnit, Group, PackageType, Product, Role, Unit, User
from app.routers.views import group_shopping_list, shopping_list
@pytest.fixture()
def user(db):
person = User(username="tester", password_hash="x", role=Role.admin)
db.add(person)
db.commit()
db.refresh(person)
return person
def _gramm(db) -> Unit:
return db.query(Unit).filter(Unit.name == "Gramm").one()
def test_produkt_bedarf_in_ganzen_gebinden(db, user):
"""500 g fehlen bei 195-g-Gläsern -> 3 Gläser (aufgerundet), 500 g als Hinweis."""
db.add(PackageType(singular="Glas", plural="Gläser"))
db.add(Product(name="Pesto", base_unit=BaseUnit.gram, package_size=195,
package_label="Glas", min_stock=500))
db.commit() # kein Bestand -> es fehlt der volle Mindestbestand
(item,) = shopping_list(db=db, _=user)
assert item.need is not None
assert item.need.is_package is True
assert item.need.count == 3 # ceil(500 / 195)
assert item.need.text == "3 Gläser"
assert item.need.hint == "500 g"
def test_gruppe_mit_einheitlichem_gebinde(db, user):
"""Eine Gruppe, deren Produkte alle dasselbe Gebinde haben, zeigt es auch."""
db.add(PackageType(singular="Glas", plural="Gläser"))
gruppe = Group(name="Pesto alla Genovese", min_stock=500, min_stock_unit=_gramm(db))
db.add(gruppe)
db.add(Product(name="Pesto Barilla", base_unit=BaseUnit.gram, package_size=195,
package_label="Glas", group=gruppe))
db.commit()
(item,) = group_shopping_list(db=db, _=user)
assert item.need is not None
assert item.need.is_package is True
assert item.need.text == "3 Gläser"
assert item.need.hint == "500 g"
def test_gruppe_ohne_gebinde_bleibt_basiseinheit(db, user):
"""Ohne (einheitliches) Gebinde bleibt es bei der Basiseinheit kein Rateraten."""
gruppe = Group(name="Mehl", min_stock=500, min_stock_unit=_gramm(db))
db.add(gruppe)
db.add(Product(name="Mehl 405", base_unit=BaseUnit.gram, package_size=None, group=gruppe))
db.commit()
(item,) = group_shopping_list(db=db, _=user)
assert item.need is not None
assert item.need.is_package is False
assert item.need.text == "500 Gramm"
assert item.need.hint == ""