Apps · Operations

Warehouse Management

Your stock already knows how much. This knows where — down to the zone, aisle or bin — so nothing is hunted for again, every move is recorded rather than remembered, and the month's storage invoice builds itself from what actually happened.

User guideApps › Warehouse Management

It sits on the stock you already run

Warehouse Management doesn’t replace your inventory — it adds the one thing stock figures never had: where each unit physically sits. Your catalogue, quantities, costing and movement history stay exactly where they are, in Stock & warehouse.

The relationship is worth understanding, because it explains most of the module’s behaviour:

  • Stock says there are 100 units of a product in the Riverside warehouse.
  • Warehouse Management says 60 of them are in C5-1, 40 are in B2-2, and one pallet arrived on 3 March.

Those two must always agree. Moving a pallet from C5-1 to B2-2 changes where, not how much — so an internal move leaves your stock figure completely untouched and never writes a movement-ledger entry. Only things that genuinely add or remove stock (a receipt, a sale, a real shortage found during a count) touch the numbers.

Turning it off leaves your stock alone

Warehouse Management holds placements — the where — in its own tables. Uninstalling it removes the spatial detail; your products, quantities, costing and movement history are untouched, because it never owned them in the first place.

Screens and who can open them

Everything lives under Warehouse in the sidebar. Module access gets somebody in; the finer actions each need a capability, granted per role under Settings → Module Access. Links a member can’t use are hidden rather than shown and then refused.

Screen What it does Needs
Overview Read-only summary of the site. Access to Warehouse
Zones & locations The tree of buildings, zones and bins. Reading is open; editing isn’t. Access to read · Manage layout to edit
Put away Assign incoming stock to locations, split a receipt across zones. Put away
Pick Where to take goods from for an order. Reading is open; confirming isn’t. Access to read · Move stock to confirm
Moves Move stock between locations inside a warehouse. Move stock
Floor plan Drag zones into a real layout of the building. Access to read · Manage layout to edit
Availability What’s free, what’s full, what’s unmeasured. Access to Warehouse
Statistics Space yield, dwell, revenue mix, concentration. Access to Warehouse (revenue figures need Run billing)
Measurement units The units you model and price space in. Manage layout
Contracts & rates Customer contracts and storage / handling rate cards. Manage rates
Billing Generate, issue and send storage documents. Run billing
Receivables Payments, allocations, ageing and chasing. Run billing

Read-only screens are deliberately ungated. How full the building is isn’t a privileged fact, and gating it would grey out the page for exactly the floor staff who need it. Contracts, billing and receivables are gated — even previewing a run exposes every customer’s negotiated rates.

Setting up

1. Decide what you measure space in

Warehouse → Measurement units is a list you edit, not a fixed menu, because the unit is what you price against. Eight ship on install — m², ft², m³, kg, tonne, pallet, container, unit — and you can rename them, add your own, or delete ones you’ll never use.

Units fall into two kinds, and the difference matters:

Kind Examples Converts?
Physical m², ft², m³, kg, tonne Yes, freely. A tonne is 1000 kg in every warehouse on earth, so the ratio is safe to apply anywhere.
Countable pallet, container, unit Only to itself. How much floor one pallet occupies depends on your building, your racking and your stacking policy.
Pallets don't convert to square metres — your building does

There’s no universal pallet-to-m² ratio, so the module refuses to invent one. The equivalence your site has is a property of that warehouse, and you set it on the warehouse profile as a pallet area. Then a 1400 m² building can answer “how many pallets is that” for you specifically, and stay silent for everyone else.

2. Give each warehouse its spatial facts

Your POS warehouses already exist as names and codes. Warehouse Management decorates them with usable area, the unit it’s measured in, and the pallet area if you use pallet positions. Nothing is written back to the POS record.

Ask for a capacity it can’t work out honestly — an unprofiled building, no area set, or a cross-kind conversion like square metres to kilograms — and you get blank, not zero. A blank is a prompt to finish configuring. A fabricated number would quietly misprice space.

3. Build the tree

Warehouse → Zones & locations holds buildings, zones, aisles, racks and bins as a tree. Codes must be unique within a warehouse — staff navigate by them (“it’s in C5-2”), so two of them in one building makes that instruction ambiguous. The same code across different warehouses is fine; every site having an “A1” is normal.

Capacity on each node is set one of two ways, and you choose per node:

  • Declared — you state it. Right for a building, whose usable area is not the sum of its zones: aisles, docks and circulation live in the gap.
  • Rolled up — the sum of its children. Right for a rack, whose capacity genuinely is the sum of its shelves.

