This article is currently available in English only. Translation coming soon.
By the end of this recipe, your Odoo 19 warehouse will move goods through a three-step process — pick from stock, pack for shipping, ship to the customer — with each step assigned to a different team and tracked separately. Skill required: Odoo administrator who has configured at least one product and warehouse. Time required: 2 hours setup plus 1 hour testing. ECOSIRE has rolled this out for e-commerce fulfillment centers, manufacturers, and 3PLs — and the recipe below is the playbook we ship to clients on day 1 of every warehouse implementation.
The reason multi-step routes confuse new users: Odoo separates the LOGICAL flow (pick > pack > ship) from the PHYSICAL flow (which warehouse zone does each step happen in). A single 3-step route involves at least 4 stock locations: stock, output for picking, packing zone, output for shipping. Get any of those locations wrong and stock counts drift between operations.
What you will need
- Odoo version: 17, 18, or 19. The route engine is unchanged across these versions.
- Modules:
stock(always installed),delivery(optional but recommended for shipping carrier integration). - User access: Inventory Manager + Settings administrator.
- Existing data: at least one warehouse, one storable product, and one customer with a delivery address.
- Time: 2 hours configuration, 1 hour synthetic testing.
Step-by-step
1. Enable multi-step warehouse settings
Go to Inventory > Configuration > Settings > Warehouse. Tick:
- Storage Locations — required for any internal location structure.
- Multi-Step Routes — unlocks the routes engine.
- Warehouses — already on by default.
Click Save. Verification: a new menu "Inventory > Configuration > Routes" appears.
2. Configure your warehouse for 3-step delivery
Go to Inventory > Configuration > Warehouses > [Your Warehouse]. In the "Outgoing Shipments" section, change the dropdown from "Deliver goods directly (1 step)" to "Pack goods, send goods in output and then deliver (3 steps)". Save.
Odoo automatically creates four locations under your warehouse:
WH/Stock(the main storage)WH/Output(post-pick staging)WH/Packing Zone(where packing happens)WH/Output(the loading dock — yes, this is reused as both post-pick and pre-ship in the default setup; rename one for clarity)
It also creates three operation types: Pick, Pack, Delivery.
Verification: Inventory > Configuration > Operations Types shows Pick, Pack, Delivery for your warehouse. Inventory > Configuration > Locations shows the four sub-locations under your warehouse.
3. Verify the default route was created
Go to Inventory > Configuration > Routes. You should see a route named "Your Warehouse: Pack > Output > Customer" (the exact name varies by version). Open it. The route has three rules:
- Pick rule: Stock > Packing Zone, operation type Pick.
- Pack rule: Packing Zone > Output, operation type Pack.
- Ship rule: Output > Customers, operation type Delivery.
Each rule has an "Action" of "Pull From" — meaning the next step pulls stock from the previous one when needed. Verification: each rule shows the correct source and destination location.
4. Test with a real sales order
Create a sales order for 5 units of any storable product. Confirm. Look at the Delivery linked to the SO. You should see THREE pickings now (not one):
- Pick in state "Ready" (because stock is on hand).
- Pack in state "Waiting" (waiting for the Pick to complete).
- Delivery in state "Waiting" (waiting for the Pack).
# Verify programmatically via odoo shell
order = self.env['sale.order'].search([('state', '=', 'sale')], limit=1)
pickings = order.picking_ids
print([(p.name, p.location_id.name, p.location_dest_id.name, p.state) for p in pickings])
# Expected output:
# [('WH/PICK/00001', 'Stock', 'Packing Zone', 'assigned'),
# ('WH/PACK/00001', 'Packing Zone', 'Output', 'waiting'),
# ('WH/OUT/00001', 'Output', 'Customers', 'waiting')]
Verification: process the Pick. Once validated, the Pack moves from Waiting to Ready. Process the Pack. The Delivery becomes Ready.
5. Add a custom intermediate location (optional)
For complex flows like quality inspection between Pick and Pack, add a custom location. Go to Inventory > Configuration > Locations > Create. Name it "Quality Hold", parent location "Your Warehouse", type "Internal". Save.
Now create a custom route. Go to Inventory > Configuration > Routes > Create. Name "Pick > QC > Pack > Ship". Tick "Sales Order Lines" so it can be selected on quotations. Add four rules:
Rule 1: Stock > Quality Hold, operation type Pick
Rule 2: Quality Hold > Packing Zone, operation type "QC Inspection" (create new operation type)
Rule 3: Packing Zone > Output, operation type Pack
Rule 4: Output > Customers, operation type Delivery
Each rule's "Action" is "Pull From" with priority based on the upstream rule chain. Verification: select this route on a quotation, confirm, and see four pickings created instead of three.
6. Configure push rules for receiving
Multi-step routes also work for incoming shipments. On the warehouse settings, set "Incoming Shipments" to "Receive goods in input, then quality, then stock (3 steps)". The route engine creates Receipt > QC > Putaway pickings on every purchase order receipt.
Verification: create a purchase order, confirm, and the linked Receipt now spawns three sequential pickings.
7. Set up putaway strategies
For warehouses with multiple stock zones, configure putaway rules so the Putaway step automatically routes products to the right shelf. Go to Inventory > Configuration > Putaway Rules > Create. Set product = a specific SKU (or category), source location = WH/Stock, destination location = WH/Stock/Aisle-A.
# Bulk-create putaway rules from a CSV
import csv
with open('/tmp/putaway.csv') as f:
for row in csv.DictReader(f):
product = self.env['product.product'].search([('default_code', '=', row['sku'])], limit=1)
if product:
self.env['stock.putaway.rule'].create({
'product_id': product.id,
'location_in_id': self.env.ref('stock.stock_location_stock').id,
'location_out_id': self.env['stock.location'].search([('name', '=', row['aisle'])]).id,
})
self.env.cr.commit()
Verification: a putaway rule fires automatically when a Receipt validates and the destination is your generic stock location.
8. Configure carrier integration on the Delivery step
Install the delivery_dhl (or FedEx, UPS, USPS) module. Go to the Delivery picking, set the Carrier field, and click "Send to Shipper". Odoo calls the carrier API, gets the tracking number, and saves the label PDF as an attachment. Verification: the picking now has a tracking number, a label attachment, and the customer email confirmation includes the tracking link.
Common mistakes
- Toggling between 1-step and 3-step on a warehouse with open pickings. Odoo will not migrate existing pickings — process them first or you get orphaned moves.
- Using the same operation type for Pick and Pack. The picking sequence collides and reports break. Each step must have its own operation type.
- Forgetting to tick "Sales Order Lines" on a custom route. The route exists but cannot be selected anywhere, so it never fires.
- Setting a custom route as "Warehouse" scope without specifying the warehouse. The route fires on every warehouse, including ones where the locations don't exist, causing red picking errors.
- Overlooking lot/serial tracking. If products are tracked by lot, the multi-step flow needs the same lot to flow through each step. Without it, Pack picks lot A but Delivery ships lot B.
Going further
Cross-docking: route products directly from the Receipt's Input location to the Output location, bypassing storage. Useful for direct-to-customer drop-shipments.
Wave picking: group multiple Pick pickings into a single wave so the warehouse worker walks the aisles once. Enable in Inventory > Operations > Wave Transfers.
Cluster picking: pick multiple sales orders simultaneously into a multi-bin cart, then sort at the Pack step. Configure in v18+ via the wave + multi-bin cart features.
Returns flow: configure a reverse 3-step route — Customer > Output > QC > Stock — for inbound returns. The QC step is critical: it determines whether returned goods go back to sellable stock or to a write-off location.
For complex 4PL or multi-warehouse setups, ECOSIRE's Odoo customization team builds bespoke route engines that handle ABC velocity zones, expiry-date FEFO, and per-customer SLA constraints. Pair this with how to track inventory turnover by warehouse for the analytics layer.
Frequently Asked Questions
Can I switch from 1-step to 3-step in production?
Yes, but only if all open pickings are completed first. The system creates new operation types and locations when you switch — existing pickings keep their old configuration but new ones use the new flow. We recommend doing this on a Friday after end-of-day processing.
How do I print pick lists in batch?
Select multiple Pick pickings in list view and use the "Print" button. Odoo merges them into a single PDF with one page per picking. For wave-style printing where lines are grouped by aisle, you need a custom report.
What is the performance impact of 3-step vs 1-step?
Each picking is a separate database row in stock.picking with its own stock.move and stock.move.line records. A 3-step flow generates roughly 3x the records of a 1-step flow. For warehouses doing under 1,000 orders per day this is invisible; above 5,000 per day, consider optimizing the operation type sequences and partitioning the move lines table.
Can different products use different routes?
Yes. On the product form, in the Inventory tab, set the "Routes" field to one or more routes. Odoo evaluates them in priority order. Use this for products that need a different flow (e.g., dropship-only products bypass your warehouse entirely).
For complex warehouse implementations, ECOSIRE's Odoo support ships full-package warehouse setups including barcode scanning, mobile picking apps, and carrier integration. Or read how to deploy Odoo on AWS EC2 for the infrastructure layer.
تحریر
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
ECOSIRE
Odoo ERP کے ساتھ اپنے کاروبار کو تبدیل کریں
آپ کے کاموں کو ہموار کرنے کے لیے ماہر Odoo کا نفاذ، حسب ضرورت، اور معاونت۔
متعلقہ مضامین
How to Add a Custom Button to an Odoo Form View (2026)
Add custom action buttons to Odoo 19 form views: Python action method, view inheritance, conditional visibility, confirmation dialogs. Production-tested.
How to Add a Custom Field in Odoo Without Studio (2026)
Add custom fields via custom module in Odoo 19: model inheritance, view extension, computed fields, store/non-store decisions. Code-first, version-controlled.
How to Add a Custom Report in Odoo Using External Layout
Build a branded PDF report in Odoo 19 using web.external_layout: QWeb template, paperformat, action binding. With print logo + footer overrides.