Files
Vorrania/backend/app/crud.py
Scarriffle de6c8632a1 Mehrere EAN-Codes je Produkt und je Gruppe (mit Notiz) + Gruppen umbenennen
Barcodes:
- Neue Tabelle barcodes (code, note, product_id ODER group_id). Damit lassen sich
  einem Produkt mehrere Codes geben und einer Gruppe beliebig viele Marken
  zuordnen (z.B. alle Mehlmarken zu "Mehl"). Je Code eine Notiz wie
  "Mehl bei Aldi".
- Lookup loest jetzt auch Alias-Codes auf; ist ein Code einer Gruppe zugeordnet,
  liefert die API group_id/group_name zurueck. Beim Anlegen aus einem Scan wird
  diese Gruppe automatisch gesetzt (Vorrang vor dem Kategorie-Rateversuch).
- Endpunkte: POST/DELETE /products/{id}/barcodes und /groups/{id}/barcodes.
- Web: wiederverwendbare BarcodeList-Komponente, eingebunden im Produktformular
  und auf der Gruppen-Seite.

Gruppen:
- Namen lassen sich jetzt direkt in der Tabelle aendern (PATCH war vorhanden,
  nur die Oberflaeche fehlte).

UI-Fix: Auswahlfelder in Tabellen richten sich nach ihrem Text statt nach der
Zellenbreite (Gruppen-/Benutzer-Dropdowns waren abgeschnitten).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-22 14:48:14 +02:00

68 lines
2.3 KiB
Python

"""Kleine gemeinsame Helfer für Router."""
from __future__ import annotations
from datetime import date
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from .models import Barcode, Lot, Product
from .schemas import BarcodeOut, ProductOut
from .services.conversion import KIND_OF_BASE, display_unit_info
from .services.stock import current_stock
def product_to_out(db: Session, product: Product) -> ProductOut:
out = ProductOut.model_validate(product)
out.stock = current_stock(db, product.id)
out.expired_count = (
db.query(Lot)
.filter(
Lot.product_id == product.id,
Lot.best_before.isnot(None),
Lot.best_before < date.today(),
Lot.quantity > 0,
)
.count()
)
out.barcodes = [
BarcodeOut.model_validate(b)
for b in db.query(Barcode).filter(Barcode.product_id == product.id).order_by(Barcode.id).all()
]
out.kind = KIND_OF_BASE[product.base_unit].value
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
def resolve_product(
db: Session, product_id: int | None, barcode: str | None
) -> Product:
"""Findet ein Produkt per ID oder Barcode; wirft 404, wenn keins passt."""
product: Product | None = None
if product_id is not None:
product = db.get(Product, product_id)
elif barcode:
product = db.query(Product).filter(Product.barcode == barcode).first()
else:
raise HTTPException(
status.HTTP_400_BAD_REQUEST, "product_id oder barcode erforderlich"
)
if product is None:
raise HTTPException(status.HTTP_404_NOT_FOUND, "Produkt nicht gefunden")
return product