A rollup returns blank rather than a partial sum when a node has no children, when any child’s capacity is unknown, or when children are measured in units that don’t convert. A partial sum shown as a total reads as “this is how much room there is” while silently omitting whatever hasn’t been measured.

To create many locations at once, use a pattern like C5-1..C5-40. Re-running a pattern to fill a gap fills the gap — existing codes are skipped, and you’re told how many were created versus skipped rather than the whole thing failing on a duplicate.

A zone with no capacity set is *not measured*, never empty

This rule runs through the entire module. Calling an unmeasured zone empty would let somebody promise space that may not exist; calling it full would stop them using space they have. So it shows hatched, is excluded from “where will this fit”, and is left out of site totals — which report blank rather than summing around the hole.

Everyday work

Putting stock away

Warehouse → Put away shows what has arrived but hasn’t been given a location. That backlog isn’t a queue somebody maintains — it’s worked out as stock on hand minus everything already placed. A receipt taken before you installed the module, or a manual stock edit, shows up on its own.

One receipt can be split across several zones. Exceeding a zone’s declared capacity is a warning, not a refusal — real warehouses overflow, and refusing to record where the pallet actually went would leave the system lying about a spot you can walk up to and see.

Moving stock

Warehouse → Moves shifts goods between locations inside one building. Your stock figure doesn’t change, because nothing left the building.

Every move is written to an append-only log with who did it and when. A wrong move is corrected by moving the goods back, leaving both events on the record — the trail is the point. It survives people leaving the company and zones being deleted years later.

A pallet keeps its arrival date across a move. That matters more than it sounds: resetting it would send the picker to the newest goods and make dwell-time statistics reward shuffling old stock around.

Picking

Warehouse → Pick answers “where do I take this from”. The order is your existing stock rule, unchanged: earliest received for ordinary products, earliest expiring for lot-tracked ones. Warehouse Management adds exactly one thing — which spot.

A short pick is an outcome, not an error. You get every take that can be made plus what’s missing, rather than a refusal that leaves the picker guessing.

Counting

Warehouse → Count / reconcile runs a cycle count against a zone. Counting a parent automatically covers everything beneath it, so counting C5 covers C5-1 to C5-3 without listing them.

Counts are blind by default — the expected figure is withheld while the sheet is open. Show a counter the number you expect and you’ve measured their agreement, not the shelf.

What happens to a variance depends on what kind it is:

What you found What happens
Units in a different spot than recorded (the common case) Placements are rebalanced. Nothing is posted to stock — the goods went nowhere.
A genuine shortage or find Posted as a stock adjustment, so weighted-average cost, FIFO layers and the ledger all stay correct.

A line nobody counted is skipped, not treated as zero — otherwise an incomplete count invents shrinkage for every shelf nobody reached. A line counted as zero is a real finding and is posted as one.

Seeing what’s free

Warehouse → Availability is the “can I say yes to this” screen: occupied versus free, rolled up so “how full is C5” means the whole subtree, not just the label on the door.

  • Unmeasured is not zero and not full. It renders hatched and is excluded from the totals.
  • Over-capacity is shown, not hidden. A zone holding 110 of a stated 100 is real — it gets a critical colour plus an icon and a label, never colour alone.
  • “Where will this fit” skips unmeasured locations rather than assuming they’d do. A map that promises space is a stronger promise than a suggestion a human confirms.

Warehouse → Floor plan is the same information as an actual layout of the building — drag zones into position and the plan fills in as stock arrives. Reading it is open to everybody; rearranging it needs Manage layout.

Holding goods for other companies

This is a first-class mode, not an edge case. Every placement can carry an owner: blank means your own goods, set means you’re holding them for a customer.

  • Stock with different owners never merges, even in the same spot. One customer’s pallets are not interchangeable with another’s.
  • A member who can’t see a customer’s record can’t see that customer’s stock, invoices or balances either. The permission you already set is the one that applies.
Customer goods aren't your inventory

Goods you hold for someone else are bailment, not an asset — but your stock valuation has no concept of ownership, so it counts them as yours. For a busy 3PL that can dwarf the company’s real assets. The module reports the figure that must be backed out and names the products involved; it does not silently change how your books are computed, because that’s an accounting decision rather than a side effect of a warehouse screen.

Contracts

Warehouse → Contracts & rates records what you agreed with each customer.

Model Tracked down to Who’s responsible for the contents
Space only The pallet The customer’s. You’re renting space; contents aren’t counted.
Full custody The box or the individual unit Yours — and pick order can be made obligatory.

