Files
enterprise-addons/sale_commission/model/commission_plan_target.py
T

78 lines
3.7 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from collections import defaultdict
from odoo import api, models, fields
from odoo.exceptions import ValidationError
class SaleCommissionPlanTarget(models.Model):
_name = 'sale.commission.plan.target'
_description = 'Commission Plan Target'
_order = 'id'
plan_id = fields.Many2one('sale.commission.plan', ondelete='cascade', index='btree_not_null')
name = fields.Char("Period", required=True, readonly=True)
date_from = fields.Date("From", required=True, readonly=True, index=True)
date_to = fields.Date("To", required=True, readonly=True, index=True)
payment_date = fields.Date(compute='_compute_payment_date', store=True, readonly=False, index=True)
amount = fields.Monetary("Target", default=0, required=True, currency_field='currency_id')
payment_amount = fields.Monetary(compute='_compute_payment_amount', currency_field='currency_id', store=True,
help="Sum of amounts paid on the same payment date")
currency_id = fields.Many2one('res.currency', related='plan_id.currency_id')
@api.constrains('plan_id', 'date_from', 'date_to')
def _constrains_overlapping_dates(self):
targets_by_plans = self._read_group(
domain=[('plan_id', 'in', self.plan_id.ids)],
groupby=['plan_id'],
aggregates=['id:recordset'],
)
for plan_id, target_ids in targets_by_plans:
if len(target_ids) < 2:
continue
target_ids = target_ids.sorted(lambda t: (t.date_from, t.date_to))
for idx in range(len(target_ids) - 1):
current = target_ids[idx]
next_one = target_ids[idx + 1]
if current.date_from >= current.date_to:
raise ValidationError(self.env._(
"The start date must be before the end date.\nPeriod: %s"
) % current.name)
# Overlap detection: sorting can be indeterministic when the same date_from is used
if current.date_to >= next_one.date_from:
raise ValidationError(self.env._(
"Overlapping periods detected for plan '%(plan)s':\n"
"- %(c_name)s [%(c_from)s → %(c_to)s]\n"
"- %(n_name)s [%(n_from)s → %(n_to)s]",
plan=plan_id.display_name,
c_name=current.name,
c_from=current.date_from,
c_to=current.date_to,
n_name=next_one.name,
n_from=next_one.date_from,
n_to=next_one.date_to,
))
@api.depends('date_to')
def _compute_payment_date(self):
for target in self:
if not target.payment_date:
target.payment_date = target.date_to
@api.depends('payment_date', 'amount', 'plan_id')
def _compute_payment_amount(self):
""" Recompute the sum of amounts of the records sharing the same payment_date
"""
amount_per_payment_date = defaultdict(list)
total_per_date = defaultdict(float)
# We need to group all related targets
for target in self.plan_id.target_ids:
# plan_id is used to avoid mixing targets from different plans
key = (target.plan_id, target.payment_date)
amount_per_payment_date[key].append(target.id)
total_per_date[key] += target.amount
for keys, target_ids in amount_per_payment_date.items():
targets = self.browse(target_ids)
targets.payment_amount = total_per_date[keys]