diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py
index 00c48e1e54d..bcf5ef728c0 100644
--- a/addons/hr_timesheet/models/hr_timesheet.py
+++ b/addons/hr_timesheet/models/hr_timesheet.py
@@ -84,9 +84,14 @@ class AccountAnalyticLine(models.Model):
return False
def _compute_readonly_timesheet(self):
- readonly_timesheets = self.filtered(lambda timesheet: timesheet._is_readonly())
- readonly_timesheets.readonly_timesheet = True
- (self - readonly_timesheets).readonly_timesheet = False
+ # Since the mrp_module gives write access to portal user on timesheet, we check that the user is an internal one before giving the write access.
+ # It is not supposed to be needed, since portal user are not supposed to have access to the views using this field, but better be safe than sorry
+ if not self.env.user.has_group('base.group_user'):
+ self.readonly_timesheet = True
+ else:
+ readonly_timesheets = self.filtered(lambda timesheet: timesheet._is_readonly())
+ readonly_timesheets.readonly_timesheet = True
+ (self - readonly_timesheets).readonly_timesheet = False
def _compute_encoding_uom_id(self):
for analytic_line in self:
diff --git a/addons/hr_timesheet/models/project_task.py b/addons/hr_timesheet/models/project_task.py
index 80b48d43fab..89443e4f0d7 100644
--- a/addons/hr_timesheet/models/project_task.py
+++ b/addons/hr_timesheet/models/project_task.py
@@ -161,14 +161,32 @@ class Task(models.Model):
def action_view_subtask_timesheet(self):
self.ensure_one()
+ is_internal_user = self.env.user.has_group('base.group_user')
task_ids = self.with_context(active_test=False)._get_subtask_ids_per_task_id().get(self.id, [])
action = self.env["ir.actions.actions"]._for_xml_id("hr_timesheet.timesheet_action_all")
graph_view_id = self.env.ref("hr_timesheet.view_hr_timesheet_line_graph_by_employee").id
new_views = []
for view in action['views']:
+ if not is_internal_user:
+ if view[1] == 'tree':
+ tree_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.hr_timesheet_line_portal_tree')
+ if tree_view_id:
+ new_views.insert(0, (tree_view_id, 'tree'))
+ continue
+ elif view[1] == 'form':
+ form_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.timesheet_view_form_portal_user')
+ if form_view_id:
+ new_views.append((form_view_id, 'form'))
+ continue
+ elif view[1] == 'kanban':
+ kanban_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.view_kanban_account_analytic_line_portal_user')
+ if kanban_view_id:
+ new_views.append((kanban_view_id, 'kanban'))
+ continue
if view[1] == 'graph':
view = (graph_view_id, 'graph')
new_views.insert(0, view) if view[1] == 'tree' else new_views.append(view)
+
action.update({
'display_name': _('Timesheets'),
'context': {'default_project_id': self.project_id.id, 'grid_range': 'week'},
diff --git a/addons/hr_timesheet/tests/test_portal_timesheet.py b/addons/hr_timesheet/tests/test_portal_timesheet.py
index 33132f8f0ac..7785e7455b7 100644
--- a/addons/hr_timesheet/tests/test_portal_timesheet.py
+++ b/addons/hr_timesheet/tests/test_portal_timesheet.py
@@ -23,3 +23,48 @@ class TestPortalTimesheet(TestProjectSharingCommon):
self.env.invalidate_all()
# Should not raise any access error
self.env['account.analytic.line'].with_user(self.user_portal).get_view(view_type=view)
+
+ def test_action_view_subtask_timesheet(self):
+ """ Ensure that the action view_subtask_timesheet is accessible without
+ raising an error for all portal users
+ """
+ # A portal collaborator is added to a project to enable the rule analytic.account.analytic.line.timesheet.portal.user
+ self.project_portal.write({
+ 'collaborator_ids': [
+ Command.create({'partner_id': self.user_portal.partner_id.id}),
+ ],
+ })
+ action = self.task_portal.action_view_subtask_timesheet()
+ tree_view_id = form_view_id = kanban_view_id = False
+ for view_id, view_type in action['views']:
+ if view_type == 'tree':
+ tree_view_id = view_id
+ elif view_type == 'form':
+ form_view_id = view_id
+ elif view_type == 'kanban':
+ kanban_view_id = view_id
+
+ action = self.task_portal.with_user(self.user_portal).action_view_subtask_timesheet()
+ portal_tree_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.hr_timesheet_line_portal_tree')
+ portal_form_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.timesheet_view_form_portal_user')
+ portal_kanban_view_id = self.env['ir.model.data']._xmlid_to_res_id('hr_timesheet.view_kanban_account_analytic_line_portal_user')
+ if portal_tree_view_id and portal_form_view_id and portal_kanban_view_id:
+ # no need to check that if the views are not installed or already removed
+ for view_id, view_type in action['views']:
+ if view_type == 'tree':
+ self.assertEqual(view_id, portal_tree_view_id)
+ elif view_type == 'form':
+ self.assertEqual(view_id, portal_form_view_id)
+ elif view_type == 'kanban':
+ self.assertEqual(view_id, portal_kanban_view_id)
+
+ self.env['ir.ui.view'].browse([portal_tree_view_id, portal_form_view_id, portal_kanban_view_id]).unlink()
+
+ action = self.task_portal.with_user(self.user_portal).action_view_subtask_timesheet()
+ for view_id, view_type in action['views']:
+ if view_type == 'tree':
+ self.assertEqual(view_id, tree_view_id)
+ elif view_type == 'form':
+ self.assertEqual(view_id, form_view_id)
+ elif view_type == 'kanban':
+ self.assertEqual(view_id, kanban_view_id)
diff --git a/addons/hr_timesheet/views/hr_timesheet_views.xml b/addons/hr_timesheet/views/hr_timesheet_views.xml
index f400825b641..ccd41971c14 100644
--- a/addons/hr_timesheet/views/hr_timesheet_views.xml
+++ b/addons/hr_timesheet/views/hr_timesheet_views.xml
@@ -23,6 +23,24 @@
+
+ portal.hr_timesheet.account.analytic.line.tree
+ account.analytic.line
+
+ primary
+ 10
+
+
+ 0
+ 0
+ 0
+
+
+ {'no_create_edit': True, 'no_open': True}
+
+
+
+
account.analytic.line.view.tree.with.user
account.analytic.line
@@ -194,6 +212,31 @@
+
+ account.analytic.line.form
+ account.analytic.line
+
+ primary
+ 10
+
+
+ 1
+ many2one_avatar_employee
+ {'active_test': True}
+ {'no_open': True}
+ 1
+
+
+ {'no_create_edit': True, 'no_open': True}
+ 1
+
+
+ {'no_create_edit': True, 'no_open': True}
+ 1
+
+
+
+
view.search.my.timesheet.menu
account.analytic.line
@@ -261,6 +304,19 @@
+
+ portal.account.analytic.line.kanban
+ account.analytic.line
+
+ primary
+ 10
+
+
+
+ o_kanban_record_headings pe-2
+
+
+