diff --git a/addons/hr_timesheet/controllers/project.py b/addons/hr_timesheet/controllers/project.py index 970ae2fdb0f..102b6cfe530 100644 --- a/addons/hr_timesheet/controllers/project.py +++ b/addons/hr_timesheet/controllers/project.py @@ -10,8 +10,8 @@ from odoo.addons.project.controllers.portal import CustomerPortal class ProjectCustomerPortal(CustomerPortal): - def _prepare_project_sharing_session_info(self, project): - session_info = super()._prepare_project_sharing_session_info(project) + def _prepare_project_sharing_session_info(self, project, task=None): + session_info = super()._prepare_project_sharing_session_info(project, task) company = project.company_id timesheet_encode_uom = company.timesheet_encode_uom_id diff --git a/addons/project/controllers/portal.py b/addons/project/controllers/portal.py index 63cd3bd2a22..b7e108713d6 100644 --- a/addons/project/controllers/portal.py +++ b/addons/project/controllers/portal.py @@ -147,19 +147,22 @@ class ProjectCustomerPortal(CustomerPortal): return request.render("project.portal_my_projects", values) @http.route(['/my/project/'], type='http', auth="public", website=True) - def portal_my_project(self, project_id=None, access_token=None, page=1, date_begin=None, date_end=None, sortby=None, search=None, search_in='content', groupby=None, **kw): + def portal_my_project(self, project_id=None, access_token=None, page=1, date_begin=None, date_end=None, sortby=None, search=None, search_in='content', groupby=None, task_id=None, **kw): try: project_sudo = self._document_check_access('project.project', project_id, access_token) except (AccessError, MissingError): return request.redirect('/my') if project_sudo.with_user(request.env.user)._check_project_sharing_access(): - return request.render("project.project_sharing_portal", {'project_id': project_id}) + values = {'project_id': project_id} + if task_id: + values['task_id'] = task_id + return request.render("project.project_sharing_portal", values) project_sudo = project_sudo if access_token else project_sudo.with_user(request.env.user) values = self._project_get_page_view_values(project_sudo, access_token, page, date_begin, date_end, sortby, search, search_in, groupby, **kw) values['task_url'] = 'project/%s/task' % project_id return request.render("project.portal_my_project", values) - def _prepare_project_sharing_session_info(self, project): + def _prepare_project_sharing_session_info(self, project, task=None): session_info = request.env['ir.http'].session_info() user_context = request.session.get_context() if request.session.uid else {} mods = conf.server_wide_modules or [] @@ -188,17 +191,19 @@ class ProjectCustomerPortal(CustomerPortal): # FIXME: See if we prefer to give only the currency that the portal user just need to see the correct information in project sharing currencies=request.env['ir.http'].get_currencies(), ) + if task: + session_info['open_task_action'] = task.action_project_sharing_open_task() return session_info @http.route("/my/project//project_sharing", type="http", auth="user", methods=['GET']) - def render_project_backend_view(self, project_id): + def render_project_backend_view(self, project_id, task_id=None): project = request.env['project.project'].sudo().browse(project_id) if not project.exists() or not project.with_user(request.env.user)._check_project_sharing_access(): return request.not_found() - + task = task_id and request.env['project.task'].browse(int(task_id)) return request.render( 'project.project_sharing_embed', - {'session_info': self._prepare_project_sharing_session_info(project)}, + {'session_info': self._prepare_project_sharing_session_info(project, task)}, ) @http.route('/my/project//task/', type='http', auth='public', website=True) diff --git a/addons/project/models/project.py b/addons/project/models/project.py index 00faf9fed40..29b2dbfc3f5 100644 --- a/addons/project/models/project.py +++ b/addons/project/models/project.py @@ -44,6 +44,7 @@ PROJECT_TASK_READABLE_FIELDS = { 'legend_blocked', 'legend_done', 'user_ids', + 'display_parent_task_button', } PROJECT_TASK_WRITABLE_FIELDS = { @@ -1038,6 +1039,9 @@ class Task(models.Model): domain="[('allow_task_dependencies', '=', True), ('id', '!=', id)]") dependent_tasks_count = fields.Integer(string="Dependent Tasks", compute='_compute_dependent_tasks_count') + # Project sharing fields + display_parent_task_button = fields.Boolean(compute='_compute_display_parent_task_button', compute_sudo=True) + # recurrence fields allow_recurring_tasks = fields.Boolean(related='project_id.allow_recurring_tasks') recurring_task = fields.Boolean(string="Recurrent") @@ -1470,6 +1474,11 @@ class Task(models.Model): """ return [('id', 'inselect', (query, [f'%{value}%']))] + def _compute_display_parent_task_button(self): + accessible_parent_tasks = self.parent_id.with_user(self.env.user)._filter_access_rules('read') + for task in self: + task.display_parent_task_button = task.parent_id in accessible_parent_tasks + @api.returns('self', lambda value: value.id) def copy(self, default=None): if default is None: @@ -2113,6 +2122,26 @@ class Task(models.Model): 'context': self._context } + def action_project_sharing_view_parent_task(self): + if self.parent_id.project_id != self.project_id and self.user_has_groups('base.group_portal'): + project = self.parent_id.project_id._filter_access_rules_python('read') + if project: + url = f"/my/projects/{self.parent_id.project_id.id}/task/{self.parent_id.id}" + if project._check_project_sharing_access(): + url = f"/my/projects/{self.parent_id.project_id.id}?task_id={self.parent_id.id}" + return { + "name": "Portal Parent Task", + "type": "ir.actions.act_url", + "url": url, + } + elif self.display_parent_task_button: + return self.parent_id.get_portal_url() + # The portal user has no access to the parent task, so normally the button should be invisible. + return {} + action = self.action_open_parent_task() + action['views'] = [(self.env.ref('project.project_sharing_project_task_view_form').id, 'form')] + return action + # ------------ # Actions # ------------ @@ -2126,6 +2155,11 @@ class Task(models.Model): 'context': self._context } + def action_project_sharing_open_task(self): + action = self.action_open_task() + action['views'] = [[self.env.ref('project.project_sharing_project_task_view_form').id, 'form']] + return action + def action_dependent_tasks(self): self.ensure_one() action = { diff --git a/addons/project/static/src/project_sharing/project_sharing.js b/addons/project/static/src/project_sharing/project_sharing.js index 0f9a53db202..98d4118cffd 100644 --- a/addons/project/static/src/project_sharing/project_sharing.js +++ b/addons/project/static/src/project_sharing/project_sharing.js @@ -43,7 +43,7 @@ export class ProjectSharingWebClient extends Component { } async _showView() { - const { action_name, project_id } = session; + const { action_name, project_id, open_task_action } = session; await this.actionService.doAction( action_name, { @@ -53,6 +53,9 @@ export class ProjectSharingWebClient extends Component { } } ); + if (open_task_action) { + await this.actionService.doAction(open_task_action); + } } } diff --git a/addons/project/views/project_sharing_templates.xml b/addons/project/views/project_sharing_templates.xml index 38dda9405d8..3759ba7fcdf 100644 --- a/addons/project/views/project_sharing_templates.xml +++ b/addons/project/views/project_sharing_templates.xml @@ -9,7 +9,7 @@