Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ADD] sale_commission: Automate sales commission calculation #558

Closed
wants to merge 1 commit into from
Closed
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
1 change: 1 addition & 0 deletions sale_commission/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import models
16 changes: 16 additions & 0 deletions sale_commission/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
'name': 'Sale Commission',
'version': '1.0',
'category': 'Sales/Commission',
'summary': "Manage your salespersons' commissions",
'depends': ['sale_management'],
'data': [
'demo/commission_rule.xml',
'views/commission_rule_views.xml',
'report/commission_report.xml',
'views/commission_menu.xml',
'security/ir.model.access.csv',
],
'installable': True,
'license': 'AGPL-3',
}
44 changes: 44 additions & 0 deletions sale_commission/demo/commission_rule.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="commission_rule_1" model="commission.rule">
<field name="rate">0.5</field>
<field name="due_at">invoicing</field>
<field name="commission_for">person</field>
<field name="user_id" ref="base.user_admin"/>
<field name="product_expired">no_impact</field>
</record>

<record id="commission_rule_2" model="commission.rule">
<field name="rate">0.3</field>
<field name="due_at">payment</field>
<field name="commission_for">team</field>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="product_expired">no_impact</field>
</record>

<record id="commission_rule_3" model="commission.rule">
<field name="rate">0.7</field>
<field name="due_at">invoicing</field>
<field name="commission_for">person</field>
<field name="product_category_id" ref="product.product_category_all"/>
<field name="user_id" ref="base.user_admin"/>
<field name="product_expired">no_impact</field>
</record>

<record id="commission_rule_4" model="commission.rule">
<field name="product_category_id" ref="product.product_category_consumable"/>
<field name="rate">0.4</field>
<field name="due_at">payment</field>
<field name="commission_for">team</field>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="product_expired">no_impact</field>
</record>

<record id="commission_rule_5" model="commission.rule">
<field name="rate">0.2</field>
<field name="due_at">invoicing</field>
<field name="commission_for">person</field>
<field name="user_id" ref="base.user_admin"/>
<field name="product_expired">no_impact</field>
</record>
</odoo>
3 changes: 3 additions & 0 deletions sale_commission/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from . import account_move
from . import commission_rule
from . import commission_rule_line
72 changes: 72 additions & 0 deletions sale_commission/models/account_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from odoo import models


class AccountMove(models.Model):
_inherit = 'account.move'

def _post(self, soft=True):
"""Overrides invoice posting to trigger commission calculation."""
res = super()._post(soft)

self.filtered(
lambda m:
m.move_type == 'out_invoice'
and (m.invoice_user_id or m.team_id)
)._create_commission_rule_lines('invoicing')

return res

def action_register_payment(self):
"""Overrides payment registration to trigger commision calculation."""
res = super().action_register_payment()
commission_lines = self._get_applicable_commission()
self.filtered(
lambda m: (
m.move_type == 'out_invoice'
and (m.invoice_user_id or m.team_id)
and m not in commission_lines.move_id
)
)._create_commission_rule_lines('payment')
return res

def _get_applicable_commission(self):
"""Check if a commission entry already exists for recordset."""
return self.env['commission.rule.line'].search_fetch(
[('move_id', 'in', self.ids)],
['move_id']
)

def _create_commission_rule_lines(self, trigger_stage):
for move in self:
commission_rules = self.env['commission.rule'].search([
('due_at', '=', trigger_stage),
('product_id', 'in', move.invoice_line_ids.product_id.ids + [False]),
('product_category_id', 'in', move.invoice_line_ids.product_id.categ_id.ids + [False]),
('user_id', 'in', [move.invoice_user_id.id ,False]),
('team_id', 'in', [move.invoice_user_id.sale_team_id.id, False]),
])

person_rule = self.env['commission.rule']
team_rule = self.env['commission.rule']

for rule in commission_rules:
if rule.commission_for == 'person' and not person_rule:
person_rule = rule
if rule.commission_for == 'team' and not team_rule:
team_rule = rule
if person_rule and team_rule:
break

