diff --git a/sale_commission_product_criteria_sections/README.rst b/sale_commission_product_criteria_sections/README.rst new file mode 100644 index 000000000..e86b09b88 --- /dev/null +++ b/sale_commission_product_criteria_sections/README.rst @@ -0,0 +1,129 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +========================================= +Sale Commission Product Criteria Sections +========================================= + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:633ef64596534a332b6fc3014f7ebc0dd3c79da3db070ed38f744d83d4351a1b + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fcommission-lightgray.png?logo=github + :target: https://github.com/OCA/commission/tree/16.0/sale_commission_product_criteria_sections + :alt: OCA/commission +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/commission-16-0/commission-16-0-sale_commission_product_criteria_sections + :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/commission&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends sale_commission_product_criteria to allow configuring +section-based (tiered) commission rates per product criteria rule. + +Each commission item can define its own set of rate sections with amount +ranges and percentages, enabling complex commission structures where +different products have different tiered rates based on the order amount. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +Go to Commissions > Configuration > Commission types and open a commission of +type "Product criteria". + +On the rule to be changed, set "Compute Price" to "By sections" and add the +rate sections on the "Rate definition" group: + +* From: lower limit of the amount range, included +* To: upper limit of the amount range, included +* Percent: commission percentage applied to the whole base amount + +Usage +===== + +The commission base amount of the line is matched against the rate sections +of the rule, which are closed intervals: the first section whose limits +contain the base amount is applied, so sections should not overlap. + +For example, a rule with these sections: + +* 0 - 100: 5% +* 100.01 - 500: 8% +* 500.01 - 1000: 10% + +A sale of 200 grants 200 * 8% = 16 of commission and a sale of 600 grants +600 * 10% = 60. A sale of 1500 grants no commission at all, because the base +amount is out of every section. + +The base amount is the same used by the other commission types, that is, the +sale amount or, when the commission base is set to "Margin (Amount - Cost)", +the amount without the product cost. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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 `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Madooit + +Contributors +~~~~~~~~~~~~ + +* `Madooit `__: + + * Rodrigo A. Madureira + +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-rodmad85| image:: https://github.com/rodmad85.png?size=40px + :target: https://github.com/rodmad85 + :alt: rodmad85 + +Current `maintainer `__: + +|maintainer-rodmad85| + +This module is part of the `OCA/commission `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_commission_product_criteria_sections/__init__.py b/sale_commission_product_criteria_sections/__init__.py new file mode 100644 index 000000000..dfbf964a3 --- /dev/null +++ b/sale_commission_product_criteria_sections/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/sale_commission_product_criteria_sections/__manifest__.py b/sale_commission_product_criteria_sections/__manifest__.py new file mode 100644 index 000000000..6aec01894 --- /dev/null +++ b/sale_commission_product_criteria_sections/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Sale Commission Product Criteria Sections", + "summary": "Compute product criteria commissions by amount sections", + "version": "16.0.1.0.0", + "category": "Sales Management", + "website": "https://github.com/OCA/commission", + "author": "Madooit, Odoo Community Association (OCA)", + "maintainers": ["rodmad85"], + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "sale_commission_product_criteria", + ], + "data": [ + "views/commission_views.xml", + ], +} diff --git a/sale_commission_product_criteria_sections/models/__init__.py b/sale_commission_product_criteria_sections/models/__init__.py new file mode 100644 index 000000000..d607d5edb --- /dev/null +++ b/sale_commission_product_criteria_sections/models/__init__.py @@ -0,0 +1,7 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import account_invoice_line_agent +from . import commission +from . import sale_commission_line_mixin +from . import sale_order_line_agent diff --git a/sale_commission_product_criteria_sections/models/account_invoice_line_agent.py b/sale_commission_product_criteria_sections/models/account_invoice_line_agent.py new file mode 100644 index 000000000..2766e0fbe --- /dev/null +++ b/sale_commission_product_criteria_sections/models/account_invoice_line_agent.py @@ -0,0 +1,20 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class AccountInvoiceLineAgent(models.Model): + _inherit = "account.invoice.line.agent" + + def _get_single_commission_amount(self, commission, subtotal, product, quantity): + # It is defined on the models and not on the mixin because other + # modules of the product criteria family replace it on the models. + commission_item = self._get_section_commission_item(commission, product) + if commission_item: + return self._get_section_commission_amount( + commission_item, commission, subtotal, product, quantity + ) + return super()._get_single_commission_amount( + commission, subtotal, product, quantity + ) diff --git a/sale_commission_product_criteria_sections/models/commission.py b/sale_commission_product_criteria_sections/models/commission.py new file mode 100644 index 000000000..f01c8c9fd --- /dev/null +++ b/sale_commission_product_criteria_sections/models/commission.py @@ -0,0 +1,51 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import _, fields, models + + +class CommissionItem(models.Model): + _inherit = "commission.item" + + commission_type = fields.Selection( + selection_add=[("section", _("By sections"))], + ondelete={"section": "set default"}, + ) + section_ids = fields.One2many( + comodel_name="commission.section", + inverse_name="commission_item_id", + string="Sections", + copy=True, + ) + + def _compute_commission_item_name_value(self): + res = super()._compute_commission_item_name_value() + for item in self.filtered(lambda x: x.commission_type == "section"): + item.commission_value = _("By sections") + return res + + def _calculate_section_amount(self, base): + """Return the commission amount for ``base``, following the sections. + + Sections are closed intervals, so the first one whose ``amount_from`` + and ``amount_to`` contain ``base`` is applied. Zero is returned when + no section matches ``base``. + """ + self.ensure_one() + section = self.section_ids.filtered( + lambda x: x.amount_from <= base <= x.amount_to + )[:1] + if not section: + return 0.0 + return base * section.percent / 100.0 + + +class CommissionSection(models.Model): + _inherit = "commission.section" + + commission_item_id = fields.Many2one( + comodel_name="commission.item", + string="Commission Item", + ondelete="cascade", + index=True, + ) diff --git a/sale_commission_product_criteria_sections/models/sale_commission_line_mixin.py b/sale_commission_product_criteria_sections/models/sale_commission_line_mixin.py new file mode 100644 index 000000000..1c6f577a0 --- /dev/null +++ b/sale_commission_product_criteria_sections/models/sale_commission_line_mixin.py @@ -0,0 +1,44 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class SaleCommissionLineMixin(models.AbstractModel): + _inherit = "commission.line.mixin" + + def _get_section_commission_item(self, commission, product): + """Return the commission item to compute by sections, if any. + + The first item matching the product and the commission is the one + applied, so the section type is only used when that item defines it. + """ + self.ensure_one() + # Items are fetched with a raw query on every line, so the section + # type is looked for before resolving them. + if not any( + commission_item.commission_type == "section" + for commission_item in commission.item_ids + ): + return self.env["commission.item"] + item_ids = self._get_commission_items(commission, product) + if not item_ids: + return self.env["commission.item"] + commission_item = self.env["commission.item"].browse(item_ids[0]) + if commission_item.commission_type != "section": + return self.env["commission.item"] + return commission_item + + def _get_section_commission_amount( + self, commission_item, commission, subtotal, product, quantity + ): + """Return the amount of ``commission_item`` following its sections.""" + self.ensure_one() + if commission.amount_base_type == "net_amount": + # If subtotal (sale_price * quantity) is less than + # standard_price * quantity, it means that we are selling at + # lower price than we bought, so set amount_base to 0 + subtotal = max([0, subtotal - product.standard_price * quantity]) + self.applied_commission_item_id = commission_item + self.applied_commission_id = commission_item.commission_id + return commission_item._calculate_section_amount(subtotal) diff --git a/sale_commission_product_criteria_sections/models/sale_order_line_agent.py b/sale_commission_product_criteria_sections/models/sale_order_line_agent.py new file mode 100644 index 000000000..51015a672 --- /dev/null +++ b/sale_commission_product_criteria_sections/models/sale_order_line_agent.py @@ -0,0 +1,20 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class SaleOrderLineAgent(models.Model): + _inherit = "sale.order.line.agent" + + def _get_single_commission_amount(self, commission, subtotal, product, quantity): + # It is defined on the models and not on the mixin because other + # modules of the product criteria family replace it on the models. + commission_item = self._get_section_commission_item(commission, product) + if commission_item: + return self._get_section_commission_amount( + commission_item, commission, subtotal, product, quantity + ) + return super()._get_single_commission_amount( + commission, subtotal, product, quantity + ) diff --git a/sale_commission_product_criteria_sections/readme/CONFIGURE.rst b/sale_commission_product_criteria_sections/readme/CONFIGURE.rst new file mode 100644 index 000000000..5a10fddbe --- /dev/null +++ b/sale_commission_product_criteria_sections/readme/CONFIGURE.rst @@ -0,0 +1,9 @@ +Go to Commissions > Configuration > Commission types and open a commission of +type "Product criteria". + +On the rule to be changed, set "Compute Price" to "By sections" and add the +rate sections on the "Rate definition" group: + +* From: lower limit of the amount range, included +* To: upper limit of the amount range, included +* Percent: commission percentage applied to the whole base amount diff --git a/sale_commission_product_criteria_sections/readme/CONTRIBUTORS.rst b/sale_commission_product_criteria_sections/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..7242da82a --- /dev/null +++ b/sale_commission_product_criteria_sections/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Madooit `__: + + * Rodrigo A. Madureira diff --git a/sale_commission_product_criteria_sections/readme/DESCRIPTION.rst b/sale_commission_product_criteria_sections/readme/DESCRIPTION.rst new file mode 100644 index 000000000..598a86457 --- /dev/null +++ b/sale_commission_product_criteria_sections/readme/DESCRIPTION.rst @@ -0,0 +1,6 @@ +This module extends sale_commission_product_criteria to allow configuring +section-based (tiered) commission rates per product criteria rule. + +Each commission item can define its own set of rate sections with amount +ranges and percentages, enabling complex commission structures where +different products have different tiered rates based on the order amount. diff --git a/sale_commission_product_criteria_sections/readme/USAGE.rst b/sale_commission_product_criteria_sections/readme/USAGE.rst new file mode 100644 index 000000000..e5c274fe0 --- /dev/null +++ b/sale_commission_product_criteria_sections/readme/USAGE.rst @@ -0,0 +1,17 @@ +The commission base amount of the line is matched against the rate sections +of the rule, which are closed intervals: the first section whose limits +contain the base amount is applied, so sections should not overlap. + +For example, a rule with these sections: + +* 0 - 100: 5% +* 100.01 - 500: 8% +* 500.01 - 1000: 10% + +A sale of 200 grants 200 * 8% = 16 of commission and a sale of 600 grants +600 * 10% = 60. A sale of 1500 grants no commission at all, because the base +amount is out of every section. + +The base amount is the same used by the other commission types, that is, the +sale amount or, when the commission base is set to "Margin (Amount - Cost)", +the amount without the product cost. diff --git a/sale_commission_product_criteria_sections/static/description/index.html b/sale_commission_product_criteria_sections/static/description/index.html new file mode 100644 index 000000000..b746766a4 --- /dev/null +++ b/sale_commission_product_criteria_sections/static/description/index.html @@ -0,0 +1,470 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Sale Commission Product Criteria Sections

