from odoo import api, fields, models, _ from odoo.exceptions import ValidationError RATING_VALUATION = { '1': 'unsatisfactory', '2': 'needs_improvements', '3': 'meet_expectation', '4': 'exceed_expectation', '5': 'outstanding', } class EmployeeAppraisal(models.Model): _name = 'employee.appraisal.template.config' _description = 'Employee Appraisal' _inherit = ['mail.thread', 'mail.activity.mixin'] _order = "tracking_date desc" _rec_name = 'template_id' name = fields.Char(string="Reference", copy=False) employee_evaluator_name_id = fields.Many2one('employee.appraisal.evaluator', string="Employee Appraisal Evaluator") hr_apprai_id = fields.Many2one('hr.employee') hr_head_apprai_id = fields.Many2one('hr.employee') managerapp_id = fields.Many2one('hr.employee', string='Manager') # managerapp_id = fields.Many2one('hr.employee', compute='_compute_managerapp_id', string='Manager') performance_evaluator = fields.Selection([('manager', 'Manager'), ('colleague', 'Colleague'), ('own', 'Own')]) template_id = fields.Many2one('employee.appraisal.template', string="Template") stage_id = fields.Many2one('employee.stage.config', string='Stage') stage_name = fields.Char(related='stage_id.name', store=True) available_stage_ids = fields.Many2many('employee.stage.config', compute='_compute_available_stages') tracking_date = fields.Datetime(default=fields.Datetime.now, readonly=True, index=True) company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company) state = fields.Selection([ ('new', 'New'), # ('draft', 'Draft'), ('self_evaluation', 'Self Evaluation'), ('colleague_manager', 'colleague Feedback & Manager Evaluation'), # ('in_progress', 'Manager Evaluation'), ('hr_evaluation', 'HR Evaluation'), ('finance_team', 'Finance Evaluation'), ('management_team', 'Management Evaluation'), ('done', 'Completed'), ('reject', 'Rejected') ], string='Status', copy=False, tracking=1, default='new') appraisal_period_id = fields.Many2one('employee.appraisal.year') employee_appraisal_id = fields.Many2one('hr.employee') image_1920 = fields.Image(related='employee_appraisal_id.image_1920') employee_code = fields.Char(string='Employee Code', related='employee_appraisal_id.employee_id', store=True) department_appraisal_id = fields.Many2one("hr.department", string="Department", related="employee_appraisal_id.department_id", store=True) job_appraisal_id = fields.Many2one("hr.job", string="Job Position", related="employee_appraisal_id.job_id", store=True) kra_line_ids = fields.One2many('employee.appraisal.kra.line', 'config_id', string='Kra') manager_remarks = fields.Char(string="Manager Remarks") hr_remarks = fields.Char(string="HR Remarks") hr_head_remarks = fields.Text(string="HR Head Remarks") finance_head_remarks = fields.Text(string="Finance Head Remarks") colleague_feed_ids = fields.One2many('colleague.feedback', 'employee_appraisal_feed_id', 'Colleague Feed Back') created_by_id = fields.Many2one('hr.employee', string="Created By", default=lambda self: self.env.user.employee_id, readonly=True) created_user_id = fields.Many2one('res.users', default=lambda self: self.env.user, readonly=True) creator_email = fields.Char(related='created_by_id.work_email', string="Creator Email", readonly=True) notice_id = fields.Many2one('hr.notice.appraisal', string="Notice") start_date = fields.Date(string="Start Date") end_date = fields.Date(string="End Date") college_end_date = fields.Datetime(string="Colleges End Date") college_end_date_time = fields.Datetime(string="Colleague End Date") is_readonly = fields.Boolean(compute="_compute_is_readonly") mail_sent_employee_ids = fields.Many2many('hr.employee', string="Mail Sent Employees") seq = fields.Char(string="Sequence", readonly=True, copy=False) employee_ids = fields.Many2many('hr.employee', 'appraisal_config_employee_rel', 'config_id', 'employee_id', string="Employees") manager_ids = fields.Many2many('hr.employee', 'appraisal_config_manager_rel', 'config_id', 'manager_id', string="Managers") total_employee_score = fields.Float(string="Employee Total Points", compute="_compute_total_scores", store=True) total_manager_score = fields.Float(string="Manager Total Points", compute="_compute_total_scores", store=True) total_hr_score = fields.Float(string="HR Total Points", compute="_compute_total_scores", store=True) manager_email = fields.Char(compute="_compute_manager_email", string="Manager Email") Note_appraisal = fields.Char(string="User Note", default="Please click KRA's Name, To open the KPI's", Readonly=True) overall_score = fields.Float(compute="_compute_overall_scores", store=True) overall_rating = fields.Selection([ ('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ], string="Overall Rating") overall_rating_value = fields.Selection([ ('unsatisfactory', 'Unsatisfactory'), ('needs_improvements', 'Needs Improvements'), ('meet_expectation', 'Meets Expectation'), ('exceed_expectation', 'Exceeds Expectation'), ('outstanding', 'Outstanding'), ], string="Overall Rating Value") overall_rating_star = fields.Selection([ ('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ], string="Overall Stars") employee_overall_score = fields.Float(compute='_compute_overall_scores', store=True) manager_overall_score = fields.Float(compute='_compute_overall_scores', store=True) hr_overall_score = fields.Float(compute='_compute_overall_scores', store=True) employee_overall_star = fields.Selection([ ('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ], compute='_compute_overall_scores', store=True) manager_overall_star = fields.Selection([ ('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ], compute='_compute_overall_scores', store=True) hr_overall_star = fields.Selection([ ('0', '0'), ('1', '1'), ('2', '2'), ('3', '3'), ('4', '4'), ('5', '5'), ], compute='_compute_overall_scores', store=True) finance_user_id = fields.Many2one('res.users', string="Finance Approved By", readonly=True) currency_id = fields.Many2one('res.currency',string='Currency',default=lambda self: self.env.company.currency_id.id) current_salary = fields.Monetary(string="Current Salary",related="employee_appraisal_id.contract_id.wage",currency_field='currency_id',store=True,readonly=True,) appraisal_percentage = fields.Float(string="Appraisal %") appraisal_amount = fields.Float(string="Appraisal Amount") new_salary = fields.Float(string="Revised Salary", compute="_compute_new_salary", store=True) finance_remarks = fields.Text(string="Finance Remarks") finance_date = fields.Datetime(string="Finance Approval Date", readonly=True) salary_update = fields.Boolean(string="Salary Update",default=False,readonly=True) contract_count = fields.Integer(string="Contracts",compute="_compute_contract_count") template_empl_rating_bool = fields.Boolean('Star Rating') template_empl_point_bool = fields.Boolean('Point Rating') disciplinary_ids = fields.Many2many( 'hr.employee.disciplinary',string='Disciplinary Actions',compute='_compute_disciplinary_ids') achievement_summary = fields.Html(string="Achievement Summary") employee_comments = fields.Text(string="Employee Comments") attachment_ids = fields.Many2many( 'ir.attachment', 'employee_appraisal_attachment_rel', 'appraisal_id', 'attachment_id', string='Supporting Documents' ) stage_color_class = fields.Char(compute="_compute_stage_color_class") invite_pip = fields.Boolean('Invite Pip',default=False) is_manager_reviewer = fields.Boolean(compute="_compute_user_roles",store=False) is_hr_reviewer = fields.Boolean(compute="_compute_user_roles",store=False) is_current_employee = fields.Boolean(compute='_compute_is_current_employee') @api.depends('employee_appraisal_id') def _compute_is_current_employee(self): current_employee = self.env.user.employee_id for rec in self: rec.is_current_employee = ( rec.employee_appraisal_id == current_employee ) def _compute_user_roles(self): current_user = self.env.user for rec in self: rec.is_manager_reviewer = ( rec.managerapp_id.user_id.id == current_user.id ) rec.is_hr_reviewer = ( rec.hr_apprai_id.user_id.id == current_user.id ) @api.depends('stage_id') def _compute_stage_color_class(self): for rec in self: if rec.stage_id.colour_seq == 1: rec.stage_color_class = 'success' elif rec.stage_id.colour_seq == 2: rec.stage_color_class = 'info' elif rec.stage_id.colour_seq == 3: rec.stage_color_class = 'warning' elif rec.stage_id.colour_seq == 4: rec.stage_color_class = 'primary' else: rec.stage_color_class = 'danger' @api.depends('employee_appraisal_id') def _compute_disciplinary_ids(self): for rec in self: rec.disciplinary_ids = self.env['hr.employee.disciplinary'].search([ ('employee_id', '=', rec.employee_appraisal_id.id) ]) @api.depends('template_id') def _compute_available_stages(self): for rec in self: rec.available_stage_ids = rec.template_id.stage_config_ids @api.depends( 'kra_line_ids.kpi_line_ids.rating_star', 'kra_line_ids.kpi_line_ids.manager_rating_star', 'kra_line_ids.kpi_line_ids.hr_rating_star', 'kra_line_ids.kpi_line_ids.employee_score', 'kra_line_ids.kpi_line_ids.manager_score', 'kra_line_ids.kpi_line_ids.hr_score', ) def _compute_overall_scores(self): for rec in self: employee_score = 0.0 manager_score = 0.0 hr_score = 0.0 # ================================== # STAR RATING MODE # ================================== if rec.template_empl_rating_bool: employee_ratings = [ int(kpi.rating_star) for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.rating_star ] manager_ratings = [ int(kpi.manager_rating_star) for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.manager_rating_star ] hr_ratings = [ int(kpi.hr_rating_star) for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.hr_rating_star ] employee_score = ( sum(employee_ratings) / len(employee_ratings) if employee_ratings else 0 ) manager_score = ( sum(manager_ratings) / len(manager_ratings) if manager_ratings else 0 ) hr_score = ( sum(hr_ratings) / len(hr_ratings) if hr_ratings else 0 ) rec.employee_overall_score = round(employee_score, 2) rec.manager_overall_score = round(manager_score, 2) rec.hr_overall_score = round(hr_score, 2) rec.employee_overall_star = str(round(employee_score)) if employee_score else '0' rec.manager_overall_star = str(round(manager_score)) if manager_score else '0' rec.hr_overall_star = str(round(hr_score)) if hr_score else '0' # Final Overall overall_avg = ( employee_score + manager_score + hr_score ) / 3 if (employee_score or manager_score or hr_score) else 0 rec.overall_score = round(overall_avg, 2) overall_star = str(round(overall_avg)) if overall_avg else '0' rec.overall_rating = overall_star rec.overall_rating_star = overall_star rec.overall_rating_value = RATING_VALUATION.get( overall_star, False ) # ================================== # POINT RATING MODE # ================================== elif rec.template_empl_point_bool: employee_scores = [ kpi.employee_score for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.employee_score is not False ] manager_scores = [ kpi.manager_score for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.manager_score is not False ] hr_scores = [ kpi.hr_score for kra in rec.kra_line_ids for kpi in kra.kpi_line_ids if kpi.hr_score is not False ] employee_score = ( sum(employee_scores) / len(employee_scores) if employee_scores else 0 ) manager_score = ( sum(manager_scores) / len(manager_scores) if manager_scores else 0 ) hr_score = ( sum(hr_scores) / len(hr_scores) if hr_scores else 0 ) rec.employee_overall_score = round(employee_score, 2) rec.manager_overall_score = round(manager_score, 2) rec.hr_overall_score = round(hr_score, 2) rec.employee_overall_star = '0' rec.manager_overall_star = '0' rec.hr_overall_star = '0' # Final Overall overall_avg = ( employee_score + manager_score + hr_score ) / 3 if (employee_score or manager_score or hr_score) else 0 rec.overall_score = round(overall_avg, 2) if overall_avg <= 1: rating = '1' elif overall_avg <= 2: rating = '2' elif overall_avg <= 3: rating = '3' elif overall_avg <= 4: rating = '4' else: rating = '5' rec.overall_rating = rating rec.overall_rating_star = rating rec.overall_rating_value = RATING_VALUATION.get( rating, False ) # ================================== # NO CONFIGURATION # ================================== else: rec.employee_overall_score = 0 rec.manager_overall_score = 0 rec.hr_overall_score = 0 rec.employee_overall_star = '0' rec.manager_overall_star = '0' rec.hr_overall_star = '0' rec.overall_score = 0 rec.overall_rating = '0' rec.overall_rating_star = '0' rec.overall_rating_value = False # # @api.onchange('overall_rating_value') # def _onchange_overall_rating_value(self): # for rec in self: # if rec.overall_rating_value == 'outstanding': # rec.appraisal_percentage = 20 # elif rec.overall_rating_value == 'exceed_expectation': # rec.appraisal_percentage = 15 # elif rec.overall_rating_value == 'meet_expectation': # rec.appraisal_percentage = 10 # elif rec.overall_rating_value == 'needs_improvements': # rec.appraisal_percentage = 5 # else: # rec.appraisal_percentage = 0 # @api.depends('manager_ids') # def _compute_manager_email(self): # for rec in self: # emails = rec.manager_ids.mapped('work_email') # rec.manager_email = ",".join(filter(None, emails)) @api.depends('managerapp_id') def _compute_managerapp_id(self): for rec in self: rec.manager_email = rec.managerapp_id.work_email or '' @api.onchange('employee_appraisal_id') def _onchange_employee_appraisal_id(self): self.managerapp_id = self.employee_appraisal_id.parent_id @api.depends('end_date') def _compute_is_readonly(self): today = fields.Date.today() for rec in self: if rec.end_date and today > rec.end_date: rec.is_readonly = True else: rec.is_readonly = False @api.depends( 'kra_line_ids.kpi_line_ids.rating_star', 'kra_line_ids.kpi_line_ids.manager_rating_star', 'kra_line_ids.kpi_line_ids.hr_rating_star', 'kra_line_ids.kpi_line_ids.employee_score', 'kra_line_ids.kpi_line_ids.manager_score', 'kra_line_ids.kpi_line_ids.hr_score', ) def _compute_total_scores(self): for rec in self: employee_total = 0.0 manager_total = 0.0 hr_total = 0.0 for kra in rec.kra_line_ids: for kpi in kra.kpi_line_ids: # Star Rating Mode if rec.template_empl_rating_bool: employee_total += float(kpi.rating_star or 0) manager_total += float(kpi.manager_rating_star or 0) hr_total += float(kpi.hr_rating_star or 0) # Point Rating Mode elif rec.template_empl_point_bool: employee_total += float(kpi.employee_score or 0) manager_total += float(kpi.manager_score or 0) hr_total += float(kpi.hr_score or 0) rec.total_employee_score = employee_total rec.total_manager_score = manager_total rec.total_hr_score = hr_total @api.depends('current_salary', 'appraisal_amount') def _compute_new_salary(self): for rec in self: rec.new_salary = rec.current_salary + rec.appraisal_amount @api.onchange('appraisal_percentage') def _onchange_appraisal_percentage(self): for rec in self: if rec.current_salary: rec.appraisal_amount = ( rec.current_salary * rec.appraisal_percentage ) / 100 def _move_to_next_stage(self): self.ensure_one() next_stage = self.env['employee.stage.config'].search( [('id', 'in', self.available_stage_ids.ids), ('seq', '>', self.stage_id.seq)], order='seq asc', limit=1 ) if next_stage: self.stage_id = next_stage.id def action_create_pip(self): self.ensure_one() pip = self.env['employee.pip'].create({ 'employee_id': self.employee_appraisal_id.id, 'manager_id': self.managerapp_id.id, 'appraisal_id': self.id, 'objective': 'Improve performance and achieve expected goals.', 'timeline': '90', }) return { 'type': 'ir.actions.act_window', 'res_model': 'employee.pip', 'res_id': pip.id, 'view_mode': 'form', 'target': 'current', } def _compute_contract_count(self): for rec in self: rec.contract_count = len(rec.employee_appraisal_id.contract_ids) def action_view_current_contract(self): self.ensure_one() contract = self.employee_appraisal_id.contract_id if not contract: return False return { 'type': 'ir.actions.act_window', 'name': 'Contract', 'res_model': 'hr.contract', 'view_mode': 'form', 'res_id': contract.id, 'target': 'current', } def action_update_contract_salary(self): for rec in self: contract = rec.employee_appraisal_id.contract_id if not contract: raise ValidationError( "No active contract found for employee.") if rec.new_salary <= 0: raise ValidationError("Revised Salary must be greater than zero.") contract.write({ 'wage': rec.new_salary }) rec.salary_update = True def action_finance_approve(self): self.ensure_one() if not self.finance_remarks: raise ValidationError( _('Please provide Finance Remarks.') ) if not self.current_salary: raise ValidationError( _('Please enter Current Salary.') ) if not self.appraisal_percentage: raise ValidationError( _('Please enter Appraisal Percentage.') ) # email_to = ",".join( # filter(None, [ # self.employee_appraisal_id.work_email, # self.managerapp_id.work_email, # self.creator_email # ]) # ) finance_head_group = self.env.ref('hrms_employee_appraisal.group_appraisal_finance_head') email_list = [] partner_ids = [] for user in finance_head_group.users: if user.partner_id.email: email_list.append(user.partner_id.email) if user.partner_id: partner_ids.append(user.partner_id.id) body_html = f"""