commission_rule_lines = [{
'date': move.invoice_date,
'user_id': rule.user_id.id,
'team_id': rule.team_id.id,
'move_id': move.id,
'amount': move.amount_total * (rule.rate),
'currency_id': move.currency_id.id,
'commission_rule_id' : rule.id,
}
for rule in (person_rule, team_rule)
]
if commission_rule_lines:
self.env['commission.rule.line'].create(commission_rule_lines)
67 changes: 67 additions & 0 deletions sale_commission/models/commission_rule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from odoo import _, api, fields, models


class CommissionRule(models.Model):
_name = 'commission.rule'
_description = "Commission Rule"

sequence = fields.Integer('Sequence', default=1, help="Used to order commission rule.")
rate = fields.Float(string="Commission Rate", required=True)
commission_for = fields.Selection(
selection=[
('person', "Salesperson"),
('team', "Sales Team")
],
string="Commission for",
required=True,
default='person'
)
due_at = fields.Selection(
selection=[
('invoicing', "Invoicing"),
('payment', "Payment"),
],
string="Due at",
required=True,
)
product_expired = fields.Selection(
selection=[
('no_impact', "No Impact"),
('yes', "Yes"),
('no', "No")
],
string="Product Expired",
required=True
)
max_discount = fields.Float(string="Max Discount")
on_fast_payment = fields.Boolean(string="On Fast Payment")
fast_payment_days = fields.Integer(string="Before Days")
display_name = fields.Char(string="Condition", compute="_compute_display_name", store=True)

product_category_id = fields.Many2one('product.category', string="Product Category")
product_id = fields.Many2one('product.product', string="Product")
user_id = fields.Many2one('res.users', string="Salesperson")
team_id = fields.Many2one('crm.team', string="Sales Team")

@api.depends('product_category_id', 'product_id', 'team_id', 'user_id')
def _compute_display_name(self):
"""Computes the display name based on selected fields in 'Apply On'."""
_ = self.env._
fields_mapping = (
(_("Category"), 'product_category_id'),
(_("Product"), 'product_id'),
(_("Salesperson"), 'user_id'),
(_("Sales Team"), 'team_id')
)
for rule in self:
conditions = [
_("%(display_name)s: %(value)s", display_name=display_name, value=rule[fname].name)
for display_name, fname in fields_mapping
if rule[fname]
]
if rule.max_discount:
conditions.append(_("Max Discount: %s", rule.max_discount))
if rule.product_expired:
conditions.append(_("Product Expired: %s", rule.product_expired))

rule.display_name = _(" AND ").join(conditions) if conditions else _("No Conditions")
14 changes: 14 additions & 0 deletions sale_commission/models/commission_rule_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from odoo import fields, models


class CommissionRuleLine(models.Model):
_name = 'commission.rule.line'
_description = "Commission Rule Line"

date = fields.Date(string="Date", default=fields.Date.today)
user_id = fields.Many2one('res.users', string="User")
team_id = fields.Many2one('crm.team', string="Sales Team")
move_id = fields.Many2one('account.move', string="Invoice")
currency_id = fields.Many2one('res.currency', default=lambda self: self.move_id.company_id.currency_id)
commission_rule_id = fields.Many2one('commission.rule', string="Rule")
amount = fields.Monetary("Amount", required=True, currency_field='currency_id')
85 changes: 85 additions & 0 deletions sale_commission/report/commission_report.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="sale_commission_report_view_list" model="ir.ui.view">
<field name="name">sale.commission.report.view.list</field>
<field name="model">commission.rule.line</field>
<field name="arch" type="xml">
<list create="0" editable="top">
<field name="date"/>
<field name="user_id"/>
<field name="team_id" string="Team"/>
<field name="move_id"/>
<field name="amount"/>
<field name="currency_id" column_invisible="True"/>
</list>
</field>
</record>

<record id="sale_commission_report_view_graph" model="ir.ui.view">
<field name="name">sale.commission.report.view.graph</field>
<field name="model">commission.rule.line</field>
<field name="arch" type="xml">
<graph>
<field name="date" type="col"/>
<field name="user_id" type="row"/>
<field name="amount" type="measure"/>
</graph>
</field>
</record>

