Skip to content

Commit 0238bbf

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 766b048 commit 0238bbf

File tree

5 files changed

+119
-0
lines changed

5 files changed

+119
-0
lines changed

Diff for: discount_update/__init__.py

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

Diff for: 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+
}

Diff for: 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

Diff for: discount_update/models/sale_order_inherit.py

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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+
print(discount_ol)
14+
if not discount_ol:
15+
return
16+
17+
discount_per = discount_ol[0].discount_percentage
18+
discount_product = discount_ol[0].product_id
19+
discount_ol.unlink()
20+
total_price_per_tax_groups = defaultdict(float)
21+
22+
for line in order.order_line:
23+
if not line.product_uom_qty or not line.price_unit:
24+
continue
25+
26+
total_price_per_tax_groups[line.tax_id] += (
27+
line.price_unit * line.product_uom_qty
28+
)
29+
30+
if not total_price_per_tax_groups:
31+
return
32+
33+
elif len(total_price_per_tax_groups) == 1:
34+
taxes = next(iter(total_price_per_tax_groups.keys()))
35+
subtotal = total_price_per_tax_groups[taxes]
36+
self.env["sale.order.line"].create(
37+
{
38+
"order_id": order.id,
39+
"product_id": discount_product.id,
40+
"name": _("Discount: %(percent)s%%", percent=discount_per),
41+
"product_uom_qty": 1,
42+
"tax_id": [(6, 0, taxes.ids)],
43+
"price_unit": -subtotal * discount_per / 100,
44+
}
45+
)
46+
47+
else:
48+
vals_list = [
49+
(
50+
{
51+
"order_id": order.id,
52+
"product_id": discount_product.id,
53+
"name": _(
54+
"Discount: %(percent)s%%"
55+
"- On products with the following taxes %(taxes)s",
56+
percent=discount_per,
57+
taxes=", ".join(taxes.mapped("name")),
58+
),
59+
"product_uom_qty": 1,
60+
"tax_id": [(6, 0, taxes.ids)],
61+
"price_unit": -subtotal * discount_per / 100,
62+
}
63+
)
64+
for taxes, subtotal in total_price_per_tax_groups.items()
65+
]
66+
self.env["sale.order.line"].create(vals_list)

Diff for: discount_update/models/sale_order_line_inherit.py

+44
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)