From 322a532b241ccc592db12e401657bc755f997212 Mon Sep 17 00:00:00 2001 From: rugot-odoo Date: Thu, 12 Feb 2026 18:23:11 +0530 Subject: [PATCH] [IMP] new_product_type : Implemented custom Kit/Bundle product functionality: - Product-level kit configuration - Sale order wizard for component adjustment - Parent-child linking of kit lines - Optional breakdown visibility in reports - Prevent users from deleting kit sub-products directly - Automatically delete all child lines when parent kit line is removed - Load price from extra_price instead of price_unit --- new_product_type/__init__.py | 2 + new_product_type/__manifest__.py | 18 ++++ new_product_type/models/__init__.py | 3 + new_product_type/models/product_template.py | 15 +++ new_product_type/models/sale_order.py | 7 ++ new_product_type/models/sale_order_line.py | 38 ++++++++ new_product_type/report/invoice_report.xml | 13 +++ .../report/sale_order_portal_report.xml | 14 +++ new_product_type/report/sale_order_report.xml | 15 +++ new_product_type/security/ir.model.access.csv | 3 + new_product_type/views/kit_wizard_views.xml | 29 ++++++ new_product_type/views/product_views.xml | 17 ++++ .../views/sale_order_line_views.xml | 40 ++++++++ new_product_type/wizard/__init__.py | 2 + new_product_type/wizard/kit_wizard.py | 93 +++++++++++++++++++ new_product_type/wizard/kit_wizard_line.py | 12 +++ 16 files changed, 321 insertions(+) create mode 100644 new_product_type/__init__.py create mode 100644 new_product_type/__manifest__.py create mode 100644 new_product_type/models/__init__.py create mode 100644 new_product_type/models/product_template.py create mode 100644 new_product_type/models/sale_order.py create mode 100644 new_product_type/models/sale_order_line.py create mode 100644 new_product_type/report/invoice_report.xml create mode 100644 new_product_type/report/sale_order_portal_report.xml create mode 100644 new_product_type/report/sale_order_report.xml create mode 100644 new_product_type/security/ir.model.access.csv create mode 100644 new_product_type/views/kit_wizard_views.xml create mode 100644 new_product_type/views/product_views.xml create mode 100644 new_product_type/views/sale_order_line_views.xml create mode 100644 new_product_type/wizard/__init__.py create mode 100644 new_product_type/wizard/kit_wizard.py create mode 100644 new_product_type/wizard/kit_wizard_line.py 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 @@ + + + + + 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 @@ + + + + + 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 @@ + + + + + + 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 + +
+ + + + +
Sub Products
+ + + + + + + +
+
+
+
+
+
+ +
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 + + + + +