614 lines
32 KiB
Python
614 lines
32 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from dateutil.relativedelta import relativedelta
|
|
import datetime
|
|
import logging
|
|
|
|
from odoo import api, fields, models
|
|
|
|
from odoo.exceptions import UserError
|
|
from odoo.tools import convert
|
|
from odoo.tools.misc import format_date
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class HrAppraisal(models.Model):
|
|
_name = 'hr.appraisal'
|
|
_inherit = ['hr.mixin', 'mail.thread', 'mail.activity.mixin']
|
|
_description = "Employee Appraisal"
|
|
_order = 'state, date_close, id desc'
|
|
_rec_name = 'employee_id'
|
|
_mail_post_access = 'read'
|
|
|
|
def _get_default_employee(self):
|
|
if self.env.context.get('active_model') in ('hr.employee', 'hr.employee.public') and 'active_id' in self.env.context:
|
|
return self.env.context.get('active_id')
|
|
elif self.env.context.get('active_model') == 'res.users' and 'active_id' in self.env.context:
|
|
return self.env['res.users'].browse(self.env.context['active_id']).employee_id
|
|
if not self.env.user.has_group('hr_appraisal.group_hr_appraisal_user'):
|
|
return self.env.user.employee_id
|
|
|
|
active = fields.Boolean(default=True)
|
|
employee_id = fields.Many2one(
|
|
'hr.employee', required=True, string='Employee', index=True,
|
|
default=_get_default_employee, ondelete='cascade')
|
|
employee_user_id = fields.Many2one('res.users', string="Employee User", related='employee_id.user_id')
|
|
company_id = fields.Many2one('res.company', related='employee_id.company_id', store=True)
|
|
department_id = fields.Many2one(
|
|
'hr.department', compute='_compute_department_id', string='Department', store=True)
|
|
job_id = fields.Many2one('hr.job', compute='_compute_job_id', string='Job', store=True)
|
|
image_128 = fields.Image(related='employee_id.image_128')
|
|
image_1920 = fields.Image(related='employee_id.image_1920')
|
|
avatar_128 = fields.Image(related='employee_id.avatar_128')
|
|
avatar_1920 = fields.Image(related='employee_id.avatar_1920')
|
|
last_appraisal_id = fields.Many2one('hr.appraisal', related='employee_id.last_appraisal_id')
|
|
employee_appraisal_count = fields.Integer(related='employee_id.appraisal_count')
|
|
uncomplete_goals_count = fields.Integer(related='employee_id.uncomplete_goals_count')
|
|
appraisal_template_id = fields.Many2one('hr.appraisal.template', string="Appraisal Template", compute="_compute_appraisal_template",
|
|
check_company=True, store=True, readonly=False, domain="[('department_ids', 'in', [department_id, False])]")
|
|
employee_feedback_template = fields.Html(compute='_compute_feedback_templates', translate=True)
|
|
manager_feedback_template = fields.Html(compute='_compute_feedback_templates', translate=True)
|
|
|
|
date_close = fields.Date(
|
|
string='Appraisal Date', help='Closing date of the current appraisal', required=True, index=True,
|
|
default=lambda self: datetime.date.today() + relativedelta(months=+1))
|
|
next_appraisal_date = fields.Date(related="employee_id.next_appraisal_date",
|
|
help='Date where the new appraisal will be automatically created', readonly=False)
|
|
state = fields.Selection(
|
|
[('1_new', 'Draft'),
|
|
('2_pending', 'Ongoing'),
|
|
('3_done', 'Done')],
|
|
string='Status', tracking=True, required=True, copy=False,
|
|
default='1_new', index=True, group_expand=True)
|
|
manager_ids = fields.Many2many(
|
|
'hr.employee', 'appraisal_manager_rel', 'hr_appraisal_id',
|
|
default=lambda self: self.env.user.employee_id,
|
|
context={'active_test': False},
|
|
domain="[('id', '!=', employee_id), ('active', '=', 'True'), '|', ('company_id', '=', False), ('company_id', 'in', allowed_company_ids)]")
|
|
is_manager = fields.Boolean(compute='_compute_user_manager_rights')
|
|
employee_autocomplete_ids = fields.Many2many('hr.employee', compute='_compute_employee_autocomplete', compute_sudo=True)
|
|
waiting_feedback = fields.Boolean(
|
|
string="Waiting Feedback from Employee/Managers", compute='_compute_waiting_feedback')
|
|
employee_feedback = fields.Html(compute='_compute_employee_feedback', store=True, readonly=False, groups="hr_appraisal.group_hr_appraisal_user")
|
|
accessible_employee_feedback = fields.Html(compute='_compute_accessible_employee_feedback', inverse="_inverse_accessible_employee_feedback")
|
|
show_employee_feedback_full = fields.Boolean(compute='_compute_show_employee_feedback_full')
|
|
manager_feedback = fields.Html(compute='_compute_manager_feedback', store=True, readonly=False, groups="hr_appraisal.group_hr_appraisal_user")
|
|
accessible_manager_feedback = fields.Html(compute='_compute_accessible_manager_feedback', inverse="_inverse_accessible_manager_feedback")
|
|
show_manager_feedback_full = fields.Boolean(compute='_compute_show_manager_feedback_full')
|
|
employee_feedback_published = fields.Boolean(string="Employee Feedback Published", default=True, tracking=True,
|
|
help="If greened, the manager will be able to see and edit your feedback. Otherwise, your feedback is blurred and visible only to you.")
|
|
manager_feedback_published = fields.Boolean(string="Manager Feedback Published", default=True, tracking=True,
|
|
help="If greened, the employee will be able to see your feedback. Otherwise, your feedback is blurred and visible only to you.")
|
|
can_see_employee_publish = fields.Boolean(compute='_compute_buttons_display')
|
|
can_see_manager_publish = fields.Boolean(compute='_compute_buttons_display')
|
|
assessment_note = fields.Many2one('hr.appraisal.note', string="Final Rating", help="This field is not visible to the Employee.", check_company=True)
|
|
note = fields.Html(string="Private Note")
|
|
appraisal_plan_posted = fields.Boolean()
|
|
appraisal_properties = fields.Properties("Properties", definition="department_id.appraisal_properties_definition", precompute=False)
|
|
duplicate_appraisal_id = fields.Many2one('hr.appraisal', compute='_compute_duplicate_appraisal_id', export_string_translation=False, store=False)
|
|
|
|
@api.depends('employee_id', 'manager_ids')
|
|
def _compute_duplicate_appraisal_id(self):
|
|
"""
|
|
Note: We only care for duplicate_appraisal_id when we create a
|
|
new appraisal. This field is currently only used in form view
|
|
and for performance reasons it should rather stay that way.
|
|
"""
|
|
ongoing_appraisals = self.search([
|
|
('state', 'in', ['1_new', '2_pending']),
|
|
('employee_id', 'in', self.employee_id.ids),
|
|
('manager_ids', 'in', self.manager_ids.ids),
|
|
], order='date_close')
|
|
self.duplicate_appraisal_id = False
|
|
for appraisal in self:
|
|
if appraisal.id or appraisal.state != '1_new':
|
|
continue
|
|
for ongoing_appraisal in ongoing_appraisals:
|
|
if ongoing_appraisals.manager_ids == appraisal.manager_ids._origin\
|
|
and ongoing_appraisal.employee_id == appraisal.employee_id\
|
|
and ongoing_appraisal.id != appraisal.id:
|
|
appraisal.duplicate_appraisal_id = ongoing_appraisal.id
|
|
break
|
|
|
|
@api.depends('employee_id.department_id', 'state')
|
|
def _compute_department_id(self):
|
|
for appraisal in self.filtered(lambda a: a.state == '1_new'):
|
|
appraisal.department_id = appraisal.employee_id.department_id
|
|
|
|
@api.depends('employee_id.job_id', 'state')
|
|
def _compute_job_id(self):
|
|
for appraisal in self.filtered(lambda a: a.state == '1_new'):
|
|
appraisal.job_id = appraisal.employee_id.job_id
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('employee_id', 'manager_ids')
|
|
def _compute_buttons_display(self):
|
|
new_appraisals = self.filtered(lambda a: a.state == '1_new')
|
|
new_appraisals.update({
|
|
'can_see_employee_publish': False,
|
|
'can_see_manager_publish': False,
|
|
})
|
|
user_employees = self.env.user.employee_ids
|
|
is_manager = self.env.user.has_group('hr_appraisal.group_hr_appraisal_user')
|
|
for appraisal in self:
|
|
user_employee_in_appraisal_manager = bool(set(user_employees.ids) & set(appraisal.manager_ids.ids))
|
|
# Appraisal manager can edit feedback in draft state
|
|
appraisal.can_see_employee_publish = appraisal.employee_id in user_employees or \
|
|
(user_employee_in_appraisal_manager and appraisal.state == '1_new')
|
|
appraisal.can_see_manager_publish = user_employee_in_appraisal_manager
|
|
for appraisal in self - new_appraisals:
|
|
if is_manager and not appraisal.can_see_employee_publish and not appraisal.can_see_manager_publish:
|
|
appraisal.can_see_employee_publish, appraisal.can_see_manager_publish = True, True
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('manager_ids', 'employee_id', 'employee_id.parent_id')
|
|
def _compute_employee_autocomplete(self):
|
|
self.employee_autocomplete_ids = self.env.user.get_employee_autocomplete_ids()
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('manager_ids', 'employee_id', 'employee_id.parent_id')
|
|
def _compute_user_manager_rights(self):
|
|
for appraisal in self:
|
|
appraisal.is_manager =\
|
|
self.env.user.has_group('hr_appraisal.group_hr_appraisal_user')\
|
|
or self.env.user.employee_ids in (appraisal.manager_ids | appraisal.employee_id.parent_id)
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('employee_id', 'employee_feedback_published')
|
|
def _compute_show_employee_feedback_full(self):
|
|
for appraisal in self:
|
|
is_appraisee = appraisal.employee_id.user_id == self.env.user
|
|
appraisal.show_employee_feedback_full = is_appraisee and not appraisal.employee_feedback_published
|
|
|
|
@api.depends_context('uid')
|
|
@api.depends('manager_ids', 'manager_feedback_published')
|
|
def _compute_show_manager_feedback_full(self):
|
|
for appraisal in self:
|
|
is_appraiser = self.env.user in appraisal.manager_ids.user_id
|
|
appraisal.show_manager_feedback_full = is_appraiser and not appraisal.manager_feedback_published
|
|
|
|
@api.depends('department_id', 'appraisal_template_id')
|
|
def _compute_employee_feedback(self):
|
|
for appraisal in self.filtered(lambda a: a.state in ['1_new', '2_pending']):
|
|
employee_template = appraisal._get_appraisal_template('employee')
|
|
if appraisal.state == '1_new':
|
|
appraisal.employee_feedback = employee_template
|
|
else:
|
|
appraisal.employee_feedback = appraisal.employee_feedback or employee_template
|
|
|
|
@api.depends('department_id', 'appraisal_template_id')
|
|
def _compute_manager_feedback(self):
|
|
for appraisal in self.filtered(lambda a: a.state in ['1_new', '2_pending']):
|
|
manager_template = appraisal._get_appraisal_template('manager')
|
|
if appraisal.state == '1_new':
|
|
appraisal.manager_feedback = manager_template
|
|
else:
|
|
appraisal.manager_feedback = appraisal.manager_feedback or manager_template
|
|
|
|
@api.depends('department_id', 'company_id', 'appraisal_template_id')
|
|
def _compute_feedback_templates(self):
|
|
for appraisal in self:
|
|
appraisal.employee_feedback_template = appraisal._get_appraisal_template('employee')
|
|
appraisal.manager_feedback_template = appraisal._get_appraisal_template('manager')
|
|
|
|
@api.depends('department_id', 'company_id')
|
|
def _compute_appraisal_template(self):
|
|
all_department_template_ids = self.env['hr.appraisal.template'].search(
|
|
[('department_ids', '=', False), ('company_id', 'in', self.company_id.ids + [False])])
|
|
for appraisal in self:
|
|
# Keep the current template only when it belongs to the appraisal's
|
|
# department; otherwise prefer the department's own template and fall
|
|
# back to a generic one. This prevents a generic template that was
|
|
# auto-selected before the department was known from sticking, while
|
|
# still preserving a deliberate template chosen for that department.
|
|
current_template = appraisal.appraisal_template_id \
|
|
if appraisal.appraisal_template_id in appraisal.department_id.appraisal_template_ids else False
|
|
appraisal.appraisal_template_id = current_template or \
|
|
appraisal.department_id.appraisal_template_ids[:1] or \
|
|
all_department_template_ids.filtered(lambda t: t.company_id.id in [appraisal.company_id.id, False])[:1]
|
|
|
|
@api.depends('employee_feedback_published', 'manager_feedback_published')
|
|
def _compute_waiting_feedback(self):
|
|
for appraisal in self:
|
|
appraisal.waiting_feedback = not appraisal.employee_feedback_published or not appraisal.manager_feedback_published
|
|
|
|
@api.depends('employee_id', 'date_close')
|
|
@api.depends_context('include_date_in_name')
|
|
def _compute_display_name(self):
|
|
if not self.env.context.get('include_date_in_name'):
|
|
return super()._compute_display_name()
|
|
for appraisal in self:
|
|
appraisal.display_name = self.env._(
|
|
"Appraisal for %(employee)s on %(date)s",
|
|
employee=appraisal.employee_id.name, date=appraisal.date_close)
|
|
|
|
@api.onchange('employee_id')
|
|
def _onchange_employee_id(self):
|
|
self = self.sudo() # fields are not on the employee public
|
|
if self.employee_id:
|
|
manager = self.employee_id.parent_id or self.env.user.employee_id
|
|
self.manager_ids = manager if manager != self.employee_id else False
|
|
# Allow indirect managers to request appraisals for employees
|
|
if self.env.user.employee_id != self.employee_id and not self.env.user.has_group('hr_appraisal.group_hr_appraisal_user'):
|
|
self.manager_ids |= self.env.user.employee_id
|
|
self.department_id = self.employee_id.department_id
|
|
|
|
def subscribe_employees(self):
|
|
for appraisal in self:
|
|
partners_ids = (appraisal.manager_ids.sudo().related_partner_id + appraisal.employee_id.sudo().related_partner_id).ids
|
|
appraisal.message_subscribe(partner_ids=partners_ids)
|
|
|
|
def send_appraisal(self):
|
|
# TDE FIXME: probably some cleanup to do here
|
|
for appraisal in self:
|
|
confirmation_mail_template = appraisal.company_id.appraisal_confirm_mail_template
|
|
mapped_data = {
|
|
**{appraisal.employee_id: confirmation_mail_template},
|
|
**{manager: confirmation_mail_template for manager in appraisal.manager_ids}
|
|
}
|
|
for employee, mail_template in mapped_data.items():
|
|
if not employee.work_email or not self.env.user.email or not mail_template:
|
|
continue
|
|
ctx = {
|
|
'employee_to_name': appraisal.employee_id.name,
|
|
'recipient_users': employee.user_id,
|
|
'url': '/mail/view?model=%s&res_id=%s' % ('hr.appraisal', appraisal.id),
|
|
}
|
|
mail_template = mail_template.with_context(**ctx)
|
|
subject = mail_template._render_field('subject', appraisal.ids)[appraisal.id]
|
|
body = mail_template._render_field('body_html', appraisal.ids)[appraisal.id]
|
|
# post the message
|
|
mail_values = {
|
|
'email_from': self.env.user.email_formatted,
|
|
'author_id': self.env.user.partner_id.id,
|
|
'model': None,
|
|
'res_id': None,
|
|
'subject': subject,
|
|
'body_html': body,
|
|
'auto_delete': True,
|
|
'email_to': employee.work_email
|
|
}
|
|
mail_values['body_html'] = self.env['mail.render.mixin']._render_encapsulate(
|
|
'mail.mail_notification_light', mail_values['body_html'],
|
|
context_record=appraisal,
|
|
add_context={
|
|
'record_name': self.env._("Appraisal Request"),
|
|
},
|
|
)
|
|
self.env['mail.mail'].sudo().create(mail_values)
|
|
|
|
from_cron = 'from_cron' in self.env.context
|
|
# When cron creates appraisal, it creates specific activities
|
|
# In this case, no need to create activities, not to be repetitive
|
|
if employee.user_id and not from_cron:
|
|
appraisal.activity_schedule(
|
|
'mail.mail_activity_data_todo', appraisal.date_close,
|
|
summary=self.env._('Appraisal Form to Fill'),
|
|
note=self.env._('Fill appraisal for %s', appraisal.employee_id._get_html_link()),
|
|
user_id=employee.user_id.id)
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
today = fields.Date.today()
|
|
for vals in vals_list:
|
|
self._ensure_min_next_appraisal_date(vals, today)
|
|
appraisals = super().create(vals_list)
|
|
appraisals_to_send = self.env['hr.appraisal']
|
|
for appraisal, vals in zip(appraisals, vals_list):
|
|
if vals.get('state') and vals['state'] == '2_pending':
|
|
appraisals_to_send |= appraisal
|
|
if vals.get('state') and vals['state'] == '1_new':
|
|
appraisal.employee_id.sudo().write({
|
|
'last_appraisal_id': appraisal.id,
|
|
})
|
|
appraisals_to_send.send_appraisal()
|
|
# TDE FIXME: check if we can use suggested recipients instead (master)
|
|
appraisals.subscribe_employees()
|
|
return appraisals
|
|
|
|
@api.model
|
|
def _ensure_min_next_appraisal_date(self, vals, min_date):
|
|
next_appraisal_date = vals.get('next_appraisal_date')
|
|
if not next_appraisal_date:
|
|
return
|
|
if isinstance(next_appraisal_date, str):
|
|
next_appraisal_date = fields.Date.from_string(next_appraisal_date)
|
|
vals['next_appraisal_date'] = max(min_date, next_appraisal_date)
|
|
|
|
@api.depends('employee_feedback', 'can_see_employee_publish', 'employee_feedback_published')
|
|
def _compute_accessible_employee_feedback(self):
|
|
for appraisal in self:
|
|
if appraisal.can_see_employee_publish or appraisal.employee_feedback_published:
|
|
appraisal.accessible_employee_feedback = appraisal.sudo().employee_feedback
|
|
else:
|
|
appraisal.accessible_employee_feedback = self.env._("Unpublished")
|
|
|
|
def _inverse_accessible_employee_feedback(self):
|
|
for appraisal in self:
|
|
if appraisal.can_see_employee_publish:
|
|
appraisal.sudo().employee_feedback = appraisal.accessible_employee_feedback
|
|
else:
|
|
raise UserError(self.env._('The employee feedback cannot be changed by managers.'))
|
|
|
|
@api.depends('manager_feedback', 'can_see_manager_publish', 'manager_feedback_published')
|
|
def _compute_accessible_manager_feedback(self):
|
|
for appraisal in self:
|
|
if appraisal.can_see_manager_publish or appraisal.manager_feedback_published:
|
|
appraisal.accessible_manager_feedback = appraisal.sudo().manager_feedback
|
|
else:
|
|
appraisal.accessible_manager_feedback = self.env._("Unpublished")
|
|
|
|
def _inverse_accessible_manager_feedback(self):
|
|
for appraisal in self:
|
|
if appraisal.can_see_manager_publish:
|
|
appraisal.sudo().manager_feedback = appraisal.accessible_manager_feedback
|
|
else:
|
|
raise UserError(self.env._('The manager feedback cannot be changed by an employee.'))
|
|
|
|
def _get_appraisal_template(self, template):
|
|
self.ensure_one()
|
|
if not self.appraisal_template_id:
|
|
return False
|
|
if template == 'employee':
|
|
return self.appraisal_template_id.appraisal_employee_feedback_template
|
|
else:
|
|
return self.appraisal_template_id.appraisal_manager_feedback_template
|
|
|
|
def _find_previous_appraisals(self):
|
|
result = {}
|
|
all_appraisals = self.env['hr.appraisal'].search([
|
|
('employee_id', 'in', self.mapped('employee_id').ids),
|
|
], order='employee_id, id desc')
|
|
for appraisal in self:
|
|
previous_appraisals = all_appraisals.filtered(lambda x: x.employee_id == appraisal.employee_id and x.id != appraisal.id and x.create_date < appraisal.create_date)
|
|
if previous_appraisals:
|
|
result[appraisal.id] = previous_appraisals[0]
|
|
return result
|
|
|
|
def write(self, vals):
|
|
if 'manager_feedback_published' in vals and not all(a.can_see_manager_publish for a in self):
|
|
raise UserError(self.env._('The "Manager Feedback Published" cannot be changed by an employee.'))
|
|
|
|
self._ensure_min_next_appraisal_date(vals, fields.Date.today())
|
|
force_published = self.env['hr.appraisal']
|
|
if vals.get('employee_feedback_published'):
|
|
user_employees = self.env.user.employee_ids
|
|
force_published = self.filtered(lambda a: (a.is_manager) and not (a.employee_feedback_published or a.employee_id in user_employees))
|
|
if vals.get('state') in ['2_pending', '3_done']:
|
|
self.activity_ids.action_feedback()
|
|
not_done_appraisal = self.env['hr.appraisal']
|
|
for appraisal in self:
|
|
appraisal.employee_id.sudo().write({
|
|
'last_appraisal_id': appraisal.id,
|
|
})
|
|
if appraisal.state != '3_done':
|
|
not_done_appraisal |= appraisal
|
|
if vals.get('state') == '2_pending':
|
|
vals['employee_feedback_published'] = False
|
|
vals['manager_feedback_published'] = False
|
|
not_done_appraisal.send_appraisal()
|
|
else:
|
|
vals['employee_feedback_published'] = True
|
|
vals['manager_feedback_published'] = True
|
|
self._appraisal_plan_post()
|
|
if self.env.user.partner_id.email_formatted:
|
|
body = self.env._("The appraisal's status has been set to Done by %s", self.env.user.name)
|
|
self.message_notify(
|
|
body=body,
|
|
subject=self.env._("Your Appraisal has been completed"),
|
|
partner_ids=appraisal.message_partner_ids.ids,
|
|
)
|
|
self.message_post(body=body)
|
|
result = super().write(vals)
|
|
if force_published:
|
|
for appraisal in force_published:
|
|
role = self.env._('Manager') if self.env.user.employee_id in appraisal.manager_ids else self.env._('Appraisal Officer')
|
|
appraisal.message_post(body=self.env._('%(user)s decided, as %(role)s, to publish the employee\'s feedback', user=self.env.user.name, role=role))
|
|
return result
|
|
|
|
def unlink(self):
|
|
previous_appraisals = self._find_previous_appraisals()
|
|
for appraisal in self:
|
|
# If current appraisal is the last_appraisal_id for the employee we should update last_appraisal_id bfore deleting
|
|
if appraisal.employee_id and appraisal.employee_id.last_appraisal_id == appraisal:
|
|
previous_appraisal = previous_appraisals.get(appraisal.id)
|
|
appraisal.employee_id.sudo().write({
|
|
'last_appraisal_id': previous_appraisal.id if previous_appraisal else False,
|
|
})
|
|
return super(HrAppraisal, self).unlink()
|
|
|
|
def _appraisal_plan_post(self):
|
|
odoobot = self.env.ref('base.partner_root')
|
|
dates = self.employee_id.sudo()._upcoming_appraisal_creation_date()
|
|
for appraisal in self:
|
|
# The only ongoing appraisal is the current one
|
|
if not appraisal.appraisal_plan_posted and appraisal.company_id.appraisal_plan and appraisal.employee_id.sudo().ongoing_appraisal_count == 1:
|
|
date = dates[appraisal.employee_id.id]
|
|
formated_date = format_date(self.env, date, date_format="MMM d y")
|
|
body = self.env._('Thanks to your Appraisal Plan, without any new manual Appraisal, the new Appraisal will be automatically created on %s.', formated_date)
|
|
appraisal._message_log(body=body, author_id=odoobot.id)
|
|
appraisal.appraisal_plan_posted = True
|
|
|
|
def _generate_activities(self):
|
|
today = fields.Date.today()
|
|
for appraisal in self:
|
|
employee = appraisal.employee_id
|
|
managers = appraisal.manager_ids
|
|
last_appraisal_date = employee.last_appraisal_id.date_close
|
|
last_appraisal_months = last_appraisal_date and (
|
|
today.year - last_appraisal_date.year) * 12 + (today.month - last_appraisal_date.month)
|
|
if employee.user_id:
|
|
# an appraisal has been just created
|
|
if employee.appraisal_count == 1:
|
|
months = (appraisal.date_close.year - employee.create_date.year) * \
|
|
12 + (appraisal.date_close.month - employee.create_date.month)
|
|
note = self.env._("You arrived %s months ago. Your appraisal is created and you can fill it here.", months)
|
|
else:
|
|
note = self.env._("Your last appraisal was %s months ago. Your appraisal is created and you can fill it here.", last_appraisal_months)
|
|
appraisal.with_context(mail_activity_quick_update=True).activity_schedule(
|
|
'mail.mail_activity_data_todo', today,
|
|
summary=self.env._('Appraisal to fill'),
|
|
note=note, user_id=employee.user_id.id)
|
|
for manager in managers.filtered('user_id'):
|
|
if employee.appraisal_count == 1:
|
|
note = self.env._(
|
|
"The employee %(employee)s arrived %(months)s months ago. The appraisal is created and you can fill it here.",
|
|
employee=employee._get_html_link(), months=months)
|
|
else:
|
|
note = self.env._(
|
|
"The last appraisal of %(employee)s was %(months)s months ago. The appraisal is created and you can fill it here.",
|
|
employee=appraisal.employee_id._get_html_link(), months=last_appraisal_months)
|
|
appraisal.with_context(mail_activity_quick_update=True).activity_schedule(
|
|
'mail.mail_activity_data_todo', today,
|
|
summary=self.env._('Appraisal for %s to fill', employee.name),
|
|
note=note, user_id=manager.user_id.id)
|
|
|
|
@api.ondelete(at_uninstall=False)
|
|
def _unlink_if_new_or_cancel(self):
|
|
if any(appraisal.state != '1_new' for appraisal in self):
|
|
raise UserError(self.env._("Oops! You can only delete draft appraisals."))
|
|
|
|
def read(self, fields=None, load='_classic_read'):
|
|
fields_set = set(fields) if fields is not None else set()
|
|
check_feedback = fields_set & {'manager_feedback', 'employee_feedback'}
|
|
check_notes = fields_set & {'note', 'assessment_note'}
|
|
if check_feedback:
|
|
fields = fields + ['can_see_employee_publish', 'can_see_manager_publish', 'employee_feedback_published', 'manager_feedback_published']
|
|
if check_notes:
|
|
fields = fields + ['employee_id']
|
|
records = super().read(fields, load)
|
|
if check_notes:
|
|
for appraisal in records:
|
|
if appraisal['employee_id'] == self.env.user.employee_id.id:
|
|
appraisal['note'] = self.env._('Note')
|
|
appraisal['assessment_note'] = False
|
|
return records
|
|
|
|
def action_calendar_event(self):
|
|
self.ensure_one()
|
|
partners = self.manager_ids.work_contact_id | self.employee_id.work_contact_id | self.env.user.partner_id
|
|
action = self.env["ir.actions.actions"]._for_xml_id("calendar.action_calendar_event")
|
|
action['context'] = {
|
|
'default_partner_ids': partners.ids,
|
|
'default_res_model': 'hr.appraisal',
|
|
'default_res_id': self.id,
|
|
'default_name': self.env._('Appraisal of %s', self.employee_id.name),
|
|
'initial_date': self.date_close,
|
|
}
|
|
action['domain'] = [('partner_ids', 'in', partners.ids)]
|
|
return action
|
|
|
|
def action_confirm(self):
|
|
self.state = '2_pending'
|
|
|
|
def action_done(self):
|
|
appraisals_with_rating = self.filtered('assessment_note')
|
|
appraisals_with_rating.state = '3_done'
|
|
if self - appraisals_with_rating:
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': 'Required:',
|
|
'message': self.env._('Please set a final rating to validate these appraisals:\n- %s',
|
|
'\n- '.join((self - appraisals_with_rating).mapped('display_name'))),
|
|
'type': 'danger',
|
|
'sticky': True
|
|
}
|
|
}
|
|
|
|
def action_back(self):
|
|
self.state = '1_new'
|
|
self.assessment_note = False
|
|
|
|
def action_reopen(self):
|
|
self.state = '2_pending'
|
|
|
|
def action_open_employee_appraisals(self):
|
|
view_id = self.env.ref('hr_appraisal.hr_appraisal_view_tree_orderby_create_date').id
|
|
return {
|
|
'name': self.env._('Previous Appraisals'),
|
|
'res_model': 'hr.appraisal',
|
|
'view_mode': 'list,kanban,form,gantt,calendar,activity',
|
|
'views': [(view_id, 'list'), (False, 'kanban'), (False, 'form'), (False, 'gantt'), (False, 'calendar'), (False, 'activity')],
|
|
'domain': [('employee_id', '=', self.employee_id.ids)],
|
|
'type': 'ir.actions.act_window',
|
|
'target': 'current',
|
|
'context': {
|
|
'search_default_groupby_date_close': True,
|
|
}
|
|
}
|
|
|
|
def action_open_goals(self):
|
|
self.ensure_one()
|
|
return {
|
|
'name': self.env._("%s's Goals", self.employee_id.name),
|
|
'view_mode': 'kanban,list,form,graph',
|
|
'res_model': 'hr.appraisal.goal',
|
|
'type': 'ir.actions.act_window',
|
|
'views': [
|
|
(self.env.ref('hr_appraisal.hr_appraisal_goal_view_tree').id, 'list'),
|
|
(False, 'kanban'),
|
|
(self.env.ref('hr_appraisal.hr_appraisal_goal_view_form').id, 'form'),
|
|
(False, 'graph'),
|
|
],
|
|
'target': 'current',
|
|
'domain': [('employee_ids', '=', self.employee_id.id), ('child_ids', '=', False)],
|
|
'context': {'default_employee_ids': self.employee_id.ids},
|
|
}
|
|
|
|
def action_send_appraisal_request(self):
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'view_mode': 'form',
|
|
'res_model': 'request.appraisal',
|
|
'target': 'new',
|
|
'name': self.env._('Appraisal Request'),
|
|
'context': {'default_appraisal_id': self.id},
|
|
}
|
|
|
|
def action_open_appraisal_campaign_wizard(self):
|
|
employee_ids = False
|
|
if self.env.context.get('active_model') == 'hr.employee':
|
|
employee_ids = self.env.context.get('active_ids')
|
|
elif self.env.context.get('active_model') == 'hr.appraisal':
|
|
appraisals = self.env['hr.appraisal'].browse(self.env.context.get('active_ids'))
|
|
employee_ids = appraisals.employee_id.ids
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': self.env._('Appraisal Campaign'),
|
|
'res_model': 'hr.appraisal.campaign.wizard',
|
|
'view_mode': 'form',
|
|
'context': {
|
|
'default_mode': 'employee',
|
|
'default_employee_ids': employee_ids,
|
|
},
|
|
'target': 'new',
|
|
}
|
|
|
|
@api.model
|
|
def has_demo_data(self):
|
|
if not self.env.user.has_group("hr_appraisal.group_hr_appraisal_user"):
|
|
return True
|
|
# This record only exists if the scenario has been already launched
|
|
goal_tag = self.env.ref('hr_appraisal.hr_appraisal_goal_tag_softskills', raise_if_not_found=False)
|
|
if goal_tag:
|
|
return True
|
|
return bool(self.env['ir.module.module'].search_count([
|
|
'&',
|
|
('state', 'in', ['installed', 'to upgrade', 'uninstallable']),
|
|
('demo', '=', True)
|
|
]))
|
|
|
|
def _load_demo_data(self):
|
|
if self.has_demo_data():
|
|
return
|
|
env_sudo = self.sudo().with_context({}).env
|
|
env_sudo['hr.employee']._load_scenario()
|
|
convert.convert_file(env_sudo, 'hr_appraisal', 'data/scenarios/hr_appraisal_scenario.xml', None, mode='init')
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'reload',
|
|
}
|