Warehouses & Carriers

Where AIR's stock physically lives, and who moves it to customers. This page covers the Warehouse record — including its stock locations, its linked carriers and the regions it serves — and the standalone Delivery Carrier master list.

Permissions

Both Warehouses and Delivery Carriers are restricted to super administrators only — a hardcoded check, not a permission string. The seeded permissions warehouse.view/create/edit/delete and delivery_carrier.view/manage exist for the future API surface but currently have no effect on who can open these pages in the console — only the super_admin role can.

Warehouses Built

Warehouses live under Warehouses → Warehouses. A warehouse is where AIR holds stock — the head-office godown, a branch, or a purely notional location used for accounting. Full create/edit/delete is available.

Warehouse list

ColumnShows
nameWarehouse name. Searchable, sortable.
codeCode badge, searchable.
warehouse_typeType badge — HO or Branch.
warehouse_purposePurpose badge — Internal, Sale, Display or Not Used.
is_defaultTick/cross icon — whether this is the system's default warehouse.
stock_locations_countLabel "Locations" — how many stock locations exist under this warehouse. Sortable.
created_atSortable; hidden by default.

Filters: Warehouse Type, Warehouse Purpose (both drop-downs), and Default (yes/no/either).

Screenshot placeholderThe Warehouses list — Name, Code badge, Type badge, Purpose badge, Default icon and Locations count columns, with the Warehouse Type filter open.

Create / edit a Warehouse

The form has three sections. "Warehouse Details" is expanded by default; "Address" and "Operations" are collapsible and start collapsed.

Warehouse Details

FieldTypeRequiredNotes
nameTextYesMax 255.
codeTextYesMax 20, unique. Locked (disabled) once the warehouse exists.
warehouse_typeSelectYesHO or Branch.
warehouse_purposeSelectNoInternal / Sale / Display / Not Used.
default_carrier_idSelect (searchable)NoWhich carrier is pre-selected for despatches from this warehouse. Nullable.
region_idSelect (searchable)NoThe Region this warehouse sits in. Nullable.
pincode_idSelect (searchable)NoThe Pincode this warehouse sits in. Nullable.
warehouse_descriptionTextareaNoFull width.

Address (collapsible, starts collapsed)

FieldTypeRequiredNotes
street, street2, street3, street4TextNoFree-text address lines, all nullable.
geolocationTextNoNullable — coordinates or map link, free-text.

Operations (collapsible, starts collapsed)

FieldTypeRequiredNotes
is_defaultToggleNoNo preset default — marks this as the system's fallback warehouse.
is_moving_warehouseToggleNoNo preset default — flags a mobile/non-fixed warehouse (e.g. a vehicle).
is_returned_by_customerToggleNoNo preset default — marks this warehouse as where customer returns are booked in.
allow_autoreservationToggleNoDefaults on. See callout below.
stock_transfer_permittedToggleNoDefaults on. See callout below.
stock_max_time_to_deliverNumberNoNullable — typical days-to-deliver from this warehouse.
transit_time_from_hoNumberNoNullable — typical transit days from Head Office to this warehouse.
What allow_autoreservation actually does

This toggle is the gate the console gives you over the daily automatic reservation scheduler. When it is switched on (the default), the scheduler is allowed to consider this warehouse's stock when it automatically reserves stock against ready sale orders. Switch it off and this warehouse's stock is skipped entirely during that automated run — useful for a warehouse that is, say, mid-stocktake or otherwise not ready to promise stock. It has no effect on manual reservation, only the scheduled/automatic pass. See Schedulers for exactly when and how often that job runs, and how it interacts with the matching per-carrier allow_autoreservation flag described below.

What stock_transfer_permitted does

Governs whether this warehouse may take part in stock transfers to or from other warehouses at all. Switching it off is a way to freeze a warehouse's stock in place — for example while it is being physically audited — without touching individual stock levels. (Note: the stock-transfer workflow itself has no dedicated console screen yet — see the callout under Stock Locations below.)

Screenshot placeholderThe Warehouse create/edit form with Warehouse Details expanded and Address, Operations shown collapsed, including the allow_autoreservation and stock_transfer_permitted toggles.

Actions on the list/edit page are the standard Edit and Delete — plain CRUD, no service logic and no delete guard (unlike Regions, deleting a warehouse that still has stock locations or stock is not blocked at this level).

Stock Locations tab

Open a Warehouse and switch to Stock Locations to manage the individual bins/areas inside it that stock actually sits in.

ColumnShows
location_nameName of the stock location.
stock_location_numberShort reference number/code.
location_typeType badge — see the 11 types below.
whether_in_useLabel "In Use" — tick/cross icon.
allow_in_wave_schedulerLabel "Wave" — tick/cross icon. See callout below.
  1. Open a Warehouse's edit page and select the Stock Locations tab

    You'll see every location currently defined under this warehouse.

  2. Select New stock location (header) to add one

    Fill in location_name (required, max 255), stock_location_number (optional, max 50), and choose a location_type (required).

  3. Choose the correct Location Type

    See the table below — pick a "physical" type for a real shelf/bin that will hold countable stock, or one of the logical types for accounting-only movement points.

  4. Set In Use, Wave and Auto-gen closing stock record as needed

    All three are toggles on the same form.

  5. Save, or use row Edit/Delete later

    This is plain CRUD — no service calls, no guard on delete.

The 11 LocationType options, in the order they appear in the Select:

Location TypePhysical?Typical use
InternalPhysicalA normal internal storage location — the everyday case for a real shelf or bin.
SupplierLogicalRepresents stock still with, or coming from, a supplier — not a physical shelf at AIR.
CustomerLogicalRepresents stock that has left AIR's premises into a customer's hands.
ProductionLogicalAccounting point for stock being converted/assembled (e.g. into a Set).
TransitLogicalStock in transit between two other locations, not sitting anywhere yet.
ViewLogicalA grouping/organisational node with no stock of its own — never holds items directly.
MainStockPhysicalThe primary saleable-stock area of a warehouse.
AttachedStockPhysicalA secondary physical area attached to the main stock area.
LooseStockPhysicalLoose/unbundled physical stock, not yet shelved formally.
ReturnStockPhysicalWhere physical customer returns are received and held pending disposition.
LossLogicalAccounting point for written-off/lost stock — not a real shelf.

"Physical" here means the location represents a real, countable shelf/bin that closing-stock counts and stock-on-hand figures meaningfully apply to; the "Logical" types are accounting/organisational points that stock passes through without being physically shelved there.

What allow_in_wave_scheduler does

Marks whether this specific stock location is eligible for the automated picking-wave scheduler to pull stock from when it builds waves. Turn it off for a location that should only ever be picked manually — for example a returns bay. See Schedulers for the wave-building job itself, and Picking Waves for how a wave is worked once built.

What autogen_closing_stock_record does

When on, this location is automatically included whenever new closing-stock statements are generated for its warehouse, rather than needing to be added by hand. See Closing Stock for the counting workflow itself, and Stock on Hand for the live balances a location holds day to day.

Stock transfers between warehouses — Planned

Seeded permissions (stock_transfer.request, stock_transfer.view, create, approve, receive) and a full six-state transfer workflow exist behind the scenes, but no Transfer screen exists anywhere in this console yet. The stock_transfer_permitted toggle on the Warehouse form above only controls eligibility for that future workflow — there is nothing to action from the console today.

Screenshot placeholderA Warehouse's Stock Locations tab — Location Name, Number, Type badge, In Use and Wave icon columns, with the New stock location form open showing the Location Type select.

Delivery Carriers tab

Open a Warehouse and switch to Delivery Carriers to see and manage which carriers this warehouse can despatch through. This is a many-to-many link — there is no create/edit here, only attach/detach.

