diff --git a/backend/app/services/stock.py b/backend/app/services/stock.py index eabd878..3df4ab1 100644 --- a/backend/app/services/stock.py +++ b/backend/app/services/stock.py @@ -634,13 +634,16 @@ def verbrauchen( def _fefo_lots(db: Session, product_id: int) -> list[Lot]: - """Lots eines Produkts, sortiert nach dem WIRKSAMEN Datum (NULL zuletzt). + """Lots eines Produkts: Angebrochenes zuerst, dann nach dem wirksamen Datum. - Wirksam ist das frühere aus MHD und Verbrauchsdatum (``Lot.faellig_am``). - Eine angebrochene Packung kommt damit meist von selbst zuerst, weil ihr - Verbrauchsdatum näher liegt – aber eine ungeöffnete Charge, die morgen - abläuft, geht vor. Erst bei gleichem Datum wird das Angebrochene vorgezogen, - damit nicht eine zweite Packung aufgeht, während eine offen ist. + Eine offene Packung wird immer vor einer geschlossenen geleert, auch wenn + die geschlossene früher datiert ist. Das aufgedruckte MHD einer + ungeöffneten Packung ist konservativ und wird real meist überlebt – eine + offene verdirbt dagegen tatsächlich. Und wer eine zweite Packung aufmacht, + während die erste offen steht, hat am Ende zwei angebrochene. + + Innerhalb beider Gruppen gilt weiter FEFO über das nähere von MHD und + Verbrauchsdatum (``Lot.faellig_am``), NULL zuletzt. """ lots = ( db.query(Lot) @@ -652,9 +655,9 @@ def _fefo_lots(db: Session, product_id: int) -> list[Lot]: return sorted( lots, key=lambda lot: ( + lot.opened_on is None, lot.faellig_am is None, lot.faellig_am or date.max, - lot.opened_on is None, lot.id, ), ) diff --git a/backend/tests/test_ablauf_zwei_daten.py b/backend/tests/test_ablauf_zwei_daten.py index 446c269..d68f055 100644 --- a/backend/tests/test_ablauf_zwei_daten.py +++ b/backend/tests/test_ablauf_zwei_daten.py @@ -142,7 +142,7 @@ def test_zustand_ohne_daten_bleibt_no_date(db): # -------------------------------------------------------------------------- # FEFO # -------------------------------------------------------------------------- -def test_fefo_nimmt_das_fruehere_wirksame_datum(db, milch): +def test_angebrochenes_wird_zuerst_geleert(db, milch): spaet = _lot(db, milch, 1000, best_before=date.today() + timedelta(days=30)) offen = _lot(db, milch, 500, best_before=date.today() + timedelta(days=30), opened_on=date.today(), consume_by=date.today() + timedelta(days=2)) @@ -152,22 +152,28 @@ def test_fefo_nimmt_das_fruehere_wirksame_datum(db, milch): assert reihenfolge == [offen.id, spaet.id] -def test_knappes_mhd_geht_vor_der_offenen_packung(db, milch): - """Verderb schlägt Bequemlichkeit: Was morgen abläuft, kommt zuerst.""" +def test_angebrochenes_geht_auch_vor_knappem_mhd(db, milch): + """Offen zuerst, selbst wenn eine geschlossene Packung früher datiert ist. + + Das MHD einer ungeöffneten Packung ist konservativ und wird real meist + überlebt; eine offene verdirbt dagegen tatsächlich. Und wer die zweite + aufmacht, während die erste offen steht, hat am Ende zwei angebrochene. + """ morgen = _lot(db, milch, 1000, best_before=date.today() + timedelta(days=1)) offen = _lot(db, milch, 500, opened_on=date.today(), consume_by=date.today() + timedelta(days=5)) reihenfolge = [lot.id for lot in _fefo_lots(db, milch.id)] - assert reihenfolge == [morgen.id, offen.id] + assert reihenfolge == [offen.id, morgen.id] -def test_bei_gleichem_datum_gewinnt_das_angebrochene(db, milch): - gleich = date.today() + timedelta(days=3) - zu = _lot(db, milch, 1000, best_before=gleich) - offen = _lot(db, milch, 500, opened_on=date.today(), consume_by=gleich) +def test_unter_den_angebrochenen_gilt_weiter_fefo(db, milch): + spaet = _lot(db, milch, 500, opened_on=date.today(), + consume_by=date.today() + timedelta(days=5)) + frueh = _lot(db, milch, 500, opened_on=date.today(), + consume_by=date.today() + timedelta(days=1)) reihenfolge = [lot.id for lot in _fefo_lots(db, milch.id)] - assert reihenfolge == [offen.id, zu.id] + assert reihenfolge == [frueh.id, spaet.id]