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..04d43c56d0d
--- /dev/null
+++ b/new_product_type/__manifest__.py
@@ -0,0 +1,18 @@
+{
+ "name": "New Product Kit",
+ "version": "1.0",
+ 'author': "Ruchita Gothi (Rugot)",
+ "depends": ["sale", "product", 'account'],
+ "data": [
+ 'security/ir.model.access.csv',
+ "views/product_views.xml",
+ "views/sale_order_line_views.xml",
+ "views/kit_wizard_views.xml",
+ "report/sale_order_report.xml",
+ "report/invoice_report.xml",
+ "report/sale_order_portal_report.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..8f2f8c0cbc1
--- /dev/null
+++ b/new_product_type/models/__init__.py
@@ -0,0 +1,3 @@
+from . import product_template
+from . import sale_order_line
+from . import sale_order
diff --git a/new_product_type/models/product_template.py b/new_product_type/models/product_template.py
new file mode 100644
index 00000000000..17bed59a976
--- /dev/null
+++ b/new_product_type/models/product_template.py
@@ -0,0 +1,15 @@
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ is_kit = fields.Boolean()
+ sub_product = fields.Many2many("product.product")
+
+ @api.constrains("sub_product")
+ def _check_no_self_product_reference(self):
+ for record in self:
+ if record.product_variant_id in record.sub_product:
+ raise ValidationError("A product cannot be added as a sub-product in its own kit.")
diff --git a/new_product_type/models/sale_order.py b/new_product_type/models/sale_order.py
new file mode 100644
index 00000000000..3493e2c585c
--- /dev/null
+++ b/new_product_type/models/sale_order.py
@@ -0,0 +1,7 @@
+from odoo import fields, models
+
+
+class SaleOrder(models.Model):
+ _inherit = "sale.order"
+
+ print_in_report = fields.Boolean()
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..72248d88ae8
--- /dev/null
+++ b/new_product_type/models/sale_order_line.py
@@ -0,0 +1,38 @@
+from odoo import models, fields, _
+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")
+ extra_price = fields.Float(default=0.0)
+
+ def unlink(self):
+ parents_in_self = self.filtered(lambda l: not l.is_kit_product)
+ # parents = self.env["sale.order.line"].search([
+ # ("id", "in", self.ids),
+ # ("is_kit_product", "=", False),
+ # ])
+ children_in_self = self.filtered(lambda l: l.is_kit_product)
+ for child in children_in_self:
+ if child.kit_parent_line_id not in parents_in_self:
+ raise UserError(_("You cannot delete a kit sub product directly."))
+ child_lines = self.search([
+ ("kit_parent_line_id", "in", parents_in_self.ids)
+ ])
+ if child_lines:
+ child_lines.unlink()
+ return super().unlink()
+
+ 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/report/invoice_report.xml b/new_product_type/report/invoice_report.xml
new file mode 100644
index 00000000000..f8ffdd43f9e
--- /dev/null
+++ b/new_product_type/report/invoice_report.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ o._get_move_lines_to_report().filtered(lambda l: not (any(sl.is_kit_product for sl in l.sale_line_ids)
+ and not any(sl.order_id.print_in_report for sl in l.sale_line_ids)))
+
+
+
+
+
diff --git a/new_product_type/report/sale_order_portal_report.xml b/new_product_type/report/sale_order_portal_report.xml
new file mode 100644
index 00000000000..137ffa39620
--- /dev/null
+++ b/new_product_type/report/sale_order_portal_report.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ sale_order._get_order_lines_to_report().filtered(
+ lambda l: not l.is_kit_product or sale_order.print_in_report
+ )
+
+
+
+
+
diff --git a/new_product_type/report/sale_order_report.xml b/new_product_type/report/sale_order_report.xml
new file mode 100644
index 00000000000..30231808faa
--- /dev/null
+++ b/new_product_type/report/sale_order_report.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+ doc._get_order_lines_to_report().filtered(
+ lambda l: not l.is_kit_product or doc.print_in_report
+ )
+
+
+
+
+
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..31f4ca32d13
--- /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_write,perm_create,perm_unlink
+product_kit_wizard,product_kit_wizard,model_product_kit_wizard,base.group_user,1,1,1,1
+kit_wizard_line,kit_wizard_line,model_kit_wizard_line,base.group_user,1,1,1,1
\ No newline at end of file
diff --git a/new_product_type/views/kit_wizard_views.xml b/new_product_type/views/kit_wizard_views.xml
new file mode 100644
index 00000000000..49a49c07a0d
--- /dev/null
+++ b/new_product_type/views/kit_wizard_views.xml
@@ -0,0 +1,29 @@
+
+
+
+ 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..79627734f1c
--- /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..8c92262ce85
--- /dev/null
+++ b/new_product_type/views/sale_order_line_views.xml
@@ -0,0 +1,40 @@
+
+
+
+ sale.order.line.form.view
+ sale.order
+
+
+
+
+
+
+
+ is_kit_product
+
+
+
+ is_kit_product
+
+
+ is_kit_product
+
+
+ is_kit_product
+
+
+ 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..453be23cece
--- /dev/null
+++ b/new_product_type/wizard/__init__.py
@@ -0,0 +1,2 @@
+from . import kit_wizard_line
+from . import kit_wizard
diff --git a/new_product_type/wizard/kit_wizard.py b/new_product_type/wizard/kit_wizard.py
new file mode 100644
index 00000000000..40d54d8de03
--- /dev/null
+++ b/new_product_type/wizard/kit_wizard.py
@@ -0,0 +1,93 @@
+from odoo import models, fields, api
+
+from odoo.fields import Command
+
+
+class ProductKitWizard(models.TransientModel):
+ _name = "product.kit.wizard"
+ _description = "Kit Sub Product Wizard"
+
+ sale_line_id = fields.Many2one("sale.order.line")
+ main_product_id = fields.Many2one("product.product", string="Product")
+ line_ids = fields.One2many("kit.wizard.line", "wizard_id", string="Sub Products")
+
+ @api.model
+ def default_get(self, fields_list):
+ res = super().default_get(fields_list)
+ sale_line = self.env["sale.order.line"].browse(self._context.get("active_id"))
+ if sale_line.product_id:
+ product = sale_line.product_id
+ sale_order = sale_line.order_id
+ kit_sub_product = []
+ for sub in product.product_tmpl_id.sub_product:
+ existing_line = sale_order.order_line.filtered(
+ lambda line: line.product_id == sub
+ and line.kit_parent_line_id == sale_line
+ )
+ # existing_line = self.env["sale.order.line"].search([
+ # ("order_id", "=", sale_order.id),
+ # ("product_id", "=", sub.id),
+ # ("kit_parent_line_id", "=", sale_line.id),
+ # ], limit=1)
+
+ kit_sub_product.append(
+ Command.create(
+ {
+ "product_id": sub.id,
+ "quantity": existing_line.product_uom_qty if existing_line else 1.0,
+ "price": existing_line.extra_price if existing_line else sub.lst_price,
+ # "existing_line_id": existing_line.id if existing_line else False,
+ }
+ )
+ )
+
+ res.update(
+ {
+ "main_product_id": product.id,
+ "sale_line_id": sale_line.id,
+ "line_ids": kit_sub_product,
+ }
+ )
+ return res
+
+ def action_confirm(self):
+ order = self.sale_line_id.order_id
+ Parent_kit_product_line = self.sale_line_id
+ parent_product = Parent_kit_product_line.product_id
+ parent_sequence = Parent_kit_product_line.sequence
+ total_price = parent_product.list_price
+ for wizard_line in self.line_ids:
+ existing_line = order.order_line.filtered(
+ lambda line: line.product_id == wizard_line.product_id
+ and line.kit_parent_line_id == Parent_kit_product_line
+
+ )
+ # existing_line = self.env["sale.order.line"].search([
+ # ("order_id", "=", order.id),
+ # ("product_id", "=", wizard_line.product_id.id),
+ # ("kit_parent_line_id", "=", Parent_kit_product_line.id),
+ # ], limit=1)
+ values = {
+ "product_uom_qty": wizard_line.quantity,
+ "price_unit": 0.0,
+ "extra_price": wizard_line.price,
+ "sequence": parent_sequence,
+ }
+
+ if existing_line:
+ existing_line.write(values)
+ else:
+ self.env["sale.order.line"].create(
+ {
+ **values,
+ "name": wizard_line.product_id.name,
+ "order_id": order.id,
+ "product_id": wizard_line.product_id.id,
+ "is_kit_product": True,
+ "kit_parent_line_id": Parent_kit_product_line.id,
+ }
+ )
+
+ total_price += wizard_line.price * wizard_line.quantity
+
+ Parent_kit_product_line.write({"price_unit": total_price})
diff --git a/new_product_type/wizard/kit_wizard_line.py b/new_product_type/wizard/kit_wizard_line.py
new file mode 100644
index 00000000000..a14ff256c7e
--- /dev/null
+++ b/new_product_type/wizard/kit_wizard_line.py
@@ -0,0 +1,12 @@
+from odoo import models, fields
+
+
+class KitWizardLine(models.TransientModel):
+ _name = "kit.wizard.line"
+ _description = "Kit Wizard Line"
+
+ wizard_id = fields.Many2one("product.kit.wizard")
+ product_id = fields.Many2one("product.product", readonly=True)
+ quantity = fields.Float()
+ price = fields.Float()
+ # existing_line_id = fields.Many2one("sale.order.line", string="Existing Product order line")