ColumnShows
nameCarrier name.
codeCode badge.
carrier_typeType badge — Courier / Post / Hand Delivery / Transport.
is_activeTick/cross icon.
  1. Select Add carrier (header)

    Opens a dialog titled "Add a delivery carrier" with a pre-loaded searchable Carrier select listing every carrier already defined in the Delivery Carrier master list below.

  2. Choose the carrier and confirm

    The link is created immediately — no separate save step.

  3. To remove a link, select row Remove

    Or select several rows and use the bulk Remove selected action. Removing never deletes the carrier itself, only the link to this warehouse.

Regions tab

Open a Warehouse and switch to Regions to record which Regions this warehouse serves. Also a many-to-many, attach/detach only.

ColumnShows
nameRegion name.
region_typeType badge.

Same pattern as Delivery Carriers: header Assign region (a "Assign a coverage region" dialog with a pre-loaded searchable Region select over every Region), row Remove, bulk Remove selected. This is a separate link from the single region_id field on the Warehouse's own "Warehouse Details" section — that field says where the warehouse physically is; this tab says which regions it serves, and a warehouse can serve many regions beyond its own.

Screenshot placeholderA Warehouse's Delivery Carriers and Regions tabs, each showing their table and the "Add carrier" / "Assign region" button opening a pre-loaded, labelled select.

Hand-delivery beats are now defined separately, on their own Delivery Routes page — each route is owned by one warehouse and lists the ordered pincode stops the beat walks.

Delivery Carriers Partial

Delivery Carriers live under Warehouses → Delivery Carriers — the master list of every courier, postal or transport service AIR despatches through. Unlike the resources on the Geography page, this is a straightforward, flat, full-CRUD master list with no tree and no relation managers of its own (carriers are linked to warehouses and regions from the warehouse side, and to pincode serviceability from the pincode side — see Zones, Regions & Pincodes).

Delivery Carrier list

ColumnShows
nameCarrier name. Searchable, sortable.
codeCode badge, searchable.
carrier_typeType badge — Courier / Post / Hand Delivery / Transport.
is_activeLabel "Active" — tick/cross icon.
created_atSortable; hidden by default.

Filters: Carrier Type (Courier/Post/Hand Delivery/Transport) and Active Status (yes/no/either).

Screenshot placeholderThe Delivery Carriers list — Name, Code badge, Carrier Type badge and Active icon columns, with the Carrier Type filter open.

Create / edit a Delivery Carrier

Select New delivery carrier or row Edit. Single section, "Carrier Details":

FieldTypeRequiredNotes
nameTextYesMax 255, unique.
codeTextYesMax 50, unique. Locked (disabled) once the carrier exists.
carrier_typeSelectYesCourier / Post / Hand Delivery / Transport.
tracking_url_templateTextNoMax 500, full width — a template URL for building a tracking link from a tracking number.
is_activeToggleNoDefaults on.
Screenshot placeholderThe Delivery Carrier create/edit form — Name, Code, Carrier Type select, Tracking URL Template and the Active toggle.

Actions are the standard Edit and Delete — plain CRUD, no service logic, no bulk actions, no guard on delete.

Carriers also have an allow_autoreservation flag — not yet editable here

Every Delivery Carrier record carries its own allow_autoreservation column (defaulting to on), mirroring the Warehouse flag of the same name, and it feeds the same family of background jobs — the auto-wave and auto-reservation schedulers — but from the carrier side: when off, the scheduler skips building reservations or waves destined for that carrier, even if the warehouse holding the stock has its own flag switched on. The Delivery Carrier form in this console does not currently expose a toggle for it — the "Carrier Details" section stops at Name, Code, Carrier Type, Tracking URL Template and Active. Until a form field is added, changing a carrier's autoreservation eligibility requires going outside the console. See Schedulers for how the warehouse-side and carrier-side flags combine.

Delivery-date estimate Built

A read-only API endpoint that answers "if we ship from this warehouse today (or on a given date), when should the customer expect delivery?" — used by the SE field app and the HO console wherever an expected delivery date needs showing without duplicating the day-count logic client-side.

API

