Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Master holiday access rights nsa #19270

Merged
merged 2 commits into from Apr 9, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 39 additions & 0 deletions addons/hr_holidays/models/hr_leave.py
Expand Up @@ -17,6 +17,22 @@
_logger = logging.getLogger(__name__)

class HolidaysRequest(models.Model):
"""
Here are the rights associated with the flow of leave requests

State Groups Restriction
============================================================================================================
Draft Anybody Own Request only and state in [confirm, refuse]
Holiday Manager State in [confirm, refuse]
Confirm All State = draft
Validate1 Holiday Officer Not his own Request and state = confirm
Holiday Manager Not his own Request or has no manager and State = confirm
Validate Holiday Officer Not his own Request and state = confirm
Holiday Manager Not his own Request or has no manager and State in [validate1, confirm]
Refuse Holiday Officer State in [confirm, validate, validate1]
Holiday Manager State in [confirm, validate, validate1]
============================================================================================================
"""
_name = "hr.leave"
_description = "Leave"
_order = "date_from desc"
Expand Down Expand Up @@ -89,6 +105,7 @@ def _default_employee(self):
help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
validation_type = fields.Selection('Validation Type', related='holiday_status_id.validation_type')
can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve')

_sql_constraints = [
('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
Expand Down Expand Up @@ -119,6 +136,18 @@ def _compute_can_reset(self):
if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
holiday.can_reset = True

@api.depends('employee_id', 'department_id')
def _compute_can_approve(self):
""" User can only approve a leave request if it is not his own
Exception : User is holiday manager and has no manager
"""
for holiday in self:
# User is holiday manager and has no manager
manager = self.user_has_groups('hr_holidays.group_hr_holidays_manager') \
and not holiday.employee_id.parent_id \
and not holiday.department_id.manager_id
holiday.can_approve = (holiday.employee_id.user_id.id != self.env.uid) or manager

@api.onchange('holiday_type')
def _onchange_type(self):
if self.holiday_type == 'employee' and not self.employee_id:
Expand Down Expand Up @@ -399,6 +428,9 @@ def action_validate(self):
if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))

if any(not holiday.can_approve for holiday in self):
raise UserError(_('Only your manager can approve your leave requests'))

current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
for holiday in self:
if holiday.state not in ['confirm', 'validate1']:
Expand Down Expand Up @@ -518,3 +550,10 @@ def _notify_get_groups(self, message, groups):
})

return [new_group] + groups

@api.multi
def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None, force=True):
# due to record rule can not allow to add follower and mention on validated leave so subscribe through sudo
if self.state in ['validate', 'validate1']:
return super(HolidaysRequest, self.sudo()).message_subscribe(partner_ids=partner_ids, channel_ids=channel_ids, subtype_ids=subtype_ids, force=force)
return super(HolidaysRequest, self).message_subscribe(partner_ids=partner_ids, channel_ids=channel_ids, subtype_ids=subtype_ids, force=force)
39 changes: 39 additions & 0 deletions addons/hr_holidays/models/hr_leave_allocation.py
Expand Up @@ -13,6 +13,22 @@


class HolidaysAllocation(models.Model):
"""
Here are the rights associated with the flow of allocation requests

State Groups Restriction
============================================================================================================
Draft Anybody Own Allocation only and state in [confirm, refuse]
Holiday Manager State in [confirm, refuse]
Confirm All State = draft
Validate1 Holiday Officer Not his own Allocation and state = confirm
Holiday Manager Not his own Allocation or has no manager and State = confirm
Validate Holiday Officer Not his own Allocation and state = confirm
Holiday Manager Not his own Allocation or has no manager and State in [validate1, confirm]
Refuse Holiday Officer State in [confirm, validate, validate1]
Holiday Manager State in [confirm, validate, validate1]
============================================================================================================
"""
_name = "hr.leave.allocation"
_description = "Leaves Allocation"
_inherit = ['mail.thread', 'mail.activity.mixin']
Expand Down Expand Up @@ -67,6 +83,7 @@ def _default_holiday_status_id(self):
help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
validation_type = fields.Selection('Validation Type', related='holiday_status_id.validation_type')
can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve')

_sql_constraints = [
('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
Expand All @@ -91,6 +108,18 @@ def _compute_can_reset(self):
if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
holiday.can_reset = True

@api.depends('employee_id', 'department_id')
def _compute_can_approve(self):
""" User can only approve a leave request if it is not his own
Exception : User is holiday manager and has no manager
"""
for holiday in self:
# User is holiday manager and has no manager
manager = self.user_has_groups('hr_holidays.group_hr_holidays_manager') \
and not holiday.employee_id.parent_id \
and not holiday.department_id.manager_id
holiday.can_approve = (holiday.employee_id.user_id.id != self.env.uid) or manager

@api.onchange('holiday_type')
def _onchange_type(self):
if self.holiday_type == 'employee' and not self.employee_id:
Expand Down Expand Up @@ -242,6 +271,9 @@ def action_validate(self):
if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))