Hello,

Finance review has been completed for the appraisal of {self.employee_appraisal_id.name}.

The salary revision details are given below for further approval.


Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Current Salary {self.current_salary or 0}
Appraisal Percentage {self.appraisal_percentage or 0}%
Appraisal Amount {self.appraisal_amount or 0}
Revised Salary {self.new_salary or 0}
Finance Remarks {self.finance_remarks or ''}

Kindly review and proceed with the next level approval.


Regards,

Finance Team

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': ",".join(email_list), 'default_partner_ids': [(6, 0, list(set(partner_ids)))], 'default_subject': f'Finance Approval - {self.employee_appraisal_id.name}', 'default_body': body_html, 'move_next_stage': True, 'mark_finance_approved': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def action_finance_head(self): self.ensure_one() if not self.finance_head_remarks: raise ValidationError( _('Please provide Finance Head Remarks.') ) # email_to = ",".join( # filter(None, [ # self.employee_appraisal_id.work_email, # self.managerapp_id.work_email, # self.creator_email # ]) # ) management_group = self.env.ref('hrms_employee_appraisal.group_appraisal_management') email_list = [] partner_ids = [] for user in management_group.users: if user.partner_id.email: email_list.append(user.partner_id.email) if user.partner_id: partner_ids.append(user.partner_id.id) body_html = f"""

Hello,

Finance Head has completed the appraisal review for {self.employee_appraisal_id.name}.

The compensation revision details have been reviewed and approved.


Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Current Salary {self.current_salary or 0}
Appraisal % {self.appraisal_percentage or 0}%
Increment Amount {self.appraisal_amount or 0}
Revised Salary {self.new_salary or 0}
Finance Head Remarks {self.finance_head_remarks or ''}

Kindly proceed with the next level approval.


Regards,

Finance Head

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': ",".join(email_list), 'default_partner_ids': [(6, 0, list(set(partner_ids)))], 'default_subject': f'Finance Head Approval - {self.employee_appraisal_id.name}', 'default_body': body_html, 'move_next_stage': True, 'mark_finance_head_approved': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def _send_manager_notification_mail(self): self.ensure_one() email_to = self.manager_email if not email_to: return body_html = f"""

Hello Manager,

Employee appraisal is ready for Manager Evaluation.


Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Performance Period {self.appraisal_period_id.appraisal_name or ''}

Please complete manager evaluation.


Regards,
HR Team

""" mail_values = { 'subject': 'Manager Evaluation Notification', 'body_html': body_html, 'email_to': email_to, } self.env['mail.mail'].create(mail_values).send() # def check_colleague_feedback_deadline(self): # now = fields.Datetime.now() # records = self.search([ # ('college_end_date_time', '!=', False), # ('college_end_date_time', '<=', now), # ('state', '=', 'colleague_feedback') # ]) # for rec in records: # rec.write({ # 'state': 'in_progress' # }) # rec.message_post( # body=_( # "Colleague feedback deadline completed automatically. " # "Stage moved to Manager Evaluation." # ) # ) # rec._send_manager_notification_mail() # return True def action_sent_employee_appraisal(self): self.ensure_one() missing = [] for kra in self.kra_line_ids: for kpi in kra.kpi_line_ids: if self.template_empl_rating_bool and not kpi.manager_rating_star: missing.append(_("Please give the Star rating for KPI '%s'.") % (kpi.question or '')) if self.template_empl_point_bool and kpi.manager_score <= 0: missing.append(_("Please enter the score for KPI '%s'.") % (kpi.question or '')) if missing: raise ValidationError("\n".join(missing)) if not self.manager_remarks: raise ValidationError("Please give the Manager Remarks") manager_email = self.managerapp_id.work_email or '' employee_email = self.employee_appraisal_id.work_email or '' creator_email = self.creator_email or '' emails = ",".join( filter(None, [ manager_email, employee_email, creator_email ]) ) partner_ids = [] # if self.managerapp_id.user_id.partner_id: # partner_ids.append(self.managerapp_id.user_id.partner_id.id) # if self.employee_appraisal_id.user_id.partner_id: # partner_ids.append(self.employee_appraisal_id.user_id.partner_id.id) if self.hr_apprai_id.user_id.partner_id: partner_ids.append(self.hr_apprai_id.user_id.partner_id.id) # creator = self.env['res.users'].search( # [('email', '=', self.creator_email)], # limit=1 # ) # if creator.partner_id: # partner_ids.append(creator.partner_id.id) body_html = f"""

