Mindestbestand in jeder angelegten Einheit erfassbar (und gemerkt)
- Produktformular: Auswahl aller verwalteten Einheiten passender Art (plus Packungen), inkl. Umrechnung beim Wechsel. - Die gewaehlte Erfassungseinheit wird gespeichert (products.min_stock_unit_id / min_stock_in_packages) und exakt so wieder angezeigt - vorher wurde sie aus der Produkteinheit abgeleitet. - ProductOut liefert min_stock_display + min_stock_unit_label; Produkttabelle zeigt den Mindestbestand damit in der erfassten Einheit mit Einheit dahinter. - Migration: ADD COLUMN IF NOT EXISTS fuer die zwei neuen Spalten. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,18 @@ def product_to_out(db: Session, product: Product) -> ProductOut:
|
||||
name, factor = display_unit_info(product)
|
||||
out.unit_name = name
|
||||
out.unit_factor = factor
|
||||
|
||||
# Mindestbestand in der Einheit anzeigen, in der er erfasst wurde.
|
||||
if product.min_stock is not None:
|
||||
if product.min_stock_in_packages and product.package_size:
|
||||
out.min_stock_display = product.min_stock / product.package_size
|
||||
out.min_stock_unit_label = "Pkg"
|
||||
elif product.min_stock_unit is not None:
|
||||
out.min_stock_display = product.min_stock / product.min_stock_unit.factor
|
||||
out.min_stock_unit_label = product.min_stock_unit.name
|
||||
else:
|
||||
out.min_stock_display = product.min_stock / factor
|
||||
out.min_stock_unit_label = name
|
||||
return out
|
||||
|
||||
|
||||
|
||||
@@ -37,6 +37,10 @@ def _ensure_schema() -> None:
|
||||
"REFERENCES units(id) ON DELETE SET NULL",
|
||||
"ALTER TABLE groups ADD COLUMN IF NOT EXISTS min_stock_unit_id INTEGER "
|
||||
"REFERENCES units(id) ON DELETE SET NULL",
|
||||
"ALTER TABLE products ADD COLUMN IF NOT EXISTS min_stock_unit_id INTEGER "
|
||||
"REFERENCES units(id) ON DELETE SET NULL",
|
||||
"ALTER TABLE products ADD COLUMN IF NOT EXISTS min_stock_in_packages BOOLEAN "
|
||||
"NOT NULL DEFAULT FALSE",
|
||||
]
|
||||
with engine.begin() as conn:
|
||||
for stmt in stmts:
|
||||
|
||||
@@ -124,13 +124,21 @@ class Product(Base):
|
||||
ForeignKey("groups.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
min_stock: Mapped[float | None] = mapped_column(Float, nullable=True) # in base units
|
||||
# In welcher Einheit der Mindestbestand erfasst wurde (nur für die Anzeige).
|
||||
min_stock_unit_id: Mapped[int | None] = mapped_column(
|
||||
ForeignKey("units.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
min_stock_in_packages: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False, default=False
|
||||
)
|
||||
|
||||
source: Mapped[str] = mapped_column(String(16), nullable=False, default="manual")
|
||||
off_raw: Mapped[str | None] = mapped_column(Text, nullable=True) # JSON blob from OFF
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=_now)
|
||||
|
||||
group: Mapped[Group | None] = relationship(back_populates="products")
|
||||
display_unit: Mapped[Unit | None] = relationship()
|
||||
display_unit: Mapped[Unit | None] = relationship(foreign_keys=[display_unit_id])
|
||||
min_stock_unit: Mapped[Unit | None] = relationship(foreign_keys=[min_stock_unit_id])
|
||||
lots: Mapped[list[Lot]] = relationship(
|
||||
back_populates="product", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
@@ -84,6 +84,8 @@ def create_product(
|
||||
package_size=payload.package_size,
|
||||
group_id=payload.group_id,
|
||||
min_stock=payload.min_stock,
|
||||
min_stock_unit_id=payload.min_stock_unit_id,
|
||||
min_stock_in_packages=bool(payload.min_stock_in_packages),
|
||||
source="manual",
|
||||
)
|
||||
db.add(product)
|
||||
@@ -125,6 +127,8 @@ def update_product(
|
||||
product.base_unit, product.display_unit_id = resolve_product_unit(db, unit_id)
|
||||
except ConversionError as exc:
|
||||
raise HTTPException(status.HTTP_400_BAD_REQUEST, str(exc)) from exc
|
||||
if data.get("min_stock_in_packages") is None:
|
||||
data.pop("min_stock_in_packages", None) # Spalte ist NOT NULL
|
||||
for field, value in data.items():
|
||||
setattr(product, field, value)
|
||||
db.commit()
|
||||
|
||||
@@ -100,7 +100,10 @@ class ProductBase(BaseModel):
|
||||
unit_id: int | None = None
|
||||
package_size: float | None = Field(default=None, gt=0)
|
||||
group_id: int | None = None
|
||||
min_stock: float | None = Field(default=None, ge=0)
|
||||
min_stock: float | None = Field(default=None, ge=0) # immer in Basiseinheiten
|
||||
# Nur für die Anzeige: in welcher Einheit der Mindestbestand erfasst wurde.
|
||||
min_stock_unit_id: int | None = None
|
||||
min_stock_in_packages: bool = False
|
||||
|
||||
|
||||
class ProductCreate(ProductBase):
|
||||
@@ -117,6 +120,8 @@ class ProductUpdate(BaseModel):
|
||||
package_size: float | None = Field(default=None, gt=0)
|
||||
group_id: int | None = None
|
||||
min_stock: float | None = Field(default=None, ge=0)
|
||||
min_stock_unit_id: int | None = None
|
||||
min_stock_in_packages: bool | None = None
|
||||
|
||||
|
||||
class ProductOut(BaseModel):
|
||||
@@ -131,6 +136,8 @@ class ProductOut(BaseModel):
|
||||
package_size: float | None
|
||||
group_id: int | None
|
||||
min_stock: float | None
|
||||
min_stock_unit_id: int | None = None
|
||||
min_stock_in_packages: bool = False
|
||||
source: str
|
||||
created_at: datetime
|
||||
# angereichert:
|
||||
@@ -139,6 +146,9 @@ class ProductOut(BaseModel):
|
||||
kind: str = ""
|
||||
unit_name: str = ""
|
||||
unit_factor: float = 1.0
|
||||
# Mindestbestand in der erfassten Einheit (für die Anzeige):
|
||||
min_stock_display: float | None = None
|
||||
min_stock_unit_label: str = ""
|
||||
|
||||
|
||||
class LookupResult(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user