if any(not holiday.can_approve for holiday in self):
raise UserError(_('Only your manager can approve your allocations'))

current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
for holiday in self:
if holiday.state not in ['confirm', 'validate1']:
Expand Down Expand Up @@ -353,3 +385,10 @@ def _notify_get_groups(self, message, groups):
})

return [new_group] + groups

@api.multi
def message_subscribe(self, partner_ids=None, channel_ids=None, subtype_ids=None, force=True):
# due to record rule can not allow to add follower and mention on validated leave so subscribe through sudo
if self.state in ['validate', 'validate1']:
return super(HolidaysAllocation, self.sudo()).message_subscribe(partner_ids=partner_ids, channel_ids=channel_ids, subtype_ids=subtype_ids, force=force)
return super(HolidaysAllocation, self).message_subscribe(partner_ids=partner_ids, channel_ids=channel_ids, subtype_ids=subtype_ids, force=force)
2 changes: 1 addition & 1 deletion addons/hr_holidays/report/hr_leave_reports.xml
@@ -1,6 +1,6 @@
<?xml version="1.0"?>
<odoo>

<record id="view_hr_holidays_filter_report" model="ir.ui.view">
<field name="name">hr.holidays.filter</field>
<field name="model">hr.leave.report</field>
Expand Down
30 changes: 22 additions & 8 deletions addons/hr_holidays/security/hr_holidays_security.xml
Expand Up @@ -17,7 +17,7 @@
<field name="groups_id" eval="[(4,ref('hr_holidays.group_hr_holidays_manager'))]"/>
</record>

<record id="property_rule_request_employee" model="ir.rule">
<record id="hr_leave_rule_employee" model="ir.rule">
<field name="name">Employee Leaves</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.user_id','=',user.id)]</field>
Expand All @@ -27,22 +27,29 @@
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>

<record id="property_rule_request_employee_write" model="ir.rule">
<record id="hr_leave_rule_employee_write" model="ir.rule">
<field name="name">Employee Leaves Create, Write, Unlink</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])]</field>
<field name="perm_read" eval="False"/>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>

<record id="property_rule_request_officer" model="ir.rule">
<record id="hr_leave_rule_officer" model="ir.rule">
<field name="name">Leaves Officer</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="domain_force">['|','|','|', ('employee_id.user_id', '=', user.id), ('department_id', '=', False), ('department_id.manager_id', '=', False), ('department_id.manager_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4,ref('hr_holidays.group_hr_holidays_user'))]"/>
</record>

<record id="property_rule_allocation_employee" model="ir.rule">
<record id="hr_leave_rule_manager" model="ir.rule">
<field name="name">Manager Leaves</field>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_hr_holidays_manager'))]"/>
</record>

<record id="hr_leave_allocation_rule_employee" model="ir.rule">
<field name="name">Employee Allocations</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[('employee_id.user_id','=',user.id)]</field>
Expand All @@ -52,21 +59,28 @@
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>

<record id="property_rule_allocation_employee_write" model="ir.rule">
<record id="hr_leave_allocation_rule_employee_write" model="ir.rule">
<field name="name">Employee Allocations Create, Write, Unlink</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])]</field>
<field name="perm_read" eval="False"/>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>

<record id="property_rule_allocation_officer" model="ir.rule">
<record id="hr_leave_allocation_rule_officer" model="ir.rule">
<field name="name">Leaves Officer</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="domain_force">['|','|','|', ('employee_id.user_id', '=', user.id), ('department_id', '=', False), ('department_id.manager_id', '=', False), ('department_id.manager_id.user_id', '=', user.id)]</field>
<field name="groups" eval="[(4,ref('hr_holidays.group_hr_holidays_user'))]"/>
</record>

<record id="hr_leave_allocation_rule_manager" model="ir.rule">
<field name="name">Manager Allocations</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('group_hr_holidays_manager'))]"/>
</record>

