DEV Community

Isaac Mwendwa Muthui
Isaac Mwendwa Muthui

Posted on Originally published at factoraerp.com

Model Dispatch So Held Lots Cannot Ship: WarehouseHold, PickList, FEFO and DispatchOrder

By Isaac Muthui, CEO, Tamalaki Business Network Ltd. Originally published at factoraerp.com.

In food manufacturing ERPs, dispatch is often modelled as "create delivery note → reduce stock." That model fails the moment a held pallet looks identical to a released one, or FEFO loses to "whatever was nearest the door." The operational risk is not forgetting to print a DN. It is letting a status machine lie: WarehouseHold stock riding out on a DispatchOrder that should only allocate Released lots.

This piece is for builders modelling warehouse status, pick allocation and outbound gates. The factory-floor checklist behind it lives on the FactoraERP blog (canonical link above).

The domain objects you need

At minimum:

  • FinishedGoodsLot — batch/lot identity, expiry / best-before, quantity, location.
  • WarehouseStatus — at least hold | quarantine | released | rejected (names vary; the invariant matters).
  • SalesOrder / DeliveryNote — customer, lines (SKU, qty), optional lot constraints.
  • PickList — allocated lot lines for a delivery (SKU, lot_id, qty).
  • FEFOPolicy — allocation rule: earliest expiry among released lots that meet remaining-life rules.
  • DispatchOrder — vehicle, load lines (must equal pick), destination drops, documents.
  • DispatchSignOff — named user, timestamp, vehicle registration, sample counts / pallet IDs.

Sketch:

FinishedGoodsLot.status ∈ {hold, quarantine, released, rejected}
SalesOrder 1---* DeliveryNoteLine
PickList allocates only lots where status == released
FEFOPolicy orders candidate lots by expiry ASC (+ remaining-life filter)
DispatchOrder 1---* LoadLine (lot_id, qty) ⊆ PickList
DispatchOrder 1---0..1 DispatchSignOff (required before ship_state = departed)
Enter fullscreen mode Exit fullscreen mode

Rule: held lots cannot ship

Encode the gate checklist as preconditions, not as a PDF:

  1. Every LoadLine.lot.status == released at allocation and again at sign-off (status can flip after pick).
  2. sum(LoadLine.qty) per SKU matches DeliveryNote (short picks require an amended DN before depart).
  3. Allocation ran through FEFO (or recorded exception with reason + approver).
  4. Pack condition / cold-chain checks are checklist events on DispatchOrder when product type requires them.
  5. Document snapshot (DN + customer PO refs) matches load lines at sign-off time.
  6. Customer / drop identity on load zones matches DN destinations (no silent multi-customer mash).
  7. DispatchSignOff exists before dispatch_state may leave staged → departed.

If any gate fails, the API should refuse mark_departed, not show a warning toast warehouse staff click through while the gate opens.

Status machine vs physical bay

The hard bug is drift: UI says released, pallet still has a faded hold tag; or system says hold, picker already staged it. Two mitigations:

  • Re-validate status at sign-off, not only at allocate.
  • Location constraints: hold/quarantine locations are not valid sources for PickList creation.
allocate(lot)  → require lot.status == released && lot.location.pickable
sign_off(load) → for each line: reload lot; require still released
depart(load)   → require sign_off_id != null
Enter fullscreen mode Exit fullscreen mode

FEFO as allocation, not a report

A "FEFO report" operators ignore is not FEFO. Put earliest-expiry selection inside create_pick_list:

  • Candidate set = released lots for SKU with qty available and remaining life ≥ customer/contract minimum (if any).
  • Sort by expiry ascending, then allocate.
  • Persist allocation_method = fefo | exception on the pick; exceptions need approved_by.

That is what later powers honest recall queries: which outbound shipments contain lot X? If dispatch allowed a silent substitution, your graph is fiction.

Vehicle load and cold chain as optional extensions

Do not overbuild day one. For chilled/frozen SKUs, attach a ColdChainCheck (pre-cool yes/no, temperature reading, checked_by) required before sign-off. Ambient SKUs skip the entity. Same pattern as allergen checks on changeovers: required when the product type says so.

What not to overbuild

You do not need a full WMS on day one. You do need:

  • A real status enum shipping cannot ignore
  • Pick allocation that refuses non-released lots
  • FEFO (or explicit exception) at allocate time
  • Re-check status + named sign-off before departed

Paper checklists work for tiny plants. Software earns its keep when lot count, holds mid-shift and multi-drop trucks outgrow the notebook—and when "the customer is waiting" keeps defeating your SOPs.


Isaac Muthui, CEO, Tamalaki Business Network Ltd

Top comments (0)