548 lines
26 KiB
Python
548 lines
26 KiB
Python
# -*- coding: utf-8 -*-
|
|
#############################################################################
|
|
# A part of Open HRMS Project <https://www.openhrms.com>
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
#
|
|
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
|
|
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>)
|
|
#
|
|
# You can modify it under the terms of the GNU LESSER
|
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
|
|
#
|
|
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
|
|
# (LGPL v3) along with this program.
|
|
# If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
#############################################################################
|
|
from datetime import timedelta
|
|
from odoo import api, fields, models, _
|
|
from odoo.exceptions import ValidationError, UserError
|
|
from odoo.tools.safe_eval import datetime
|
|
|
|
date_format = "%Y-%m-%d"
|
|
RESIGNATION_TYPE = [('resigned', 'Normal Resignation'),
|
|
('fired', 'Fired by the company'),
|
|
('abosconded','Absconded')]
|
|
|
|
|
|
class HrResignation(models.Model):
|
|
""" Model for HR Resignations. This model is used to track employee
|
|
resignations."""
|
|
_name = 'hr.resignation'
|
|
_description = 'HR Resignation'
|
|
_inherit = 'mail.thread'
|
|
_rec_name = 'employee_id'
|
|
|
|
@api.model
|
|
def default_get(self, default_fields):
|
|
"""If we're creating a new account through a many2one, there are chances that we typed the account code
|
|
instead of its name. In that case, switch both fields values.
|
|
"""
|
|
context = {}
|
|
if 'notice_period' in default_fields:
|
|
notice_period = self.env['ir.config_parameter'].sudo().get_param('hr_resignation.notice_period')
|
|
no_of_days = self.env['ir.config_parameter'].sudo().get_param('hr_resignation.no_of_days')
|
|
|
|
defaults = super(HrResignation, self).default_get(default_fields)
|
|
if notice_period == 'True':
|
|
defaults['notice_period'] = int(no_of_days)
|
|
defaults['emp_requested_lwd'] = str(fields.Datetime.now() + timedelta(days=int(no_of_days)))
|
|
return defaults
|
|
|
|
name = fields.Char(string='Order Reference', copy=False,
|
|
readonly=True, index=True,
|
|
default=lambda self: _('New'), tracking=True)
|
|
employee_id = fields.Many2one('hr.employee', string="Employee",
|
|
default=lambda
|
|
self: self.env.user.employee_id.id,
|
|
help='Name of the employee for '
|
|
'whom the request is creating', tracking=True)
|
|
department_id = fields.Many2one('hr.department', string="Department",
|
|
related='employee_id.department_id',
|
|
help='Department of the employee', tracking=True)
|
|
resign_confirm_date = fields.Date(string="Confirmed Date",
|
|
help='Date on which the request '
|
|
'is confirmed by the employee.',
|
|
track_visibility="always")
|
|
approved_revealing_date = fields.Date(
|
|
string="Finalized Last Day Of Employee",
|
|
help='Date on which the request is confirmed by the HR.',
|
|
track_visibility="always")
|
|
joined_date = fields.Date(string="Join Date",
|
|
help='Joining date of the employee.'
|
|
'i.e Start date of the first contract', tracking=True)
|
|
emp_requested_lwd = fields.Date(string="Employee Request LWD",
|
|
required=False,
|
|
help='Employee requested date on '
|
|
'which employee is revealing '
|
|
'from the company.', tracking=True)
|
|
reason = fields.Text(string="Reason", required=True,
|
|
help='Specify reason for leaving the company', tracking=True)
|
|
notice_period = fields.Char(string="Notice Period (day's)",
|
|
help="Notice Period of the employee in Days.", tracking=True)
|
|
state = fields.Selection(
|
|
[('draft', 'Draft'),
|
|
('in_process','In Process'),
|
|
('approved','Done'),
|
|
('cancel_refuse','Canceled/Refused'),
|
|
('withdrawl', 'Withdrawal')],
|
|
string='Status', default='draft', track_visibility="always")
|
|
resignation_type = fields.Selection(selection=RESIGNATION_TYPE, default='resigned',
|
|
help="Select the type of resignation: "
|
|
"normal resignation or "
|
|
"fired by the company", tracking=True)
|
|
|
|
#normal resignaiton
|
|
normal_resignation_status = fields.Selection([('sent_to_manager','Under Manager Review'),('meeting_with_emp','Meeting Scheduled'),('submit_hr','Under HR Review'),('hr_approve_and_request_asset_collection','Approve and process Collection'),('process_relieving_documents','Process Relieving Letters'),('final_clearance','Issue Final Clearance')], tracking=True)
|
|
|
|
emp_meeting_points = fields.Html(tracking=True)
|
|
agree_with_emp_lwd = fields.Boolean(tracking=True)
|
|
manager_proposed_lwd = fields.Date(tracking=True)
|
|
|
|
fired_status = fields.Selection([('warning_sent','Warning Issued'),('meeting_with_emp','Meeting Scheduled'),('hr_approve_and_request_asset_collection','Approve and Process Collection'),('process_relieving_documents','Process Relieving Letters'),('final_clearance','Issue Final Clearance')], tracking=True)
|
|
meeting_sheduled = fields.Boolean(tracking=True)
|
|
absconded_status = fields.Selection([('contact_emp','Contact Employee via phone, email'),('responed','Responed'),('meeting_with_emp','Meeting Scheduled'),('send_hr','Under HR Review'),('hr_approve_and_request_asset_collection','Process Checklist'),('process_relieving_documents','Process Relieving Letters'),('final_clearance','Issue Final Clearance')], tracking=True)
|
|
emp_responded = fields.Boolean()
|
|
|
|
change_employee = fields.Boolean(string="Change Employee",
|
|
compute="_compute_change_employee",
|
|
help="Checks , if the user has permission"
|
|
" to change the employee", tracking=True)
|
|
employee_contract = fields.Char(String="Contract",tracking=True)
|
|
is_manager = fields.Boolean(compute="_compute_user_rights")
|
|
is_finance_manager = fields.Boolean(compute="_compute_user_rights")
|
|
is_admin = fields.Boolean(compute="_compute_user_rights")
|
|
is_hr = fields.Boolean(compute="_compute_user_rights")
|
|
is_it_manager = fields.Boolean(compute="_compute_user_rights")
|
|
is_emp = fields.Boolean(compute="_compute_user_rights")
|
|
|
|
emp_comments = fields.Text(tracking=True)
|
|
manager_comments = fields.Text(tracking=True)
|
|
finance_manager_comments = fields.Text(tracking=True)
|
|
it_manager_comments = fields.Text(tracking=True)
|
|
admin_comments = fields.Text(tracking=True)
|
|
hr_comments = fields.Text(tracking=True)
|
|
|
|
show_withdraw = fields.Boolean(tracking=True,default=True)
|
|
|
|
|
|
checklist_line_ids = fields.One2many('resignation.checklist.line', 'resignation_id', string="All Checklist Items")
|
|
|
|
|
|
it_checklist_ids = fields.One2many(
|
|
'resignation.checklist.line', 'resignation_id',
|
|
domain=[('team', '=', 'it')],
|
|
string="IT Checklist"
|
|
)
|
|
admin_checklist_ids = fields.One2many(
|
|
'resignation.checklist.line', 'resignation_id',
|
|
domain=[('team', '=', 'admin')],
|
|
string="Admin Checklist"
|
|
)
|
|
manager_checklist_ids = fields.One2many(
|
|
'resignation.checklist.line', 'resignation_id',
|
|
domain=[('team', '=', 'manager')],
|
|
string="Manager Checklist"
|
|
)
|
|
finance_checklist_ids = fields.One2many(
|
|
'resignation.checklist.line', 'resignation_id',
|
|
domain=[('team', '=', 'finance')],
|
|
string="Finance Checklist"
|
|
)
|
|
hr_checklist_ids = fields.One2many(
|
|
'resignation.checklist.line', 'resignation_id',
|
|
domain=[('team', '=', 'hr')],
|
|
string="HR Checklist"
|
|
)
|
|
|
|
manager_checklist_submitted = fields.Boolean(tracking=True)
|
|
it_checklist_submitted = fields.Boolean(tracking=True)
|
|
finance_checklist_submitted = fields.Boolean(tracking=True)
|
|
admin_checklist_submitted = fields.Boolean(tracking=True)
|
|
hr_checklist_submitted = fields.Boolean(tracking=True)
|
|
|
|
|
|
relieving_documents = fields.Many2many('ir.attachment')
|
|
|
|
@api.depends('employee_id')
|
|
def _compute_user_rights(self):
|
|
current_user_id = self.env.user.id
|
|
for resignation in self:
|
|
resignation.is_manager = True if resignation.employee_id.parent_id.user_id.id == current_user_id else False
|
|
resignation.is_it_manager = True if int(self.env['ir.config_parameter'].sudo().get_param('hr_employee_extended.emp_it_manager')) == current_user_id else False
|
|
resignation.is_hr = True if int(self.env['ir.config_parameter'].sudo().get_param('hr_employee_extended.emp_hr_id')) == current_user_id else False
|
|
resignation.is_finance_manager = True if int(self.env['ir.config_parameter'].sudo().get_param('hr_employee_extended.emp_finance_manager')) == current_user_id else False
|
|
resignation.is_admin = True if int(self.env['ir.config_parameter'].sudo().get_param('hr_employee_extended.emp_admin')) == current_user_id else False
|
|
resignation.is_emp = True if resignation.employee_id.user_id.id == current_user_id else False
|
|
|
|
|
|
@api.onchange('agree_with_emp_lwd')
|
|
def onchange_agree_with_emp_lwd(self):
|
|
for resingation in self:
|
|
if resingation.emp_requested_lwd and resingation.agree_with_emp_lwd:
|
|
resingation.approved_revealing_date = resingation.emp_requested_lwd
|
|
|
|
@api.depends('employee_id')
|
|
def _compute_change_employee(self):
|
|
""" Check whether the user has the permission to change the employee"""
|
|
res_user = self.env['res.users'].browse(self._uid)
|
|
self.change_employee = res_user.has_group('hr.group_hr_user')
|
|
|
|
@api.constrains('employee_id')
|
|
def _check_employee_id(self):
|
|
""" Constraint method to check if the current user has the permission
|
|
to create a resignation request for the specified employee.
|
|
"""
|
|
for resignation in self:
|
|
if not self.env.user.has_group('hr.group_hr_user'):
|
|
if (resignation.employee_id.user_id.id and
|
|
resignation.employee_id.user_id.id != self.env.uid):
|
|
raise ValidationError(
|
|
_('You cannot create a request for other employees'))
|
|
|
|
|
|
@api.constrains('joined_date')
|
|
def _check_joined_date(self):
|
|
""" Check if there is an active resignation request for the
|
|
same employee with a confirmed or approved state, based on the
|
|
'joined_date' of the current resignation."""
|
|
for resignation in self:
|
|
resignation_request = self.env['hr.resignation'].search(
|
|
[('employee_id', '=', resignation.employee_id.id),
|
|
('state', 'in', ['in_process', 'approved'])])
|
|
if resignation_request:
|
|
raise ValidationError(
|
|
_('There is a resignation request in confirmed or'
|
|
' approved state for this employee'))
|
|
|
|
|
|
|
|
def action_abscond_emp_response(self):
|
|
for resignation in self:
|
|
resignation.absconded_status = 'responed'
|
|
resignation.emp_responded = True
|
|
|
|
def action_abscond_emp_no_response(self):
|
|
for resignation in self:
|
|
resignation.absconded_status = 'send_hr'
|
|
|
|
def action_abscond_send_hr(self):
|
|
for resignation in self:
|
|
resignation.absconded_status = 'send_hr'
|
|
|
|
def action_abscond_meeting_emp(self):
|
|
for resignation in self:
|
|
resignation.absconded_status = 'meeting_with_emp'
|
|
|
|
def action_submit_manager_checklist(self):
|
|
for rec in self:
|
|
rec.manager_checklist_submitted=True
|
|
|
|
def action_revert_manager_checklist(self):
|
|
for rec in self:
|
|
rec.manager_checklist_submitted=False
|
|
|
|
# For IT Checklist
|
|
def action_submit_it_checklist(self):
|
|
for rec in self:
|
|
rec.it_checklist_submitted = True
|
|
|
|
def action_revert_it_checklist(self):
|
|
for rec in self:
|
|
rec.it_checklist_submitted = False
|
|
|
|
# For Admin Checklist
|
|
def action_submit_admin_checklist(self):
|
|
for rec in self:
|
|
rec.admin_checklist_submitted = True
|
|
|
|
def action_revert_admin_checklist(self):
|
|
for rec in self:
|
|
rec.admin_checklist_submitted = False
|
|
|
|
# For Finance Checklist
|
|
def action_submit_finance_checklist(self):
|
|
for rec in self:
|
|
rec.finance_checklist_submitted = True
|
|
|
|
def action_revert_finance_checklist(self):
|
|
for rec in self:
|
|
rec.finance_checklist_submitted = False
|
|
|
|
def action_submit_hr_checklist(self):
|
|
for rec in self:
|
|
rec.hr_checklist_submitted = True
|
|
|
|
def action_revert_hr_checklist(self):
|
|
for rec in self:
|
|
rec.hr_checklist_submitted = False
|
|
|
|
def action_process_relieving_docs(self):
|
|
for rec in self:
|
|
if rec.resignation_type == 'resigned':
|
|
rec.normal_resignation_status = 'process_relieving_documents'
|
|
elif rec.resignation_type == 'abosconded':
|
|
rec.absconded_status = 'process_relieving_documents'
|
|
elif rec.resignation_type == 'fired':
|
|
rec.fired_status = 'process_relieving_documents'
|
|
|
|
@api.onchange('employee_id')
|
|
def _onchange_employee_id(self):
|
|
""" Method triggered when the 'employee_id' field is changed."""
|
|
self.joined_date = self.employee_id.doj
|
|
if self.employee_id:
|
|
resignation_request = self.env['hr.resignation'].search(
|
|
[('employee_id', '=', self.employee_id.id),
|
|
('state', 'in', ['in_process', 'approved'])])
|
|
if resignation_request:
|
|
raise ValidationError(
|
|
_('There is a resignation request in confirmed or'
|
|
' approved state for this employee'))
|
|
employee_contract = self.env['hr.contract'].search(
|
|
[('employee_id', '=', self.employee_id.id)])
|
|
for contracts in employee_contract:
|
|
if contracts.state == 'open':
|
|
self.employee_contract = contracts.name
|
|
self.notice_period = contracts.notice_days
|
|
|
|
@api.model
|
|
def create(self, vals):
|
|
"""Override of the create method to assign a sequence for the record."""
|
|
if vals.get('name', _('New')) == _('New'):
|
|
vals['name'] = self.env['ir.sequence'].next_by_code(
|
|
'hr.resignation') or _('New')
|
|
return super(HrResignation, self).create(vals)
|
|
|
|
def action_confirm_resignation(self):
|
|
""" Method triggered by the 'Confirm' button to confirm the
|
|
resignation request."""
|
|
for resignation in self:
|
|
if resignation.resignation_type == 'resigned':
|
|
if resignation.joined_date:
|
|
if (resignation.joined_date >=
|
|
resignation.emp_requested_lwd):
|
|
raise ValidationError(
|
|
_('Last date of the Employee must '
|
|
'be anterior to Joining date'))
|
|
else:
|
|
raise ValidationError(
|
|
_('Please set a Joining Date for employee'))
|
|
resignation.state = 'in_process'
|
|
if resignation.resignation_type == 'resigned':
|
|
resignation.normal_resignation_status = 'sent_to_manager'
|
|
|
|
if resignation.resignation_type == 'abosconded':
|
|
resignation.absconded_status = 'contact_emp'
|
|
|
|
if resignation.resignation_type == 'fired':
|
|
resignation.fired_status = 'warning_sent'
|
|
resignation.resign_confirm_date = str(fields.Datetime.now())
|
|
if resignation.notice_period:
|
|
resignation.approved_revealing_date = str(fields.Datetime.now() + timedelta(days=int(resignation.notice_period)))
|
|
|
|
def action_schedule_Meeting(self):
|
|
for resignation in self:
|
|
resignation.meeting_sheduled = True
|
|
if resignation.resignation_type == 'resigned':
|
|
resignation.normal_resignation_status = 'meeting_with_emp'
|
|
elif resignation.resignation_type == 'fired':
|
|
resignation.fired_status = 'meeting_with_emp'
|
|
|
|
def action_proceed_further(self):
|
|
for resignation in self:
|
|
resignation.show_withdraw = False
|
|
resignation.normal_resignation_status = 'submit_hr'
|
|
|
|
def action_proceed_further_fired(self):
|
|
for resignation in self:
|
|
resignation.show_withdraw = False
|
|
resignation.checklist_line_ids.unlink()
|
|
# Fetch all active checklists
|
|
checklists = self.env['pre.resignation.requirements.proceedings'].search([('active', '=', True)])
|
|
|
|
# Create checklist lines
|
|
for checklist in checklists:
|
|
self.env['resignation.checklist.line'].create({
|
|
'resignation_id': resignation.id,
|
|
'checklist_name': checklist.name,
|
|
'team': checklist.team,
|
|
})
|
|
resignation.fired_status = 'hr_approve_and_request_asset_collection'
|
|
|
|
def action_process_resignation(self):
|
|
for resignation in self:
|
|
# Clean previous lines if needed
|
|
resignation.checklist_line_ids.unlink()
|
|
|
|
# Fetch all active checklists
|
|
checklists = self.env['pre.resignation.requirements.proceedings'].search([('active', '=', True)])
|
|
|
|
# Create checklist lines
|
|
for checklist in checklists:
|
|
self.env['resignation.checklist.line'].create({
|
|
'resignation_id': resignation.id,
|
|
'checklist_name': checklist.name,
|
|
'team': checklist.team,
|
|
})
|
|
|
|
if resignation.resignation_type == 'resigned':
|
|
resignation.normal_resignation_status = 'hr_approve_and_request_asset_collection'
|
|
elif resignation.resignation_type == 'abosconded':
|
|
resignation.absconded_status = 'hr_approve_and_request_asset_collection'
|
|
|
|
def action_withdraw_resignation(self):
|
|
for resignation in self:
|
|
resignation.state = 'withdrawl'
|
|
|
|
def action_cancel_resignation(self):
|
|
""" Method triggered by the 'Cancel' button to cancel the resignation
|
|
request."""
|
|
for resignation in self:
|
|
resignation.state = 'cancel_refuse'
|
|
|
|
def action_reject_resignation(self):
|
|
""" Method triggered by the 'Reject' button to reject the
|
|
resignation request."""
|
|
for resignation in self:
|
|
resignation.state = 'cancel_refuse'
|
|
|
|
def action_reset_to_draft(self):
|
|
""" Method triggered by the 'Set to Draft' button to reset the
|
|
resignation request to the 'draft' state."""
|
|
for resignation in self:
|
|
resignation.state = 'draft'
|
|
resignation.employee_id.active = True
|
|
resignation.employee_id.resigned = False
|
|
resignation.employee_id.fired = False
|
|
resignation.employee_id.user_id.active = True
|
|
resignation.fired_status = False
|
|
resignation.absconded_status = False
|
|
resignation.normal_resignation_status = False
|
|
|
|
def action_approve_resignation(self):
|
|
""" Method triggered by the 'Approve' button to
|
|
approve the resignation."""
|
|
for resignation in self:
|
|
if (resignation.emp_requested_lwd and
|
|
resignation.resign_confirm_date):
|
|
employee_contract = self.env['hr.contract'].search(
|
|
[('employee_id', '=', self.employee_id.id)])
|
|
if not employee_contract:
|
|
raise ValidationError(
|
|
_("There are no Contracts found for this employee"))
|
|
for contract in employee_contract:
|
|
if contract.state == 'open':
|
|
resignation.employee_contract = contract.name
|
|
resignation.state = 'approved'
|
|
resignation.approved_revealing_date = (
|
|
resignation.resign_confirm_date + timedelta(
|
|
days=contract.notice_days))
|
|
else:
|
|
resignation.approved_revealing_date = (
|
|
resignation.emp_requested_lwd)
|
|
# Cancelling contract
|
|
contract.state = 'cancel' if contract.state == "open" else \
|
|
contract.state
|
|
# Changing state of the employee if resigning today
|
|
if (resignation.emp_requested_lwd <= fields.Date.today()
|
|
and resignation.employee_id.active):
|
|
resignation.employee_id.active = False
|
|
# Changing fields in the employee table
|
|
# with respect to resignation
|
|
resignation.employee_id.resign_date = (
|
|
resignation.emp_requested_lwd)
|
|
if resignation.resignation_type == 'resigned':
|
|
resignation.employee_id.resigned = True
|
|
departure_reason_id = self.env[
|
|
'hr.departure.reason'].search(
|
|
[('name', '=', 'Resigned')])
|
|
else:
|
|
resignation.employee_id.fired = True
|
|
departure_reason_id = self.env[
|
|
'hr.departure.reason'].search(
|
|
[('name', '=', 'Fired')])
|
|
running_contract_ids = self.env['hr.contract'].search([
|
|
('employee_id', '=', resignation.employee_id.id),
|
|
('company_id', '=', resignation.employee_id.company_id.id),
|
|
('state', '=', 'open'),
|
|
]).filtered(lambda c: c.date_start <= fields.Date.today() and (
|
|
not c.date_end or c.date_end >= fields.Date.today()))
|
|
running_contract_ids.state = 'close'
|
|
resignation.employee_id.departure_reason_id = departure_reason_id
|
|
resignation.employee_id.departure_date = resignation.approved_revealing_date
|
|
# Removing and deactivating user
|
|
if resignation.employee_id.user_id:
|
|
resignation.employee_id.user_id.active = False
|
|
resignation.employee_id.user_id = None
|
|
else:
|
|
raise ValidationError(_('Please Enter Valid Dates.'))
|
|
|
|
def update_employee_status(self):
|
|
pass
|
|
|
|
def process_final_clearance(self):
|
|
"""Process final clearance and exit the employee"""
|
|
for resignation in self:
|
|
employee = resignation.employee_id
|
|
# Check if employee exists and is active
|
|
if not employee:
|
|
raise UserError(_("Employee is already exited or doesn't exist."))
|
|
|
|
# Update employee status
|
|
if resignation.resignation_type == 'resigned':
|
|
departure_reason = self.env.ref("hr.departure_resigned").id
|
|
if resignation.resignation_type == 'fired':
|
|
departure_reason = self.env.ref("hr.departure_fired").id
|
|
elif resignation.resignation_type == 'abosconded':
|
|
departure_reason = self.env.ref("hr_resignation.departure_absconded").id
|
|
|
|
|
|
employee.write({
|
|
'active': False,
|
|
'departure_reason_id': departure_reason,
|
|
'departure_description': resignation.reason,
|
|
'departure_date': datetime.date.today(),
|
|
})
|
|
|
|
# Update resignation status
|
|
if resignation.resignation_type == 'resigned':
|
|
resignation.write({
|
|
'state': 'approved',
|
|
'normal_resignation_status': 'final_clearance'
|
|
})
|
|
elif resignation.resignation_type == 'abosconded':
|
|
resignation.write({
|
|
'state': 'approved',
|
|
'absconded_status': 'final_clearance'
|
|
})
|
|
|
|
elif resignation.resignation_type == 'fired':
|
|
resignation.write({
|
|
'state': 'approved',
|
|
'fired_status': 'final_clearance'
|
|
})
|
|
|
|
# Archive related user if exists
|
|
if employee.user_id:
|
|
employee.sudo().user_id.write({'active': False})
|
|
|
|
# Log the activity
|
|
message = _("Employee %s has been exited through final clearance process.") % employee.name
|
|
resignation.message_post(body=message)
|
|
|
|
return {
|
|
'type': 'ir.actions.client',
|
|
'tag': 'display_notification',
|
|
'params': {
|
|
'title': _('Success'),
|
|
'message': _('Final clearance processed successfully. Employee has been exited.'),
|
|
'sticky': False,
|
|
'next': {'type': 'ir.actions.act_window_close'},
|
|
}
|
|
} |