Planning · Outbound fulfilment

Turning a flat order backlog into a plan the floor can pick

Planning software usually gets blamed for the wrong thing. The plan itself is rarely the problem. The problem is that running it means twelve minutes of driving someone else's system by hand, the result comes back as a file, and nothing joins that file to the order list the planners actually work from. So the plan gets run less often than it should, and the list is stale in between.

15 reported pipeline stages
39 order status codes translated
9 planning views generated
Confidential client & operation

Built for the outbound planning and picking operation of a contract-logistics site, working against a customer-owned planning system and a customer-owned warehouse platform — neither of which it controlled.

From a flat backlog feed to a wave plan, a verdict per order and a pick queueThe daily backlog feed is a flat list of order, due date and quantity that never deletes a row. One button runs the whole chain: a planning run in the customer's planning system, a wait for its background job matched by task name and submission time rather than task number and bounded at ten minutes, a scheduling run and a second wait, then narrowing to committed jobs, export, reconciling against the latest feed, enrichment from the warehouse system and a go or no-go flag with a reason. Fifteen stages report into a run record in the database, so a reload reattaches to the run in flight; one run at a time is enforced by a row in the run table and a silent crash counts as stale after an hour. The enriched order list carries the verdict and reason per order, and a pick queue with five states runs each order step by step with a stop honoured between steps. The machine owns the system columns and never touches the columns a person typed, and an empty feed table makes the reconcile refuse to run.DAILY BACKLOG FEED · FLATORDERDUEQTYA-311d+124A-314d+18A-320d+240A-287d-312not refreshedorder · due · quantity, nothing elseThe feed never deletes a row: anorder that leaves the backlog juststops being refreshed. Survivorsare decided from the single latestload timestamp, never from thetable's contents.Run recordevery stage reports its progress herereload the page: the timeline reattachesone run at a time, held by a rowstale after an hour if it goes silentWHAT WE BUILTOne button, 15 reported stagesthe screen polls the run record every 5 secondsPlanning rundriven in the customer's planning systemWait for its background jobmatched by task name and submission time, neverby task number; bounded at 10 minutesScheduling runWait againsame match, same 10-minute boundNarrow to committed jobsthe ones ready to runExport the job listReconcile against the latest feedadds, refreshes and drops orders by the latest loadEnrich each orderstatus, processed and pending quantities andon-hand stock, from the warehouse systemGo / no-go flag, with a reasongo if built or released; else a no-go with a reasoncountdowncountdownOne run at a time: a second request is refused outrightThe same reconcile runs unattended every 2 hours andskips its turn while a run is activeENRICHED ORDER LIST · SAMPLE VALUESORDJOBSTATEGO?REASONA-311J-204releasedGOA-314J-205builtGOA-320J-209plannedNO-GOnot releasedA-322—openNO-GOnot builtPick queuean order and a drop pallet go in from a form;the order is marked in progress in the sharedqueue table before a worker touches itrunning item count · latest log line5 states · stop honoured between stepssplit picks escalate: item, pallet, serial countThe machine owns the system columnsand never touches the columns a persontyped: status, comment, owner andresolution date survive every reconcile.An empty feed table makes the reconcilerefuse to run rather than deleteeverything.
A flat daily backlog feed goes through one fifteen-stage run, reported into a run record, and comes out as an order list with a go or no-go verdict and a reason on every line, then a queued pick. Order and job values are illustrative.

How do you turn a daily order backlog into a wave plan the floor can pick?

Run the whole chain as one recorded job. One button drives the customer's planning and scheduling runs, waits for each, folds the committed jobs into the order list and reports fifteen stages into a run record. Every order gets a go or no-go build-readiness verdict with a reason, and picks run from a queue in SQL Server.

The problem

The order backlog arrived as a daily feed from the customer's systems. It said what had been ordered and when it was due, and very little else — not whether the parts were on hand, not whether the job had been released to the floor, not whether anyone had already looked at it. Answering those questions meant running the planning system by hand, exporting the result, and reconciling that export against a workbook somebody maintained on the side.

