Skip to content
ERPNext Data Model
Esc
navigateopen⌘Jpreview
On this page

Putaway & Warehouse Slotting Rules

Rule-based automatic bin/warehouse assignment for incoming stock

1. Requirements

1.1 Functional requirements

  • Let a warehouse manager define, per item-and-warehouse pair, a Putaway Rule: a capacity ceiling and a priority, so incoming stock is routed automatically instead of a person typing a destination on every line. Several rules can be layered for one item across warehouses — primary rack, then overflow, then floor stock.
  • Capacity may be authored in whatever unit the manager finds natural, but the system resolves it internally to the item’s own stock-keeping unit.
  • On save, the system evaluates every candidate rule for a line, orders them, and splits the incoming quantity across as many rule-backed warehouses as it takes, or until no candidate has room. A quantity no rule can accommodate is left out of the document and reported back, not posted into a default location.
  • One shared entry point drives both an external receiving document and an internal stock-transfer/receipt document, and a hand-typed destination is still checked against a matching rule’s ceiling at save time.
  • Rule utilization should be inspectable in aggregate — per warehouse, how full each slot is as a percentage of capacity.

1.2 Non-functional requirements

  • Determinism: re-saving a draft with unchanged rules and stock levels reproduces the same split and row identities.
  • Fractional-unit safety: when a unit of measure must be whole, headroom is never exploited fractionally.
  • Company scoping: a rule, its warehouse, and the posted transaction must share one legal entity.

1.3 Constraints

  • Both the auto-placement pass and the standalone capacity check read “how much is already there” from the live stock ledger balance at evaluation time, not a snapshot — free capacity is only as fresh as the last posted movement.
  • Only one rule may exist per item-and-warehouse pair.
  • The rule set auto-assigns a destination only from a small, fixed set of receiving document types; others can be blocked by a ceiling but never have it choose a destination.

2. High-Level Design

2.1 Component diagram

2.2 Rule setup and invocation

Saving a rule runs a short ordered check, not a branching flow: reject a duplicate for the item+warehouse; reject priority below 1; confirm the warehouse belongs to the rule’s company; reject a capacity smaller than what’s already stocked there, or zero; then compute stock_capacity = capacity * conversion_factor — the figure every later check reads.

Only two document types invoke the assignment pass automatically: an external receiving document (unless it’s a return), and an internal stock-transfer/receipt document whose purpose is a material receipt or warehouse-to-warehouse transfer. Two more document types — an update-stock purchase invoice and a stock reconciliation — never get an auto-assigned destination but are still subject to the Capacity Guard (§3.4).


3. Deep Dive

3.1 Data model

Putaway Rule

Field Purpose
item_code / warehouse / company identity of the rule; unique per item+warehouse
priority lower fires first; must be ≥ 1
capacity ceiling in the rule’s own chosen unit of measure
uom / conversion_factor the chosen unit, and its ratio to the item’s stock unit
stock_capacity capacity * conversion_factor, stored at save time
disable excludes the rule from the assignment pass

Receiving Line — the generic name for one row of the receiving document’s item table: requested quantity in its own unit, the corresponding stock-unit quantity, the resolved warehouse, and a back-reference to the rule that placed it. A repeated item across several rows in one document shares one rule’s shrinking headroom rather than each row re-reading the original capacity.

3.2 Rule selection and ordering

Two details matter. The initial fetch orders by priority then declared capacity, but that’s provisional: among same-priority rules, the real tie-break is remaining free space, computed only after the live balance read. And “no candidates” differs from “candidates exist but none has room” — the former leaves the line’s original warehouse untouched; the latter reports the item as unplaceable.

3.3 Splitting a quantity across multiple destinations

for each rule in the ordered list, while pending stock-unit qty > 0:
    allocate = min(pending qty, rule.free_space)
    convert allocate back to the line's own unit of measure
    if that unit must be whole: floor it, recompute stock-unit amount used
    if the (possibly floored) amount is zero: stop
    write a Receiving Line: this warehouse, this qty, tagged with the rule
    pending -= amount placed; rule.free_space -= amount placed