Method & pathPermissionPurpose
GET /api/v1/warehouses/{warehouse}/delivery-estimate warehouse.view Computes an expected delivery date for the given warehouse. Query params: location_id (optional, must be a Stock Location belonging to this warehouse) and ship_date (optional, any parseable date; defaults to today). Returns 404 for an unknown warehouse and 422 if location_id resolves to a location belonging to a different warehouse.

Response shape:

FieldMeaning
warehouse_idThe path warehouse's id.
location_idEchoes the resolved location_id, or null when none was supplied.
ship_dateThe ship date used for the calculation, YYYY-MM-DD (today's date, start of day, when omitted from the request).
transit_daysDays from Head Office to the warehouse — see the coalesce rule below.
max_delivery_daysTypical days-to-deliver once stock reaches the warehouse — same coalesce rule.
total_daystransit_days + max_delivery_days.
expected_delivery_dateship_date + total_days, YYYY-MM-DD.
Location overrides warehouse, then falls back to zero

Both transit_days and max_delivery_days are resolved location-first: if a location_id is given and that Stock Location's own transit_time_from_ho / stock_max_time_to_deliver is set, it wins. Otherwise the figure falls back to the same-named column on the Warehouse record above. If neither the location nor the warehouse has a value set, the figure coalesces to 0 — the estimate is never rejected for missing data, it just degrades to same-day.

User↔location assignment & warehouse context Built

Two related pieces of API-only plumbing (no console screen yet) that let a field operator's app learn "which warehouse(s) am I working in, and which specific stock location am I assigned to?" — used by the SE field app and any HO tooling that needs to default a stock location for the acting user.

Assigning a user to a stock location

A user_stock_locations row links one user to one Stock Location, with an is_primary flag.

Method & pathPermissionPurpose
POST /api/v1/user-stock-locations stock_location.edit Body: user_id, location_id, optional is_primary (default false). Idempotent — re-posting the same user_id/location_id pair updates the existing row (e.g. to flip is_primary) instead of creating a duplicate.
DELETE /api/v1/user-stock-locations/{userStockLocation} stock_location.edit Removes one assignment by its own id. Returns 204.
GET /api/v1/users/{user}/stock-locations stock_location.view Every location assigned to the given user, each with the linked location embedded (id, location_name, warehouse_id).
GET /api/v1/locations/{location}/assigned-users stock_location.view Every user assigned to the given location, each with the linked user embedded.
A user has at most one primary location

Assigning a location with is_primary: true automatically demotes any other location already marked primary for that same user — a user can be assigned to several locations at once, but never has more than one primary location.

Reading your own warehouse context

Method & pathPermissionPurpose
GET /api/v1/me/warehouse-context None — any authenticated, password-changed user Resolves the calling user's own warehouse context. Like /me, there is no permission gate: a user is always allowed to read their own context.

Response shape:

FieldMeaning
warehousesArray of {id, name, code} — every Warehouse reachable via the user's effective geography scope (their scoped regions plus descendants, per Regions), derived through the Warehouse↔Region link on the Regions tab. Empty array if the user has no geography scope.
assigned_locationsArray of {id, location_name, warehouse_id} — every Stock Location directly assigned to the user via user_stock_locations above. Independent of the geography-derived warehouse list.
primary_location{id, location_name, warehouse_id}, or null if the user has no location marked primary.
needs_selectionBoolean. false only when warehouses contains exactly one entry (the app can auto-select it silently); true when it contains zero (the app must block until an admin assigns geography) or more than one (the app must show a warehouse picker).
Warehouses come from geography, locations come from direct assignment

warehouses is derived — it is never assigned directly to a user, it falls out of whichever regions the user is geography-scoped to (see Zones, Regions & Pincodes) intersected with each Warehouse's Regions tab links. assigned_locations and primary_location, by contrast, come only from the explicit user_stock_locations rows above — a user can have a location assigned inside a warehouse that geography scoping would not otherwise surface, and the two lists are not cross-validated against each other.

Related pages