User Manual
Help Center/User Manual/Inventory: inquiry, adjustments, counts, and transfers

Inventory: inquiry, adjustments, counts, and transfers

Last updated 2026-10-23

Look up stock on desktop and mobile, post direct adjustments, run cycle counts (including ABC auto-counts), and move stock between warehouses.

WMS inventory uses an append-only ledger (wms_inventory_ledger) as its single source of truth: every inventory movement writes a ledger line with a positive/negative qty_change — balances are never edited directly. The balance projection is maintained by the system, and the ledger forbids UPDATE/DELETE at the database level.

Looking up inventory#

  • Desktop WMS → Inventory: a detail view (filter by SKU, location, status, lot, serial number, channel and more) and a summary view (quantities grouped by item + status + type).
  • In the detail view, Location shows the location code (plus its alias when one is set) and the filter dropdown uses the same label; the receipt number is a link straight to that inbound receipt. Shipped Time is now shown by default so you can see at a glance when stock left the building. Internal ID columns are hidden by default — if you ever need them, turn them on under Customize.
  • The Locked Locations page (Inventory → Locked Locations) lists locations locked by in-progress count tasks. Search by location code, task number or task name; tick multiple rows to cancel the locking count tasks in bulk and release the locations — a confirmation dialog lists every task that will be cancelled before you confirm.
  • Mobile inventory page: search by SKU or product name; cards show SKU/name/location/quantity (read-only).
  • Lot inventory is sorted by expiry date ascending (FEFO). Expiry alerts (expiry-alerts?days=90) list available stock with qty>0 and expiry within today+days, returning days_to_expiry.
  • The “Allocated” tab shows inventory already reserved by picks but not yet picked: outbound order / pick task / product / quantity. Check it before promising stock to someone — allocated stock is spoken for.

Inventory import (CSV)#

On WMS → Inventory, download the CSV template, fill it in, and upload it. The system validates every row first and shows you any problem rows (with the reason in plain words) before anything is written — fix them and re-upload, or go ahead and import the valid rows. Imported stock counts in your balances right away and can be allocated and picked like received stock.

  • Serialized items: one row per serial number, with qty = 1. A serial number that is already in the system (or repeated inside the file) is rejected row by row — the same unit is never counted twice.
  • Batch-tracked items: the lot number is required on every row. Quantities must be greater than 0.
  • Do not import the same file twice after a successful import: the rows would be added again on top of the stock you already imported (serialized rows are protected by the duplicate check above). If an import stops because the summary sync failed, the rows it just wrote are rolled back automatically and no data is left behind — fix the problem and import the file again. Only when the message explicitly tells you not to retry should you contact your administrator instead.

Inventory adjustments (direct mode)#

Adjustments are posted directly to the ledger — there is no “adjustment document” state machine and no approval flow. The current entry point is the API (POST /api/wms/inventory/adjust); there is no dedicated desktop entry page yet, and callers must explicitly hold the wms.inventory_adjust permission.

Separately, the desktop Inventory → Adjustments pages work with adjustment documents (create → approval → posting). Open any document: the Processing Timeline on the right side of the detail page shows, in order, who created it and when, and the approval outcome — approved or auto-approved, or rejected together with the rejection reason.

When you create an adjustment document, each line (except “new inventory”) is tied to one exact stock row: pick the source location first — picking locations list their product rows directly, storage locations ask you to pick the pallet on that location first — then tick the row to adjust. Posting checks every line before anything is written: if a line is not linked to a stock row (for example the stock was deleted after the document was created), approval stops with the line number and the reason in plain language, and nothing is posted — reject this adjustment and create it again. Changing the owner on a line updates the client shown in the inventory list.

Three stricter guards now protect adjustment documents: status is a fixed list (no free text — pick one of the allowed values), the destination location is validated before posting (a location that does not exist or belongs to another warehouse stops the approval in plain words), and serial-number lines show the current on-hand serial values so you can compare before confirming — a quantity that does not match the serial list is rejected instead of being posted.

  1. Required: warehouse_id, location_id, item_id, reason_code (free text such as damage/lost/found — WMS has no reason-code master data).
  2. Quantity, pick one: qty_after (adjust to a target value) or qty_delta (non-zero increase/decrease). Filling both or neither fails validation; qty_after with a zero delta returns {adjusted:false} without writing to the ledger.
  3. Status can only be available / hold / damaged (default available). Negative adjustments validate available quantity first (WMS_STOCK_001 on shortage); locations locked by an unfinished count task are rejected (LOCATION_LOCKED).
  4. One ledger line is written with transaction_type=adjust, qty_change=delta (positive = increase, negative = decrease) and the mandatory reason_code.
  5. Storage locations only hold pallets: any adjustment that increases stock on a storage location must carry a pallet_id (the pallet must belong to your tenant, sit on that location, and be in a valid status). On the desktop adjustment form a pallet picker appears for storage locations (required).

Cycle counts#

  1. Create the task (desktop/API): scope is cycle (cycle count — pass location_ids or location+item lines), full (auto-generates from every stocked location×item in the warehouse, capped at 5000 lines) or spot (sample check); count_type is blind or open (default open). The task records a system_qty snapshot from balances at creation; overlapping an unfinished count task’s locations rejects creation (this is where the lock takes effect).
  2. Execute on the phone: enter the counted qty per location/product line → enter-count submits one line at a time (counted lines show system/counted/variance; re-entering a counted line marks it recounted). The task moves pending/assigned → in_progress.
  3. Approve on desktop: approve passes lines (counted/recounted → approved) or rejects them back to pending (counted qty and variance are cleared) for re-counting.
  4. Post: post only processes approved lines (zero-variance lines are skipped). Surpluses write adjust lines with reason_code=found, shortages with reason_code=lost (reference_type=count_task). When all approved lines are processed the task completes and the location locks release automatically.
  5. ABC auto-counts: POST /api/wms/count-tasks/abc auto-generates cycle tasks when a product’s abc_class count interval expires. Defaults: A=7 days, B=30 days, C=90 days; overridable per warehouse (abc_interval_a/b/c_days) or per call (interval_days). Triggered by cron or manually.

Inter-warehouse transfers#

Transfers are document-style (draft → submit → approve → ship → receive) and currently API-only — there is no desktop document page yet. Transfer number format: TRF-YYYYMMDD-XXXX. Moving stock between two locations in the same warehouse is allowed (locations must differ).

  1. Create → draft; submit → submitted.
  2. Approve: approved validates each line’s source-location available qty (shortage → WMS_STOCK_001); rejected ends the flow.
  3. Ship → in_transit: available qty is validated again (in case someone else picked it after approval) → writes a transfer double-leg ledger (source location available −qty / same location in_transit +qty).
  4. Receive → completed: source in_transit −qty / destination available +qty; detail lines pending → done. Cancel is only allowed from draft/submitted/approved — in_transit can only be completed by receiving.

Inventory statuses#

available (on hand) → allocated when reserved by outbound/pick → shipped on dispatch. Quality/exception holds move stock to hold; breakage moves it to damaged; after a transfer ships, the source location holds in_transit. Adjustments may only specify available/hold/damaged.

Count lock: locations covered by an unfinished count task are locked — adjustments, moves and replenishment drops are all rejected with LOCATION_LOCKED. The lock releases automatically when the task is completed or cancelled.