Hello Team,

The employee has completed the self-assessment as part of the performance appraisal process. Your feedback and evaluation are now requested.


Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Appraisal Template {self.template_id.name or ''}
Performance Period {self.appraisal_period_id.appraisal_name or ''}

Kindly review the employee's self-assessment and provide your feedback, evaluation, and recommendations within the appraisal timeline.

Your valuable input will contribute to the employee's overall performance review.


Regards,

Manager Team

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': emails, 'default_partner_ids': [(6, 0, list(set(partner_ids)))], 'default_subject': 'Employee Appraisal Notification', 'default_body': body_html, 'mark_appraisal_sent': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def action_confirm_hr(self): self.ensure_one() missing = [] for kra in self.kra_line_ids: for kpi in kra.kpi_line_ids: if self.template_empl_rating_bool and not kpi.hr_rating_star: missing.append(_("Please give the Star rating for KPI '%s'.") % (kpi.question or '')) if self.template_empl_point_bool and kpi.hr_score <= 0: missing.append(_("Please enter the score for KPI '%s'.") % (kpi.question or '')) if missing: raise ValidationError("\n".join(missing)) if not self.hr_remarks: raise ValidationError ('Please Provide the Remarks') email_to = self.managerapp_id.work_email or '' partner_ids = [] if self.hr_head_apprai_id.user_id.partner_id: partner_ids.append(self.hr_head_apprai_id.user_id.partner_id.id) body_html = f"""

