78 lines
3.7 KiB
Python
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]
|