Your Warehouse Is Either Sitting on Dead Stock or Losing Sales to Stockouts
Every manufacturing and distribution company faces the same fundamental tension: stock too much and you tie up capital in unsold inventory; stock too little and you lose orders to lead time. The difference between companies that manage this well and those that don't comes down to one decision — which products get produced or purchased to stock, and which get produced or purchased only when a customer orders them.
Odoo 19 gives you two core procurement strategies to solve this: Make-to-Stock (MTS) and Make-to-Order (MTO). MTS keeps inventory on hand, replenished automatically by reorder rules. MTO triggers procurement only when a confirmed sales order demands it — no forecast, no safety stock, no risk of obsolescence. Most businesses need both, applied at the product level.
This guide walks through both strategies in detail — how to configure routes, set up procurement rules, build hybrid MTS+MTO workflows, tune reorder points, and avoid the configuration mistakes that cause silent procurement failures. Every setting is shown with the exact Odoo 19 path and XML if you need to automate it.
Make-to-Stock in Odoo 19: How Automatic Replenishment Keeps Your Shelves Full
Make-to-Stock is the default procurement strategy in Odoo 19. When a sales order is confirmed, Odoo checks the available stock in the delivery warehouse. If enough quantity exists, it reserves it immediately and creates a delivery order. If not, the product sits in a waiting state until stock arrives — either from a purchase order, a manufacturing order, or an inter-warehouse transfer triggered by reorder rules.
The key characteristic of MTS: procurement is decoupled from demand. You don't buy or manufacture because a customer ordered something. You buy or manufacture because your stock dropped below a threshold. The customer order simply consumes what's already there.
When to Use MTS
| Criteria | MTS Is the Right Choice When... |
|---|---|
| Demand predictability | Products have stable, forecastable demand (e.g., consumables, standard parts) |
| Lead time tolerance | Customers expect same-day or next-day shipping |
| Shelf life | Products don't expire or become obsolete quickly |
| Unit economics | Holding cost is low relative to lost-sale cost |
| Batch economies | Manufacturing or purchasing in bulk significantly reduces per-unit cost |
Enabling MTS on a Product
MTS is the default behavior in Odoo 19. A product with no special routes configured uses MTS implicitly. However, you can make this explicit for clarity:
Inventory > Products > [Your Product] > Inventory tab
Routes: (leave empty for default MTS behavior)
OR check "Replenish on Order (MTO)" to override — do NOT check this for MTS
To enable automatic replenishment:
Inventory > Operations > Replenishment > Create Reorder Rule
Product: [Your Product]
Warehouse: [Your Warehouse]
Min Qty: 10 (trigger replenishment when stock falls to this)
Max Qty: 50 (order up to this quantity)
Route: Buy / Manufacture (depends on product type)<odoo>
<data>
<!-- Reorder rule: replenish Widget-A when stock drops below 10 -->
<record id="orderpoint_widget_a" model="stock.warehouse.orderpoint">
<field name="product_id" ref="product.product_widget_a"/>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="stock.stock_location_stock"/>
<field name="product_min_qty">10</field>
<field name="product_max_qty">50</field>
<field name="qty_multiple">5</field>
<field name="trigger">auto</field>
</record>
</data>
</odoo>Setting a product to MTS without creating a reorder rule means Odoo will never automatically replenish it. The product will sell until stock hits zero, then every new sales order will sit in "Waiting" status indefinitely. MTS only works as a strategy when paired with properly tuned reorder rules — otherwise you're running Make-to-Nothing.
Make-to-Order in Odoo 19: Procure Only What Customers Actually Order
Make-to-Order flips the procurement logic. Instead of maintaining stock and hoping demand matches your forecast, MTO triggers procurement directly from the sales order. When a customer orders 50 units, Odoo immediately creates either a purchase order (Buy route) or a manufacturing order (Manufacture route) for exactly 50 units. No more, no less.
In Odoo 19, MTO underwent a significant architectural change. The old procurement.group-based MTO was replaced with a stock reference system that links the sales order line directly to the procurement move. This means better traceability — you can trace any manufacturing order or PO back to the exact customer order that triggered it.
When to Use MTO
| Criteria | MTO Is the Right Choice When... |
|---|---|
| Demand predictability | Products have sporadic or unpredictable demand (custom items, seasonal goods) |
| Product value | High-value items where holding cost is significant (machinery, electronics) |
| Customization | Products are configured or customized per order (made-to-spec) |
| Obsolescence risk | Products depreciate quickly or have short lifecycles (tech, fashion) |
| Lead time acceptance | Customers accept longer lead times in exchange for customization or lower price |
Enabling MTO on a Product
First, make the MTO route visible. In Odoo 19, it is archived by default:
Step 1: Unarchive the MTO route
Inventory > Configuration > Routes
Remove the "Active" filter (or add "Archived" filter)
Find "Replenish on Order (MTO)" > Unarchive it
Step 2: Assign MTO to the product
Inventory > Products > [Your Product] > Inventory tab
Routes: Check "Replenish on Order (MTO)"
Also check "Buy" or "Manufacture" (the supply method)
Step 3: Confirm a sales order
The system will auto-create a PO or MO linked to that SO line# In a custom module or server action
mto_route = self.env.ref('stock.route_warehouse0_mto')
buy_route = self.env.ref('purchase_stock.route_warehouse0_buy')
product = self.env['product.template'].browse(product_id)
product.route_ids = [(6, 0, [mto_route.id, buy_route.id])]
# Verify the configuration
for route in product.route_ids:
print(f"Route: {{route.name}} (active={{route.active}})")MTO alone only defines when to procure (on demand). You must also specify how to procure. If the product is purchased from a vendor, add the Buy route alongside MTO. If it's manufactured in-house, add the Manufacture route. Without a supply route, confirming a sales order will create a stock move with no source — it will sit in "Waiting Another Operation" forever.
Odoo 19 Route and Procurement Rule Configuration for MTO and MTS
Routes are the backbone of Odoo's procurement system. A route is a collection of procurement rules (pull rules) that define how goods move between locations. Understanding the route architecture is essential for configuring MTO and MTS correctly.
How Procurement Rules Chain Together
When a sales order creates demand, Odoo walks the route's pull rules to find a supply source. Each rule defines a source location, a destination location, and an action (buy, manufacture, or pull from another location). Rules chain together: the output of one rule becomes the input of the next.
Sales Order confirmed
|
v
Check stock at WH/Stock
|
+--[Stock available]--> Reserve stock --> Create delivery order
|
+--[Stock NOT available]--> Delivery stays in "Waiting"
|
Reorder rule fires (scheduled or manual)
|
v
Create PO / MO to replenish WH/Stock
|
v
Goods received --> Stock updated --> Delivery auto-assignedSales Order confirmed
|
v
MTO route triggers immediately (no stock check)
|
+--[Buy route]--> Create Purchase Order for exact SO qty
| |
| v
| PO confirmed --> Goods received --> Delivery created
|
+--[Manufacture route]--> Create Manufacturing Order for exact SO qty
|
v
MO completed --> Goods in stock --> Delivery createdInspecting Route Rules in the Database
When debugging procurement issues, you often need to see exactly which rules are attached to a route and in what order they evaluate. This query shows you the full picture:
SELECT
r.name AS route_name,
r.active AS route_active,
rule.name AS rule_name,
rule.action AS rule_action,
src.complete_name AS source_location,
dest.complete_name AS dest_location,
rule.procure_method AS procure_method,
rule.sequence AS sequence
FROM stock_rule rule
JOIN stock_route r ON rule.route_id = r.id
LEFT JOIN stock_location src ON rule.location_src_id = src.id
LEFT JOIN stock_location dest ON rule.location_dest_id = dest.id
WHERE rule.warehouse_id = 1
ORDER BY r.name, rule.sequence;| Field | MTS Rule Value | MTO Rule Value |
|---|---|---|
procure_method | make_to_stock — take from stock, wait if unavailable | make_to_order — always trigger upstream procurement |
action | pull — pull goods from source location | pull — pull goods but force upstream creation |
trigger | Reorder rule (scheduler-driven) | Sales order confirmation (event-driven) |
| Stock reservation | Reserves existing inventory immediately | Creates new procurement, reserves after receipt |
Hybrid MTS + MTO: The Best Procurement Strategy for Products with Variable Demand
Most real-world businesses don't fit neatly into pure MTS or pure MTO. A furniture manufacturer might stock their 10 best-selling chairs (MTS) but make custom-colored versions only when ordered (MTO). A distributor might keep safety stock of fast-moving SKUs (MTS) while ordering slow-movers only on demand (MTO).
Odoo 19 supports a hybrid approach using the MTS+MTO route, available through the stock_mts_mto_rule module. This route first checks available stock (MTS behavior). If enough exists, it reserves it. If not, it procures only the shortfall via MTO.
Enabling the MTS+MTO Route
Step 1: Install the module
Apps > Search "MTS+MTO" > Install "MTS+MTO Procurement Strategy"
Technical name: stock_mts_mto_rule
Step 2: A new route appears automatically
Inventory > Configuration > Routes
You'll see: "Make To Order + Make To Stock"
Step 3: Assign to products
Inventory > Products > [Your Product] > Inventory tab
Routes: Check "Make To Order + Make To Stock"
Also check "Buy" or "Manufacture"
Step 4: Set a reorder rule (critical!)
The MTS portion needs a reorder rule to replenish base stock.
Without it, the MTS check always finds zero — making it pure MTO.How the Hybrid Split Works
Consider a product with 30 units in stock and a customer orders 50:
Customer orders 50 units
|
v
MTS+MTO route evaluates available stock: 30 units
|
+--[MTS portion: 30 units]--> Reserve from existing stock
| Create delivery for 30 units (ready)
|
+--[MTO portion: 20 units]--> Trigger procurement for shortfall
Create PO/MO for 20 units
Create delivery for 20 units (waiting)
|
v
Goods received --> Delivery assigned
Result: Customer gets 30 units immediately, 20 units after procurement
(or: both ship together once the MTO portion arrives)<odoo>
<data>
<!-- Assign MTS+MTO + Buy routes to a product -->
<record id="product_hybrid_example" model="product.template">
<field name="name">Industrial Bearing X200</field>
<field name="type">product</field>
<field name="route_ids"
eval="[(6, 0, [
ref('stock_mts_mto_rule.route_mto_mts'),
ref('purchase_stock.route_warehouse0_buy'),
])]"/>
</record>
<!-- Reorder rule for the MTS base stock -->
<record id="orderpoint_bearing_x200" model="stock.warehouse.orderpoint">
<field name="product_id" ref="product_hybrid_example"/>
<field name="warehouse_id" ref="stock.warehouse0"/>
<field name="location_id" ref="stock.stock_location_stock"/>
<field name="product_min_qty">20</field>
<field name="product_max_qty">100</field>
<field name="qty_multiple">10</field>
</record>
</data>
</odoo>The MTS+MTO split creates two delivery orders by default — one for the available stock and one for the procured shortfall. If your customer expects a single shipment, configure the delivery order to use backorder merging or set the product's Delivery Lead Time to account for procurement time. Otherwise, the warehouse team will ship partial orders and confuse customers who expected everything in one box.
Tuning Reorder Points: The Safety Stock Formula That Prevents Both Stockouts and Overstock
Reorder rules are the engine behind MTS. A badly tuned reorder point either leaves you with $200,000 in dead inventory or causes stockouts every week. Here's how to calculate the right values for your business:
# ── Core Formulas ──
Safety Stock = Z x sigma_d x sqrt(L)
Where:
Z = Service level factor (1.65 for 95%, 2.33 for 99%)
sigma_d = Standard deviation of daily demand
L = Lead time in days (vendor or manufacturing)
Reorder Point (Min Qty) = (Average Daily Demand x Lead Time) + Safety Stock
Max Qty = Reorder Point + Economic Order Quantity (EOQ)
EOQ = sqrt((2 x Annual Demand x Ordering Cost) / Holding Cost per Unit)
# ── Worked Example: Widget-A ──
Annual demand: 12,000 units (avg 33/day)
Demand std dev: 8 units/day
Lead time: 14 days (vendor)
Service level: 95% (Z = 1.65)
Ordering cost: $25 per PO
Holding cost: $2 per unit per year
Safety Stock = 1.65 x 8 x sqrt(14) = 1.65 x 8 x 3.74 = 49.4 ≈ 50 units
Reorder Point = (33 x 14) + 50 = 462 + 50 = 512 units
EOQ = sqrt((2 x 12000 x 25) / 2) = sqrt(300000) = 548 units
Max Qty = 512 + 548 = 1,060 units
Odoo reorder rule:
Min Qty: 512
Max Qty: 1060
Qty Multiple: 50 (vendor sells in cases of 50)Odoo 19 Reorder Rule Settings Explained
| Field | What It Controls | Common Mistake |
|---|---|---|
| Min Qty | When on-hand + incoming - outgoing drops below this, trigger replenishment | Setting to 0 — replenishment only fires after stockout |
| Max Qty | Order enough to bring forecasted stock up to this level | Setting equal to Min — creates tiny POs with high ordering cost |
| Qty Multiple | Round up order quantity to this multiple (vendor MOQ) | Ignoring vendor case quantities — leads to receiving mismatches |
| Trigger | auto = scheduler checks daily; manual = only via "Order Once" button | Using manual for products that need continuous replenishment |
| Visibility Days | Look ahead N days of forecasted demand when calculating order qty | Not using it — leads to ordering exactly to Max, stockout before next order |
# Server action to update reorder rules from product fields
for product in self.env['product.product'].search([
('type', '=', 'product'),
('x_reorder_min', '>', 0),
]):
orderpoint = self.env['stock.warehouse.orderpoint'].search([
('product_id', '=', product.id),
('warehouse_id', '=', warehouse_id),
], limit=1)
vals = {{
'product_min_qty': product.x_reorder_min,
'product_max_qty': product.x_reorder_max,
'qty_multiple': product.x_order_multiple or 1.0,
}}
if orderpoint:
orderpoint.write(vals)
else:
vals.update({{
'product_id': product.id,
'warehouse_id': warehouse_id,
'location_id': warehouse.lot_stock_id.id,
}})
self.env['stock.warehouse.orderpoint'].create(vals)Without Visibility Days, the scheduler only looks at current stock levels. If your Min Qty is 100 and you have 101 units, no order fires — even if 200 units are committed to outgoing deliveries next week. Setting Visibility Days to your lead time (e.g., 14 days) tells the scheduler to factor in forecasted demand for the next 14 days, triggering earlier replenishment that arrives before the stockout.
4 MTO/MTS Configuration Mistakes That Silently Break Your Supply Chain
MTO Route Is Archived by Default in Odoo 19
In Odoo 19, the MTO route ships in an archived state. If you go to a product's Inventory tab and don't see "Replenish on Order (MTO)" in the route selection, it's not missing — it's hidden. Teams often work around this by manually creating procurement rules, which creates a fragile parallel system that breaks on Odoo upgrades.
Go to Inventory > Configuration > Routes, remove the Active filter, find "Replenish on Order (MTO)", and unarchive it. Then assign it to products normally. If you need this in a data file for automated deployments: <function model="stock.route" name="write">[[ref('stock.route_warehouse0_mto')], {'active': True}]</function>
MTO Without a Supply Route Creates Ghost Moves
Assigning only the MTO route to a product (without Buy or Manufacture) means Odoo knows it should procure on demand but has no rule telling it how. The result: confirming a sales order creates a stock move from "Vendors" or "Production" to "WH/Stock" with no corresponding PO or MO. The delivery order shows "Waiting Another Operation" and nothing ever arrives. There's no error message — just a silent procurement void.
Always pair MTO with either Buy (for purchased products) or Manufacture (for produced products). Run this diagnostic query periodically to catch orphaned MTO products: SELECT pt.name FROM product_template pt JOIN stock_route_product rel ON rel.product_template_id = pt.id WHERE rel.route_id = (SELECT id FROM stock_route WHERE name ILIKE '%mto%') AND pt.id NOT IN (SELECT product_template_id FROM stock_route_product WHERE route_id IN (SELECT id FROM stock_route WHERE name ILIKE '%buy%' OR name ILIKE '%manufacture%'))
The Scheduler Doesn't Run in Real-Time
MTS replenishment depends on the procurement scheduler (Inventory > Operations > Run Scheduler). By default, this scheduled action runs once per day. If a sales order depletes stock at 9 AM and the scheduler runs at midnight, the replenishment PO won't be created until the next day — adding 24 hours of invisible lead time. For high-volume operations, this daily gap causes cascading delays across the supply chain.
Increase scheduler frequency for critical products. Go to Settings > Technical > Scheduled Actions > "Scheduler: Run procurement" and change the interval to every 2-4 hours. For truly time-sensitive products, use the "Order Once" button on the replenishment dashboard for immediate procurement, or configure reorder rules with trigger='auto' which checks on every stock move.
Mixing MTO and MTS on the Same Product Without the Hybrid Module
Some teams assign both the MTO route and a reorder rule to the same product, thinking they'll get hybrid behavior. They won't. MTO takes priority over reorder rules. Every sales order will trigger a new PO/MO regardless of available stock. The reorder rule keeps buying to maintain Min Qty, and MTO keeps buying per order — resulting in double procurement and ballooning inventory that nobody ordered.
If you need hybrid behavior, install the stock_mts_mto_rule module and use the dedicated MTS+MTO route. Never combine the standard MTO route with reorder rules on the same product. Audit your current products with this check: any product that has both an MTO route and an active reorder rule is likely double-procuring.
What the Right Procurement Strategy Saves Your Business
Choosing MTO vs MTS isn't a technical decision — it's a financial one. Here's what changes when you match each product to the right strategy:
Moving slow-movers from MTS to MTO eliminates safety stock for products that sell twice a year. No warehouse space, no insurance, no write-offs.
Properly tuned MTS reorder points with Visibility Days keep best-sellers in stock. Customers get same-day shipping instead of "2-3 week lead time."
MTO means you never produce or purchase a custom-spec product that a customer hasn't already committed to buying. No obsolescence risk.
The real ROI comes from the ABC analysis approach: classify your products by revenue contribution (A = top 20%, B = middle 30%, C = bottom 50%), then assign strategies accordingly. A-items get MTS with tight reorder points and high service levels. C-items get MTO with zero safety stock. B-items get the hybrid MTS+MTO approach. This segmentation alone typically reduces total inventory investment by 25-35% while improving fill rates.
Optimization Metadata
Configure MTO vs MTS procurement strategies in Odoo 19. Route setup, reorder rules, hybrid MTS+MTO, safety stock formulas, and common gotchas.
1. "Make-to-Stock in Odoo 19: How Automatic Replenishment Keeps Your Shelves Full"
2. "Make-to-Order in Odoo 19: Procure Only What Customers Actually Order"
3. "Hybrid MTS + MTO: The Best Procurement Strategy for Products with Variable Demand"
4. "4 MTO/MTS Configuration Mistakes That Silently Break Your Supply Chain"