-
![]()
-
Bench Management
bench.management.line
kanban,list,form
@@ -247,6 +239,7 @@
diff --git a/addons_extensions/disciplinary/models/employee_displane.py b/addons_extensions/disciplinary/models/employee_displane.py
new file mode 100644
index 000000000..0abcba19e
--- /dev/null
+++ b/addons_extensions/disciplinary/models/employee_displane.py
@@ -0,0 +1,179 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+from datetime import date
+
+
+class HRDisciplinaryAction(models.Model):
+ _name = 'hr.employee.disciplinary'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _description = 'Employee Disciplinary Management'
+
+ active = fields.Boolean(default=True)
+ name = fields.Char('Reference', copy=False, readonly=True, default=lambda x: _('New'))
+ employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
+ company_id = fields.Many2one('res.company', string="Company", required=True, default=lambda self: self.env.company)
+ employee_code = fields.Char(string='Employee Code', related='employee_id.employee_id',tracking=True,required=True)
+ # unit_id = fields.Many2one('unit.master', string="Unit",tracking=True)
+ department_id = fields.Many2one('hr.department', string="Department",tracking=True)
+ designation_id = fields.Many2one('hr.job', string="Designation",tracking=True)
+ doj = fields.Date(string="Date of Joining",tracking=True)
+ referred_by_id = fields.Many2one('res.users', string="Referred By",tracking=True)
+ loss_of_cost = fields.Float(string="Loss of Cost")
+ # employee_section_id = fields.Many2one('section.master',string='Section')
+ disciplinary_complaint_line_ids = fields.One2many('hr.disciplinary.complaint.line','disciplinary_id',string = 'Complaint Lines')
+ disciplinary_action_line_ids = fields.One2many('hr.disciplinary.action.line','disciplinary_id',string = 'Action Lines')
+ state = fields.Selection([
+ ('new', 'New'),
+ ('submitted', 'Submitted'),
+ ('pending', 'Pending'),
+ ('closed', 'Closed'),
+ ('cancel', 'Cancel')
+ ], default='new',tracking=True,string='State')
+ complaint_name = fields.Text('Complaint', compute='_compute_complaint_name', store=True)
+ name_1 = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary', string="Disciplinary")
+ complaint_date = fields.Date('Complaint Date')
+ language_id = fields.Many2one('res.lang', 'Language')
+ complaint_type_id = fields.Many2one('disciplinary.complaint.type', string="Complaint Type")
+ mistake_type_id = fields.Many2one('disciplinary.mistake.type', string="Mistake Type", required=True)
+ complaint = fields.Char(string='Complaints')
+ employee_id_2 = fields.Many2one('hr.employee', string='Employee')
+ related_record_count = fields.Integer(string="Disciplinary Action Records Count", compute="_compute_related_record_count")
+ # general_cat = fields.Many2one('general.category', string="General Category", tracking=True)
+ # cat_id = fields.Many2one('hr.category','Category')
+ occurrences = fields.Integer('Occurrences', store=True)
+ severe = fields.Char('Severe')
+ major = fields.Char('Major')
+ less_major = fields.Char('Less Major')
+ negligible = fields.Char('Negligible')
+ normal = fields.Char('Normal')
+ total_mistakes = fields.Char('Total Mistakes')
+ memo = fields.Char('Memo')
+ explanation = fields.Char('Explanation')
+ show_cause = fields.Char('Show Cause')
+ charge_sheet = fields.Char('Charge Sheet')
+ warning = fields.Char('Warning')
+ enquiry_notice = fields.Char('Enquiry Notice')
+ recovery_order = fields.Char('Recovery_ Order')
+ stoppage_of_increment = fields.Char('Stoppage Of Increment')
+ demotion = fields.Char('Demotion')
+ total_actions = fields.Char('Total Actions')
+ normal_action = fields.Char('Normal Actions')
+ suspension = fields.Char('Suspension')
+ total_cost = fields.Float('Total Cost')
+
+ @api.depends('employee_id')
+ def _compute_related_record_count(self):
+ for record in self:
+ record.related_record_count = self.env['hr.employee.disciplinary'].search_count([('employee_id', '=', record.employee_id.id)])
+
+ def action_open_related_records(self):
+ return {
+ 'name': 'Disciplinary Action Records',
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'hr.employee.disciplinary',
+ 'view_mode': 'list',
+ 'domain': [('employee_id', '=', self.employee_id.id)],
+ 'context': {'default_employee_id': self.employee_id.id},
+ }
+
+ @api.depends('disciplinary_complaint_line_ids.complaint')
+ def _compute_complaint_name(self):
+ for record in self:
+ complaints = record.disciplinary_complaint_line_ids.mapped('complaint')
+ record.complaint_name = "\n".join(filter(None, complaints))
+
+ def action_set_submitted(self):
+ self.state = 'submitted'
+
+ def action_set_pending(self):
+ self.state = 'pending'
+
+ def action_set_closed(self):
+ self.state = 'closed'
+
+ def action_set_cancel(self):
+ self.state = 'cancel'
+
+ def action_reset_to_new(self):
+ self.state = 'new'
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ for vals in vals_list:
+ if not vals.get('name') or vals['name'] == _('New'):
+ vals['name'] = self.env['ir.sequence'].next_by_code('hr.employee.sequence') or _('New')
+ return super().create(vals_list)
+
+ @api.onchange('employee_id')
+ def _onchange_employee_id(self):
+ for rec in self:
+ if rec.employee_id:
+ rec.employee_code = rec.employee_id.employee_id or ''
+ rec.department_id = rec.employee_id.department_id.id
+ rec.designation_id = rec.employee_id.job_id.id
+ rec.doj = rec.employee_id.doj
+ rec.company_id = rec.employee_id.company_id.id
+ # rec.unit_id = rec.employee_id.unit_name_hr.id if rec.employee_id.unit_name_hr else False
+ # rec.employee_section_id = rec.employee_id.section_name_hr.id if rec.employee_id.section_name_hr else False
+ else:
+ rec.employee_code = False
+ rec.department_id = False
+ rec.designation_id = False
+ rec.doj = False
+
+
+class DisciplinaryComplaintLine(models.Model):
+ _name = 'hr.disciplinary.complaint.line'
+ _description = 'Disciplinary Complaint Line'
+
+ name = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary',string="Disciplinary")
+ complaint_date = fields.Date('Complaint Date')
+ language_id = fields.Many2one('res.lang','Language')
+ complaint_type_id = fields.Many2one('disciplinary.complaint.type',string="Complaint Type")
+ mistake_type_id = fields.Many2one('disciplinary.mistake.type',string="Mistake Type")
+ complaint = fields.Char(string='Complaints')
+ employee_id = fields.Many2one('hr.employee', string='Employee')
+
+
+
+
+class DisciplinaryActionLine(models.Model):
+ _name = 'hr.disciplinary.action.line'
+ _description = 'Disciplinary Action Line'
+
+ name = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary',string="Disciplinary")
+ action_taken_date = fields.Date('Action On')
+ action_type_id = fields.Many2one('disciplinary.action.type',string="Action Type")
+ action = fields.Char(string='Description')
+ action_name = fields.Char('ActionName')
+ related_complaint_id = fields.Many2one('hr.disciplinary.complaint.line', string="Related Complaint",
+ domain="[('disciplinary_id', '=', disciplinary_id)]")
+ employee_id = fields.Many2one('hr.employee', string='Employee')
+
+ @api.constrains('action_taken_date')
+ def _check_action_taken_date(self):
+ for record in self:
+ if record.action_taken_date and record.action_taken_date > date.today():
+ raise ValidationError("The Action On date cannot be in the future.")
+
+
+class DisciplinaryActionType(models.Model):
+ _name = 'disciplinary.action.type'
+ _description = 'Action Type'
+
+ name = fields.Char('Name', required=True)
+
+class DisciplinaryComplaintType(models.Model):
+ _name = 'disciplinary.complaint.type'
+ _description = 'Complaint Type'
+
+ name = fields.Char('Name', required=True)
+
+class DisciplinaryMistakeType(models.Model):
+ _name = 'disciplinary.mistake.type'
+ _description = 'Mistake Type'
+
+ name = fields.Char('Name', required=True)
diff --git a/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml b/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml
new file mode 100644
index 000000000..569a0caf9
--- /dev/null
+++ b/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml
@@ -0,0 +1,122 @@
+
+
+
+ disciplinary.complaint.type
+ disciplinary.complaint.type
+
+
+
+
+
+
+
+
+ disciplinary.complaint.type.form
+ disciplinary.complaint.type
+
+
+
+
+
+
+ Employee Disciplinary Complaint Type
+ disciplinary.complaint.type
+ list,form
+
+
+
+
+
+
+ hr.disciplinary.action.line
+ hr.disciplinary.action.line
+
+
+
+
+
+
+
+
+ hr.disciplinary.action.line.form
+ hr.disciplinary.action.line
+
+
+
+
+
+
+ Employee Disciplinary Action
+ hr.disciplinary.action.line
+ list,form
+
+
+
+
+
+
+
+ disciplinary.action.type
+ disciplinary.action.type
+
+
+
+
+
+
+
+
+ disciplinary.action.type.form
+ disciplinary.action.type
+
+
+
+
+
+
+ Employee Disciplinary Action Type
+ disciplinary.action.type
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/views/employee_displance.xml b/addons_extensions/disciplinary/views/employee_displance.xml
new file mode 100644
index 000000000..f762c695b
--- /dev/null
+++ b/addons_extensions/disciplinary/views/employee_displance.xml
@@ -0,0 +1,169 @@
+
+
+
+ employee.disciplinary.form
+ hr.employee.disciplinary
+
+
+
+
+
+
+ hr.employee.disciplinary.list
+ hr.employee.disciplinary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.disciplinary.search
+ hr.employee.disciplinary
+
+
+
+
+
+
+
+
+
+ Employee Disciplinary
+ hr.employee.disciplinary
+ list,form
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/employee_it_declaration/models/payroll_periods.py b/addons_extensions/employee_it_declaration/models/payroll_periods.py
index ed9bc86c8..5cc558492 100644
--- a/addons_extensions/employee_it_declaration/models/payroll_periods.py
+++ b/addons_extensions/employee_it_declaration/models/payroll_periods.py
@@ -1,12 +1,13 @@
-from odoo import models, fields, api
-from odoo.exceptions import ValidationError
-import calendar
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+import calendar
-class PayrollPeriod(models.Model):
+class PayrollPeriod(models.Model):
_name = 'payroll.period'
_description = 'Payroll Period'
_rec_name = 'name'
+ _order = 'id desc'
_sql_constraints = [
('unique_name', 'unique(name)', 'The name must be unique.')
]
@@ -14,22 +15,22 @@ class PayrollPeriod(models.Model):
from_date = fields.Date(string="From Date", required=True)
to_date = fields.Date(string="To Date", required=True)
name = fields.Char(string="Name", required=True)
- period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods")
-
- @api.model_create_multi
- def create(self, vals_list):
- periods = super().create(vals_list)
- active_investment_types = self.env['it.investment.type'].search([('active', '=', True)])
- if active_investment_types:
- active_investment_types.write({
- 'period_ids': [(4, period.id) for period in periods],
- })
- return periods
-
- @api.onchange('from_date', 'to_date')
- def onchange_from_to_date(self):
- for rec in self:
- if rec.from_date and rec.to_date:
+ period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods")
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ periods = super().create(vals_list)
+ active_investment_types = self.env['it.investment.type'].search([('active', '=', True)])
+ if active_investment_types:
+ active_investment_types.write({
+ 'period_ids': [(4, period.id) for period in periods],
+ })
+ return periods
+
+ @api.onchange('from_date', 'to_date')
+ def onchange_from_to_date(self):
+ for rec in self:
+ if rec.from_date and rec.to_date:
rec.name = f"{rec.from_date.year}-{rec.to_date.year}"
diff --git a/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml b/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
index cf0b7b0cf..d0ce96181 100644
--- a/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
+++ b/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
@@ -41,6 +41,11 @@
+
diff --git a/addons_extensions/hr_resignation/__manifest__.py b/addons_extensions/hr_resignation/__manifest__.py
index 8ab0426f3..06b508c07 100644
--- a/addons_extensions/hr_resignation/__manifest__.py
+++ b/addons_extensions/hr_resignation/__manifest__.py
@@ -34,6 +34,7 @@
'data': [
'security/hr_resignation_security.xml',
'security/ir.model.access.csv',
+ 'security/resignation_groups.xml',
'data/data.xml',
'data/ir_sequence_data.xml',
'data/ir_cron_data.xml',
diff --git a/addons_extensions/hr_resignation/models/hr_resignation.py b/addons_extensions/hr_resignation/models/hr_resignation.py
index cf6e9cbdb..cd39f8e11 100644
--- a/addons_extensions/hr_resignation/models/hr_resignation.py
+++ b/addons_extensions/hr_resignation/models/hr_resignation.py
@@ -167,8 +167,33 @@ class HrResignation(models.Model):
admin_checklist_submitted = fields.Boolean(tracking=True)
hr_checklist_submitted = fields.Boolean(tracking=True)
+ manager_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ it_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ finance_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed') ], compute='_compute_checklist_status')
+ admin_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ hr_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
relieving_documents = fields.Many2many('ir.attachment')
+ applied_date = fields.Date(string="Applied Date",default=fields.Date.context_today,readonly=True,tracking=True)
+
+ @api.depends('manager_checklist_submitted','it_checklist_submitted','finance_checklist_submitted','admin_checklist_submitted', 'hr_checklist_submitted')
+ def _compute_checklist_status(self):
+ for rec in self:
+ rec.manager_checklist_status = (
+ 'completed' if rec.manager_checklist_submitted else 'pending'
+ )
+ rec.it_checklist_status = (
+ 'completed' if rec.it_checklist_submitted else 'pending'
+ )
+ rec.finance_checklist_status = (
+ 'completed' if rec.finance_checklist_submitted else 'pending'
+ )
+ rec.admin_checklist_status = (
+ 'completed' if rec.admin_checklist_submitted else 'pending'
+ )
+ rec.hr_checklist_status = (
+ 'completed' if rec.hr_checklist_submitted else 'pending'
+ )
@api.depends('employee_id')
def _compute_user_rights(self):
diff --git a/addons_extensions/hr_resignation/security/resignation_groups.xml b/addons_extensions/hr_resignation/security/resignation_groups.xml
new file mode 100644
index 000000000..822086f4b
--- /dev/null
+++ b/addons_extensions/hr_resignation/security/resignation_groups.xml
@@ -0,0 +1,15 @@
+
+
+ Resignation Management
+ 41
+
+
+ Resignation User
+
+
+
+
+ Resignation Manager
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hr_resignation/views/hr_resignation_views.xml b/addons_extensions/hr_resignation/views/hr_resignation_views.xml
index 1363c4746..d1f3a1dc1 100644
--- a/addons_extensions/hr_resignation/views/hr_resignation_views.xml
+++ b/addons_extensions/hr_resignation/views/hr_resignation_views.xml
@@ -150,6 +150,7 @@
+
@@ -173,12 +174,30 @@
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -186,7 +205,8 @@
-
+
@@ -387,7 +407,7 @@
diff --git a/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml b/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
index 6589247d8..00277d65f 100644
--- a/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
+++ b/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
@@ -24,7 +24,7 @@
-
+
"""
ctx = {
'default_model': 'employee.appraisal.template.config',
@@ -549,17 +894,132 @@ class EmployeeAppraisal(models.Model):
'context': ctx,
}
- def action_confirm(self):
- for rec in self:
- rec.state = 'self_evaluation'
-
- def action_confirm_manager(self):
- for rec in self:
- rec.state = 'hr_evaluation'
-
def action_confirm_hr(self):
- for rec in self:
- rec.state = 'finance_team'
+ self.ensure_one()
+
+ if not self.hr_remarks:
+ raise ValidationError ('Please Provide the Remarks')
+
+ email_to = self.managerapp_id.work_email or ''
+
+ 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_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 ''
+
+ 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': email_to,
+ '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:
@@ -568,6 +1028,7 @@ class EmployeeAppraisal(models.Model):
('id', '!=', rec.employee_appraisal_id.id)
])
vals = []
+ email_list = []
for emp in employees:
already_exists = self.env['colleague.feedback'].search([
('employee_appraisal_feed_id', '=', rec.id),
@@ -578,7 +1039,123 @@ class EmployeeAppraisal(models.Model):
'colleague_feed_id': emp.id,
}))
rec.colleague_feed_ids = vals
- rec.state = 'colleague_manager'
+ if self.managerapp_id and self.managerapp_id.work_email:
+ email_list.append(self.managerapp_id.work_email)
+ email_to = ",".join(filter(None, email_list))
+
+ 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': email_to,
+ '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):
@@ -624,7 +1201,6 @@ class ColleagueFeedBack(models.Model):
submitted_date = fields.Datetime()
def action_submit_feedback(self):
-
for rec in self:
rec.write({
'state': 'submitted',
diff --git a/addons_extensions/hrms_employee_appraisal/models/employee_pip.py b/addons_extensions/hrms_employee_appraisal/models/employee_pip.py
new file mode 100644
index 000000000..905d9c21a
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/employee_pip.py
@@ -0,0 +1,89 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class EmployeePIP(models.Model):
+ _name = 'employee.pip'
+ _description = 'Performance Improvement Plan'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _rec_name = 'employee_id'
+
+ name = fields.Char(string="PIP Reference",default=lambda self: _('New'),readonly=True )
+ employee_id = fields.Many2one( 'hr.employee',required=True)
+ manager_id = fields.Many2one('hr.employee',string="Manager")
+ appraisal_id = fields.Many2one('employee.appraisal.template.config',string="Appraisal")
+ objective = fields.Text(string="Improvement Objective",required=True)
+ timeline = fields.Selection([
+ ('30', '30 Days'),
+ ('60', '60 Days'),
+ ('90', '90 Days')
+ ], default='30', required=True)
+ start_date = fields.Date(default=fields.Date.today)
+ end_date = fields.Date()
+ review_date = fields.Date()
+ employee_acknowledged = fields.Boolean(string="Employee Acknowledged")
+ state = fields.Selection([
+ ('draft', 'Draft'),
+ ('running', 'In Progress'),
+ ('review', 'Under Review'),
+ ('completed', 'Completed'),
+ ('failed', 'Failed')
+ ], default='draft', tracking=True)
+ task_ids = fields.One2many('employee.pip.task','pip_id',string='Improvement Tasks')
+ progress_percentage = fields.Float(compute='_compute_progress',store=True)
+ remarks = fields.Text()
+
+ @api.depends('task_ids.state')
+ def _compute_progress(self):
+ for rec in self:
+
+ total = len(rec.task_ids)
+
+ completed = len(
+ rec.task_ids.filtered(
+ lambda l: l.state == 'done'
+ )
+ )
+
+ rec.progress_percentage = (
+ (completed / total) * 100
+ ) if total else 0
+
+ @api.onchange('timeline', 'start_date')
+ def _onchange_timeline(self):
+ for rec in self:
+ if rec.start_date and rec.timeline:
+ rec.end_date = fields.Date.add(
+ rec.start_date,
+ days=int(rec.timeline)
+ )
+
+ def action_start(self):
+ self.state = 'running'
+
+ def action_review(self):
+ self.state = 'review'
+
+ def action_complete(self):
+ self.state = 'completed'
+
+ def action_fail(self):
+ self.state = 'failed'
+
+
+
+
+class EmployeePIPTask(models.Model):
+ _name = 'employee.pip.task'
+ _description = 'PIP Task'
+
+ pip_id = fields.Many2one('employee.pip',ondelete='cascade')
+ name = fields.Char(required=True)
+ description = fields.Text()
+ target_date = fields.Date()
+ training_course = fields.Char(string="Suggested Training")
+ state = fields.Selection([
+ ('pending', 'Pending'),
+ ('progress', 'In Progress'),
+ ('done', 'Completed')
+ ], default='pending')
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py b/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py
new file mode 100644
index 000000000..38366da68
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py
@@ -0,0 +1,121 @@
+from odoo import api, fields, models
+
+
+class HrHeadNofication(models.Model):
+ _name = 'hr.head.notification'
+ _description = 'HeadNofication'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+
+ @api.returns('self')
+ def _default_employee_get(self):
+ return self.env.user.employee_id
+
+ hr_employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee_get)
+ name = fields.Char("Subject")
+ appraisal_type_id = fields.Many2one('employee.appraisal.type')
+ appraisal_period_id = fields.Many2one('employee.appraisal.year',
+ domain="[('appraisal_type_id', '=', appraisal_type_id)]")
+ body = fields.Html(string="Notice Body", required=True)
+ start_date = fields.Date()
+ end_date = fields.Date()
+ hr_ids = fields.Many2many('hr.employee', string="HR Team")
+ # hr_employee_domain_ids = fields.Many2many('hr.employee',compute='_compute_hr_employee_domain')
+ stage_config_ids = fields.Many2many('employee.stage.config', string="Stages")
+ state = fields.Selection([
+ ('draft', 'Draft'),
+ ('sent', 'Sent')
+ ], default='draft')
+ seq = fields.Char(string="Reference", readonly=True, copy=False, default="New")
+
+ @api.model
+ def _get_hr_users_domain(self):
+ group = self.env.ref('hrms_employee_appraisal.group_appraisal_hr')
+ if group:
+ return [('groups_id', 'in', [group.id])]
+ return [('id', '=', False)]
+
+ hr_users_ids = fields.Many2many('res.users', string="HR Team", copy=False, domain=_get_hr_users_domain)
+
+ @api.model
+ def create(self, vals):
+ if vals.get('seq', 'New') == 'New':
+ company = self.env.company
+ company_code = company.short_code or 'CMP'
+ today = fields.Datetime.now()
+ month = str(today.month).zfill(2)
+ year = str(today.year)[-2:]
+ prefix = f"{company_code}/{month}/{year}"
+ last_record = self.search([
+ ('seq', '=like', f'{prefix}%')
+ ], order='id desc', limit=1)
+ number = 1
+ if last_record and last_record.seq:
+ try:
+ number = int(
+ last_record.seq.split('/')[-1]
+ ) + 1
+ except Exception:
+ number = 1
+ vals['seq'] = (
+ f"{prefix}/{str(number).zfill(3)}"
+ )
+ return super().create(vals)
+
+
+ def action_sent_hr(self):
+ self.ensure_one()
+
+ hr_emails = self.hr_users_ids.mapped('email')
+ email_to = ",".join(filter(None, hr_emails))
+
+ body_html = f"""
+
+
Hello HR Team,
+
+
{self.body or ''}
+
+
+
+ | Appraisal Type |
+ {self.appraisal_type_id.name or ''} |
+
+
+ | Appraisal Period |
+ {self.appraisal_period_id.appraisal_name or ''} |
+
+
+ | Start Date |
+ {self.start_date or ''} |
+
+
+ | End Date |
+ {self.end_date or ''} |
+
+
+
+
+
+
Please initiate the appraisal process.
+
+
Regards,
+
HR Head
+
+ """
+
+ ctx = {
+ 'default_model': 'hr.head.notification',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': email_to,
+ 'default_subject': self.name,
+ 'default_body': body_html,
+ 'mark_hr_notification_sent': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py b/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
index 4432a9b6c..546a6c78d 100644
--- a/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
+++ b/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
@@ -2,6 +2,8 @@ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from random import randint
+import logging
+_logger = logging.getLogger(__name__)
class HrNoticeAppraisal(models.Model):
_name = 'hr.notice.appraisal'
@@ -15,10 +17,11 @@ class HrNoticeAppraisal(models.Model):
return self.env.user.employee_id
hr_employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee_get)
+ notification_id = fields.Many2one('hr.head.notification','HR')
subject = fields.Char(string="Subject", required=True, tracking=True)
body = fields.Html(string="Notice Body", required=True)
- start_date = fields.Datetime(string="Start Date", required=True)
- end_date = fields.Datetime(string="End Date", required=True)
+ start_date = fields.Date(string="Start Date")
+ end_date = fields.Date(string="End Date")
employee_ids = fields.Many2many('hr.employee', 'notice_employee_rel', 'notice_id', 'employee_id',string="Employees")
manager_ids = fields.Many2many('hr.employee', 'notice_manager_rel', 'notice_id', 'manager_id', string="Managers")
state = fields.Selection([
@@ -45,36 +48,81 @@ class HrNoticeAppraisal(models.Model):
stage_config = fields.Many2many('employee.stage.config',string='Stages')
hr_department_ids = fields.Many2many('hr.department', string="Departments")
- @api.model
- def create(self, vals):
- if vals.get('seq', 'New') == 'New':
- company = self.env.company
- company_code = company.short_code or 'CMP'
- today = fields.Datetime.now()
- month = str(today.month).zfill(2)
- year = str(today.year)[-2:]
- prefix = f"{company_code}/{month}/{year}"
- last_record = self.search([
- ('seq', '=like', f'{prefix}%')
- ], order='id desc', limit=1)
- number = 1
- if last_record and last_record.seq:
- try:
- number = int(
- last_record.seq.split('/')[-1]
- ) + 1
- except Exception:
- number = 1
- vals['seq'] = (
- f"{prefix}/{str(number).zfill(3)}"
- )
- return super().create(vals)
+ # @api.model
+ # def create(self, vals):
+ # if vals.get('seq', 'New') == 'New':
+ # company = self.env.company
+ # company_code = company.short_code or 'CMP'
+ # today = fields.Datetime.now()
+ # month = str(today.month).zfill(2)
+ # year = str(today.year)[-2:]
+ # prefix = f"{company_code}/{month}/{year}"
+ # last_record = self.search([
+ # ('seq', '=like', f'{prefix}%')
+ # ], order='id desc', limit=1)
+ # number = 1
+ # if last_record and last_record.seq:
+ # try:
+ # number = int(
+ # last_record.seq.split('/')[-1]
+ # ) + 1
+ # except Exception:
+ # number = 1
+ # vals['seq'] = (
+ # f"{prefix}/{str(number).zfill(3)}"
+ # )
+ # return super().create(vals)
- @api.constrains('start_date', 'end_date')
- def _check_dates(self):
+ @api.constrains('start_date', 'end_date','employee_ids','manager_ids','appraisal_notice_id','state')
+ def _check_appraisal_validations(self):
for rec in self:
- if rec.end_date < rec.start_date:
- raise ValidationError(_("End Date must be greater than Start Date."))
+ if rec.start_date and rec.end_date:
+ if rec.end_date <= rec.start_date:
+ raise ValidationError(
+ _("End Date must be greater than Start Date.")
+ )
+ if rec.state == 'cancelled':
+ continue
+ duplicate_period = self.search([
+ ('id', '!=', rec.id),
+ ('appraisal_notice_id', '=', rec.appraisal_notice_id.id),
+ ('hr_employee_id', '=', rec.hr_employee_id.id),
+ ('state', '!=', 'cancelled'),
+ ], limit=1)
+ if duplicate_period:
+ raise ValidationError(_(
+ "An appraisal notification already exists for appraisal period '%s'."
+ ) % rec.appraisal_notice_id.display_name)
+
+ for employee in rec.employee_ids:
+ duplicate_employee = self.search([
+ ('id', '!=', rec.id),
+ ('employee_ids', 'in', employee.id),
+ ('state', '!=', 'cancelled'),
+ ('start_date', '<=', rec.end_date),
+ ('end_date', '>=', rec.start_date),
+ ], limit=1)
+ _logger.info(
+ "Duplicate Period Records: %s",
+ duplicate_period.ids
+ )
+ if duplicate_employee:
+ raise ValidationError(_(
+ "Employee '%s' is already assigned in another appraisal notification for the selected period."
+ ) % employee.name)
+
+ for manager in rec.manager_ids:
+ duplicate_manager = self.search([
+ ('id', '!=', rec.id),
+ ('manager_ids', 'in', manager.id),
+ ('state', '!=', 'cancelled'),
+ ('start_date', '<=', rec.end_date),
+ ('end_date', '>=', rec.start_date),
+ ], limit=1)
+ if duplicate_manager:
+ raise ValidationError(_(
+ "Manager '%s' is already assigned in another appraisal notification for the selected period."
+ ) % manager.name)
@api.onchange('employee_ids')
def _onchange_employee_ids(self):
@@ -89,45 +137,6 @@ class HrNoticeAppraisal(models.Model):
manager_emails = self.manager_ids.mapped('work_email')
all_emails = employee_emails + manager_emails
email_to = ",".join(filter(None, all_emails))
- template_obj = self.env['employee.appraisal.template']
- grouped_employees = {}
- for employee in self.employee_ids:
- manager = employee.parent_id
- department = employee.department_id
- key = (
- manager.id if manager else False,
- department.id if department else False
- )
- if key not in grouped_employees:
- grouped_employees[key] = self.env['hr.employee']
- grouped_employees[key] |= employee
- for (manager_id, department_id), employees in grouped_employees.items():
- already_exists = template_obj.search([
- ('notice_id', '=', self.id),
- # ('employee_eva_id', '=', manager_id),
- ('employee_department_id', '=', department_id),
- ], limit=1)
- if already_exists:
- continue
- template_obj.create({
- 'name': self.subject,
- 'seq': self.seq,
- 'employee_eva_id': manager_id,
- 'employee_department_id': department_id,
- 'employee_ids': [(6, 0, employees.ids)],
- 'manager_ids': [(6, 0, [manager_id])] if manager_id else [(5, 0, 0)],
- 'notice_id': self.id,
- 'start_date': self.start_date,
- 'end_date': self.end_date,
- 'appraisal_period_id': self.appraisal_notice_id.id,
- 'appraisal_period_type_id': self.appraisal_type_id.id,
- 'template_rating_bool': self.employee_rating,
- 'template_point_bool': self.employee_points,
- 'hr_employee_id': self.hr_employee_id.id,
- 'stage_config_ids': [(6, 0, self.stage_config.ids)],
- })
- # print('1234',template_obj.create({}))
-
body_html = f"""
Hello,
@@ -226,6 +235,7 @@ class StageConfig(models.Model):
name = fields.Char(required=True)
seq = fields.Integer(required=True)
+ colour_seq = fields.Integer(required=True)
active = fields.Boolean(default=True)
color = fields.Integer('Color', default=_get_default_color_stage)
@@ -240,26 +250,157 @@ class MailComposeMessage(models.TransientModel):
model = self.env.context.get('default_model')
res_ids = self.env.context.get('default_res_ids')
if self.env.context.get('mark_notice_sent'):
-
if model == 'hr.notice.appraisal' and res_ids:
records = self.env[model].browse(res_ids)
-
- records.write({
- 'state': 'sent'
- })
- if self.env.context.get('mark_appraisal_sent'):
+ template_obj = self.env['employee.appraisal.template']
+ for rec in records:
+ grouped_employees = {}
+ for employee in rec.employee_ids:
+ manager = employee.parent_id
+ department = employee.department_id
+ key = (
+ manager.id if manager else False,
+ department.id if department else False
+ )
+ if key not in grouped_employees:
+ grouped_employees[key] = self.env['hr.employee']
+ grouped_employees[key] |= employee
+ for (manager_id, department_id), employees in grouped_employees.items():
+ already_exists = template_obj.search([
+ ('notice_id', '=', rec.id),
+ ('employee_department_id', '=', department_id),
+ ], limit=1)
+ if already_exists:
+ continue
+ template_obj.create({
+ 'name': rec.subject,
+ 'seq': rec.seq,
+ 'employee_eva_id': manager_id,
+ 'employee_department_id': department_id,
+ 'employee_ids': [(6, 0, employees.ids)],
+ 'manager_ids': [(6, 0, [manager_id])] if manager_id else [(5, 0, 0)],
+ 'notice_id': rec.id,
+ 'start_date': rec.start_date,
+ 'end_date': rec.end_date,
+ 'appraisal_period_id': rec.appraisal_notice_id.id,
+ 'appraisal_period_type_id': rec.appraisal_type_id.id,
+ 'template_rating_bool': rec.employee_rating,
+ 'template_point_bool': rec.employee_points,
+ 'hr_employee_id': rec.hr_employee_id.id,
+ 'stage_config_ids': [(6, 0, rec.stage_config.ids)],
+ })
+ rec.write({
+ 'state': 'sent'
+ })
+ if (
+ self.env.context.get('mark_appraisal_sent')
+ or self.env.context.get('mark_colleague_feedback_sent')
+ or self.env.context.get('move_hr_next_stage')
+ or self.env.context.get('move_hr_head_next_stage')
+ or self.env.context.get('mark_finance_approved')
+ or self.env.context.get('mark_finance_head_approved')
+ or self.env.context.get('mark_invite_pip')
+ ):
if model == 'employee.appraisal.template.config' and res_ids:
records = self.env[model].browse(res_ids)
for record in records:
record._move_to_next_stage()
+
+ # if self.env.context.get('mark_appraisal_sent'):
+ # if model == 'employee.appraisal.template.config' and res_ids:
+ # records = self.env[model].browse(res_ids)
+ # for record in records:
+ # record._move_to_next_stage()
+ #
+ # if self.env.context.get('mark_colleague_feedback_sent'):
+ # if model == 'employee.appraisal.template.config' and res_ids:
+ # records = self.env[model].browse(res_ids)
+ # for record in records:
+ # record._move_to_next_stage()
+
if self.env.context.get('mark_appraisal_sent_appraisal'):
-
if model == 'employee.appraisal.template' and res_ids:
- records = self.env[model].browse(res_ids)
- records.write({
- 'employee_state': 'sent'
+ templates = self.env[model].browse(res_ids)
+ appraisal_config_obj = self.env[
+ 'employee.appraisal.template.config'
+ ]
+ for rec in templates:
+ first_stage = rec.stage_config_ids.sorted(
+ key=lambda s: s.seq
+ )[:1]
+ for employee in rec.employee_ids:
+ already_exists = appraisal_config_obj.search([
+ ('template_id', '=', rec.id),
+ ('employee_appraisal_id', '=', employee.id)
+ ], limit=1)
+ if already_exists:
+ continue
+ appraisal = appraisal_config_obj.create({
+ 'template_id': rec.id,
+ 'seq': rec.seq,
+ 'employee_appraisal_id': employee.id,
+ 'employee_ids': [(6, 0, rec.employee_ids.ids)],
+ 'manager_ids': [(6, 0, rec.manager_ids.ids)],
+ 'notice_id': rec.notice_id.id,
+ 'start_date': rec.start_date,
+ 'end_date': rec.end_date,
+ 'appraisal_period_id': rec.appraisal_period_id.id,
+ 'hr_apprai_id': rec.hr_employee_id.id,
+ 'managerapp_id': rec.employee_eva_id.id,
+ 'template_empl_rating_bool': rec.template_rating_bool,
+ 'template_empl_point_bool': rec.template_point_bool,
+ 'available_stage_ids': [
+ (6, 0, rec.stage_config_ids.ids)
+ ],
+ 'stage_id': first_stage.id if first_stage else False,
+ })
+
+ appraisal._onchange_template_id()
+
+ rec.write({
+ 'employee_state': 'sent'
+ })
+
+ if self.env.context.get('mark_hr_notification_sent'):
+ records = self.env[model].browse(res_ids)
+ for record in records:
+ for user in record.hr_users_ids:
+ employee = self.env['hr.employee'].search([
+ ('user_id', '=', user.id)
+ ], limit=1)
+ if not employee:
+ continue
+ already_exists = self.env[
+ 'hr.notice.appraisal'
+ ].search([
+ ('notification_id', '=', record.id),
+ ('hr_employee_id', '=', employee.id)
+ ], limit=1)
+ if already_exists:
+ continue
+ self.env['hr.notice.appraisal'].create({
+ 'notification_id': record.id,
+ 'hr_employee_id': employee.id,
+ 'subject': record.name,
+ 'appraisal_type_id':
+ record.appraisal_type_id.id,
+ 'appraisal_notice_id':
+ record.appraisal_period_id.id,
+ 'start_date': record.start_date,
+ 'end_date': record.end_date,
+ 'seq': record.seq,
+ 'body': record.body,
+ 'stage_config': [
+ (6, 0,
+ record.stage_config_ids.ids)
+ ],
+ })
+
+ record.write({
+ 'state': 'sent'
})
- return res
+
+ return res
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py b/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
index 20edde453..aaa370f3a 100644
--- a/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
+++ b/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
@@ -373,6 +373,21 @@ class EmployeeAppraisalKPILine(models.Model):
('4', '4'),
('5', '5'),
], string="Stars", copy=False)
+ is_employee_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+ is_manager_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+ is_hr_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+
+ def _compute_user_roles(self):
+ current_user = self.env.user
+ for rec in self:
+ rec.is_employee_reviewer = (
+ rec.kra_line_id.config_id.employee_appraisal_id.user_id.id == current_user.id)
+ rec.is_manager_reviewer= (
+ rec.kra_line_id.config_id.managerapp_id.user_id.id == current_user.id)
+ rec.is_hr_reviewer= (
+ rec.kra_line_id.config_id.hr_apprai_id.user_id.id == current_user.id
+ )
+
# self_rating = fields.Selection([
# ('0', '0'),
# ('1', '1'),
diff --git a/addons_extensions/hrms_employee_appraisal/models/setting_config.py b/addons_extensions/hrms_employee_appraisal/models/setting_config.py
new file mode 100644
index 000000000..4d7075129
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/setting_config.py
@@ -0,0 +1,76 @@
+from odoo import api, fields, models
+from datetime import date
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+
+ appraisal_reminder_days = fields.Integer(
+ string="Appraisal Reminder Before (Days)",
+ config_parameter='hrms_employee_appraisal.appraisal_reminder_days',
+ default=7
+ )
+
+ appraisal_reminder_enabled = fields.Boolean(
+ string="Enable Appraisal Reminders",
+ config_parameter='hrms_employee_appraisal.appraisal_reminder_enabled',
+ default=True
+ )
+
+
+
+
+
+
+class EmployeeAppraisal(models.Model):
+ _inherit = 'employee.appraisal.template.config'
+
+ def cron_send_appraisal_reminder(self):
+
+ enabled = self.env['ir.config_parameter'].sudo().get_param(
+ 'hrms_employee_appraisal.appraisal_reminder_enabled'
+ )
+
+ if not enabled:
+ return
+
+ reminder_days = int(
+ self.env['ir.config_parameter'].sudo().get_param(
+ 'hrms_employee_appraisal.appraisal_reminder_days',
+ 7
+ )
+ )
+
+ today = date.today()
+
+ records = self.search([
+ ('end_date', '!=', False)
+ ])
+
+ for rec in records:
+
+ days_left = (rec.end_date - today).days
+
+ if days_left == reminder_days:
+
+ if rec.employee_appraisal_id.work_email:
+
+ self.env['mail.mail'].sudo().create({
+ 'subject': 'Performance Appraisal Reminder',
+ 'email_to': rec.employee_appraisal_id.work_email,
+ 'body_html': f"""
+
Dear {rec.employee_appraisal_id.name},
+
+
+ Your appraisal period is ending in
+ {reminder_days} days.
+
+
+
+ Please complete your self appraisal.
+
+
+
+
Regards,
HR Team
+ """
+ }).send()
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv b/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
index 247be6304..f6abb9a48 100644
--- a/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
+++ b/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
@@ -23,7 +23,14 @@ access_appraisal_postpone_wizard,appraisal_postpone_wizard,model_appraisal_postp
access_appraisal_cancel_wizard,appraisal.cancel.wizard,model_appraisal_cancel_wizard,base.group_user,1,1,1,1
+access_hr_head_notification,hr.head.notification,model_hr_head_notification,base.group_user,1,1,1,1
+
+
access_employee_stage_config,employee.stage.config,model_employee_stage_config,base.group_user,1,1,1,1
+access_employee_pip,employee.pip,model_employee_pip,base.group_user,1,1,1,1
+access_employee_pip_task,employee.pip.task,model_employee_pip_task,base.group_user,1,1,1,1
+
+
diff --git a/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml b/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml
new file mode 100644
index 000000000..fa17c46bc
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml
@@ -0,0 +1,111 @@
+
+
+
+ HR Notice - HR Access
+
+
+ [('hr_employee_id.user_id', '=', user.id)]
+
+
+
+
+
+ HR Notice - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Appraisal Template Manager
+
+
+ [('employee_eva_id.user_id', '=', user.id)]
+
+
+
+
+
+ Appraisal Template HR
+
+
+ [('hr_employee_id.user_id', '=', user.id)]
+
+
+
+
+ Appraisal Template - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - employee access
+
+
+ [('employee_appraisal_id.user_id','=', user.id)]
+
+
+
+
+
+ Manager Access
+
+
+ [('managerapp_id.user_id','=',user.id)]
+
+
+
+
+
+
+
+
+
+ Employee Appraisal - HR Access
+
+
+ [('hr_apprai_id.user_id', '=', user.id)]
+
+
+
+
+
+ Employee Appraisal - HR Head Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Finance Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Finance Head Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/security/security_groups.xml b/addons_extensions/hrms_employee_appraisal/security/security_groups.xml
new file mode 100644
index 000000000..d9fdcda58
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/security/security_groups.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ Performance Management
+ 40
+
+
+
+ Employee
+
+
+
+
+ Appraisal Manager
+
+
+
+
+ Appraisal HR
+
+
+
+
+ Appraisal HR Head
+
+
+
+
+ Appraisal Finance
+
+
+
+
+ Appraisal Finance Head
+
+
+
+
+ Appraisal Management
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
index 7e6ce572a..9d75ce7d2 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
@@ -13,10 +13,31 @@
-
+
+
+
+
+ employee.app.search
+ employee.appraisal.template.config
+
+
+
+
+
+
+
+
+
+
+
employee.appraisal.template.config.form
employee.appraisal.template.config
@@ -32,23 +53,42 @@
domain="[('id', 'in', available_stage_ids)]"/>
-
+ groups="hrms_employee_appraisal.group_appraisal_manager"
+ class="btn-primary" invisible="stage_name != 'COLLEAGUES&MANAGER'"/>
-
-
-
+ type="object"
+ class="btn-primary"
+ invisible="stage_name != 'NEW' or not is_current_employee"/>
+
+
+
+
+ groups="hrms_employee_appraisal.group_appraisal_hr"
+ class="btn-primary" invisible="stage_name != 'HR'"/>
+
+ type="object" class="btn-primary" invisible="stage_name != 'FINANCE'"/>
+
+
+
@@ -60,21 +100,38 @@
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
+
+
+
@@ -146,27 +206,64 @@
-
-
+
+
+
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174,47 +271,35 @@
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
@@ -228,6 +313,7 @@
Employee Performance Review
employee.appraisal.template.config
list,form
+
@@ -241,7 +327,7 @@
name="Employee Performance Review"
parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template"
- sequence="03"/>
+ sequence="04"/>
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml b/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
index af47e91bb..87ec9322d 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
@@ -2,8 +2,23 @@
+ sequence="06"/>
+
+ Settings
+ res.config.settings
+ form
+ inline
+ {'module':'hrms_employee_appraisal'}
+
+
+
+
employee.appraisal.year.form
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml b/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml
new file mode 100644
index 000000000..1906e30b6
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml
@@ -0,0 +1,84 @@
+
+
+
+ employee.pip.form
+ employee.pip
+
+
+
+
+
+ employee.pip.tree
+ employee.pip
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Performance Improvement Plans
+ employee.pip
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
index fca95d041..1e0c7c385 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
@@ -10,6 +10,7 @@
type="object"
string="Send To Employee"
class="oe_highlight"
+ groups="hrms_employee_appraisal.group_appraisal_manager,hrms_employee_appraisal.group_appraisal_management"
invisible="employee_state != 'new'"/>
@@ -18,7 +19,7 @@
-
+
@@ -48,8 +49,10 @@
-
-
+
+
-
+
@@ -96,9 +102,9 @@
name="Employee Appraisal Templates"
parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template_conf"
- sequence="02"/>
+ groups="group_appraisal_management,group_appraisal_finance_head,group_appraisal_finance,group_appraisal_hr_head,group_appraisal_hr,group_appraisal_manager"
+ sequence="03"/>
-
employee.appraisal.kpi.list
@@ -119,8 +125,6 @@
-
-
employee.appraisal.kpi.form
employee.appraisal.kpi
@@ -132,22 +136,8 @@
diff --git a/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
index 8751ab333..10ef6d5cf 100644
--- a/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
@@ -6,7 +6,8 @@
-
+
Recruitment Requisitions
recruitment.requisition
list,form
+
+
+ No Recruitment Requisitions Found
+
+
+ Click Create to raise a new recruitment requisition.
+
+