Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
from typing import List, Sequence
from .dates_days_between import days_between ← from dates.days-between ^1.0.0 · built alongside by fune
from .inventory_expiry_fefo_types import FefoPick, PickLine, StockBatch
def _whole_units(name: str, value: int) -> None:
if isinstance(value, bool) or not isinstance(value, int) or value < 0:
raise ValueError("%s must be a whole number, not negative, received %r" % (name, value))
def fefo_pick(batches: Sequence[StockBatch], quantity: int, as_at: str, min_shelf_life_days: int) -> FefoPick:
"""Pick ``quantity`` units earliest expiry first, from batches with at least
``min_shelf_life_days`` of life left on ``as_at``. Short stock is a
shortfall, not an error.
"""
_whole_units("quantity", quantity)
_whole_units("minShelfLifeDays", min_shelf_life_days)
dated = []
for batch in batches:
_whole_units('quantity of batch "%s"' % batch.batch, batch.quantity)
dated.append((days_between(as_at, batch.expiry), batch))
# sorted() is stable, so equal expiry dates keep their order.
dated = sorted(dated, key=lambda pair: pair[0])
lines: List[PickLine] = []
skipped: List[str] = []
remaining = quantity
for life, batch in dated:
if batch.quantity == 0:
continue
if life < min_shelf_life_days:
skipped.append(batch.batch)
continue
if remaining == 0:
continue
take = min(remaining, batch.quantity)
lines.append(PickLine(batch=batch.batch, expiry=batch.expiry, quantity=take))
remaining -= take
return FefoPick(lines=lines, picked=quantity - remaining, shortfall=remaining, skipped=skipped)