Files
Odoo-Modules/fusion_projects/fusion_project_portal/controllers/portal.py
gsinghpal 6ac6d24da6 CHANGES
2026-04-28 19:43:16 -04:00

245 lines
9.5 KiB
Python

from odoo import http, _
from odoo.exceptions import AccessError, MissingError
from odoo.http import request
from odoo.addons.project.controllers.portal import ProjectCustomerPortal
class FusionProjectCustomerPortal(ProjectCustomerPortal):
def _task_get_searchbar_sortings(self, milestones_allowed, project=False):
values = super()._task_get_searchbar_sortings(milestones_allowed, project)
values['sequence, id'] = {
'label': _('Manual'), 'order': 'sequence, id', 'sequence': 5,
}
return values
def _project_get_page_view_values(self, project, access_token, page=1, date_begin=None,
date_end=None, sortby=None, search=None,
search_in='content', groupby=None, **kwargs):
values = super()._project_get_page_view_values(
project, access_token, page, date_begin, date_end,
sortby, search, search_in, groupby, **kwargs,
)
values['can_create_task'] = self._fp_can_create_task(project)
groups = values.get('grouped_tasks')
depth_map = {}
if isinstance(groups, list):
new_groups = []
for tasks in groups:
if not tasks:
new_groups.append(tasks)
continue
ordered_ids = self._fp_hierarchical_order(tasks, depth_map)
new_groups.append(tasks.browse(ordered_ids))
values['grouped_tasks'] = new_groups
values['fp_task_depth'] = depth_map
return values
def _fp_hierarchical_order(self, tasks, depth_map):
task_ids = set(tasks.ids)
children = {tid: [] for tid in task_ids}
roots = []
for t in tasks:
if t.parent_id and t.parent_id.id in task_ids:
children[t.parent_id.id].append(t.id)
else:
roots.append((t.sequence, t.id))
roots.sort()
all_tasks_by_id = {t.id: t for t in tasks}
for tid in children:
children[tid].sort(key=lambda cid: (all_tasks_by_id[cid].sequence, cid))
order = []
def walk(tid, depth):
order.append(tid)
depth_map[tid] = depth
for child in children.get(tid, []):
walk(child, depth + 1)
for _seq, r in roots:
walk(r, 0)
return order
def _task_get_page_view_values(self, task, access_token, **kwargs):
values = super()._task_get_page_view_values(task, access_token, **kwargs)
descendants = []
depth_map = {}
Task = request.env['project.task'].sudo()
def walk(parent_id, depth):
children = Task.search(
[('parent_id', '=', parent_id)],
order='sequence, id',
)
for c in children:
descendants.append(c)
depth_map[c.id] = depth
walk(c.id, depth + 1)
walk(task.id, 0)
values['fp_task_descendants'] = descendants
values['fp_task_depth_inside'] = depth_map
values['fp_can_create_task'] = self._fp_can_create_task(task.project_id)
values['fp_priority_options'] = request.env['project.task']._fields['priority']._description_selection(request.env)
timesheets = request.env['account.analytic.line'].sudo().search(
[('task_id', '=', task.id)],
order='date desc, id desc',
)
values['fp_timesheets'] = timesheets
values['fp_timesheets_total_hours'] = sum(timesheets.mapped('unit_amount'))
values['fp_state_options'] = task._fields['state']._description_selection(request.env)
values['fp_state_label'] = dict(values['fp_state_options']).get(task.state, task.state)
return values
def _fp_can_create_task(self, project):
user = request.env.user
if not project or not project.exists():
return False
if not user or user.id == request.env.ref('base.public_user').id:
return False
if user._is_internal():
return True
partner = user.partner_id
if not partner:
return False
if project.partner_id and (
partner == project.partner_id
or partner.parent_id == project.partner_id
or partner.commercial_partner_id == project.partner_id.commercial_partner_id
):
return True
follower = request.env['mail.followers'].sudo().search_count([
('res_model', '=', 'project.project'),
('res_id', '=', project.id),
('partner_id', '=', partner.id),
], limit=1)
return bool(follower)
@http.route(
['/my/projects/<int:project_id>/task/<int:task_id>/state'],
type='http', auth='user', website=True, methods=['POST'],
csrf=True,
)
def fp_portal_task_set_state(self, project_id, task_id, **post):
try:
project_sudo = self._document_check_access('project.project', project_id)
except (AccessError, MissingError):
return request.redirect('/my')
if not self._fp_can_create_task(project_sudo):
return request.redirect(f'/my/projects/{project_id}/task/{task_id}')
task = request.env['project.task'].sudo().search([
('id', '=', task_id),
('project_id', '=', project_id),
], limit=1)
if not task:
return request.redirect(f'/my/projects/{project_id}')
new_state = post.get('state')
allowed = ['01_in_progress', '02_changes_requested', '03_approved', '1_done']
if new_state not in allowed:
return request.redirect(f'/my/projects/{project_id}/task/{task_id}')
comment = (post.get('comment') or '').strip()
partner = request.env.user.partner_id
labels = dict(task._fields['state']._description_selection(request.env))
label = labels.get(new_state, new_state)
body = f'Status set to <b>{label}</b> by {partner.name} from the customer portal.'
if comment:
body += f'<br/><br/><b>Comment:</b><br/>{comment}'
task.write({'state': new_state})
task.message_post(
body=body,
author_id=partner.id,
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
return request.redirect(f'/my/projects/{project_id}/task/{task_id}')
@http.route(
[
'/my/projects/<int:project_id>/task/new',
'/my/projects/<int:project_id>/task/<int:parent_task_id>/subtask/new',
],
type='http', auth='user', website=True, methods=['GET', 'POST'],
csrf=True,
)
def fp_portal_task_new(self, project_id, parent_task_id=None, **post):
try:
project_sudo = self._document_check_access('project.project', project_id)
except (AccessError, MissingError):
return request.redirect('/my')
if not self._fp_can_create_task(project_sudo):
return request.redirect(f'/my/projects/{project_id}')
parent_task = None
if parent_task_id:
parent_task = request.env['project.task'].sudo().search([
('id', '=', parent_task_id),
('project_id', '=', project_id),
], limit=1)
if not parent_task:
return request.redirect(f'/my/projects/{project_id}')
priority_selection = request.env['project.task']._fields['priority']._description_selection(request.env)
render_values = {
'project': project_sudo,
'parent_task': parent_task,
'priority_options': priority_selection,
'errors': {},
'values': {},
'page_name': 'fp_new_task',
}
if request.httprequest.method == 'POST':
name = (post.get('name') or '').strip()
description = (post.get('description') or '').strip()
priority = post.get('priority') or '0'
if priority not in dict(priority_selection):
priority = '0'
errors = {}
if not name:
errors['name'] = 'Please enter a title.'
if errors:
if parent_task:
return request.redirect(f'/my/projects/{project_id}/task/{parent_task.id}')
render_values.update({'errors': errors, 'values': post})
return request.render('fusion_project_portal.portal_new_task_form', render_values)
partner = request.env.user.partner_id
vals = {
'name': name,
'description': description,
'priority': priority,
'project_id': project_sudo.id,
'partner_id': project_sudo.partner_id.id or partner.id,
'message_partner_ids': [(4, partner.id)],
}
if parent_task:
vals['parent_id'] = parent_task.id
vals['display_in_project'] = False
task = request.env['project.task'].sudo().create(vals)
task.sudo().message_post(
body=f'Submitted from customer portal by {partner.name}.',
author_id=partner.id,
message_type='comment',
subtype_xmlid='mail.mt_comment',
)
if parent_task:
return request.redirect(f'/my/projects/{project_id}/task/{parent_task.id}')
return request.redirect(f'/my/projects/{project_id}/task/{task.id}')
return request.render('fusion_project_portal.portal_new_task_form', render_values)