1309 lines
47 KiB
Python
1309 lines
47 KiB
Python
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"""
|
|
<div>
|
|
<p>Hello,</p>
|
|
<p>
|
|
Finance review has been completed for the appraisal of
|
|
<b>{self.employee_appraisal_id.name}</b>.
|
|
</p>
|
|
<p>
|
|
The salary revision details are given below for further approval.
|
|
</p>
|
|
<br/>
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Current Salary</b></td>
|
|
<td>{self.current_salary or 0}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Appraisal Percentage</b></td>
|
|
<td>{self.appraisal_percentage or 0}%</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Appraisal Amount</b></td>
|
|
<td>{self.appraisal_amount or 0}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Revised Salary</b></td>
|
|
<td>{self.new_salary or 0}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Finance Remarks</b></td>
|
|
<td>{self.finance_remarks or ''}</td>
|
|
</tr>
|
|
</table>
|
|
<br/>
|
|
<p>
|
|
Kindly review and proceed with the next level approval.
|
|
</p>
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>Finance Team</p>
|
|
|
|
</div>
|
|
"""
|
|
|
|
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"""
|
|
<div>
|
|
|
|
<p>Hello,</p>
|
|
|
|
<p>
|
|
Finance Head has completed the appraisal review for
|
|
<b>{self.employee_appraisal_id.name}</b>.
|
|
</p>
|
|
|
|
<p>
|
|
The compensation revision details have been reviewed and approved.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Current Salary</b></td>
|
|
<td>{self.current_salary or 0}</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Appraisal %</b></td>
|
|
<td>{self.appraisal_percentage or 0}%</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Increment Amount</b></td>
|
|
<td>{self.appraisal_amount or 0}</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Revised Salary</b></td>
|
|
<td>{self.new_salary or 0}</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td><b>Finance Head Remarks</b></td>
|
|
<td>{self.finance_head_remarks or ''}</td>
|
|
</tr>
|
|
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
<p>
|
|
Kindly proceed with the next level approval.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>Finance Head</p>
|
|
|
|
</div>
|
|
"""
|
|
|
|
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"""
|
|
<div>
|
|
<p>Hello Manager,</p>
|
|
<p>
|
|
Employee appraisal is ready for Manager Evaluation.
|
|
</p>
|
|
<br/>
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Performance Period</b></td>
|
|
<td>{self.appraisal_period_id.appraisal_name or ''}</td>
|
|
</tr>
|
|
</table>
|
|
<br/>
|
|
<p>
|
|
Please complete manager evaluation.
|
|
</p>
|
|
<br/>
|
|
<p>
|
|
Regards,<br/>
|
|
HR Team
|
|
</p>
|
|
</div>
|
|
"""
|
|
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"""
|
|
<div>
|
|
<p>Hello Team,</p>
|
|
|
|
<p>
|
|
The employee has completed the self-assessment as part of the performance appraisal process.
|
|
Your feedback and evaluation are now requested.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Appraisal Template</b></td>
|
|
<td>{self.template_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Performance Period</b></td>
|
|
<td>{self.appraisal_period_id.appraisal_name or ''}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
<p>
|
|
Kindly review the employee's self-assessment and provide your feedback,
|
|
evaluation, and recommendations within the appraisal timeline.
|
|
</p>
|
|
|
|
<p>
|
|
Your valuable input will contribute to the employee's overall performance review.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<p>
|
|
Regards,
|
|
</p>
|
|
|
|
<p>
|
|
Manager Team
|
|
</p>
|
|
</div>
|
|
"""
|
|
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"""
|
|
<div>
|
|
<p>Hello,</p>
|
|
|
|
<p>
|
|
HR evaluation has been completed for the appraisal of
|
|
<b>{self.employee_appraisal_id.name}</b>.
|
|
</p>
|
|
|
|
<p>
|
|
Kindly review the appraisal and take the necessary action.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Template</b></td>
|
|
<td>{self.template_id.name or ''}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>HR Team</p>
|
|
|
|
</div>
|
|
"""
|
|
|
|
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"""
|
|
<div>
|
|
<p>Hello,</p>
|
|
|
|
<p>
|
|
HR Head review has been completed for the appraisal of
|
|
<b>{self.employee_appraisal_id.name}</b>.
|
|
</p>
|
|
|
|
<p>
|
|
Kindly proceed with the next level approval.
|
|
</p>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>HR Head Remarks</b></td>
|
|
<td>{self.hr_head_remarks or ''}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>HR Head</p>
|
|
</div>
|
|
"""
|
|
|
|
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"""
|
|
<div>
|
|
<p>Hello Team,</p>
|
|
|
|
<p>
|
|
{self.employee_appraisal_id.name} has completed the self-assessment.
|
|
Please provide your feedback as part of the appraisal process.
|
|
</p>
|
|
|
|
<p>
|
|
Your feedback will help in evaluating the employee's overall performance.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>{self.employee_appraisal_id.name}</p>
|
|
</div>
|
|
"""
|
|
|
|
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"""
|
|
<div>
|
|
<p>Dear {self.employee_appraisal_id.name},</p>
|
|
|
|
<p>
|
|
Based on the recent performance appraisal review, your overall performance
|
|
rating indicates that improvement is required in certain areas.
|
|
</p>
|
|
|
|
<p>
|
|
Therefore, you are requested to attend a Performance Improvement Plan (PIP)
|
|
discussion meeting with Management and HR.
|
|
</p>
|
|
|
|
<table border="1" cellpadding="5" cellspacing="0">
|
|
<tr>
|
|
<td><b>Employee</b></td>
|
|
<td>{self.employee_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Department</b></td>
|
|
<td>{self.department_appraisal_id.name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Performance Period</b></td>
|
|
<td>{self.appraisal_period_id.appraisal_name or ''}</td>
|
|
</tr>
|
|
<tr>
|
|
<td><b>Overall Rating</b></td>
|
|
<td>{self.overall_rating or ''}</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<br/>
|
|
|
|
<p>
|
|
During this meeting, we will discuss performance concerns,
|
|
expectations, improvement objectives, and the Performance
|
|
Improvement Plan (PIP) timeline.
|
|
</p>
|
|
|
|
<p>
|
|
Kindly acknowledge and attend the meeting as scheduled.
|
|
</p>
|
|
|
|
<br/>
|
|
|
|
<p>Regards,</p>
|
|
<p>Management Team</p>
|
|
</div>
|
|
"""
|
|
|
|
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'))
|