From 012e3461ae3dc151b43cd72722c6fcd19bc40fcf Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Wed, 29 Jul 2026 14:12:35 +0200 Subject: [PATCH] Backend: Chargenliste - Art (food/object) + nur Charge-Artikel /lots/rows liefert jetzt die Art (Lebensmittel/Gegenstand) mit und listet nur Charge-Artikel (Lebensmittel + Verbrauchsgegenstand). Lots von "Menge je Lagerort" (und etwaige Einzelstueck-Altlasten) fallen raus. Co-Authored-By: Claude Opus 4.8 --- backend/app/routers/stock.py | 16 ++++++++++++---- backend/app/schemas.py | 1 + backend/tests/test_gegenstaende.py | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/backend/app/routers/stock.py b/backend/app/routers/stock.py index 7049e64..804331f 100644 --- a/backend/app/routers/stock.py +++ b/backend/app/routers/stock.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import Session, joinedload from ..crud import product_tracking, resolve_product from ..database import get_db from ..deps import get_current_user -from ..models import CategoryTracking, Location, Lot, Movement, MovementType, User +from ..models import CategoryTracking, Location, Lot, Movement, MovementType, Product, User from ..schemas import ( BatchCheckInRequest, BatchCheckInResponse, @@ -243,20 +243,28 @@ def list_all_lots( db: Session = Depends(get_db), _: User = Depends(get_current_user), ) -> list[LotRow]: - """Alle Chargen über alle Artikel – für die übergreifende Chargenliste mit - Artikelname, Menge und Lagerort. Der Artikel wird eager geladen (kein N+1).""" + """Chargen der Charge-Artikel (Lebensmittel + Verbrauchsgegenstand) über alle + Artikel – für die übergreifende Chargenliste. „Menge je Lagerort" und + Einzelstücke gehören nicht hierher und werden übersprungen. Der Artikel wird + eager geladen (kein N+1).""" lots = ( db.query(Lot) - .options(joinedload(Lot.product)) + .options(joinedload(Lot.product).joinedload(Product.category)) .order_by(Lot.created_at.desc()) .all() ) rows: list[LotRow] = [] for lot in lots: p = lot.product + tracking = product_tracking(db, p) + # Nur Charge-Artikel: Lebensmittel (food) oder Verbrauchsgegenstand (bulk). + food_like = tracking != CategoryTracking.object.value or bool(p.bulk) + if not food_like: + continue name, factor = display_unit_info(p) rows.append(LotRow( id=lot.id, product_id=p.id, product_name=p.name, product_brand=p.brand, + tracking=CategoryTracking(tracking), quantity=lot.quantity, base_unit=p.base_unit, package_size=p.package_size, package_label=p.package_label, unit_name=name, unit_factor=factor, diff --git a/backend/app/schemas.py b/backend/app/schemas.py index bb84d44..05f9788 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -456,6 +456,7 @@ class LotRow(BaseModel): product_id: int product_name: str product_brand: str | None = None + tracking: CategoryTracking = CategoryTracking.food # food / object quantity: float # in Basiseinheiten base_unit: BaseUnit package_size: float | None = None diff --git a/backend/tests/test_gegenstaende.py b/backend/tests/test_gegenstaende.py index 4fb2a97..541bf48 100644 --- a/backend/tests/test_gegenstaende.py +++ b/backend/tests/test_gegenstaende.py @@ -114,6 +114,27 @@ def test_bulk_liste_zaehlt_bestand_wie_einzeln(db): assert gebuendelt[powerbank.id] == 2 +def test_chargenliste_nur_charge_artikel(db): + # Die Chargenliste zeigt nur Lebensmittel + Verbrauchsgegenstände – nicht die + # Lots von „Menge je Lagerort". + from app.routers.stock import list_all_lots + a = _loc(db, "Regal") + food = Product(name="Reis", base_unit=BaseUnit.gram) + db.add(food) + db.flush() + object_add(db, food, 500, a.id, None) + unterhose, _ = _object_product(db, name="Unterhose") # Menge je Lagerort + object_add(db, unterhose, 5, a.id, None) + creme = _bulk_product(db, name="Sonnencreme") # Verbrauchsgegenstand + object_add(db, creme, 200, a.id, None) + db.commit() + + namen = {r.product_name for r in list_all_lots(db=db, _=None)} + assert "Reis" in namen + assert "Sonnencreme" in namen + assert "Unterhose" not in namen + + def test_neue_oberkategorie_ist_gegenstand(db): out = create_category(CategoryCreate(name="Werkzeug"), db, None) assert out.tracking == CategoryTracking.object