Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions sale_commission_product_criteria_sections/README.rst
Original file line number Diff line number Diff line change
@@ -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 <https://github.com/OCA/commission/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/commission/issues/new?body=module:%20sale_commission_product_criteria_sections%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
~~~~~~~

* Madooit

Contributors
~~~~~~~~~~~~

* `Madooit <https://github.com/madooit>`__:

* Rodrigo A. Madureira <https://github.com/rodmad85>

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 <https://odoo-community.org/page/maintainer-role>`__:

|maintainer-rodmad85|

This module is part of the `OCA/commission <https://github.com/OCA/commission/tree/16.0/sale_commission_product_criteria_sections>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
4 changes: 4 additions & 0 deletions sale_commission_product_criteria_sections/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Copyright 2025 Madooit
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from . import models
20 changes: 20 additions & 0 deletions sale_commission_product_criteria_sections/__manifest__.py
Original file line number Diff line number Diff line change
@@ -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",
],
}
7 changes: 7 additions & 0 deletions sale_commission_product_criteria_sections/models/__init__.py
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
)
51 changes: 51 additions & 0 deletions sale_commission_product_criteria_sections/models/commission.py
Original file line number Diff line number Diff line change
@@ -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,
)
Original file line number Diff line number Diff line change
@@ -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)
Original file line number Diff line number Diff line change
@@ -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
)
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
* `Madooit <https://github.com/madooit>`__:

* Rodrigo A. Madureira <https://github.com/rodmad85>
Original file line number Diff line number Diff line change
@@ -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.
17 changes: 17 additions & 0 deletions sale_commission_product_criteria_sections/readme/USAGE.rst
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading