Mindestbestaende: Bestand je Lagerort anzeigen (nicht nur Gesamt)

Per-Lagerort-Zeilen (Produkt und Gruppe) zeigten '-' beim Bestand, weil die Seite
nur den Gesamtbestand kannte. Jetzt liefert LocationMinStockOut.stock den Bestand
AN DEM Ort (inkl. Unterorte) mit - bei Produkten in Basiseinheiten, bei Gruppen in
der Gruppen-Einheit (analog zu ProductOut.stock/GroupOut.stock). Berechnet ueber
location_subtree_stock_base; die Weboberflaeche zeigt ihn wie beim Gesamt-Bestand.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-30 13:57:40 +02:00
parent 4ed33e221f
commit f3bab15362
4 changed files with 22 additions and 4 deletions

View File

@@ -12,7 +12,7 @@ from sqlalchemy.orm import Session
from .models import Barcode, Category, CategoryTracking, Item, Lot, Product, ProductImage
from .schemas import BarcodeOut, LocationMinStockOut, ProductOut
from .services.conversion import KIND_OF_BASE, display_unit_info
from .services.stock import current_stock
from .services.stock import current_stock, location_subtree_stock_base
def product_tracking(db: Session, product: Product) -> str:
@@ -85,6 +85,7 @@ def product_to_out(db: Session, product: Product) -> ProductOut:
location_id=e.location_id,
location_name=e.location.name if e.location else None,
min_stock=e.min_stock,
stock=location_subtree_stock_base(db, product, e.location_id),
)
for e in sorted(product.location_min_stocks, key=lambda x: x.id)
]
@@ -165,6 +166,7 @@ def products_to_out_bulk(db: Session, products: list[Product]) -> list[ProductOu
location_id=e.location_id,
location_name=e.location.name if e.location else None,
min_stock=e.min_stock,
stock=location_subtree_stock_base(db, product, e.location_id),
)
for e in sorted(product.location_min_stocks, key=lambda x: x.id)
]

View File

@@ -16,7 +16,7 @@ from ..schemas import (
ProductBarcodeOut,
)
from ..services.conversion import BASE_OF_KIND
from ..services.stock import current_stock
from ..services.stock import current_stock, location_subtree_stock_base
router = APIRouter(prefix="/groups", tags=["groups"])
@@ -77,13 +77,20 @@ def _group_to_out(db: Session, group: Group) -> GroupOut:
out.min_stock_unit_name = unit.name
out.kind = unit.kind.value
else:
matching = list(products)
out.stock = float(sum(current_stock(db, p.id) for p in products))
# Bestand je Lagerort (inkl. Unterorte) in derselben Einheit wie out.stock.
def _loc_stock(loc_id: str) -> float:
total = sum(location_subtree_stock_base(db, p, loc_id) for p in matching)
return total / unit.factor if unit is not None else float(total)
out.location_min_stocks = [
LocationMinStockOut(
location_id=e.location_id,
location_name=e.location.name if e.location else None,
min_stock=e.min_stock,
stock=_loc_stock(e.location_id),
)
for e in sorted(group.location_min_stocks, key=lambda x: x.id)
]

View File

@@ -120,6 +120,11 @@ class LocationMinStockOut(BaseModel):
location_id: str
location_name: str | None = None
min_stock: float
# Bestand AN DIESEM Ort (inkl. Unterorte): bei Produkten in Basiseinheiten
# (wie ``ProductOut.stock``), bei Gruppen in der Gruppen-Einheit (wie
# ``GroupOut.stock``). So kann die Oberfläche auch je Lagerort einen Bestand
# zeigen, nicht nur „Gesamt".
stock: float | None = None
class GroupOut(BaseModel):

View File

@@ -73,7 +73,9 @@ export default function MinStock() {
locId: l.location_id, name: p.name, catId: p.category_id,
ort: locationPathById(l.location_id, locations) || "?",
// je-Lagerort ist in Artikeleinheiten gespeichert → in Anzeigeeinheit umrechnen.
soll: (l.min_stock * articleUnit(p)) / dispFactor(p), unit: dispLabel(p), bestand: null,
soll: (l.min_stock * articleUnit(p)) / dispFactor(p), unit: dispLabel(p),
// Bestand kommt in Basiseinheiten (wie der Gesamt-Bestand) → in Anzeigeeinheit.
bestand: l.stock != null ? l.stock / dispFactor(p) : null, bestandUnit: dispLabel(p),
});
}
}
@@ -88,7 +90,9 @@ export default function MinStock() {
key: `g:${g.id}:${l.location_id}`, kind: "group", scope: "loc", entity: g,
locId: l.location_id, name: g.name, catId: null,
ort: locationPathById(l.location_id, locations) || "?",
soll: l.min_stock, unit: g.min_stock_unit_name || "", bestand: null,
// Gruppen-Bestand je Ort kommt schon in der Gruppen-Einheit (wie Gesamt).
soll: l.min_stock, unit: g.min_stock_unit_name || "",
bestand: l.stock ?? null, bestandUnit: g.min_stock_unit_name || "",
});
}
}