273 lines
12 KiB
Python
273 lines
12 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.exceptions import UserError
|
|
from odoo.fields import Domain
|
|
from odoo.tools.misc import get_lang
|
|
|
|
|
|
class HrAppraisalGoal(models.Model):
|
|
_name = 'hr.appraisal.goal'
|
|
_inherit = ['hr.mixin', 'mail.thread', 'mail.activity.mixin']
|
|
_description = "Appraisal Goal"
|
|
_parent_name = 'parent_id'
|
|
_parent_store = True
|
|
|
|
name = fields.Char(required=True)
|
|
employee_ids = fields.Many2many(
|
|
'hr.employee', 'hr_appraisal_goal_hr_employee_rel', 'hr_appraisal_goal_id',
|
|
string="Employee", ondelete='cascade', tracking=True)
|
|
employee_autocomplete_ids = fields.Many2many('hr.employee', compute='_compute_employee_autocomplete', compute_sudo=True)
|
|
company_id = fields.Many2one('res.company', related="employee_ids.company_id", store=True)
|
|
manager_ids = fields.Many2many(
|
|
'hr.employee', 'hr_appraisal_goal_hr_employee_manager_rel', 'hr_appraisal_goal_id',
|
|
string="Manager", compute="_compute_manager_ids", readonly=False, store=True, tracking=True)
|
|
department_ids = fields.Many2many('hr.department', compute="_compute_department_ids", store=True)
|
|
job_ids = fields.Many2many('hr.job', compute="_compute_job_ids", store=True, groups="hr.group_hr_user")
|
|
progression = fields.Selection(selection=[
|
|
('000', '0%'),
|
|
('025', '25%'),
|
|
('050', '50%'),
|
|
('075', '75%'),
|
|
('100', '100%')
|
|
], string="Progress", default="000", tracking=True, required=True, copy=False, compute="_compute_progression",
|
|
store=True, readonly=False, recursive=True)
|
|
description = fields.Html()
|
|
deadline = fields.Date(tracking=True)
|
|
is_manager = fields.Boolean(compute='_compute_is_manager', search='_search_is_manager')
|
|
has_edit_right = fields.Boolean(compute='_compute_has_edit_right')
|
|
tag_ids = fields.Many2many('hr.appraisal.goal.tag', string="Tags")
|
|
template_goal_id = fields.Many2one("hr.appraisal.goal", "Goal Template")
|
|
number_of_sibling_goals = fields.Integer(compute='_compute_number_of_sibling_goals', store=True)
|
|
number_of_completed_sibling_goals = fields.Integer(compute='compute_number_of_completed_sibling_goals', store=True)
|
|
sibling_goals_ratio = fields.Float(compute="_compute_sibling_goals_ratio", store=True)
|
|
# Goal Template
|
|
active = fields.Boolean(default=True)
|
|
parent_path = fields.Char(index=True)
|
|
parent_id = fields.Many2one("hr.appraisal.goal", "Parent Goal Template", index=True, domain=[('employee_ids', '=', False)])
|
|
child_ids = fields.One2many("hr.appraisal.goal", 'parent_id', "Sub Goal Template")
|
|
usual_duration_month = fields.Integer(string="Usual Timing",
|
|
help="The average timing for the goals to be completed. It will populate the estimated end date when assigned")
|
|
|
|
@api.depends_context('uid')
|
|
def _compute_has_edit_right(self):
|
|
# Due to a strange framework behavior the view is not readonly when the user can't write on this model.
|
|
for goal in self:
|
|
goal.has_edit_right = goal.has_access('write')
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('employee_ids')
|
|
def _compute_employee_autocomplete(self):
|
|
self.employee_autocomplete_ids = self.env.user.get_employee_autocomplete_ids()
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('employee_ids')
|
|
def _compute_is_manager(self):
|
|
self.is_manager =\
|
|
self.env.user.has_group('hr_appraisal.group_hr_appraisal_user')\
|
|
or len(self.employee_autocomplete_ids) > 1
|
|
|
|
def _search_is_manager(self, operator, value):
|
|
if operator != 'in':
|
|
return NotImplemented
|
|
if self.env.user.has_group('hr_appraisal.group_hr_appraisal_user'):
|
|
return Domain.TRUE
|
|
return Domain('employee_ids', 'in', self.env.user.get_employee_autocomplete_ids().ids)
|
|
|
|
@api.depends('employee_ids')
|
|
def _compute_manager_ids(self):
|
|
for goal in self:
|
|
goal.manager_ids = goal.employee_ids.parent_id
|
|
|
|
@api.depends('parent_id.child_ids')
|
|
def _compute_number_of_sibling_goals(self):
|
|
for goal in self:
|
|
if not goal.parent_id:
|
|
goal.number_of_sibling_goals = 0
|
|
else:
|
|
goal.number_of_sibling_goals = len(goal.parent_id.child_ids)
|
|
|
|
@api.depends('parent_id.child_ids.progression')
|
|
def compute_number_of_completed_sibling_goals(self):
|
|
for goal in self:
|
|
if not goal.parent_id:
|
|
goal.number_of_completed_sibling_goals = 0
|
|
else:
|
|
goal.number_of_completed_sibling_goals = len(goal.parent_id.child_ids.filtered(lambda goal: goal.progression == '100'))
|
|
|
|
@api.depends('number_of_sibling_goals', 'number_of_completed_sibling_goals')
|
|
def _compute_sibling_goals_ratio(self):
|
|
for goal in self:
|
|
goal.sibling_goals_ratio = 100 * goal.number_of_completed_sibling_goals / goal.number_of_sibling_goals if goal.number_of_sibling_goals else 0
|
|
|
|
@api.depends('child_ids.progression')
|
|
def _compute_progression(self):
|
|
for goal in self:
|
|
if not goal.child_ids:
|
|
continue
|
|
if all(goal_child.progression == '100' for goal_child in goal.child_ids):
|
|
goal.progression = '100'
|
|
else:
|
|
goal.progression = '000'
|
|
|
|
@api.depends("employee_ids.department_id")
|
|
def _compute_department_ids(self):
|
|
for goal in self:
|
|
goal.department_ids = goal.employee_ids.department_id
|
|
|
|
@api.depends("employee_ids.job_id")
|
|
def _compute_job_ids(self):
|
|
for goal in self:
|
|
goal.job_ids = goal.employee_ids.job_id
|
|
|
|
def _notify_by_email_prepare_rendering_context(self, message, msg_vals=False, model_description=False,
|
|
force_email_company=False, force_email_lang=False,
|
|
force_record_name=False):
|
|
render_context = super()._notify_by_email_prepare_rendering_context(
|
|
message, msg_vals=msg_vals, model_description=model_description,
|
|
force_email_company=force_email_company, force_email_lang=force_email_lang,
|
|
force_record_name=force_record_name,
|
|
)
|
|
if self.deadline:
|
|
render_context['subtitles'].append(
|
|
self.env._('Deadline: %s', self.deadline.strftime(get_lang(self.env).date_format)))
|
|
return render_context
|
|
|
|
def action_confirm(self):
|
|
self.write({'progression': '100'})
|
|
|
|
########################################## Goal template ##########################################################
|
|
|
|
def generate_goals(self, employee_ids=[], employee_goal_dict={}):
|
|
if not employee_ids and 'default_employee_id' in self.env.context:
|
|
employee_ids = [self.env.context['default_employee_id']]
|
|
if not employee_ids:
|
|
return
|
|
employee_goals_values = []
|
|
for goal in self:
|
|
for employee_id in employee_ids:
|
|
parent_id = False
|
|
if goal.parent_id and employee_goal_dict.get((goal.parent_id.id, employee_id), False):
|
|
parent_id = employee_goal_dict[goal.parent_id.id, employee_id].id
|
|
employee_goals_values.append(goal._get_goals_values(employee_id, parent_id))
|
|
|
|
employee_goals = self.env['hr.appraisal.goal'].sudo().create(employee_goals_values)
|
|
if self.child_ids:
|
|
employee_goals_by_parent_template_by_employee = employee_goals.grouped(
|
|
lambda goal: (goal.template_goal_id.id, goal.employee_ids.id)
|
|
)
|
|
self.child_ids.generate_goals(employee_ids, employee_goals_by_parent_template_by_employee)
|
|
|
|
def _get_goals_values(self, employee_id, goal_parent_id):
|
|
self.ensure_one()
|
|
return {
|
|
'name': self.name,
|
|
'description': self.description,
|
|
'tag_ids': self.tag_ids.ids,
|
|
'deadline': fields.Date.today() + relativedelta(months=self.usual_duration_month) if self.usual_duration_month else False,
|
|
'employee_ids': [employee_id],
|
|
'parent_id': goal_parent_id,
|
|
'template_goal_id': self.id,
|
|
}
|
|
|
|
def action_save_as_template(self):
|
|
if any(goal.template_goal_id for goal in self):
|
|
raise UserError(self.env._("A goal template already exist for this goal."))
|
|
goal_templates = self.env['hr.appraisal.goal'].create([{
|
|
'name': goal.name,
|
|
'description': goal.description,
|
|
'usual_duration_month': relativedelta(goal.deadline, goal.create_date.date()).months
|
|
if goal.deadline and goal.deadline > goal.create_date.date() else 0,
|
|
'tag_ids': goal.tag_ids,
|
|
} for goal in self])
|
|
for goal_template, goal in zip(goal_templates, self):
|
|
goal.template_goal_id = goal_template
|
|
action = self.env['ir.actions.act_window']._for_xml_id('hr_appraisal.action_hr_appraisal_goal_template')
|
|
if len(goal_templates.ids) == 1:
|
|
action['view_mode'] = 'form'
|
|
action['views'] = [(self.env.ref('hr_appraisal.hr_appraisal_goal_template_view_form').id, 'form')]
|
|
action['res_id'] = goal_templates.id
|
|
action['domain'] = [('id', 'in', goal_templates.ids)]
|
|
return action
|
|
|
|
def action_open_goal_template(self):
|
|
self.ensure_one()
|
|
return {
|
|
'name': self.env._('Goals'),
|
|
'type': 'ir.actions.act_window',
|
|
'view_mode': 'hierarchy,form',
|
|
'res_model': 'hr.appraisal.goal',
|
|
'views': [
|
|
(self.env.ref('hr_appraisal.hr_appraisal_goal_view_hierarchy').id, 'hierarchy'),
|
|
(False, 'form'),
|
|
],
|
|
'domain': [('id', '=', self.id)],
|
|
}
|
|
|
|
@api.model
|
|
def action_select_employees(self):
|
|
model = "hr.employee.public"
|
|
list_id = "hr_appraisal.hr_employee_public_select_from_goal_view_list"
|
|
if self.env['hr.employee'].has_access('read'):
|
|
model = "hr.employee"
|
|
list_id = "hr_appraisal.hr_employee_select_from_goal_view_list"
|
|
return {
|
|
'name': self.env._("Select Employees"),
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': model,
|
|
'views': [
|
|
[self.env.ref(list_id).id, 'list'],
|
|
],
|
|
'domain': [('id', 'in', self.env.user.get_employee_autocomplete_ids().ids)],
|
|
'target': 'new',
|
|
'context': {'goals_ids': self.env.context.get('goals_ids')}
|
|
}
|
|
|
|
######################################### ORM method ##############################################################
|
|
|
|
def action_archive(self):
|
|
super().action_archive()
|
|
if self.child_ids:
|
|
self.child_ids.action_archive()
|
|
|
|
def copy_data(self, default=None):
|
|
vals_list = super().copy_data(default)
|
|
for goal, vals in zip(self, vals_list):
|
|
vals['name'] = self.env._('%s (copy)', goal.name)
|
|
if goal.deadline and goal.deadline < fields.Date.today():
|
|
vals['deadline'] = False
|
|
return vals_list
|
|
|
|
def copy(self, default=None):
|
|
if not self.child_ids:
|
|
return super().copy(default)
|
|
list_of_goals_domain = [
|
|
Domain.AND([
|
|
Domain('id', 'child_of', goal.id),
|
|
Domain('id', '!=', goal.id)
|
|
]) for goal in self
|
|
]
|
|
if len(list_of_goals_domain) == 1:
|
|
domain = list_of_goals_domain
|
|
else:
|
|
domain = Domain.OR(list_of_goals_domain)
|
|
|
|
all_goal_children_ids = self.env['hr.appraisal.goal'].search(domain).ids
|
|
if any(goal.id in all_goal_children_ids for goal in self):
|
|
raise UserError(self.env._('You cannot duplicate at the same time a goal and one of its children.\n'
|
|
'Duplicating a goal will also duplicate its children.'))
|
|
|
|
new_goal_templates = super().copy(default)
|
|
# Build the hierarchy of new goals
|
|
res = self.child_ids.copy(default).grouped('parent_id')
|
|
for old_goal_template, new_goal_template in zip(self, new_goal_templates):
|
|
if res.get(old_goal_template):
|
|
res[old_goal_template].parent_id = new_goal_template.id
|
|
return new_goal_templates
|
|
|
|
def recursive_unlink(self):
|
|
if self.child_ids:
|
|
self.child_ids.recursive_unlink()
|
|
self.exists().unlink()
|