Hello,

HR evaluation has been completed for the appraisal of {self.employee_appraisal_id.name}.

Kindly review the appraisal and take the necessary action.


Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Template {self.template_id.name or ''}

Regards,

HR Team

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': email_to, 'default_partner_ids': [(6, 0, list(set(partner_ids)))], 'default_subject': f'HR Evaluation Completed - {self.employee_appraisal_id.name}', 'default_body': body_html, 'move_hr_next_stage': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def action_head_hr(self): self.ensure_one() if not self.hr_head_remarks: raise ValidationError( _('Please provide HR Head Remarks.') ) # email_to = self.created_by_id.work_email or '' # partner_ids = [] # if self.created_by_id.user_id.partner_id: # partner_ids.append(self.created_by_id.user_id.partner_id.id) finance_group = self.env.ref('hrms_employee_appraisal.group_appraisal_finance') email_list = [] partner_ids = [] for user in finance_group.users: if user.partner_id.email: email_list.append(user.partner_id.email) if user.partner_id: partner_ids.append(user.partner_id.id) body_html = f"""

Hello,

HR Head review has been completed for the appraisal of {self.employee_appraisal_id.name}.

Kindly proceed with the next level approval.

Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
HR Head Remarks {self.hr_head_remarks or ''}

Regards,

HR Head

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': ",".join(email_list), 'default_partner_ids': [(6, 0, list(set(partner_ids)))], 'default_subject': f'HR Head Approval - {self.employee_appraisal_id.name}', 'default_body': body_html, 'move_hr_head_next_stage': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def action_send_colleague_feedback(self): for rec in self: # for kra in rec.kra_line_ids: # for kpi in kra.kpi_line_ids: # if rec.template_empl_rating_bool and not kpi.rating_star: # raise ValidationError(_( # "Please give the self rating for the KPI '%s' before sending the colleague feedback." # ) % (kpi.question or '')) # if rec.template_empl_point_bool and not kpi.employee_score <= 0: # raise ValidationError(_("Please enter the self score for the KPI '%s' before sending the colleague feedback." # ) % (kpi.question or '')) missing = [] for kra in rec.kra_line_ids: for kpi in kra.kpi_line_ids: if rec.template_empl_rating_bool and not kpi.rating_star: missing.append(_("Please give the self rating for KPI '%s'.") % (kpi.question or '')) if rec.template_empl_point_bool and kpi.employee_score <= 0: missing.append(_("Please enter the self score for KPI '%s'.") % (kpi.question or '')) if missing: raise ValidationError("\n".join(missing)) employees = self.env['hr.employee'].search([ ('department_id', '=', rec.department_appraisal_id.id), ('id', '!=', rec.employee_appraisal_id.id) ]) vals = [] for emp in employees: already_exists = self.env['colleague.feedback'].search([ ('employee_appraisal_feed_id', '=', rec.id), ('colleague_feed_id', '=', emp.id) ], limit=1) if not already_exists: vals.append((0, 0, { 'colleague_feed_id': emp.id, })) rec.colleague_feed_ids = vals email_list = [] partner_ids = [] if rec.managerapp_id: if rec.managerapp_id.work_email: email_list.append(rec.managerapp_id.work_email) if rec.managerapp_id.user_id.partner_id: partner_ids.append(rec.managerapp_id.user_id.partner_id.id) body_html = f"""

Hello Team,

{self.employee_appraisal_id.name} has completed the self-assessment. Please provide your feedback as part of the appraisal process.

Your feedback will help in evaluating the employee's overall performance.


Regards,

{self.employee_appraisal_id.name}

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': ",".join(email_list), 'default_partner_ids': [(6, 0, partner_ids)], 'default_subject': f'Colleague Feedback Request - {self.employee_appraisal_id.name}', 'default_body': body_html, 'mark_colleague_feedback_sent': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } def action_initiate_pip(self): self.ensure_one() self.write({'invite_pip': True}) employee_email = self.employee_appraisal_id.work_email or '' body_html = f"""

Dear {self.employee_appraisal_id.name},

Based on the recent performance appraisal review, your overall performance rating indicates that improvement is required in certain areas.

Therefore, you are requested to attend a Performance Improvement Plan (PIP) discussion meeting with Management and HR.

Employee {self.employee_appraisal_id.name or ''}
Department {self.department_appraisal_id.name or ''}
Performance Period {self.appraisal_period_id.appraisal_name or ''}
Overall Rating {self.overall_rating or ''}

During this meeting, we will discuss performance concerns, expectations, improvement objectives, and the Performance Improvement Plan (PIP) timeline.

Kindly acknowledge and attend the meeting as scheduled.


