From 2cfb25c033e069fed98b9d78755277e51e96125f Mon Sep 17 00:00:00 2001 From: damr Date: Tue, 27 Feb 2024 11:09:51 +0000 Subject: [PATCH] [FIX] hr_timesheet: make portal view readonly for ts Currently, there is an access right problem concerning timesheet. The module mrp gives write access to portal user on analytic line, but it is not something we want for the project app. step to reproduce: - install hr_timesheet on a saas-16.3 db - go to project app, open office design - select any task, and add a subtask to it - go the the view form of the subtask - create a new timesheet for this subtask - go back to the view form of the parent task a button 'subtask timesheet' is now visible - share the project in edit mode with a portal user - connect with that portal user - open the office design project - go to the view form of the parent task and click on the button - the user is redirected to a view tree with the timesheet of the subtask this view tree is editable Source of the problem: in the mrp module, the access rights of the analytic line is overwritten to give write access to it to portal user. Soluce: We have to make the view in readonly mode for timesheet when the connected user is a portal one. Unfortunately, it is not 100% possible because setting the field to readonly still gives the user the opportunity to click on those fields (which triggers access errors). So we have to create new views and specifically open those views when the user is a portal one. affected version: saas-16.3 - master task - 3751315 closes odoo/odoo#160959 X-original-commit: 7b733f118f60ff98d9f82c69453187d70e6bece0 Signed-off-by: Xavier Bol (xbo) --- addons/hr_timesheet/models/hr_timesheet.py | 11 +++- addons/hr_timesheet/models/project_task.py | 18 ++++++ .../tests/test_portal_timesheet.py | 45 +++++++++++++++ .../hr_timesheet/views/hr_timesheet_views.xml | 56 +++++++++++++++++++ 4 files changed, 127 insertions(+), 3 deletions(-) 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 + + +