[IMP] project: add subtasks stat button in project sharing form view
Before this commit, the "Sub-tasks" has been added in the basic form view of project.task in backend. This commit adds this stat button in the project sharing form view to have the same button than the one in backend. task-2648955 Closes #82379
This commit is contained in:
@@ -11,7 +11,7 @@ from odoo.http import request
|
||||
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager
|
||||
from odoo.tools import groupby as groupbyelem
|
||||
|
||||
from odoo.osv.expression import OR
|
||||
from odoo.osv.expression import OR, AND
|
||||
|
||||
from odoo.addons.web.controllers.main import HomeStaticTemplateHelpers
|
||||
|
||||
@@ -177,6 +177,38 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
values['project'] = project_sudo
|
||||
return request.render("project.portal_my_task", values)
|
||||
|
||||
@http.route('/my/projects/<int:project_id>/task/<int:task_id>/subtasks', type='http', auth='user', methods=['GET'], website=True)
|
||||
def portal_my_project_subtasks(self, project_id, task_id, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, search=None, search_in='content', groupby=None, **kw):
|
||||
try:
|
||||
project_sudo = self._document_check_access('project.project', project_id)
|
||||
task_sudo = request.env['project.task'].search([('project_id', '=', project_id), ('id', '=', task_id)]).sudo()
|
||||
task_domain = [('id', 'child_of', task_id), ('id', '!=', task_id)]
|
||||
searchbar_filters = self._get_my_tasks_searchbar_filters([('id', '=', task_sudo.project_id.id)], task_domain)
|
||||
|
||||
if not filterby:
|
||||
filterby = 'all'
|
||||
domain = searchbar_filters.get(filterby, searchbar_filters.get('all'))['domain']
|
||||
|
||||
values = self._prepare_tasks_values(page, date_begin, date_end, sortby, search, search_in, groupby, url=f'/my/projects/{project_id}/task/{task_id}/subtasks', domain=AND([task_domain, domain]))
|
||||
values['page_name'] = 'project_subtasks'
|
||||
|
||||
# pager
|
||||
pager_vals = values['pager']
|
||||
pager_vals['url_args'].update(filterby=filterby)
|
||||
pager = portal_pager(**pager_vals)
|
||||
|
||||
values.update({
|
||||
'project': project_sudo,
|
||||
'task': task_sudo,
|
||||
'grouped_tasks': values['grouped_tasks'](pager['offset']),
|
||||
'pager': pager,
|
||||
'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())),
|
||||
'filterby': filterby,
|
||||
})
|
||||
return request.render("project.portal_my_tasks", values)
|
||||
except (AccessError, MissingError):
|
||||
return request.not_found()
|
||||
|
||||
# ------------------------------------------------------------
|
||||
# My Task
|
||||
# ------------------------------------------------------------
|
||||
@@ -357,14 +389,13 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
})
|
||||
return values
|
||||
|
||||
@http.route(['/my/tasks', '/my/tasks/page/<int:page>'], type='http', auth="user", website=True)
|
||||
def portal_my_tasks(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, search=None, search_in='content', groupby=None, **kw):
|
||||
def _get_my_tasks_searchbar_filters(self, project_domain=None, task_domain=None):
|
||||
searchbar_filters = {
|
||||
'all': {'label': _('All'), 'domain': []},
|
||||
}
|
||||
|
||||
# extends filterby criteria with project the customer has access to
|
||||
projects = request.env['project.project'].search([])
|
||||
projects = request.env['project.project'].search(project_domain or [])
|
||||
for project in projects:
|
||||
searchbar_filters.update({
|
||||
str(project.id): {'label': project.name, 'domain': [('project_id', '=', project.id)]}
|
||||
@@ -372,7 +403,7 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
|
||||
# extends filterby criteria with project (criteria name is the project id)
|
||||
# Note: portal users can't view projects they don't follow
|
||||
project_groups = request.env['project.task'].read_group([('project_id', 'not in', projects.ids)],
|
||||
project_groups = request.env['project.task'].read_group(AND([[('project_id', 'not in', projects.ids)], task_domain or []]),
|
||||
['project_id'], ['project_id'])
|
||||
for group in project_groups:
|
||||
proj_id = group['project_id'][0] if group['project_id'] else False
|
||||
@@ -380,6 +411,11 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
searchbar_filters.update({
|
||||
str(proj_id): {'label': proj_name, 'domain': [('project_id', '=', proj_id)]}
|
||||
})
|
||||
return searchbar_filters
|
||||
|
||||
@http.route(['/my/tasks', '/my/tasks/page/<int:page>'], type='http', auth="user", website=True)
|
||||
def portal_my_tasks(self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, search=None, search_in='content', groupby=None, **kw):
|
||||
searchbar_filters = self._get_my_tasks_searchbar_filters()
|
||||
|
||||
if not filterby:
|
||||
filterby = 'all'
|
||||
@@ -406,7 +442,7 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
raise MissingError(_('There is nothing to report.'))
|
||||
|
||||
@http.route(['/my/tasks/<int:task_id>'], type='http', auth="public", website=True)
|
||||
def portal_my_task(self, task_id, report_type=None, access_token=None, **kw):
|
||||
def portal_my_task(self, task_id, report_type=None, access_token=None, project_sharing=False, **kw):
|
||||
try:
|
||||
task_sudo = self._document_check_access('project.task', task_id, access_token)
|
||||
except (AccessError, MissingError):
|
||||
@@ -418,5 +454,9 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
# ensure attachment are accessible with access token inside template
|
||||
for attachment in task_sudo.attachment_ids:
|
||||
attachment.generate_access_token()
|
||||
if project_sharing is True:
|
||||
# Then the user arrives to the stat button shown in form view of project.task and the portal user can see only 1 task
|
||||
# so the history should be reset.
|
||||
request.session['my_tasks_history'] = task_sudo.ids
|
||||
values = self._task_get_page_view_values(task_sudo, access_token, **kw)
|
||||
return request.render("project.portal_my_task", values)
|
||||
|
||||
@@ -2160,6 +2160,22 @@ class Task(models.Model):
|
||||
action['views'] = [[self.env.ref('project.project_sharing_project_task_view_form').id, 'form']]
|
||||
return action
|
||||
|
||||
def action_project_sharing_open_subtasks(self):
|
||||
self.ensure_one()
|
||||
subtasks = self.env['project.task'].search([('id', 'child_of', self.id), ('id', '!=', self.id)])
|
||||
if subtasks.project_id == self.project_id:
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('project.project_sharing_project_task_action_sub_task')
|
||||
if len(subtasks) == 1:
|
||||
action['view_mode'] = 'form'
|
||||
action['views'] = [(view_id, view_type) for view_id, view_type in action['views'] if view_type == 'form']
|
||||
action['res_id'] = subtasks.id
|
||||
return action
|
||||
return {
|
||||
'name': 'Portal Sub-tasks',
|
||||
'type': 'ir.actions.act_url',
|
||||
'url': f'/my/projects/{self.project_id.id}/task/{self.id}/subtasks' if len(subtasks) > 1 else subtasks.get_portal_url(query_string='project_sharing=1'),
|
||||
}
|
||||
|
||||
def action_dependent_tasks(self):
|
||||
self.ensure_one()
|
||||
action = {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<a t-if="project" t-attf-href="/my/projects?{{ keep_query() }}">Projects</a>
|
||||
<t t-else="">Projects</t>
|
||||
</li>
|
||||
<li t-if="page_name == 'project_task' and project" class="breadcrumb-item active">
|
||||
<li t-if="page_name in ['project_task', 'project_subtasks'] and project" class="breadcrumb-item active">
|
||||
<a t-if="project" t-attf-href="/my/projects/{{ project.id }}?{{ keep_query() }}"><t t-esc="project.name"/></a>
|
||||
</li>
|
||||
<li t-elif="project" t-attf-class="breadcrumb-item #{'active ' if not project else ''} text-truncate col-8 col-lg-10">
|
||||
@@ -16,9 +16,19 @@
|
||||
<a t-if="task" t-attf-href="/my/tasks?{{ keep_query() }}">Tasks</a>
|
||||
<t t-else="">Tasks</t>
|
||||
</li>
|
||||
<li t-if="task" class="breadcrumb-item active text-truncate">
|
||||
<li t-if="page_name == 'project_subtasks' and task and project" class="breadcrumb-item active">
|
||||
<a t-attf-href="/my/projects/{{ project.id }}/task/{{ task.id }}?{{ keep_query() }}"><t t-esc="task.name"/></a>
|
||||
</li>
|
||||
<li t-elif="task" class="breadcrumb-item active text-truncate">
|
||||
<span t-field="task.name"/>
|
||||
</li>
|
||||
<li t-if="page_name == 'project_subtasks' or (task and subtask and project)" t-attf-class="breadcrumb-item text-truncate #{'active ' if not subtask else ''}">
|
||||
<a t-if="subtask" t-attf-href="/my/tasks/{{ task.id }}/subtasks?{{ keep_query() }}">Sub-tasks</a>
|
||||
<t t-else="">Sub-tasks</t>
|
||||
</li>
|
||||
<li t-if="subtask" class="breadcrumb-item active text-truncate">
|
||||
<span t-field="subtask.name"/>
|
||||
</li>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -161,6 +161,15 @@
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<field name="display_parent_task_button" invisible="1"/>
|
||||
<button name="action_project_sharing_view_parent_task" type="object" class="oe_stat_button" icon="fa-tasks" string="Parent Task" attrs="{'invisible': [('display_parent_task_button', '=', False)]}"/>
|
||||
<button name="action_project_sharing_open_subtasks" type="object" class="oe_stat_button" icon="fa-tasks"
|
||||
attrs="{'invisible' : ['|', '|', ('allow_subtasks', '=', False), ('id', '=', False), ('subtask_count', '=', 0)]}" context="{'default_user_ids': [(6, 0, [uid])]}">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value">
|
||||
<field name="subtask_count" widget="statinfo" nolabel="1"/>
|
||||
</span>
|
||||
<span class="o_stat_text">Sub-tasks</span>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="bg-danger" attrs="{'invisible': [('active', '=', True)]}"/>
|
||||
<div class="oe_title pr-0">
|
||||
@@ -305,4 +314,39 @@
|
||||
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_project_task_action_sub_task" model="ir.actions.act_window">
|
||||
<field name="name">Sub-tasks</field>
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_mode">tree,kanban,form</field>
|
||||
<field name="search_view_id" ref="project.project_sharing_project_task_view_search"/>
|
||||
<field name="domain">[('id', 'child_of', active_id), ('id', '!=', active_id)]</field>
|
||||
<field name="context">{'default_parent_id': active_id}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No tasks found. Let's create one!
|
||||
</p><p>
|
||||
To get things done, use activities and status on tasks.<br/>
|
||||
Chat in real time or by email to collaborate efficiently.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_subtasks_tree_action_view" model="ir.actions.act_window.view">
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
|
||||
<field name="view_id" ref="project.project_sharing_project_task_view_tree"/>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_subtasks_kanban_action_view" model="ir.actions.act_window.view">
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
|
||||
<field name="view_id" ref="project.project_sharing_project_task_view_kanban"/>
|
||||
</record>
|
||||
|
||||
<record id="project_sharing_subtasks_form_action_view" model="ir.actions.act_window.view">
|
||||
<field name="view_mode">form</field>
|
||||
<field name="act_window_id" ref="project.project_sharing_project_task_action_sub_task"/>
|
||||
<field name="view_id" ref="project.project_sharing_project_task_view_form"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user