A full-custody contract tracked only to pallet depth is refused: full custody promises a unit-level count, and recording a promise the system can’t deliver on is a gap that only surfaces in a dispute.

Where a contract requires it, picking out of order is refused rather than warned about — and the requirement is read from the customer’s contract on the server, so it can’t be skipped by whoever is doing the picking.

A contract records terms, never a forecast. There’s deliberately no expected-volume field: contracts are annual while volume is genuinely seasonal, and a stored expectation gets read as a commitment nobody made.

Charging for it

Rate cards

Rates resolve most-specific-first: customer and zone → customer → default and zone → default. Every resolved price tells you which tier it matched, because “why did you charge me that” is a question operators get asked and have to be able to answer.

A price rise adds a new line rather than editing the old one, with the effective date on the line. Last month’s invoice still reproduces from the same data.

Two refusals worth knowing, both there to prevent invented money:

  • An m² rate will not pay a pallet quantity. Matching across units means converting a price nobody negotiated.
  • No matching rate returns nothing at all, never zero. Billing zero for storage hands the customer a free month that nobody notices until the year-end numbers look wrong. The charge is reported as unpriced and blocks the invoice until you add a rate.

How space is measured for billing

Occupancy is captured daily as an append-only record and never recalculated, because recalculating history would silently change an invoice you already sent.

It bills in unit-days, not an average. A customer who held 100 m² for 10 days of a 30-day month owes for 1000 m²-days. Averaging first and multiplying by the period systematically over-bills customers whose stock comes and goes — exactly the pattern a dynamic contract is bought to avoid.

Missing days are surfaced, not smoothed. If six days of a month were never captured, the document says so on the customer’s own copy.

Handling and services

Handling is derived, services are ordered. Goods-in and goods-out already happened and already live in the move log, so charges come from there. A service — stickering, re-packing, labelling — exists from the moment the customer asks, before any work is done.

  • Only put-away and picking count as handling. Internal moves and counts are you reorganising your own warehouse, and a customer must never be charged for that.
  • A service is billable only once it’s marked complete. A job half-done at month end belongs on next month’s invoice.
  • The rate is stamped at completion, not looked up at billing time, so a price change can’t re-price work already agreed and done.
  • Billing follows what was actually done when that’s recorded — a container may be 80% stickered because the rest arrived damaged. A recorded zero is honoured; an unrecorded one bills what was ordered.

The monthly run

  1. PreviewPreview writes nothing at all. Look at every line, every rate and every total before anything exists. Documents already on file for that period are flagged rather than offered again.
  2. CommitCreates the documents as drafts. Lines are still editable, and a draft has no number yet.
  3. IssueAssigns the number and freezes the lines. From here the document is a real reference the customer can quote.
  4. SendEmails it. The same renderer produces the email and the print view, so the two can’t drift.

Re-running the same period doesn’t double-bill: a second run creates nothing and bills no services again.

One document, or one per container

Mode Shape Who needs it
Consolidated One document per customer per period. Almost everybody.
Per receipt One document per inbound consignment, plus one period document. Large logistics accounts whose customs and landed-cost accounting needs each container’s cost attached to that container.

Per-receipt can’t split storage, and says so. Space is measured per location per owner per day; there’s no honest way to decide which container’s pallets accounted for which square metres on the 14th. So storage lands on a single period document alongside the per-container ones, rather than being apportioned by guesswork onto a document a customs broker relies on.

Tax sits on the contract

Not on a rate card, not on a workspace setting. The distinction is real and per-customer: a bonded customer moving goods in transit is outside VAT, while a domestic customer storing for local sale is standard-rated — and one operator routinely has both in the same building.

The rate is stamped onto the document when it’s created, so a change next year can’t re-state an invoice already sent. A customer with no rate gets no tax row at all, which is deliberately different from a zero — a “VAT 0.00” line on a standard-rated invoice looks like a decision somebody made, while an absent row looks like what it is.

A draft can't be sent

A draft has no number and mutable lines, so sending one hands the customer a reference that can legitimately change underneath them. Issue it first. Re-sending an already sent document is allowed — “they say they never got it” is the most common request an operator gets.

Sending is refused up front if no email sender is configured, rather than reporting “Sent” for a document that went nowhere and finding out from the customer weeks later.

Getting paid

Warehouse → Receivables tracks what’s owed.

Payments and the invoices they settle are kept separately, because one wire transfer routinely settles several invoices and one invoice is routinely settled by several payments. Forcing one-to-one makes you invent fake payments to split a transfer, and then the bank statement stops reconciling.

