#fix: Employee Performance Management Module and few HRMS bugs

This commit is contained in:
seshikanth 2026-06-24 12:20:06 +05:30
parent a8570dea30
commit adc4733e15
33 changed files with 2503 additions and 377 deletions

View File

@ -5,7 +5,7 @@
<field name="name">bench.management.line.list</field> <field name="name">bench.management.line.list</field>
<field name="model">bench.management.line</field> <field name="model">bench.management.line</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<list string="Bench Management"> <list create="0" string="Bench Management">
<field name="employee_id"/> <field name="employee_id"/>
<field name="job_id"/> <field name="job_id"/>
<field name="status"/> <field name="status"/>
@ -41,7 +41,7 @@
<field name="name">bench.management.line.form</field> <field name="name">bench.management.line.form</field>
<field name="model">bench.management.line</field> <field name="model">bench.management.line</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Bench Management"> <form create="0" string="Bench Management">
<sheet> <sheet>
<group> <group>
@ -73,9 +73,7 @@
<field name="name">bench.management.line.kanban</field> <field name="name">bench.management.line.kanban</field>
<field name="model">bench.management.line</field> <field name="model">bench.management.line</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<kanban create="0" class="o_kanban_mobile">
<kanban class="o_kanban_mobile">
<field name="employee_id"/> <field name="employee_id"/>
<field name="job_id"/> <field name="job_id"/>
<field name="status"/> <field name="status"/>
@ -85,17 +83,12 @@
<field name="future_project_count"/> <field name="future_project_count"/>
<field name="completed_project_count"/> <field name="completed_project_count"/>
<field name="project_names_tooltip"/> <field name="project_names_tooltip"/>
<templates> <templates>
<t t-name="kanban-box"> <t t-name="kanban-box">
<div class="oe_kanban_card oe_kanban_global_click" <div class="oe_kanban_card oe_kanban_global_click"
style="border-radius:16px;border:1px solid #dbe4ee;background:linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);padding:16px;min-height:260px;box-shadow:0 8px 24px rgba(15, 23, 42, 0.06);"> style="border-radius:16px;border:1px solid #dbe4ee;background:linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);padding:16px;min-height:260px;box-shadow:0 8px 24px rgba(15, 23, 42, 0.06);">
<!-- Header --> <!-- Header -->
<div class="d-flex align-items-center mb-3"> <div class="d-flex align-items-center mb-3">
<img t-att-src="'/web/image/hr.employee/' + record.employee_id.raw_value + '/avatar_128'" <img t-att-src="'/web/image/hr.employee/' + record.employee_id.raw_value + '/avatar_128'"
style=" style="
width:42px; width:42px;
@ -235,7 +228,6 @@
</field> </field>
</record> </record>
<record id="action_bench_management" model="ir.actions.act_window"> <record id="action_bench_management" model="ir.actions.act_window">
<field name="name">Bench Management</field> <field name="name">Bench Management</field>
<field name="res_model">bench.management.line</field> <field name="res_model">bench.management.line</field>
<field name="view_mode">kanban,list,form</field> <field name="view_mode">kanban,list,form</field>
@ -247,6 +239,7 @@
<menuitem id="menu_bench_management" <menuitem id="menu_bench_management"
name="Employee Bench" name="Employee Bench"
parent="hr.menu_hr_root" parent="hr.menu_hr_root"
groups="hr.group_hr_manager"
action="action_bench_management" action="action_bench_management"
sequence="3"/> sequence="3"/>
</odoo> </odoo>

View File

@ -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)

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_disciplinary_complaint_type_list" model="ir.ui.view">
<field name="name">disciplinary.complaint.type</field>
<field name="model">disciplinary.complaint.type</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
</list>
</field>
</record>
<record id="view_disciplinary_complaint_type_form" model="ir.ui.view">
<field name="name">disciplinary.complaint.type.form</field>
<field name="model">disciplinary.complaint.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_disciplinary_complaint_type" model="ir.actions.act_window">
<field name="name">Employee Disciplinary Complaint Type</field>
<field name="res_model">disciplinary.complaint.type</field>
<field name="view_mode">list,form</field>
</record>
<menuitem
id="menu_view_disciplinary_complaint"
name="Disciplinary Complaints"
action="action_disciplinary_complaint_type"
parent="menu_employee_disciplinary_root"
sequence="19"/>
<record id="view_employee_disciplinary_complaint_line_list" model="ir.ui.view">
<field name="name">hr.disciplinary.action.line</field>
<field name="model">hr.disciplinary.action.line</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
</list>
</field>
</record>
<record id="view_employee_disciplinary_complaint_line_form" model="ir.ui.view">
<field name="name">hr.disciplinary.action.line.form</field>
<field name="model">hr.disciplinary.action.line</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" />
<field name="disciplinary_id" />
<field name="action_taken_date" />
<field name="action_type_id" />
<field name="action" />
<field name="related_complaint_id" />
<field name="employee_id" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_employee_disciplinary_complaint_line" model="ir.actions.act_window">
<field name="name">Employee Disciplinary Action</field>
<field name="res_model">hr.disciplinary.action.line</field>
<field name="view_mode">list,form</field>
</record>
<menuitem
id="menu_view_employee__disciplinary_complaint"
name="Employee Disciplinary Complaints"
action="action_employee_disciplinary_complaint_line"
parent="menu_employee_disciplinary_root"
sequence="20"/>
<record id="view_disciplinary_action_type_list" model="ir.ui.view">
<field name="name">disciplinary.action.type</field>
<field name="model">disciplinary.action.type</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
</list>
</field>
</record>
<record id="view_disciplinary_action_type_form" model="ir.ui.view">
<field name="name">disciplinary.action.type.form</field>
<field name="model">disciplinary.action.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name" />
</group>
</sheet>
</form>
</field>
</record>
<record id="action_disciplinary_action_type" model="ir.actions.act_window">
<field name="name">Employee Disciplinary Action Type</field>
<field name="res_model">disciplinary.action.type</field>
<field name="view_mode">list,form</field>
</record>
<menuitem
id="menu_view_disciplinary_action_type"
name="Disciplinary Action Type"
action="action_disciplinary_action_type"
parent="menu_employee_disciplinary_root"
sequence="21"/>
</odoo>

View File

@ -0,0 +1,169 @@
<odoo>
<!-- Disciplinary Form View -->
<record id="view_hr_employee_disciplinary_form" model="ir.ui.view">
<field name="name">employee.disciplinary.form</field>
<field name="model">hr.employee.disciplinary</field>
<field name="arch" type="xml">
<form string="Employee Disciplinary">
<header>
<button name="action_set_submitted"
type="object"
string="Submit"
class="btn-primary"
invisible="state != 'new'"/>
<button name="action_set_pending"
type="object"
string="Pending"
class="btn-warning"
invisible="state != 'submitted'"/>
<button name="action_set_closed"
type="object"
string="Closed"
class="btn-success"
invisible="state != 'pending'"/>
<button name="action_set_cancel" type="object" string="Cancel"
class="btn-danger" invisible="state not in ['new', 'submitted', 'pending']"/>
<button name="action_reset_to_new" type="object"
string="Reset to New" class="btn-secondary" invisible="state != 'cancel'"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_open_related_records"
type="object"
icon="fa-gavel"
class="oe_stat_button full-width-button"
string="Disciplinary">
<field name="related_record_count" widget="statinfo"/>
</button>
</div>
<div>
<h3>
<field name="name" readonly="1"/>
</h3>
</div>
<group>
<group>
<field name="employee_code"/>
<field name="employee_id"/>
<field name="designation_id"/>
<field name="doj"/>
<!-- <field name="general_cat"/>-->
<field name="referred_by_id"/>
<!-- <field name="occurrences"/>-->
</group>
<group>
<field name="company_id"/>
<!-- <field name="unit_id"/>-->
<field name="department_id"/>
<!-- <field name="employee_section_id"/>-->
<field name="loss_of_cost"/>
<!-- <field name="cat_id"/>-->
<field name="total_cost"/>
</group>
</group>
<group string="Complaints" colspan="2">
<group colspan="1">
<field name="complaint_date"/>
<field name="language_id"/>
<field name="complaint_type_id"/>
</group>
<group colspan="1">
<field name="mistake_type_id"/>
<field name="complaint"/>
</group>
</group>
<notebook>
<!-- <page name="'complaints" string = "Complaints">-->
<!-- <field name="disciplinary_complaint_line_ids">-->
<!-- <list string="complaints" editable="bottom">-->
<field name="name" column_invisible="1"/>
<field name="complaint_date"/>
<field name="language_id"/>
<field name="complaint_type_id"/>
<field name="mistake_type_id"/>
<field name="complaint"/>
<field name="disciplinary_id" column_invisible="1"/>
<field name="employee_id" column_invisible="1"/>
<!-- </list>-->
<!-- </field>-->
<!-- </page>-->
<page name="'actions" string="Actions">
<field name="disciplinary_action_line_ids">
<list string="Action Lines" editable="bottom">
<field name="name" column_invisible="1"/>
<field name="action_name"/>
<!-- <field name="action_taken_date"/>-->
<field name="action_taken_date"
context="{'max_date': time.strftime('%Y-%m-%d')}"/>
<field name="action_type_id"/>
<field name="action"/>
<field name="disciplinary_id" column_invisible="1"/>
<field name="employee_id" column_invisible="1"/>
</list>
</field>
</page>
</notebook>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- list View for Employee Disciplinary -->
<record id="view_hr_employee_disciplinary_list" model="ir.ui.view">
<field name="name">hr.employee.disciplinary.list</field>
<field name="model">hr.employee.disciplinary</field>
<field name="arch" type="xml">
<list string="Employee Disciplinary">
<field name="name"/>
<field name="employee_id" string="Employee"/>
<field name="designation_id" string="Designation"/>
<field name="doj" string="Date of Joining"/>
<field name="company_id" string="Company"/>
<!-- <field name="unit_id" string="Unit"/>-->
<field name="department_id" string="Department"/>
<field name="state"
widget="badge"
decoration-primary="state == 'new'"
decoration-warning="state == 'submitted'"
decoration-info="state == 'pending'"
decoration-success="state == 'closed'"
decoration-danger="state == 'cancel'"/>
</list>
</field>
</record>
<record id="view_hr_employee_disciplinary_search" model="ir.ui.view">
<field name="name">hr.employee.disciplinary.search</field>
<field name="model">hr.employee.disciplinary</field>
<field name="arch" type="xml">
<search string="Search Employee Disciplinary">
<field name="employee_id" string="Employee"/>
<field name="employee_code" string="Employee Code"/>
</search>
</field>
</record>
<record id="action_hr_employee_disciplinary" model="ir.actions.act_window">
<field name="name">Employee Disciplinary</field>
<field name="res_model">hr.employee.disciplinary</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_hr_employee_disciplinary_search"/>
</record>
<!-- <menuitem id="menu_employee_disciplinary_root" name="Employee Disciplinary" sequence="15" parent="hr.menu_hr_root"/>-->
<!-- <menuitem id="menu_employee_disciplinary" name="Employee Disciplinary"-->
<!-- parent="menu_employee_disciplinary_root"-->
<!-- action="action_hr_employee_disciplinary"-->
<!-- sequence="10"/>-->
</odoo>

View File

@ -7,6 +7,7 @@ class PayrollPeriod(models.Model):
_name = 'payroll.period' _name = 'payroll.period'
_description = 'Payroll Period' _description = 'Payroll Period'
_rec_name = 'name' _rec_name = 'name'
_order = 'id desc'
_sql_constraints = [ _sql_constraints = [
('unique_name', 'unique(name)', 'The name must be unique.') ('unique_name', 'unique(name)', 'The name must be unique.')
] ]

View File

@ -41,6 +41,11 @@
<field name="payslip_count" readonly="1"/> <field name="payslip_count" readonly="1"/>
</group> </group>
</sheet> </sheet>
<footer>
<button string="Close"
special="cancel"
class="btn-secondary"/>
</footer>
</form> </form>
</field> </field>
</record> </record>

View File

@ -34,6 +34,7 @@
'data': [ 'data': [
'security/hr_resignation_security.xml', 'security/hr_resignation_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'security/resignation_groups.xml',
'data/data.xml', 'data/data.xml',
'data/ir_sequence_data.xml', 'data/ir_sequence_data.xml',
'data/ir_cron_data.xml', 'data/ir_cron_data.xml',

View File

@ -167,8 +167,33 @@ class HrResignation(models.Model):
admin_checklist_submitted = fields.Boolean(tracking=True) admin_checklist_submitted = fields.Boolean(tracking=True)
hr_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') 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') @api.depends('employee_id')
def _compute_user_rights(self): def _compute_user_rights(self):

View File

@ -0,0 +1,15 @@
<odoo>
<record model="ir.module.category" id="module_resignation_management_group">
<field name="name">Resignation Management</field>
<field name="sequence">41</field>
</record>
<record id="group_resignation_user" model="res.groups">
<field name="name">Resignation User</field>
<field name="category_id" ref="module_resignation_management_group"/>
</record>
<record id="group_resignation_manager" model="res.groups">
<field name="name">Resignation Manager</field>
<field name="category_id" ref="module_resignation_management_group"/>
</record>
</odoo>

View File

@ -150,6 +150,7 @@
<field name="employee_id" <field name="employee_id"
readonly="change_employee == False or state != 'draft'"/> readonly="change_employee == False or state != 'draft'"/>
<field name="department_id"/> <field name="department_id"/>
<field name="applied_date"/>
<field name="employee_contract" invisible="state == 'draft'"/> <field name="employee_contract" invisible="state == 'draft'"/>
<field name="emp_responded" readonly="1" force_save="1" invisible="resignation_type != 'abosconded'"/> <field name="emp_responded" readonly="1" force_save="1" invisible="resignation_type != 'abosconded'"/>
<field name="show_withdraw" invisible="not is_hr"/> <field name="show_withdraw" invisible="not is_hr"/>
@ -173,12 +174,30 @@
<field name="reason" <field name="reason"
readonly="state != 'draft'"/> readonly="state != 'draft'"/>
</group> </group>
<!-- <group string="Checklist Submission Status">-->
<!-- <field name="manager_checklist_submitted" string="Manager" readonly="1" force_save="1"/>-->
<!-- <field name="it_checklist_submitted" string="IT Manager" readonly="1" force_save="1"/>-->
<!-- <field name="finance_checklist_submitted" string="Finance" readonly="1" force_save="1"/>-->
<!-- <field name="admin_checklist_submitted" string="Admin" readonly="1" force_save="1"/>-->
<!-- <field name="hr_checklist_submitted" string="HR" readonly="1" force_save="1" invisible="not is_hr or normal_resignation_status not in ['process_relieving_documents','final_clearance']"/>-->
<!-- </group>-->
<group string="Checklist Submission Status"> <group string="Checklist Submission Status">
<field name="manager_checklist_submitted" string="Manager" readonly="1" force_save="1"/> <field name="manager_checklist_status" string="Manager" widget="badge"
<field name="it_checklist_submitted" string="IT Manager" readonly="1" force_save="1"/> decoration-success="manager_checklist_status == 'completed'"
<field name="finance_checklist_submitted" string="Finance" readonly="1" force_save="1"/> decoration-warning="manager_checklist_status == 'pending'"/>
<field name="admin_checklist_submitted" string="Admin" readonly="1" force_save="1"/> <field name="it_checklist_status" string="IT Manager" widget="badge"
<field name="hr_checklist_submitted" string="HR" readonly="1" force_save="1" invisible="not is_hr or normal_resignation_status not in ['process_relieving_documents','final_clearance']"/> decoration-success="it_checklist_status == 'completed'"
decoration-warning="it_checklist_status == 'pending'"/>
<field name="finance_checklist_status" string="Finance" widget="badge"
decoration-success="finance_checklist_status == 'completed'"
decoration-warning="finance_checklist_status == 'pending'"/>
<field name="admin_checklist_status" string="Admin" widget="badge"
decoration-success="admin_checklist_status == 'completed'"
decoration-warning="admin_checklist_status == 'pending'"/>
<field name="hr_checklist_status" string="HR" widget="badge"
decoration-success="hr_checklist_status == 'completed'"
decoration-warning="hr_checklist_status == 'pending'"
invisible="not is_hr or normal_resignation_status not in ['process_relieving_documents','final_clearance']"/>
</group> </group>
</group> </group>
<notebook> <notebook>
@ -186,7 +205,8 @@
<group> <group>
<field name="emp_comments" readonly="not is_emp" string="Employee"/> <field name="emp_comments" readonly="not is_emp" string="Employee"/>
<field name="manager_comments" readonly="not is_manager" string="Manager"/> <field name="manager_comments" readonly="not is_manager" string="Manager"/>
<field name="finance_manager_comments" readonly="not is_finance_manager" string="Finance Manager"/> <field name="finance_manager_comments" readonly="not is_finance_manager"
string="Finance Manager"/>
<field name="it_manager_comments" readonly="not is_it_manager" string="IT Manager"/> <field name="it_manager_comments" readonly="not is_it_manager" string="IT Manager"/>
<field name="admin_comments" readonly="not is_admin" string="Admin"/> <field name="admin_comments" readonly="not is_admin" string="Admin"/>
<field name="hr_comments" readonly="not is_hr" string="HR"/> <field name="hr_comments" readonly="not is_hr" string="HR"/>
@ -387,7 +407,7 @@
<!-- Menu item for Approved Resignation --> <!-- Menu item for Approved Resignation -->
<menuitem id="hr_resignation_menu_approved_request" <menuitem id="hr_resignation_menu_approved_request"
parent="hr_resignation_menu_root" parent="hr_resignation_menu_root"
name="Approved Resignation" name="Resignation Status"
action="hr_resignation_approved_action" action="hr_resignation_approved_action"
groups="hr.group_hr_user" groups="hr.group_hr_user"
sequence="4"/> sequence="4"/>

View File

@ -24,7 +24,7 @@
</field> </field>
</record> </record>
<menuitem id="menu_hr_resignation_configurations" name="Configurations" parent="hr_resignation_menu_root" sequence="100"/> <menuitem id="menu_hr_resignation_configurations" name="Configurations" groups="hr_resignation.group_resignation_manager" parent="hr_resignation_menu_root" sequence="100"/>
<menuitem id="menu_pre_resignation_root" name="Pre-Resignation" parent="menu_hr_resignation_configurations" sequence="100"/> <menuitem id="menu_pre_resignation_root" name="Pre-Resignation" parent="menu_hr_resignation_configurations" sequence="100"/>
<menuitem id="menu_pre_resignation_requirements" name="Requirements Proceedings" <menuitem id="menu_pre_resignation_requirements" name="Requirements Proceedings"

View File

@ -109,9 +109,27 @@
margin-bottom: 4px; margin-bottom: 4px;
} }
.hrms-icon-button.primary { /*.hrms-icon-button.primary {*/
/* background: #16a34a;*/
/* border-color: #22c55e;*/
/*}*/
.hrms-icon-button.checkin {
background: #16a34a; background: #16a34a;
border-color: #22c55e; color: #fff;
}
.hrms-icon-button.checkout {
background: #dc2626;
color: #fff;
}
.hrms-icon-button.checkout:hover {
background: #b91c1c;
}
.hrms-icon-button.checkin:hover {
background: #15803d;
} }
.hrms-filter-box { .hrms-filter-box {

View File

@ -292,12 +292,15 @@ class HrmsEmployeeDashboard extends Component {
} }
// raiseHelpdeskTicket() {
// window.open(
// '/helpdesk/new',
// 'helpdesk',
// 'width=1200,height=800,resizable=yes,scrollbars=yes'
// );
// }
raiseHelpdeskTicket() { raiseHelpdeskTicket() {
window.open( window.open('/helpdesk/new', '_blank');
'/helpdesk/new',
'helpdesk',
'width=1200,height=800,resizable=yes,scrollbars=yes'
);
} }
async toggleAttendance() { async toggleAttendance() {
@ -309,6 +312,9 @@ class HrmsEmployeeDashboard extends Component {
} }
this.notification.add(response.message, { type: "success" }); this.notification.add(response.message, { type: "success" });
await this.loadData(); await this.loadData();
setTimeout(() =>{
window.location.reload();
}, 500);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
this.notification.add("Unable to update attendance", { type: "danger" }); this.notification.add("Unable to update attendance", { type: "danger" });

View File

@ -27,10 +27,21 @@
</div> </div>
<div class="hrms-employee-actions"> <div class="hrms-employee-actions">
<div class="hrms-action-buttons"> <div class="hrms-action-buttons">
<button class="hrms-icon-button primary" t-on-click="toggleAttendance" t-att-title="statusText"> <!-- <button class="hrms-icon-button primary" t-on-click="toggleAttendance" t-att-title="statusText">-->
<i t-att-class="state.data.attendance_state === 'checked_in' ? 'fa fa-sign-out' : 'fa fa-sign-in'"/> <!-- <i t-att-class="state.data.attendance_state === 'checked_in' ? 'fa fa-sign-out' : 'fa fa-sign-in'"/>-->
<!-- <span t-esc="statusText"/>-->
<!-- </button>-->
<button
t-att-class="'hrms-icon-button ' + (state.data.attendance_state === 'checked_in' ? 'checkout' : 'checkin')"
t-on-click="toggleAttendance"
t-att-title="statusText">
<i t-att-class="state.data.attendance_state === 'checked_in'
? 'fa fa-sign-out'
: 'fa fa-sign-in'"/>
<span t-esc="statusText"/> <span t-esc="statusText"/>
</button> </button>
<button class="hrms-icon-button" t-on-click="downloadPayslip" title="Download Payslip"> <button class="hrms-icon-button" t-on-click="downloadPayslip" title="Download Payslip">
<i class="fa fa-download"/> <i class="fa fa-download"/>
<span>Payslip</span> <span>Payslip</span>

View File

@ -13,12 +13,16 @@
'depends': ['base', 'hr','hr_employee_extended'], 'depends': ['base', 'hr','hr_employee_extended'],
'data': [ 'data': [
'data/reminder_corn.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'security/security_groups.xml',
'security/performace_record_rules.xml',
'views/employee_appraisal.xml', 'views/employee_appraisal.xml',
'views/employee_evalutor.xml', 'views/employee_evalutor.xml',
'views/employee_template_appraisal.xml', 'views/employee_template_appraisal.xml',
'views/hr_notice_appraisal.xml', 'views/hr_notice_appraisal.xml',
'views/stage_config.xml', 'views/stage_config.xml',
'views/employee_pip.xml',
'Wizard/cancel_wizard_hr.xml', 'Wizard/cancel_wizard_hr.xml',
'Wizard/postpone_hr_appraisal.xml', 'Wizard/postpone_hr_appraisal.xml',
], ],

View File

@ -2,3 +2,6 @@ from . import employee_appraisal
from . import apprasial_conf from . import apprasial_conf
from . import kpi_kra from . import kpi_kra
from . import hr_notice_appraisal from . import hr_notice_appraisal
from . import employee_pip
from . import setting_config
from . import hr_head_nofication

View File

@ -1,4 +1,8 @@
from odoo import api, fields, models from odoo import api, fields, models,_
from odoo.exceptions import ValidationError
from dateutil.relativedelta import relativedelta
class AppraisalYear(models.Model): class AppraisalYear(models.Model):
_name = 'employee.appraisal.year' _name = 'employee.appraisal.year'
@ -9,8 +13,8 @@ class AppraisalYear(models.Model):
year = fields.Integer(string="Performance Period") year = fields.Integer(string="Performance Period")
appraisal_name_id = fields.Char(string="Appraisal Type") appraisal_name_id = fields.Char(string="Appraisal Type")
appraisal_name = fields.Char(string="Appraisal Name") appraisal_name = fields.Char(string="Appraisal Name")
start_month = fields.Datetime('Starting Month') start_month = fields.Date('Starting Month')
end_month = fields.Datetime('End Month') end_month = fields.Date('End Month')
active = fields.Boolean(default=True) active = fields.Boolean(default=True)
year_seq = fields.Integer(string="Sequence") year_seq = fields.Integer(string="Sequence")
appraisal_type_id = fields.Many2one( appraisal_type_id = fields.Many2one(
@ -18,6 +22,55 @@ class AppraisalYear(models.Model):
string="Appraisal Type" string="Appraisal Type"
) )
@api.constrains('appraisal_type_id', 'start_month', 'end_month')
def _check_appraisal_type(self):
for rec in self:
if not rec.start_month or not rec.end_month:
continue
if rec.start_month > rec.end_month:
raise ValidationError(
_("Start Month cannot be greater than End Month.")
)
expected_end = False
if rec.appraisal_type_id.name == 'Monthly':
expected_end = rec.start_month + relativedelta(months=1, days=-1)
elif rec.appraisal_type_id.name == 'Quarterly':
expected_end = rec.start_month + relativedelta(months=3, days=-1)
elif rec.appraisal_type_id.name == 'Half-yearly':
expected_end = rec.start_month + relativedelta(months=6, days=-1)
elif rec.appraisal_type_id.name == 'Annual':
expected_end = rec.start_month + relativedelta(months=12, days=-1)
if expected_end and rec.end_month != expected_end:
raise ValidationError(_(
"Invalid period for %s.\nExpected End Date: %s"
) % (
rec.appraisal_type_id.name,
expected_end.strftime('%d/%m/%Y')
))
duplicate = self.search([
('id', '!=', rec.id),
('appraisal_type_id', '=', rec.appraisal_type_id.id),
('start_month', '<=', rec.end_month),
('end_month', '>=', rec.start_month),
], limit=1)
if duplicate:
raise ValidationError(
_("This appraisal period overlaps with existing period: %s")
% duplicate.appraisal_name
)
class EmployeeAppraisalType(models.Model): class EmployeeAppraisalType(models.Model):
_name = 'employee.appraisal.type' _name = 'employee.appraisal.type'
_description = 'Employee Appraisal Type' _description = 'Employee Appraisal Type'
@ -36,7 +89,7 @@ class AppraisalTemplate(models.Model):
name = fields.Char(string="Name") name = fields.Char(string="Name")
employee_evaluator_name_id = fields.Many2one('employee.appraisal.evaluator', string="Employee Appraisal Evaluator") employee_evaluator_name_id = fields.Many2one('employee.appraisal.evaluator', string="Employee Appraisal Evaluator")
employee_eva_id = fields.Many2one('hr.employee',string="Employee Appraisal Evaluator") employee_eva_id = fields.Many2one('hr.employee',string="Manager")
hr_employee_id = fields.Many2one('hr.employee',string="Employee HR Employee") hr_employee_id = fields.Many2one('hr.employee',string="Employee HR Employee")
employee_department_id = fields.Many2one('hr.department',string="Department") employee_department_id = fields.Many2one('hr.department',string="Department")
company_id = fields.Many2one('res.company', string="Company",default=lambda self: self.env.company) company_id = fields.Many2one('res.company', string="Company",default=lambda self: self.env.company)
@ -69,39 +122,40 @@ class AppraisalTemplate(models.Model):
rec.kra_ids.mapped('kra_weightage')) rec.kra_ids.mapped('kra_weightage'))
def action_sent_employee(self): def action_sent_employee(self):
appraisal_config_obj = self.env['employee.appraisal.template.config']
for rec in self: for rec in self:
first_stage = rec.stage_config_ids.sorted( # appraisal_config_obj = self.env['employee.appraisal.template.config']
key=lambda s: s.seq # for rec in self:
)[:1] # first_stage = rec.stage_config_ids.sorted(
for employee in rec.employee_ids: # key=lambda s: s.seq
already_exists = appraisal_config_obj.search([ # )[:1]
('template_id', '=', rec.id), # for employee in rec.employee_ids:
('employee_appraisal_id', '=', employee.id) # already_exists = appraisal_config_obj.search([
], limit=1) # ('template_id', '=', rec.id),
if already_exists: # ('employee_appraisal_id', '=', employee.id)
continue # ], limit=1)
appraisal = appraisal_config_obj.create({ # if already_exists:
'template_id': rec.id, # continue
'seq': rec.seq, # appraisal = appraisal_config_obj.create({
'employee_appraisal_id': employee.id, # 'template_id': rec.id,
'employee_ids': [(6, 0, rec.employee_ids.ids)], # 'seq': rec.seq,
'manager_ids': [(6, 0, rec.manager_ids.ids)], # 'employee_appraisal_id': employee.id,
'notice_id': rec.notice_id.id, # 'employee_ids': [(6, 0, rec.employee_ids.ids)],
'start_date': rec.start_date, # 'manager_ids': [(6, 0, rec.manager_ids.ids)],
'end_date': rec.end_date, # 'notice_id': rec.notice_id.id,
'appraisal_period_id': rec.appraisal_period_id.id, # 'start_date': rec.start_date,
'hr_apprai_id': rec.hr_employee_id.id, # 'end_date': rec.end_date,
'managerapp_id': rec.employee_eva_id.id, # 'appraisal_period_id': rec.appraisal_period_id.id,
'template_empl_rating_bool': rec.template_rating_bool, # 'hr_apprai_id': rec.hr_employee_id.id,
'template_empl_point_bool': rec.template_point_bool, # 'managerapp_id': rec.employee_eva_id.id,
'available_stage_ids': [ # 'template_empl_rating_bool': rec.template_rating_bool,
(6, 0, rec.stage_config_ids.ids) # 'template_empl_point_bool': rec.template_point_bool,
], # 'available_stage_ids': [
'stage_id': first_stage.id if first_stage else False, # (6, 0, rec.stage_config_ids.ids)
# 'state': 'self_evaluation', # ],
}) # 'stage_id': first_stage.id if first_stage else False,
appraisal._onchange_template_id() # # 'state': 'self_evaluation',
# })
# appraisal._onchange_template_id()
employee_emails = rec.employee_ids.mapped('work_email') employee_emails = rec.employee_ids.mapped('work_email')
manager_emails = rec.manager_ids.mapped('work_email') manager_emails = rec.manager_ids.mapped('work_email')
all_emails = employee_emails + manager_emails all_emails = employee_emails + manager_emails

View File

@ -20,11 +20,13 @@ class EmployeeAppraisal(models.Model):
name = fields.Char(string="Reference", copy=False) name = fields.Char(string="Reference", copy=False)
employee_evaluator_name_id = fields.Many2one('employee.appraisal.evaluator', string="Employee Appraisal Evaluator") employee_evaluator_name_id = fields.Many2one('employee.appraisal.evaluator', string="Employee Appraisal Evaluator")
hr_apprai_id = fields.Many2one('hr.employee') hr_apprai_id = fields.Many2one('hr.employee')
managerapp_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')]) performance_evaluator = fields.Selection([('manager', 'Manager'), ('colleague', 'Colleague'), ('own', 'Own')])
template_id = fields.Many2one('employee.appraisal.template', string="Template") template_id = fields.Many2one('employee.appraisal.template', string="Template")
stage_id = fields.Many2one('employee.stage.config',string='Stage') stage_id = fields.Many2one('employee.stage.config', string='Stage')
available_stage_ids = fields.Many2many('employee.stage.config',compute='_compute_available_stages') 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) 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) company_id = fields.Many2one('res.company', string="Company", default=lambda self: self.env.company)
state = fields.Selection([ state = fields.Selection([
@ -43,13 +45,18 @@ class EmployeeAppraisal(models.Model):
employee_appraisal_id = fields.Many2one('hr.employee') employee_appraisal_id = fields.Many2one('hr.employee')
image_1920 = fields.Image(related='employee_appraisal_id.image_1920') 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) 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) department_appraisal_id = fields.Many2one("hr.department", string="Department",
job_appraisal_id = fields.Many2one("hr.job", string="Job Position", related="employee_appraisal_id.job_id",store=True) 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') kra_line_ids = fields.One2many('employee.appraisal.kra.line', 'config_id', string='Kra')
manager_remarks = fields.Char(string="Manager Remarks") manager_remarks = fields.Char(string="Manager Remarks")
hr_remarks = fields.Char(string="HR 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') 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_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) 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) creator_email = fields.Char(related='created_by_id.work_email', string="Creator Email", readonly=True)
notice_id = fields.Many2one('hr.notice.appraisal', string="Notice") notice_id = fields.Many2one('hr.notice.appraisal', string="Notice")
@ -60,13 +67,16 @@ class EmployeeAppraisal(models.Model):
is_readonly = fields.Boolean(compute="_compute_is_readonly") is_readonly = fields.Boolean(compute="_compute_is_readonly")
mail_sent_employee_ids = fields.Many2many('hr.employee', string="Mail Sent Employees") mail_sent_employee_ids = fields.Many2many('hr.employee', string="Mail Sent Employees")
seq = fields.Char(string="Sequence", readonly=True, copy=False) 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") employee_ids = fields.Many2many('hr.employee', 'appraisal_config_employee_rel', 'config_id', 'employee_id',
manager_ids = fields.Many2many('hr.employee', 'appraisal_config_manager_rel', 'config_id', 'manager_id',string="Managers") 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_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_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) 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") 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) 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_score = fields.Float(compute="_compute_overall_scores", store=True)
overall_rating = fields.Selection([ overall_rating = fields.Selection([
('0', '0'), ('0', '0'),
@ -91,9 +101,9 @@ class EmployeeAppraisal(models.Model):
('4', '4'), ('4', '4'),
('5', '5'), ('5', '5'),
], string="Overall Stars") ], string="Overall Stars")
employee_overall_score = fields.Float(compute='_compute_overall_scores',store=True) employee_overall_score = fields.Float(compute='_compute_overall_scores', store=True)
manager_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) hr_overall_score = fields.Float(compute='_compute_overall_scores', store=True)
employee_overall_star = fields.Selection([ employee_overall_star = fields.Selection([
('0', '0'), ('0', '0'),
('1', '1'), ('1', '1'),
@ -119,14 +129,73 @@ class EmployeeAppraisal(models.Model):
('5', '5'), ('5', '5'),
], compute='_compute_overall_scores', store=True) ], compute='_compute_overall_scores', store=True)
finance_user_id = fields.Many2one('res.users', string="Finance Approved By", readonly=True) finance_user_id = fields.Many2one('res.users', string="Finance Approved By", readonly=True)
current_salary = fields.Float(string="Current Salary") 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_percentage = fields.Float(string="Appraisal %")
appraisal_amount = fields.Float(string="Appraisal Amount") appraisal_amount = fields.Float(string="Appraisal Amount")
new_salary = fields.Float(string="Revised Salary", compute="_compute_new_salary", store=True) new_salary = fields.Float(string="Revised Salary", compute="_compute_new_salary", store=True)
finance_remarks = fields.Text(string="Finance Remarks") finance_remarks = fields.Text(string="Finance Remarks")
finance_date = fields.Datetime(string="Finance Approval Date", readonly=True) 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_rating_bool = fields.Boolean('Star Rating')
template_empl_point_bool = fields.Boolean('Point 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') @api.depends('template_id')
def _compute_available_stages(self): def _compute_available_stages(self):
@ -322,11 +391,20 @@ class EmployeeAppraisal(models.Model):
else: else:
rec.appraisal_percentage = 0 rec.appraisal_percentage = 0
@api.depends('manager_ids') # @api.depends('manager_ids')
def _compute_manager_email(self): # 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: for rec in self:
emails = rec.manager_ids.mapped('work_email') rec.manager_email = rec.managerapp_id.work_email or ''
rec.manager_email = ",".join(filter(None, emails))
@api.onchange('employee_appraisal_id')
def _onchange_employee_appraisal_id(self):
self.managerapp_id = self.employee_appraisal_id.parent_id
@api.depends('end_date') @api.depends('end_date')
def _compute_is_readonly(self): def _compute_is_readonly(self):
@ -373,44 +451,298 @@ class EmployeeAppraisal(models.Model):
@api.depends('current_salary', 'appraisal_amount') @api.depends('current_salary', 'appraisal_amount')
def _compute_new_salary(self): def _compute_new_salary(self):
for rec in self: for rec in self:
rec.new_salary = ( rec.new_salary = rec.current_salary + rec.appraisal_amount
rec.current_salary +
rec.appraisal_amount
)
@api.onchange('appraisal_percentage') @api.onchange('appraisal_percentage')
def _onchange_appraisal_percentage(self): def _onchange_appraisal_percentage(self):
for rec in self: for rec in self:
if rec.current_salary: if rec.current_salary:
rec.appraisal_amount = ( rec.appraisal_amount = (
rec.current_salary * rec.current_salary * rec.appraisal_percentage
rec.appraisal_percentage
) / 100 ) / 100
def _move_to_next_stage(self): def _move_to_next_stage(self):
self.ensure_one() self.ensure_one()
next_stage = self.env['employee.stage.config'].search( next_stage = self.env['employee.stage.config'].search(
[('seq', '>', self.stage_id.seq)], [('id', 'in', self.available_stage_ids.ids),
('seq', '>', self.stage_id.seq)],
order='seq asc', order='seq asc',
limit=1 limit=1
) )
if next_stage: if next_stage:
self.stage_id = next_stage.id self.stage_id = next_stage.id
def action_finance_approve(self): def action_create_pip(self):
for rec in self: self.ensure_one()
rec.write({
'state': 'management_team',
'finance_user_id': self.env.user.id,
'finance_date': fields.Datetime.now()
})
rec.message_post( pip = self.env['employee.pip'].create({
body=_( 'employee_id': self.employee_appraisal_id.id,
"Finance appraisal approved." '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
])
)
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': email_to,
'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
])
)
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': email_to,
'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): def _send_manager_notification_mail(self):
self.ensure_one() self.ensure_one()
email_to = self.manager_email email_to = self.manager_email
@ -434,7 +766,7 @@ class EmployeeAppraisal(models.Model):
</tr> </tr>
<tr> <tr>
<td><b>Performance Period</b></td> <td><b>Performance Period</b></td>
<td>{self.appraisal_period_id.appraisal_type_id.id or ''}</td> <td>{self.appraisal_period_id.appraisal_name or ''}</td>
</tr> </tr>
</table> </table>
<br/> <br/>
@ -455,81 +787,94 @@ class EmployeeAppraisal(models.Model):
} }
self.env['mail.mail'].create(mail_values).send() self.env['mail.mail'].create(mail_values).send()
def check_colleague_feedback_deadline(self): # def check_colleague_feedback_deadline(self):
now = fields.Datetime.now() # now = fields.Datetime.now()
records = self.search([ # records = self.search([
('college_end_date_time', '!=', False), # ('college_end_date_time', '!=', False),
('college_end_date_time', '<=', now), # ('college_end_date_time', '<=', now),
('state', '=', 'colleague_feedback') # ('state', '=', 'colleague_feedback')
]) # ])
for rec in records: # for rec in records:
rec.write({ # rec.write({
'state': 'in_progress' # 'state': 'in_progress'
}) # })
rec.message_post( # rec.message_post(
body=_( # body=_(
"Colleague feedback deadline completed automatically. " # "Colleague feedback deadline completed automatically. "
"Stage moved to Manager Evaluation." # "Stage moved to Manager Evaluation."
) # )
) # )
rec._send_manager_notification_mail() # rec._send_manager_notification_mail()
return True # return True
def action_sent_employee_appraisal(self): def action_sent_employee_appraisal(self):
self.ensure_one() self.ensure_one()
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 '' employee_email = self.employee_appraisal_id.work_email or ''
creator_email = self.creator_email or '' creator_email = self.creator_email or ''
emails = ",".join( emails = ",".join(
filter(None, [employee_email, creator_email]) filter(None, [
manager_email,
employee_email,
creator_email
])
) )
body_html = f""" body_html = f"""
<div> <div>
<p>Hello,Team</p> <p>Hello Team,</p>
<p>
Your appraisal evaluation has been initiated.
</p>
<br/>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<td>
<b>Employee</b>
</td>
<td> <p>
{self.employee_appraisal_id.name or ''} The employee has completed the self-assessment as part of the performance appraisal process.
</td> Your feedback and evaluation are now requested.
</tr> </p>
<tr>
<td>
<b>Template</b>
</td>
<td> <br/>
{self.template_id.name or ''}
</td>
</tr>
<tr>
<td>
<b>Performance Period</b>
</td>
<td> <table border="1" cellpadding="5" cellspacing="0">
{self.appraisal_period_id.appraisal_type_id.id or ''} <tr>
</td> <td><b>Employee</b></td>
</tr> <td>{self.employee_appraisal_id.name or ''}</td>
</table> </tr>
<br/> <tr>
<p> <td><b>Department</b></td>
Please complete the self evaluation before deadline. <td>{self.department_appraisal_id.name or ''}</td>
</p> </tr>
<br/> <tr>
<p> <td><b>Appraisal Template</b></td>
Regards, <td>{self.template_id.name or ''}</td>
</p> </tr>
<p> <tr>
HR Team <td><b>Performance Period</b></td>
</p> <td>{self.appraisal_period_id.appraisal_name or ''}</td>
</div> </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 = { ctx = {
'default_model': 'employee.appraisal.template.config', 'default_model': 'employee.appraisal.template.config',
@ -549,17 +894,132 @@ class EmployeeAppraisal(models.Model):
'context': ctx, '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): def action_confirm_hr(self):
for rec in self: self.ensure_one()
rec.state = 'finance_team'
if not self.hr_remarks:
raise ValidationError ('Please Provide the Remarks')
email_to = self.managerapp_id.work_email or ''
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_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"""
<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': 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): def action_send_colleague_feedback(self):
for rec in self: for rec in self:
@ -568,6 +1028,7 @@ class EmployeeAppraisal(models.Model):
('id', '!=', rec.employee_appraisal_id.id) ('id', '!=', rec.employee_appraisal_id.id)
]) ])
vals = [] vals = []
email_list = []
for emp in employees: for emp in employees:
already_exists = self.env['colleague.feedback'].search([ already_exists = self.env['colleague.feedback'].search([
('employee_appraisal_feed_id', '=', rec.id), ('employee_appraisal_feed_id', '=', rec.id),
@ -578,7 +1039,123 @@ class EmployeeAppraisal(models.Model):
'colleague_feed_id': emp.id, 'colleague_feed_id': emp.id,
})) }))
rec.colleague_feed_ids = vals 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"""
<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': 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"""
<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') @api.onchange('template_id')
def _onchange_template_id(self): def _onchange_template_id(self):
@ -624,7 +1201,6 @@ class ColleagueFeedBack(models.Model):
submitted_date = fields.Datetime() submitted_date = fields.Datetime()
def action_submit_feedback(self): def action_submit_feedback(self):
for rec in self: for rec in self:
rec.write({ rec.write({
'state': 'submitted', 'state': 'submitted',

View File

@ -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')

View File

@ -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"""
<div>
<p>Hello HR Team,</p>
<p>{self.body or ''}</p>
<table border="1" cellpadding="5" cellspacing="0">
<tr>
<td><b>Appraisal Type</b></td>
<td>{self.appraisal_type_id.name or ''}</td>
</tr>
<tr>
<td><b>Appraisal Period</b></td>
<td>{self.appraisal_period_id.appraisal_name or ''}</td>
</tr>
<tr>
<td><b>Start Date</b></td>
<td>{self.start_date or ''}</td>
</tr>
<tr>
<td><b>End Date</b></td>
<td>{self.end_date or ''}</td>
</tr>
</table>
<br/>
<p>Please initiate the appraisal process.</p>
<p>Regards,</p>
<p>HR Head</p>
</div>
"""
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,
}

View File

@ -2,6 +2,8 @@ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
from random import randint from random import randint
import logging
_logger = logging.getLogger(__name__)
class HrNoticeAppraisal(models.Model): class HrNoticeAppraisal(models.Model):
_name = 'hr.notice.appraisal' _name = 'hr.notice.appraisal'
@ -15,10 +17,11 @@ class HrNoticeAppraisal(models.Model):
return self.env.user.employee_id return self.env.user.employee_id
hr_employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee_get) 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) subject = fields.Char(string="Subject", required=True, tracking=True)
body = fields.Html(string="Notice Body", required=True) body = fields.Html(string="Notice Body", required=True)
start_date = fields.Datetime(string="Start Date", required=True) start_date = fields.Date(string="Start Date")
end_date = fields.Datetime(string="End Date", required=True) end_date = fields.Date(string="End Date")
employee_ids = fields.Many2many('hr.employee', 'notice_employee_rel', 'notice_id', 'employee_id',string="Employees") 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") manager_ids = fields.Many2many('hr.employee', 'notice_manager_rel', 'notice_id', 'manager_id', string="Managers")
state = fields.Selection([ state = fields.Selection([
@ -45,36 +48,81 @@ class HrNoticeAppraisal(models.Model):
stage_config = fields.Many2many('employee.stage.config',string='Stages') stage_config = fields.Many2many('employee.stage.config',string='Stages')
hr_department_ids = fields.Many2many('hr.department', string="Departments") hr_department_ids = fields.Many2many('hr.department', string="Departments")
@api.model # @api.model
def create(self, vals): # def create(self, vals):
if vals.get('seq', 'New') == 'New': # if vals.get('seq', 'New') == 'New':
company = self.env.company # company = self.env.company
company_code = company.short_code or 'CMP' # company_code = company.short_code or 'CMP'
today = fields.Datetime.now() # today = fields.Datetime.now()
month = str(today.month).zfill(2) # month = str(today.month).zfill(2)
year = str(today.year)[-2:] # year = str(today.year)[-2:]
prefix = f"{company_code}/{month}/{year}" # prefix = f"{company_code}/{month}/{year}"
last_record = self.search([ # last_record = self.search([
('seq', '=like', f'{prefix}%') # ('seq', '=like', f'{prefix}%')
], order='id desc', limit=1) # ], order='id desc', limit=1)
number = 1 # number = 1
if last_record and last_record.seq: # if last_record and last_record.seq:
try: # try:
number = int( # number = int(
last_record.seq.split('/')[-1] # last_record.seq.split('/')[-1]
) + 1 # ) + 1
except Exception: # except Exception:
number = 1 # number = 1
vals['seq'] = ( # vals['seq'] = (
f"{prefix}/{str(number).zfill(3)}" # f"{prefix}/{str(number).zfill(3)}"
) # )
return super().create(vals) # return super().create(vals)
@api.constrains('start_date', 'end_date') @api.constrains('start_date', 'end_date','employee_ids','manager_ids','appraisal_notice_id','state')
def _check_dates(self): def _check_appraisal_validations(self):
for rec in self: for rec in self:
if rec.end_date < rec.start_date: if rec.start_date and rec.end_date:
raise ValidationError(_("End Date must be greater than Start 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') @api.onchange('employee_ids')
def _onchange_employee_ids(self): def _onchange_employee_ids(self):
@ -89,45 +137,6 @@ class HrNoticeAppraisal(models.Model):
manager_emails = self.manager_ids.mapped('work_email') manager_emails = self.manager_ids.mapped('work_email')
all_emails = employee_emails + manager_emails all_emails = employee_emails + manager_emails
email_to = ",".join(filter(None, all_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""" body_html = f"""
<div> <div>
<p>Hello,</p> <p>Hello,</p>
@ -226,6 +235,7 @@ class StageConfig(models.Model):
name = fields.Char(required=True) name = fields.Char(required=True)
seq = fields.Integer(required=True) seq = fields.Integer(required=True)
colour_seq = fields.Integer(required=True)
active = fields.Boolean(default=True) active = fields.Boolean(default=True)
color = fields.Integer('Color', default=_get_default_color_stage) color = fields.Integer('Color', default=_get_default_color_stage)
@ -240,26 +250,157 @@ class MailComposeMessage(models.TransientModel):
model = self.env.context.get('default_model') model = self.env.context.get('default_model')
res_ids = self.env.context.get('default_res_ids') res_ids = self.env.context.get('default_res_ids')
if self.env.context.get('mark_notice_sent'): if self.env.context.get('mark_notice_sent'):
if model == 'hr.notice.appraisal' and res_ids: if model == 'hr.notice.appraisal' and res_ids:
records = self.env[model].browse(res_ids) records = self.env[model].browse(res_ids)
template_obj = self.env['employee.appraisal.template']
records.write({ for rec in records:
'state': 'sent' grouped_employees = {}
}) for employee in rec.employee_ids:
if self.env.context.get('mark_appraisal_sent'): 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: if model == 'employee.appraisal.template.config' and res_ids:
records = self.env[model].browse(res_ids) records = self.env[model].browse(res_ids)
for record in records: for record in records:
record._move_to_next_stage() 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 self.env.context.get('mark_appraisal_sent_appraisal'):
if model == 'employee.appraisal.template' and res_ids: if model == 'employee.appraisal.template' and res_ids:
records = self.env[model].browse(res_ids)
records.write({ templates = self.env[model].browse(res_ids)
'employee_state': 'sent' 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

View File

@ -373,6 +373,21 @@ class EmployeeAppraisalKPILine(models.Model):
('4', '4'), ('4', '4'),
('5', '5'), ('5', '5'),
], string="Stars", copy=False) ], 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([ # self_rating = fields.Selection([
# ('0', '0'), # ('0', '0'),
# ('1', '1'), # ('1', '1'),

View File

@ -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"""
<p>Dear {rec.employee_appraisal_id.name},</p>
<p>
Your appraisal period is ending in
<b>{reminder_days}</b> days.
</p>
<p>
Please complete your self appraisal.
</p>
<br/>
<p>Regards,<br/>HR Team</p>
"""
}).send()

View File

@ -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_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_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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
23
24
25
26
27
28
29
30
31
32
33
34
35
36

View File

@ -0,0 +1,111 @@
<odoo>
<data>
<record id="hr_notice_hr_rule" model="ir.rule">
<field name="name">HR Notice - HR Access</field>
<field name="model_id" ref="model_hr_notice_appraisal"/>
<field name="domain_force">
[('hr_employee_id.user_id', '=', user.id)]
</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_hr'))]"/>
</record>
<!-- Management Access - HR Notice -->
<record id="hr_notice_management_rule" model="ir.rule">
<field name="name">HR Notice - Management Access</field>
<field name="model_id" ref="model_hr_notice_appraisal"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_management'))]"/>
</record>
<record id="appraisal_template_manager_rule" model="ir.rule">
<field name="name">Appraisal Template Manager</field>
<field name="model_id" ref="model_employee_appraisal_template"/>
<field name="domain_force">
[('employee_eva_id.user_id', '=', user.id)]
</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_manager'))]"/>
</record>
<record id="appraisal_template_hr_rule" model="ir.rule">
<field name="name">Appraisal Template HR</field>
<field name="model_id" ref="model_employee_appraisal_template"/>
<field name="domain_force">
[('hr_employee_id.user_id', '=', user.id)]
</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_hr'))]"/>
</record>
<record id="appraisal_template_management_rule" model="ir.rule">
<field name="name">Appraisal Template - Management Access</field>
<field name="model_id" ref="model_employee_appraisal_template"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_management'))]"/>
</record>
<record id="record_rule_employee" model="ir.rule">
<field name="name">Employee Appraisal - employee access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">
[('employee_appraisal_id.user_id','=', user.id)]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
</record>
<record id="record_rule_manager" model="ir.rule">
<field name="name">Manager Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">
[('managerapp_id.user_id','=',user.id)]
</field>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
<field name="perm_create" eval="True"/>
<field name="perm_unlink" eval="True"/>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_manager'))]"/>
</record>
<record id="employee_appraisal_hr_rule" model="ir.rule">
<field name="name">Employee Appraisal - HR Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">
[('hr_apprai_id.user_id', '=', user.id)]
</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_hr'))]"/>
</record>
<record id="employee_appraisal_hr_head_rule" model="ir.rule">
<field name="name">Employee Appraisal - HR Head Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_hr_head'))]"/>
</record>
<record id="employee_appraisal_finance_rule" model="ir.rule">
<field name="name">Employee Appraisal - Finance Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_finance'))]"/>
</record>
<record id="employee_appraisal_finance_head_rule" model="ir.rule">
<field name="name">Employee Appraisal - Finance Head Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_finance_head'))]"/>
</record>
<record id="employee_appraisal_management_rule" model="ir.rule">
<field name="name">Employee Appraisal - Management Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_management'))]"/>
</record>
<record id="employee_appraisal_management_rule" model="ir.rule">
<field name="name">Employee Appraisal - Management Access</field>
<field name="model_id" ref="model_employee_appraisal_template_config"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4, ref('hrms_employee_appraisal.group_appraisal_management'))]"/>
</record>
</data>
</odoo>

View File

@ -0,0 +1,45 @@
<odoo>
<data noupdate="1">
<record model="ir.module.category" id="module_performance_management_group">
<field name="name">Performance Management</field>
<field name="sequence">40</field>
</record>
<record id="group_appraisal_employee" model="res.groups">
<field name="name">Employee</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_manager" model="res.groups">
<field name="name">Appraisal Manager</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_hr" model="res.groups">
<field name="name">Appraisal HR</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_hr_head" model="res.groups">
<field name="name">Appraisal HR Head</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_finance" model="res.groups">
<field name="name">Appraisal Finance</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_finance_head" model="res.groups">
<field name="name">Appraisal Finance Head</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
<record id="group_appraisal_management" model="res.groups">
<field name="name">Appraisal Management</field>
<field name="category_id" ref="module_performance_management_group"/>
</record>
</data>
</odoo>

View File

@ -13,10 +13,31 @@
<!-- <field name="performance_evaluator"/>--> <!-- <field name="performance_evaluator"/>-->
<field name="template_id" string="Template"/> <field name="template_id" string="Template"/>
<field name="appraisal_period_id" string="Performance Period"/> <field name="appraisal_period_id" string="Performance Period"/>
<!-- <field name="state" widget="badge"/>--> <field name="appraisal_period_id" string="Performance Period"/>
<field name="stage_id" widget="badge"
decoration-success="stage_color_class == 'success'"
decoration-info="stage_color_class == 'info'"
decoration-warning="stage_color_class == 'warning'"
decoration-primary="stage_color_class == 'primary'"
decoration-danger="stage_color_class == 'danger'"/>
</list> </list>
</field> </field>
</record> </record>
<record id="view_employee_app_search" model="ir.ui.view">
<field name="name">employee.app.search</field>
<field name="model">employee.appraisal.template.config</field>
<field name="arch" type="xml">
<search>
<searchpanel>
<field name="company_id" icon="fa-building"/>
<field name="department_appraisal_id" icon="fa-users"/>
<field name="stage_id" icon="fa-tasks" select="multi" enable_counters="1"/>
</searchpanel>
</search>
</field>
</record>
<record id="view_employee_appraisal_form" model="ir.ui.view"> <record id="view_employee_appraisal_form" model="ir.ui.view">
<field name="name">employee.appraisal.template.config.form</field> <field name="name">employee.appraisal.template.config.form</field>
<field name="model">employee.appraisal.template.config</field> <field name="model">employee.appraisal.template.config</field>
@ -32,23 +53,42 @@
domain="[('id', 'in', available_stage_ids)]"/> domain="[('id', 'in', available_stage_ids)]"/>
<button name="action_sent_employee_appraisal" string="Send" type="object" <button name="action_sent_employee_appraisal" string="Send" type="object"
confirm="Do you want to move to next stage?" confirm="Do you want to move to next stage?"
class="btn-primary" invisible="stage_id.name != 'NEW'"/> groups="hrms_employee_appraisal.group_appraisal_manager"
<!-- class="btn-primary" invisible="state != 'new'"/>--> class="btn-primary" invisible="stage_name != 'COLLEAGUES&amp;MANAGER'"/>
<button name="action_send_colleague_feedback" <button name="action_send_colleague_feedback"
string="Send Colleague Feedback &amp; Manager" string="Send Colleague Feedback &amp; Manager"
type="object"
class="btn-primary" invisible="stage_id.name != 'COLLEAGE&amp;MANAGER'"/>
<!-- invisible="state != 'self_evaluation'"/>-->
<button name="action_confirm_manager" string="Manager" type="object"
confirm="Do you want to move to next stage?" confirm="Do you want to move to next stage?"
class="btn-primary" invisible="stage_id.name != 'HR'"/> type="object"
<!-- invisible="state != 'colleague_manager'"/>--> class="btn-primary"
invisible="stage_name != 'NEW' or not is_current_employee"/>
<!-- groups="group_appraisal_employee,group_appraisal_management"-->
<!-- -->
<!-- class="btn-primary" invisible="stage_name != 'COLLEAGE&amp;MANAGER'"/>-->
<button name="action_confirm_hr" string="HR Confirm" type="object" <button name="action_confirm_hr" string="HR Confirm" type="object"
confirm="Do you want to move to next stage?" confirm="Do you want to move to next stage?"
class="btn-primary" invisible="stage_id.name != 'HR'"/> groups="hrms_employee_appraisal.group_appraisal_hr"
class="btn-primary" invisible="stage_name != 'HR'"/>
<button name="action_head_hr"
string="HR Head Approval" type="object" class="btn-primary"
invisible="stage_name != 'HR HEAD'"/>
<!-- invisible="state != 'hr_evaluation'"/>--> <!-- invisible="state != 'hr_evaluation'"/>-->
<button name="action_finance_approve" string="Finance Appraisal" <button name="action_finance_approve" string="Finance Appraisal"
type="object" class="btn-primary" invisible="stage_id.name != 'FINANCE TEAM'"/> type="object" class="btn-primary" invisible="stage_name != 'FINANCE'"/>
<button name="action_finance_head" string="Finance Head Approval" type="object"
class="btn-primary" invisible="stage_name != 'FINANCE HEAD'"/>
<button name="action_create_pip"
string="Create PIP"
type="object"
class="btn-danger"
invisible="invite_pip == False or overall_rating not in ('0','1','2')"/>
<button name="action_initiate_pip"
string="Initiate PIP"
type="object"
class="btn-danger"
invisible="stage_name != 'MANAGEMENT'
or overall_rating not in ('0', '1', '2')
or invite_pip"/>
<!-- invisible="state != 'finance_team'"/>--> <!-- invisible="state != 'finance_team'"/>-->
<!-- <button name="action_confirm" string="Confirm" type="object"--> <!-- <button name="action_confirm" string="Confirm" type="object"-->
<!-- confirm="Do you want to move to next stage?"--> <!-- confirm="Do you want to move to next stage?"-->
@ -60,21 +100,38 @@
<!-- invisible="state != 'colleague_feedback'"/>--> <!-- invisible="state != 'colleague_feedback'"/>-->
</header> </header>
<sheet> <sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_current_contract"
type="object"
class="oe_stat_button"
icon="fa-file-text-o">
<field name="contract_count"
string="Contracts"
widget="statinfo"/>
</button>
</div>
<group col="2"> <group col="2">
<group> <group>
<field name="seq" string="Performance Id"/> <field name="seq" string="Performance Id"/>
<field name="employee_appraisal_id" string="Employee"/> <field name="employee_appraisal_id" string="Employee" readonly="1"
<field name="job_appraisal_id" string="Current Job Title" readonly="1"/> options="{'no_open': True}"/>
<field name="department_appraisal_id" string="Department" readonly="1"/> <field name="job_appraisal_id" string="Current Job Title" readonly="1"
<field name="template_id" string="Template"/> options="{'no_open': True}"/>
<!-- <field name="available_stage_ids" invisible="0" widget="many2many_tags"/>--> <field name="department_appraisal_id" string="Department" readonly="1"
<field name="appraisal_period_id" string="Performance Period"/> options="{'no_open': True}"/>
<!-- <field name="manager_ids" widget="many2many_tags"/>--> <!-- <field name="template_id" string="Template"/>-->
<field name="managerapp_id"/> <!-- <field name="available_stage_ids" invisible="0" widget="many2many_tags"/>-->
<field name="manager_remarks"/> <field name="appraisal_period_id" string="Performance Period" readonly="1"
<field name="hr_apprai_id" string="HR"/> options="{'no_open': True}"/>
<field name="hr_remarks"/> <!-- <field name="manager_ids" widget="many2many_tags"/>-->
<field name="start_date" readonly="1"/> <field name="managerapp_id" readonly="1" options="{'no_open': True}"/>
<field name="is_manager_reviewer" invisible="1"/>
<field name="manager_remarks" readonly="not is_manager_reviewer"/>
<field name="hr_apprai_id" string="HR" readonly="1" options="{'no_open': True}"/>
<field name="is_hr_reviewer" invisible="1"/>
<field name="hr_remarks" readonly="not is_hr_reviewer"/>
</group> </group>
<group> <group>
<field name="image_1920" widget="image" class="oe_avatar" nolabel="1" <field name="image_1920" widget="image" class="oe_avatar" nolabel="1"
@ -82,10 +139,13 @@
<field name="total_employee_score" readonly="1"/> <field name="total_employee_score" readonly="1"/>
<field name="total_manager_score" readonly="1"/> <field name="total_manager_score" readonly="1"/>
<field name="total_hr_score" readonly="1"/> <field name="total_hr_score" readonly="1"/>
<field name="company_id" string="Company"/> <field name="company_id" string="Company" readonly="1" options="{'no_open': True}"/>
<field name="start_date" readonly="1"/>
<field name="end_date" readonly="1"/> <field name="end_date" readonly="1"/>
<field name="template_empl_rating_bool"/> <field name="template_empl_rating_bool" readonly="1" invisible="1"/>
<field name="template_empl_point_bool"/> <field name="template_empl_point_bool" readonly="1" invisible="1"/>
<field name="hr_head_remarks" placeholder="Enter HR Head Remarks..."
invisible="stage_name != 'HR HEAD'"/>
</group> </group>
<group string="Performance Standards"> <group string="Performance Standards">
<div> <div>
@ -146,27 +206,64 @@
<field name="kpi_line_ids" nolabel="1" string="KPI"> <field name="kpi_line_ids" nolabel="1" string="KPI">
<list editable="bottom" delete="0"> <list editable="bottom" delete="0">
<field name="sequence" widget="handle"/> <field name="sequence" widget="handle"/>
<field name="question"/> <field name="question" readonly="1"/>
<field name="description"/> <field name="description" readonly="1"/>
<!-- <field name="kpi_line_weightage" sum="Total Weight Age"/>--> <!-- <field name="kpi_line_weightage" sum="Total Weight Age"/>-->
<field name="template_empl_rating_bool" column_invisible="1"/> <field name="template_empl_rating_bool" column_invisible="1"/>
<field name="template_empl_point_bool" column_invisible="1"/> <field name="template_empl_point_bool" column_invisible="1"/>
<field name="is_employee_reviewer" column_invisible="1"/>
<field name="employee_score" string="self Rating" sum="Total score" <field name="employee_score" string="self Rating" sum="Total score"
readonly="not is_employee_reviewer"
column_invisible="parent.template_empl_point_bool == False"/> column_invisible="parent.template_empl_point_bool == False"/>
<field name="rating_star" widget="priority" string="Self Rating" <field name="rating_star" widget="priority" string="Self Rating"
readonly="not is_employee_reviewer"
column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/> column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/>
<field name="manager_rating_star" widget="priority" string="Manager Rating" <field name="is_manager_reviewer" column_invisible="1"/>
<field name="is_hr_reviewer" column_invisible="1"/>
<field name="manager_rating_star" widget="priority"
string="Manager Rating" readonly="not is_manager_reviewer"
column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/> column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/>
<field name="hr_rating_star" widget="priority" string="HR Rating" <field name="hr_rating_star" widget="priority" string="HR Rating"
readonly="not is_hr_reviewer"
column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/> column_invisible="parent.template_empl_rating_bool == False or parent.template_empl_point_bool == True"/>
<field name="manager_score" sum="Total Points" column_invisible="parent.template_empl_point_bool == False"/> <field name="manager_score" sum="Total Points"
<field name="hr_score" sum="Total Points" column_invisible="parent.template_empl_point_bool == False"/> readonly="not is_manager_reviewer"
column_invisible="parent.template_empl_point_bool == False"/>
<field name="hr_score" sum="Total Points"
readonly="not is_hr_reviewer"
column_invisible="parent.template_empl_point_bool == False"/>
</list> </list>
</field> </field>
</sheet> </sheet>
</form> </form>
</field> </field>
</page> </page>
<page string="Disciplinary Actions">
<field name="disciplinary_ids" readonly="1">
<list create="0" delete="0" open_form_view="1">
<field name="complaint_date"/>
<field name="complaint_type_id"/>
<field name="mistake_type_id"/>
<field name="complaint"/>
<field name="state"
widget="badge"
decoration-success="state == 'closed'"
decoration-warning="state == 'submitted'"
decoration-info="state == 'pending'"
decoration-danger="state == 'cancel'"/>
</list>
</field>
</page>
<page string="Achievements &amp; Documents">
<group>
<field name="achievement_summary" widget="html"
placeholder="Describe your achievements during this appraisal period..."/>
<field name="employee_comments" placeholder="Additional comments..."/>
</group>
<group string="Supporting Documents">
<field name="attachment_ids" widget="many2many_binary"/>
</group>
</page>
<page string="Team Feed Back"> <page string="Team Feed Back">
<!-- <field name="colleague_feed_ids" readonly="state != 'colleague_feedback'">--> <!-- <field name="colleague_feed_ids" readonly="state != 'colleague_feedback'">-->
<field name="colleague_feed_ids"> <field name="colleague_feed_ids">
@ -174,47 +271,35 @@
<field name="colleague_feed_id"/> <field name="colleague_feed_id"/>
<field name="feedback"/> <field name="feedback"/>
<!-- <field name="feedback" readonly="state == 'submitted'"/>--> <!-- <field name="feedback" readonly="state == 'submitted'"/>-->
<!-- <field name="state"/>-->
<button name="action_submit_feedback" type="object" string="Submit"/> <button name="action_submit_feedback" type="object" string="Submit"
invisible="state == 'submitted'"/>
<field name="state"/>
<!-- invisible="state == 'submitted'"/>--> <!-- invisible="state == 'submitted'"/>-->
</list> </list>
</field> </field>
</page> </page>
<page string="Finance Review"> <page string="Finance Review">
<group> <group>
<button name="action_update_contract_salary"
string="Update Contract Salary"
type="object"
class="btn-primary"
invisible="salary_update"/>
<group string="Salary Details"> <group string="Salary Details">
<field name="current_salary"/> <field name="current_salary"/>
<field name="appraisal_percentage"/> <field name="appraisal_percentage"/>
<field name="appraisal_amount"/> <field name="appraisal_amount"/>
<field name="new_salary" readonly="1"/> <field name="new_salary" readonly="1"/>
</group> </group>
<group string="Finance Remarks"> <group string="Finance Remarks">
<field name="salary_update" readonly="1"/>
<field name="finance_remarks"/> <field name="finance_remarks"/>
<field name="finance_head_remarks"/>
<field name="finance_user_id"
readonly="1"/>
<field name="finance_date"
readonly="1"/>
</group> </group>
</group> </group>
<footer> <footer>
</footer> </footer>
</page> </page>
</notebook> </notebook>
</sheet> </sheet>
@ -228,6 +313,7 @@
<field name="name">Employee Performance Review</field> <field name="name">Employee Performance Review</field>
<field name="res_model">employee.appraisal.template.config</field> <field name="res_model">employee.appraisal.template.config</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_employee_app_search"/>
</record> </record>
<!-- ROOT MENU --> <!-- ROOT MENU -->
@ -241,7 +327,7 @@
name="Employee Performance Review" name="Employee Performance Review"
parent="menu_employee_appraisal_root" parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template" action="action_employee_appraisal_template"
sequence="03"/> sequence="04"/>
</data> </data>
</odoo> </odoo>

View File

@ -2,8 +2,23 @@
<menuitem id="configuration_id_employee" <menuitem id="configuration_id_employee"
name="Configuration" name="Configuration"
groups="group_appraisal_hr_head,group_appraisal_hr,group_appraisal_management"
parent="menu_employee_appraisal_root" parent="menu_employee_appraisal_root"
sequence="04"/> sequence="06"/>
<record id="action_appraisal_settings" model="ir.actions.act_window">
<field name="name">Settings</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module':'hrms_employee_appraisal'}</field>
</record>
<menuitem id="menu_appraisal_settings"
name="Settings"
parent="configuration_id_employee"
action="action_appraisal_settings"
sequence="100"/>
<record id="view_employee_appraisal_yea_form" model="ir.ui.view"> <record id="view_employee_appraisal_yea_form" model="ir.ui.view">
<field name="name">employee.appraisal.year.form</field> <field name="name">employee.appraisal.year.form</field>

View File

@ -0,0 +1,84 @@
<odoo>
<data>
<record id="view_employee_stage_conf_form" model="ir.ui.view">
<field name="name">employee.pip.form</field>
<field name="model">employee.pip</field>
<field name="arch" type="xml">
<form string="Performance Improvement Plan">
<header>
<button name="action_start" string="Start" type="object" class="btn-primary" invisible="state != 'draft'"/>
<button name="action_review" string="Review" type="object" class="btn-info" invisible="state != 'running'"/>
<button name="action_complete" string="Complete" type="object" class="btn-success" invisible="state != 'review'"/>
<button name="action_fail" string="Fail" type="object" class="btn-danger" invisible="state != 'review'"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group>
<field name="employee_id"/>
<field name="manager_id"/>
<field name="timeline"/>
<field name="progress_percentage" widget="progressbar"/>
<field name="objective"/>
</group>
<group>
<field name="start_date"/>
<field name="end_date"/>
<field name="review_date"/>
<field name="employee_acknowledged"/>
</group>
</group>
<notebook>
<page string="Improvement Tasks">
<field name="task_ids">
<list editable="bottom">
<field name="name"/>
<field name="description"/>
<field name="target_date"/>
<field name="training_course"/>
<field name="state"/>
</list>
</field>
</page>
<page string="Manager Remarks">
<field name="remarks"/>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="view_employee_pip_tree" model="ir.ui.view">
<field name="name">employee.pip.tree</field>
<field name="model">employee.pip</field>
<field name="arch" type="xml">
<list string="Performance Improvement Plans">
<field name="employee_id"/>
<field name="manager_id"/>
<field name="timeline"/>
<field name="start_date"/>
<field name="end_date"/>
<field name="progress_percentage" widget="progressbar"/>
<field name="review_date"/>
<field name="employee_acknowledged"/>
<field name="state"
widget="badge"
decoration-success="state == 'completed'"
decoration-warning="state == 'review'"
decoration-info="state == 'running'"
decoration-danger="state == 'failed'"/>
</list>
</field>
</record>
<record id="action_employe_pip" model="ir.actions.act_window">
<field name="name">Performance Improvement Plans</field>
<field name="res_model">employee.pip</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_employe_pip"
name="Performance Improvement Plans"
parent="menu_employee_appraisal_root"
action="action_employe_pip"
sequence="05"/>
</data>
</odoo>

View File

@ -10,6 +10,7 @@
type="object" type="object"
string="Send To Employee" string="Send To Employee"
class="oe_highlight" class="oe_highlight"
groups="hrms_employee_appraisal.group_appraisal_manager,hrms_employee_appraisal.group_appraisal_management"
invisible="employee_state != 'new'"/> invisible="employee_state != 'new'"/>
<field name="employee_state" widget="statusbar"/> <field name="employee_state" widget="statusbar"/>
</header> </header>
@ -18,7 +19,7 @@
<group> <group>
<field name="seq" string="Performance Id"/> <field name="seq" string="Performance Id"/>
<field name="name" string="Reference" placeholder="Administration Appraisal Template"/> <field name="name" string="Reference" placeholder="Administration Appraisal Template"/>
<field name="manager_ids" widget="many2many_tags" string="Performance Evaluator"/> <field name="manager_ids" widget="many2many_tags" string="Performance Evaluator" invisible="1"/>
<field name="employee_eva_id"/> <field name="employee_eva_id"/>
<field name="employee_department_id"/> <field name="employee_department_id"/>
<field name="appraisal_period_type_id" string="Performance Type"/> <field name="appraisal_period_type_id" string="Performance Type"/>
@ -48,8 +49,10 @@
<!-- <field name="kra_weightage" sum="Total Weightage"/>--> <!-- <field name="kra_weightage" sum="Total Weightage"/>-->
<field name="kra_template_rating_bool" column_invisible="1"/> <field name="kra_template_rating_bool" column_invisible="1"/>
<field name="kra_template_point_bool" column_invisible="1"/> <field name="kra_template_point_bool" column_invisible="1"/>
<field name="max_star_rating" widget="priority" column_invisible="parent.template_rating_bool == False"/> <field name="max_star_rating" widget="priority"
<field name="max_points" column_invisible="parent.template_point_bool == False"/> column_invisible="parent.template_rating_bool == False"/>
<field name="max_points"
column_invisible="parent.template_point_bool == False"/>
<field name="kpi_count" readonly="1"/> <field name="kpi_count" readonly="1"/>
<button name="action_open_questions" <button name="action_open_questions"
type="object" type="object"
@ -80,7 +83,10 @@
<field name="appraisal_period_type_id" string="Performance Type"/> <field name="appraisal_period_type_id" string="Performance Type"/>
<field name="appraisal_period_id" string="Performance Period"/> <field name="appraisal_period_id" string="Performance Period"/>
<field name="company_id"/> <field name="company_id"/>
<field name="hr_email_notify"/> <field name="employee_state"
widget="badge"
decoration-muted="employee_state == 'new'"
decoration-success="employee_state == 'sent'"/>
</list> </list>
</field> </field>
</record> </record>
@ -96,9 +102,9 @@
name="Employee Appraisal Templates" name="Employee Appraisal Templates"
parent="menu_employee_appraisal_root" parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template_conf" 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"/>
<!-- KPI TREE VIEW -->
<record id="view_employee_appraisal_kpi_list" model="ir.ui.view"> <record id="view_employee_appraisal_kpi_list" model="ir.ui.view">
<field name="name">employee.appraisal.kpi.list</field> <field name="name">employee.appraisal.kpi.list</field>
@ -119,8 +125,6 @@
</record> </record>
<!-- KPI FORM VIEW -->
<record id="view_employee_appraisal_kpi_form" model="ir.ui.view"> <record id="view_employee_appraisal_kpi_form" model="ir.ui.view">
<field name="name">employee.appraisal.kpi.form</field> <field name="name">employee.appraisal.kpi.form</field>
<field name="model">employee.appraisal.kpi</field> <field name="model">employee.appraisal.kpi</field>
@ -132,22 +136,8 @@
</group> </group>
</sheet> </sheet>
<footer> <footer>
<button string="Save" type="object" special="save" class="btn-primary"/>
<!-- SAVE --> <button name="action_delete_record" string="Delete" type="object" class="btn-danger"/>
<button string="Save"
type="object"
special="save"
class="btn-primary"/>
<!-- DELETE -->
<button name="action_delete_record"
string="Delete"
type="object"
class="btn-danger"/>
<!-- CLOSE -->
</footer> </footer>
</form> </form>
</field> </field>

View File

@ -6,7 +6,8 @@
<form string="HR Notice"> <form string="HR Notice">
<header> <header>
<button name="action_send_notice" string="Send Appraisal Initiation" type="object" <button name="action_send_notice" string="Send Appraisal Initiation" type="object"
class="btn btn-success" invisible="state != 'draft'"/> class="btn btn-success" groups="hrms_employee_appraisal.group_appraisal_hr,hrms_employee_appraisal.group_appraisal_management"
invisible="state != 'draft'"/>
<button name="action_open_postpone_wizard" string="Postpone" type="object" <button name="action_open_postpone_wizard" string="Postpone" type="object"
class="btn-warning" invisible="state != 'sent'"/> class="btn-warning" invisible="state != 'sent'"/>
<button name="action_open_cancel_wizard" string="Cancel" type="object" <button name="action_open_cancel_wizard" string="Cancel" type="object"
@ -17,30 +18,36 @@
<group col="2"> <group col="2">
<group> <group>
<field name="seq"/> <field name="seq"/>
<field name="hr_employee_id" string="Created BY"/> <field name="hr_employee_id" string="Created BY" readonly="1" options="{'no_open': True}"/>
<field name="appraisal_type_id"/> <field name="appraisal_type_id"
<field name="appraisal_notice_id"/> options="{'no_edit': True, 'no_create': True, 'no_open': True}"
<field name="subject"/> readonly="state != 'draft'"/>
<field name="start_date"/> <field name="appraisal_notice_id"
<field name="end_date"/> options="{'no_edit': True, 'no_create': True, 'no_open': True}"
readonly="state != 'draft'"/>
<field name="subject" readonly="state != 'draft'"/>
<field name="start_date" readonly="state != 'draft'" force_save="1"/>
<field name="end_date" readonly="state != 'draft'" force_save="1"/>
</group> </group>
<group> <group>
<field name="employee_ids" widget="many2many_tags"/> <field name="employee_ids" widget="many2many_tags" required="1"
<field name="manager_ids" widget="many2many_tags"/> readonly="state != 'draft'"/>
<field name="hr_department_ids" widget="many2many_tags"/> <field name="manager_ids" widget="many2many_tags" readonly="state != 'draft'"/>
<field name="stage_config" widget="many2many_tags"/> <field name="hr_department_ids" widget="many2many_tags" readonly="state != 'draft'"/>
<field name="employee_rating"/> <field name="stage_config" widget="many2many_tags" required="1"
<field name="employee_points"/> readonly="state != 'draft'"/>
<field name="employee_rating" readonly="state != 'draft'"/>
<field name="employee_points" readonly="state != 'draft'"/>
</group> </group>
</group> </group>
<group> <group>
<field name="body" widget="html"/> <field name="body" widget="html" readonly="state != 'draft'"/>
</group> </group>
<notebook> <notebook>
<page string="Postponed Details"> <page string="Postponed Details">
<group> <group>
<group> <group>
<field name="postponed_by_id" readonly="1"/> <field name="postponed_by_id" readonly="1" options="{'no_open': True}"/>
<field name="postponed_date" readonly="1"/> <field name="postponed_date" readonly="1"/>
<field name="new_start_date" readonly="1"/> <field name="new_start_date" readonly="1"/>
<field name="new_end_date" readonly="1"/> <field name="new_end_date" readonly="1"/>
@ -53,7 +60,7 @@
<page string="Cancelled Details"> <page string="Cancelled Details">
<group> <group>
<group> <group>
<field name="cancelled_by_id" readonly="1"/> <field name="cancelled_by_id" readonly="1" options="{'no_open': True}"/>
<field name="cancelled_date" readonly="1"/> <field name="cancelled_date" readonly="1"/>
</group> </group>
<group> <group>
@ -74,6 +81,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<list> <list>
<field name="seq"/> <field name="seq"/>
<field name="hr_employee_id"/>
<field name="subject"/> <field name="subject"/>
<field name="start_date"/> <field name="start_date"/>
<field name="end_date"/> <field name="end_date"/>
@ -97,6 +105,95 @@
name="Performance Cycle Notification" name="Performance Cycle Notification"
parent="menu_employee_appraisal_root" parent="menu_employee_appraisal_root"
action="action_hr_notice_appraisal" action="action_hr_notice_appraisal"
groups="group_appraisal_hr,group_appraisal_hr_head,group_appraisal_management"
sequence="02"/>
<record id="view_hr_appraisal_notification_form" model="ir.ui.view">
<field name="name">hr.head.notification.form</field>
<field name="model">hr.head.notification</field>
<field name="arch" type="xml">
<form string="Appraisal Notification">
<header>
<button name="action_sent_hr" string="Send To HR Team" type="object" class="btn-primary"
invisible="state != 'draft'"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name" placeholder="Performance Appraisal Notification"/>
</h1>
</div>
<group col="2">
<group string="Appraisal Details">
<field name="seq"/>
<field name="hr_employee_id"/>
<field name="appraisal_type_id" options="{'no_create': True,'no_open': True}"/>
<field name="appraisal_period_id" options="{'no_create': True,'no_open': True}"/>
<field name="start_date"/>
<field name="end_date"/>
</group>
<group string="Assignment">
<!-- <field name="hr_employee_domain_ids" invisible="1"/>-->
<!-- <field name="hr_ids"-->
<!-- widget="many2many_tags"/>-->
<!-- <field name="hr_user_domain_ids" invisible="1"/>-->
<field name="hr_users_ids"
widget="many2many_tags"/>
<field name="stage_config_ids" widget="many2many_tags" required="1"/>
</group>
</group>
<notebook>
<page string="Notification Message">
<field name="body" widget="html"/>
</page>
<page string="Audit">
<group>
<field name="create_uid" readonly="1"/>
<field name="create_date" readonly="1"/>
<field name="write_uid" readonly="1"/>
<field name="write_date" readonly="1"/>
</group>
</page>
</notebook>
</sheet>
<chatter/>
</form>
</field>
</record>
<record id="view_hr_appraisal_notification_tree" model="ir.ui.view">
<field name="name">hr.head.notification.tree</field>
<field name="model">hr.head.notification</field>
<field name="arch" type="xml">
<list>
<field name="seq"/>
<field name="hr_employee_id"/>
<field name="name"/>
<field name="appraisal_type_id"/>
<field name="appraisal_period_id"/>
<field name="start_date"/>
<field name="end_date"/>
<field name="state"
widget="badge"
decoration-success="state == 'sent'"
decoration-muted="state == 'draft'"/>
</list>
</field>
</record>
<record id="action_hr_head_appraisal" model="ir.actions.act_window">
<field name="name">Appraisal Notifications</field>
<field name="res_model">hr.head.notification</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_hr_head_appraisal"
name="Appraisal Notifications"
parent="menu_employee_appraisal_root"
action="action_hr_head_appraisal"
groups="group_appraisal_hr_head,group_appraisal_management"
sequence="01"/> sequence="01"/>
</odoo> </odoo>

View File

@ -9,6 +9,7 @@
<group> <group>
<field name="seq" invisible="0"/> <field name="seq" invisible="0"/>
<field name="name"/> <field name="name"/>
<field name="colour_seq"/>
<field name="color" widget="color_picker"/> <field name="color" widget="color_picker"/>
</group> </group>
</sheet> </sheet>
@ -20,7 +21,7 @@
<field name="model">employee.stage.config</field> <field name="model">employee.stage.config</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<list string="Employee Appraisal Period"> <list string="Employee Appraisal Period">
<field name="seq" widget="handle"/> <field name="seq" widget="handle"/>
<field name="name"/> <field name="name"/>
<field name="color" widget="color_picker"/> <field name="color" widget="color_picker"/>
</list> </list>
@ -36,5 +37,43 @@
parent="configuration_id_employee" parent="configuration_id_employee"
action="action_employee_stage_confr" action="action_employee_stage_confr"
sequence="20"/> sequence="20"/>
<record id="res_config_settings_view_form_appraisal" model="ir.ui.view">
<field name="name">res.config.settings.appraisal.form</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base_setup.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<app string="Employee Appraisal" name="employee_appraisal">
<block title="Appraisal Settings">
<setting string="Enable Appraisal Reminders"><field name="appraisal_reminder_enabled"/></setting>
<setting string="Reminder Days"><field name="appraisal_reminder_days"/>
<div class="text-muted">
Reminder email will be sent before appraisal end date.
</div>
</setting>
</block>
</app>
</xpath>
</field>
</record>
<record id="action_employee_appraisal_settings" model="ir.actions.act_window">
<field name="name">Appraisal Settings</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module': 'hrms_employee_appraisal'}</field>
</record>
<menuitem id="menu_employee_appraisal_settings"
name="Settings"
parent="configuration_id_employee"
action="action_employee_appraisal_settings"
sequence="10"/>
<!-- groups="hrms_employee_appraisal.group_appraisal_hr_head,-->
<!-- hrms_employee_appraisal.group_appraisal_hr,-->
<!-- hrms_employee_appraisal.group_appraisal_management"-->
</data> </data>
</odoo> </odoo>

View File

@ -80,10 +80,18 @@
</list> </list>
</field> </field>
</record> </record>
<record id="action_recruitment_requisition" model="ir.actions.act_window"> <record id="action_recruitment_requisition" model="ir.actions.act_window">
<field name="name">Recruitment Requisitions</field> <field name="name">Recruitment Requisitions</field>
<field name="res_model">recruitment.requisition</field> <field name="res_model">recruitment.requisition</field>
<field name="view_mode">list,form</field> <field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No Recruitment Requisitions Found
</p>
<p>
Click Create to raise a new recruitment requisition.
</p>
</field>
</record> </record>
<menuitem <menuitem