Regards,

Management Team

""" ctx = { 'default_model': 'employee.appraisal.template.config', 'default_res_ids': [self.id], 'default_composition_mode': 'comment', 'default_email_to': employee_email, 'default_subject': 'Performance Improvement Plan (PIP) Meeting Notification', 'default_body': body_html, 'mark_invite_pip': True, } return { 'type': 'ir.actions.act_window', 'res_model': 'mail.compose.message', 'view_mode': 'form', 'target': 'new', 'context': ctx, } @api.onchange('template_id') def _onchange_template_id(self): self.kra_line_ids = [(5, 0, 0)] if not self.template_id: return kra_vals = [] for kra in self.template_id.kra_ids: kpi_vals = [] for kpi in kra.kpi_line_ids: kpi_vals.append((0, 0, { 'question': kpi.name, 'description': kpi.kpi_description, 'kpi_line_weightage': kpi.kpi_weightage, 'manager_kpi_points': kpi.kpi_points, 'manager_kpi_stars': kpi.kpi_ratings, })) kra_vals.append((0, 0, { 'kra_id': kra.id, 'name': kra.name, 'description_line_kra': kra.description, 'kra_line_weightage': kra.kra_weightage, 'max_kra_points': kra.max_points, 'max_kra_stars': kra.max_star_rating, 'kpi_line_ids': kpi_vals, })) self.kra_line_ids = kra_vals class ColleagueFeedBack(models.Model): _name = 'colleague.feedback' _description = 'Colleague Feedback' colleague_feed_id = fields.Many2one('hr.employee', 'Employee', default=lambda self: self.env.user.employee_id.id) feedback = fields.Char(string="Feedback") employee_appraisal_feed_id = fields.Many2one('employee.appraisal.template.config') state = fields.Selection([ ('draft', 'Draft'), ('submitted', 'Submitted') ], default='draft') submitted_date = fields.Datetime() def action_submit_feedback(self): for rec in self: rec.write({ 'state': 'submitted', 'submitted_date': fields.Datetime.now() }) @api.constrains('colleague_feed_id', 'employee_appraisal_feed_id') def _check_colleague_feed_id(self): for rec in self: duplicate = self.search([ ('id', '!=', rec.id), ('colleague_feed_id', '=', rec.colleague_feed_id.id), ('employee_appraisal_feed_id', '=', rec.employee_appraisal_feed_id.id) ], limit=1) if duplicate: raise ValidationError(_('Colleague Feed Back already exists'))