diff --git a/sale_mrp_modular_type/__init__.py b/sale_mrp_modular_type/__init__.py
new file mode 100644
index 00000000000..9b4296142f4
--- /dev/null
+++ b/sale_mrp_modular_type/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import wizard
diff --git a/sale_mrp_modular_type/__manifest__.py b/sale_mrp_modular_type/__manifest__.py
new file mode 100644
index 00000000000..d25d9a2c63f
--- /dev/null
+++ b/sale_mrp_modular_type/__manifest__.py
@@ -0,0 +1,15 @@
+{
+ 'name': 'Modular Type',
+ 'description': """This module adds modular type to Manufacturing orders""",
+ 'author': 'aykhu',
+ 'license': 'LGPL-3',
+ 'depends': ['sale_management', 'sale_mrp'],
+ 'data': [
+ 'security/ir.model.access.csv',
+ 'wizard/modular_type_wizard_views.xml',
+ 'views/modular_type_views.xml',
+ 'views/product_template_views.xml',
+ 'views/mrp_bom_views.xml',
+ 'views/sale_order_views.xml',
+ ],
+}
diff --git a/sale_mrp_modular_type/models/__init__.py b/sale_mrp_modular_type/models/__init__.py
new file mode 100644
index 00000000000..75e58d3b4b8
--- /dev/null
+++ b/sale_mrp_modular_type/models/__init__.py
@@ -0,0 +1,6 @@
+from . import modular_type
+from . import product_template
+from . import mrp_bom
+from . import stock_move
+from . import sale_order_line
+from . import sale_order_line_modular_value
diff --git a/sale_mrp_modular_type/models/modular_type.py b/sale_mrp_modular_type/models/modular_type.py
new file mode 100644
index 00000000000..942076017c5
--- /dev/null
+++ b/sale_mrp_modular_type/models/modular_type.py
@@ -0,0 +1,8 @@
+from odoo import fields, models
+
+
+class ModularType(models.Model):
+ _name = 'modular.type'
+ _description = 'Modular Types'
+
+ name = fields.Char()
diff --git a/sale_mrp_modular_type/models/mrp_bom.py b/sale_mrp_modular_type/models/mrp_bom.py
new file mode 100644
index 00000000000..24cd2c83256
--- /dev/null
+++ b/sale_mrp_modular_type/models/mrp_bom.py
@@ -0,0 +1,17 @@
+from odoo import api, fields, models
+
+
+class MrpBomLine(models.Model):
+ _inherit = 'mrp.bom.line'
+
+ modular_type_id = fields.Many2one(
+ 'modular.type', domain="[('id', 'in', available_modular_type_ids)]"
+ )
+ available_modular_type_ids = fields.Many2many(
+ 'modular.type', compute='_compute_available_modular_type_ids'
+ )
+
+ @api.depends('product_id')
+ def _compute_available_modular_type_ids(self):
+ for line in self:
+ line.available_modular_type_ids = line.parent_product_tmpl_id.modular_type_ids
diff --git a/sale_mrp_modular_type/models/product_template.py b/sale_mrp_modular_type/models/product_template.py
new file mode 100644
index 00000000000..761f25ec14c
--- /dev/null
+++ b/sale_mrp_modular_type/models/product_template.py
@@ -0,0 +1,7 @@
+from odoo import fields, models
+
+
+class ProductTemplate(models.Model):
+ _inherit = 'product.template'
+
+ modular_type_ids = fields.Many2many('modular.type')
diff --git a/sale_mrp_modular_type/models/sale_order_line.py b/sale_mrp_modular_type/models/sale_order_line.py
new file mode 100644
index 00000000000..58ba57cdc26
--- /dev/null
+++ b/sale_mrp_modular_type/models/sale_order_line.py
@@ -0,0 +1,33 @@
+from odoo import api, fields, models
+
+
+class SaleOrderLine(models.Model):
+ _inherit = 'sale.order.line'
+
+ has_modular_type = fields.Boolean(
+ compute='_compute_has_modular_type', store=True
+ )
+ modular_value_ids = fields.One2many(
+ 'sale.order.line.modular.value', 'order_line_id'
+ )
+
+ @api.depends('product_template_id', 'product_template_id.modular_type_ids')
+ def _compute_has_modular_type(self):
+ for line in self:
+ line.has_modular_type = bool(
+ line.product_template_id.modular_type_ids
+ )
+
+ def _set_default_modular_values(self):
+ vals_list = []
+ for line in self:
+ existing_types = line.modular_value_ids.mapped('modular_type_id')
+ missing_types = line.product_template_id.modular_type_ids - existing_types
+ for modular_type in missing_types:
+ vals_list.append({
+ 'order_line_id': line.id,
+ 'modular_type_id': modular_type.id,
+ 'value': 0.0,
+ })
+ if vals_list:
+ self.env['sale.order.line.modular.value'].create(vals_list)
diff --git a/sale_mrp_modular_type/models/sale_order_line_modular_value.py b/sale_mrp_modular_type/models/sale_order_line_modular_value.py
new file mode 100644
index 00000000000..9233253a2d2
--- /dev/null
+++ b/sale_mrp_modular_type/models/sale_order_line_modular_value.py
@@ -0,0 +1,12 @@
+from odoo import fields, models
+
+
+class SaleOrderLineModularValue(models.Model):
+ _name = 'sale.order.line.modular.value'
+ _description = 'Sale Order Line Modular Value'
+
+ order_line_id = fields.Many2one(
+ 'sale.order.line', required=True, ondelete='cascade'
+ )
+ modular_type_id = fields.Many2one('modular.type', required=True)
+ value = fields.Float(help="Quantity multiplier")
diff --git a/sale_mrp_modular_type/models/stock_move.py b/sale_mrp_modular_type/models/stock_move.py
new file mode 100644
index 00000000000..2b11d555e39
--- /dev/null
+++ b/sale_mrp_modular_type/models/stock_move.py
@@ -0,0 +1,37 @@
+from odoo import api, fields, models
+
+
+class StockMove(models.Model):
+ _inherit = 'stock.move'
+
+ modular_type_id = fields.Many2one(
+ 'modular.type', compute="_compute_modular_type", store=True
+ )
+ sale_order_line_modular_value_id = fields.Many2one('sale.order.line.modular.value')
+ base_bom_qty = fields.Float()
+
+ @api.depends('production_id.bom_id.bom_line_ids')
+ def _compute_modular_type(self):
+ for move in self:
+ mo = move.raw_material_production_id
+ move.modular_type_id = mo.bom_id.bom_line_ids.filtered(
+ lambda line: line.product_id == move.product_id).modular_type_id
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ moves = super().create(vals_list)
+ for move in moves:
+ mo = move.raw_material_production_id
+ so_line = mo.sale_line_id
+ if so_line:
+ so_line._set_default_modular_values()
+ bom_line = mo.bom_id.bom_line_ids.filtered(
+ lambda line: line.product_id == move.product_id
+ )[:1]
+ move.base_bom_qty = bom_line.product_qty * mo.product_qty
+ modular_value = so_line.modular_value_ids.filtered(
+ lambda mv: mv.modular_type_id == bom_line.modular_type_id
+ )
+ if bom_line.modular_type_id and modular_value:
+ move.product_uom_qty = move.base_bom_qty * modular_value.value
+ return moves
diff --git a/sale_mrp_modular_type/security/ir.model.access.csv b/sale_mrp_modular_type/security/ir.model.access.csv
new file mode 100644
index 00000000000..ae1199ec1b3
--- /dev/null
+++ b/sale_mrp_modular_type/security/ir.model.access.csv
@@ -0,0 +1,5 @@
+id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
+access_modular_types,access_modular_types,model_modular_type,base.group_user,1,1,1,1
+access_modular_type_wizard,access_modular_type_wizard,model_modular_type_wizard,base.group_user,1,1,1,1
+access_modular_type_wizard_line,access_modular_type_wizard_line,model_modular_type_wizard_line,base.group_user,1,1,1,1
+access_sale_order_line_modular_value,access_sale_order_line_modular_value,model_sale_order_line_modular_value,base.group_user,1,1,1,1
diff --git a/sale_mrp_modular_type/views/modular_type_views.xml b/sale_mrp_modular_type/views/modular_type_views.xml
new file mode 100644
index 00000000000..839cf439527
--- /dev/null
+++ b/sale_mrp_modular_type/views/modular_type_views.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ modular.type.view.form
+ modular.type
+
+
+
+
+
+
+ Modular Type
+ modular.type
+ list,form
+
+
+
+
+
diff --git a/sale_mrp_modular_type/views/mrp_bom_views.xml b/sale_mrp_modular_type/views/mrp_bom_views.xml
new file mode 100644
index 00000000000..cff7d45edf1
--- /dev/null
+++ b/sale_mrp_modular_type/views/mrp_bom_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ mrp.bom.form.modular.type
+ mrp.bom
+
+
+
+
+
+
+
+
+
diff --git a/sale_mrp_modular_type/views/product_template_views.xml b/sale_mrp_modular_type/views/product_template_views.xml
new file mode 100644
index 00000000000..96b90043c3b
--- /dev/null
+++ b/sale_mrp_modular_type/views/product_template_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ product.template.form.modular.types
+ product.template
+
+
+
+
+
+
+
+
+
diff --git a/sale_mrp_modular_type/views/sale_order_views.xml b/sale_mrp_modular_type/views/sale_order_views.xml
new file mode 100644
index 00000000000..597f30ba5d4
--- /dev/null
+++ b/sale_mrp_modular_type/views/sale_order_views.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ Modular Type Value
+ modular.type.wizard
+ form
+ new
+
+
+
+ sale.order.form.modular.types
+ sale.order
+
+
+
+
+
+
+
+
+
diff --git a/sale_mrp_modular_type/wizard/__init__.py b/sale_mrp_modular_type/wizard/__init__.py
new file mode 100644
index 00000000000..66e64aedc69
--- /dev/null
+++ b/sale_mrp_modular_type/wizard/__init__.py
@@ -0,0 +1 @@
+from . import modular_type_wizard
diff --git a/sale_mrp_modular_type/wizard/modular_type_wizard.py b/sale_mrp_modular_type/wizard/modular_type_wizard.py
new file mode 100644
index 00000000000..b71e2e9b6a4
--- /dev/null
+++ b/sale_mrp_modular_type/wizard/modular_type_wizard.py
@@ -0,0 +1,51 @@
+from odoo import api, fields, models
+
+
+class ModularTypeWizard(models.TransientModel):
+ _name = 'modular.type.wizard'
+ _description = 'Modular Type Wizard'
+
+ @api.model
+ def default_get(self, fields_list):
+ res = super().default_get(fields_list)
+ order_line = self.env['sale.order.line'].browse(
+ self.env.context.get('active_order_line_id')
+ )
+ product = order_line.product_template_id
+ res.update({
+ 'product_id': product.id,
+ 'wizard_line_ids': [
+ (0, 0, {'modular_type_id': mt.id, 'value': 0})
+ for mt in product.modular_type_ids
+ ]
+ })
+ return res
+
+ product_id = fields.Many2one('product.template', readonly=True)
+ wizard_line_ids = fields.One2many('modular.type.wizard.line', 'wizard_id')
+
+ def add_modular_value(self):
+ active_order_line_id = self.env.context.get('active_order_line_id')
+ for line in self.wizard_line_ids:
+ existing = self.env['sale.order.line.modular.value'].search([
+ ('order_line_id', '=', active_order_line_id),
+ ('modular_type_id', '=', line.modular_type_id.id),
+ ], limit=1)
+ if existing:
+ existing.value = line.value
+ else:
+ self.env['sale.order.line.modular.value'].create({
+ 'order_line_id': active_order_line_id,
+ 'modular_type_id': line.modular_type_id.id,
+ 'value': line.value,
+ })
+ return
+
+
+class ModularTypeWizardLine(models.TransientModel):
+ _name = 'modular.type.wizard.line'
+ _description = 'Modular Type Wizard Line'
+
+ wizard_id = fields.Many2one('modular.type.wizard')
+ modular_type_id = fields.Many2one('modular.type')
+ value = fields.Float(default=0)
diff --git a/sale_mrp_modular_type/wizard/modular_type_wizard_views.xml b/sale_mrp_modular_type/wizard/modular_type_wizard_views.xml
new file mode 100644
index 00000000000..9db3f6c5b5f
--- /dev/null
+++ b/sale_mrp_modular_type/wizard/modular_type_wizard_views.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ modular.type.wizard.view.form
+ modular.type.wizard
+
+
+
+
+
+
\ No newline at end of file