Odoo MTO with Stock Check – Customize MTO to Consider Available Inventory
Here’s your improved and properly formatted version:
Odoo MTO with Stock Check – Customize MTO to Consider Available Inventory
Overview
By default, the Make to Order (MTO) route in Odoo does not consider available stock — it creates a manufacturing order (MO) or purchase order for the full sales order quantity.
If you want to consume existing inventory first and manufacture only the shortfall, you’ll need to customize the procurement rule logic or use a custom route.
Example Scenario
You have 100 pcs of finished goods (tables) in stock.
You create a Sales Order for 500 pcs.
The product is configured with the MTO + Manufacture route.
🔹 Expected Result:
MO should be created only for 400 pcs, because 100 pcs are already available.
Goal
- Stock available: 100 pcs
- Sales Order quantity: 500 pcs
- MO generated: 400 pcs
Implementation Steps
Step 1: Create the Module Skeleton
./odoo-bin scaffold mto_stock_check addons
Directory structure:
mto_stock_check/
├── __init__.py
├── __manifest__.py
└── models/
├── __init__.py
└── stock_rule.py
Step 2: Custom Logic in stock_rule.py
from odoo import models, api, _
from odoo.exceptions import UserError
import logging
_logger = logging.getLogger(__name__)
class StockRule(models.Model):
_inherit = 'stock.rule'
def is_root_level_product(self, product_id):
# Check if this product is used as a component in any BoM
component_in_bom = self.env['mrp.bom.line'].search_count([
('product_id', '=', product_id.id)
])
return component_in_bom == 0
@api.multi
def _run_manufacture(self, product_id, product_qty, product_uom, location_id, name, origin, values):
# check product_id is in bom root level or not
available_qty= 0
if self.is_root_level_product(product_id):
available_qty = product_id.qty_available
# Ensure product_id is a record
if not isinstance(product_id, models.BaseModel):
product_id = self.env['product.product'].browse(product_id)
# Calculate quantity to manufacture (ignore BOM component stock)
if available_qty:
adjusted_qty = max(0, product_qty - available_qty)
else:
adjusted_qty = product_qty
# If no manufacturing needed, return
if adjusted_qty <= 0:
return True
# Get BoM - use same logic as original
bom = self._get_matching_bom(product_id, values)
if not bom:
msg = _('No Bill of Material found for product: %s') % product_id.display_name
_logger.error(msg)
raise UserError(msg)
# Create manufacturing order for the adjusted quantity
Production = self.env['mrp.production'].sudo().with_context(
force_company=values['company_id'].id
)
mo_vals = self._prepare_mo_vals(
product_id,
adjusted_qty,
product_uom,
location_id,
name,
origin,
values,
bom
)
production = Production.create(mo_vals)
# Link to orderpoint/origin if exists
orderpoint = values.get('orderpoint_id')
if orderpoint:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': orderpoint},
subtype_id=self.env.ref('mail.mt_note').id
)
origin_production = values.get('move_dest_ids') and values['move_dest_ids'][0].raw_material_production_id
if origin_production:
production.message_post_with_view(
'mail.message_origin_link',
values={'self': production, 'origin': origin_production},
subtype_id=self.env.ref('mail.mt_note').id
)
return True
Step 3: __init__.py
in the models/
Folder
from . import stock_rule
Step 4: __manifest__.py
in the Root Module Folder
{
'name': "MTO with Stock Check",
'summary': "MTO route that considers available stock before triggering MO",
'author': "My Company",
'website': "https://genkiware.com",
'category': 'Manufacturing',
'version': '1.0',
'depends': ['mrp', 'stock'],
'data': [],
'installable': True,
}
Related Post
16° PrestaShop Meetup in Hong Kong: eCommerce in Metaverse
We successfully done an online meet up on December 30th,…
There are no reviews yet.