if pending stock-unit qty > 0 after every rule is tried:
    do not write a line for it — report the item and the leftover instead

Worked example. 350 kg of an item arrives. Two rules apply, both priority 1 (a tie), both currently empty: Rule A on Warehouse 1, 100 kg capacity; Rule B on Warehouse 2, 200 kg capacity. Free space breaks the tie — Warehouse 2 is tried first:

pending = 350 kg
Warehouse 2 (free 200 kg): allocate 200 kg -> Line: 200 kg -> Warehouse 2; pending = 150 kg
Warehouse 1 (free 100 kg): allocate 100 kg -> Line: 100 kg -> Warehouse 1; pending = 50 kg
No rules left. 50 kg dropped from the document; item + 50 kg reported as unassigned.

Two lines are written (300 kg placed); the remaining 50 kg becomes only a message, never a row in the saved document.

3.4 Error handling

  • Capacity Guard: re-validates every line’s destination at save/submit — receiving document, transfer document, update-stock purchase invoice, and stock reconciliation — regardless of whether the assignment pass ran. The first three compare against remaining free space; a reconciliation compares against the rule’s full capacity, since it sets an absolute ending balance rather than adding to what’s there.
  • Whole-unit rounding: fractional leftover headroom, converted to a must-be-whole unit, is floored away rather than rounded up — a rule can show stock-unit capacity unusable in the line’s own unit.
  • Idempotent resave: reapplying the pass compares the proposed result to existing lines field-by-field; if nothing changed, existing row identities are left alone.
  • Batch/serial items: serial numbers already picked for a line split across the resulting rows in the same order as the quantity split.

4. Scale and Reliability

  • Evaluation cost scales with candidate-rule count per item, not catalog size — each candidate costs one live balance lookup.
  • The balance read is always live, so it’s correct at read time, but nothing locks a rule’s headroom during one pass — two documents for the same item saved concurrently can both see the same “free” space and both claim it. The design relies on low-frequency saves rather than guarding against this race.
  • The Capacity Guard aggregates all lines resolving to the same rule within one document before comparing against the ceiling, so it can’t be slipped past line-by-line.
  • The per-warehouse utilization view re-runs the same live lookup per rule per request — fine at its paginated page size, not built to scale past it.

5. Trade-off Analysis

Decision Trade-off
Capacity authored in a configurable unit, converted to the stock unit once at save Lets staff think in bags/pallets, but the stored conversion factor is a snapshot — a later change to the item’s own conversion leaves rules stale until resaved.
Same-priority ties broken by live remaining free space Intuitive “fill the emptier destination first,” but fill order between close-capacity rules isn’t stable across saves as stock shifts.
An unplaceable remainder is dropped, not posted to a fallback location Keeps the ceiling honest, but the only record of the leftover is a transient message — nothing persists it for follow-up.
Live ledger read per rule per save, no reservation or lock Simple and always fresh, no allocation table to keep in sync — but concurrent receipts against the same rule can race.
One shared routine serves both external receipt and internal transfer A single algorithm to test, but document-specific nuances (excluding a transfer’s own source warehouse) leak into an otherwise generic routine.
Capacity enforced twice — auto-assignment and a standalone guard for typed destinations Closes an obvious bypass, but the two checks compute “available capacity” differently for the reconciliation case, inviting drift.

6. What to Revisit as the System Grows

  • Concurrency: add a lock or reservation scoped to one rule for the duration of an assignment pass once concurrent same-item receiving becomes routine.
  • Stale conversion factors: refresh a rule’s stored stock-unit capacity automatically when the item’s own unit-of-measure conversion changes, instead of requiring a manual resave.
  • Persisting the unplaced remainder: today it is only a one-time message; a durable record would make follow-up auditable.
  • Unifying the two capacity checks: the assignment pass and the standalone guard should share one definition of “available capacity,” particularly for the reconciliation special case.

This document runs shorter than the module’s High-priority siblings by design: the rule-based placement logic is a small, self-contained pass over a handful of files, and the source genuinely supports only this much grounded detail before further length would be padding.

Was this page helpful?