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') managerapp_id = fields.Many2one('hr.employee') 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') 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") 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) current_salary = fields.Float(string="Current Salary") 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) template_empl_rating_bool = fields.Boolean('Star Rating') template_empl_point_bool = fields.Boolean('Point Rating') @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('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( [('seq', '>', self.stage_id.seq)], order='seq asc', limit=1 ) if next_stage: self.stage_id = next_stage.id def action_finance_approve(self): for rec in self: rec.write({ 'state': 'management_team', 'finance_user_id': self.env.user.id, 'finance_date': fields.Datetime.now() }) rec.message_post( body=_( "Finance appraisal approved." ) ) 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_type_id.id or ''} |
Please complete manager evaluation.
Regards,
HR Team
Hello,Team
Your appraisal evaluation has been initiated.
| Employee | {self.employee_appraisal_id.name or ''} |
| Template | {self.template_id.name or ''} |
| Performance Period | {self.appraisal_period_id.appraisal_type_id.id or ''} |
Please complete the self evaluation before deadline.
Regards,
HR Team