-
-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[ADD] sale_loyalty_general_discount_promo_code
- Loading branch information
Showing
17 changed files
with
1,089 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
======================================== | ||
Sale Loyalty General Discount Promo Code | ||
======================================== | ||
|
||
.. | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! This file is generated by oca-gen-addon-readme !! | ||
!! changes will be overwritten. !! | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
!! source digest: sha256:f607e90aa781cba94a03d02423c322fb46de6faf028ce7deb5389e0635d6a29e | ||
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! | ||
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png | ||
:target: https://odoo-community.org/page/development-status | ||
:alt: Beta | ||
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png | ||
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html | ||
:alt: License: AGPL-3 | ||
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--promotion-lightgray.png?logo=github | ||
:target: https://github.com/OCA/sale-promotion/tree/16.0/sale_loyalty_general_discount_promo_code | ||
:alt: OCA/sale-promotion | ||
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png | ||
:target: https://translation.odoo-community.org/projects/sale-promotion-16-0/sale-promotion-16-0-sale_loyalty_general_discount_promo_code | ||
:alt: Translate me on Weblate | ||
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png | ||
:target: https://runboat.odoo-community.org/builds?repo=OCA/sale-promotion&target_branch=16.0 | ||
:alt: Try me on Runboat | ||
|
||
|badge1| |badge2| |badge3| |badge4| |badge5| | ||
|
||
This module automatically applies the promo code discount percentage to | ||
each sales order line when the promo code is activated. | ||
|
||
**Table of contents** | ||
|
||
.. contents:: | ||
:local: | ||
|
||
Use Cases / Context | ||
=================== | ||
|
||
Currently when a discount code is added to a sales order in Odoo, the | ||
discount is added as a single sales order line. This causes issues with | ||
the way taxes are calculated on the order, leading to potential tax | ||
errors and audit issues. | ||
|
||
Configuration | ||
============= | ||
|
||
1. Go to **Sales** > **Configuration** > **Settings**. | ||
2. Scroll down to the **Pricing** section. | ||
3. Enable the checkbox **Automatically apply promo code discount | ||
percentage**. | ||
4. The checkbox **Discounts** should be disabled by default | ||
5. Click **Save**. | ||
|
||
Usage | ||
===== | ||
|
||
1. Create a Coupon Program | ||
-------------------------- | ||
|
||
- Go to **Sale > Products > Discount & Loyalty**. | ||
- Create a **program** with the **program type** set to ``Coupons`` or | ||
``Discount`` or ``Promotions``. | ||
|
||
2. Define Rewards | ||
----------------- | ||
|
||
- Create **rewards** for the program with: | ||
|
||
- **Reward Type**: ``Discount`` | ||
- **Discount Mode**: ``Percent`` | ||
|
||
3. Generate Coupons | ||
------------------- | ||
|
||
- Generate **coupon codes** for the program. | ||
|
||
4. Apply Coupon to a Quotation | ||
------------------------------ | ||
|
||
- Create a **new quotation** and add products. | ||
- Apply the **coupon code** to the quotation. | ||
|
||
5. Verify Discount Application | ||
------------------------------ | ||
|
||
- The **discount column (``disc``)** will be **recomputed**. | ||
- The **reward description** will display the **amount of savings** | ||
applied by the discount code. | ||
|
||
-------------- | ||
|
||
Notes | ||
~~~~~ | ||
|
||
- Ensure the program is correctly configured before applying coupons. | ||
- The discount applies based on the program settings (``order``, | ||
``specific``, or ``cheapest``). | ||
|
||
Bug Tracker | ||
=========== | ||
|
||
Bugs are tracked on `GitHub Issues <https://github.com/OCA/sale-promotion/issues>`_. | ||
In case of trouble, please check there if your issue has already been reported. | ||
If you spotted it first, help us to smash it by providing a detailed and welcomed | ||
`feedback <https://github.com/OCA/sale-promotion/issues/new?body=module:%20sale_loyalty_general_discount_promo_code%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. | ||
|
||
Do not contact contributors directly about support or help with technical issues. | ||
|
||
Credits | ||
======= | ||
|
||
Authors | ||
------- | ||
|
||
* Kencove | ||
|
||
Contributors | ||
------------ | ||
|
||
- `Trobz <https://trobz.com>`__: | ||
|
||
- Tuan Nguyen <[email protected]> | ||
|
||
Maintainers | ||
----------- | ||
|
||
This module is maintained by the OCA. | ||
|
||
.. image:: https://odoo-community.org/logo.png | ||
:alt: Odoo Community Association | ||
:target: https://odoo-community.org | ||
|
||
OCA, or the Odoo Community Association, is a nonprofit organization whose | ||
mission is to support the collaborative development of Odoo features and | ||
promote its widespread use. | ||
|
||
.. |maintainer-natuan9| image:: https://github.com/natuan9.png?size=40px | ||
:target: https://github.com/natuan9 | ||
:alt: natuan9 | ||
|
||
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__: | ||
|
||
|maintainer-natuan9| | ||
|
||
This module is part of the `OCA/sale-promotion <https://github.com/OCA/sale-promotion/tree/16.0/sale_loyalty_general_discount_promo_code>`_ project on GitHub. | ||
|
||
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import models |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# Copyright 2025 Kencove | ||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) | ||
{ | ||
"name": "Sale Loyalty General Discount Promo Code", | ||
"summary": "Apply the promo code discount percentage to each sales order line", | ||
"version": "16.0.1.0.0", | ||
"category": "web", | ||
"website": "https://github.com/OCA/sale-promotion", | ||
"author": "Kencove, Odoo Community Association (OCA)", | ||
"maintainers": ["natuan9"], | ||
"license": "AGPL-3", | ||
"depends": [ | ||
"sale_loyalty", | ||
], | ||
"data": [ | ||
"views/res_config_settings_views.xml", | ||
], | ||
"assets": {}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
from . import sale_order, res_config_settings |
12 changes: 12 additions & 0 deletions
12
sale_loyalty_general_discount_promo_code/models/res_config_settings.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from odoo import fields, models | ||
|
||
|
||
class ResConfigSettings(models.TransientModel): | ||
_inherit = "res.config.settings" | ||
|
||
automatically_apply_promo_code_discount_percentage = fields.Boolean( | ||
config_parameter=( | ||
"sale_loyalty_general_discount_promo_code." | ||
"automatically_apply_promo_code_discount_percentage" | ||
), | ||
) |
158 changes: 158 additions & 0 deletions
158
sale_loyalty_general_discount_promo_code/models/sale_order.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
from odoo import _, models | ||
from odoo.tools import format_amount | ||
|
||
|
||
class SaleOrder(models.Model): | ||
_inherit = "sale.order" | ||
|
||
def _get_total_saving_specific(self, reward): | ||
self.ensure_one() | ||
assert reward.discount_applicability == "specific" | ||
|
||
order_lines = self.order_line - self._get_no_effect_on_threshold_lines() | ||
lines_to_discount = order_lines.filtered( | ||
lambda line: not line.reward_id | ||
and line.product_uom_qty | ||
and line.price_total | ||
and line.product_id.filtered_domain(reward._get_discount_product_domain()) | ||
) | ||
|
||
return sum( | ||
line.price_unit * line.product_uom_qty * (reward.discount / 100) | ||
for line in lines_to_discount | ||
) | ||
|
||
def _get_total_saving_cheapest(self, reward): | ||
self.ensure_one() | ||
assert reward.discount_applicability == "cheapest" | ||
|
||
cheapest_line = self._cheapest_line() | ||
if not cheapest_line: | ||
return 0 | ||
return ( | ||
cheapest_line.price_unit | ||
* cheapest_line.product_uom_qty | ||
* (reward.discount / 100) | ||
) | ||
|
||
def _get_total_saving_order(self, reward): | ||
self.ensure_one() | ||
assert reward.discount_applicability == "order" | ||
total_saving = 0 | ||
for line in self.order_line: | ||
if line.reward_id: | ||
continue | ||
total_saving += ( | ||
line.price_unit * line.product_uom_qty * (reward.discount / 100) | ||
) | ||
return total_saving | ||
|
||
def _get_total_saved_amount(self, reward): | ||
total_saving = 0 | ||
reward_applies_on = reward.discount_applicability | ||
if reward_applies_on == "order": | ||
total_saving = self._get_total_saving_order(reward) | ||
elif reward_applies_on == "specific": | ||
total_saving = self._get_total_saving_specific(reward) | ||
elif reward_applies_on == "cheapest": | ||
total_saving = self._get_total_saving_cheapest(reward) | ||
|
||
return format_amount(self.env, total_saving, self.pricelist_id.currency_id) | ||
|
||
def _get_reward_values_discount(self, reward, coupon, **kwargs): | ||
rewards = super()._get_reward_values_discount(reward, coupon, **kwargs) | ||
|
||
if ( | ||
not self.env["ir.config_parameter"] | ||
.sudo() | ||
.get_param( | ||
"sale_loyalty_general_discount_promo_code." | ||
"automatically_apply_promo_code_discount_percentage" | ||
) | ||
): | ||
return rewards | ||
|
||
for reward_line in rewards: | ||
if ( | ||
reward.reward_type == "discount" | ||
and reward.discount_mode == "percent" | ||
and ( | ||
reward.program_id.program_type | ||
in ["coupons", "promo_code", "promotion"] | ||
) | ||
): | ||
# Get the saved amount | ||
# Cannot use the reward_line.price_unit for the saved amount | ||
# because _get_reward_line_values is called twice. | ||
# Once in _apply_program_reward and once in _update_programs_and_rewards | ||
saved_amount = self._get_total_saved_amount(reward) | ||
reward_line.update( | ||
{ | ||
"display_type": "line_note", | ||
"name": _( | ||
f"You saved {saved_amount} with promo {reward.program_id.name}" | ||
), | ||
"price_unit": 0, | ||
"product_uom_qty": 0, | ||
"product_id": None, | ||
"product_uom": None, | ||
} | ||
) | ||
|
||
return rewards | ||
|
||
def update_discount_percentage(self): | ||
self.ensure_one() | ||
reward_lines = self.order_line.filtered(lambda line: line.reward_id) | ||
order_lines = self.order_line - reward_lines | ||
|
||
reward_groups = {"cheapest": [], "specific": [], "order": []} | ||
for reward_line in reward_lines: | ||
reward = reward_line.reward_id | ||
if reward.discount_mode == "percent": | ||
reward_groups[reward.discount_applicability].append(reward) | ||
|
||
cheapest_line = self._cheapest_line() if reward_groups["cheapest"] else None | ||
specific_domains = { | ||
reward: reward._get_discount_product_domain() | ||
for reward in reward_groups["specific"] | ||
} | ||
|
||
for line in order_lines: | ||
line.discount = 0 | ||
|
||
# Update discount based on `order` rewards | ||
for reward in reward_groups["order"]: | ||
line.discount = 100 - (100 - line.discount) * ( | ||
1 - reward.discount / 100 | ||
) | ||
|
||
# Update discount based on `cheapest` rewards | ||
if line == cheapest_line: | ||
for reward in reward_groups["cheapest"]: | ||
line.discount = 100 - (100 - line.discount) * ( | ||
1 - reward.discount / 100 | ||
) | ||
|
||
# Update discount based on `specific` rewards | ||
for reward, domain in specific_domains.items(): | ||
if line.product_id.filtered_domain(domain): | ||
line.discount = 100 - (100 - line.discount) * ( | ||
1 - reward.discount / 100 | ||
) | ||
|
||
def _write_vals_from_reward_vals(self, reward_vals, old_lines, delete=True): | ||
result = super()._write_vals_from_reward_vals( | ||
reward_vals, old_lines, delete=delete | ||
) | ||
if ( | ||
not self.env["ir.config_parameter"] | ||
.sudo() | ||
.get_param( | ||
"sale_loyalty_general_discount_promo_code." | ||
"automatically_apply_promo_code_discount_percentage" | ||
) | ||
): | ||
return result | ||
self.update_discount_percentage() | ||
return result |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
1. Go to **Sales** > **Configuration** > **Settings**. | ||
2. Scroll down to the **Pricing** section. | ||
3. Enable the checkbox **Automatically apply promo code discount percentage**. | ||
4. The checkbox **Discounts** should be disabled by default | ||
4. Click **Save**. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Currently when a discount code is added to a sales order in Odoo, the discount is added as a single sales order line. This causes issues with the way taxes are calculated on the order, leading to potential tax errors and audit issues. |
2 changes: 2 additions & 0 deletions
2
sale_loyalty_general_discount_promo_code/readme/CONTRIBUTORS.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
- [Trobz](https://trobz.com): | ||
- Tuan Nguyen \<<[email protected]>\> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This module automatically applies the promo code discount percentage to each sales order line when the promo code is activated. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
## 1. Create a Coupon Program | ||
- Go to **Sale > Products > Discount & Loyalty**. | ||
- Create a **program** with the **program type** set to `Coupons` or `Discount` or `Promotions`. | ||
|
||
## 2. Define Rewards | ||
- Create **rewards** for the program with: | ||
- **Reward Type**: `Discount` | ||
- **Discount Mode**: `Percent` | ||
|
||
## 3. Generate Coupons | ||
- Generate **coupon codes** for the program. | ||
|
||
## 4. Apply Coupon to a Quotation | ||
- Create a **new quotation** and add products. | ||
- Apply the **coupon code** to the quotation. | ||
|
||
## 5. Verify Discount Application | ||
- The **discount column (`disc`)** will be **recomputed**. | ||
- The **reward description** will display the **amount of savings** applied by the discount code. | ||
|
||
--- | ||
|
||
### Notes | ||
- Ensure the program is correctly configured before applying coupons. | ||
- The discount applies based on the program settings (`order`, `specific`, or `cheapest`). |
Oops, something went wrong.