From edc4e8b6ef890ba29e7257073e836c4796100a92 Mon Sep 17 00:00:00 2001 From: jakan-odoo Date: Wed, 11 Feb 2026 19:00:26 +0530 Subject: [PATCH 1/3] [ADD] sale_management: initialize discount update feature - Added manifest file and included sale_order_discount module --- sale_order_discount/__init__.py | 0 sale_order_discount/__manifest__.py | 10 ++++++++++ 2 files changed, 10 insertions(+) create mode 100644 sale_order_discount/__init__.py create mode 100644 sale_order_discount/__manifest__.py diff --git a/sale_order_discount/__init__.py b/sale_order_discount/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/sale_order_discount/__manifest__.py b/sale_order_discount/__manifest__.py new file mode 100644 index 00000000000..aa9a1c711e2 --- /dev/null +++ b/sale_order_discount/__manifest__.py @@ -0,0 +1,10 @@ +{ + 'name': "Sale Order Discount", + 'version': '1.0', + 'category': 'Sales', + 'description': 'Updates global discount when order lines are changed', + 'depends': ['sale_management'], + 'installable': True, + 'author': "jakan", + 'license': "LGPL-3", +} From 8a06ac55fff72dd916d1bc1db3bf767105ed1767 Mon Sep 17 00:00:00 2001 From: jakan-odoo Date: Fri, 13 Feb 2026 15:07:01 +0530 Subject: [PATCH 2/3] [ADD] sale_management: inherit sale.order and sale.order.line - Inherit sale.order and sale.order.line model - Discount updated if orderlines are updated --- sale_order_discount/__init__.py | 1 + sale_order_discount/__manifest__.py | 6 +-- sale_order_discount/models/__init__.py | 2 + sale_order_discount/models/sale_order.py | 47 +++++++++++++++++++ .../models/sales_order_line.py | 44 +++++++++++++++++ 5 files changed, 97 insertions(+), 3 deletions(-) create mode 100644 sale_order_discount/models/__init__.py create mode 100644 sale_order_discount/models/sale_order.py create mode 100644 sale_order_discount/models/sales_order_line.py diff --git a/sale_order_discount/__init__.py b/sale_order_discount/__init__.py index e69de29bb2d..0650744f6bc 100644 --- a/sale_order_discount/__init__.py +++ b/sale_order_discount/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_order_discount/__manifest__.py b/sale_order_discount/__manifest__.py index aa9a1c711e2..83c62ae4e39 100644 --- a/sale_order_discount/__manifest__.py +++ b/sale_order_discount/__manifest__.py @@ -1,8 +1,8 @@ { 'name': "Sale Order Discount", - 'version': '1.0', - 'category': 'Sales', - 'description': 'Updates global discount when order lines are changed', + 'version': "1.0", + 'category': "Sales", + 'description': "Updates global discount when order lines are changed", 'depends': ['sale_management'], 'installable': True, 'author': "jakan", diff --git a/sale_order_discount/models/__init__.py b/sale_order_discount/models/__init__.py new file mode 100644 index 00000000000..9dc204cd95b --- /dev/null +++ b/sale_order_discount/models/__init__.py @@ -0,0 +1,2 @@ +from . import sale_order +from . import sales_order_line diff --git a/sale_order_discount/models/sale_order.py b/sale_order_discount/models/sale_order.py new file mode 100644 index 00000000000..5c86a640800 --- /dev/null +++ b/sale_order_discount/models/sale_order.py @@ -0,0 +1,47 @@ +from odoo import api, fields, models +from odoo.exceptions import ValidationError + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + discount_percentage = fields.Float( + string="Discount Amount", + ) + + @api.model + def _get_discount_product(self): + return self.env["product.product"].search([("name", "=", "Discount")], limit=1) + + def _update_discount(self): + discount_product = self._get_discount_product() + discount_line = self.order_line.filtered( + lambda l: l.product_id == discount_product + ) + product_lines = self.order_line.filtered( + lambda l: l.product_id != discount_product + ) + + # if not product_lines and discount_line: + # discount_line.unlink() + # return + + total = sum(product_lines.mapped("price_subtotal")) + discount_amount = -(total * self.discount_percentage / 100.0) + + if discount_line: + discount_line.write({"price_unit": discount_amount}) + + def action_confirm(self): + res = super().action_confirm() + if self.amount_total <= 0: + raise ValidationError( + "Cannot confirm the sale order with zero total amount." + ) + return res + +# if discount_line: +# discount_line.write({ +# "price_unit": discount_amount, +# "product_uom_qty": 1, +# }) diff --git a/sale_order_discount/models/sales_order_line.py b/sale_order_discount/models/sales_order_line.py new file mode 100644 index 00000000000..67c44b9337a --- /dev/null +++ b/sale_order_discount/models/sales_order_line.py @@ -0,0 +1,44 @@ +from odoo import api, models + + +class SalesOrderLine(models.Model): + _inherit = "sale.order.line" + + def is_discount_line(self): + discountProduct = self.order_id.env["product.product"].search( + [("name", "=", "Discount")], limit=1 + ) + return self.product_id == discountProduct + + def unlink(self): + order_ids = self.mapped("order_id") + result = super().unlink() + + for order in order_ids: + order._update_discount() + + return result + + @api.model + def create(self, vals): + line = super().create(vals) + + if line.order_id and not line.is_discount_line(): + line.order_id._update_discount() + + return line + + def write(self, vals): + result = super().write(vals) + + for line in self: + if not line.is_discount_line(): + line.order_id._update_discount() + return result + +# def unlink(self): +# orders = self.mapped("order_id") +# res = super().unlink() +# for order in orders: +# order._update_discount() +# return res From e941c16a4967e57a62a86d2471a0716921733048 Mon Sep 17 00:00:00 2001 From: jakan-odoo Date: Mon, 16 Feb 2026 14:22:56 +0530 Subject: [PATCH 3/3] [ADD] sale_management: update global discount on order line changes - Recomputed discount when order lines are added or removed - Removed discount automatically when no order lines remain - Handled single global discount case --- sale_order_discount/models/__init__.py | 1 - sale_order_discount/models/sale_order.py | 86 +++++++++---------- .../models/sales_order_line.py | 44 ---------- 3 files changed, 43 insertions(+), 88 deletions(-) delete mode 100644 sale_order_discount/models/sales_order_line.py diff --git a/sale_order_discount/models/__init__.py b/sale_order_discount/models/__init__.py index 9dc204cd95b..6aacb753131 100644 --- a/sale_order_discount/models/__init__.py +++ b/sale_order_discount/models/__init__.py @@ -1,2 +1 @@ from . import sale_order -from . import sales_order_line diff --git a/sale_order_discount/models/sale_order.py b/sale_order_discount/models/sale_order.py index 5c86a640800..9d8457e158f 100644 --- a/sale_order_discount/models/sale_order.py +++ b/sale_order_discount/models/sale_order.py @@ -1,47 +1,47 @@ -from odoo import api, fields, models -from odoo.exceptions import ValidationError +import re +from odoo import api, models +from odoo.exceptions import UserError class SaleOrder(models.Model): - _inherit = "sale.order" - - discount_percentage = fields.Float( - string="Discount Amount", - ) - - @api.model - def _get_discount_product(self): - return self.env["product.product"].search([("name", "=", "Discount")], limit=1) - - def _update_discount(self): - discount_product = self._get_discount_product() - discount_line = self.order_line.filtered( - lambda l: l.product_id == discount_product - ) - product_lines = self.order_line.filtered( - lambda l: l.product_id != discount_product - ) - - # if not product_lines and discount_line: - # discount_line.unlink() - # return - - total = sum(product_lines.mapped("price_subtotal")) - discount_amount = -(total * self.discount_percentage / 100.0) - - if discount_line: - discount_line.write({"price_unit": discount_amount}) - - def action_confirm(self): - res = super().action_confirm() - if self.amount_total <= 0: - raise ValidationError( - "Cannot confirm the sale order with zero total amount." + _inherit = 'sale.order' + + @api.onchange('order_line') + def _onchange_recalculate_global_discount(self): + discount_lines = self.env['sale.order.line'] + for line in self.order_line: + if line._is_global_discount(): + discount_lines += line + + if not discount_lines: + return + + product_lines = self.env['sale.order.line'] + for line in self.order_line: + if not line._is_global_discount(): + product_lines += line + + if not product_lines: + self.order_line -= discount_lines + return + + subtotal = 0 + for line in product_lines: + subtotal += line.price_subtotal + + for discount_line in discount_lines: + match = re.search(r"(\d+(?:\.\d+)?)%", discount_line.name) + if match: + percent = float(match.group(1)) + discount_line.price_unit = -(subtotal * percent / 100) + + @api.constrains('order_line') + def _check_single_global_discount(self): + for order in self: + discounts = order.order_line.filtered( + lambda l: l._is_global_discount() ) - return res - -# if discount_line: -# discount_line.write({ -# "price_unit": discount_amount, -# "product_uom_qty": 1, -# }) + if len(discounts) > 1: + raise UserError( + "Only one global discount is allowed per order." + ) diff --git a/sale_order_discount/models/sales_order_line.py b/sale_order_discount/models/sales_order_line.py deleted file mode 100644 index 67c44b9337a..00000000000 --- a/sale_order_discount/models/sales_order_line.py +++ /dev/null @@ -1,44 +0,0 @@ -from odoo import api, models - - -class SalesOrderLine(models.Model): - _inherit = "sale.order.line" - - def is_discount_line(self): - discountProduct = self.order_id.env["product.product"].search( - [("name", "=", "Discount")], limit=1 - ) - return self.product_id == discountProduct - - def unlink(self): - order_ids = self.mapped("order_id") - result = super().unlink() - - for order in order_ids: - order._update_discount() - - return result - - @api.model - def create(self, vals): - line = super().create(vals) - - if line.order_id and not line.is_discount_line(): - line.order_id._update_discount() - - return line - - def write(self, vals): - result = super().write(vals) - - for line in self: - if not line.is_discount_line(): - line.order_id._update_discount() - return result - -# def unlink(self): -# orders = self.mapped("order_id") -# res = super().unlink() -# for order in orders: -# order._update_discount() -# return res