<record id="resource_leaves_officer" model="ir.rule">
<field name="name">Leaves Resources Officer</field>
<field name="model_id" ref="model_resource_calendar_leaves"/>
Expand Down
5 changes: 4 additions & 1 deletion addons/hr_holidays/security/ir.model.access.csv
@@ -1,10 +1,13 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_holidays_manager_request,hr.holidays.manager.request,model_hr_leave,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_hr_holidays_user_request,hr.holidays.user.request,model_hr_leave,hr_holidays.group_hr_holidays_user,1,1,1,1
access_hr_holidays_employee_request,hr.holidays.employee.request,model_hr_leave,base.group_user,1,1,1,1
access_hr_holidays_manager_allocation,hr.holidays.manager.allocation,model_hr_leave_allocation,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_hr_holidays_user_allocation,hr.holidays.user.allocation,model_hr_leave_allocation,hr_holidays.group_hr_holidays_user,1,1,1,1
access_hr_holidays_employee_allocation,hr.holidays.employee.allocation,model_hr_leave_allocation,base.group_user,1,1,1,1
access_hr_holidays_status_employee,hr.holidays.status employee,model_hr_leave_type,base.group_user,1,0,0,0
access_hr_holidays_status_manager,hr.holidays.status manager,model_hr_leave_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_hr_holidays_status_user,hr.holidays.status user,model_hr_leave_type,hr_holidays.group_hr_holidays_user,1,0,0,0
access_hr_holidays_status_employee,hr.holidays.status employee,model_hr_leave_type,base.group_user,1,0,0,0
access_hr_leave_report,access_hr_leave_report,model_hr_leave_report,,1,0,0,0
access_resource_calendar_leaves_user,resource_calendar_leaves_user,resource.model_resource_calendar_leaves,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_hr_user,calendar.event.hr.user,calendar.model_calendar_event,hr_holidays.group_hr_holidays_user,1,1,1,1
Expand Down
12 changes: 12 additions & 0 deletions addons/hr_holidays/tests/common.py
Expand Up @@ -33,6 +33,12 @@ def setUp(self):
'email': 'david.employee@example.com',
'groups_id': [(6, 0, [group_employee_id])]
}).id
self.user_hrmanager_2_id = Users.create({
'name': 'Florence HrManager',
'login': 'florence',
'email': 'florence.hrmanager@example.com',
'groups_id': [(6, 0, [group_employee_id, self.ref('hr_holidays.group_hr_holidays_manager')])]
}).id

# Hr Data
Department = self.env['hr.department'].with_context(tracking_disable=True)
Expand Down Expand Up @@ -60,4 +66,10 @@ def setUp(self):
'department_id': self.hr_dept.id,
}).id

self.employee_hrmanager_2_id = self.env['hr.employee'].create({
'name': 'Florence HrManager',
'user_id': self.user_hrmanager_2_id,
'parent_id': self.employee_hrmanager_id,
}).id

self.rd_dept.write({'manager_id': self.employee_hruser_id})
72 changes: 72 additions & 0 deletions addons/hr_holidays/tests/test_holidays_flow.py
Expand Up @@ -280,6 +280,78 @@ def _check_holidays_status(holiday_status, ml, lt, rl, vrl):
'number_of_days_temp': 1,
})

hol41 = HolidaysEmployeeGroup.create({
'name': 'Hol41',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_status_1.id,
'date_from': (datetime.today() + relativedelta(days=9)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=10)),
'number_of_days_temp': 1,
})

# A simple user should be able to reset it's own leave
hol41.action_draft()
hol41.unlink()

hol42 = Requests.sudo(self.user_hrmanager_id).create({
'name': 'Hol41',
'employee_id': self.employee_hrmanager_id,
'holiday_status_id': self.holidays_status_1.id,
'date_from': (datetime.today() + relativedelta(days=9)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=10)),
'number_of_days_temp': 1,
})

# But not someone else's leave
with self.assertRaises(AccessError):
hol42.sudo(self.user_employee_id).action_draft()

# An officer should not be able to reset someone else's leave
with self.assertRaises(UserError):
hol42.sudo(self.user_hruser_id).action_draft()

# A manager should be able to reset someone else's leave
hol42.action_draft()
hol42.unlink()

# Officer should not be able to approve it's own leave
hol50 = HolidaysEmployeeGroup.sudo(self.user_hruser_id).create({
'name': 'Hol50',
'employee_id': self.employee_hruser_id,
'holiday_status_id': self.holidays_status_1.id,
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=16)),
'number_of_days_temp': 1,
})

with self.assertRaises(UserError):
hol50.action_approve()

# Manager should not be able to approve it's own leave
hol51 = HolidaysEmployeeGroup.sudo(self.user_hrmanager_2_id).create({
'name': 'Hol51',
'employee_id': self.employee_hrmanager_2_id,
'holiday_status_id': self.holidays_status_1.id,
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=16)),
'number_of_days_temp': 1,
})

with self.assertRaises(UserError):
hol51.action_approve()

# Unless there is not manager above
hol52 = HolidaysEmployeeGroup.sudo(self.user_hrmanager_id).create({
'name': 'Hol52',
'employee_id': self.employee_hrmanager_id,
'holiday_status_id': self.holidays_status_1.id,
'date_from': (datetime.today() + relativedelta(days=15)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=16)),
'number_of_days_temp': 1,
})

hol52.action_approve()

def test_10_leave_summary_reports(self):
# Print the HR Holidays(Summary Department) Report through the wizard
ctx = {
Expand Down