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.
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
| Column | Shows |
|---|---|
name | Warehouse name. Searchable, sortable. |
code | Code badge, searchable. |
warehouse_type | Type badge — HO or Branch. |
warehouse_purpose | Purpose badge — Internal, Sale, Display or Not Used. |
is_default | Tick/cross icon — whether this is the system's default warehouse. |
stock_locations_count | Label "Locations" — how many stock locations exist under this warehouse. Sortable. |
created_at | Sortable; hidden by default. |
Filters: Warehouse Type, Warehouse Purpose (both drop-downs), and Default (yes/no/either).
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
| Field | Type | Required | Notes |
|---|---|---|---|
name | Text | Yes | Max 255. |
code | Text | Yes | Max 20, unique. Locked (disabled) once the warehouse exists. |
warehouse_type | Select | Yes | HO or Branch. |
warehouse_purpose | Select | No | Internal / Sale / Display / Not Used. |
default_carrier_id | Select (searchable) | No | Which carrier is pre-selected for despatches from this warehouse. Nullable. |
region_id | Select (searchable) | No | The Region this warehouse sits in. Nullable. |
pincode_id | Select (searchable) | No | The Pincode this warehouse sits in. Nullable. |
warehouse_description | Textarea | No | Full width. |
Address (collapsible, starts collapsed)
| Field | Type | Required | Notes |
|---|---|---|---|
street, street2, street3, street4 | Text | No | Free-text address lines, all nullable. |
geolocation | Text | No | Nullable — coordinates or map link, free-text. |
Operations (collapsible, starts collapsed)
| Field | Type | Required | Notes |
|---|---|---|---|
is_default | Toggle | No | No preset default — marks this as the system's fallback warehouse. |
is_moving_warehouse | Toggle | No | No preset default — flags a mobile/non-fixed warehouse (e.g. a vehicle). |
is_returned_by_customer | Toggle | No | No preset default — marks this warehouse as where customer returns are booked in. |
allow_autoreservation | Toggle | No | Defaults on. See callout below. |
stock_transfer_permitted | Toggle | No | Defaults on. See callout below. |
stock_max_time_to_deliver | Number | No | Nullable — typical days-to-deliver from this warehouse. |
transit_time_from_ho | Number | No | Nullable — typical transit days from Head Office to this warehouse. |
allow_autoreservation actually doesThis 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.
stock_transfer_permitted doesGoverns 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. (The transfers themselves are raised and actioned on Stock Transfers.) Be aware: nothing in the transfer workflow currently reads this toggle, so switching it off does not actually block a transfer today — treat it as a record of intent rather than an enforced control.
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.
| Column | Shows |
|---|---|
location_name | Name of the stock location. |
stock_location_number | Short reference number/code. |
location_type | Type badge — see the 11 types below. |
whether_in_use | Label "In Use" — tick/cross icon. |
allow_in_wave_scheduler | Label "Wave" — tick/cross icon. See callout below. |
- Open a Warehouse's edit page and select the Stock Locations tab
You'll see every location currently defined under this warehouse.
- Select New stock location (header) to add one
Fill in
location_name(required, max 255),stock_location_number(optional, max 50), and choose alocation_type(required). - 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.
- Set In Use, Wave and Auto-gen closing stock record as needed
All three are toggles on the same form.
- 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 Type | Physical? | Typical use |
|---|---|---|
| Internal | Physical | A normal internal storage location — the everyday case for a real shelf or bin. |
| Supplier | Logical | Represents stock still with, or coming from, a supplier — not a physical shelf at AIR. |
| Customer | Logical | Represents stock that has left AIR's premises into a customer's hands. |
| Production | Logical | Accounting point for stock being converted/assembled (e.g. into a Set). |
| Transit | Logical | Stock in transit between two other locations, not sitting anywhere yet. |
| View | Logical | A grouping/organisational node with no stock of its own — never holds items directly. |
| MainStock | Physical | The primary saleable-stock area of a warehouse. |
| AttachedStock | Physical | A secondary physical area attached to the main stock area. |
| LooseStock | Physical | Loose/unbundled physical stock, not yet shelved formally. |
| ReturnStock | Physical | Where physical customer returns are received and held pending disposition. |
| Loss | Logical | Accounting 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.
Alongside the locations you create inside a warehouse, the system seeds one global location for each of the six logical types — Suppliers, Production, Customers, In-Transit, Inventory Loss and View. They belong to no warehouse, so they do not appear on this tab and cannot be created or edited from it. Every stock movement into or out of AIR's physical estate has one of them as its counterparty — a goods receipt comes from Suppliers, a despatch goes to Customers, a write-off goes to Inventory Loss. There is one Customers node shared by every delivery; adding a customer never creates a location.
You may still see per-warehouse Supplier, Production or Loss locations listed on this tab. They are still honoured: Goods Receipts, returns and Closing Stock use a warehouse's own location when it has one, and fall back to the shared global endpoint when it does not. Keeping one is optional now rather than required.
The form deliberately has no address or delivery-time fields. A location's address is its Warehouse's address, and the delivery-time figures live on the Warehouse too. The address a parcel is actually sent to comes from the sale order's shipping address and is printed on the Package label — never read from a stock location. An offsite godown that needs its own address should be set up as its own Warehouse, not as a location.
allow_in_wave_scheduler doesMarks 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.
autogen_closing_stock_record doesWhen 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.
Moving stock from one warehouse to another is not done here. It has a full screen of its own under Inventory › Stock Transfers, with a six-state lifecycle and Process, Dispatch, Receive, Complete and Cancel actions — see Stock Transfers. It is gated by stock_transfer.view, stock_transfer.create, stock_transfer.approve and stock_transfer.receive. (A fifth seeded permission, stock_transfer.request, is not used by anything.)
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.
| Column | Shows |
|---|---|
name | Carrier name. |
code | Code badge. |
carrier_type | Type badge — Courier / Post / Hand Delivery / Transport. |
is_active | Tick/cross icon. |
- 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.
- Choose the carrier and confirm
The link is created immediately — no separate save step.
- 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.
| Column | Shows |
|---|---|
name | Region name. |
region_type | Type 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.
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
| Column | Shows |
|---|---|
name | Carrier name. Searchable, sortable. |
code | Code badge, searchable. |
carrier_type | Type badge — Courier / Post / Hand Delivery / Transport. |
supports_cod | Label "COD" — tick/cross icon showing whether this carrier can collect cash on delivery. Only Speed Post ships with this on. |
is_active | Label "Active" — tick/cross icon. |
created_at | Sortable; hidden by default. |
Filters: Carrier Type (Courier/Post/Hand Delivery/Transport) and Active Status (yes/no/either).
Create / edit a Delivery Carrier
Select New delivery carrier or row Edit. Single section, "Carrier Details":
| Field | Type | Required | Notes |
|---|---|---|---|
name | Text | Yes | Max 255, unique. |
code | Text | Yes | Max 50, unique. Locked (disabled) once the carrier exists. |
carrier_type | Select | Yes | Courier / Post / Hand Delivery / Transport. |
tracking_url_template | Text | No | Max 500, full width — a template URL for building a tracking link from a tracking number. |
supports_cod | Toggle | No | Defaults off. Whether this carrier can collect cash on delivery — in practice India Post only. Recorded for reference; nothing currently blocks a COD package on a carrier without it. |
is_active | Toggle | No | Defaults on. |
Actions are the standard Edit and Delete — plain CRUD, no service logic, no bulk actions, no guard on delete.
allow_autoreservation flag — not yet editable hereEvery 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 & path | Permission | Purpose |
|---|---|---|
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 — it is validated and echoed back, but does not affect the figures) 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:
| Field | Meaning |
|---|---|
warehouse_id | The path warehouse's id. |
location_id | Echoes the resolved location_id, or null when none was supplied. |
ship_date | The ship date used for the calculation, YYYY-MM-DD (today's date, start of day, when omitted from the request). |
transit_days | The warehouse's transit_time_from_ho — days from Head Office to the warehouse. See the coalesce rule below. |
max_delivery_days | The warehouse's stock_max_time_to_deliver — typical days-to-deliver once stock reaches the warehouse. Same coalesce rule. |
total_days | transit_days + max_delivery_days. |
expected_delivery_date | ship_date + total_days, YYYY-MM-DD. |
Both transit_days and max_delivery_days come from the transit_time_from_ho / stock_max_time_to_deliver columns on the Warehouse record above, and from nowhere else. Stock Locations no longer carry their own copies of these — a shelf has no independent transit time — so passing location_id never changes the numbers. If the warehouse has no value set, the figure coalesces to 0 — the estimate is never rejected for missing data, it just degrades to same-day. The count is plain calendar days; there is no working-day or holiday calendar.
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 & path | Permission | Purpose |
|---|---|---|
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. |
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 & path | Permission | Purpose |
|---|---|---|
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:
| Field | Meaning |
|---|---|
warehouses | Array 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_locations | Array 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_selection | Boolean. 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 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
- Zones, Regions & Pincodes — the Region/Pincode records a Warehouse links to, and where carrier-pincode serviceability is recorded.
- Schedulers — the daily automated jobs that read
allow_autoreservation(on both warehouses and carriers) andallow_in_wave_scheduler(on stock locations). - Stock on Hand — live quantities per stock location.
- Closing Stock — the physical counting workflow that
autogen_closing_stock_recordfeeds into. - Picking Waves — what happens once the wave scheduler has pulled eligible stock locations into a wave.