Gruppen-Gebinde: Mindestbestand/Bestand in Packungen (Backend)

Gruppen bekommen ein eigenes Richt-Gebinde (package_size/label) plus
min_stock_in_packages. Weil die Produkte einer Gruppe unterschiedlich
große Packungen haben können (Pesto 99 g vs. 160 g), legt die Gruppe einen
gemeinsamen Richtwert fest (1 Glas ≈ X g).

- Neuer Helfer group_min_context() zentralisiert, in welcher Einheit der
  Gruppen-Mindestbestand zaehlt (Gebinde ODER verwaltete Einheit).
- Einkaufsliste (gesamt + je Ort), Dashboard-Bedarf und GroupOut nutzen ihn;
  need.text kommt so als 'x Glaeser (y g)'.
- update_group rechnet bestehende Werte beim Umschalten der Einheit um, damit
  der physische Bedarf gleich bleibt.
- Migration: groups.package_size/package_label/min_stock_in_packages.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-07 18:38:12 +02:00
co-authored by Claude Opus 4.8
parent 28785b0a10
commit ecc1570100
8 changed files with 168 additions and 128 deletions
+34 -10
View File
@@ -1,11 +1,17 @@
"""Einkaufsliste: Fehlmenge als Gebinde-Leitangabe (auf ganze Packungen
aufgerundet) mit der Basiseinheit als kleinem Hinweis – „3 Gläser (500 g)".
aufgerundet) mit der Basiseinheit als kleinem Hinweis – „3 Gläser (450 g)".
Produkte tragen ihre eigene Packungsgröße; Gruppen legen ein eigenes
Gruppen-Gebinde als Richtwert fest (weil ihre Produkte unterschiedlich große
Packungen haben können).
"""
import pytest
from app.models import BaseUnit, Group, PackageType, Product, Role, Unit, User
from app.routers.groups import update_group
from app.routers.views import group_shopping_list, shopping_list
from app.schemas import GroupUpdate
@pytest.fixture()
@@ -36,27 +42,28 @@ def test_produkt_bedarf_in_ganzen_gebinden(db, user):
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."""
def test_gruppe_mit_gruppen_gebinde(db, user):
"""Gruppe mit eigenem Richt-Gebinde (1 Glas ≈ 150 g), Mindestbestand 3 Gläser."""
db.add(PackageType(singular="Glas", plural="Gläser"))
gruppe = Group(name="Pesto alla Genovese", min_stock=500, min_stock_unit=_gramm(db))
gruppe = Group(name="Pesto alla Genovese", min_stock=3, min_stock_unit=_gramm(db),
package_size=150, package_label="Glas", min_stock_in_packages=True)
db.add(gruppe)
db.add(Product(name="Pesto Barilla", base_unit=BaseUnit.gram, package_size=195,
package_label="Glas", group=gruppe))
db.commit()
db.add(Product(name="Pesto Barilla", base_unit=BaseUnit.gram, package_size=99, group=gruppe))
db.commit() # kein Bestand -> es fehlen 3 Gläser (450 g)
(item,) = group_shopping_list(db=db, _=user)
assert item.need is not None
assert item.need.is_package is True
assert item.need.count == 3
assert item.need.text == "3 Gläser"
assert item.need.hint == "500 g"
assert item.need.hint == "450 g" # 3 * 150 g
def test_gruppe_ohne_gebinde_bleibt_basiseinheit(db, user):
"""Ohne (einheitliches) Gebinde bleibt es bei der Basiseinheit – kein Rateraten."""
"""Ohne aktives Gruppen-Gebinde bleibt es bei der verwalteten Einheit."""
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.add(Product(name="Mehl 405", base_unit=BaseUnit.gram, group=gruppe))
db.commit()
(item,) = group_shopping_list(db=db, _=user)
@@ -64,3 +71,20 @@ def test_gruppe_ohne_gebinde_bleibt_basiseinheit(db, user):
assert item.need.is_package is False
assert item.need.text == "500 Gramm"
assert item.need.hint == ""
def test_gruppe_umschalten_haelt_physischen_bedarf(db, user):
"""Umschalten g -> Gebinde rechnet den gespeicherten Wert um (500 g / 150 = 3,33 Glas)."""
gruppe = Group(name="Pesto", min_stock=500, min_stock_unit=_gramm(db))
db.add(gruppe)
db.add(Product(name="Pesto Barilla", base_unit=BaseUnit.gram, group=gruppe))
db.commit()
update_group(
group_id=gruppe.id,
payload=GroupUpdate(min_stock_in_packages=True, package_size=150, package_label="Glas"),
db=db, _=user,
)
db.refresh(gruppe)
assert gruppe.min_stock_in_packages is True
assert gruppe.min_stock == pytest.approx(500 / 150, abs=0.01) # ~3,33 Gläser