time-off FIX

This commit is contained in:
Pranay 2025-04-07 16:08:02 +05:30 committed by raman
parent 909bf116b6
commit 27eed9ee63
4 changed files with 192 additions and 6 deletions

View File

@ -22,6 +22,7 @@
# always loaded # always loaded
'data': [ 'data': [
'security/security.xml',
'views/hr_employee.xml', 'views/hr_employee.xml',
'views/hr_timeoff.xml' 'views/hr_timeoff.xml'
], ],

View File

@ -1,3 +1,4 @@
from asyncore import write
from calendar import month from calendar import month
from dateutil.utils import today from dateutil.utils import today
@ -7,6 +8,7 @@ from datetime import datetime, date, time, timedelta
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from odoo.exceptions import ValidationError, UserError from odoo.exceptions import ValidationError, UserError
from odoo.addons.hr_holidays.models.hr_leave import HolidaysRequest
class hrLeaveAccrualLevel(models.Model): class hrLeaveAccrualLevel(models.Model):
_inherit = 'hr.leave.accrual.level' _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 'Approved', when time off request is approved by manager." +
"\nThe status is 'Cancelled', when time off request is cancelled.") "\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): def _check_validity(self):
for rec in self: for rec in self:
@ -293,7 +335,7 @@ class HRLeave(models.Model):
for rec in self: for rec in self:
if rec.employee_id.user_id.id != self.env.user.id: if rec.employee_id.user_id.id != self.env.user.id:
raise ValidationError(_("Only employee can submit his own leave")) raise ValidationError(_("Only employee can submit his own leave"))
rec.submitted_date = fields.Datetime.now()
self._check_validity() self._check_validity()
rec.state = 'confirm' rec.state = 'confirm'
@ -310,8 +352,30 @@ class HRLeave(models.Model):
def action_approve(self): def action_approve(self):
for rec in self: for rec in self:
if rec.employee_id.leave_manager_id.id != self.env.user.id: if rec.employee_id.leave_manager_id.id != self.env.user.id :
raise ValidationError(_("Only Employees Time Off Approver can approve this ")) 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) @api.ondelete(at_uninstall=False)
def _unlink_if_correct_states(self): def _unlink_if_correct_states(self):
@ -323,13 +387,60 @@ class HRLeave(models.Model):
for hol in self: for hol in self:
if hol.state not in ['draft', 'cancel']: if hol.state not in ['draft', 'cancel']:
raise UserError(error_message % state_description_values.get(self[:1].state)) 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: else:
for holiday in self.filtered(lambda holiday: holiday.state not in ['cancel', 'draft']): for holiday in self.filtered(lambda holiday: holiday.state not in ['cancel', 'draft']):
raise UserError(error_message % (state_description_values.get(holiday.state),)) 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): class HRLeaveType(models.Model):
_inherit='hr.leave.type' _inherit='hr.leave.type'

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" ?>
<odoo>
<data>
<function model="ir.model.data" name="write">
<value model="ir.model.data" search="[('module', '=', 'hr_holidays'),('name', '=', 'hr_leave_rule_employee_unlink')]"/>
<value eval="{'noupdate': False}"/>
</function>
<record id="hr_holidays.hr_leave_rule_employee_unlink" model="ir.rule">
<field name="domain_force">[('employee_id.user_id', '=', user.id), ('state', 'in', ['draft'])]
</field>
</record>
</data>
</odoo>

View File

@ -155,6 +155,10 @@
<field name="model">hr.leave</field> <field name="model">hr.leave</field>
<field name="inherit_id" ref="hr_holidays.hr_leave_view_form"/> <field name="inherit_id" ref="hr_holidays.hr_leave_view_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//form" position="attributes">
<attribute name="duplicate">0</attribute>
</xpath>
<xpath expr="//field[@name='state']" position="attributes"> <xpath expr="//field[@name='state']" position="attributes">
<attribute name="statusbar_visible">draft,confirm,validate,cancel</attribute> <attribute name="statusbar_visible">draft,confirm,validate,cancel</attribute>
</xpath> </xpath>
@ -177,8 +181,11 @@
<xpath expr="//div[3]/field[@name='request_unit_hours']" position="attributes"> <xpath expr="//div[3]/field[@name='request_unit_hours']" position="attributes">
<attribute name="readonly">state != 'draft'</attribute> <attribute name="readonly">state != 'draft'</attribute>
</xpath> </xpath>
<xpath expr="//div[3]" position="inside">
<field name="submitted_date" readonly="1" force_save="1"/>
</xpath>
<xpath expr="//field[@name='name']" position="attributes"> <xpath expr="//field[@name='name']" position="attributes">
<attribute name="readonly">state != 'draft'</attribute> <attribute name="readonly">state not in ('draft','confirm')</attribute>
</xpath> </xpath>
<xpath expr="//label[@for='supported_attachment_ids']" position="attributes"> <xpath expr="//label[@for='supported_attachment_ids']" position="attributes">
<attribute name="invisible">not leave_type_support_document or state not in ('draft', 'confirm', <attribute name="invisible">not leave_type_support_document or state not in ('draft', 'confirm',
@ -195,6 +202,56 @@
</field> </field>
</record> </record>
<record id="hr_leave_view_list_inherit" model="ir.ui.view">
<field name="name">hr.leave.view.list.inherit</field>
<field name="model">hr.leave</field>
<field name="inherit_id" ref="hr_holidays.hr_leave_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='date_to']" position="after">
<field name="submitted_date" readonly="1" force_save="1"/>
</xpath>
<xpath expr="//list" position="attributes">
<attribute name="duplicate">0</attribute>
</xpath>
</field>
</record>
<record id="view_hr_holidays_filter_inherit" model="ir.ui.view">
<field name="name">hr.holidays.filter.inherit</field>
<field name="model">hr.leave</field>
<field name="inherit_id" ref="hr_holidays.view_hr_holidays_filter"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='waiting_for_me_manager']" position="attributes">
<attribute name="domain">[
('state', 'in', ['confirm', 'validate1']),
('employee_id.user_id', '!=', uid),
'|',
'&amp;',
('state', '=', 'confirm'),
('employee_id.leave_manager_id', '=', uid),
'&amp;',
('state', '=', 'validate1'),
('holiday_status_id.responsible_ids', 'in', uid)
]
</attribute>
</xpath>
</field>
</record>
<record id="hr_leave_view_form_manager_inherit" model="ir.ui.view">
<field name="name">hr.leave.manager.form.inherit</field>
<field name="model">hr.leave</field>
<field name="inherit_id" ref="hr_holidays.hr_leave_view_form_manager"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='employee_id']" position="attributes">
<attribute name="readonly">state in ['cancel', 'refuse', 'validate', 'validate1', 'confirm']</attribute>
</xpath>
<xpath expr="//form" position="attributes">
<attribute name="duplicate">0</attribute>
</xpath>
</field>
</record>
<record id="hr_accrual_level_view_form_inherit" model="ir.ui.view"> <record id="hr_accrual_level_view_form_inherit" model="ir.ui.view">
<field name="name">hr.leave.accrual.level.form.inherit</field> <field name="name">hr.leave.accrual.level.form.inherit</field>