From e78a03f013d5e2f0354350a31c89006a3e99591d Mon Sep 17 00:00:00 2001 From: dhbr-odoo Date: Tue, 12 Aug 2025 22:04:00 +0530 Subject: [PATCH 1/5] [ADD] module_types: Implemented BOM components multiplication from sales This change allows for dynamic adjustment of Bill of Materials component quantities based on a value set on the sales order. It provides greater flexibility for make-to-order products where certain components must scale with a customer's selection (e.g., number of sections in a sofa). The implementation adds a multiplication value field to the sales order line. When a manufacturing order is generated, this factor is applied to BoM components linked to a specific module type, such as 'Sections', ensuring the correct quantity of raw materials is planned. Task-ID: 5003230 --- module_types/__init__.py | 2 + module_types/__manifest__.py | 20 +++++++++ module_types/models/__init__.py | 6 +++ module_types/models/module_types.py | 9 ++++ module_types/models/mrp_bom.py | 42 +++++++++++++++++++ module_types/models/product_template.py | 7 ++++ module_types/models/sales_order.py | 14 +++++++ module_types/models/sales_order_line.py | 14 +++++++ module_types/models/stock_move.py | 15 +++++++ module_types/security/ir.model.access.csv | 4 ++ module_types/views/mrp_bom_views.xml | 18 ++++++++ module_types/views/product_template_views.xml | 15 +++++++ module_types/views/sales_order_views.xml | 20 +++++++++ module_types/views/stock_move_views.xml | 13 ++++++ module_types/wizard/__init__.py | 1 + .../wizard/sale_module_types_wizard.py | 42 +++++++++++++++++++ .../wizard/sale_module_types_wizard.xml | 30 +++++++++++++ 17 files changed, 272 insertions(+) create mode 100644 module_types/__init__.py create mode 100644 module_types/__manifest__.py create mode 100644 module_types/models/__init__.py create mode 100644 module_types/models/module_types.py create mode 100644 module_types/models/mrp_bom.py create mode 100644 module_types/models/product_template.py create mode 100644 module_types/models/sales_order.py create mode 100644 module_types/models/sales_order_line.py create mode 100644 module_types/models/stock_move.py create mode 100644 module_types/security/ir.model.access.csv create mode 100644 module_types/views/mrp_bom_views.xml create mode 100644 module_types/views/product_template_views.xml create mode 100644 module_types/views/sales_order_views.xml create mode 100644 module_types/views/stock_move_views.xml create mode 100644 module_types/wizard/__init__.py create mode 100644 module_types/wizard/sale_module_types_wizard.py create mode 100644 module_types/wizard/sale_module_types_wizard.xml diff --git a/module_types/__init__.py b/module_types/__init__.py new file mode 100644 index 0000000000..9b4296142f --- /dev/null +++ b/module_types/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/module_types/__manifest__.py b/module_types/__manifest__.py new file mode 100644 index 0000000000..8b20e3ccba --- /dev/null +++ b/module_types/__manifest__.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +{ + 'name': 'Product Module type', + 'version': '1.0', + 'category': 'Sales/Manufacturing', + 'summary': 'Enable Modular types feature for Manufacturing orders', + 'depends': ['sale_mrp', 'sale_management'], + 'data': [ + 'security/ir.model.access.csv', + 'wizard/sale_module_types_wizard.xml', + 'views/product_template_views.xml', + 'views/mrp_bom_views.xml', + 'views/sales_order_views.xml', + 'views/stock_move_views.xml' + ], + 'installable': True, + 'license': 'LGPL-3' +} diff --git a/module_types/models/__init__.py b/module_types/models/__init__.py new file mode 100644 index 0000000000..59c891dbe7 --- /dev/null +++ b/module_types/models/__init__.py @@ -0,0 +1,6 @@ +from . import module_types +from . import product_template +from . import mrp_bom +from . import sales_order_line +from . import sales_order +from . import stock_move diff --git a/module_types/models/module_types.py b/module_types/models/module_types.py new file mode 100644 index 0000000000..dbfe760b2e --- /dev/null +++ b/module_types/models/module_types.py @@ -0,0 +1,9 @@ +from odoo import fields, models + + +class ModuleTypes(models.Model): + _name = "module.types" + _description = "Module Types" + + name = fields.Char("Module type name", required=True) + value = fields.Integer("Module type value", default=0) diff --git a/module_types/models/mrp_bom.py b/module_types/models/mrp_bom.py new file mode 100644 index 0000000000..bbd5fc3092 --- /dev/null +++ b/module_types/models/mrp_bom.py @@ -0,0 +1,42 @@ +from odoo import models, fields, api + + +class MrpBom(models.Model): + _inherit = 'mrp.bom' + + module_type_ids = fields.Many2many( + 'module.types', + string="Available Module Types", + related='product_tmpl_id.module_types', + readonly=True + ) + + @api.onchange('product_tmpl_id') + def _onchange_garden(self): + if self.bom_line_ids: + for line in self.bom_line_ids: + line.module_types_id = False + + +class MrpBomLine(models.Model): + _inherit = 'mrp.bom.line' + + module_types_id = fields.Many2one( + 'module.types', + string="Module Type", + domain="[('id', 'in', available_module_type_ids)] or []" + ) + + available_module_type_ids = fields.Many2many( + 'module.types', + compute='_compute_available_module_type_ids', + store=False + ) + + @api.depends('bom_id.product_tmpl_id') + def _compute_available_module_type_ids(self): + for line in self: + if line.bom_id.product_tmpl_id: + line.available_module_type_ids = line.bom_id.product_tmpl_id.module_types + else: + line.available_module_type_ids = False diff --git a/module_types/models/product_template.py b/module_types/models/product_template.py new file mode 100644 index 0000000000..68cda028fa --- /dev/null +++ b/module_types/models/product_template.py @@ -0,0 +1,7 @@ +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + module_types = fields.Many2many("module.types", string="Module types") diff --git a/module_types/models/sales_order.py b/module_types/models/sales_order.py new file mode 100644 index 0000000000..43f32409e4 --- /dev/null +++ b/module_types/models/sales_order.py @@ -0,0 +1,14 @@ +from odoo import models + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + def action_confirm(self): + res = super().action_confirm() + for production in self.mrp_production_ids: + selected_order_line = self.order_line.filtered(lambda line: line.product_template_id == production.product_tmpl_id and line.product_uom_qty == production.product_qty) + for stock_line in production.move_raw_ids.filtered(lambda component: component.module_type_id): + have_module_types = bool(selected_order_line.product_template_id.module_types) + stock_line.product_uom_qty *= stock_line.module_type_id.value if have_module_types else 0 + return res diff --git a/module_types/models/sales_order_line.py b/module_types/models/sales_order_line.py new file mode 100644 index 0000000000..c2db635e26 --- /dev/null +++ b/module_types/models/sales_order_line.py @@ -0,0 +1,14 @@ +from odoo import api, fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + have_module_types = fields.Integer( + compute="_compute_module_types_count" + ) + + @api.depends('product_template_id.module_types') + def _compute_module_types_count(self): + for line in self: + line.have_module_types = bool(line.product_id.module_types) diff --git a/module_types/models/stock_move.py b/module_types/models/stock_move.py new file mode 100644 index 0000000000..c7fb05b37c --- /dev/null +++ b/module_types/models/stock_move.py @@ -0,0 +1,15 @@ +from odoo import api, fields, models + + +class StockMove(models.Model): + _inherit = 'stock.move' + + module_type_id = fields.Many2one( + comodel_name='module.types', + compute="_compute_modular_type_id", + string='Modular Type') + + @api.depends('production_id.bom_id.bom_line_ids') + def _compute_modular_type_id(self): + for line in self: + line.module_type_id = line.raw_material_production_id.bom_id.bom_line_ids.filtered(lambda bom_line: bom_line.product_id == line.product_id).module_types_id diff --git a/module_types/security/ir.model.access.csv b/module_types/security/ir.model.access.csv new file mode 100644 index 0000000000..df97d8aa35 --- /dev/null +++ b/module_types/security/ir.model.access.csv @@ -0,0 +1,4 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +module_types.access_module_types,access_module_types,module_types.model_module_types,base.group_user,1,1,1,1 +module_types.access_sale_module_type_wizard,access_sale_module_type_wizard,module_types.model_sale_module_type_wizard,base.group_user,1,1,1,1 +module_types.access_sale_module_type_wizard_line,access_sale_module_type_wizard_line,module_types.model_sale_module_type_wizard_line,base.group_user,1,1,1,1 \ No newline at end of file diff --git a/module_types/views/mrp_bom_views.xml b/module_types/views/mrp_bom_views.xml new file mode 100644 index 0000000000..1d0def89a8 --- /dev/null +++ b/module_types/views/mrp_bom_views.xml @@ -0,0 +1,18 @@ + + + + + res.mrp.bom.form.inherit.module_types + mrp.bom + + + + + + + + + diff --git a/module_types/views/product_template_views.xml b/module_types/views/product_template_views.xml new file mode 100644 index 0000000000..a9e4741e0e --- /dev/null +++ b/module_types/views/product_template_views.xml @@ -0,0 +1,15 @@ + + + + + res.product.form.inherit.module_types + product.template + + + + + + + + + diff --git a/module_types/views/sales_order_views.xml b/module_types/views/sales_order_views.xml new file mode 100644 index 0000000000..3f34e88d08 --- /dev/null +++ b/module_types/views/sales_order_views.xml @@ -0,0 +1,20 @@ + + + + + sale.order.line.form.module.button + sale.order + + + +