diff --git a/backend/app/main.py b/backend/app/main.py index 071804c..2796e3e 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -41,6 +41,7 @@ def _ensure_schema() -> None: "REFERENCES units(id) ON DELETE SET NULL", "ALTER TABLE products ADD COLUMN IF NOT EXISTS min_stock_in_packages BOOLEAN " "NOT NULL DEFAULT FALSE", + "ALTER TABLE products ADD COLUMN IF NOT EXISTS package_label VARCHAR(32)", ] with engine.begin() as conn: for stmt in stmts: diff --git a/backend/app/models.py b/backend/app/models.py index 255a29c..9f277ab 100644 --- a/backend/app/models.py +++ b/backend/app/models.py @@ -119,6 +119,8 @@ class Product(Base): ) # Number of base units contained in one package (e.g. 500 g per package). package_size: Mapped[float | None] = mapped_column(Float, nullable=True) + # Bezeichnung eines Gebindes: "Packung", "Glas", "Tüte", "Flasche", … + package_label: Mapped[str | None] = mapped_column(String(32), nullable=True) group_id: Mapped[int | None] = mapped_column( ForeignKey("groups.id", ondelete="SET NULL"), nullable=True diff --git a/backend/app/routers/products.py b/backend/app/routers/products.py index 1cea035..ef990a6 100644 --- a/backend/app/routers/products.py +++ b/backend/app/routers/products.py @@ -82,6 +82,7 @@ def create_product( base_unit=base_unit, display_unit_id=display_unit_id, package_size=payload.package_size, + package_label=payload.package_label or None, group_id=payload.group_id, min_stock=payload.min_stock, min_stock_unit_id=payload.min_stock_unit_id, diff --git a/backend/app/schemas.py b/backend/app/schemas.py index 2e5b92e..b032c72 100644 --- a/backend/app/schemas.py +++ b/backend/app/schemas.py @@ -99,6 +99,8 @@ class ProductBase(BaseModel): # Optionale verwaltete Einheit; setzt base_unit anhand ihrer Art und die Anzeigeeinheit. unit_id: int | None = None package_size: float | None = Field(default=None, gt=0) + # Bezeichnung eines Gebindes ("Packung", "Glas", "Tüte", …) + package_label: str | None = Field(default=None, max_length=32) group_id: int | None = None min_stock: float | None = Field(default=None, ge=0) # immer in Basiseinheiten # Nur für die Anzeige: in welcher Einheit der Mindestbestand erfasst wurde. @@ -118,6 +120,7 @@ class ProductUpdate(BaseModel): base_unit: BaseUnit | None = None unit_id: int | None = None package_size: float | None = Field(default=None, gt=0) + package_label: str | None = Field(default=None, max_length=32) group_id: int | None = None min_stock: float | None = Field(default=None, ge=0) min_stock_unit_id: int | None = None @@ -134,6 +137,7 @@ class ProductOut(BaseModel): base_unit: BaseUnit display_unit_id: int | None = None package_size: float | None + package_label: str | None = None group_id: int | None min_stock: float | None min_stock_unit_id: int | None = None diff --git a/web/src/pages/ProductForm.jsx b/web/src/pages/ProductForm.jsx index 058e58d..dfa8541 100644 --- a/web/src/pages/ProductForm.jsx +++ b/web/src/pages/ProductForm.jsx @@ -8,9 +8,12 @@ import { daysUntil, expiryRowClass, fmt, isExpired, unitShort } from "../units"; const EMPTY = { barcode: "", name: "", brand: "", image_url: "", - unit_id: "", package_size: "", min_stock: "", group_id: "", + unit_id: "", package_size: "", package_label: "", min_stock: "", group_id: "", }; +// Vorschläge für die Gebinde-Bezeichnung (frei überschreibbar). +const PACKAGE_LABELS = ["Packung", "Glas", "Tüte", "Flasche", "Dose", "Tube", "Becher", "Beutel", "Karton"]; + // Kanonische Einheit je Basiseinheit (für OFF-Vorschläge und Altbestand). const CANONICAL_NAME = { piece: "Stück", gram: "Gramm", milliliter: "Milliliter" }; @@ -135,7 +138,7 @@ export default function ProductForm() { setForm({ barcode: p.barcode || "", name: p.name, brand: p.brand || "", image_url: p.image_url || "", unit_id: uid, - package_size: p.package_size ?? "", + package_size: p.package_size ?? "", package_label: p.package_label || "", min_stock: p.min_stock != null ? p.min_stock / f : "", group_id: p.group_id ?? "", }); @@ -168,6 +171,7 @@ export default function ProductForm() { image_url: form.image_url || null, unit_id: form.unit_id === "" ? null : Number(form.unit_id), package_size: form.package_size === "" ? null : Number(form.package_size), + package_label: form.package_label.trim() || null, min_stock: minBase, min_stock_unit_id: sel === "package" || sel === "" ? null : Number(sel), min_stock_in_packages: sel === "package", @@ -271,6 +275,18 @@ export default function ProductForm() { onChange={(e) => set("package_size", e.target.value)} disabled={readOnly} placeholder="z.B. 500" /> +
+ +
+