diff --git a/sale_product_kit/__init__.py b/sale_product_kit/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/sale_product_kit/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/sale_product_kit/__manifest__.py b/sale_product_kit/__manifest__.py new file mode 100644 index 00000000000..c515c585078 --- /dev/null +++ b/sale_product_kit/__manifest__.py @@ -0,0 +1,17 @@ +{ + 'name': 'Product Kit', + 'author': 'Ayush Khubchandani (aykhu)', + 'license': 'LGPL-3', + 'depends': ["sale", "product"], + 'data': [ + 'security/ir.model.access.csv', + 'views/product_views.xml', + 'views/sale_order_views.xml', + 'wizard/product_kit_wizard_views.xml', + ], + 'assets': { + 'web.assets_backend': [ + 'sale_product_kit/static/src/js/product_kit_sale_order_line.js', + ], + }, +} diff --git a/sale_product_kit/models/__init__.py b/sale_product_kit/models/__init__.py new file mode 100644 index 00000000000..fbe8e5e1443 --- /dev/null +++ b/sale_product_kit/models/__init__.py @@ -0,0 +1,4 @@ +from . import product_template +from . import sale_order_line +from . import sale_order +from . import account_move diff --git a/sale_product_kit/models/account_move.py b/sale_product_kit/models/account_move.py new file mode 100644 index 00000000000..072cd44b239 --- /dev/null +++ b/sale_product_kit/models/account_move.py @@ -0,0 +1,14 @@ +from odoo import models + + +class AccountMove(models.Model): + _inherit = 'account.move' + + def _get_move_lines_to_report(self): + lines = super()._get_move_lines_to_report() + return lines.filtered( + lambda l: ( + not l.sale_line_ids.is_kit_child + or l.sale_line_ids.order_id.print_kit_components + ) + ) diff --git a/sale_product_kit/models/product_template.py b/sale_product_kit/models/product_template.py new file mode 100644 index 00000000000..40897ab24b1 --- /dev/null +++ b/sale_product_kit/models/product_template.py @@ -0,0 +1,15 @@ +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + is_kit = fields.Boolean() + sub_product_ids = fields.Many2many('product.product') + + @api.constrains("sub_product_ids") + def _check_no_self_product_reference(self): + for record in self: + if record.product_variant_id in record.sub_product_ids: + raise ValidationError("A product cannot be added as a sub-product in its own kit.") diff --git a/sale_product_kit/models/sale_order.py b/sale_product_kit/models/sale_order.py new file mode 100644 index 00000000000..0fd95dff0bb --- /dev/null +++ b/sale_product_kit/models/sale_order.py @@ -0,0 +1,16 @@ +from odoo import fields, models + + +class SaleOrder(models.Model): + _inherit = 'sale.order' + + print_kit_components = fields.Boolean() + + def _get_order_lines_to_report(self): + lines = super()._get_order_lines_to_report() + return lines.filtered( + lambda l: ( + not l.is_kit_child + or l.order_id.print_kit_components + ) + ) diff --git a/sale_product_kit/models/sale_order_line.py b/sale_product_kit/models/sale_order_line.py new file mode 100644 index 00000000000..80289bd21f4 --- /dev/null +++ b/sale_product_kit/models/sale_order_line.py @@ -0,0 +1,17 @@ +from odoo import fields, models + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + is_kit = fields.Boolean(related='product_template_id.is_kit') + parent_kit_line_id = fields.Many2one( + "sale.order.line", + ondelete="cascade", + ) + child_kit_line_ids = fields.One2many( + "sale.order.line", + "parent_kit_line_id", + ) + is_kit_child = fields.Boolean() + kit_component_price = fields.Float() diff --git a/sale_product_kit/security/ir.model.access.csv b/sale_product_kit/security/ir.model.access.csv new file mode 100644 index 00000000000..1a7a4f167f2 --- /dev/null +++ b/sale_product_kit/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_kit_wizard,access_product_kit_wizard,model_product_kit_wizard,base.group_user,1,1,1,1 +access_product_kit_wizard_line,access_product_kit_wizard_line,model_product_kit_wizard_line,base.group_user,1,1,1,1 diff --git a/sale_product_kit/static/src/js/product_kit_sale_order_line.js b/sale_product_kit/static/src/js/product_kit_sale_order_line.js new file mode 100644 index 00000000000..45c7c1aea1c --- /dev/null +++ b/sale_product_kit/static/src/js/product_kit_sale_order_line.js @@ -0,0 +1,47 @@ +import { registry } from "@web/core/registry"; +import { + SaleOrderLineListRenderer, + SaleOrderLineOne2Many, +} from "@sale/js/sale_order_line_field/sale_order_line_field"; + + +export class ProductKitSaleOrderLineListRenderer extends SaleOrderLineListRenderer { + + isKitChild(record) { + return !!record.data.is_kit_child; + } + + isKitParent(record){ + return !!record.data.is_kit; + } + + isCellReadonly(column, record) { + return super.isCellReadonly(column, record) || ( + this.isKitChild(record) + ) || ( + this.isKitParent(record) + ); + } + + displayDeleteIcon(record) { + return super.displayDeleteIcon(record) + && !this.isKitChild(record); + } +} + +export class ProductKitSaleOrderLineOne2Many extends SaleOrderLineOne2Many { + static components = { + ...SaleOrderLineOne2Many.components, + ListRenderer: ProductKitSaleOrderLineListRenderer, + }; +} + +export const productKitSaleOrderLineOne2Many = { + ...registry.category("fields").get("sol_o2m"), + component: ProductKitSaleOrderLineOne2Many, +}; + +registry.category("fields").add( + "product_kit_sol_o2m", + productKitSaleOrderLineOne2Many +); diff --git a/sale_product_kit/views/product_views.xml b/sale_product_kit/views/product_views.xml new file mode 100644 index 00000000000..7b884c67352 --- /dev/null +++ b/sale_product_kit/views/product_views.xml @@ -0,0 +1,17 @@ + + + + + product.kit.form.inherit.product.template + product.template + 5 + + + + + + + + + + diff --git a/sale_product_kit/views/sale_order_views.xml b/sale_product_kit/views/sale_order_views.xml new file mode 100644 index 00000000000..792e15c7227 --- /dev/null +++ b/sale_product_kit/views/sale_order_views.xml @@ -0,0 +1,36 @@ + + + + Product Kit Values + product.kit.wizard + form + new + + + + sale.order.form.product.kit + sale.order + + + + product_kit_sol_o2m + + + + + + + + +