diff --git a/new_product_type/__init__.py b/new_product_type/__init__.py
new file mode 100644
index 00000000000..9b4296142f4
--- /dev/null
+++ b/new_product_type/__init__.py
@@ -0,0 +1,2 @@
+from . import models
+from . import wizard
diff --git a/new_product_type/__manifest__.py b/new_product_type/__manifest__.py
new file mode 100644
index 00000000000..5ae68a935bf
--- /dev/null
+++ b/new_product_type/__manifest__.py
@@ -0,0 +1,14 @@
+{
+ "name": "New Product Type",
+ "version": "1.0",
+ 'author': "abkus",
+ "depends": ["sale", "product"],
+ "data": [
+ "security/ir.model.access.csv",
+ "views/product_kit_wizard_views.xml",
+ "views/product_views.xml",
+ "views/sale_order_line_views.xml",
+ ],
+ "installable": True,
+ 'license': 'LGPL-3',
+}
diff --git a/new_product_type/models/__init__.py b/new_product_type/models/__init__.py
new file mode 100644
index 00000000000..83527162d00
--- /dev/null
+++ b/new_product_type/models/__init__.py
@@ -0,0 +1,2 @@
+from . import product_template
+from . import sale_order_line
diff --git a/new_product_type/models/product_template.py b/new_product_type/models/product_template.py
new file mode 100644
index 00000000000..aea78ba4aad
--- /dev/null
+++ b/new_product_type/models/product_template.py
@@ -0,0 +1,8 @@
+from odoo import fields, models
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ is_kit = fields.Boolean()
+ sub_product_ids = fields.Many2many('product.product')
diff --git a/new_product_type/models/sale_order_line.py b/new_product_type/models/sale_order_line.py
new file mode 100644
index 00000000000..19ff7754b83
--- /dev/null
+++ b/new_product_type/models/sale_order_line.py
@@ -0,0 +1,32 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import UserError
+
+
+class SaleOrderLine(models.Model):
+ _inherit = "sale.order.line"
+
+ is_kit = fields.Boolean(
+ related="product_id.product_tmpl_id.is_kit",
+ store=True
+ )
+ is_kit_product = fields.Boolean()
+ kit_parent_line_id = fields.Many2one('sale.order.line', ondelete='cascade')
+ extra_price = fields.Float(default=0.0)
+
+ @api.ondelete(at_uninstall=False)
+ def _check_kit_product_restriction(self):
+ for line in self:
+ if line.is_kit_product:
+ raise UserError(_("You can't direclty delete the sub product"))
+
+ def action_open_kit_wizard(self):
+ return {
+ "type": "ir.actions.act_window",
+ "name": "Configure Kit",
+ "res_model": "product.kit.wizard",
+ "view_mode": "form",
+ "target": "new",
+ "context": {
+ "active_id": self.id
+ },
+ }
diff --git a/new_product_type/security/ir.model.access.csv b/new_product_type/security/ir.model.access.csv
new file mode 100644
index 00000000000..7219eb233fb
--- /dev/null
+++ b/new_product_type/security/ir.model.access.csv
@@ -0,0 +1,3 @@
+id,name,model_id:id,group_id:id,perm_read,perm_create,perm_write,perm_unlink
+access_product_wizard_kit_line,access_product_wizard_kit_line,model_product_wizard_kit_line,base.group_user,1,1,1,1
+access_product_kit_wizard,access_product_kit_wizard,model_product_kit_wizard,base.group_user,1,1,1,1
diff --git a/new_product_type/views/product_kit_wizard_views.xml b/new_product_type/views/product_kit_wizard_views.xml
new file mode 100644
index 00000000000..4f3f826f16a
--- /dev/null
+++ b/new_product_type/views/product_kit_wizard_views.xml
@@ -0,0 +1,25 @@
+
+
+
+ product.kit.wizard.form
+ product.kit.wizard
+
+
+
+
+
diff --git a/new_product_type/views/product_views.xml b/new_product_type/views/product_views.xml
new file mode 100644
index 00000000000..c69ce243fb9
--- /dev/null
+++ b/new_product_type/views/product_views.xml
@@ -0,0 +1,17 @@
+
+
+
+ product.template.view_form
+ product.template
+
+
+
+
+
+
+
+
+
+
+
diff --git a/new_product_type/views/sale_order_line_views.xml b/new_product_type/views/sale_order_line_views.xml
new file mode 100644
index 00000000000..2a5288d5afe
--- /dev/null
+++ b/new_product_type/views/sale_order_line_views.xml
@@ -0,0 +1,20 @@
+
+
+
+ sale.order.line.form.view
+ sale.order
+
+
+
+ is_kit_product
+
+
+
+
+
+
+
diff --git a/new_product_type/wizard/__init__.py b/new_product_type/wizard/__init__.py
new file mode 100644
index 00000000000..dc907f3ddb5
--- /dev/null
+++ b/new_product_type/wizard/__init__.py
@@ -0,0 +1,2 @@
+from . import product_kit_wizard_line
+from . import product_kit_wizard
diff --git a/new_product_type/wizard/product_kit_wizard.py b/new_product_type/wizard/product_kit_wizard.py
new file mode 100644
index 00000000000..965d558bbe4
--- /dev/null
+++ b/new_product_type/wizard/product_kit_wizard.py
@@ -0,0 +1,59 @@
+from odoo import api, fields, models
+
+
+class ProductKitWizard(models.TransientModel):
+ _name = "product.kit.wizard"
+ _description = "Sub Product Kit Wizard"
+
+ main_product_id = fields.Many2one(
+ 'product.product',
+ string="Product",
+ )
+ sale_line_id = fields.Many2one('sale.order.line')
+ line_ids = fields.One2many(
+ 'product.wizard.kit.line',
+ 'wizard_id',
+ string="Sub Product"
+ )
+
+ @api.model
+ def _default_get(self, fields_list):
+ res = super().default_get(fields_list)
+
+ sale_line = self.env['sale.order.line'].browse(
+ self.env.context.get('active_id')
+ )
+ if not sale_line.product_id:
+ return res
+ order = sale_line.order_id
+ product = sale_line.product_id
+ lines = []
+
+ for sub_product in product.product_tmpl_id.sub_product:
+ existing_line = order.order_line.filtered(
+ lambda l:
+ l.product_id == sub_product
+ and l.kit_parent_line_id == sale_line
+ )[:1]
+
+ lines.append(
+ (0, 0, {
+ "product_id": sub_product.id,
+ "quantity": (
+ existing_line.product_uom_qty
+ if existing_line else 1.0
+ ),
+ "price": (
+ existing_line.extra_price
+ if existing_line else sub_product.lst_price
+ ),
+ })
+ )
+
+ res.update({
+ "sale_line_id": sale_line.id,
+ "main_product_id": product.id,
+ "line_ids": lines,
+ })
+
+ return res
diff --git a/new_product_type/wizard/product_kit_wizard_line.py b/new_product_type/wizard/product_kit_wizard_line.py
new file mode 100644
index 00000000000..21457edbd1d
--- /dev/null
+++ b/new_product_type/wizard/product_kit_wizard_line.py
@@ -0,0 +1,24 @@
+from odoo import api, fields, models
+
+
+class ProductKitWizardLine(models.TransientModel):
+ _name = "product.wizard.kit.line"
+ _description = "Kit Wizard Line"
+
+ wizard_id = fields.Many2one('product.kit.wizard')
+ product_id = fields.Many2one(
+ 'product.product',
+ readonly=True
+ )
+ quantity = fields.Float(default=1.0)
+ price = fields.Float()
+ price_subtotal = fields.Float(
+ string="Total Amount",
+ compute="_check_total_price",
+ store=True
+ )
+
+ @api.depends('quantity', 'price')
+ def _check_total_price(self):
+ for line in self:
+ line.price_subtotal = line.quantity * line.price