Skip to content

Commit 605ad98

Browse files
committed
[ADD] discount_update: update global discount on order line changes
- Implemented dynamic recalculation of global discount lines whenever a product is added, removed, or updated in the sale order lines. - Prevents outdated or incorrect discount amounts by cleaning and recreating discount lines on each change. - Handles both single-tax and multi-tax scenarios to preserve tax consistency in discounts.
1 parent 74302d7 commit 605ad98

File tree

5 files changed

+118
-0
lines changed

5 files changed

+118
-0
lines changed

discount_update/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

discount_update/__manifest__.py

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"name": "Update Discount OL",
3+
"depends": ["base", "sale", "sale_management"],
4+
"data": [],
5+
"license": "LGPL-3",
6+
}

discount_update/models/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import sale_order_inherit
2+
from . import sale_order_line_inherit
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from collections import defaultdict
2+
from odoo import _, models
3+
4+
5+
class SaleOrderInherit(models.Model):
6+
_inherit = "sale.order"
7+
8+
def recalculate_discount(self):
9+
for order in self:
10+
discount_ol = order.order_line.filtered(
11+
lambda ol: ol.is_global_discount_line
12+
)
13+
if not discount_ol:
14+
return
15+
16+
discount_per = discount_ol[0].discount_percentage
17+
discount_product = discount_ol[0].product_id
18+
discount_ol.unlink()
19+
total_price_per_tax_groups = defaultdict(float)
20+
21+
for line in order.order_line:
22+
if not line.product_uom_qty or not line.price_unit:
23+
continue
24+
25+
total_price_per_tax_groups[line.tax_id] += (
26+
line.price_unit * line.product_uom_qty
27+
)
28+
29+
if not total_price_per_tax_groups:
30+
return
31+
32+
elif len(total_price_per_tax_groups) == 1:
33+
taxes = next(iter(total_price_per_tax_groups.keys()))
34+
subtotal = total_price_per_tax_groups[taxes]
35+
self.env["sale.order.line"].create(
36+
{
37+
"order_id": order.id,
38+
"product_id": discount_product.id,
39+
"name": _("Discount: %(percent)s%%", percent=discount_per),
40+
"product_uom_qty": 1,
41+
"tax_id": [(6, 0, taxes.ids)],
42+
"price_unit": -subtotal * discount_per / 100,
43+
}
44+
)
45+
46+
else:
47+
vals_list = [
48+
(
49+
{
50+
"order_id": order.id,
51+
"product_id": discount_product.id,
52+
"name": _(
53+
"Discount: %(percent)s%%"
54+
"- On products with the following taxes %(taxes)s",
55+
percent=discount_per,
56+
taxes=", ".join(taxes.mapped("name")),
57+
),
58+
"product_uom_qty": 1,
59+
"tax_id": [(6, 0, taxes.ids)],
60+
"price_unit": -subtotal * discount_per / 100,
61+
}
62+
)
63+
for taxes, subtotal in total_price_per_tax_groups.items()
64+
]
65+
self.env["sale.order.line"].create(vals_list)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import re
2+
from odoo import api, fields, models
3+
4+
5+
class SaleOrderLineInherit(models.Model):
6+
_inherit = "sale.order.line"
7+
8+
is_global_discount_line = fields.Boolean(default=False)
9+
discount_percentage = fields.Float()
10+
11+
@api.model_create_multi
12+
def create(self, vals_list):
13+
res = super().create(vals_list)
14+
for line in res:
15+
if "%" in line.name:
16+
match = re.search(r"(\d+(?:\.\d+)?)%", line.name)
17+
if match:
18+
value = float(match.group(1))
19+
line.discount_percentage = value
20+
line.is_global_discount_line = True
21+
else:
22+
line.order_id.recalculate_discount()
23+
return res
24+
25+
def write(self, vals):
26+
if self.env.context.get("skip_recalculate_discount"):
27+
return super().write(vals)
28+
29+
res = super().write(vals)
30+
31+
for line in self:
32+
line.order_id.with_context(
33+
skip_recalculate_discount=True
34+
).recalculate_discount()
35+
return res
36+
37+
def unlink(self):
38+
orders = self.filtered(lambda ol: not ol.is_global_discount_line).mapped(
39+
"order_id"
40+
)
41+
res = super().unlink()
42+
for order in orders:
43+
order.recalculate_discount()
44+
return res

0 commit comments

Comments
 (0)