Files

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."))