The feed had a trap in it. It never deletes a row. When an order comes off the customer's live backlog, its row simply stops being refreshed — so anything reading the table as a whole would accumulate dead orders forever and never notice one had gone.

  • The planning and scheduling runs execute as background jobs the browser cannot see finish
  • Job numbers only exist after a planning run, so nothing could be matched before one
  • The go / no-go call, the planners' status column and their comments were all kept by hand in a workbook alongside the exported data
  • Picking an order meant walking a handheld terminal through the same screens, unit by unit

What we built

One button that runs the whole wave planning chain, and a run record that says exactly where it has got to. The pipeline drives the customer's planning system through the planning run with the screen automation underneath, waits for it, drives the scheduling run, waits for that, narrows the job list to the jobs that are committed and ready to run, exports it, and then folds the export back into the order list — reconciling against the latest feed, matching jobs onto orders, pulling order status, processed and pending quantities and on-hand stock out of the warehouse system, and finally computing a go / no-go build-readiness flag with a reason attached.

Fifteen stages report their own progress into the run record. The screen polls it every five seconds and draws a timeline, with a countdown on the two stages known to be long waits. Reload the page halfway through and the timeline reattaches to the run still in flight, because the state lives in the database rather than the browser.

The second half is the pick queue. An order number and a drop pallet go in from a form; the processor marks each order as in progress in the shared queue table before a worker touches it. The picker screen refreshes every five seconds and shows five states, a duration, a running item count, the latest log line, and the full step-by-step log for any order on request.

  • One planning run at a time, enforced by a row in the run table rather than an in-process flag, so a second request is refused outright instead of queuing behind the first
  • The same reconcile runs unattended every two hours and skips its turn while a planning run is active
  • A run that crashed without reporting failure counts as stale after an hour, so it cannot block the unattended reconcile forever
  • Enrichment can be re-run from the last export on its own, so a failure in the last mile does not cost another full browser run
  • The queue lives in SQL rather than in the web application's memory, so the pickers can be run as a standalone operating-system process that keeps working while the application is stopped — with an environment flag that shuts off the in-process worker so only one of them is consuming the queue
  • Every step of a pick is written to a log against the order, so a failed run is read back line by line instead of reproduced

Knowing when someone else's job has finished

The hard part of this pipeline was never the clicking. It was that the planning and scheduling runs are background jobs on the customer's server: the browser fires them and returns immediately, and there is no reliable on-screen signal that they are done. Watching the page would have meant guessing.

So it doesn't. The automation records the exact instant it confirmed each run, then polls the planning system's own background-task history for a row whose task name matches and whose submission time falls inside a two-minute window either side of that instant. The obvious key — the task number — is a global sequence shared with every other user of that system, so it identifies nothing. The submission timestamp is what isolates our run from somebody else's. Each poll opens an autocommitting connection, because a pooled connection sitting on a transaction snapshot will cheerfully report a job as still running long after it has finished.

The wait is bounded at ten minutes, and every poll line goes to both the server log and the run's status feed. The operator watching the timeline sees what an engineer would see: the server's own clock, the row that matched, and whether its completion time is still empty.

Refusing to delete the humans' work

Everything that writes to the order list follows one rule: the machine owns the system columns and never touches the columns a person typed into. A reconcile refreshes dates, quantities, statuses and part numbers on every surviving order, adds the ones that appeared and deletes the ones that fell off the feed. Status, planner comment, owner and resolution date are left exactly as they were, and only disappear when the order they belong to does.

What counts as 'fell off' is decided from the single most recent load timestamp in the feed table rather than from the table's contents — which is the only thing that makes deletion safe against a source that never deletes anything itself. And if that table comes back empty, the reconcile refuses to run at all rather than concluding that every order has vanished and taking every planner's notes with it. The enrichment steps that follow are update-only: they never insert and never delete, so re-running one is harmless, and a failed enrichment cannot invalidate a reconcile that has already committed.

The build-readiness rule was derived, not specified. There was no document — there was a report somebody maintained by hand. We reproduced its go / no-go column from the underlying data until the two agreed on every order but one, and that one turned out to be a manual override typed into the sheet. What came out is narrower than anyone would have written from scratch: an order is a go when it is already built or has been released to the floor, and everything else is a no-go with a categorical reason. Holds and on-hand coverage were deliberately left out — the reference report keeps an already-built order on the go side whatever happens to it afterwards, and an order that is neither built nor released lands on no-go regardless. Encoding what the report actually did mattered more than encoding what it looked like it ought to do.