+ +

Beta License: AGPL-3 OCA/commission Translate me on Weblate Try me on Runboat

+

This module extends sale_commission_product_criteria to allow configuring +section-based (tiered) commission rates per product criteria rule.

+

Each commission item can define its own set of rate sections with amount +ranges and percentages, enabling complex commission structures where +different products have different tiered rates based on the order amount.

+

Table of contents

+ +
+

Configuration

+

Go to Commissions > Configuration > Commission types and open a commission of +type “Product criteria”.

+

On the rule to be changed, set “Compute Price” to “By sections” and add the +rate sections on the “Rate definition” group:

+
    +
  • From: lower limit of the amount range, included
  • +
  • To: upper limit of the amount range, included
  • +
  • Percent: commission percentage applied to the whole base amount
  • +
+
+
+

Usage

+

The commission base amount of the line is matched against the rate sections +of the rule, which are closed intervals: the first section whose limits +contain the base amount is applied, so sections should not overlap.

+

For example, a rule with these sections:

+
    +
  • 0 - 100: 5%
  • +
  • 100.01 - 500: 8%
  • +
  • 500.01 - 1000: 10%
  • +
+

A sale of 200 grants 200 * 8% = 16 of commission and a sale of 600 grants +600 * 10% = 60. A sale of 1500 grants no commission at all, because the base +amount is out of every section.

