Merge remote-tracking branch 'origin/feature/share_module' into feature/share_module

This commit is contained in:
Bhagya-K 2026-06-29 11:50:43 +05:30
commit 80e90ca88a
64 changed files with 3477 additions and 463 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,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Disciplinary',
'version': '1.0.0',
'category': 'Apps',
'summary': 'Disciplinary',
'description': 'Employee Disciplinary',
'sequence': '10',
'author': '',
'company': 'FTPROTECH',
'website': 'https://www.ftprotech.in',
'depends': ['mail', 'hr', 'base', 'website_hr_recruitment', 'contacts', 'point_of_sale'],
'demo': [],
'data': [
'data/sequence.xml',
'security/ir.model.access.csv',
'views/disciplinary_view.xml',
'views/employee_displance.xml',
'views/mistake_type_views.xml',
'views/incident_sub_type.xml',
'views/disciplinary_complaint_type.xml',
],
'installable': True,
'application': False,
'auto_install': False,
'license': 'LGPL-3',
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">
<record id="incident_report_sequence" model="ir.sequence">
<field name="name">Employee Disciplinary</field>
<field name="code">employee.disciplinary</field>
<field name="prefix">IR</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="manage_incident_report_sequence" model="ir.sequence">
<field name="name">Manage Incident</field>
<field name="code">manage.incident</field>
<field name="prefix">MI</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="seq_employee_disciplinary_sequence" model="ir.sequence">
<field name="name">Disciplinary Sequence</field>
<field name="code">hr.employee.sequence</field>
<field name="prefix">ED</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@ -0,0 +1,2 @@
from . import disciplinary
from . import employee_displane

View File