The result

The backlog is a live view rather than a snapshot somebody remembered to refresh. It reconciles itself every two hours whether or not anyone presses anything, planning runs on demand behind a visible timeline instead of twelve minutes of silence, and every order carries a build-readiness verdict with a reason rather than an opinion. Nine pivot and drill-down views are generated from the same data and export to a workbook that matches what is on screen; they are 3PL service and floor reports drawn from live data instead of a weekly export. The reconcile and the enrichment are batch jobs that restart cleanly: re-running one is harmless.

On the picking side, the work is a queue with a record instead of a sequence in somebody's head, which is what 3PL software that works the warehouse floor should look like. A stop request is honoured between steps and reports how much was picked before it landed. An order that genuinely needs a person — a split pick where serial numbers have to be scanned by hand — stops and escalates with the item, the pallet and the number of serials required attached, instead of failing with a stack trace and leaving someone to work out what happened. The same queue-and-log pattern fits most pick, pack and ship workflows.

Planning and picking, before and after

BeforeAfter
Running the planTwelve minutes of driving the planning system by handOne button, fifteen stages on a visible timeline
The order listStale between manual runsReconciled every two hours, unattended
Go / no-goKept by hand in a workbookComputed per order, with a categorical reason
PickingA handheld terminal walked unit by unitA queue in SQL Server with a step-by-step log

Questions about this build

How do you know a background planning job has finished?

Not from the screen, which gives no reliable signal. The automation records the instant it confirmed each run, then polls the planning system's own background-task history for a row with the matching task name and a submission time within two minutes either side. Each poll opens an autocommitting connection, because a pooled one on an old snapshot reports a finished job as still running.

How does the reconcile avoid deleting planners' comments?

The machine owns the system columns and never touches the ones a person typed into. Status, planner comment, owner and resolution date survive every reconcile and disappear only with their order. What counts as fallen off is decided from the most recent load timestamp, and if the feed table comes back empty the reconcile refuses to run at all.

How was the go or no-go rule decided?

It was derived from a report somebody maintained by hand. Its go / no-go column was reproduced from the underlying data until the two agreed on every order but one, which turned out to be a manual override. The rule that came out: an order is a go when it is already built or released to the floor, and everything else is a no-go with a categorical reason.

Built with

  • Python
  • Flask
  • SQL Server
  • Selenium WebDriver
  • Background worker threads
  • openpyxl

On the details we left out

Client, location, system names and volumes are withheld deliberately. We're happy to go deeper on architecture and approach on a call.

Services this draws on

Workload Optimization

Squeeze more throughput out of the hardware and licenses you already pay for.

See Workload Optimization →

System Integration

Connect aging systems to modern APIs, data platforms, and SaaS tools.

See System Integration →

3PL & Logistics Systems

WMS, TMS, and warehouse-floor workflows for 3PLs, brokers, and distributors.

See 3PL & Logistics Systems →

More projects

Integration · Inbound logistics

Knowing where inbound freight actually is

Inbound shipments were tracked by opening carrier websites one tab at a time. We replaced the tabs with live carrier integrations and a workflow that routes exceptions to the person who can fix them.

Read the story →
Automation · Warehouse systems

Automating a system that has no API

The warehouse platform offered no write interface, so every transaction went in by hand. We automated the screens themselves — carefully enough to trust with live inventory.

Read the story →
Automation · Inventory transfers

The order builds itself from the attachment

Inventory transfer requests arrived as spreadsheets attached to email, in as many shapes as there were requesters, and were keyed in line by line. Now the attachment becomes a validated, reference-numbered order on its own.

Read the story →
Get in touch

Got a backlog the floor can't pick from?

Tell us where the backlog comes from and how the plan is run today. Free assessment.

Prefer to talk?

Call 832-598-8234 or email msco@stoneagesoftware.com. Houston, Texas — serving Houston, The Woodlands, Conroe, Sugar Land, Katy, Pearland, and the Greater Houston metro.

What happens next

We read your message, an engineer replies with questions or a straight answer, and if a call would help, we book one. From the first reply on, you're talking to the people who'd do the work.

No obligation — we reply within one business day. This form sends your details to Stone Age Software LLC so we can respond to you. See our privacy policy for how we handle them. This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.