<record id="sale_commission_report_view_pivot" model="ir.ui.view">
<field name="name">sale.commission.report.view.pivot</field>
<field name="model">commission.rule.line</field>
<field name="arch" type="xml">
<pivot>
<field name="date" type="col"/>
<field name="user_id" type="row"/>
<field name="team_id" type="row"/>
<field name="amount" type="measure"/>
</pivot>
</field>
</record>

<record id="sale_commission_report_view_search" model="ir.ui.view">
<field name="name">sale.commission.report.view.search</field>
<field name="model">commission.rule.line</field>
<field name="arch" type="xml">
<search string="Commission Report">
<field name="user_id"/>
<field name="team_id"/>
<field name="commission_rule_id"/>
<filter string="My commission" name="my"
domain="[('user_id', '=', uid)]"/>
<filter string="Date" name="filter_date"
date="date"/>

<group string="Group By">
<filter string="Salesperson"
name="group_by_salesperson"
context="{'group_by':'user_id'}"/>
<filter string="Sales Team"
name="group_by_salesteam"
context="{'group_by':'team_id'}"/>
<filter string="Date"
name="group_by_date"
context="{'group_by':'date'}"/>
</group>
</search>
</field>
</record>

<record id="sale_commission_action_report" model="ir.actions.act_window">
<field name="name">Commissions</field>
<field name="res_model">commission.rule.line</field>
<field name="view_mode">list,graph,pivot</field>
<field name="context">{'search_default_my': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_neutral_face">
Unfortunately, there are no commissions for you
</p>
<p>
Ensure you are assigned to a commission rule and have made sales that qualify for commissions
</p>
</field>
</record>
</odoo>
3 changes: 3 additions & 0 deletions sale_commission/security/ir.model.access.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_commission_rule,access.commission.rule,sale_commission.model_commission_rule,sales_team.group_sale_manager,1,1,1,1
access_commission_rule_line,access.commission.rule.line,sale_commission.model_commission_rule_line,sales_team.group_sale_manager,1,1,1,1
14 changes: 14 additions & 0 deletions sale_commission/views/commission_menu.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<menuitem
id="menu_sale_commission_rule"
name="Commission Rules"
parent="sale.menu_sale_config"
sequence="20"
action="sale_commission.sale_commission_rule"/>
<menuitem
id="menu_sale_commission_report"
name="Sales Commissions"
parent="sale.menu_sale_report"
action="sale_commission.sale_commission_action_report"/>
</odoo>
63 changes: 63 additions & 0 deletions sale_commission/views/commission_rule_views.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="sale_commission_rule_view_form" model="ir.ui.view">
<field name="name">sale.commission.rule.view.form</field>
<field name="model">commission.rule</field>
<field name="arch" type="xml">
<form string="Commission Rule">
<sheet>
<group>
<group>
<field name="rate" widget="percentage"/>
<field name="due_at"/>
</group>
<group>
<field name="commission_for"/>
</group>
</group>
<group string="Apply on">
<group>
<field name="product_category_id" placeholder="All"/>
<field name="product_id" placeholder="All"/>
<field name="product_expired"/>
<field name="max_discount" widget="percentage"/>
</group>
<group>
<label for="on_fast_payment" string="On Fast Payment"/>
<div>
<field name="on_fast_payment" class="oe_inline"/>
<span invisible="not on_fast_payment">
<label for="fast_payment_days" string="Before" class="o_light_label"/>
<field name="fast_payment_days" class="oe_inline" nolabel="1"/>
Days
</span>
</div>
<field name="user_id" placeholder="All" invisible="commission_for != 'person'"/>
<field name="team_id" placeholder="All" invisible="commission_for != 'team'"/>
</group>
</group>
</sheet>
</form>
</field>
</record>

<record id="sale_commission_rule_view_list" model="ir.ui.view">
<field name="name">sale.commission.rule.view.list</field>
<field name="model">commission.rule</field>
<field name="arch" type="xml">
<list string="Commission Rules">
<field name="sequence" widget="handle"/>
<field name="due_at"/>
<field name="rate" string="Rate" widget="percentage"/>
<field name="commission_for"/>
<field name="display_name"/>
</list>
</field>
</record>

<record id="sale_commission_rule" model="ir.actions.act_window">
<field name="name">Commission Rules</field>
<field name="res_model">commission.rule</field>
<field name="view_mode">list,form</field>
</record>
</odoo>