@ -0,0 +1,188 @@
from datetime import datetime, date
from odoo import fields, models, api
#
# class NameChangeHrEmployee(models.Model):
# _inherit = "hr.employee"
#
# employee_name_ids1 = fields.One2many('employee.disciplinary', 'disp_name')
# employee_self_service_line_ids = fields.One2many('manage.incident', 'emp_incident', domain=[('state', '=', 'closed')])
#
# def name_get(self):
# result = []
# for record in self:
# if self.env.context.get('new_custom_name', False):
# result.append((record.id, "{} - {}".format(record.name, record.identification_id)))
# else:
# return super(NameChangeHrEmployee, self).name_get()
# return result
class EmployeeDisciplinary(models.Model):
_name = 'employee.disciplinary'
_inherit = ['mail.thread', 'mail.activity.mixin']
_rec_name = 'incident_type'
incident_date = fields.Datetime(string='Incident Date & Time', tracking=True, default=datetime.now(), required=True)
incident_type = fields.Many2one('incident.employee', string='Incident Type', tracking=True, required=True)
incident_sub_type = fields.Many2many('incident.sub.employee', string='Incident Sub Type', tracking=True,
required=True)
incident_details = fields.Char(string='Incident Details', tracking=True, required=True)
seized_items = fields.Char(string='Seized Items', tracking=True)
incident_summary = fields.Text(string='Incident Summary', tracking=True, required=True)
attach = fields.Many2many('ir.attachment', string='Attachments', tracking=True)
emp_many_disp = fields.Many2many('hr.employee', 'new_custom_table', string='Employees Involved in the Incident',
tracking=True, required=True)
date_action = fields.Date(string='Date')
employee = fields.Many2one('manage.incident')
employee_code = fields.Many2one("hr.employee", string="Employee Name", required=True)
employee_name = fields.Char(related="employee_code.identification_id")
disp_name = fields.Many2one('hr.employee')
@api.onchange('incident_type')
def return_incident_sub_type(self):
print(self.incident_type.sub_type)
listed = []
for recs in self.incident_type.sub_type:
listed.append(recs.id)
return {'domain': {'incident_sub_type': [('id', 'in', listed)]}}
@api.constrains('incident_type')
def create_manage_incidents(self):
for rec in self:
print('created')
self.env['manage.incident'].create({
'employee_disciplinary_id': rec.id,
})
@api.constrains('employee')
def holds_hr_employee(self):
for rec in self:
rec.disp_name = rec.employee.employee_code_list1
class IncidentEmployee(models.Model):
_name = 'incident.employee'
name = fields.Char(string='Incident')
sub_type = fields.Many2many('incident.sub.employee', string='Sub type')
class IncidentSubEmployee(models.Model):
_name = 'incident.sub.employee'
name = fields.Char(string='Incident Sub')
class DisciplinaryMistakeType(models.Model):
_name = 'disciplinary.mistake.type'
_description = 'Disciplinary Mistake Type'
name = fields.Char(string="Mistake Type", required=True)
class IncidentSubEmployee(models.Model):
_name = 'incident.sub.employee'
_description = 'Incident Sub Type'
name = fields.Char(string="Incident Sub Type", required=True)
class EmployeeDisciplinaryLines(models.Model):
_name = 'employee.disciplinary.line'
_rec_name = 'hr_emp_many'
emp_many = fields.Many2one('employee.disciplinary', string='Employee Disp')
hr_emp_many = fields.Many2one('hr.employee', string='Employee Number')
hr_emp_many_name = fields.Char(related='hr_emp_many.name', string='Employee Name')
class ManageIncident(models.Model):
_name = 'manage.incident'
_inherit = ['mail.thread', 'mail.activity.mixin']
# employee_name_ids = fields.One2many('employee.disciplinary','employee',string="Employee Name")
employee_disciplinary_id = fields.Many2one("employee.disciplinary", string="Employee Disp")
employee_code_list1 = fields.Many2many("hr.employee", string="Employees Involved in the Incident",
related='employee_disciplinary_id.emp_many_disp', tracking=True)
incident_dat = fields.Datetime(related='employee_disciplinary_id.incident_date', string='Incident Date & Time',
tracking=True)
employee_by_code = fields.Many2one(related='employee_disciplinary_id.employee_code',
string="Reported By Employee Name")
incident_sum = fields.Text(related='employee_disciplinary_id.incident_summary', string='Incident Summary',
tracking=True)
incident_typ = fields.Many2one(related='employee_disciplinary_id.incident_type', string="Incident Type",
tracking=True)
incident_sub_typ = fields.Many2many(related='employee_disciplinary_id.incident_sub_type',
string="Incident Sub Type", tracking=True)
# corrective_action_emp_id = fields.Many2one(related='employee_disciplinary_id.corrective_action_id',
# string="Corrective Action", tracking=True)
state = fields.Selection(([
('pending_inquiry', 'Pending Inquiry'),
('in_progress', 'In Process'),
('closed', 'Closed')
]), string="Status", default='pending_inquiry', tracking=True)
emp_incident = fields.Many2one('hr.employee')
employee_inquiry = fields.One2many('manage.incident.line', 'employee_inquiry_state')
def button_in_progress(self):
self.state = 'in_progress'
# def button_closed(self):
# for rec in self:
# rec.state = 'closed'
def button_closed(self):
for rec in self:
rec.state = 'closed'
update_into_employee = rec.env['hr.employee'].search([('id', '=', rec.employee_code_list1.id)])
records = {
}
if records:
update_into_employee.write(records)
print('triggered 2')
class CorrectiveActions(models.Model):
_name = "corrective.actions"
name = fields.Char(string="Name")
class ManageIncidentLine(models.Model):
_name = 'manage.incident.line'
_inherit = ['mail.thread']
_description = 'Manage Incident Line'
corrective_action_id = fields.Many2one('corrective.actions', string="Corrective Action", tracking=True,
required=True)
internal_panel = fields.Many2many('hr.employee', string="Internal Panel Members", tracking=True,
required=True)
external_panel = fields.Char(string="External Panel Members")
due_date = fields.Date(string="Due Date")
last_action_date = fields.Datetime(string="Last Action Date", compute='_compute_last_action_date',
default=date.today())
recommendation = fields.Char(string="Recommendation", tracking=True, required=True)
venue = fields.Char(string='Venue')
inquiry_summary = fields.Char(string='Inquiry Summary', tracking=True, required=True)
is_guilty = fields.Selection(([
('yes', 'Yes'),
('no', 'No'),
]), string="Is the Employee Guilt of the Incident", default='no', tracking=True)
inquiry_date = fields.Datetime(string="Inquiry Date and Time", required=True)
employee_inquiry_state = fields.Many2one('manage.incident')
@api.depends('inquiry_date')
def _compute_last_action_date(self):
for line in self:
if not line.employee_inquiry_state or line == line.employee_inquiry_state.employee_inquiry[0]:
line.last_action_date = False
else:
previous_line = line.employee_inquiry_state.employee_inquiry.filtered(lambda l: l.inquiry_date < line.inquiry_date)
sorted_previous_line = previous_line.sorted(key=lambda l: l.inquiry_date, reverse=True)
if sorted_previous_line:
line.last_action_date = sorted_previous_line[0].inquiry_date
else:
line.last_action_date = False

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,22 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_employee_disciplinary,employee_disciplinary,model_employee_disciplinary,,1,1,1,1
access_incident_employee,incident_employee,model_incident_employee,,1,1,1,1
access_incident_sub_employee,incident_sub_employee,model_incident_sub_employee,,1,1,1,1
access_employee_disciplinary_line,employee_disciplinary_line,model_employee_disciplinary_line,,1,1,1,1
access_manage_incident,manage_incident,model_manage_incident,,1,1,1,1
access_manage_incident_line,manage_incident_line,model_manage_incident_line,,1,1,1,1
access_corrective_actions,corrective_actions,model_corrective_actions,,1,1,1,1
access_hr_employee_disciplinary,hr.employee.disciplinary,model_hr_employee_disciplinary,,1,1,1,1
access_hr_disciplinary_complaint_line,hr.disciplinary.complaint.line,model_hr_disciplinary_complaint_line,,1,1,1,1
access_hr_disciplinary_action_line,hr.disciplinary.action.line,model_hr_disciplinary_action_line,,1,1,1,1
access_disciplinary_action_type,disciplinary.action.type,model_disciplinary_action_type,,1,1,1,1
access_disciplinary_complaint_type,disciplinary.complaint.type,model_disciplinary_complaint_type,,1,1,1,1
access_disciplinary_mistake_type,disciplinary.mistake.type,model_disciplinary_mistake_type,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_employee_disciplinary employee_disciplinary model_employee_disciplinary 1 1 1 1
3 access_incident_employee incident_employee model_incident_employee 1 1 1 1
4 access_incident_sub_employee incident_sub_employee model_incident_sub_employee 1 1 1 1
5 access_employee_disciplinary_line employee_disciplinary_line model_employee_disciplinary_line 1 1 1 1
6 access_manage_incident manage_incident model_manage_incident 1 1 1 1
7 access_manage_incident_line manage_incident_line model_manage_incident_line 1 1 1 1
8 access_corrective_actions corrective_actions model_corrective_actions 1 1 1 1
9 access_hr_employee_disciplinary hr.employee.disciplinary model_hr_employee_disciplinary 1 1 1 1
10 access_hr_disciplinary_complaint_line hr.disciplinary.complaint.line model_hr_disciplinary_complaint_line 1 1 1 1
11 access_hr_disciplinary_action_line hr.disciplinary.action.line model_hr_disciplinary_action_line 1 1 1 1
12 access_disciplinary_action_type disciplinary.action.type model_disciplinary_action_type 1 1 1 1
13 access_disciplinary_complaint_type disciplinary.complaint.type model_disciplinary_complaint_type 1 1 1 1
14 access_disciplinary_mistake_type disciplinary.mistake.type model_disciplinary_mistake_type 1 1 1 1

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,264 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="employee_disciplinary_list" model="ir.ui.view">
<field name="name">Employee Disciplinary list</field>
<field name="model">employee.disciplinary</field>
<field name="arch" type="xml">
<list>
<field name="incident_date"/>
<field name="incident_type"/>
<field name="incident_sub_type" widget="many2many_tags"/>
</list>
</field>
</record>
<record id="employee_disciplinary_form" model="ir.ui.view">
<field name="name">Employee Disciplinary form</field>
<field name="model">employee.disciplinary</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="incident_date"/>
<field name="incident_type" options="{'no_open': True,}"/>
<field name="incident_sub_type" widget="many2many_tags" readonly="0"
options="{'no_open': True}"/>
<label for="employee_code" string="Reported By Employee Code"/>
<div class="address_format">
<field name="employee_code" style="width: 50%" options="{'no_open': True,}"/>
<field name="employee_name" style="width: 50%"/>
</div>
<field name="incident_details"/>
<field name="seized_items"/>
<field name="incident_summary"/>
<field name="attach" widget="many2many_binary" options="{'preview_image': True}"/>
<field name="emp_many_disp" widget="many2many_tags" context="{'new_custom_name': True}"/>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<record id="manage_incident_list" model="ir.ui.view">
<field name="name">Manage Incident list</field>
<field name="model">manage.incident</field>
<field name="arch" type="xml">
<list create="0">
<field name="employee_code_list1" widget="many2many_tags" context="{'new_custom_name': True}"/>
<!-- <field name="incident_dat"/>-->
<field name="incident_typ"/>
<field name="incident_sub_typ" widget="many2many_tags"/>
<field name="incident_sum"/>
<field name="state"/>
</list>
</field>
</record>
<record id="manage_incident_form" model="ir.ui.view">
<field name="name">Manage Incident form</field>
<field name="model">manage.incident</field>
<field name="arch" type="xml">
<form create="0">
<header>
<button name="button_in_progress" string="In Progress" class="oe_highlight" type="object"/>
<!-- states="pending_inquiry"/>-->
<!-- attrs="{'invisible' : ('state','!=','pending_inquiry')}"/>-->
<button name="button_closed" string="Closed" class="oe_highlight" type="object"/>
<!-- states="in_progress"/>-->
<!-- attrs="{'invisible' : ('state','!=','in_progress')}"/>-->
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group>
<field name="employee_code_list1" widget="many2many_tags"
context="{'new_custom_name': True}"/>
<field name="employee_by_code" options="{'no_open': True,}"/>
<!-- <field name="incident_dat"/>-->
</group>
<group>
<field name="incident_typ" options="{'no_open': True,}"/>
<field name="incident_sub_typ" widget="many2many_tags"/>
<field name="incident_sum"/>
</group>
</group>
<field name="employee_inquiry" string="Manage Incident">
<!-- attrs="{'readonly': [('state', '=','closed')]}">-->
<list>
<field name="inquiry_date"/>
<field name="corrective_action_id"/>
<field name="due_date"/>
<field name="last_action_date"/>
</list>
<form>
<group>
<field name="inquiry_date"/>
<field name="venue"/>
<field name="internal_panel" widget="many2many_tags"
context="{'new_custom_name': True}"/>
<field name="external_panel"/>
<field name="inquiry_summary"/>
<field name="is_guilty" widget="radio" options="{'horizontal':true}"/>
<field name="corrective_action_id" options="{'no_open': True,}"/>
<field name="recommendation"/>
<field name="due_date"/>
<field name="last_action_date"/>
</group>
</form>
</field>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- <record id="career_history_tab_sub_menu" model="ir.ui.view">-->
<!-- <field name="name">Career History Tab Sub Menu</field>-->
<!-- <field name="model">hr.employee</field>-->
<!-- <field name="inherit_id" ref="employee_life_cycle.career_history_tab_menu"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//page/field[@name='career_history_field']" position="after">-->
<!-- &lt;!&ndash; <group name="career_hist_sub_menu" string="Disciplinary Actions">&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="employee_name_ids1" string="Manage Incident">&ndash;&gt;-->
<!-- &lt;!&ndash; <list editable="0" create="0">&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_date"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_type"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_sub_type"/>&ndash;&gt;-->
<!-- &lt;!&ndash;&lt;!&ndash; <field name="corrective_action_id"/>&ndash;&gt;&ndash;&gt;-->
<!-- &lt;!&ndash; </list>&ndash;&gt;-->
<!-- &lt;!&ndash; </field>&ndash;&gt;-->
<!-- &lt;!&ndash; </group>&ndash;&gt;-->
<!-- <field name="employee_self_service_line_ids" string="Manage Incident" readonly="1">-->
<!-- <list>-->
<!-- <field name="incident_dat"/>-->
<!-- <field name="incident_typ"/>-->
<!-- <field name="incident_sub_typ"/>-->
<!-- </list>-->
<!-- </field>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<!-- <record id="career_history_tab_sub_menu_self" model="ir.ui.view">-->
<!-- <field name="name">Career History Tab Sub Menu Self Service</field>-->
<!-- <field name="model">hr.employee</field>-->
<!-- <field name="inherit_id" ref="employee_self_service.view_employee_form_self_service"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//page/field[@name='career_history_field']" position="after">-->
<!-- &lt;!&ndash; <group name="career_hist_sub_menu" string="Disciplinary Actions">&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="employee_name_ids1" string="Manage Incident">&ndash;&gt;-->
<!-- &lt;!&ndash; <list editable="0" create="0">&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_date"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_type"/>&ndash;&gt;-->
<!-- &lt;!&ndash; <field name="incident_sub_type"/>&ndash;&gt;-->
<!-- &lt;!&ndash; &lt;!&ndash; <field name="corrective_action_id"/>&ndash;&gt;&ndash;&gt;-->
<!-- &lt;!&ndash; </list>&ndash;&gt;-->
<!-- &lt;!&ndash; </field>&ndash;&gt;-->
<!-- &lt;!&ndash; </group>&ndash;&gt;-->
<!-- <field name="employee_self_service_line_ids" string="Manage Incident" readonly="1">-->
<!-- <list>-->
<!-- <field name="incident_dat"/>-->
<!-- <field name="incident_typ"/>-->
<!-- <field name="incident_sub_typ"/>-->
<!-- </list>-->
<!-- </field>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<record id="incident_employee_list" model="ir.ui.view">
<field name="name">incident Employee list</field>
<field name="model">incident.employee</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="sub_type" widget="many2many_tags"/>
</list>
</field>
</record>
<record id="incident_employee_form" model="ir.ui.view">
<field name="name">incident Employee form</field>
<field name="model">incident.employee</field>
<field name="arch" type="xml">
<form>
<sheet>
<form>
<group>
<field name="name"/>
<field name="sub_type" widget="many2many_tags"/>
</group>
</form>
</sheet>
</form>
</field>
</record>
<record id="employee_disciplinary_action" model="ir.actions.act_window">
<field name="name">Incident Reporting</field>
<field name="res_model">employee.disciplinary</field>
<field name="view_mode">list,form</field>
</record>
<record id="manage_incident_action" model="ir.actions.act_window">
<field name="name">Manage Incident</field>
<field name="res_model">manage.incident</field>
<field name="view_mode">list,form</field>
</record>
<record id="incident_employee_action" model="ir.actions.act_window">
<field name="name">Incident Type</field>
<field name="res_model">incident.employee</field>
<field name="view_mode">list,form</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>
</record>
<menuitem id="employee_disciplinary_menu"
name="Employee Disciplinary Management"
parent="hr.menu_hr_root"
action="employee_disciplinary_action"
sequence="105"/>
<!-- Child Menu (moved inside) -->
<menuitem id="menu_employee_disciplinary_root"
name="Employee Disciplinary Configuration"
parent="employee_disciplinary_menu"
sequence="10"/>
<!-- Sub Menu -->
<menuitem id="menu_employee_disciplinary"
name="Employee Disciplinary"
parent="employee_disciplinary_menu"
action="action_hr_employee_disciplinary"
sequence="01"/>
<!-- <menuitem id="manage_incident_employee"-->
<!-- name="Incident Type"-->
<!-- parent="employee_disciplinary_menu"-->
<!-- action="incident_employee_action"-->
<!-- sequence="3"/>-->
<!-- <menuitem id="manage_incident_sub_menu"-->
<!-- name="Manage Incident"-->
<!-- parent="employee_disciplinary_menu"-->
<!-- action="manage_incident_action"-->
<!-- sequence="2"/>-->
<!-- <menuitem id="employee_disciplinary_sub_menu"-->
<!-- name="Incident Reporting"-->
<!-- parent="employee_disciplinary_menu"-->
<!-- action="employee_disciplinary_action"-->
<!-- sequence="1"/>-->
</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

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<data>
<!-- list View -->
<record id="view_incident_sub_employee_list" model="ir.ui.view">
<field name="name">incident.sub.employee.list</field>
<field name="model">incident.sub.employee</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
</list>
</field>
</record>
<!-- Form View -->
<record id="view_incident_sub_employee_form" model="ir.ui.view">
<field name="name">incident.sub.employee.form</field>
<field name="model">incident.sub.employee</field>
<field name="arch" type="xml">
<form string="Incident Sub Type">
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- Action -->
<record id="action_incident_sub_employee" model="ir.actions.act_window">
<field name="name">Incident Sub Type</field>
<field name="res_model">incident.sub.employee</field>
<field name="view_mode">list,form</field>
</record>
<!-- Menu -->
<!-- <menuitem id="menu_incident_sub_employee"-->
<!-- name="Incident Sub Type"-->
<!-- parent="employee_disciplinary_menu"-->
<!-- action="action_incident_sub_employee"/>-->
</data>
</odoo>

View File

@ -0,0 +1,42 @@
<odoo>
<!-- Tree View -->
<record id="view_mistake_type_list" model="ir.ui.view">
<field name="name">mistake.type.list</field>
<field name="model">disciplinary.mistake.type</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
</list>
</field>
</record>
<!-- Form View -->
<record id="view_mistake_type_form" model="ir.ui.view">
<field name="name">mistake.type.form</field>
<field name="model">disciplinary.mistake.type</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<field name="name"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- Action -->
<record id="action_mistake_type" model="ir.actions.act_window">
<field name="name">Mistake Type</field>
<field name="res_model">disciplinary.mistake.type</field>
<field name="view_mode">list,form</field>
</record>
<menuitem
id="menu_mistake_type"
name="Mistake Type"
parent="menu_employee_disciplinary_root"
action="action_mistake_type"
sequence="02"/>
</odoo>

View File

@ -1,12 +1,13 @@
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
import calendar import calendar
class PayrollPeriod(models.Model): 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.')
] ]
@ -14,22 +15,22 @@ class PayrollPeriod(models.Model):
from_date = fields.Date(string="From Date", required=True) from_date = fields.Date(string="From Date", required=True)
to_date = fields.Date(string="To Date", required=True) to_date = fields.Date(string="To Date", required=True)
name = fields.Char(string="Name", required=True) name = fields.Char(string="Name", required=True)
period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods") period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods")
@api.model_create_multi @api.model_create_multi
def create(self, vals_list): def create(self, vals_list):
periods = super().create(vals_list) periods = super().create(vals_list)
active_investment_types = self.env['it.investment.type'].search([('active', '=', True)]) active_investment_types = self.env['it.investment.type'].search([('active', '=', True)])
if active_investment_types: if active_investment_types:
active_investment_types.write({ active_investment_types.write({
'period_ids': [(4, period.id) for period in periods], 'period_ids': [(4, period.id) for period in periods],
}) })
return periods return periods
@api.onchange('from_date', 'to_date') @api.onchange('from_date', 'to_date')
def onchange_from_to_date(self): def onchange_from_to_date(self):
for rec in self: for rec in self:
if rec.from_date and rec.to_date: if rec.from_date and rec.to_date:
rec.name = f"{rec.from_date.year}-{rec.to_date.year}" rec.name = f"{rec.from_date.year}-{rec.to_date.year}"

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

@ -179,9 +179,8 @@ class CustomerPortal(portal.CustomerPortal):
('use_website_helpdesk_form', '=', True), ('use_website_helpdesk_form', '=', True),
'|', '|',
('company_id', '=', False), ('company_id', '=', False),
('company_id', 'in', [request.env.company.id]) ('company_id', 'in', [request.env.user.company_id.id])
]) ])
selection_dict = dict( selection_dict = dict(
request.env['helpdesk.team']._fields['portal_ticket_type'].selection request.env['helpdesk.team']._fields['portal_ticket_type'].selection
) )

View File

@ -483,6 +483,10 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
} }
def _get_jd_required_fields(self): def _get_jd_required_fields(self):
category_dict = {
str(category['id']): category['category_name']
for category in self.env['job.category'].sudo().search_read([], ['id', 'category_name'])
}
return { return {
"request_id": {"type": "string", "description": "Request or requisition identifier"}, "request_id": {"type": "string", "description": "Request or requisition identifier"},
"start_date": {"type": "string", "description": "Requested start date"}, "start_date": {"type": "string", "description": "Requested start date"},
@ -495,6 +499,7 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
"secondary_skills": {"type": "list", "description": "Secondary or nice to have skills"}, "secondary_skills": {"type": "list", "description": "Secondary or nice to have skills"},
"budget": {"type": "string", "description": "Budget or salary range"}, "budget": {"type": "string", "description": "Budget or salary range"},
"experience_years": {"type": "float", "description": "Expected total years of experience"}, "experience_years": {"type": "float", "description": "Expected total years of experience"},
"job_category": {"type": "selection", "description": "Based on over all Job description which one matches more accurately give me the key: %s"%(category_dict)}
} }
def _get_resume_prompt(self): def _get_resume_prompt(self):
@ -1766,8 +1771,11 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
"secondary_skill_ids": [(6, 0, secondary_skills.ids)] if secondary_skills else False, "secondary_skill_ids": [(6, 0, secondary_skills.ids)] if secondary_skills else False,
} }
job_category = self._match_job_category(parsed_data, parsed_payload) job_category = self._match_job_category(parsed_data, parsed_payload)
if job_category:
write_vals["job_category"] = job_category.id if job_request and job_request.job_category:
if job_request.job_id and job_request.job_id.job_category != job_request.job_category:
job_request.job_id.job_category = job_request.job_category.id
elif job_category:
if job_request.job_id and job_request.job_id.job_category != job_category: if job_request.job_id and job_request.job_id.job_category != job_category:
job_request.job_id.job_category = job_category.id job_request.job_id.job_category = job_category.id
write_vals = {key: value for key, value in write_vals.items() if value not in (False, None, "")} write_vals = {key: value for key, value in write_vals.items() if value not in (False, None, "")}
@ -1805,7 +1813,14 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
job_category = self._match_job_category(parsed_data, parsed_payload) job_category = self._match_job_category(parsed_data, parsed_payload)
if job_category and job.job_category != job_category: if job_category and job.job_category != job_category:
job.job_category = job_category.id job.job_category = job_category.id
category_dict = {
str(category['id']): category['category_name']
for category in self.env['job.category'].search_read([], ['id', 'category_name'])
}
reverse_category = {v: k for k, v in category_dict.items()}
job_category = parsed_data.get("job_category")
job_category_id = reverse_category.get(job_category, job_category)
create_vals = { create_vals = {
"job_id": job.id, "job_id": job.id,
"company_id": self.env.company.id, "company_id": self.env.company.id,
@ -1813,12 +1828,17 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
"requirements": parsed_data.get("requirements"), "requirements": parsed_data.get("requirements"),
"target_from": self._parse_date_value(parsed_data.get("start_date")), "target_from": self._parse_date_value(parsed_data.get("start_date")),
"target_to": self._parse_date_value(parsed_data.get("end_date")), "target_to": self._parse_date_value(parsed_data.get("end_date")),
"job_category": job_category.id if job_category else False, "job_category": int(job_category_id) if job_category_id and job_category_id.isdigit() else False,
"address_id":False,
"recruitment_type": 'external'
} }
if request_id: if request_id:
create_vals["recruitment_sequence"] = request_id create_vals["recruitment_sequence"] = request_id
create_vals = {key: value for key, value in create_vals.items() if value not in (False, None, "")} create_vals = {key: value for key, value in create_vals.items() if value not in (False, None, "")}
return job_request_model.create(create_vals), "created" job_request = job_request_model.create(create_vals)
job_request.sudo().write({"address_id" : False})
return job_request, "created"
def _match_existing_job_position(self, job_title): def _match_existing_job_position(self, job_title):
hr_job_model = self.env["hr.job"] hr_job_model = self.env["hr.job"]

View File

@ -5,10 +5,10 @@
<field name="model">hr.recruitment.auto.doc.wizard</field> <field name="model">hr.recruitment.auto.doc.wizard</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<form string="Parse Recruitment Documents" create="0" edit="1"> <form string="Parse Recruitment Documents" create="0" edit="1">
<header> <!-- <header>-->
<button name="action_parse_documents" string="Parse Documents" type="object" class="btn-primary" invisible="not attachment_ids and not resume_file"/> <!-- <button name="action_parse_documents" string="Parse Documents" type="object" class="btn-primary" invisible="not attachment_ids and not resume_file"/>-->
<!-- <button string="Close" special="cancel" class="btn-secondary"/>--> <!--&lt;!&ndash; <button string="Close" special="cancel" class="btn-secondary"/>&ndash;&gt;-->
</header> <!-- </header>-->
<sheet> <sheet>
<group> <group>
<group> <group>
@ -39,7 +39,15 @@
invisible="1"/> invisible="1"/>
</group> </group>
<group string="Uploaded Files"> <div class="text-end">
<button name="action_parse_documents"
string="Parse Documents"
type="object"
class="btn-primary"
invisible="not attachment_ids and not resume_file"/>
</div>
<group string="Uploaded Files" invisible="not line_ids">
<field name="line_ids" nolabel="1" readonly="1"> <field name="line_ids" nolabel="1" readonly="1">
<kanban class="o_kanban_small_column"> <kanban class="o_kanban_small_column">
<templates> <templates>

View File

@ -29,6 +29,7 @@
'data/sequence.xml', 'data/sequence.xml',
'data/mail_template.xml', 'data/mail_template.xml',
'data/templates.xml', 'data/templates.xml',
'views/submission_share_history.xml',
'views/job_category.xml', 'views/job_category.xml',
'views/hr_location.xml', 'views/hr_location.xml',
'views/stages.xml', 'views/stages.xml',

View File

@ -14,7 +14,7 @@
<t t-set="user_names" <t t-set="user_names"
t-value="', '.join(object.user_id.mapped('name')) if object.user_id else 'Recruiter'"/> t-value="', '.join(object.user_id.mapped('name')) if object.user_id else 'Recruiter'"/>
<t t-set="location_names" <t t-set="location_names"
t-value="', '.join(object.locations.mapped('name')) if object.locations else 'N/A'"/> t-value="', '.join(object.locations.mapped('location_name')) if object.locations else 'N/A'"/>
<div style="font-family: Arial, sans-serif; font-size: 14px; color: #333; line-height: 1.5; padding: 20px;"> <div style="font-family: Arial, sans-serif; font-size: 14px; color: #333; line-height: 1.5; padding: 20px;">
@ -669,14 +669,14 @@
<t t-set="locations"> <t t-set="locations">
<t t-if="object.hr_job_recruitment.locations"> <t t-if="object.hr_job_recruitment.locations">
<t t-set="locations_str" <t t-set="locations_str"
t-value="', '.join(object.hr_job_recruitment.locations.mapped('name'))"/> t-value="', '.join(object.hr_job_recruitment.locations.mapped('location_name'))"/>
<t t-set="locations" t-value="locations_str"/> <t t-set="locations" t-value="locations_str"/>
</t> </t>
<t t-else=""> <t t-else="">
<t t-set="locations" t-value="''"/> <t t-set="locations" t-value="''"/>
</t> </t>
</t> </t>
<div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.6;"> <div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.6; background:#ffffff; color:#000000;">
<p>Dear Sir/Madam,</p> <p>Dear Sir/Madam,</p>
<p>Please find the applicant details below for your review.</p> <p>Please find the applicant details below for your review.</p>
<table style="width: 100%; border-collapse: collapse; margin: 16px 0;"> <table style="width: 100%; border-collapse: collapse; margin: 16px 0;">
@ -784,10 +784,10 @@
<field name="body_html" type="html"> <field name="body_html" type="html">
<t t-set="job_name" t-value="object.job_id.name or object.name or object.display_name or ''"/> <t t-set="job_name" t-value="object.job_id.name or object.name or object.display_name or ''"/>
<t t-set="recruitment_name" t-value="object.recruitment_sequence or object.display_name or ''"/> <t t-set="recruitment_name" t-value="object.recruitment_sequence or object.display_name or ''"/>
<t t-set="locations" t-value="', '.join(object.locations.mapped('name')) if object.locations else ''"/> <t t-set="locations" t-value="', '.join(object.locations.mapped('location_name')) if object.locations else ''"/>
<t t-set="primary_skills" t-value="', '.join(object.skill_ids.mapped('name')) if object.skill_ids else ''"/> <t t-set="primary_skills" t-value="', '.join(object.skill_ids.mapped('name')) if object.skill_ids else ''"/>
<t t-set="secondary_skills" t-value="', '.join(object.secondary_skill_ids.mapped('name')) if object.secondary_skill_ids else ''"/> <t t-set="secondary_skills" t-value="', '.join(object.secondary_skill_ids.mapped('name')) if object.secondary_skill_ids else ''"/>
<div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.6;"> <div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.6; background:#ffffff; color:#000000;">
<p>Dear Sir/Madam,</p> <p>Dear Sir/Madam,</p>
<p>Please find the job description and hiring details below for your review and sourcing support.</p> <p>Please find the job description and hiring details below for your review and sourcing support.</p>
<table style="width: 100%; border-collapse: collapse; margin: 16px 0;"> <table style="width: 100%; border-collapse: collapse; margin: 16px 0;">

View File

@ -1,9 +1,10 @@
from . import submission_share_history
from . import hr_recruitment from . import hr_recruitment
from . import hr_job_recruitment from . import hr_job_recruitment
from . import stages from . import stages
from . import applicant_request_forms from . import applicant_request_forms
from . import hr_applicant_stage_comment from . import hr_applicant_stage_comment
from . import hr_applicant from . import hr_applicant
from . import hr_job from . import hr_job
from . import res_partner from . import res_partner
from . import candidate_experience from . import candidate_experience

View File

@ -61,6 +61,30 @@ class HRApplicant(models.Model):
) )
stage_comment_count = fields.Integer(compute='_compute_stage_comment_count') stage_comment_count = fields.Integer(compute='_compute_stage_comment_count')
stage_comment_tooltips = fields.Json(compute='_compute_stage_comment_tooltips') stage_comment_tooltips = fields.Json(compute='_compute_stage_comment_tooltips')
submission_tracker = fields.One2many('recruitment.share.tracker','applicant_id')
submission_count = fields.Integer(
string="Submission Count",
compute="_compute_submission_count",
)
def _compute_submission_count(self):
for rec in self:
rec.submission_count = len(rec.submission_tracker)
def action_open_submissions_wizard(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"name": "Submission History",
"res_model": "recruitment.share.tracker",
"view_mode": "list",
"views": [
(self.env.ref("hr_recruitment_extended.view_recruitment_share_tracker_list_popup").id, "list"),
],
"domain": [("applicant_id", "=", self.id)],
"target": "new",
}
@api.depends('is_on_hold') @api.depends('is_on_hold')
def _compute_hold_state(self): def _compute_hold_state(self):

View File

@ -175,6 +175,7 @@ class HRJobRecruitment(models.Model):
requested_by = fields.Many2one('res.partner', string="Requested By", requested_by = fields.Many2one('res.partner', string="Requested By",
default=lambda self: self.env.user.partner_id, domain="[('contact_type','=',recruitment_type)]", tracking=True) default=lambda self: self.env.user.partner_id, domain="[('contact_type','=',recruitment_type)]", tracking=True)
submission_tracker = fields.One2many('recruitment.share.tracker','job_recruitment_id')
def action_toggle_chatter_visibility(self): def action_toggle_chatter_visibility(self):
for record in self: for record in self:
record.hide_chatter_suggestion = not record.hide_chatter_suggestion record.hide_chatter_suggestion = not record.hide_chatter_suggestion
@ -405,6 +406,7 @@ class HRJobRecruitment(models.Model):
'default_template_id': self.env.ref( 'default_template_id': self.env.ref(
'hr_recruitment_extended.job_recruitment_share_email_template' 'hr_recruitment_extended.job_recruitment_share_email_template'
).id, ).id,
'default_is_job_recruitment': True,
}, },
} }

View File

@ -0,0 +1,51 @@
from odoo import api, fields, models
class RecruitmentShareTracker(models.Model):
_name = "recruitment.share.tracker"
_description = "Recruitment Share Tracker"
_order = "date desc, id desc"
date = fields.Datetime(
string="Date",
required=True,
default=fields.Datetime.now,
)
share_type = fields.Selection(
[
("job", "Job"),
("applicant", "Applicant"),
],
string="Share Type",
required=True,
)
reference = fields.Char(
string="Reference",
)
applicant_id = fields.Many2one(
"hr.applicant",
string="Applicant",
ondelete="set null",
)
job_recruitment_id = fields.Many2one(
"hr.job.recruitment",
string="Job Position",
ondelete="set null",
)
email_from = fields.Char(
string="Email From",
)
email_to = fields.Char(
string="Email To",
)
is_client_submission = fields.Boolean(
string="Client Submission",
default=False,
)

View File

@ -1,6 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_location,hr.location,model_hr_location,base.group_user,1,1,1,1 access_hr_location,hr.location,model_hr_location,base.group_user,1,1,1,1
access_recruitment_share_tracker_user,recruitment.share.tracker.user,model_recruitment_share_tracker,hr_recruitment.group_hr_recruitment_user,1,1,1,1
access_job_category_user,job.category.user,model_job_category,base.group_user,1,0,0,0 access_job_category_user,job.category.user,model_job_category,base.group_user,1,0,0,0
access_job_category_manager,job.category.manager,model_job_category,hr_recruitment.group_hr_recruitment_user,1,1,1,1 access_job_category_manager,job.category.manager,model_job_category,hr_recruitment.group_hr_recruitment_user,1,1,1,1
@ -35,4 +36,5 @@ access_applicant_stage_comment_wizard,applicant.stage.comment.wizard.user,model_
access_hr_application_public,hr.applicant.public.access,hr_recruitment.model_hr_applicant,base.group_public,1,0,0,0 access_hr_application_public,hr.applicant.public.access,hr_recruitment.model_hr_applicant,base.group_public,1,0,0,0
access_hr_application_group_hr,hr.applicant.hr.access,hr_recruitment.model_hr_applicant,hr.group_hr_manager,1,1,0,0 access_hr_application_group_hr,hr.applicant.hr.access,hr_recruitment.model_hr_applicant,hr.group_hr_manager,1,1,0,0
access_applicant_request_forms_hr_user,access.applicant.request.forms.hr.user,model_applicant_request_forms,hr.group_hr_user,1,1,1,1 access_applicant_request_forms_hr_user,access.applicant.request.forms.hr.user,model_applicant_request_forms,hr.group_hr_user,1,1,1,1
access_hr_skill,access.hr.skill.user,hr_skills.model_hr_skill,base.group_public,1,0,0,0 access_hr_skill,access.hr.skill.user,hr_skills.model_hr_skill,base.group_public,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_location hr.location model_hr_location base.group_user 1 1 1 1
3 access_job_category_user access_recruitment_share_tracker_user job.category.user recruitment.share.tracker.user model_job_category model_recruitment_share_tracker base.group_user hr_recruitment.group_hr_recruitment_user 1 0 1 0 1 0 1
4 access_job_category_user job.category.user model_job_category base.group_user 1 0 0 0
5 access_job_category_manager job.category.manager model_job_category hr_recruitment.group_hr_recruitment_user 1 1 1 1
6 access_hr_job_recruitment_user access.hr.job.recruitment.user model_hr_job_recruitment base.group_user 1 1 0 0
7 access_hr_job_recruitment_manager access.hr.job.recruitment.manager model_hr_job_recruitment hr_recruitment.group_hr_recruitment_user 1 1 1 1
36
37
38
39
40

View File

@ -90,6 +90,10 @@
<button name="action_open_stage_comment_wizard" type="object" class="oe_stat_button" icon="fa-comments"> <button name="action_open_stage_comment_wizard" type="object" class="oe_stat_button" icon="fa-comments">
<field name="stage_comment_count" widget="statinfo" string="Stage Notes"/> <field name="stage_comment_count" widget="statinfo" string="Stage Notes"/>
</button> </button>
<button name="action_open_submissions_wizard" type="object" class="oe_stat_button" icon="fa-file-text-o" invisible="not submission_tracker">
<field name="submission_count" widget="statinfo" string="Share History"/>
</button>
</xpath> </xpath>
<xpath expr="//page[@name='application_details']/group[1]" position="after"> <xpath expr="//page[@name='application_details']/group[1]" position="after">
<group string="Client Submission"> <group string="Client Submission">

View File

@ -110,10 +110,10 @@
</group> </group>
<group> <group>
<group> <group>
<field name="job_category" force_save="1"/> <field name="job_category" force_save="1" placeholder="Select Category"/>
</group> </group>
<group> <group>
<field name="job_priority"/> <field name="job_priority" placeholder="Select Priority"/>
</group> </group>
</group> </group>
@ -201,6 +201,20 @@
<separator string="Process Details"/> <separator string="Process Details"/>
<field name="job_details" nolabel="1"/> <field name="job_details" nolabel="1"/>
</page> </page>
<page string="Submission History" name="submission_tracker" invisible="not submission_tracker">
<field name="submission_tracker">
<list create="0" edit="0" delete="0">
<field name="date"/>
<field name="share_type" column_invisible="1"/>
<field name="reference"/>
<field name="applicant_id" column_invisible="1"/>
<field name="job_recruitment_id" column_invisible="1"/>
<field name="email_from"/>
<field name="email_to"/>
<field name="is_client_submission" column_invisible="1"/>
</list>
</field>
</page>
</notebook> </notebook>
</sheet> </sheet>
<chatter open_attachments="True" invisible="hide_chatter_suggestion"/> <chatter open_attachments="True" invisible="hide_chatter_suggestion"/>
@ -429,6 +443,7 @@
<field name="name">Job Positions Recruitment</field> <field name="name">Job Positions Recruitment</field>
<field name="res_model">hr.job.recruitment</field> <field name="res_model">hr.job.recruitment</field>
<field name="view_mode">kanban,list,form</field> <field name="view_mode">kanban,list,form</field>
<field name="domain"></field>
<field name="search_view_id" ref="view_job_recruitment_filter"/> <field name="search_view_id" ref="view_job_recruitment_filter"/>
<field name="help" type="html"> <field name="help" type="html">
<p class="o_view_nocontent_smiling_face"> <p class="o_view_nocontent_smiling_face">
@ -446,6 +461,7 @@
<field name="res_model">hr.job.recruitment</field> <field name="res_model">hr.job.recruitment</field>
<field name="view_mode">kanban,list,form,search</field> <field name="view_mode">kanban,list,form,search</field>
<field name="search_view_id" ref="view_job_recruitment_filter"/> <field name="search_view_id" ref="view_job_recruitment_filter"/>
<field name="domain"></field>
<field name="context">{"search_default_open_status":1,"search_default_my_assignments":1}</field> <field name="context">{"search_default_open_status":1,"search_default_my_assignments":1}</field>
<field name="help" type="html"> <field name="help" type="html">
<p class="o_view_nocontent_smiling_face"> <p class="o_view_nocontent_smiling_face">
@ -482,7 +498,7 @@
active="0" active="0"
sequence="2"/> sequence="2"/>
<menuitem name="JP" <menuitem name="Job Position"
id="menu_hr_job_descriptions" id="menu_hr_job_descriptions"
parent="hr_recruitment.menu_hr_recruitment_root" parent="hr_recruitment.menu_hr_recruitment_root"
action="action_hr_job_recruitment_awaiting_published" action="action_hr_job_recruitment_awaiting_published"

View File

@ -117,7 +117,7 @@
<xpath expr="//notebook" position="before"> <xpath expr="//notebook" position="before">
<group> <group>
<field name="department_id"/> <field name="department_id"/>
<field name="job_category"/> <field name="job_category" placeholder="Select Category"/>
</group> </group>
</xpath> </xpath>
<xpath expr="//notebook" position="inside"> <xpath expr="//notebook" position="inside">
@ -338,6 +338,7 @@
<record id="hr_recruitment.action_hr_candidate" model="ir.actions.act_window"> <record id="hr_recruitment.action_hr_candidate" model="ir.actions.act_window">
<field name="search_view_id" ref="hr_recruitment.hr_candidate_view_search"/> <field name="search_view_id" ref="hr_recruitment.hr_candidate_view_search"/>
<field name="context">{'search_default_my_candidates': 1,'active_test': False}</field> <field name="context">{'search_default_my_candidates': 1,'active_test': False}</field>
<field name="view_mode">kanban,list,form,activity</field>
</record> </record>
<menuitem <menuitem

View File

@ -12,7 +12,7 @@
<attribute name="required">1</attribute> <attribute name="required">1</attribute>
</xpath> </xpath>
<xpath expr="//field[@name='job_id']" position="after"> <xpath expr="//field[@name='job_id']" position="after">
<field name="job_category" required="1" readonly="state not in ['draft']"/> <field name="job_category" placeholder="Select Category" required="1" readonly="state not in ['draft']"/>
<field name="hr_job_recruitment" string="Job Recruitment ID" readonly="not is_hr or state == 'jd_created'" force_save="1" invisible="state not in ['final','jd_created']" options="{'no_quick_create': True, 'no_create_edit': True, 'no_open': True}"/> <field name="hr_job_recruitment" string="Job Recruitment ID" readonly="not is_hr or state == 'jd_created'" force_save="1" invisible="state not in ['final','jd_created']" options="{'no_quick_create': True, 'no_create_edit': True, 'no_open': True}"/>
<field name="recruitment_status" widget="statusbar" readonly="1" force_save="1" invisible="not hr_job_recruitment" options="{'clickable': '1', 'fold_field': 'fold'}"/> <field name="recruitment_status" widget="statusbar" readonly="1" force_save="1" invisible="not hr_job_recruitment" options="{'clickable': '1', 'fold_field': 'fold'}"/>
</xpath> </xpath>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<record id="view_recruitment_share_tracker_list_popup" model="ir.ui.view">
<field name="name">recruitment.share.tracker.popup.list</field>
<field name="model">recruitment.share.tracker</field>
<field name="arch" type="xml">
<list create="0"
edit="0"
delete="0"
open_form_view="0">
<field name="date"/>
<field name="share_type"/>
<field name="reference"/>
<field name="email_from"/>
<field name="email_to"/>
<field name="is_client_submission"/>
</list>
</field>
</record>
</odoo>

View File

@ -1,65 +1,128 @@
from odoo import models, fields, api from odoo import models, fields, api
from odoo.exceptions import UserError from odoo.exceptions import UserError
class ClientSubmissionsMailTemplateWizard(models.TransientModel): class ClientSubmissionsMailTemplateWizard(models.TransientModel):
_name = 'client.submission.mails.template.wizard' _name = 'client.submission.mails.template.wizard'
_description = 'Client Submission Mails Template Wizard' _description = 'Client Submission Mails Template Wizard'
template_id = fields.Many2one('mail.template', string='Email Template') template_id = fields.Many2one('mail.template', string='Email Template')
submit_date = fields.Date(string='Submission Date', required=True, default=fields.Date.today()) submit_date = fields.Date(string='Submission Date', required=True, default=fields.Date.today())
send_email_from_odoo = fields.Boolean(string="Send Email From Odoo", default=False) is_job_recruitment = fields.Boolean(string='Is Job Recruitment', default=False)
is_client_submission = fields.Boolean(string='Is Client Submission?', default=False)
send_email_from_odoo = fields.Boolean(string="Send Email", default=True)
email_from = fields.Char('Email From') email_from = fields.Char('Email From')
email_to = fields.Char('Email To') email_to = fields.Char('Email To')
email_cc = fields.Text('Email CC') email_cc = fields.Text('Email CC')
email_subject = fields.Char() email_subject = fields.Char()
email_body = fields.Html( email_body = fields.Html(
'Body', render_engine='qweb', render_options={'post_process': True}, 'Body',
prefetch=True, translate=True, sanitize='email_outgoing', render_engine='qweb',
render_options={'post_process': True},
prefetch=True,
translate=True,
sanitize='email_outgoing',
) )
@api.onchange('template_id') @api.onchange('template_id')
def _onchange_template_id(self): def _onchange_template_id(self):
""" Update the email body and recipients based on the selected template. """ if not self.template_id:
if self.template_id: return
record_id = self.env.context.get('active_id')
if record_id:
record = self.env[self.template_id.model].browse(record_id)
if not record.exists(): record_id = self.env.context.get('active_id')
raise UserError("The record does not exist or is not accessible.") active_model = self.env.context.get('active_model')
# Fetch email template if not record_id:
email_template = self.env['mail.template'].browse(self.template_id.id) return
if not email_template: record = self.env[active_model].browse(record_id)
raise UserError("Email template not found.")
self.email_from = record.user_id.partner_id.email self.email_from = record.user_id.partner_id.email
self.email_to = record.requested_by.email
self.email_body = email_template.body_html # Assign the rendered email bodyc
self.email_subject = email_template.subject
if active_model == 'hr.applicant':
self.email_to = record.hr_job_recruitment.requested_by.email
else:
self.email_to = record.requested_by.email
# Render subject
self.email_subject = self.template_id._render_field(
'subject',
[record.id],
)[record.id]
# Render body
self.email_body = self.template_id._render_field(
'body_html',
[record.id],
)[record.id]
def action_send_email(self): def action_send_email(self):
""" Send email to the selected partners """ """Send email and create recruitment share tracker."""
self.ensure_one()
record_id = self.env.context.get('active_id') record_id = self.env.context.get('active_id')
for rec in self: model_name = self.env.context.get('active_model')
record = self.env[self.template_id.model].browse(record_id)
if rec.send_email_from_odoo: record = self.env[self.template_id.model].browse(record_id)
template = self.env.ref('hr_recruitment_extended.application_client_submission_email_template')
values = { tracker_values = {
'email_from': rec.email_from, 'date': fields.Datetime.now(),
'email_to': rec.email_to, 'is_client_submission': self.is_client_submission,
'email_cc': rec.email_cc, }
'subject' : rec.email_subject,
'body_html': rec.email_body, if model_name == 'hr.applicant':
} tracker_values.update({
render_ctx = dict(client_name=record.hr_job_recruitment.requested_by.name) 'share_type': 'applicant',
# Use 'with_context' to override the email template fields dynamically 'reference': "%s/%s" % (
template.sudo().with_context(**render_ctx).send_mail(self.env.context.get('active_id'),email_values=values, force_send=True) record.hr_job_recruitment.recruitment_sequence,
record.candidate_id.candidate_sequence,
),
'applicant_id': record.id,
})
else:
tracker_values.update({
'share_type': 'job',
'reference': record.recruitment_sequence,
'job_recruitment_id': record.id,
})
if self.send_email_from_odoo:
# Use the selected template
template = self.template_id
values = {
'email_from': self.email_from,
'email_to': self.email_to,
'email_cc': self.email_cc,
'subject': self.email_subject,
'body_html': self.email_body,
}
if model_name == 'hr.applicant':
client_name = record.hr_job_recruitment.requested_by.name
else:
client_name = record.requested_by.name
render_ctx = {
'client_name': client_name,
}
tracker_values['email_from'] = self.email_from
tracker_values['email_to'] = self.email_to
template.sudo().with_context(**render_ctx).send_mail(
record.id,
email_values=values,
force_send=True,
)
if self.is_client_submission and model_name == 'hr.applicant':
record.sudo().write({ record.sudo().write({
'submitted_to_client': True, 'submitted_to_client': True,
'client_submission_date': rec.submit_date, 'client_submission_date': self.submit_date,
'submitted_stage': record.recruitment_stage_id.id, 'submitted_stage': record.recruitment_stage_id.id,
}) })
return {'type': 'ir.actions.act_window_close'} # Close wizard after sending
self.env['recruitment.share.tracker'].sudo().create(tracker_values)
return {'type': 'ir.actions.act_window_close'}

View File

@ -9,6 +9,8 @@
<form> <form>
<group> <group>
<field name="submit_date"/> <field name="submit_date"/>
<field name="is_job_recruitment" invisible="1"/>
<field name="is_client_submission" invisible="is_job_recruitment"/>
<field name="send_email_from_odoo"/> <field name="send_email_from_odoo"/>
<separator string="Section Title"/> <separator string="Section Title"/>
<field name="template_id" options="{'no_create': True}" invisible="not send_email_from_odoo" required="send_email_from_odoo" readonly="1" force_save="1"/> <field name="template_id" options="{'no_create': True}" invisible="not send_email_from_odoo" required="send_email_from_odoo" readonly="1" force_save="1"/>

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

@ -33,9 +33,9 @@ class HrContract(models.Model):
"""Get the default notice period from the configuration. """Get the default notice period from the configuration.
:return: The default notice period in days. :return: The default notice period in days.
:rtype: int """ :rtype: int """
return self.env['ir.config_parameter'].get_param( return self.env['ir.config_parameter'].sudo().get_param(
'hr_employee_updation.no_of_days') if self.env[ 'hr_employee_updation.no_of_days') if self.env[
'ir.config_parameter'].get_param( 'ir.config_parameter'].sudo().get_param(
'hr_employee_updation.notice_period') else 0 'hr_employee_updation.notice_period') else 0
notice_days = fields.Integer(string="Notice Period", notice_days = fields.Integer(string="Notice Period",

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,8 @@ 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")
image_template = fields.Image(related='employee_eva_id.image_1920', string="Image")
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 +123,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,122 @@
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)
image_1920 = fields.Image(related='hr_employee_id.image_1920')
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([
@ -44,37 +47,83 @@ class HrNoticeAppraisal(models.Model):
new_end_date = fields.Datetime(string="New End Date") new_end_date = fields.Datetime(string="New End Date")
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")
image_1920 = fields.Image(related='hr_employee_id.image_1920',string='Employee Image')
@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 +138,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 +236,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 +251,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,21 +10,27 @@
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>
<sheet> <sheet>
<field name="image_template"
widget="image"
class="oe_avatar"
options="{'preview_image': 'image_template'}"/>
<group string="Details" col="2"> <group string="Details" col="2">
<group> <group>
<field name="seq" string="Performance Id"/> <field name="seq" string="Reference"/>
<field name="name" string="Reference" placeholder="Administration Appraisal Template"/> <field name="name" string="Subject" readonly="1" placeholder="Administration Appraisal Template"/>
<field name="manager_ids" widget="many2many_tags" string="Performance Evaluator"/> <field name="manager_ids" widget="many2many_tags" string="Performance Evaluator"
<field name="employee_eva_id"/> invisible="1"/>
<field name="employee_department_id"/> <field name="employee_eva_id" readonly="1" options="{'no_open': True}"/>
<field name="appraisal_period_type_id" string="Performance Type"/> <field name="employee_department_id" readonly="1" options="{'no_open': True}"/>
<field name="appraisal_period_id" string="Performance Period"/> <field name="appraisal_period_type_id" string="Appraisal Type" readonly="1" options="{'no_open' : True}"/>
<field name="hr_employee_id" string="HR"/> <field name="appraisal_period_id" string="Appraisal Period" readonly="1" options="{'no_open' : True}"/>
<field name="stage_config_ids" widget="many2many_tags"/> <field name="hr_employee_id" string="HR" readonly="1" options="{'no_open' : True}"/>
<field name="stage_config_ids" widget="many2many_tags" readonly="1"/>
</group> </group>
<group> <group>
<field name="company_id" string="Company"/> <field name="company_id" string="Company"/>
@ -33,8 +39,8 @@
<!-- <field name="hr_email_notify" string="Send Email if HR APPROVE or REJECT Evaluation?"/>--> <!-- <field name="hr_email_notify" string="Send Email if HR APPROVE or REJECT Evaluation?"/>-->
<field name="start_date" readonly="1"/> <field name="start_date" readonly="1"/>
<field name="end_date" readonly="1"/> <field name="end_date" readonly="1"/>
<field name="template_rating_bool"/> <field name="template_rating_bool" string="Employee Rating"/>
<field name="template_point_bool"/> <field name="template_point_bool" string="Employee points"/>
<field name="kra_weightage"/> <field name="kra_weightage"/>
</group> </group>
</group> </group>
@ -48,8 +54,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 +88,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 +107,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 +130,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 +141,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,9 @@
<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"
@ -14,33 +16,53 @@
<field name="state" widget="statusbar"/> <field name="state" widget="statusbar"/>
</header> </header>
<sheet> <sheet>
<!-- <div class="oe_title" style="display:flex; align-items:center; gap:20px;">-->
<!-- <field name="image_1920"-->
<!-- widget="image"-->
<!-- class="oe_avatar"-->
<!-- readonly="1"/>-->
<!-- <h1>-->
<!-- <field name="seq" readonly="1"/>-->
<!-- </h1>-->
<!-- </div>-->
<field name="image_1920"
widget="image"
class="oe_avatar"
readonly="1"/>
<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="Employee" 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="1"/>
<field name="start_date"/> <field name="appraisal_notice_id"
<field name="end_date"/> options="{'no_edit': True, 'no_create': True, 'no_open': True}"
readonly="1"/>
<field name="subject" readonly="1"/>
<field name="start_date" readonly="1" force_save="1"/>
<field name="end_date" readonly="1" 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="1"/>
<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="1"/>
</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 +75,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 +96,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 +120,105 @@
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_button_box" name="button_box"/>
<field name="image_1920"
widget="image"
class="oe_avatar"
options="{'preview_image': 'image_1920'}"/>
<div class="oe_title">
<h1>
<field name="name" placeholder="Performance Appraisal Notification" required="1"
readonly="state != 'draft'"/>
</h1>
</div>
<group col="2">
<group string="Appraisal Details">
<field name="seq"/>
<field name="hr_employee_id" readonly="1" options="{'no_open':True}"/>
<field name="appraisal_type_id" options="{'no_create': True,'no_open': True}"
readonly="state != 'draft'"/>
<field name="appraisal_period_id" options="{'no_create': True,'no_open': True}"
readonly="state != 'draft'"/>
<field name="start_date" readonly="state != 'draft'"/>
<field name="end_date" readonly="state != 'draft'"/>
</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" readonly="state != 'draft'"/>
<field name="stage_config_ids" widget="many2many_tags" required="1"
readonly="state != 'draft'"/>
</group>
</group>
<notebook>
<page string="Notification Message">
<field name="body" widget="html" readonly="state != 'draft'"/>
</page>
<page string="Audit">
<group>
<field name="create_uid" readonly="1" options="{'no_open':True}"/>
<field name="create_date" readonly="1"/>
<field name="write_uid" readonly="1" options="{'no_open':True}"/>
<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

@ -5,14 +5,14 @@
<field name="name">Applicant Offer Email Template</field> <field name="name">Applicant Offer Email Template</field>
<field name="model_id" ref="offer_letters.model_offer_letter"/> <field name="model_id" ref="offer_letters.model_offer_letter"/>
<field name="email_from">{{ user.email_formatted }}</field> <field name="email_from">{{ user.email_formatted }}</field>
<field name="email_to">{{ object.candidate_id.email_from or '' }}</field> <field name="email_to">{{ object.main_candidate_id.email_from or '' }}</field>
<field name="subject">Offer Letter - {{ object.position or object.candidate_id.job_id.name or '' }}</field> <field name="subject">Offer Letter - {{ object.position or object.main_candidate_id.job_id.name or '' }}</field>
<field name="description"> <field name="description">
Send applicant offer mail with offer letter attachment. Send applicant offer mail with offer letter attachment.
</field> </field>
<field name="body_html" type="html"> <field name="body_html" type="html">
<div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.7;"> <div style="margin: 0; padding: 0; font-size: 13px; line-height: 1.7;">
<p>Dear <t t-esc="object.candidate_id.partner_name or ''"/>,</p> <p>Dear <t t-esc="object.main_candidate_id.partner_name or ''"/>,</p>
<p> <p>
With reference to the interview and subsequent discussions you had with us, we are pleased to select With reference to the interview and subsequent discussions you had with us, we are pleased to select
you for the position of "<t t-esc="object.position or ''"/>" in our organization with the following you for the position of "<t t-esc="object.position or ''"/>" in our organization with the following

View File

@ -1,4 +1,5 @@
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.api import readonly
from odoo.exceptions import UserError from odoo.exceptions import UserError
from collections import defaultdict from collections import defaultdict
from datetime import timedelta, datetime from datetime import timedelta, datetime
@ -26,8 +27,24 @@ class OfferLetter(models.Model):
default=lambda self: _('New'), default=lambda self: _('New'),
copy=False copy=False
) )
candidate_id = fields.Many2one( 'hr.applicant', string='Candidate', required=True, candidate_id = fields.Many2one( 'hr.applicant', string='Applicant', required=False,
) )
main_candidate_id = fields.Many2one('hr.candidate',string='Candidate', readonly=False, required=True)
@api.onchange('candidate_id')
def _onchange_candidate_id(self):
if self.candidate_id:
self.main_candidate_id = self.candidate_id.candidate_id
main_candidate_name = fields.Char(compute="_compute_main_candidate_name", readonly=False)
@api.depends('candidate_id','main_candidate_id')
def _compute_main_candidate_name(self):
for rec in self:
if rec.candidate_id:
rec.main_candidate_name = rec.candidate_id.partner_name
elif rec.main_candidate_id:
rec.main_candidate_name = rec.main_candidate_id.partner_name
requested_by_id = fields.Many2one('res.users', string='Requested By', readonly=True, tracking=True) requested_by_id = fields.Many2one('res.users', string='Requested By', readonly=True, tracking=True)
request_date = fields.Datetime(string='Requested On', readonly=True, tracking=True) request_date = fields.Datetime(string='Requested On', readonly=True, tracking=True)

View File

@ -62,9 +62,9 @@
<div style="margin-bottom: 25px;"> <div style="margin-bottom: 25px;">
<strong>To,</strong> <strong>To,</strong>
<br/> <br/>
<strong t-esc="o.candidate_id.partner_name"/> <strong t-esc="o.main_candidate_id.partner_name"/>
<br/> <br/>
<div t-if="o.candidate_id.private_street and o.candidate_id.private_city"> <div t-if="o.candidate_id and o.candidate_id.private_street and o.candidate_id.private_city">
<t t-esc="o.candidate_id.private_street"/> <t t-esc="o.candidate_id.private_street"/>
<br/> <br/>
<t t-esc="o.candidate_id.private_street2" t-if="o.candidate_id.private_street2"/> <t t-esc="o.candidate_id.private_street2" t-if="o.candidate_id.private_street2"/>
@ -81,7 +81,7 @@
<!-- DEAR LINE --> <!-- DEAR LINE -->
<div style="margin-bottom: 20px;"> <div style="margin-bottom: 20px;">
<strong>Dear <strong>Dear
<t t-esc="o.candidate_id.partner_name"/>, <t t-esc="o.main_candidate_id.partner_name"/>,
</strong> </strong>
</div> </div>
@ -368,7 +368,7 @@
<br/> <br/>
<br/> <br/>
<br/> <br/>
<strong t-esc="o.candidate_id.partner_name"/> <strong t-esc="o.main_candidate_id.partner_name"/>
<br/> <br/>
<strong t-esc="o.position"/> <strong t-esc="o.position"/>
<br/> <br/>
@ -486,7 +486,7 @@
<br/> <br/>
<br/> <br/>
<strong>Employee Name : <strong>Employee Name :
<t t-esc="o.candidate_id.partner_name"/> <t t-esc="o.main_candidate_id.partner_name"/>
</strong> </strong>
<br/> <br/>
<br/> <br/>
@ -595,7 +595,7 @@
<br/> <br/>
<br/> <br/>
<br/> <br/>
<strong t-esc="o.candidate_id.partner_name"/> <strong t-esc="o.main_candidate_id.partner_name"/>
<br/> <br/>
<strong t-esc="o.position"/> <strong t-esc="o.position"/>
<br/> <br/>
@ -621,7 +621,7 @@
Company incorporated under Indian Companies Act 1956, having registered office in Company incorporated under Indian Companies Act 1956, having registered office in
Hyderabad, Hyderabad,
India ("Company") India ("Company")
<strong t-esc="o.candidate_id.partner_name"/> <strong t-esc="o.main_candidate_id.partner_name"/>
(Recipient) (Recipient)
</p> </p>
<p>Whereas "Company" wishes to explore the possibility of entering into an employment <p>Whereas "Company" wishes to explore the possibility of entering into an employment
@ -875,7 +875,7 @@
<strong t-esc="o.joining_date"/> <strong t-esc="o.joining_date"/>
</td> </td>
<td style="padding-top: 20px;"> <td style="padding-top: 20px;">
<strong t-esc="o.candidate_id.partner_name"/> <strong t-esc="o.main_candidate_id.partner_name"/>
<br/> <br/>
<strong t-esc="o.position"/> <strong t-esc="o.position"/>
<br/> <br/>

View File

@ -6,7 +6,7 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<list> <list>
<field name="name"/> <field name="name"/>
<field name="candidate_id"/> <field name="main_candidate_name"/>
<field name="position"/> <field name="position"/>
<field name="state"/> <field name="state"/>
<field name="sent_date"/> <field name="sent_date"/>
@ -32,7 +32,9 @@
<group> <group>
<group> <group>
<field name="name" readonly="state != 'requested'"/> <field name="name" readonly="state != 'requested'"/>
<field name="candidate_id"/> <field name="main_candidate_name" invisible="1"/>
<field name="candidate_id" invisible="not candidate_id" readonly="1" force_save="1"/>
<field name="main_candidate_id" invisible="candidate_id" force_save="1"/>
<field name="requested_by_id" readonly="1"/> <field name="requested_by_id" readonly="1"/>
<field name="request_date" readonly="1"/> <field name="request_date" readonly="1"/>
<field name="manager_id"/> <field name="manager_id"/>

View File

@ -34,7 +34,7 @@
<field name="jd_file" filename="jd_file_name" <field name="jd_file" filename="jd_file_name"
widget="binary" force_save="1"/> widget="binary" force_save="1"/>
<field name="jd_file_name" invisible="1" force_save="1"/> <field name="jd_file_name" invisible="1" force_save="1"/>
</group> </group>
<group> <group>
<field name="job_id" invisible="job_id == False" readonly="1" force_save="1" options="{'no_quick_create':True,'no_open':True}"/> <field name="job_id" invisible="job_id == False" readonly="1" force_save="1" options="{'no_quick_create':True,'no_open':True}"/>
<field name="number_of_positions" readonly="(state != 'draft' and not is_hr) or state == 'jd_created'"/> <field name="number_of_positions" readonly="(state != 'draft' and not is_hr) or state == 'jd_created'"/>
@ -46,7 +46,7 @@
<field name="assign_to" invisible="state not in ['final','jd_created']" options="{'no_quick_create': True, 'no_create_edit': True, 'no_open': True}"/> <field name="assign_to" invisible="state not in ['final','jd_created']" options="{'no_quick_create': True, 'no_create_edit': True, 'no_open': True}"/>
<field name="target_startdate" invisible="1" readonly="state in ['jd_created']"/> <field name="target_startdate" invisible="1" readonly="state in ['jd_created']"/>
<field name="target_deadline" widget="daterange" string="Target Deadline" options="{'start_date_field': 'target_startdate'}" on_change="1" readonly="state in ['jd_created']"/> <field name="target_deadline" widget="daterange" string="Target Deadline" options="{'start_date_field': 'target_startdate'}" on_change="1" readonly="state in ['jd_created']"/>
</group> </group>
<field name="notes" placeholder="Remarks" readonly="state == 'jd_created'" invisible="not notes"/> <field name="notes" placeholder="Remarks" readonly="state == 'jd_created'" invisible="not notes"/>
</group> </group>
<notebook> <notebook>
@ -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