A payment you haven’t allocated to anything is a prepaid balance — it falls out of the model rather than needing a separate concept.

No due date means unpaid, never overdue. A due date comes from the payment terms on the contract; without one, nothing is chased.

Chasing is two separate things, and the split is the point:

Notification Goes to Default
Overdue receivables Your admins On
Invoice reminder The customer Off

Auto-emailing a customer about money isn’t a default anybody should inherit — a reminder that goes out to a major account whose payment is already in flight is a relationship problem, and the data can’t tell the difference. Switch it on deliberately. Reminders run at three levels (1, 15 and 30 days), never a daily drip, so a customer gets at most three messages per invoice.

Statistics and what next month looks like

Warehouse → Statistics reports space yield, dwell time, revenue mix and customer concentration. The same discipline applies throughout: refuse to answer rather than answer wrongly.

  • Dwell is measured when goods leave, not from what’s still on the shelf. Averaging the age of current stock only measures the slow movers — everything fast has already gone. The age of stock on hand is reported too, but as its own figure and never called dwell.
  • The median leads, not the mean, because every warehouse has one pallet forgotten for two years. The sample count sits on the tile, since a median over three picks is noise.
  • Yield comes back blank when nothing was stored. Reserved space earns money while holding nothing, and dividing by zero would print an infinite yield on a pricing sheet.
  • How many days were actually captured is shown above the numbers. A yield computed over 3 days of a 90-day period isn’t that period’s yield, and you need to know that before reading anything else.
  • Revenue counts issued documents only. A draft is a proposal.

Projection splits next period in two, and never merges them into one confident figure:

  • Committed — reserved space you’re contractually owed for. Commitments that can’t be priced are counted and surfaced, because missing from this figure means missing from a number you might take to a bank.
  • Variable — what customers actually store, always given as a range from what a normal day looked like, never a single number.

It refuses in two situations, both deliberate. Below 14 days of observed occupancy the variable half comes back blank with the reason, rather than extrapolating from three days. And the total is blank whenever the variable half was refused — adding a certainty to an unknown produces an unknown, not a smaller certainty.

It also deliberately doesn’t follow a trend. A rising line over three weeks is as often one customer’s seasonal peak as it is growth, and projecting the slope forward turns a good month into a promise.

Why is this number blank or wrong?

Symptom Likely cause
A capacity shows blank The warehouse has no profile, no usable area, or you asked in a unit that can’t convert from the one it’s measured in — square metres to kilograms is a category error, not a conversion. Blank means “not answerable”, never “none”.
A zone shows hatched on the availability map No capacity is set on it. It’s excluded from totals and from “where will this fit” until you measure it.
A rack’s capacity is blank despite its shelves having figures Rolled-up capacity refuses a partial sum. One child with unknown capacity, or children measured in units that don’t convert, blanks the parent.
The site total won’t add up Any unmeasured root, or roots measured in different units. It reports blank rather than summing around the gap.
Stock appears in Put away that you never received The backlog is stock-on-hand minus what’s placed, so anything received before the module was installed — or edited manually — surfaces automatically. Place it once and it’s gone.
A move didn’t change my stock figure Correct. Internal moves change where, not how much, and deliberately write nothing to the movement ledger.
A count posted no shrinkage The variance was units in a different spot than recorded, so placements were rebalanced. Only genuine shortages and finds post to stock.
A picker was refused a spot That customer’s contract makes pick order obligatory. The refusal names the location that has to be picked first.
A charge shows as unpriced No rate line matched — including the case where a rate exists but in a different unit than the quantity. An m² rate won’t pay a pallet quantity. Add the matching line and re-run.
Storage is missing from a per-receipt document Per-receipt can’t split space across consignments, so storage lands on the single period document instead.
An invoice says some days weren’t charged Occupancy wasn’t captured on those days. The document discloses it rather than quietly billing a full month or a short one.
The customer isn’t being chased Either the contract has no payment terms, so nothing has a due date, or customer reminders are still off — they’re off by default on purpose.
Space yield is blank Nothing was stored in the period. Reserved-space revenue with zero occupancy has no meaningful per-unit-day figure.
Projection shows no total The variable half was refused — fewer than 14 days of occupancy history. The committed half is still shown on its own.

Where it fits

Warehouse Management reads stock from Stock & warehouse and deliveries from Purchasing & suppliers, and its customers are the same contact records the rest of the workspace uses. Billing documents it produces are its own — separate from Invoices, because a storage document carries space, handling and services rather than product lines.

Roles and permissions come from the workspace, not from a separate warehouse user list: see Permissions and Members.