+

The base amount is the same used by the other commission types, that is, the +sale amount or, when the commission base is set to “Margin (Amount - Cost)”, +the amount without the product cost.

+
+
+

Bug Tracker

+

Bugs are tracked on GitHub 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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Madooit
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

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.

+

Current maintainer:

+

rodmad85

+

This module is part of the OCA/commission project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+
+ + diff --git a/sale_commission_product_criteria_sections/tests/__init__.py b/sale_commission_product_criteria_sections/tests/__init__.py new file mode 100644 index 000000000..697a7e7f6 --- /dev/null +++ b/sale_commission_product_criteria_sections/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_sections diff --git a/sale_commission_product_criteria_sections/tests/test_sections.py b/sale_commission_product_criteria_sections/tests/test_sections.py new file mode 100644 index 000000000..9021bd847 --- /dev/null +++ b/sale_commission_product_criteria_sections/tests/test_sections.py @@ -0,0 +1,249 @@ +# Copyright 2025 Madooit +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo.tests.common import TransactionCase + + +class TestSaleCommissionProductCriteriaSections(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.commission_model = cls.env["commission"] + cls.item_model = cls.env["commission.item"] + cls.product_model = cls.env["product.product"] + + # Sections are closed intervals, the first matching one is applied. + cls.section_commission = cls._create_commission("Product criteria by sections") + cls.section_item = cls.item_model.create( + { + "commission_id": cls.section_commission.id, + "based_on": "sol", + "applied_on": "3_global", + "commission_type": "section", + "section_ids": [ + (0, 0, {"amount_from": 0.0, "amount_to": 100.0, "percent": 5.0}), + ( + 0, + 0, + { + "amount_from": 100.01, + "amount_to": 500.0, + "percent": 8.0, + }, + ), + ( + 0, + 0, + { + "amount_from": 500.01, + "amount_to": 1000.0, + "percent": 10.0, + }, + ), + ], + } + ) + # Rules more specific than the global one above take precedence, + # so the other commission types can be checked with their product. + cls.product_fixed = cls._create_product("Fixed amount product") + cls.fixed_item = cls.item_model.create( + { + "commission_id": cls._create_commission( + "Product criteria by fixed amount" + ).id, + "based_on": "sol", + "applied_on": "1_product", + "commission_type": "fixed", + "fixed_amount": 42.0, + "product_tmpl_id": cls.product_fixed.product_tmpl_id.id, + } + ) + cls.product_percentage = cls._create_product("Percentage product") + cls.percentage_item = cls.item_model.create( + { + "commission_id": cls._create_commission( + "Product criteria by percentage" + ).id, + "based_on": "sol", + "applied_on": "0_product_variant", + "commission_type": "percentage", + "percent_amount": 10.0, + "product_id": cls.product_percentage.id, + } + ) + cls.empty_commission = cls._create_commission("Without commission items") + + cls.product = cls._create_product("Generic product", standard_price=100.0) + cls.partner = cls._create_partner( + "Customer by sections", cls.section_commission + ) + cls.partner_fixed = cls._create_partner( + "Customer by fixed amount", cls.fixed_item.commission_id + ) + cls.partner_percentage = cls._create_partner( + "Customer by percentage", cls.percentage_item.commission_id + ) + cls.partner_without_items = cls._create_partner( + "Customer without commission items", cls.empty_commission + ) + cls.partner_without_agent = cls.env["res.partner"].create( + {"name": "Customer without agent"} + ) + + @classmethod + def _create_commission(cls, name): + return cls.commission_model.create({"name": name, "commission_type": "product"}) + + @classmethod + def _create_product(cls, name, standard_price=0.0): + return cls.product_model.create( + {"name": name, "standard_price": standard_price, "list_price": 0.0} + ) + + @classmethod + def _create_partner(cls, name, commission): + agent = cls.env["res.partner"].create( + { + "name": "Agent of %s" % name, + "agent": True, + "commission_id": commission.id, + } + ) + return cls.env["res.partner"].create( + {"name": name, "agent_ids": [(6, 0, agent.ids)]} + ) + + def _create_sale_order(self, partner, price_unit, product=None, quantity=1.0): + order = self.env["sale.order"].create( + { + "partner_id": partner.id, + "order_line": [ + ( + 0, + 0, + { + "product_id": (product or self.product).id, + "product_uom_qty": quantity, + "price_unit": price_unit, + }, + ) + ], + } + ) + order.recompute_lines_agents() + return order + + def _invoice_sale_order(self, sale_order): + old_invoices = sale_order.invoice_ids + wizard = ( + self.env["sale.advance.payment.inv"] + .with_context( + active_model="sale.order", + active_ids=sale_order.ids, + active_id=sale_order.id, + ) + .create({"advance_payment_method": "delivered"}) + ) + wizard.create_invoices() + return sale_order.invoice_ids - old_invoices + + def _get_commission_amount(self, partner, price_unit, **kwargs): + order = self._create_sale_order(partner, price_unit, **kwargs) + return order.order_line.agent_ids.amount + + def test_section_commission_value(self): + self.assertEqual(self.section_item.commission_type, "section") + self.assertEqual(self.section_item.commission_value, "By sections") + self.assertEqual(self.section_item.name, "All Products") + + def test_section_commission_amount(self): + # (base amount, expected commission amount) + for base_amount, expected_amount in [ + (50.0, 2.5), + (100.0, 5.0), # upper limit of the first section + (100.01, 8.0008), # lower limit of the second section + (200.0, 16.0), + (500.0, 40.0), # upper limit of the second section + (500.01, 50.001), # lower limit of the third section + (1000.0, 100.0), # upper limit of the third section + (1500.0, 0.0), # out of any section + ]: + amount = self._get_commission_amount(self.partner, base_amount) + self.assertAlmostEqual( + amount, expected_amount, places=2, msg="Base amount %s" % base_amount + ) + + def test_section_commission_amount_with_quantity(self): + order = self._create_sale_order(self.partner, 100.0, quantity=3.0) + # 3 * 100 = 300, which is inside the second section: 300 * 8% + self.assertAlmostEqual(order.order_line.agent_ids.amount, 24.0, places=2) + + def test_calculate_section_amount(self): + self.assertAlmostEqual(self.section_item._calculate_section_amount(200.0), 16.0) + # Amounts out of any section have no commission at all + self.assertEqual(self.section_item._calculate_section_amount(1500.0), 0.0) + item_without_sections = self.item_model.create( + { + "commission_id": self.empty_commission.id, + "based_on": "sol", + "applied_on": "3_global", + "commission_type": "section", + } + ) + self.assertEqual(item_without_sections._calculate_section_amount(100.0), 0.0) + + def test_section_commission_amount_from_net_amount(self): + self.section_commission.amount_base_type = "net_amount" + # 600 - 100 (cost) = 500, which is inside the second section: 500 * 8% + amount = self._get_commission_amount(self.partner, 600.0) + self.assertAlmostEqual(amount, 40.0, places=2) + + def test_section_commission_amount_below_cost(self): + self.section_commission.amount_base_type = "net_amount" + # The base is zeroed (50 - 100 < 0), so the commission is zero + amount = self._get_commission_amount(self.partner, 50.0) + self.assertAlmostEqual(amount, 0.0, places=2) + + def test_other_commission_types_are_kept(self): + amount = self._get_commission_amount( + self.partner_fixed, 200.0, product=self.product_fixed + ) + self.assertAlmostEqual(amount, 42.0, places=2) + amount = self._get_commission_amount( + self.partner_percentage, 200.0, product=self.product_percentage + ) + self.assertAlmostEqual(amount, 20.0, places=2) + + def test_commission_without_items(self): + amount = self._get_commission_amount(self.partner_without_items, 200.0) + self.assertAlmostEqual(amount, 0.0, places=2) + + def test_order_line_without_agents(self): + order = self._create_sale_order(self.partner_without_agent, 200.0) + self.assertFalse(order.order_line.agent_ids) + self.assertFalse(order.order_line.agent_ids.amount) + + def test_section_commission_amount_on_invoice(self): + order = self._create_sale_order(self.partner, 200.0) + order.action_confirm() + invoice = self._invoice_sale_order(order) + invoice.recompute_lines_agents() + # The commission of the order line is computed again on the invoice + # line, which is inside the second section: 200 * 8% + self.assertAlmostEqual( + invoice.invoice_line_ids.agent_ids.amount, 16.0, places=2 + ) + + def test_sections_are_copied_with_the_item(self): + new_item = self.section_item.copy() + self.assertNotEqual(new_item.id, self.section_item.id) + self.assertEqual(len(new_item.section_ids), len(self.section_item.section_ids)) + self.assertAlmostEqual( + new_item._calculate_section_amount(200.0), 16.0, places=2 + ) + + def test_sections_are_removed_with_the_item(self): + section_ids = self.section_item.section_ids.ids + self.section_item.unlink() + sections = self.env["commission.section"].browse(section_ids).exists() + self.assertFalse(sections) diff --git a/sale_commission_product_criteria_sections/views/commission_views.xml b/sale_commission_product_criteria_sections/views/commission_views.xml new file mode 100644 index 000000000..4d95be3c4 --- /dev/null +++ b/sale_commission_product_criteria_sections/views/commission_views.xml @@ -0,0 +1,29 @@ + + + + commission.item.form.inherit.sections + commission.item + + + + + + + + + + + + + + + + diff --git a/setup/sale_commission_product_criteria_sections/odoo/addons/sale_commission_product_criteria_sections b/setup/sale_commission_product_criteria_sections/odoo/addons/sale_commission_product_criteria_sections new file mode 120000 index 000000000..926718909 --- /dev/null +++ b/setup/sale_commission_product_criteria_sections/odoo/addons/sale_commission_product_criteria_sections @@ -0,0 +1 @@ +../../../../sale_commission_product_criteria_sections \ No newline at end of file diff --git a/setup/sale_commission_product_criteria_sections/setup.py b/setup/sale_commission_product_criteria_sections/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/sale_commission_product_criteria_sections/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)