188 lines
9.9 KiB
Python
188 lines
9.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import datetime
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.exceptions import ValidationError, UserError
|
|
|
|
|
|
class HrEmployee(models.Model):
|
|
_inherit = "hr.employee"
|
|
|
|
next_appraisal_date = fields.Date(
|
|
string='Next Appraisal Date', compute='_compute_next_appraisal_date', groups="hr.group_hr_user", readonly=False, store=True, copy=False,
|
|
help="The date of the next appraisal is computed by the appraisal plan's dates (first appraisal + periodicity).")
|
|
last_ongoing_appraisal_date = fields.Date(compute='_compute_last_ongoing_appraisal_date', groups="hr.group_hr_user")
|
|
is_last_appraisal_late = fields.Boolean(compute='_compute_last_ongoing_appraisal_date', groups="hr.group_hr_user")
|
|
related_partner_id = fields.Many2one('res.partner', compute='_compute_related_partner', groups="hr.group_hr_user")
|
|
ongoing_appraisal_count = fields.Integer(compute='_compute_ongoing_appraisal_count', store=True)
|
|
appraisal_count = fields.Integer(compute='_compute_appraisal_count', store=True, groups="hr.group_hr_user")
|
|
uncomplete_goals_count = fields.Integer(compute='_compute_uncomplete_goals_count', groups="hr.group_hr_user")
|
|
goals_count = fields.Integer(compute='_compute_goals_count', groups="hr.group_hr_user")
|
|
appraisal_ids = fields.One2many('hr.appraisal', 'employee_id', groups="hr.group_hr_user")
|
|
can_request_appraisal = fields.Boolean(compute='_compute_can_request_appraisal')
|
|
goals_ids = fields.Many2many(
|
|
'hr.appraisal.goal', 'hr_appraisal_goal_hr_employee_rel', 'hr_employee_id',
|
|
string="Goals", groups="hr.group_hr_user")
|
|
parent_user_id = fields.Many2one(related='parent_id.user_id', string="Parent User")
|
|
last_appraisal_id = fields.Many2one('hr.appraisal')
|
|
last_appraisal_state = fields.Selection(related='last_appraisal_id.state')
|
|
|
|
def _get_appraisal_plan_starting_date(self):
|
|
self.ensure_one()
|
|
return self.contract_date_start or self.date_version
|
|
|
|
def action_send_appraisal_request(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'view_mode': 'form',
|
|
'res_model': 'hr.appraisal',
|
|
'name': 'Appraisal Request',
|
|
'context': self.env.context,
|
|
}
|
|
|
|
def action_open_last_appraisal(self):
|
|
self.ensure_one()
|
|
employee_appraisals = self.with_context(active_test=False).appraisal_ids
|
|
opened_appraisals = employee_appraisals.filtered(lambda a: a.state in ['1_new', '2_pending'])
|
|
done_appraisals = employee_appraisals.filtered(lambda a: a.state == '3_done')
|
|
relevant_appraisals = employee_appraisals
|
|
if opened_appraisals:
|
|
relevant_appraisals = opened_appraisals
|
|
elif done_appraisals:
|
|
relevant_appraisals = done_appraisals[0]
|
|
if len(relevant_appraisals) == 1:
|
|
return {
|
|
'view_mode': 'form',
|
|
'res_model': 'hr.appraisal',
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'current',
|
|
'res_id': relevant_appraisals.id,
|
|
}
|
|
else:
|
|
return {
|
|
'view_mode': 'list',
|
|
'name': self.env._('New and Pending Appraisals'),
|
|
'res_model': 'hr.appraisal',
|
|
"views": [[self.env.ref('hr_appraisal.view_hr_appraisal_tree').id, "list"], [False, "form"]],
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'current',
|
|
'domain': [('id', 'in', relevant_appraisals.ids)],
|
|
}
|
|
|
|
def _compute_can_request_appraisal(self):
|
|
children_ids = self.env.user.get_employee_autocomplete_ids().ids
|
|
for employee in self.sudo():
|
|
# Since this function is used in both private and public employees, to check if an employee is in the list
|
|
# we need to check by their id, which is the same in corresponding private and public employees.
|
|
employee.can_request_appraisal = employee.id in children_ids
|
|
|
|
@api.constrains('next_appraisal_date')
|
|
def _check_next_appraisal_date(self):
|
|
today = fields.Date.today()
|
|
if not self.env.context.get('install_mode') and any(employee.next_appraisal_date and employee.next_appraisal_date < today for employee in self):
|
|
raise ValidationError(self.env._("You cannot set 'Next Appraisal Date' in the past."))
|
|
|
|
def _compute_related_partner(self):
|
|
for rec in self:
|
|
rec.related_partner_id = rec.user_id.partner_id or rec.work_contact_id
|
|
|
|
@api.depends('appraisal_ids')
|
|
def _compute_appraisal_count(self):
|
|
read_group_result = self.env['hr.appraisal'].with_context(active_test=False)._read_group([('employee_id', 'in', self.ids)], ['employee_id'], ['__count'])
|
|
result = {employee.id: count for employee, count in read_group_result}
|
|
for employee in self:
|
|
employee.appraisal_count = result.get(employee.id, 0)
|
|
|
|
@api.depends('appraisal_ids.state')
|
|
def _compute_ongoing_appraisal_count(self):
|
|
read_group_result = self.env['hr.appraisal'].with_context(active_test=False)._read_group([('employee_id', 'in', self.ids), ('state', 'in', ['1_new', '2_pending'])], ['employee_id'], ['__count'])
|
|
result = {employee.id: count for employee, count in read_group_result}
|
|
for employee in self:
|
|
employee.ongoing_appraisal_count = result.get(employee.id, 0)
|
|
|
|
def _compute_uncomplete_goals_count(self):
|
|
read_group_result = self.env['hr.appraisal.goal']._read_group([('employee_ids', 'in', self.ids), ('progression', '!=', '100'), ('child_ids', '=', False)], ['employee_ids'], ['__count'])
|
|
result = {employee.id: count for employee, count in read_group_result}
|
|
for employee in self:
|
|
employee.uncomplete_goals_count = result.get(employee.id, 0)
|
|
|
|
def _compute_goals_count(self):
|
|
read_group_result = self.env['hr.appraisal.goal']._read_group([('employee_ids', 'in', self.ids), ('child_ids', '=', False)], ['employee_ids'], ['__count'])
|
|
result = {employee.id: count for employee, count in read_group_result}
|
|
for employee in self:
|
|
employee.goals_count = result.get(employee.id, 0)
|
|
|
|
@api.depends('ongoing_appraisal_count', 'company_id.appraisal_plan', 'company_id.duration_after_recruitment', 'company_id.duration_first_appraisal', 'company_id.duration_next_appraisal')
|
|
def _compute_next_appraisal_date(self):
|
|
self.filtered('ongoing_appraisal_count').next_appraisal_date = False
|
|
employees_without_appraisal = self.filtered(lambda e: e.ongoing_appraisal_count == 0 and e.company_id.appraisal_plan and e.active)
|
|
dates = employees_without_appraisal._upcoming_appraisal_creation_date()
|
|
for employee in employees_without_appraisal:
|
|
employee.next_appraisal_date = dates[employee.id]
|
|
|
|
@api.depends('appraisal_ids.state', 'appraisal_ids.date_close')
|
|
def _compute_last_ongoing_appraisal_date(self):
|
|
for employee in self:
|
|
ongoing_appraisals = employee.appraisal_ids.filtered(lambda appraisal: appraisal.state in ['1_new', '2_pending'])
|
|
if ongoing_appraisals:
|
|
employee.last_ongoing_appraisal_date = max(ongoing_appraisals.mapped('date_close'))
|
|
employee.is_last_appraisal_late = employee.last_ongoing_appraisal_date < fields.Date.today()
|
|
else:
|
|
employee.last_ongoing_appraisal_date = False
|
|
employee.is_last_appraisal_late = False
|
|
|
|
def _upcoming_appraisal_creation_date(self):
|
|
today = fields.Date.today()
|
|
dates = {}
|
|
for employee in self:
|
|
if employee.appraisal_count == 0:
|
|
months = employee.company_id.duration_after_recruitment
|
|
starting_date = employee._get_appraisal_plan_starting_date() or today
|
|
else:
|
|
months = employee.company_id.duration_first_appraisal if employee.appraisal_count == 1 else employee.company_id.duration_next_appraisal
|
|
starting_date = employee.last_appraisal_id.date_close
|
|
|
|
if starting_date:
|
|
# In case proposed next_appraisal_date is in the past, start counting from now
|
|
starting_date = starting_date.date() if isinstance(starting_date, datetime.datetime) else starting_date
|
|
original_next_appraisal_date = starting_date + relativedelta(months=months)
|
|
dates[employee.id] = original_next_appraisal_date if original_next_appraisal_date >= today else today + relativedelta(months=months)
|
|
else:
|
|
dates[employee.id] = today + relativedelta(months=months)
|
|
return dates
|
|
|
|
def action_open_goals(self):
|
|
self.ensure_one()
|
|
action = self.env["ir.actions.actions"]._for_xml_id('hr_appraisal.action_hr_appraisal_goal')
|
|
action.update({
|
|
'domain': [('employee_ids', '=', self.id), ('child_ids', '=', False)],
|
|
'context': {'default_employee_ids': self.ids},
|
|
})
|
|
return action
|
|
|
|
def action_open_employee_appraisals(self):
|
|
self.ensure_one()
|
|
if self.appraisal_count == 1 and self.appraisal_ids:
|
|
return {
|
|
'res_model': 'hr.appraisal',
|
|
'view_mode': 'form',
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'current',
|
|
'res_id': self.appraisal_ids[0].id,
|
|
}
|
|
# Reuse the action in hr.appraisal to open the employee's previous appraisals
|
|
return self.appraisal_ids[:1].action_open_employee_appraisals()
|
|
|
|
def action_archive(self):
|
|
res = super().action_archive()
|
|
self.next_appraisal_date = False
|
|
return res
|
|
|
|
@api.ondelete(at_uninstall=False)
|
|
def _unlink_expect_goal_manager(self):
|
|
is_goal_manager = self.env['hr.appraisal.goal'].search_count([('manager_ids', 'in', self.ids)])
|
|
if is_goal_manager:
|
|
raise UserError(self.env._("You cannot delete an employee who is a goal's manager, archive it instead."))
|