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:
@@ -12,7 +12,7 @@ from sqlalchemy.orm import Session
|
|||||||
from .models import Barcode, Category, CategoryTracking, Item, Lot, Product, ProductImage
|
from .models import Barcode, Category, CategoryTracking, Item, Lot, Product, ProductImage
|
||||||
from .schemas import BarcodeOut, LocationMinStockOut, ProductOut
|
from .schemas import BarcodeOut, LocationMinStockOut, ProductOut
|
||||||
from .services.conversion import KIND_OF_BASE, display_unit_info
|
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:
|
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_id=e.location_id,
|
||||||
location_name=e.location.name if e.location else None,
|
location_name=e.location.name if e.location else None,
|
||||||
min_stock=e.min_stock,
|
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)
|
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_id=e.location_id,
|
||||||
location_name=e.location.name if e.location else None,
|
location_name=e.location.name if e.location else None,
|
||||||
min_stock=e.min_stock,
|
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)
|
for e in sorted(product.location_min_stocks, key=lambda x: x.id)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from ..schemas import (
|
|||||||
ProductBarcodeOut,
|
ProductBarcodeOut,
|
||||||
)
|
)
|
||||||
from ..services.conversion import BASE_OF_KIND
|
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"])
|
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.min_stock_unit_name = unit.name
|
||||||
out.kind = unit.kind.value
|
out.kind = unit.kind.value
|
||||||
else:
|
else:
|
||||||
|
matching = list(products)
|
||||||
out.stock = float(sum(current_stock(db, p.id) for p in 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 = [
|
out.location_min_stocks = [
|
||||||
LocationMinStockOut(
|
LocationMinStockOut(
|
||||||
location_id=e.location_id,
|
location_id=e.location_id,
|
||||||
location_name=e.location.name if e.location else None,
|
location_name=e.location.name if e.location else None,
|
||||||
min_stock=e.min_stock,
|
min_stock=e.min_stock,
|
||||||
|
stock=_loc_stock(e.location_id),
|
||||||
)
|
)
|
||||||
for e in sorted(group.location_min_stocks, key=lambda x: x.id)
|
for e in sorted(group.location_min_stocks, key=lambda x: x.id)
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -120,6 +120,11 @@ class LocationMinStockOut(BaseModel):
|
|||||||
location_id: str
|
location_id: str
|
||||||
location_name: str | None = None
|
location_name: str | None = None
|
||||||
min_stock: float
|
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):
|
class GroupOut(BaseModel):
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ export default function MinStock() {
|
|||||||
locId: l.location_id, name: p.name, catId: p.category_id,
|
locId: l.location_id, name: p.name, catId: p.category_id,
|
||||||
ort: locationPathById(l.location_id, locations) || "?",
|
ort: locationPathById(l.location_id, locations) || "?",
|
||||||
// je-Lagerort ist in Artikeleinheiten gespeichert → in Anzeigeeinheit umrechnen.
|
// 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,
|
key: `g:${g.id}:${l.location_id}`, kind: "group", scope: "loc", entity: g,
|
||||||
locId: l.location_id, name: g.name, catId: null,
|
locId: l.location_id, name: g.name, catId: null,
|
||||||
ort: locationPathById(l.location_id, locations) || "?",
|
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 || "",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user