From a25b27ee0eedcb5152e7a9121a305f25d39db0b8 Mon Sep 17 00:00:00 2001 From: Pranay Date: Mon, 7 Apr 2025 16:08:02 +0530 Subject: [PATCH] time-off FIX --- .../hr_timeoff_extended/__manifest__.py | 1 + .../hr_timeoff_extended/models/hr_timeoff.py | 121 +++++++++++++++++- .../hr_timeoff_extended/security/security.xml | 17 +++ .../hr_timeoff_extended/views/hr_timeoff.xml | 59 ++++++++- 4 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 addons_extensions/hr_timeoff_extended/security/security.xml diff --git a/addons_extensions/hr_timeoff_extended/__manifest__.py b/addons_extensions/hr_timeoff_extended/__manifest__.py index f61987969..07429174a 100644 --- a/addons_extensions/hr_timeoff_extended/__manifest__.py +++ b/addons_extensions/hr_timeoff_extended/__manifest__.py @@ -22,6 +22,7 @@ # always loaded 'data': [ + 'security/security.xml', 'views/hr_employee.xml', 'views/hr_timeoff.xml' ], diff --git a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py index 372cb9660..5230d2052 100644 --- a/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py +++ b/addons_extensions/hr_timeoff_extended/models/hr_timeoff.py @@ -1,3 +1,4 @@ +from asyncore import write from calendar import month from dateutil.utils import today @@ -7,6 +8,7 @@ from datetime import datetime, date, time, timedelta from dateutil.relativedelta import relativedelta from odoo.exceptions import ValidationError, UserError +from odoo.addons.hr_holidays.models.hr_leave import HolidaysRequest class hrLeaveAccrualLevel(models.Model): _inherit = 'hr.leave.accrual.level' @@ -260,6 +262,46 @@ class HRLeave(models.Model): "\nThe status is 'Approved', when time off request is approved by manager." + "\nThe status is 'Cancelled', when time off request is cancelled.") + submitted_date = fields.Datetime(string="Submit Date") + + + + def write(self, values): + is_officer = self.env.user.has_group('hr_holidays.group_hr_holidays_user') or self.env.is_superuser() + if not is_officer and values.keys() - {'attachment_ids', 'supported_attachment_ids', 'message_main_attachment_id'}: + # if any(hol.date_from.date() < fields.Date.today() and hol.employee_id.leave_manager_id != self.env.user for hol in self): + # raise UserError(_('You must have manager rights to modify/validate a time off that already begun')) + if any(leave.state == 'cancel' for leave in self): + # raise UserError(_('Only a manager can modify a canceled leave.')) + pass + # Unlink existing resource.calendar.leaves for validated time off + if 'state' in values and values['state'] != 'validate': + validated_leaves = self.filtered(lambda l: l.state == 'validate') + validated_leaves._remove_resource_leave() + + employee_id = values.get('employee_id', False) + if not self.env.context.get('leave_fast_create'): + if values.get('state'): + self._check_approval_update(values['state']) + if any(holiday.validation_type == 'both' for holiday in self): + if values.get('employee_id'): + employees = self.env['hr.employee'].browse(values.get('employee_id')) + else: + employees = self.mapped('employee_id') + self._check_double_validation_rules(employees, values['state']) + if 'date_from' in values: + values['request_date_from'] = values['date_from'] + if 'date_to' in values: + values['request_date_to'] = values['date_to'] + result = super(HolidaysRequest, self).write(values) + if any(field in values for field in ['request_date_from', 'date_from', 'request_date_from', 'date_to', 'holiday_status_id', 'employee_id', 'state']): + self._check_validity() + if not self.env.context.get('leave_fast_create'): + for holiday in self: + if employee_id: + holiday.add_follower(employee_id) + + return result def _check_validity(self): for rec in self: @@ -293,7 +335,7 @@ class HRLeave(models.Model): for rec in self: if rec.employee_id.user_id.id != self.env.user.id: raise ValidationError(_("Only employee can submit his own leave")) - + rec.submitted_date = fields.Datetime.now() self._check_validity() rec.state = 'confirm' @@ -310,8 +352,30 @@ class HRLeave(models.Model): def action_approve(self): for rec in self: - if rec.employee_id.leave_manager_id.id != self.env.user.id: - raise ValidationError(_("Only Employees Time Off Approver can approve this ")) + if rec.employee_id.leave_manager_id.id != self.env.user.id : + raise ValidationError(_("Only Employees Time Off Manager can approve this Leave request")) + return super(HRLeave, self).action_approve() + + def action_refuse(self): + for rec in self: + if (rec.employee_id.leave_manager_id.id != self.env.user.id) and (self.env.user.id not in rec.holiday_status_id.responsible_ids.ids): + raise ValidationError(_("only Employee / Leave type Time off Manager's can refuse this Leave request")) + return super(HRLeave, self).action_refuse() + + + def action_validate(self, check_state=True): + current_employee = self.env.user.employee_id + for holiday in self: + if check_state and holiday.state in ['validate1'] and holiday.validation_type == 'both' and (holiday.holiday_status_id.responsible_ids and (current_employee.user_id.id not in holiday.holiday_status_id.responsible_ids.ids)): + raise UserError(_('Only Timeoff officers for the %s can validate this leave'%(holiday.holiday_status_id.name))) + + return super(HRLeave, self).action_validate(check_state) + + @api.depends_context('uid') + @api.depends('state', 'employee_id') + def _compute_can_cancel(self): + for leave in self: + leave.can_cancel = leave.id and leave.employee_id.user_id == self.env.user and leave.state in ['confirm'] @api.ondelete(at_uninstall=False) def _unlink_if_correct_states(self): @@ -323,13 +387,60 @@ class HRLeave(models.Model): for hol in self: if hol.state not in ['draft', 'cancel']: raise UserError(error_message % state_description_values.get(self[:1].state)) - if hol.date_from.date() < now: - raise UserError(_('You cannot delete a time off which is in the past')) else: for holiday in self.filtered(lambda holiday: holiday.state not in ['cancel', 'draft']): raise UserError(error_message % (state_description_values.get(holiday.state),)) + def _check_approval_update(self, state): + """ Check if target state is achievable. """ + if self.env.is_superuser(): + return + + current_employee = self.env.user.employee_id + is_officer = self.env.user.has_group('hr_holidays.group_hr_holidays_user') + is_manager = self.env.user.has_group('hr_holidays.group_hr_holidays_manager') + + for holiday in self: + val_type = holiday.validation_type + + if not is_manager: + if holiday.state == 'cancel' and state != 'confirm': + raise UserError(_('A cancelled leave cannot be modified.')) + if state == 'confirm': + if holiday.state == 'refuse': + raise UserError(_('Only a Time Off Manager can reset a refused leave.')) + # if holiday.date_from and holiday.date_from.date() <= fields.Date.today(): + # raise UserError(_('Only a Time Off Manager can reset a started leave.')) + if holiday.employee_id != current_employee: + raise UserError(_('Only a Time Off Manager can reset other people leaves.')) + else: + if val_type == 'no_validation' and current_employee == holiday.employee_id and (is_officer or is_manager): + continue + # use ir.rule based first access check: department, members, ... (see security.xml) + holiday.check_access('write') + + # This handles states validate1 validate and refuse + if holiday.employee_id == current_employee\ + and self.env.user != holiday.employee_id.leave_manager_id\ + and not is_officer: + raise UserError(_('Only a Time Off Officer or Manager can approve/refuse its own requests.')) + + if (state == 'validate1' and val_type == 'both'): + if not is_officer and self.env.user != holiday.employee_id.leave_manager_id: + raise UserError(_('You must be either %s\'s manager or Time off Manager to approve this leave') % (holiday.employee_id.name)) + + if (state == 'validate' and val_type == 'manager')\ + and self.env.user != holiday.employee_id.leave_manager_id\ + and not is_officer: + raise UserError(_("You must be %s's Manager to approve this leave", holiday.employee_id.name)) + + if not is_officer and (state == 'validate' and val_type == 'hr'): + raise UserError(_('You must either be a Time off Officer or Time off Manager to approve this leave')) + +HolidaysRequest.write = HRLeave.write + + class HRLeaveType(models.Model): _inherit='hr.leave.type' diff --git a/addons_extensions/hr_timeoff_extended/security/security.xml b/addons_extensions/hr_timeoff_extended/security/security.xml new file mode 100644 index 000000000..99a2e16be --- /dev/null +++ b/addons_extensions/hr_timeoff_extended/security/security.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + [('employee_id.user_id', '=', user.id), ('state', 'in', ['draft'])] + + + + + + \ No newline at end of file diff --git a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml index 6ba6562b2..8c111abe0 100644 --- a/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml +++ b/addons_extensions/hr_timeoff_extended/views/hr_timeoff.xml @@ -155,6 +155,10 @@ hr.leave + + 0 + + draft,confirm,validate,cancel @@ -177,8 +181,11 @@ state != 'draft' + + + - state != 'draft' + state not in ('draft','confirm') not leave_type_support_document or state not in ('draft', 'confirm', @@ -195,6 +202,56 @@ + + hr.leave.view.list.inherit + hr.leave + + + + + + + 0 + + + + + + + hr.holidays.filter.inherit + hr.leave + + + + [ + ('state', 'in', ['confirm', 'validate1']), + ('employee_id.user_id', '!=', uid), + '|', + '&', + ('state', '=', 'confirm'), + ('employee_id.leave_manager_id', '=', uid), + '&', + ('state', '=', 'validate1'), + ('holiday_status_id.responsible_ids', 'in', uid) + ] + + + + + + + hr.leave.manager.form.inherit + hr.leave + + + + state in ['cancel', 'refuse', 'validate', 'validate1', 'confirm'] + + + 0 + + + hr.leave.accrual.level.form.inherit