Backend: Mindestbestand je Lagerort für Produkte und Gruppen
Zusaetzlich zum globalen Mindestbestand: je Produkt und je Gruppe laesst sich pro
Lagerort ein Mindestbestand (in Artikeleinheiten) hinterlegen.
- Neue Tabellen product_location_min_stock / group_location_min_stock.
- PUT /products/{id}/location-min-stock und /groups/{id}/location-min-stock
ersetzen die Eintraege; Produkt-/Gruppen-Ausgabe liefert sie mit.
- Neue Einkaufsliste GET /shopping-list/by-location: Bedarfe je Ort (Produkte +
Gruppen), Bestand-am-Ort gegen Mindestbestand-am-Ort.
- Helfer location_stock_base (Bestand je Ort). 4 neue Tests, Suite 144 gruen.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,7 @@ from sqlalchemy.orm import Session
|
||||
|
||||
from ..database import get_db
|
||||
from ..deps import get_current_user, require_admin
|
||||
from ..models import Barcode, Group, Product, User
|
||||
from ..models import Barcode, Group, GroupLocationMinStock, Location, Product, User
|
||||
from ..schemas import (
|
||||
BarcodeCreate,
|
||||
BarcodeNoteUpdate,
|
||||
@@ -11,6 +11,8 @@ from ..schemas import (
|
||||
GroupCreate,
|
||||
GroupOut,
|
||||
GroupUpdate,
|
||||
LocationMinStockIn,
|
||||
LocationMinStockOut,
|
||||
ProductBarcodeOut,
|
||||
)
|
||||
from ..services.conversion import BASE_OF_KIND
|
||||
@@ -76,6 +78,15 @@ def _group_to_out(db: Session, group: Group) -> GroupOut:
|
||||
out.kind = unit.kind.value
|
||||
else:
|
||||
out.stock = float(sum(current_stock(db, p.id) for p in products))
|
||||
|
||||
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,
|
||||
)
|
||||
for e in sorted(group.location_min_stocks, key=lambda x: x.id)
|
||||
]
|
||||
return out
|
||||
|
||||
|
||||
@@ -87,6 +98,38 @@ def list_groups(
|
||||
return [_group_to_out(db, g) for g in groups]
|
||||
|
||||
|
||||
@router.put("/{group_id}/location-min-stock", response_model=GroupOut)
|
||||
def set_group_location_min_stock(
|
||||
group_id: int,
|
||||
payload: list[LocationMinStockIn],
|
||||
db: Session = Depends(get_db),
|
||||
_: User = Depends(require_admin),
|
||||
) -> GroupOut:
|
||||
"""Gruppen-Mindestbestände je Lagerort ersetzen (Menge 0 = Eintrag entfällt)."""
|
||||
group = db.get(Group, group_id)
|
||||
if group is None:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Gruppe nicht gefunden")
|
||||
|
||||
db.query(GroupLocationMinStock).filter(
|
||||
GroupLocationMinStock.group_id == group_id
|
||||
).delete()
|
||||
gesehen: set[int] = set()
|
||||
for eintrag in payload:
|
||||
if eintrag.min_stock <= 0 or eintrag.location_id in gesehen:
|
||||
continue
|
||||
if db.get(Location, eintrag.location_id) is None:
|
||||
raise HTTPException(status.HTTP_404_NOT_FOUND, "Lagerort nicht gefunden")
|
||||
gesehen.add(eintrag.location_id)
|
||||
db.add(GroupLocationMinStock(
|
||||
group_id=group_id,
|
||||
location_id=eintrag.location_id,
|
||||
min_stock=eintrag.min_stock,
|
||||
))
|
||||
db.commit()
|
||||
db.refresh(group)
|
||||
return _group_to_out(db, group)
|
||||
|
||||
|
||||
@router.post("", response_model=GroupOut, status_code=status.HTTP_201_CREATED)
|
||||
def create_group(
|
||||
payload: GroupCreate,
|
||||
|
||||
Reference in New Issue
Block a user