Skip to content

Commit 41a466d

Browse files
[ADD] sale_loyalty_margin_computation: New module
TT62183
1 parent abfe3e5 commit 41a466d

16 files changed

Lines changed: 908 additions & 0 deletions
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
===============================
2+
Sale Loyalty Margin Computation
3+
===============================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:9130c282b0515dc9f3fe5dfb48a3eb3f560b930e1067711ba89332ad2cc697c9
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fmargin--analysis-lightgray.png?logo=github
20+
:target: https://github.com/OCA/margin-analysis/tree/18.0/sale_loyalty_margin_computation
21+
:alt: OCA/margin-analysis
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/margin-analysis-18-0/margin-analysis-18-0-sale_loyalty_margin_computation
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/margin-analysis&target_branch=18.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module allows configuring a formula to calculate the margin of a
32+
sale order line with loyalty rewards
33+
34+
**Table of contents**
35+
36+
.. contents::
37+
:local:
38+
39+
Bug Tracker
40+
===========
41+
42+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/margin-analysis/issues>`_.
43+
In case of trouble, please check there if your issue has already been reported.
44+
If you spotted it first, help us to smash it by providing a detailed and welcomed
45+
`feedback <https://github.com/OCA/margin-analysis/issues/new?body=module:%20sale_loyalty_margin_computation%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
46+
47+
Do not contact contributors directly about support or help with technical issues.
48+
49+
Credits
50+
=======
51+
52+
Authors
53+
-------
54+
55+
* Tecnativa
56+
57+
Contributors
58+
------------
59+
60+
- `Tecnativa <https://www.tecnativa.com>`__:
61+
62+
- Pedro M. Baeza
63+
- Andrii Kompaniiets
64+
65+
Maintainers
66+
-----------
67+
68+
This module is maintained by the OCA.
69+
70+
.. image:: https://odoo-community.org/logo.png
71+
:alt: Odoo Community Association
72+
:target: https://odoo-community.org
73+
74+
OCA, or the Odoo Community Association, is a nonprofit organization whose
75+
mission is to support the collaborative development of Odoo features and
76+
promote its widespread use.
77+
78+
This module is part of the `OCA/margin-analysis <https://github.com/OCA/margin-analysis/tree/18.0/sale_loyalty_margin_computation>`_ project on GitHub.
79+
80+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2026 Tecnativa - Andrii Kompaniiets
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
{
4+
"name": "Sale Loyalty Margin Computation",
5+
"summary": "Allows to use a formula from reward for calculate sale margin",
6+
"version": "18.0.1.0.0",
7+
"category": "Sale",
8+
"website": "https://github.com/OCA/margin-analysis",
9+
"author": "Tecnativa, Odoo Community Association (OCA)",
10+
"license": "AGPL-3",
11+
"depends": [
12+
"loyalty_margin_computation",
13+
"sale_loyalty_order_line_link",
14+
"sale_margin_pricelist_computation",
15+
],
16+
"data": [
17+
"views/loyalty_reward_views.xml",
18+
],
19+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * sale_loyalty_margin_computation
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 18.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2026-04-30 14:27+0000\n"
10+
"PO-Revision-Date: 2026-04-30 16:28+0200\n"
11+
"Last-Translator: Andrii Kompaniiets <andrii.kompaniiets@tecnativa.com>\n"
12+
"Language-Team: \n"
13+
"Language: es_ES\n"
14+
"MIME-Version: 1.0\n"
15+
"Content-Type: text/plain; charset=UTF-8\n"
16+
"Content-Transfer-Encoding: 8bit\n"
17+
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
18+
"X-Generator: Poedit 3.9\n"
19+
20+
#. module: sale_loyalty_margin_computation
21+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
22+
msgid "<code>context</code>: The context"
23+
msgstr "<code>context</code>: El contexto"
24+
25+
#. module: sale_loyalty_margin_computation
26+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
27+
msgid "<code>origin_lines</code>: The origin sale order lines"
28+
msgstr "<code>origin_lines</code>: Las líneas del pedido de venta de origen"
29+
30+
#. module: sale_loyalty_margin_computation
31+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
32+
msgid "<code>reward_line</code>: The Reward line"
33+
msgstr "<code>reward_line</code>: La línea de recompensas"
34+
35+
#. module: sale_loyalty_margin_computation
36+
#: model:ir.model,name:sale_loyalty_margin_computation.model_sale_order_line
37+
msgid "Sales Order Line"
38+
msgstr "Linea de pedido de venta"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Translation of Odoo Server.
2+
# This file contains the translation of the following modules:
3+
# * sale_loyalty_margin_computation
4+
#
5+
msgid ""
6+
msgstr ""
7+
"Project-Id-Version: Odoo Server 18.0\n"
8+
"Report-Msgid-Bugs-To: \n"
9+
"POT-Creation-Date: 2026-04-30 14:27+0000\n"
10+
"PO-Revision-Date: 2026-04-30 14:27+0000\n"
11+
"Last-Translator: \n"
12+
"Language-Team: \n"
13+
"MIME-Version: 1.0\n"
14+
"Content-Type: text/plain; charset=UTF-8\n"
15+
"Content-Transfer-Encoding: \n"
16+
"Plural-Forms: \n"
17+
18+
#. module: sale_loyalty_margin_computation
19+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
20+
msgid "<code>context</code>: The context"
21+
msgstr ""
22+
23+
#. module: sale_loyalty_margin_computation
24+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
25+
msgid "<code>origin_lines</code>: The origin sale order lines"
26+
msgstr ""
27+
28+
#. module: sale_loyalty_margin_computation
29+
#: model_terms:ir.ui.view,arch_db:sale_loyalty_margin_computation.sale_margin_product_loyalty_reward_form_view
30+
msgid "<code>reward_line</code>: The Reward line"
31+
msgstr ""
32+
33+
#. module: sale_loyalty_margin_computation
34+
#: model:ir.model,name:sale_loyalty_margin_computation.model_sale_order_line
35+
msgid "Sales Order Line"
36+
msgstr ""
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
from . import loyalty_reward
2+
from . import sale_order_line
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright 2026 Tecnativa - Andrii Kompaniiets
2+
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
3+
from odoo import Command, api, models
4+
from odoo.exceptions import ValidationError
5+
from odoo.tools.float_utils import float_compare
6+
from odoo.tools.safe_eval import safe_eval
7+
8+
9+
class LoyaltyReward(models.Model):
10+
_inherit = "loyalty.reward"
11+
12+
def _get_sale_margin_formula_eval_context(self):
13+
main_product = self.env["product.product"].new(
14+
{
15+
"name": "Main Product",
16+
"list_price": 35.0,
17+
"standard_price": 11.52,
18+
}
19+
)
20+
order = self.env["sale.order"].new(
21+
{
22+
"partner_id": self.env.ref("base.partner_admin").id,
23+
"order_line": [
24+
Command.create(
25+
{
26+
"product_id": main_product.id,
27+
"product_uom_qty": 2.0,
28+
"price_unit": 35.0,
29+
}
30+
)
31+
],
32+
}
33+
)
34+
return {
35+
"env": self.env,
36+
"context": self.env.context,
37+
"user": self.env.user,
38+
"origin_lines": order.order_line,
39+
"reward": self,
40+
"reward_line": order.order_line[0],
41+
"float_compare": float_compare,
42+
}
43+
44+
@api.constrains("sale_margin_formula")
45+
def _check_sale_margin_formula(self):
46+
res = super()._check_sale_margin_formula()
47+
main_product = self.env["product.product"].new(
48+
{
49+
"name": "Main Product",
50+
"list_price": 35.0,
51+
"standard_price": 11.52,
52+
}
53+
)
54+
order = self.env["sale.order"].new(
55+
{
56+
"partner_id": self.env.ref("base.partner_admin").id,
57+
"order_line": [
58+
Command.create(
59+
{
60+
"product_id": main_product.id,
61+
"product_uom_qty": 2.0,
62+
"price_unit": 35.0,
63+
}
64+
)
65+
],
66+
}
67+
)
68+
fake_eval_context = {
69+
"env": self.env,
70+
"context": self.env.context,
71+
"user": self.env.user,
72+
"origin_lines": order.order_line,
73+
"reward_line": order.order_line[0],
74+
"float_compare": float_compare,
75+
}
76+
for reward in self:
77+
if not reward.sale_margin_formula:
78+
continue
79+
fake_eval_context.update({"reward": reward})
80+
try:
81+
safe_eval(
82+
str(reward.sale_margin_formula).strip(),
83+
reward._get_sale_margin_formula_eval_context(),
84+
mode="exec",
85+
nocopy=True,
86+
)
87+
except Exception as e:
88+
raise ValidationError(
89+
self.env._("Invalid sale margin formula:\n%(error)s", error=e)
90+
) from e
91+
return res
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from odoo import api, models
2+
from odoo.tools.float_utils import float_compare
3+
from odoo.tools.safe_eval import safe_eval
4+
5+
6+
class SaleOrderLine(models.Model):
7+
_inherit = "sale.order.line"
8+
9+
@api.depends("reward_origin_generated_line_ids", "reward_id")
10+
def _compute_margin(self):
11+
res = super()._compute_margin()
12+
for line in self.filtered(lambda x: x.reward_origin_generated_line_ids):
13+
if not line.reward_id.sale_margin_formula:
14+
continue
15+
origin_lines = line.reward_origin_generated_line_ids
16+
eval_context = line._get_reward_eval_context()
17+
safe_eval(
18+
str(line.reward_id.sale_margin_formula).strip(),
19+
eval_context,
20+
mode="exec",
21+
nocopy=True,
22+
)
23+
origin_lines[0].margin = eval_context.get("result", 0)
24+
origin_lines[0].margin_percent = (
25+
origin_lines[0].margin / origin_lines[0].price_subtotal
26+
)
27+
break
28+
return res
29+
30+
def _get_reward_eval_context(self):
31+
return {
32+
"env": self.env,
33+
"context": self.env.context,
34+
"user": self.env.user,
35+
"origin_lines": self.reward_origin_generated_line_ids,
36+
"reward": self.reward_id,
37+
"reward_line": self,
38+
"float_compare": float_compare,
39+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["whool"]
3+
build-backend = "whool.buildapi"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- [Tecnativa](https://www.tecnativa.com):
2+
- Pedro M. Baeza
3+
- Andrii Kompaniiets

0 commit comments

Comments
 (0)