The record of every lot, and the board of every task
Underneath: lots, locations, transactions and transfers with their status enforced in the database. On top: a Control Tower that shows what is flowing, what is stuck, and who (or what) is working on it.
A warehouse management system answers what do we have and where is it. A control tower answers what is happening right now and what needs a decision. Pharmacy Flow does both in one application: the WMS is the inventory record with its rules enforced in Postgres, and the Control Tower is the live execution board over the same data. Nothing is synchronised between them because there is nothing to synchronise.
What the Control Tower shows
One board: tasks by status, the fleet with idle, busy and offline, waves and their progress.
◎
Task board
Every receive, putaway, move, pick, replenish, count, sort and pack task grouped by pending, ready, assigned, in progress, done and exception.
⚇
Fleet
Resources of every kind (human, AMR, AGV, sorter, AS/RS, put-wall) with capabilities, zone, live status and, for machines, last-seen time.
≣
Waves
Plan a wave from open orders, release it, and watch task counts roll up to completion. A wave detail page lists its tasks.
⚡
Auto-dispatch
One action assigns every ready task to a capability-matched resource, round-robin across the fleet, humans and machines alike.
!
Exceptions
A task flagged with a note stays visible until resolved; the resource that flagged it is freed.
▥
Scanner
A progressive web app for handheld work, reached from the same login and permissions.
How tasks are planned, dispatched and pulled by machines is described in full on the fulfilment automation page, including what is shipped and what is on the roadmap.
Lots and their state machine
A lot is a row with rules
Every raw-material lot has an internal lot id, supplier lot number, quantity received and remaining, unit of measure, unit cost, expiry date and a computed expiry band, controlled and hazardous flags, storage condition, facility and location. Finished-good lots carry the variant they were made as, the BUD, the source batch and the QC hold reason.
Lot status is not free text. A trigger enforces the allowed moves (Quarantine to Available on putaway, Available to Consumed when the last quantity is issued, back to Quarantine on an expiry sweep or a QA decision, Rejected when scrapped with a reason, Expired) and refuses anything else. Status changes go through a permission-checked procedure.
The list is paged in SQL with filters on status, expiry and facility, and exports to a file, which matters once you hold thousands of lots. Expiry buckets (expired, within 30 days, within 90 days) feed the anomaly scan described on the inventory page.
Receiving, QA release, putaway
1
Receive against a purchase-order line
Supplier lot number, quantity, expiry date, whether the temperature check passed and the certificate-of-analysis reference. The system creates the internal lot in Quarantine, assigns an LPN, records a GS1 DataMatrix label print, posts a receipt transaction to the quarantine location and updates the PO to Partial or Received.
2
QA release
A permission-gated action (qa.release) marks the lot released and pending putaway. Technicians cannot do this.
3
Directed putaway
The putaway screen's rule engine suggests a destination bin from the material's hazard flag, control status and storage class; the operator confirms or overrides the bin. The lot becomes Available and a putaway transaction is written from the quarantine location to the bin.
The transaction ledger and counts
Every quantity change is a row in the transaction ledger: receipt, putaway, issue (a batch draw or a transfer out), pick (a unit commissioned for an order), adjustment, count and scrap, each with the lot, the material, the delta, the unit, the from and to locations, the reference (PO number, batch number, order number, transfer code) and the user. Cycle counts are entered as counts with the variance booked as an adjustment. A lot can be quarantined by hand with a reason, and an expiry sweep quarantines every Available lot that has passed its date, which the anomaly scan offers as a one-click remediation when it finds expired stock still marked Available.
Unit trace: every commissioned serial with its lot, batch, order and current status.
Inter-site transfers
A transfer moves a lot between two facilities in three permission-gated steps. The requesting site drafts it with a quantity and reason. The sending site approves and ships it, which issues the quantity from the origin lot and writes the ledger row. The receiving site counts what arrived and books it on hand, with any discrepancy recorded on the transfer. Each step is an event with the actor. Together with facility-scoped lots, this is what lets one tenant run more than one site without a spreadsheet in between; see local manufacturing for the multi-site pattern.
Bring a receiving day
We will receive a PO line, release it, put it away, draw it into a batch and watch it leave on a parcel, with every ledger row and event visible.
A parcel is created at the pack station with carrier, service, packout and the cold-chain flag, and gets its tracking number and label there. Carrier scans arrive at a secured webhook and are stored once each, keyed on the carrier's event id so a retry cannot duplicate a scan; manual status changes are stored the same way, so the journey is complete whoever supplied each step.
Temperature readings are recorded against the parcel. For a cold-chain parcel the window is 2–8 °C; an out-of-range reading marks an excursion, opens a QA hold and flips the parcel to Exception. A held parcel cannot be advanced to Delivered, by staff or by the carrier feed, until a user with the qa.release permission records a disposition. The reading stays on the record after release.
Labels are print jobs in a ledger with printer, station, order, serial or SSCC, status and reason; reprints and voids are recorded so labels reconcile to serials. Detail on packout and excursions is in the cold-chain guide.
Facilities, capacity and routing
A facility declares the product families it compounds, a daily capacity, its state and status. Order routing reads those declarations in a fixed order: only capable sites qualify; a brand pinned to a site wins if that site is capable; a capable site in the ship-to state wins next; otherwise the least-utilised capable site, where utilisation is trailing three-day throughput against capacity. The reason is written on the order and returned to API partners. An order can be rerouted by hand. Detail on intake is on the telehealth API page.
Routing rule priority, from lib/db/routing.ts.
Rule
Wins when
1. Capability
The site lists the order's product family and is not in planned status. Non-capable sites never receive the order.
2. Brand pin
The partner brand is pinned to a specific site and that site is capable.
3. Geography
A capable site is in the ship-to state.
4. Load
None of the above decided: the capable site with the lowest utilisation.
What it is not, yet
Replenishment tasks from pick-face minimums, continuous cycle-count generation, velocity-based slotting and pick-path sequencing are on the roadmap, not in the product. Dispatch matches tasks to resources by task type; it does not yet refuse to give a controlled pick to a resource without vault authorisation. Receiving today is a WMS screen, not a WES task chain. We keep this list on the page so an evaluation is based on what exists. The comparison page and the buyer's guide apply the same standard to other vendors, and the security page covers the tenancy model these tables sit under.
Frequently asked questions
Is this a WMS or a WES?+
Both, in one application. The WMS is the lot, location, ledger, receiving, QA, putaway, transfer and parcel record. The WES is the wave, task and resource layer with the Control Tower on top. They share tables, so there is no integration between them.
Can a lot's status be changed directly?+
No. A trigger enforces the allowed transitions and status changes go through a permission-checked procedure. Quarantine to Available happens on putaway; Available to Consumed when the last quantity is issued.
How is putaway directed?+
A rule engine on the QA and putaway screen suggests a bin from the material's hazard flag, control status and storage class. The operator confirms or overrides, and the putaway transaction records the destination.
What happens to a duplicate carrier scan?+
It is ignored. Scans are stored once per parcel and carrier event id, or once per parcel, timestamp and raw status when the carrier sends no id.
Can a cold-chain parcel with an excursion be delivered?+
Not while its QA hold is open. Both the manual advance and the carrier webhook check the hold before cascading Delivered. A qa.release user records a disposition to lift it.
Does the Control Tower show robots and people together?+
Yes. Resources of every kind are on the same fleet view with status, and tasks show the resource that holds them whatever its kind.
Can we run several facilities in one tenant?+
Yes. Lots are facility-scoped, transfers move them between sites with approval and receipt, and routing assigns orders by capability, pin, geography and load.
A working platform, not a slide deck. Book a walkthrough and we'll run a real order from intake to carrier lane — or explore the public directory first, no account needed.