Skip to main content

Warehouse routing and the warehouse lock

Every product line on a sales order ships from a warehouse, and a single order can draw its lines from more than one. This page explains how a line is routed to a warehouse, how an order fans out into a separate shipment per warehouse, and the safety rule — the warehouse lock — that freezes a line's warehouse once real inventory has started to move.

New to a term?

Words like allocation, backorder, fulfillment order, shipment, and dropship are defined the first time they appear below and collected in the glossary.

Every line carries a warehouse

On a sales order, the warehouse lives at the line level, not the order level. Each product line records which warehouse it ships from, plus a routing method that says how that warehouse was chosen. Two lines on the same order can point at two different warehouses, and that's exactly what lets one order ship in parts from different locations.

The routing method a line carries determines whether it draws on local stock at all:

Routing methodWhat it meansWarehouse required?
WarehouseThe line ships from a specific local warehouse. Its warehouse is set, and stock is allocated and fulfilled there.Yes — a warehouse must be chosen.
DropshipThe line ships from a supplier straight to the customer. It bypasses warehouse allocation and local fulfillment entirely.No — the supplier holds the stock. See Create a dropship request.
UnassignedThe line hasn't been routed to a warehouse yet. It can't be allocated or shipped until a warehouse is assigned.Not yet.

For a line routed to a Warehouse, a warehouse is mandatory — the order can't be validated or approved with that line left without one. When SKU.io needs to pick a warehouse for you (during approval or in the Allocate dialog), it uses a routing suggestion based on where stock is available; you can override it. See Approve, reserve, and allocate stock.

An order can split across warehouses

Because the warehouse sits on the line, a single order naturally splits when its lines live in different places. When you approve and start fulfilling, SKU.io builds one fulfillment order per warehouse — the per-warehouse plan for what ships from where — and each fulfillment order ships as its own shipment. One sales order therefore becomes many fulfillment orders and many shipments, one set per warehouse involved.

You can reshape this split by hand: move a line to another warehouse, or slice one line's quantity across several warehouses so part ships from each. A line split across warehouses isn't duplicated on the sales channel — the customer still sees one ordered line, while SKU.io tracks the pieces internally. These actions live on the Fulfillment tab; see Split, move, and merge fulfillments.

At any moment, the set of warehouses a line is actively set to ship from is derived from its live (non-cancelled, non-terminal) fulfillment orders — so cancelling or completing a fulfillment order updates where the line is considered shippable from.

The warehouse lock: Why a warehouse freezes once you ship

Routing is fully editable — until it isn't. The moment a line begins to fulfill, its warehouse becomes load-bearing: it's the location whose stock was drawn down, the location the inventory ledger and the COGS basis were written against, and the location a provider was told to ship from. Silently repointing it afterward would leave the books describing one warehouse while reality happened at another.

To prevent that drift, SKU.io locks the warehouse on two records once fulfillment starts against them:

RecordLocks once…Effect
A shipment (the executed fulfillment record)it has recorded any inventory movementIts warehouse can no longer be changed.
An order lineit has a fulfillment line on a non-cancelled shipment — or a submitted / in-flight fulfillment order already covers itIts warehouse can no longer be changed.

The line lock deliberately arms before anything physically ships: once a fulfillment order has been submitted to a carrier or 3PL, the line is locked even though no shipment record exists yet, because a provider has already been told where to ship from.

Cancelled work never locks a line

Fulfillment lines that hang off a cancelled shipment, and cancelled fulfillment orders, don't lock anything. Those will never ship, so the line stays free to be re-routed — for example when a channel cancels a shipment and the order is re-planned to a different location.

What you see when a warehouse is locked

Because the lock protects a line that's already shipping, you meet it when you try to edit the wrong thing:

  • Changing the warehouse on a line that has already started shipping is refused. SKU.io blocks the save and the error identifies the specific record and the reason — that the line already has a shipment (or the shipment already has inventory movements), so its warehouse is locked.
  • Editing a line that's fulfilled at a warehouse and quantity is blocked the same way from the order-edit flow, which is the first line of defense; the lock itself is the backstop underneath it. See View and edit a sales order.

The remedy is always the same: to re-route a line that has shipped, void its shipment first. Voiding reverses the inventory movement and releases the lock, after which the warehouse can be reassigned. See Void, restore, or reset a shipment.

This isn't a way to "fix" history — it's a guardrail

The lock exists so that a shipped line's warehouse can never quietly change. If you genuinely need a different warehouse, you are changing the plan: void, re-route, and re-ship. There is no in-place override in the normal UI.

Clearing a line's warehouse cancels its allocations

There is one special transition worth understanding: setting a line's warehouse back to none (for example, when a channel such as Shopify unassigns a line asynchronously). A line with no warehouse is routed out of local warehousing and must hold no reservation — any allocation left behind would be a ghost commitment against a warehouse the line no longer points at, inflating committed stock and keeping a phantom backorder alive.

So whenever a line's warehouse is cleared, SKU.io automatically cancels that line's active allocations, exactly once, at the moment the warehouse is nulled. You don't trigger this by hand — it's a write-boundary guarantee that keeps a cleared line from stranding a reservation. See Inventory allocation and backorders.

Under the hood: The repair-only bypass and its safety net

The lock is intentionally strict, so there is exactly one sanctioned way past it — a bounded repair mode reserved for SKU.io's own inventory-health tooling. It exists only to reconcile pre-existing drift (for example, realigning a record to the warehouse where its inventory movement actually landed) and runs synchronously within a single operation; it's never something a product feature or a background job reaches for. Adding any new caller requires explicit engineering review.

Two things administrators and support staff should keep in mind:

  • Raw database writes bypass the lock. The lock rides on the application's save events, so a direct SQL update or a bulk query-builder write can move a warehouse without tripping it. Such writes are reviewed by hand and aren't part of any normal workflow.
  • A health sweep is the backstop — but correcting drift is a deliberate step, not silent self-healing. If drift ever does slip through — a stranded allocation on a cleared line, or a warehouse that diverged from where movements landed — an hourly sku:inventory:health:notify sweep runs in identify-only mode: it detects the drift and alerts the team on Slack, but it doesn't repair. Remediation is then applied with sku:inventory:health --fix. A separate automated sweep (every few hours) self-heals fulfillment-order drift, but the stranded-allocation and diverged-warehouse repairs named here are run on demand rather than on a schedule. The invariant holds because escapees are caught and corrected — not because those two classes quietly repair themselves each hour.

This is the "Stage 0" form of a larger change: over time, the warehouse is moving onto the fulfillment order as the single source of truth for where work ships from, which is why the lock reasons about both the line and its fulfillment orders today. See Two-tier fulfillment: intent vs execution.

Worked example: Two warehouses, one order

Follow SO-DOCS-0001 for Blue Bottle Retail with two product lines:

MomentStainless Water Bottle 750mlInsulated Tumbler 500ml
You create and route the linesRouted to Main Warehouse (editable)Routed to Overflow Warehouse (editable)
You approve the orderAllocated at Main Warehouse; still re-routableAllocated at Overflow Warehouse; still re-routable
You dispatch the Overflow line to a carrierStill editable — no work startedLocked — a submitted fulfillment order now covers it
You ship the Main Warehouse lineLocked — its shipment recorded inventory movementsLocked
You void the Overflow shipmentLockedUnlocked — void reversed the movement; re-route freely

Notice that each line locks independently, at its own moment: the Overflow line locked when its fulfillment order was submitted, before anything shipped, while the Main Warehouse line locked only when its shipment actually moved stock. Voiding is what returns a line to an editable, re-routable state.

Next steps

Last verified: