-
![]()
-
Bench Management
bench.management.line
kanban,list,form
@@ -247,6 +239,7 @@
diff --git a/addons_extensions/disciplinary/__init__.py b/addons_extensions/disciplinary/__init__.py
new file mode 100755
index 000000000..5305644df
--- /dev/null
+++ b/addons_extensions/disciplinary/__init__.py
@@ -0,0 +1,3 @@
+# -*- coding: utf-8 -*-
+
+from . import models
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/__manifest__.py b/addons_extensions/disciplinary/__manifest__.py
new file mode 100755
index 000000000..266297f2d
--- /dev/null
+++ b/addons_extensions/disciplinary/__manifest__.py
@@ -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',
+}
diff --git a/addons_extensions/disciplinary/data/sequence.xml b/addons_extensions/disciplinary/data/sequence.xml
new file mode 100755
index 000000000..e291002e1
--- /dev/null
+++ b/addons_extensions/disciplinary/data/sequence.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ Employee Disciplinary
+ employee.disciplinary
+ IR
+ 5
+
+
+
+
+ Manage Incident
+ manage.incident
+ MI
+ 5
+
+
+
+
+ Disciplinary Sequence
+ hr.employee.sequence
+ ED
+ 5
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/models/__init__.py b/addons_extensions/disciplinary/models/__init__.py
new file mode 100755
index 000000000..2a55d3a16
--- /dev/null
+++ b/addons_extensions/disciplinary/models/__init__.py
@@ -0,0 +1,2 @@
+from . import disciplinary
+from . import employee_displane
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/models/disciplinary.py b/addons_extensions/disciplinary/models/disciplinary.py
new file mode 100755
index 000000000..3c78c4edd
--- /dev/null
+++ b/addons_extensions/disciplinary/models/disciplinary.py
@@ -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
diff --git a/addons_extensions/disciplinary/models/employee_displane.py b/addons_extensions/disciplinary/models/employee_displane.py
new file mode 100644
index 000000000..0abcba19e
--- /dev/null
+++ b/addons_extensions/disciplinary/models/employee_displane.py
@@ -0,0 +1,179 @@
+from odoo import models, fields, api, _
+from odoo.exceptions import ValidationError
+from datetime import date
+
+
+class HRDisciplinaryAction(models.Model):
+ _name = 'hr.employee.disciplinary'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _description = 'Employee Disciplinary Management'
+
+ active = fields.Boolean(default=True)
+ name = fields.Char('Reference', copy=False, readonly=True, default=lambda x: _('New'))
+ employee_id = fields.Many2one('hr.employee', string="Employee", required=True)
+ company_id = fields.Many2one('res.company', string="Company", required=True, default=lambda self: self.env.company)
+ employee_code = fields.Char(string='Employee Code', related='employee_id.employee_id',tracking=True,required=True)
+ # unit_id = fields.Many2one('unit.master', string="Unit",tracking=True)
+ department_id = fields.Many2one('hr.department', string="Department",tracking=True)
+ designation_id = fields.Many2one('hr.job', string="Designation",tracking=True)
+ doj = fields.Date(string="Date of Joining",tracking=True)
+ referred_by_id = fields.Many2one('res.users', string="Referred By",tracking=True)
+ loss_of_cost = fields.Float(string="Loss of Cost")
+ # employee_section_id = fields.Many2one('section.master',string='Section')
+ disciplinary_complaint_line_ids = fields.One2many('hr.disciplinary.complaint.line','disciplinary_id',string = 'Complaint Lines')
+ disciplinary_action_line_ids = fields.One2many('hr.disciplinary.action.line','disciplinary_id',string = 'Action Lines')
+ state = fields.Selection([
+ ('new', 'New'),
+ ('submitted', 'Submitted'),
+ ('pending', 'Pending'),
+ ('closed', 'Closed'),
+ ('cancel', 'Cancel')
+ ], default='new',tracking=True,string='State')
+ complaint_name = fields.Text('Complaint', compute='_compute_complaint_name', store=True)
+ name_1 = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary', string="Disciplinary")
+ complaint_date = fields.Date('Complaint Date')
+ language_id = fields.Many2one('res.lang', 'Language')
+ complaint_type_id = fields.Many2one('disciplinary.complaint.type', string="Complaint Type")
+ mistake_type_id = fields.Many2one('disciplinary.mistake.type', string="Mistake Type", required=True)
+ complaint = fields.Char(string='Complaints')
+ employee_id_2 = fields.Many2one('hr.employee', string='Employee')
+ related_record_count = fields.Integer(string="Disciplinary Action Records Count", compute="_compute_related_record_count")
+ # general_cat = fields.Many2one('general.category', string="General Category", tracking=True)
+ # cat_id = fields.Many2one('hr.category','Category')
+ occurrences = fields.Integer('Occurrences', store=True)
+ severe = fields.Char('Severe')
+ major = fields.Char('Major')
+ less_major = fields.Char('Less Major')
+ negligible = fields.Char('Negligible')
+ normal = fields.Char('Normal')
+ total_mistakes = fields.Char('Total Mistakes')
+ memo = fields.Char('Memo')
+ explanation = fields.Char('Explanation')
+ show_cause = fields.Char('Show Cause')
+ charge_sheet = fields.Char('Charge Sheet')
+ warning = fields.Char('Warning')
+ enquiry_notice = fields.Char('Enquiry Notice')
+ recovery_order = fields.Char('Recovery_ Order')
+ stoppage_of_increment = fields.Char('Stoppage Of Increment')
+ demotion = fields.Char('Demotion')
+ total_actions = fields.Char('Total Actions')
+ normal_action = fields.Char('Normal Actions')
+ suspension = fields.Char('Suspension')
+ total_cost = fields.Float('Total Cost')
+
+ @api.depends('employee_id')
+ def _compute_related_record_count(self):
+ for record in self:
+ record.related_record_count = self.env['hr.employee.disciplinary'].search_count([('employee_id', '=', record.employee_id.id)])
+
+ def action_open_related_records(self):
+ return {
+ 'name': 'Disciplinary Action Records',
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'hr.employee.disciplinary',
+ 'view_mode': 'list',
+ 'domain': [('employee_id', '=', self.employee_id.id)],
+ 'context': {'default_employee_id': self.employee_id.id},
+ }
+
+ @api.depends('disciplinary_complaint_line_ids.complaint')
+ def _compute_complaint_name(self):
+ for record in self:
+ complaints = record.disciplinary_complaint_line_ids.mapped('complaint')
+ record.complaint_name = "\n".join(filter(None, complaints))
+
+ def action_set_submitted(self):
+ self.state = 'submitted'
+
+ def action_set_pending(self):
+ self.state = 'pending'
+
+ def action_set_closed(self):
+ self.state = 'closed'
+
+ def action_set_cancel(self):
+ self.state = 'cancel'
+
+ def action_reset_to_new(self):
+ self.state = 'new'
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ for vals in vals_list:
+ if not vals.get('name') or vals['name'] == _('New'):
+ vals['name'] = self.env['ir.sequence'].next_by_code('hr.employee.sequence') or _('New')
+ return super().create(vals_list)
+
+ @api.onchange('employee_id')
+ def _onchange_employee_id(self):
+ for rec in self:
+ if rec.employee_id:
+ rec.employee_code = rec.employee_id.employee_id or ''
+ rec.department_id = rec.employee_id.department_id.id
+ rec.designation_id = rec.employee_id.job_id.id
+ rec.doj = rec.employee_id.doj
+ rec.company_id = rec.employee_id.company_id.id
+ # rec.unit_id = rec.employee_id.unit_name_hr.id if rec.employee_id.unit_name_hr else False
+ # rec.employee_section_id = rec.employee_id.section_name_hr.id if rec.employee_id.section_name_hr else False
+ else:
+ rec.employee_code = False
+ rec.department_id = False
+ rec.designation_id = False
+ rec.doj = False
+
+
+class DisciplinaryComplaintLine(models.Model):
+ _name = 'hr.disciplinary.complaint.line'
+ _description = 'Disciplinary Complaint Line'
+
+ name = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary',string="Disciplinary")
+ complaint_date = fields.Date('Complaint Date')
+ language_id = fields.Many2one('res.lang','Language')
+ complaint_type_id = fields.Many2one('disciplinary.complaint.type',string="Complaint Type")
+ mistake_type_id = fields.Many2one('disciplinary.mistake.type',string="Mistake Type")
+ complaint = fields.Char(string='Complaints')
+ employee_id = fields.Many2one('hr.employee', string='Employee')
+
+
+
+
+class DisciplinaryActionLine(models.Model):
+ _name = 'hr.disciplinary.action.line'
+ _description = 'Disciplinary Action Line'
+
+ name = fields.Char('Name')
+ disciplinary_id = fields.Many2one('hr.employee.disciplinary',string="Disciplinary")
+ action_taken_date = fields.Date('Action On')
+ action_type_id = fields.Many2one('disciplinary.action.type',string="Action Type")
+ action = fields.Char(string='Description')
+ action_name = fields.Char('ActionName')
+ related_complaint_id = fields.Many2one('hr.disciplinary.complaint.line', string="Related Complaint",
+ domain="[('disciplinary_id', '=', disciplinary_id)]")
+ employee_id = fields.Many2one('hr.employee', string='Employee')
+
+ @api.constrains('action_taken_date')
+ def _check_action_taken_date(self):
+ for record in self:
+ if record.action_taken_date and record.action_taken_date > date.today():
+ raise ValidationError("The Action On date cannot be in the future.")
+
+
+class DisciplinaryActionType(models.Model):
+ _name = 'disciplinary.action.type'
+ _description = 'Action Type'
+
+ name = fields.Char('Name', required=True)
+
+class DisciplinaryComplaintType(models.Model):
+ _name = 'disciplinary.complaint.type'
+ _description = 'Complaint Type'
+
+ name = fields.Char('Name', required=True)
+
+class DisciplinaryMistakeType(models.Model):
+ _name = 'disciplinary.mistake.type'
+ _description = 'Mistake Type'
+
+ name = fields.Char('Name', required=True)
diff --git a/addons_extensions/disciplinary/security/ir.model.access.csv b/addons_extensions/disciplinary/security/ir.model.access.csv
new file mode 100755
index 000000000..233dcb2b0
--- /dev/null
+++ b/addons_extensions/disciplinary/security/ir.model.access.csv
@@ -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
+
+
+
+
+
+
+
diff --git a/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml b/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml
new file mode 100644
index 000000000..569a0caf9
--- /dev/null
+++ b/addons_extensions/disciplinary/views/disciplinary_complaint_type.xml
@@ -0,0 +1,122 @@
+
+
+
+ disciplinary.complaint.type
+ disciplinary.complaint.type
+
+
+
+
+
+
+
+
+ disciplinary.complaint.type.form
+ disciplinary.complaint.type
+
+
+
+
+
+
+ Employee Disciplinary Complaint Type
+ disciplinary.complaint.type
+ list,form
+
+
+
+
+
+
+ hr.disciplinary.action.line
+ hr.disciplinary.action.line
+
+
+
+
+
+
+
+
+ hr.disciplinary.action.line.form
+ hr.disciplinary.action.line
+
+
+
+
+
+
+ Employee Disciplinary Action
+ hr.disciplinary.action.line
+ list,form
+
+
+
+
+
+
+
+ disciplinary.action.type
+ disciplinary.action.type
+
+
+
+
+
+
+
+
+ disciplinary.action.type.form
+ disciplinary.action.type
+
+
+
+
+
+
+ Employee Disciplinary Action Type
+ disciplinary.action.type
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/views/disciplinary_view.xml b/addons_extensions/disciplinary/views/disciplinary_view.xml
new file mode 100755
index 000000000..08a3bfdd9
--- /dev/null
+++ b/addons_extensions/disciplinary/views/disciplinary_view.xml
@@ -0,0 +1,264 @@
+
+
+
+
+ Employee Disciplinary list
+ employee.disciplinary
+
+
+
+
+
+
+
+
+
+
+
+ Employee Disciplinary form
+ employee.disciplinary
+
+
+
+
+
+
+
+ Manage Incident list
+ manage.incident
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Manage Incident form
+ manage.incident
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ incident Employee list
+ incident.employee
+
+
+
+
+
+
+
+
+
+ incident Employee form
+ incident.employee
+
+
+
+
+
+
+
+ Incident Reporting
+ employee.disciplinary
+ list,form
+
+
+
+
+ Manage Incident
+ manage.incident
+ list,form
+
+
+
+ Incident Type
+ incident.employee
+ list,form
+
+
+
+ Employee Disciplinary
+ hr.employee.disciplinary
+ list,form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/views/employee_displance.xml b/addons_extensions/disciplinary/views/employee_displance.xml
new file mode 100644
index 000000000..f762c695b
--- /dev/null
+++ b/addons_extensions/disciplinary/views/employee_displance.xml
@@ -0,0 +1,169 @@
+
+
+
+ employee.disciplinary.form
+ hr.employee.disciplinary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.disciplinary.list
+ hr.employee.disciplinary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.disciplinary.search
+ hr.employee.disciplinary
+
+
+
+
+
+
+
+
+
+ Employee Disciplinary
+ hr.employee.disciplinary
+ list,form
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/disciplinary/views/incident_sub_type.xml b/addons_extensions/disciplinary/views/incident_sub_type.xml
new file mode 100644
index 000000000..bd4c35cce
--- /dev/null
+++ b/addons_extensions/disciplinary/views/incident_sub_type.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+ incident.sub.employee.list
+ incident.sub.employee
+
+
+
+
+
+
+
+
+
+ incident.sub.employee.form
+ incident.sub.employee
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Incident Sub Type
+ incident.sub.employee
+ list,form
+
+
+
+
+
+
+
+
+
+
diff --git a/addons_extensions/disciplinary/views/mistake_type_views.xml b/addons_extensions/disciplinary/views/mistake_type_views.xml
new file mode 100644
index 000000000..cbc9efe6c
--- /dev/null
+++ b/addons_extensions/disciplinary/views/mistake_type_views.xml
@@ -0,0 +1,42 @@
+
+
+
+
+ mistake.type.list
+ disciplinary.mistake.type
+
+
+
+
+
+
+
+
+
+ mistake.type.form
+ disciplinary.mistake.type
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Mistake Type
+ disciplinary.mistake.type
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/employee_it_declaration/models/payroll_periods.py b/addons_extensions/employee_it_declaration/models/payroll_periods.py
index ed9bc86c8..5cc558492 100644
--- a/addons_extensions/employee_it_declaration/models/payroll_periods.py
+++ b/addons_extensions/employee_it_declaration/models/payroll_periods.py
@@ -1,12 +1,13 @@
-from odoo import models, fields, api
-from odoo.exceptions import ValidationError
-import calendar
+from odoo import models, fields, api
+from odoo.exceptions import ValidationError
+import calendar
-class PayrollPeriod(models.Model):
+class PayrollPeriod(models.Model):
_name = 'payroll.period'
_description = 'Payroll Period'
_rec_name = 'name'
+ _order = 'id desc'
_sql_constraints = [
('unique_name', 'unique(name)', 'The name must be unique.')
]
@@ -14,22 +15,22 @@ class PayrollPeriod(models.Model):
from_date = fields.Date(string="From Date", required=True)
to_date = fields.Date(string="To Date", required=True)
name = fields.Char(string="Name", required=True)
- period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods")
-
- @api.model_create_multi
- def create(self, vals_list):
- periods = super().create(vals_list)
- active_investment_types = self.env['it.investment.type'].search([('active', '=', True)])
- if active_investment_types:
- active_investment_types.write({
- 'period_ids': [(4, period.id) for period in periods],
- })
- return periods
-
- @api.onchange('from_date', 'to_date')
- def onchange_from_to_date(self):
- for rec in self:
- if rec.from_date and rec.to_date:
+ period_line_ids = fields.One2many('payroll.period.line', 'period_id', string="Monthly Periods")
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ periods = super().create(vals_list)
+ active_investment_types = self.env['it.investment.type'].search([('active', '=', True)])
+ if active_investment_types:
+ active_investment_types.write({
+ 'period_ids': [(4, period.id) for period in periods],
+ })
+ return periods
+
+ @api.onchange('from_date', 'to_date')
+ def onchange_from_to_date(self):
+ for rec in self:
+ if rec.from_date and rec.to_date:
rec.name = f"{rec.from_date.year}-{rec.to_date.year}"
diff --git a/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml b/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
index cf0b7b0cf..d0ce96181 100644
--- a/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
+++ b/addons_extensions/employee_it_declaration/views/employee_payslip_download_wizard_views.xml
@@ -41,6 +41,11 @@
+
diff --git a/addons_extensions/helpdesk/controllers/portal.py b/addons_extensions/helpdesk/controllers/portal.py
index 04d265b04..c0d970109 100644
--- a/addons_extensions/helpdesk/controllers/portal.py
+++ b/addons_extensions/helpdesk/controllers/portal.py
@@ -179,9 +179,8 @@ class CustomerPortal(portal.CustomerPortal):
('use_website_helpdesk_form', '=', True),
'|',
('company_id', '=', False),
- ('company_id', 'in', [request.env.company.id])
+ ('company_id', 'in', [request.env.user.company_id.id])
])
-
selection_dict = dict(
request.env['helpdesk.team']._fields['portal_ticket_type'].selection
)
diff --git a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py
index 3979d0c06..19760fec8 100644
--- a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py
+++ b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard.py
@@ -483,6 +483,10 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
}
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 {
"request_id": {"type": "string", "description": "Request or requisition identifier"},
"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"},
"budget": {"type": "string", "description": "Budget or salary range"},
"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):
@@ -1766,8 +1771,11 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
"secondary_skill_ids": [(6, 0, secondary_skills.ids)] if secondary_skills else False,
}
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:
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, "")}
@@ -1805,7 +1813,14 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
job_category = self._match_job_category(parsed_data, parsed_payload)
if job_category and job.job_category != job_category:
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 = {
"job_id": job.id,
"company_id": self.env.company.id,
@@ -1813,12 +1828,17 @@ class HrRecruitmentAutoDocWizard(models.TransientModel):
"requirements": parsed_data.get("requirements"),
"target_from": self._parse_date_value(parsed_data.get("start_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:
create_vals["recruitment_sequence"] = request_id
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):
hr_job_model = self.env["hr.job"]
diff --git a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml
index 5bcf5ced6..83038e1b5 100644
--- a/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml
+++ b/addons_extensions/hr_recruitment_auto_doc/wizard/hr_recruitment_auto_doc_wizard_views.xml
@@ -5,10 +5,10 @@
hr.recruitment.auto.doc.wizard
-
+
+
+
+
@@ -39,7 +39,15 @@
invisible="1"/>
-
+
+
+
+
+
diff --git a/addons_extensions/hr_recruitment_extended/__manifest__.py b/addons_extensions/hr_recruitment_extended/__manifest__.py
index d48cd2c0d..3a39282b4 100644
--- a/addons_extensions/hr_recruitment_extended/__manifest__.py
+++ b/addons_extensions/hr_recruitment_extended/__manifest__.py
@@ -29,6 +29,7 @@
'data/sequence.xml',
'data/mail_template.xml',
'data/templates.xml',
+ 'views/submission_share_history.xml',
'views/job_category.xml',
'views/hr_location.xml',
'views/stages.xml',
diff --git a/addons_extensions/hr_recruitment_extended/data/mail_template.xml b/addons_extensions/hr_recruitment_extended/data/mail_template.xml
index 4cc0162b5..612894f91 100644
--- a/addons_extensions/hr_recruitment_extended/data/mail_template.xml
+++ b/addons_extensions/hr_recruitment_extended/data/mail_template.xml
@@ -14,7 +14,7 @@
+ t-value="', '.join(object.locations.mapped('location_name')) if object.locations else 'N/A'"/>
@@ -669,14 +669,14 @@
+ t-value="', '.join(object.hr_job_recruitment.locations.mapped('location_name'))"/>
-
+
Dear Sir/Madam,
Please find the applicant details below for your review.
@@ -784,10 +784,10 @@
-
+
-
+
Dear Sir/Madam,
Please find the job description and hiring details below for your review and sourcing support.
diff --git a/addons_extensions/hr_recruitment_extended/models/__init__.py b/addons_extensions/hr_recruitment_extended/models/__init__.py
index e87225262..0636edb40 100644
--- a/addons_extensions/hr_recruitment_extended/models/__init__.py
+++ b/addons_extensions/hr_recruitment_extended/models/__init__.py
@@ -1,9 +1,10 @@
+from . import submission_share_history
from . import hr_recruitment
from . import hr_job_recruitment
from . import stages
-from . import applicant_request_forms
-from . import hr_applicant_stage_comment
-from . import hr_applicant
+from . import applicant_request_forms
+from . import hr_applicant_stage_comment
+from . import hr_applicant
from . import hr_job
from . import res_partner
from . import candidate_experience
diff --git a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py
index 4010d4888..75ecf7f44 100644
--- a/addons_extensions/hr_recruitment_extended/models/hr_applicant.py
+++ b/addons_extensions/hr_recruitment_extended/models/hr_applicant.py
@@ -61,6 +61,30 @@ class HRApplicant(models.Model):
)
stage_comment_count = fields.Integer(compute='_compute_stage_comment_count')
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')
def _compute_hold_state(self):
diff --git a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py
index b8b35ba08..6e8ef76ca 100644
--- a/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py
+++ b/addons_extensions/hr_recruitment_extended/models/hr_job_recruitment.py
@@ -175,6 +175,7 @@ class HRJobRecruitment(models.Model):
requested_by = fields.Many2one('res.partner', string="Requested By",
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):
for record in self:
record.hide_chatter_suggestion = not record.hide_chatter_suggestion
@@ -405,6 +406,7 @@ class HRJobRecruitment(models.Model):
'default_template_id': self.env.ref(
'hr_recruitment_extended.job_recruitment_share_email_template'
).id,
+ 'default_is_job_recruitment': True,
},
}
diff --git a/addons_extensions/hr_recruitment_extended/models/submission_share_history.py b/addons_extensions/hr_recruitment_extended/models/submission_share_history.py
new file mode 100644
index 000000000..b22ce3268
--- /dev/null
+++ b/addons_extensions/hr_recruitment_extended/models/submission_share_history.py
@@ -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,
+ )
\ No newline at end of file
diff --git a/addons_extensions/hr_recruitment_extended/security/ir.model.access.csv b/addons_extensions/hr_recruitment_extended/security/ir.model.access.csv
index 7ca70add2..391deaad1 100644
--- a/addons_extensions/hr_recruitment_extended/security/ir.model.access.csv
+++ b/addons_extensions/hr_recruitment_extended/security/ir.model.access.csv
@@ -1,6 +1,7 @@
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_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_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_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_hr_skill,access.hr.skill.user,hr_skills.model_hr_skill,base.group_public,1,0,0,0
\ No newline at end of file
+access_hr_skill,access.hr.skill.user,hr_skills.model_hr_skill,base.group_public,1,0,0,0
+
diff --git a/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml b/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml
index 3528775de..4dcd1632a 100644
--- a/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml
+++ b/addons_extensions/hr_recruitment_extended/views/hr_applicant_views.xml
@@ -90,6 +90,10 @@
+
+
diff --git a/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml b/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml
index 194e84218..2a89d280a 100644
--- a/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml
+++ b/addons_extensions/hr_recruitment_extended/views/hr_job_recruitment.xml
@@ -110,10 +110,10 @@
-
+
-
+
@@ -201,6 +201,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -429,6 +443,7 @@
Job Positions Recruitment
hr.job.recruitment
kanban,list,form
+
@@ -446,6 +461,7 @@
hr.job.recruitment
kanban,list,form,search
+
{"search_default_open_status":1,"search_default_my_assignments":1}
@@ -482,7 +498,7 @@
active="0"
sequence="2"/>
-
@@ -338,6 +338,7 @@
{'search_default_my_candidates': 1,'active_test': False}
+ kanban,list,form,activity
-
+
diff --git a/addons_extensions/hr_recruitment_extended/views/submission_share_history.xml b/addons_extensions/hr_recruitment_extended/views/submission_share_history.xml
new file mode 100644
index 000000000..40fbda714
--- /dev/null
+++ b/addons_extensions/hr_recruitment_extended/views/submission_share_history.xml
@@ -0,0 +1,20 @@
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.py b/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.py
index 84bcc41c7..3a9ecb5cb 100644
--- a/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.py
+++ b/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.py
@@ -1,65 +1,128 @@
from odoo import models, fields, api
from odoo.exceptions import UserError
+
class ClientSubmissionsMailTemplateWizard(models.TransientModel):
_name = 'client.submission.mails.template.wizard'
_description = 'Client Submission Mails Template Wizard'
template_id = fields.Many2one('mail.template', string='Email Template')
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_to = fields.Char('Email To')
email_cc = fields.Text('Email CC')
email_subject = fields.Char()
email_body = fields.Html(
- 'Body', render_engine='qweb', render_options={'post_process': True},
- prefetch=True, translate=True, sanitize='email_outgoing',
+ 'Body',
+ render_engine='qweb',
+ render_options={'post_process': True},
+ prefetch=True,
+ translate=True,
+ sanitize='email_outgoing',
)
@api.onchange('template_id')
def _onchange_template_id(self):
- """ Update the email body and recipients based on the selected template. """
- if self.template_id:
- record_id = self.env.context.get('active_id')
- if record_id:
- record = self.env[self.template_id.model].browse(record_id)
+ if not self.template_id:
+ return
- if not record.exists():
- raise UserError("The record does not exist or is not accessible.")
+ record_id = self.env.context.get('active_id')
+ active_model = self.env.context.get('active_model')
- # Fetch email template
- email_template = self.env['mail.template'].browse(self.template_id.id)
+ if not record_id:
+ return
- if not email_template:
- raise UserError("Email template not found.")
+ record = self.env[active_model].browse(record_id)
- 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
+ self.email_from = record.user_id.partner_id.email
+ 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):
- """ Send email to the selected partners """
+ """Send email and create recruitment share tracker."""
+ self.ensure_one()
+
record_id = self.env.context.get('active_id')
- for rec in self:
- record = self.env[self.template_id.model].browse(record_id)
- if rec.send_email_from_odoo:
- template = self.env.ref('hr_recruitment_extended.application_client_submission_email_template')
- values = {
- 'email_from': rec.email_from,
- 'email_to': rec.email_to,
- 'email_cc': rec.email_cc,
- 'subject' : rec.email_subject,
- 'body_html': rec.email_body,
- }
- render_ctx = dict(client_name=record.hr_job_recruitment.requested_by.name)
- # Use 'with_context' to override the email template fields dynamically
- template.sudo().with_context(**render_ctx).send_mail(self.env.context.get('active_id'),email_values=values, force_send=True)
+ model_name = self.env.context.get('active_model')
+
+ record = self.env[self.template_id.model].browse(record_id)
+
+ tracker_values = {
+ 'date': fields.Datetime.now(),
+ 'is_client_submission': self.is_client_submission,
+ }
+
+ if model_name == 'hr.applicant':
+ tracker_values.update({
+ 'share_type': 'applicant',
+ 'reference': "%s/%s" % (
+ 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({
'submitted_to_client': True,
- 'client_submission_date': rec.submit_date,
+ 'client_submission_date': self.submit_date,
'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'}
\ No newline at end of file
diff --git a/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.xml b/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.xml
index 05d11d1ac..70553c153 100644
--- a/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.xml
+++ b/addons_extensions/hr_recruitment_extended/wizards/client_submission_mail_template_wizard.xml
@@ -9,6 +9,8 @@
+
+
diff --git a/addons_extensions/hr_resignation/__manifest__.py b/addons_extensions/hr_resignation/__manifest__.py
index 8ab0426f3..06b508c07 100644
--- a/addons_extensions/hr_resignation/__manifest__.py
+++ b/addons_extensions/hr_resignation/__manifest__.py
@@ -34,6 +34,7 @@
'data': [
'security/hr_resignation_security.xml',
'security/ir.model.access.csv',
+ 'security/resignation_groups.xml',
'data/data.xml',
'data/ir_sequence_data.xml',
'data/ir_cron_data.xml',
diff --git a/addons_extensions/hr_resignation/models/hr_contract.py b/addons_extensions/hr_resignation/models/hr_contract.py
index 725cfbcf2..f0f5a931b 100644
--- a/addons_extensions/hr_resignation/models/hr_contract.py
+++ b/addons_extensions/hr_resignation/models/hr_contract.py
@@ -33,9 +33,9 @@ class HrContract(models.Model):
"""Get the default notice period from the configuration.
:return: The default notice period in days.
: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[
- 'ir.config_parameter'].get_param(
+ 'ir.config_parameter'].sudo().get_param(
'hr_employee_updation.notice_period') else 0
notice_days = fields.Integer(string="Notice Period",
diff --git a/addons_extensions/hr_resignation/models/hr_resignation.py b/addons_extensions/hr_resignation/models/hr_resignation.py
index cf6e9cbdb..cd39f8e11 100644
--- a/addons_extensions/hr_resignation/models/hr_resignation.py
+++ b/addons_extensions/hr_resignation/models/hr_resignation.py
@@ -167,8 +167,33 @@ class HrResignation(models.Model):
admin_checklist_submitted = fields.Boolean(tracking=True)
hr_checklist_submitted = fields.Boolean(tracking=True)
+ manager_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ it_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ finance_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed') ], compute='_compute_checklist_status')
+ admin_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
+ hr_checklist_status = fields.Selection([('pending', 'Pending'),('completed', 'Completed')], compute='_compute_checklist_status')
relieving_documents = fields.Many2many('ir.attachment')
+ applied_date = fields.Date(string="Applied Date",default=fields.Date.context_today,readonly=True,tracking=True)
+
+ @api.depends('manager_checklist_submitted','it_checklist_submitted','finance_checklist_submitted','admin_checklist_submitted', 'hr_checklist_submitted')
+ def _compute_checklist_status(self):
+ for rec in self:
+ rec.manager_checklist_status = (
+ 'completed' if rec.manager_checklist_submitted else 'pending'
+ )
+ rec.it_checklist_status = (
+ 'completed' if rec.it_checklist_submitted else 'pending'
+ )
+ rec.finance_checklist_status = (
+ 'completed' if rec.finance_checklist_submitted else 'pending'
+ )
+ rec.admin_checklist_status = (
+ 'completed' if rec.admin_checklist_submitted else 'pending'
+ )
+ rec.hr_checklist_status = (
+ 'completed' if rec.hr_checklist_submitted else 'pending'
+ )
@api.depends('employee_id')
def _compute_user_rights(self):
diff --git a/addons_extensions/hr_resignation/security/resignation_groups.xml b/addons_extensions/hr_resignation/security/resignation_groups.xml
new file mode 100644
index 000000000..822086f4b
--- /dev/null
+++ b/addons_extensions/hr_resignation/security/resignation_groups.xml
@@ -0,0 +1,15 @@
+
+
+ Resignation Management
+ 41
+
+
+ Resignation User
+
+
+
+
+ Resignation Manager
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hr_resignation/views/hr_resignation_views.xml b/addons_extensions/hr_resignation/views/hr_resignation_views.xml
index 1363c4746..d1f3a1dc1 100644
--- a/addons_extensions/hr_resignation/views/hr_resignation_views.xml
+++ b/addons_extensions/hr_resignation/views/hr_resignation_views.xml
@@ -150,6 +150,7 @@
+
@@ -173,12 +174,30 @@
+
+
+
+
+
+
+
-
-
-
-
-
+
+
+
+
+
@@ -186,7 +205,8 @@
-
+
@@ -387,7 +407,7 @@
diff --git a/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml b/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
index 6589247d8..00277d65f 100644
--- a/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
+++ b/addons_extensions/hr_resignation/views/pre_resignation_requirements_proceedings.xml
@@ -24,7 +24,7 @@
-
+
@@ -455,81 +787,94 @@ class EmployeeAppraisal(models.Model):
}
self.env['mail.mail'].create(mail_values).send()
- def check_colleague_feedback_deadline(self):
- now = fields.Datetime.now()
- records = self.search([
- ('college_end_date_time', '!=', False),
- ('college_end_date_time', '<=', now),
- ('state', '=', 'colleague_feedback')
- ])
- for rec in records:
- rec.write({
- 'state': 'in_progress'
- })
- rec.message_post(
- body=_(
- "Colleague feedback deadline completed automatically. "
- "Stage moved to Manager Evaluation."
- )
- )
- rec._send_manager_notification_mail()
- return True
+ # def check_colleague_feedback_deadline(self):
+ # now = fields.Datetime.now()
+ # records = self.search([
+ # ('college_end_date_time', '!=', False),
+ # ('college_end_date_time', '<=', now),
+ # ('state', '=', 'colleague_feedback')
+ # ])
+ # for rec in records:
+ # rec.write({
+ # 'state': 'in_progress'
+ # })
+ # rec.message_post(
+ # body=_(
+ # "Colleague feedback deadline completed automatically. "
+ # "Stage moved to Manager Evaluation."
+ # )
+ # )
+ # rec._send_manager_notification_mail()
+ # return True
def action_sent_employee_appraisal(self):
self.ensure_one()
+ if not self.manager_remarks:
+ raise ValidationError("Please give the Manager Remarks")
+
+ manager_email = self.managerapp_id.work_email or ''
employee_email = self.employee_appraisal_id.work_email or ''
creator_email = self.creator_email or ''
+
emails = ",".join(
- filter(None, [employee_email, creator_email])
+ filter(None, [
+ manager_email,
+ employee_email,
+ creator_email
+ ])
)
+
body_html = f"""
-
-
Hello,Team
-
- Your appraisal evaluation has been initiated.
-
-
-
-
- |
- Employee
- |
+
+
Hello Team,
-
- {self.employee_appraisal_id.name or ''}
- |
-
-
- |
- Template
- |
+
+ The employee has completed the self-assessment as part of the performance appraisal process.
+ Your feedback and evaluation are now requested.
+
-
- {self.template_id.name or ''}
- |
-
-
- |
- Performance Period
- |
+
-
- {self.appraisal_period_id.appraisal_type_id.id or ''}
- |
-
-
-
-
- Please complete the self evaluation before deadline.
-
-
-
- Regards,
-
-
- HR Team
-
-
+
+
+ | Employee |
+ {self.employee_appraisal_id.name or ''} |
+
+
+ | Department |
+ {self.department_appraisal_id.name or ''} |
+
+
+ | Appraisal Template |
+ {self.template_id.name or ''} |
+
+
+ | Performance Period |
+ {self.appraisal_period_id.appraisal_name or ''} |
+
+
+
+
+
+
+ Kindly review the employee's self-assessment and provide your feedback,
+ evaluation, and recommendations within the appraisal timeline.
+
+
+
+ Your valuable input will contribute to the employee's overall performance review.
+
+
+
+
+
+ Regards,
+
+
+
+ Manager Team
+
+
"""
ctx = {
'default_model': 'employee.appraisal.template.config',
@@ -549,17 +894,132 @@ class EmployeeAppraisal(models.Model):
'context': ctx,
}
- def action_confirm(self):
- for rec in self:
- rec.state = 'self_evaluation'
-
- def action_confirm_manager(self):
- for rec in self:
- rec.state = 'hr_evaluation'
-
def action_confirm_hr(self):
- for rec in self:
- rec.state = 'finance_team'
+ self.ensure_one()
+
+ if not self.hr_remarks:
+ raise ValidationError ('Please Provide the Remarks')
+
+ email_to = self.managerapp_id.work_email or ''
+
+ body_html = f"""
+
+
Hello,
+
+
+ HR evaluation has been completed for the appraisal of
+ {self.employee_appraisal_id.name}.
+
+
+
+ Kindly review the appraisal and take the necessary action.
+
+
+
+
+
+
+ | Employee |
+ {self.employee_appraisal_id.name or ''} |
+
+
+ | Department |
+ {self.department_appraisal_id.name or ''} |
+
+
+ | Template |
+ {self.template_id.name or ''} |
+
+
+
+
+
+
Regards,
+
HR Team
+
+
+ """
+
+ ctx = {
+ 'default_model': 'employee.appraisal.template.config',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': email_to,
+ 'default_subject': f'HR Evaluation Completed - {self.employee_appraisal_id.name}',
+ 'default_body': body_html,
+ 'move_hr_next_stage': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
+
+ def action_head_hr(self):
+ self.ensure_one()
+
+ if not self.hr_head_remarks:
+ raise ValidationError(
+ _('Please provide HR Head Remarks.')
+ )
+
+ email_to = self.created_by_id.work_email or ''
+
+ body_html = f"""
+
+
Hello,
+
+
+ HR Head review has been completed for the appraisal of
+ {self.employee_appraisal_id.name}.
+
+
+
+ Kindly proceed with the next level approval.
+
+
+
+
+ | Employee |
+ {self.employee_appraisal_id.name or ''} |
+
+
+ | Department |
+ {self.department_appraisal_id.name or ''} |
+
+
+ | HR Head Remarks |
+ {self.hr_head_remarks or ''} |
+
+
+
+
+
+
Regards,
+
HR Head
+
+ """
+
+ ctx = {
+ 'default_model': 'employee.appraisal.template.config',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': email_to,
+ 'default_subject': f'HR Head Approval - {self.employee_appraisal_id.name}',
+ 'default_body': body_html,
+ 'move_hr_head_next_stage': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
def action_send_colleague_feedback(self):
for rec in self:
@@ -568,6 +1028,7 @@ class EmployeeAppraisal(models.Model):
('id', '!=', rec.employee_appraisal_id.id)
])
vals = []
+ email_list = []
for emp in employees:
already_exists = self.env['colleague.feedback'].search([
('employee_appraisal_feed_id', '=', rec.id),
@@ -578,7 +1039,123 @@ class EmployeeAppraisal(models.Model):
'colleague_feed_id': emp.id,
}))
rec.colleague_feed_ids = vals
- rec.state = 'colleague_manager'
+ if self.managerapp_id and self.managerapp_id.work_email:
+ email_list.append(self.managerapp_id.work_email)
+ email_to = ",".join(filter(None, email_list))
+
+ body_html = f"""
+
+
Hello Team,
+
+
+ {self.employee_appraisal_id.name} has completed the self-assessment.
+ Please provide your feedback as part of the appraisal process.
+
+
+
+ Your feedback will help in evaluating the employee's overall performance.
+
+
+
+
+
Regards,
+
{self.employee_appraisal_id.name}
+
+ """
+
+ ctx = {
+ 'default_model': 'employee.appraisal.template.config',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': email_to,
+ 'default_subject': f'Colleague Feedback Request - {self.employee_appraisal_id.name}',
+ 'default_body': body_html,
+ 'mark_colleague_feedback_sent': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
+
+ def action_initiate_pip(self):
+ self.ensure_one()
+ self.write({'invite_pip': True})
+
+ employee_email = self.employee_appraisal_id.work_email or ''
+
+ body_html = f"""
+
+
Dear {self.employee_appraisal_id.name},
+
+
+ Based on the recent performance appraisal review, your overall performance
+ rating indicates that improvement is required in certain areas.
+
+
+
+ Therefore, you are requested to attend a Performance Improvement Plan (PIP)
+ discussion meeting with Management and HR.
+
+
+
+
+ | Employee |
+ {self.employee_appraisal_id.name or ''} |
+
+
+ | Department |
+ {self.department_appraisal_id.name or ''} |
+
+
+ | Performance Period |
+ {self.appraisal_period_id.appraisal_name or ''} |
+
+
+ | Overall Rating |
+ {self.overall_rating or ''} |
+
+
+
+
+
+
+ During this meeting, we will discuss performance concerns,
+ expectations, improvement objectives, and the Performance
+ Improvement Plan (PIP) timeline.
+
+
+
+ Kindly acknowledge and attend the meeting as scheduled.
+
+
+
+
+
Regards,
+
Management Team
+
+ """
+
+ ctx = {
+ 'default_model': 'employee.appraisal.template.config',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': employee_email,
+ 'default_subject': 'Performance Improvement Plan (PIP) Meeting Notification',
+ 'default_body': body_html,
+ 'mark_invite_pip': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
@api.onchange('template_id')
def _onchange_template_id(self):
@@ -624,7 +1201,6 @@ class ColleagueFeedBack(models.Model):
submitted_date = fields.Datetime()
def action_submit_feedback(self):
-
for rec in self:
rec.write({
'state': 'submitted',
diff --git a/addons_extensions/hrms_employee_appraisal/models/employee_pip.py b/addons_extensions/hrms_employee_appraisal/models/employee_pip.py
new file mode 100644
index 000000000..905d9c21a
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/employee_pip.py
@@ -0,0 +1,89 @@
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+
+
+class EmployeePIP(models.Model):
+ _name = 'employee.pip'
+ _description = 'Performance Improvement Plan'
+ _inherit = ['mail.thread', 'mail.activity.mixin']
+ _rec_name = 'employee_id'
+
+ name = fields.Char(string="PIP Reference",default=lambda self: _('New'),readonly=True )
+ employee_id = fields.Many2one( 'hr.employee',required=True)
+ manager_id = fields.Many2one('hr.employee',string="Manager")
+ appraisal_id = fields.Many2one('employee.appraisal.template.config',string="Appraisal")
+ objective = fields.Text(string="Improvement Objective",required=True)
+ timeline = fields.Selection([
+ ('30', '30 Days'),
+ ('60', '60 Days'),
+ ('90', '90 Days')
+ ], default='30', required=True)
+ start_date = fields.Date(default=fields.Date.today)
+ end_date = fields.Date()
+ review_date = fields.Date()
+ employee_acknowledged = fields.Boolean(string="Employee Acknowledged")
+ state = fields.Selection([
+ ('draft', 'Draft'),
+ ('running', 'In Progress'),
+ ('review', 'Under Review'),
+ ('completed', 'Completed'),
+ ('failed', 'Failed')
+ ], default='draft', tracking=True)
+ task_ids = fields.One2many('employee.pip.task','pip_id',string='Improvement Tasks')
+ progress_percentage = fields.Float(compute='_compute_progress',store=True)
+ remarks = fields.Text()
+
+ @api.depends('task_ids.state')
+ def _compute_progress(self):
+ for rec in self:
+
+ total = len(rec.task_ids)
+
+ completed = len(
+ rec.task_ids.filtered(
+ lambda l: l.state == 'done'
+ )
+ )
+
+ rec.progress_percentage = (
+ (completed / total) * 100
+ ) if total else 0
+
+ @api.onchange('timeline', 'start_date')
+ def _onchange_timeline(self):
+ for rec in self:
+ if rec.start_date and rec.timeline:
+ rec.end_date = fields.Date.add(
+ rec.start_date,
+ days=int(rec.timeline)
+ )
+
+ def action_start(self):
+ self.state = 'running'
+
+ def action_review(self):
+ self.state = 'review'
+
+ def action_complete(self):
+ self.state = 'completed'
+
+ def action_fail(self):
+ self.state = 'failed'
+
+
+
+
+class EmployeePIPTask(models.Model):
+ _name = 'employee.pip.task'
+ _description = 'PIP Task'
+
+ pip_id = fields.Many2one('employee.pip',ondelete='cascade')
+ name = fields.Char(required=True)
+ description = fields.Text()
+ target_date = fields.Date()
+ training_course = fields.Char(string="Suggested Training")
+ state = fields.Selection([
+ ('pending', 'Pending'),
+ ('progress', 'In Progress'),
+ ('done', 'Completed')
+ ], default='pending')
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py b/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py
new file mode 100644
index 000000000..67c2c2712
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/hr_head_nofication.py
@@ -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"""
+
+
Hello HR Team,
+
+
{self.body or ''}
+
+
+
+ | Appraisal Type |
+ {self.appraisal_type_id.name or ''} |
+
+
+ | Appraisal Period |
+ {self.appraisal_period_id.appraisal_name or ''} |
+
+
+ | Start Date |
+ {self.start_date or ''} |
+
+
+ | End Date |
+ {self.end_date or ''} |
+
+
+
+
+
+
Please initiate the appraisal process.
+
+
Regards,
+
HR Head
+
+ """
+
+ ctx = {
+ 'default_model': 'hr.head.notification',
+ 'default_res_ids': [self.id],
+ 'default_composition_mode': 'comment',
+ 'default_email_to': email_to,
+ 'default_subject': self.name,
+ 'default_body': body_html,
+ 'mark_hr_notification_sent': True,
+ }
+
+ return {
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'mail.compose.message',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': ctx,
+ }
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py b/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
index 4432a9b6c..667ba5f68 100644
--- a/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
+++ b/addons_extensions/hrms_employee_appraisal/models/hr_notice_appraisal.py
@@ -2,6 +2,8 @@ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError
from random import randint
+import logging
+_logger = logging.getLogger(__name__)
class HrNoticeAppraisal(models.Model):
_name = 'hr.notice.appraisal'
@@ -15,10 +17,11 @@ class HrNoticeAppraisal(models.Model):
return self.env.user.employee_id
hr_employee_id = fields.Many2one('hr.employee', string='Employee', default=_default_employee_get)
+ notification_id = fields.Many2one('hr.head.notification','HR')
subject = fields.Char(string="Subject", required=True, tracking=True)
body = fields.Html(string="Notice Body", required=True)
- start_date = fields.Datetime(string="Start Date", required=True)
- end_date = fields.Datetime(string="End Date", required=True)
+ start_date = fields.Date(string="Start Date")
+ end_date = fields.Date(string="End Date")
employee_ids = fields.Many2many('hr.employee', 'notice_employee_rel', 'notice_id', 'employee_id',string="Employees")
manager_ids = fields.Many2many('hr.employee', 'notice_manager_rel', 'notice_id', 'manager_id', string="Managers")
state = fields.Selection([
@@ -44,37 +47,83 @@ class HrNoticeAppraisal(models.Model):
new_end_date = fields.Datetime(string="New End Date")
stage_config = fields.Many2many('employee.stage.config',string='Stages')
hr_department_ids = fields.Many2many('hr.department', string="Departments")
+ image_1920 = fields.Image(related='hr_employee_id.image_1920',string='Employee Image')
- @api.model
- def create(self, vals):
- if vals.get('seq', 'New') == 'New':
- company = self.env.company
- company_code = company.short_code or 'CMP'
- today = fields.Datetime.now()
- month = str(today.month).zfill(2)
- year = str(today.year)[-2:]
- prefix = f"{company_code}/{month}/{year}"
- last_record = self.search([
- ('seq', '=like', f'{prefix}%')
- ], order='id desc', limit=1)
- number = 1
- if last_record and last_record.seq:
- try:
- number = int(
- last_record.seq.split('/')[-1]
- ) + 1
- except Exception:
- number = 1
- vals['seq'] = (
- f"{prefix}/{str(number).zfill(3)}"
- )
- return super().create(vals)
+ # @api.model
+ # def create(self, vals):
+ # if vals.get('seq', 'New') == 'New':
+ # company = self.env.company
+ # company_code = company.short_code or 'CMP'
+ # today = fields.Datetime.now()
+ # month = str(today.month).zfill(2)
+ # year = str(today.year)[-2:]
+ # prefix = f"{company_code}/{month}/{year}"
+ # last_record = self.search([
+ # ('seq', '=like', f'{prefix}%')
+ # ], order='id desc', limit=1)
+ # number = 1
+ # if last_record and last_record.seq:
+ # try:
+ # number = int(
+ # last_record.seq.split('/')[-1]
+ # ) + 1
+ # except Exception:
+ # number = 1
+ # vals['seq'] = (
+ # f"{prefix}/{str(number).zfill(3)}"
+ # )
+ # return super().create(vals)
- @api.constrains('start_date', 'end_date')
- def _check_dates(self):
+ @api.constrains('start_date', 'end_date','employee_ids','manager_ids','appraisal_notice_id','state')
+ def _check_appraisal_validations(self):
for rec in self:
- if rec.end_date < rec.start_date:
- raise ValidationError(_("End Date must be greater than Start Date."))
+ if rec.start_date and rec.end_date:
+ if rec.end_date <= rec.start_date:
+ raise ValidationError(
+ _("End Date must be greater than Start Date.")
+ )
+ if rec.state == 'cancelled':
+ continue
+ duplicate_period = self.search([
+ ('id', '!=', rec.id),
+ ('appraisal_notice_id', '=', rec.appraisal_notice_id.id),
+ ('hr_employee_id', '=', rec.hr_employee_id.id),
+ ('state', '!=', 'cancelled'),
+ ], limit=1)
+ if duplicate_period:
+ raise ValidationError(_(
+ "An appraisal notification already exists for appraisal period '%s'."
+ ) % rec.appraisal_notice_id.display_name)
+
+ for employee in rec.employee_ids:
+ duplicate_employee = self.search([
+ ('id', '!=', rec.id),
+ ('employee_ids', 'in', employee.id),
+ ('state', '!=', 'cancelled'),
+ ('start_date', '<=', rec.end_date),
+ ('end_date', '>=', rec.start_date),
+ ], limit=1)
+ _logger.info(
+ "Duplicate Period Records: %s",
+ duplicate_period.ids
+ )
+ if duplicate_employee:
+ raise ValidationError(_(
+ "Employee '%s' is already assigned in another appraisal notification for the selected period."
+ ) % employee.name)
+
+ for manager in rec.manager_ids:
+ duplicate_manager = self.search([
+ ('id', '!=', rec.id),
+ ('manager_ids', 'in', manager.id),
+ ('state', '!=', 'cancelled'),
+ ('start_date', '<=', rec.end_date),
+ ('end_date', '>=', rec.start_date),
+ ], limit=1)
+ if duplicate_manager:
+ raise ValidationError(_(
+ "Manager '%s' is already assigned in another appraisal notification for the selected period."
+ ) % manager.name)
@api.onchange('employee_ids')
def _onchange_employee_ids(self):
@@ -89,45 +138,6 @@ class HrNoticeAppraisal(models.Model):
manager_emails = self.manager_ids.mapped('work_email')
all_emails = employee_emails + manager_emails
email_to = ",".join(filter(None, all_emails))
- template_obj = self.env['employee.appraisal.template']
- grouped_employees = {}
- for employee in self.employee_ids:
- manager = employee.parent_id
- department = employee.department_id
- key = (
- manager.id if manager else False,
- department.id if department else False
- )
- if key not in grouped_employees:
- grouped_employees[key] = self.env['hr.employee']
- grouped_employees[key] |= employee
- for (manager_id, department_id), employees in grouped_employees.items():
- already_exists = template_obj.search([
- ('notice_id', '=', self.id),
- # ('employee_eva_id', '=', manager_id),
- ('employee_department_id', '=', department_id),
- ], limit=1)
- if already_exists:
- continue
- template_obj.create({
- 'name': self.subject,
- 'seq': self.seq,
- 'employee_eva_id': manager_id,
- 'employee_department_id': department_id,
- 'employee_ids': [(6, 0, employees.ids)],
- 'manager_ids': [(6, 0, [manager_id])] if manager_id else [(5, 0, 0)],
- 'notice_id': self.id,
- 'start_date': self.start_date,
- 'end_date': self.end_date,
- 'appraisal_period_id': self.appraisal_notice_id.id,
- 'appraisal_period_type_id': self.appraisal_type_id.id,
- 'template_rating_bool': self.employee_rating,
- 'template_point_bool': self.employee_points,
- 'hr_employee_id': self.hr_employee_id.id,
- 'stage_config_ids': [(6, 0, self.stage_config.ids)],
- })
- # print('1234',template_obj.create({}))
-
body_html = f"""
Hello,
@@ -226,6 +236,7 @@ class StageConfig(models.Model):
name = fields.Char(required=True)
seq = fields.Integer(required=True)
+ colour_seq = fields.Integer(required=True)
active = fields.Boolean(default=True)
color = fields.Integer('Color', default=_get_default_color_stage)
@@ -240,26 +251,157 @@ class MailComposeMessage(models.TransientModel):
model = self.env.context.get('default_model')
res_ids = self.env.context.get('default_res_ids')
if self.env.context.get('mark_notice_sent'):
-
if model == 'hr.notice.appraisal' and res_ids:
records = self.env[model].browse(res_ids)
-
- records.write({
- 'state': 'sent'
- })
- if self.env.context.get('mark_appraisal_sent'):
+ template_obj = self.env['employee.appraisal.template']
+ for rec in records:
+ grouped_employees = {}
+ for employee in rec.employee_ids:
+ manager = employee.parent_id
+ department = employee.department_id
+ key = (
+ manager.id if manager else False,
+ department.id if department else False
+ )
+ if key not in grouped_employees:
+ grouped_employees[key] = self.env['hr.employee']
+ grouped_employees[key] |= employee
+ for (manager_id, department_id), employees in grouped_employees.items():
+ already_exists = template_obj.search([
+ ('notice_id', '=', rec.id),
+ ('employee_department_id', '=', department_id),
+ ], limit=1)
+ if already_exists:
+ continue
+ template_obj.create({
+ 'name': rec.subject,
+ 'seq': rec.seq,
+ 'employee_eva_id': manager_id,
+ 'employee_department_id': department_id,
+ 'employee_ids': [(6, 0, employees.ids)],
+ 'manager_ids': [(6, 0, [manager_id])] if manager_id else [(5, 0, 0)],
+ 'notice_id': rec.id,
+ 'start_date': rec.start_date,
+ 'end_date': rec.end_date,
+ 'appraisal_period_id': rec.appraisal_notice_id.id,
+ 'appraisal_period_type_id': rec.appraisal_type_id.id,
+ 'template_rating_bool': rec.employee_rating,
+ 'template_point_bool': rec.employee_points,
+ 'hr_employee_id': rec.hr_employee_id.id,
+ 'stage_config_ids': [(6, 0, rec.stage_config.ids)],
+ })
+ rec.write({
+ 'state': 'sent'
+ })
+ if (
+ self.env.context.get('mark_appraisal_sent')
+ or self.env.context.get('mark_colleague_feedback_sent')
+ or self.env.context.get('move_hr_next_stage')
+ or self.env.context.get('move_hr_head_next_stage')
+ or self.env.context.get('mark_finance_approved')
+ or self.env.context.get('mark_finance_head_approved')
+ or self.env.context.get('mark_invite_pip')
+ ):
if model == 'employee.appraisal.template.config' and res_ids:
records = self.env[model].browse(res_ids)
for record in records:
record._move_to_next_stage()
+
+ # if self.env.context.get('mark_appraisal_sent'):
+ # if model == 'employee.appraisal.template.config' and res_ids:
+ # records = self.env[model].browse(res_ids)
+ # for record in records:
+ # record._move_to_next_stage()
+ #
+ # if self.env.context.get('mark_colleague_feedback_sent'):
+ # if model == 'employee.appraisal.template.config' and res_ids:
+ # records = self.env[model].browse(res_ids)
+ # for record in records:
+ # record._move_to_next_stage()
+
if self.env.context.get('mark_appraisal_sent_appraisal'):
-
if model == 'employee.appraisal.template' and res_ids:
- records = self.env[model].browse(res_ids)
- records.write({
- 'employee_state': 'sent'
+ templates = self.env[model].browse(res_ids)
+ appraisal_config_obj = self.env[
+ 'employee.appraisal.template.config'
+ ]
+ for rec in templates:
+ first_stage = rec.stage_config_ids.sorted(
+ key=lambda s: s.seq
+ )[:1]
+ for employee in rec.employee_ids:
+ already_exists = appraisal_config_obj.search([
+ ('template_id', '=', rec.id),
+ ('employee_appraisal_id', '=', employee.id)
+ ], limit=1)
+ if already_exists:
+ continue
+ appraisal = appraisal_config_obj.create({
+ 'template_id': rec.id,
+ 'seq': rec.seq,
+ 'employee_appraisal_id': employee.id,
+ 'employee_ids': [(6, 0, rec.employee_ids.ids)],
+ 'manager_ids': [(6, 0, rec.manager_ids.ids)],
+ 'notice_id': rec.notice_id.id,
+ 'start_date': rec.start_date,
+ 'end_date': rec.end_date,
+ 'appraisal_period_id': rec.appraisal_period_id.id,
+ 'hr_apprai_id': rec.hr_employee_id.id,
+ 'managerapp_id': rec.employee_eva_id.id,
+ 'template_empl_rating_bool': rec.template_rating_bool,
+ 'template_empl_point_bool': rec.template_point_bool,
+ 'available_stage_ids': [
+ (6, 0, rec.stage_config_ids.ids)
+ ],
+ 'stage_id': first_stage.id if first_stage else False,
+ })
+
+ appraisal._onchange_template_id()
+
+ rec.write({
+ 'employee_state': 'sent'
+ })
+
+ if self.env.context.get('mark_hr_notification_sent'):
+ records = self.env[model].browse(res_ids)
+ for record in records:
+ for user in record.hr_users_ids:
+ employee = self.env['hr.employee'].search([
+ ('user_id', '=', user.id)
+ ], limit=1)
+ if not employee:
+ continue
+ already_exists = self.env[
+ 'hr.notice.appraisal'
+ ].search([
+ ('notification_id', '=', record.id),
+ ('hr_employee_id', '=', employee.id)
+ ], limit=1)
+ if already_exists:
+ continue
+ self.env['hr.notice.appraisal'].create({
+ 'notification_id': record.id,
+ 'hr_employee_id': employee.id,
+ 'subject': record.name,
+ 'appraisal_type_id':
+ record.appraisal_type_id.id,
+ 'appraisal_notice_id':
+ record.appraisal_period_id.id,
+ 'start_date': record.start_date,
+ 'end_date': record.end_date,
+ 'seq': record.seq,
+ 'body': record.body,
+ 'stage_config': [
+ (6, 0,
+ record.stage_config_ids.ids)
+ ],
+ })
+
+ record.write({
+ 'state': 'sent'
})
- return res
+
+ return res
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py b/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
index 20edde453..aaa370f3a 100644
--- a/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
+++ b/addons_extensions/hrms_employee_appraisal/models/kpi_kra.py
@@ -373,6 +373,21 @@ class EmployeeAppraisalKPILine(models.Model):
('4', '4'),
('5', '5'),
], string="Stars", copy=False)
+ is_employee_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+ is_manager_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+ is_hr_reviewer = fields.Boolean(compute="_compute_user_roles", store=False)
+
+ def _compute_user_roles(self):
+ current_user = self.env.user
+ for rec in self:
+ rec.is_employee_reviewer = (
+ rec.kra_line_id.config_id.employee_appraisal_id.user_id.id == current_user.id)
+ rec.is_manager_reviewer= (
+ rec.kra_line_id.config_id.managerapp_id.user_id.id == current_user.id)
+ rec.is_hr_reviewer= (
+ rec.kra_line_id.config_id.hr_apprai_id.user_id.id == current_user.id
+ )
+
# self_rating = fields.Selection([
# ('0', '0'),
# ('1', '1'),
diff --git a/addons_extensions/hrms_employee_appraisal/models/setting_config.py b/addons_extensions/hrms_employee_appraisal/models/setting_config.py
new file mode 100644
index 000000000..4d7075129
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/models/setting_config.py
@@ -0,0 +1,76 @@
+from odoo import api, fields, models
+from datetime import date
+
+class ResConfigSettings(models.TransientModel):
+ _inherit = 'res.config.settings'
+
+
+ appraisal_reminder_days = fields.Integer(
+ string="Appraisal Reminder Before (Days)",
+ config_parameter='hrms_employee_appraisal.appraisal_reminder_days',
+ default=7
+ )
+
+ appraisal_reminder_enabled = fields.Boolean(
+ string="Enable Appraisal Reminders",
+ config_parameter='hrms_employee_appraisal.appraisal_reminder_enabled',
+ default=True
+ )
+
+
+
+
+
+
+class EmployeeAppraisal(models.Model):
+ _inherit = 'employee.appraisal.template.config'
+
+ def cron_send_appraisal_reminder(self):
+
+ enabled = self.env['ir.config_parameter'].sudo().get_param(
+ 'hrms_employee_appraisal.appraisal_reminder_enabled'
+ )
+
+ if not enabled:
+ return
+
+ reminder_days = int(
+ self.env['ir.config_parameter'].sudo().get_param(
+ 'hrms_employee_appraisal.appraisal_reminder_days',
+ 7
+ )
+ )
+
+ today = date.today()
+
+ records = self.search([
+ ('end_date', '!=', False)
+ ])
+
+ for rec in records:
+
+ days_left = (rec.end_date - today).days
+
+ if days_left == reminder_days:
+
+ if rec.employee_appraisal_id.work_email:
+
+ self.env['mail.mail'].sudo().create({
+ 'subject': 'Performance Appraisal Reminder',
+ 'email_to': rec.employee_appraisal_id.work_email,
+ 'body_html': f"""
+
Dear {rec.employee_appraisal_id.name},
+
+
+ Your appraisal period is ending in
+ {reminder_days} days.
+
+
+
+ Please complete your self appraisal.
+
+
+
+
Regards,
HR Team
+ """
+ }).send()
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv b/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
index 247be6304..f6abb9a48 100644
--- a/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
+++ b/addons_extensions/hrms_employee_appraisal/security/ir.model.access.csv
@@ -23,7 +23,14 @@ access_appraisal_postpone_wizard,appraisal_postpone_wizard,model_appraisal_postp
access_appraisal_cancel_wizard,appraisal.cancel.wizard,model_appraisal_cancel_wizard,base.group_user,1,1,1,1
+access_hr_head_notification,hr.head.notification,model_hr_head_notification,base.group_user,1,1,1,1
+
+
access_employee_stage_config,employee.stage.config,model_employee_stage_config,base.group_user,1,1,1,1
+access_employee_pip,employee.pip,model_employee_pip,base.group_user,1,1,1,1
+access_employee_pip_task,employee.pip.task,model_employee_pip_task,base.group_user,1,1,1,1
+
+
diff --git a/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml b/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml
new file mode 100644
index 000000000..fa17c46bc
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/security/performace_record_rules.xml
@@ -0,0 +1,111 @@
+
+
+
+ HR Notice - HR Access
+
+
+ [('hr_employee_id.user_id', '=', user.id)]
+
+
+
+
+
+ HR Notice - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Appraisal Template Manager
+
+
+ [('employee_eva_id.user_id', '=', user.id)]
+
+
+
+
+
+ Appraisal Template HR
+
+
+ [('hr_employee_id.user_id', '=', user.id)]
+
+
+
+
+ Appraisal Template - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - employee access
+
+
+ [('employee_appraisal_id.user_id','=', user.id)]
+
+
+
+
+
+ Manager Access
+
+
+ [('managerapp_id.user_id','=',user.id)]
+
+
+
+
+
+
+
+
+
+ Employee Appraisal - HR Access
+
+
+ [('hr_apprai_id.user_id', '=', user.id)]
+
+
+
+
+
+ Employee Appraisal - HR Head Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Finance Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Finance Head Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+ Employee Appraisal - Management Access
+
+ [(1,'=',1)]
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/security/security_groups.xml b/addons_extensions/hrms_employee_appraisal/security/security_groups.xml
new file mode 100644
index 000000000..d9fdcda58
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/security/security_groups.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ Performance Management
+ 40
+
+
+
+ Employee
+
+
+
+
+ Appraisal Manager
+
+
+
+
+ Appraisal HR
+
+
+
+
+ Appraisal HR Head
+
+
+
+
+ Appraisal Finance
+
+
+
+
+ Appraisal Finance Head
+
+
+
+
+ Appraisal Management
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
index 7e6ce572a..9d75ce7d2 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_appraisal.xml
@@ -13,10 +13,31 @@
-
+
+
+
+
+ employee.app.search
+ employee.appraisal.template.config
+
+
+
+
+
+
+
+
+
+
+
employee.appraisal.template.config.form
employee.appraisal.template.config
@@ -32,23 +53,42 @@
domain="[('id', 'in', available_stage_ids)]"/>
-
+ groups="hrms_employee_appraisal.group_appraisal_manager"
+ class="btn-primary" invisible="stage_name != 'COLLEAGUES&MANAGER'"/>
-
-
-
+ type="object"
+ class="btn-primary"
+ invisible="stage_name != 'NEW' or not is_current_employee"/>
+
+
+
+
+ groups="hrms_employee_appraisal.group_appraisal_hr"
+ class="btn-primary" invisible="stage_name != 'HR'"/>
+
+ type="object" class="btn-primary" invisible="stage_name != 'FINANCE'"/>
+
+
+
@@ -60,21 +100,38 @@
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
+
+
+
@@ -146,27 +206,64 @@
-
-
+
+
+
-
+
+
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -174,47 +271,35 @@
-
-
+
+
+
-
-
+
-
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
@@ -228,6 +313,7 @@
Employee Performance Review
employee.appraisal.template.config
list,form
+
@@ -241,7 +327,7 @@
name="Employee Performance Review"
parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template"
- sequence="03"/>
+ sequence="04"/>
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml b/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
index af47e91bb..87ec9322d 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_evalutor.xml
@@ -2,8 +2,23 @@
+ sequence="06"/>
+
+ Settings
+ res.config.settings
+ form
+ inline
+ {'module':'hrms_employee_appraisal'}
+
+
+
+
employee.appraisal.year.form
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml b/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml
new file mode 100644
index 000000000..1906e30b6
--- /dev/null
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_pip.xml
@@ -0,0 +1,84 @@
+
+
+
+ employee.pip.form
+ employee.pip
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ employee.pip.tree
+ employee.pip
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Performance Improvement Plans
+ employee.pip
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
index fca95d041..92c0b25f8 100644
--- a/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/employee_template_appraisal.xml
@@ -10,21 +10,27 @@
type="object"
string="Send To Employee"
class="oe_highlight"
+ groups="hrms_employee_appraisal.group_appraisal_manager,hrms_employee_appraisal.group_appraisal_management"
invisible="employee_state != 'new'"/>
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
@@ -33,8 +39,8 @@
-
-
+
+
@@ -48,8 +54,10 @@
-
-
+
+
-
+
@@ -96,9 +107,9 @@
name="Employee Appraisal Templates"
parent="menu_employee_appraisal_root"
action="action_employee_appraisal_template_conf"
- sequence="02"/>
+ groups="group_appraisal_management,group_appraisal_finance_head,group_appraisal_finance,group_appraisal_hr_head,group_appraisal_hr,group_appraisal_manager"
+ sequence="03"/>
-
employee.appraisal.kpi.list
@@ -119,8 +130,6 @@
-
-
employee.appraisal.kpi.form
employee.appraisal.kpi
@@ -132,22 +141,8 @@
diff --git a/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml b/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
index 8751ab333..df58eeb9e 100644
--- a/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/hr_notice_appraisal.xml
@@ -6,7 +6,9 @@
+ class="btn btn-success"
+ groups="hrms_employee_appraisal.group_appraisal_hr,hrms_employee_appraisal.group_appraisal_management"
+ invisible="state != 'draft'"/>
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
+
@@ -53,7 +75,7 @@
-
+
@@ -74,6 +96,7 @@
+
@@ -97,6 +120,105 @@
name="Performance Cycle Notification"
parent="menu_employee_appraisal_root"
action="action_hr_notice_appraisal"
+ groups="group_appraisal_hr,group_appraisal_hr_head,group_appraisal_management"
+ sequence="02"/>
+
+
+ hr.head.notification.form
+ hr.head.notification
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.head.notification.tree
+ hr.head.notification
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Appraisal Notifications
+ hr.head.notification
+ list,form
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/hrms_employee_appraisal/views/stage_config.xml b/addons_extensions/hrms_employee_appraisal/views/stage_config.xml
index edb64a9f9..45431030e 100644
--- a/addons_extensions/hrms_employee_appraisal/views/stage_config.xml
+++ b/addons_extensions/hrms_employee_appraisal/views/stage_config.xml
@@ -9,6 +9,7 @@
+
@@ -20,7 +21,7 @@
employee.stage.config
-
+
@@ -36,5 +37,43 @@
parent="configuration_id_employee"
action="action_employee_stage_confr"
sequence="20"/>
+
+
+
+ res.config.settings.appraisal.form
+ res.config.settings
+
+
+
+
+
+
+
+
+ Reminder email will be sent before appraisal end date.
+
+
+
+
+
+
+
+
+
+ Appraisal Settings
+ res.config.settings
+ form
+ inline
+ {'module': 'hrms_employee_appraisal'}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/addons_extensions/offer_letters/data/mail_template.xml b/addons_extensions/offer_letters/data/mail_template.xml
index dcfd9eed6..958432a3e 100644
--- a/addons_extensions/offer_letters/data/mail_template.xml
+++ b/addons_extensions/offer_letters/data/mail_template.xml
@@ -5,14 +5,14 @@
Applicant Offer Email Template
{{ user.email_formatted }}
- {{ object.candidate_id.email_from or '' }}
- Offer Letter - {{ object.position or object.candidate_id.job_id.name or '' }}
+ {{ object.main_candidate_id.email_from or '' }}
+ Offer Letter - {{ object.position or object.main_candidate_id.job_id.name or '' }}
Send applicant offer mail with offer letter attachment.
-
Dear ,
+
Dear ,
With reference to the interview and subsequent discussions you had with us, we are pleased to select
you for the position of "" in our organization with the following
diff --git a/addons_extensions/offer_letters/models/offer_letter.py b/addons_extensions/offer_letters/models/offer_letter.py
index 25287fccb..7749a3520 100644
--- a/addons_extensions/offer_letters/models/offer_letter.py
+++ b/addons_extensions/offer_letters/models/offer_letter.py
@@ -1,4 +1,5 @@
from odoo import models, fields, api, _
+from odoo.api import readonly
from odoo.exceptions import UserError
from collections import defaultdict
from datetime import timedelta, datetime
@@ -26,8 +27,24 @@ class OfferLetter(models.Model):
default=lambda self: _('New'),
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)
request_date = fields.Datetime(string='Requested On', readonly=True, tracking=True)
diff --git a/addons_extensions/offer_letters/report/offer_letter_template.xml b/addons_extensions/offer_letters/report/offer_letter_template.xml
index 50e867931..a1b714787 100644
--- a/addons_extensions/offer_letters/report/offer_letter_template.xml
+++ b/addons_extensions/offer_letters/report/offer_letter_template.xml
@@ -62,9 +62,9 @@
To,
-
+
-
+
@@ -81,7 +81,7 @@
Dear
- ,
+ ,
@@ -368,7 +368,7 @@
-
+
@@ -486,7 +486,7 @@
Employee Name :
-
+
@@ -595,7 +595,7 @@
-
+
@@ -621,7 +621,7 @@
Company incorporated under Indian Companies Act 1956, having registered office in
Hyderabad,
India ("Company")
-
+
(Recipient)
Whereas "Company" wishes to explore the possibility of entering into an employment
@@ -875,7 +875,7 @@
-
+
diff --git a/addons_extensions/offer_letters/views/offer_letter_views.xml b/addons_extensions/offer_letters/views/offer_letter_views.xml
index eb7f6fa04..e708d6a05 100644
--- a/addons_extensions/offer_letters/views/offer_letter_views.xml
+++ b/addons_extensions/offer_letters/views/offer_letter_views.xml
@@ -6,7 +6,7 @@
-
+
@@ -32,7 +32,9 @@
-
+
+
+
diff --git a/addons_extensions/requisitions/views/hr_requisition.xml b/addons_extensions/requisitions/views/hr_requisition.xml
index 8fe18f155..3905329e2 100644
--- a/addons_extensions/requisitions/views/hr_requisition.xml
+++ b/addons_extensions/requisitions/views/hr_requisition.xml
@@ -34,7 +34,7 @@
-
+
@@ -46,7 +46,7 @@
-
+
@@ -80,10 +80,18 @@
-
+
Recruitment Requisitions
recruitment.requisition
list,form
+
+
+ No Recruitment Requisitions Found
+
+
+ Click Create to raise a new recruitment requisition.
+
+
|