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 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-29 14:12:35 +02:00
parent 5550897319
commit 012e3461ae
3 changed files with 34 additions and 4 deletions

View File

@@ -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,

View File

@@ -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

View File

@@ -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