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:
Scarriffle
2026-07-27 06:58:08 +02:00
parent 2bc871f229
commit d0d854be5a
8 changed files with 343 additions and 6 deletions

View File

@@ -109,6 +109,19 @@ class UserUpdate(BaseModel):
# ---- Groups ----
class LocationMinStockIn(BaseModel):
"""Ein Mindestbestand-Eintrag je Lagerort (Menge in Artikeleinheiten)."""
location_id: int
min_stock: float = Field(ge=0)
class LocationMinStockOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
location_id: int
location_name: str | None = None
min_stock: float
class GroupOut(BaseModel):
model_config = ConfigDict(from_attributes=True)
id: int
@@ -123,6 +136,8 @@ class GroupOut(BaseModel):
barcodes: list[BarcodeOut] = [] # EANs, die dieser Gruppe zugeordnet sind
# EANs der Artikel in dieser Gruppe - nur zur Anzeige, nicht bearbeitbar.
product_barcodes: list[ProductBarcodeOut] = []
# Mindestbestand je Lagerort (zusaetzlich zum Gesamt-Mindestbestand oben).
location_min_stocks: list[LocationMinStockOut] = []
class GroupCreate(BaseModel):
@@ -351,6 +366,8 @@ class ProductOut(BaseModel):
tracking: CategoryTracking = CategoryTracking.food
shop_name: str | None = None
field_values: dict[int, str | None] = {}
# Mindestbestand je Lagerort (in Artikeleinheiten), zusaetzlich zum globalen.
location_min_stocks: list[LocationMinStockOut] = []
@field_validator("field_values", mode="before")
@classmethod
@@ -606,6 +623,33 @@ class GroupShoppingItem(BaseModel):
product_count: int
class LocationNeedProduct(BaseModel):
"""Ein Produkt-Bedarf an einem Lagerort (Mengen in Artikeleinheiten)."""
product_id: int
name: str
unit_label: str = ""
stock: float
min_stock: float
deficit: float
class LocationNeedGroup(BaseModel):
group_id: int
name: str
unit_name: str = ""
stock: float
min_stock: float
deficit: float
class LocationNeeds(BaseModel):
"""Alle Bedarfe (Produkte + Gruppen) eines Lagerorts."""
location_id: int
location_name: str
products: list[LocationNeedProduct] = []
groups: list[LocationNeedGroup] = []
class MovementOut(BaseModel):
id: int
product_id: int