87 lines
3.7 KiB
Python
87 lines
3.7 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
|
|
|
|
class CommissionPlan(models.Model):
|
|
_name = 'commission.plan'
|
|
_description = 'Commission plan'
|
|
|
|
name = fields.Char('Name', required=True)
|
|
active = fields.Boolean(default=True)
|
|
product_id = fields.Many2one(
|
|
'product.product',
|
|
'Purchase Default Product',
|
|
domain=[('purchase_ok', '=', True)],
|
|
default=lambda self: self.env.ref('partner_commission.product_commission'),
|
|
required=True)
|
|
commission_rule_ids = fields.One2many('commission.rule', 'plan_id', 'Rules', copy=True)
|
|
company_id = fields.Many2one('res.company')
|
|
|
|
def _match_rules(self, product, template, pricelist):
|
|
self.ensure_one()
|
|
parent_path = product.categ_id.parent_path
|
|
ancestor_ids = parent_path and parent_path.split('/') or []
|
|
ancestor_ids = [int(cat_id) for cat_id in ancestor_ids if cat_id]
|
|
rules = self.env['commission.rule'].search([
|
|
('plan_id', '=', self.id),
|
|
('category_id', 'in', ancestor_ids),
|
|
'|',
|
|
('product_id', '=', product.id),
|
|
('product_id', '=', False),
|
|
'|',
|
|
('template_id', '=', template),
|
|
('template_id', '=', False),
|
|
'|',
|
|
('pricelist_id', '=', pricelist),
|
|
('pricelist_id', '=', False),
|
|
], order='sequence')
|
|
|
|
for cat_id in reversed(ancestor_ids):
|
|
rule = rules.filtered(lambda r: r.category_id.id == cat_id)
|
|
if rule:
|
|
return rule[0]
|
|
return self.env['commission.rule']
|
|
|
|
|
|
class CommissionRule(models.Model):
|
|
_name = 'commission.rule'
|
|
_description = 'Commission rules management.'
|
|
|
|
plan_id = fields.Many2one('commission.plan', 'Commission Plan', required=True, index=True, ondelete='cascade')
|
|
category_id = fields.Many2one('product.category', 'Product Category', required=True, ondelete='cascade')
|
|
product_id = fields.Many2one(
|
|
'product.product',
|
|
'Product',
|
|
ondelete='cascade',
|
|
help="If set, the rule does not apply to the whole category but only on the given product.\n"
|
|
"The product must belong to the selected category.\n"
|
|
"Use several rules if you need to match multiple products within a category.")
|
|
template_id = fields.Many2one('sale.order.template', 'Sale Order Template', ondelete="cascade")
|
|
pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', ondelete="cascade")
|
|
rate = fields.Float('Rate', required=True, default=0)
|
|
is_capped = fields.Boolean('Capped', required=True, default=False, help='Whether the commission is capped.')
|
|
max_commission = fields.Float('Max Commission', help="Maximum amount, specified in the currency of the pricelist, if given.")
|
|
sequence = fields.Integer(string='Sequence')
|
|
|
|
_check_rate = models.Constraint(
|
|
'CHECK(rate >= 0 AND rate <= 100)',
|
|
'Rate should be between 0 and 100.',
|
|
)
|
|
_check_combination_unique_index = models.UniqueIndex(
|
|
"(plan_id, category_id, COALESCE(product_id, -1), COALESCE(template_id, -1), COALESCE(pricelist_id, -1))"
|
|
)
|
|
|
|
@api.constrains('product_id', 'category_id')
|
|
def _check_product_category(self):
|
|
for rule in self:
|
|
if rule.product_id and rule.product_id.categ_id != rule.category_id:
|
|
raise ValidationError(_('Product %(product)s does not belong to category %(category)s', product=rule.product_id.code, category=rule.category_id.name))
|
|
|
|
@api.onchange('is_capped')
|
|
def _onchange_is_capped(self):
|
|
if not self.is_capped:
|
|
self.max_commission = 0
|