Einlagern-UX: 3-Wege-Barcode, Inline-Anlage aus OFF, Mehr-Chargen mit MHD
Einlagern: - Barcode-Erkennung dreiteilig: bekannt -> direkt Menge; unbekannt aber in OFF -> "Anlegen & einlagern" inline; gar nicht gefunden -> manuell anlegen. - Mehrere Chargen pro Vorgang: je Zeile eigene Menge + eigenes MHD (z.B. 5 Glaeser mit unterschiedlichen Daten). Neuer Endpoint /stock/checkin/batch. Produkte/OFF: - OFF-Fuellmenge (quantity) wird in Basiseinheit + Packungsgroesse geparst (kg->g, l->ml, cl/dl); parse_quantity + Tests. - Produktformular uebernimmt Barcode aus ?barcode= und schlaegt automatisch nach, fuellt Packungsgroesse; gemeinsame offUtils (guessGroup/suggestionToProduct). Lagerorte: - Baum jetzt rekursiv ueber beliebig viele Ebenen (Schrank->Fach->Kiste->...). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
|
||||
import httpx
|
||||
|
||||
@@ -10,17 +11,35 @@ from .config import get_settings
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# Faktoren, um eine Einheit in die Basiseinheit (Gramm bzw. Milliliter) umzurechnen.
|
||||
_UNIT_TO_BASE: dict[str, tuple[str, float]] = {
|
||||
"kg": ("gram", 1000.0),
|
||||
"g": ("gram", 1.0),
|
||||
"mg": ("gram", 0.001),
|
||||
"l": ("milliliter", 1000.0),
|
||||
"dl": ("milliliter", 100.0),
|
||||
"cl": ("milliliter", 10.0),
|
||||
"ml": ("milliliter", 1.0),
|
||||
}
|
||||
|
||||
def _guess_base_unit(quantity: str | None) -> str:
|
||||
"""Rät die Basiseinheit aus dem OFF-Feld 'quantity' (z.B. '500 g', '1 l')."""
|
||||
_QUANTITY_RE = re.compile(r"([\d]+(?:[.,]\d+)?)\s*(kg|mg|g|dl|cl|ml|l)\b", re.IGNORECASE)
|
||||
|
||||
|
||||
def parse_quantity(quantity: str | None) -> tuple[str, float | None]:
|
||||
"""Ermittelt Basiseinheit und Packungsgröße aus dem OFF-Feld 'quantity'.
|
||||
|
||||
Beispiele: '500 g' -> ('gram', 500), '1 kg' -> ('gram', 1000),
|
||||
'1,5 l' -> ('milliliter', 1500), '6 Stück' -> ('piece', None).
|
||||
"""
|
||||
if not quantity:
|
||||
return "piece"
|
||||
q = quantity.lower()
|
||||
if "ml" in q or "cl" in q or "l" in q or "liter" in q:
|
||||
return "milliliter"
|
||||
if "kg" in q or " g" in q or "gramm" in q or q.strip().endswith("g"):
|
||||
return "gram"
|
||||
return "piece"
|
||||
return "piece", None
|
||||
match = _QUANTITY_RE.search(quantity)
|
||||
if not match:
|
||||
return "piece", None
|
||||
amount = float(match.group(1).replace(",", "."))
|
||||
base_unit, factor = _UNIT_TO_BASE[match.group(2).lower()]
|
||||
package_size = round(amount * factor, 3)
|
||||
return base_unit, (package_size if package_size > 0 else None)
|
||||
|
||||
|
||||
def lookup_barcode(barcode: str) -> dict | None:
|
||||
@@ -65,12 +84,15 @@ def lookup_barcode(barcode: str) -> dict | None:
|
||||
categories = product.get("categories") or ""
|
||||
category_tags = product.get("categories_tags") or []
|
||||
|
||||
base_unit, package_size = parse_quantity(product.get("quantity"))
|
||||
|
||||
return {
|
||||
"barcode": barcode,
|
||||
"name": name,
|
||||
"brand": (product.get("brands") or "").strip() or None,
|
||||
"image_url": product.get("image_front_url") or product.get("image_url") or None,
|
||||
"base_unit": _guess_base_unit(product.get("quantity")),
|
||||
"base_unit": base_unit,
|
||||
"package_size": package_size,
|
||||
"quantity_text": product.get("quantity"),
|
||||
"category_suggestion": categories.split(",")[0].strip() if categories else None,
|
||||
"category_tags": category_tags,
|
||||
|
||||
Reference in New Issue
Block a user