Verwaltbare Einheiten mit Umrechnung + Chargen bearbeiten + gezieltes Auslagern

Einheiten (neu):
- Tabelle units (name, kind=count|weight|volume, factor, is_builtin); eingebaut
  Stueck/Gramm/Kilogramm/Milliliter/Liter, Admin kann eigene anlegen (z.B. Pfund=500g).
- Bestaende bleiben intern in kanonischer Basis (Stueck/Gramm/Milliliter);
  Product.display_unit_id und Group.min_stock_unit_id als nullable FKs.
- Neuer Umrechnungs-Service (services/conversion.py) ersetzt die feste Einheitenlogik;
  Ein-/Auslagern und Gruppen-Mindestbestand rechnen ueber den Faktor.
- Gruppen-Mindestbestand mit Einheit; Gruppenbestand summiert nur Produkte
  passender Art. Neue Verwaltungsseite "Einheiten" (Admin).
- Schonende Migration beim Start: ADD COLUMN IF NOT EXISTS (Postgres), damit
  bestehende Installationen ihre Daten behalten.

Chargen:
- PATCH /lots/{id} und DELETE /lots/{id}: Menge/MHD korrigieren, Charge loeschen
  (wird als Korrektur-Bewegung protokolliert). Bearbeitung im Produktdetail.

Auslagern:
- Optionales lot_id: gezielt aus einer bestimmten Charge/MHD abbuchen statt FEFO;
  Auswahl-Dropdown in der Auslagern-Seite.

Sonstiges: Roadmap aktualisiert, Tests fuer die Umrechnung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-22 12:33:14 +02:00
parent d4e7ff8e3d
commit 0310cdfbd6
26 changed files with 1015 additions and 177 deletions

View File

@@ -0,0 +1,43 @@
import pytest
from app.models import BaseUnit, Product
from app.services.conversion import ConversionError, to_base
def test_base_unit(db, rice):
assert to_base(db, rice, 200, "Gramm") == 200
assert to_base(db, rice, 200, "g") == 200 # Kürzel-Alias
def test_kilogram_converts_to_grams(db, rice):
assert to_base(db, rice, 1, "Kilogramm") == 1000
assert to_base(db, rice, 2.5, "kg") == 2500
def test_package_uses_package_size(db, rice):
# rice: package_size = 500 g
assert to_base(db, rice, 3, "package") == 1500
def test_wrong_kind_rejected(db, rice):
# rice ist ein Gewichts-Produkt -> Volumeneinheit passt nicht
with pytest.raises(ConversionError):
to_base(db, rice, 1, "Liter")
def test_unknown_unit_rejected(db, rice):
with pytest.raises(ConversionError):
to_base(db, rice, 1, "Eimer")
def test_volume_product(db):
milk = Product(name="Milch", base_unit=BaseUnit.milliliter, package_size=1000)
db.add(milk)
db.commit()
assert to_base(db, milk, 1, "Liter") == 1000
assert to_base(db, milk, 500, "ml") == 500
def test_non_positive_quantity_rejected(db, rice):
with pytest.raises(ConversionError):
to_base(db, rice, 0, "Gramm")