"""Einheiten-Umrechnung. Der Bestand einer Charge (Lot) wird immer in der Basiseinheit des Produkts gespeichert (Stück, Gramm oder Milliliter). Nutzer geben Mengen entweder in der Basiseinheit oder in "Packungen" (package) an. Eine Packung entspricht ``product.package_size`` Basiseinheiten. """ from __future__ import annotations from ..models import BaseUnit, Product # Aliase, die als "Basiseinheit" akzeptiert werden. _BASE_ALIASES: dict[str, BaseUnit] = { "piece": BaseUnit.piece, "pieces": BaseUnit.piece, "stück": BaseUnit.piece, "stueck": BaseUnit.piece, "st": BaseUnit.piece, "g": BaseUnit.gram, "gram": BaseUnit.gram, "gramm": BaseUnit.gram, "ml": BaseUnit.milliliter, "milliliter": BaseUnit.milliliter, } PACKAGE_UNITS = {"package", "packung", "pkg", "pack"} class UnitError(ValueError): """Wird geworfen, wenn eine Einheit nicht zum Produkt passt.""" def to_base_quantity(product: Product, quantity: float, unit: str) -> float: """Rechnet eine vom Nutzer angegebene Menge in Basiseinheiten um.""" if quantity <= 0: raise UnitError("Menge muss größer als 0 sein") unit_norm = unit.strip().lower() if unit_norm in PACKAGE_UNITS: if not product.package_size or product.package_size <= 0: raise UnitError( "Für dieses Produkt ist keine Packungsgröße hinterlegt, " "Packungen können nicht umgerechnet werden" ) return quantity * product.package_size mapped = _BASE_ALIASES.get(unit_norm) if mapped is None: raise UnitError(f"Unbekannte Einheit: {unit}") if mapped != product.base_unit: raise UnitError( f"Einheit '{unit}' passt nicht zur Basiseinheit " f"'{product.base_unit.value}' des Produkts" ) return float(quantity) def base_to_packages(product: Product, quantity_base: float) -> float | None: """Rechnet Basiseinheiten in (ggf. gebrochene) Packungen um, falls möglich.""" if not product.package_size or product.package_size <= 0: return None return quantity_base / product.package_size