# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from operator import itemgetter
from markupsafe import Markup
from odoo import http
from odoo.exceptions import AccessError, MissingError, UserError
from odoo.http import request
from odoo.tools.translate import _
from odoo.tools import groupby as groupbyelem
from odoo.addons.portal.controllers import portal
from odoo.addons.portal.controllers.portal import pager as portal_pager
from odoo.osv.expression import AND, FALSE_DOMAIN
class CustomerPortal(portal.CustomerPortal):
def _prepare_portal_layout_values(self):
values = super(CustomerPortal, self)._prepare_portal_layout_values()
return values
def _prepare_home_portal_values(self, counters):
values = super()._prepare_home_portal_values(counters)
if 'ticket_count' in counters:
values['ticket_count'] = (
request.env['helpdesk.ticket'].search_count(self._prepare_helpdesk_tickets_domain())
if request.env['helpdesk.ticket'].has_access('read')
else 0
)
return values
def _prepare_helpdesk_tickets_domain(self):
return []
def _ticket_get_page_view_values(self, ticket, access_token, **kwargs):
values = {
'page_name': 'ticket',
'ticket': ticket,
'ticket_link_section': [],
'ticket_closed': kwargs.get('ticket_closed', False),
'preview_object': ticket,
}
return self._get_page_view_values(ticket, access_token, values, 'my_tickets_history', False, **kwargs)
def _ticket_get_searchbar_inputs(self):
return {
'name': {'input': 'name', 'label': _(
'Search%(left)s Tickets%(right)s',
left=Markup(''),
right=Markup(''),
), 'sequence': 10},
'user_id': {'input': 'user_id', 'label': _('Search in Assigned to'), 'sequence': 20},
'partner_id': {'input': 'partner_id', 'label': _('Search in Customer'), 'sequence': 30},
'team_id': {'input': 'team_id', 'label': _('Search in Helpdesk Team'), 'sequence': 40},
'stage_id': {'input': 'stage_id', 'label': _('Search in Stage'), 'sequence': 50},
}
def _ticket_get_searchbar_groupby(self):
return {
'none': {'label': _('None'), 'sequence': 10},
'user_id': {'label': _('Assigned to'), 'sequence': 20},
'team_id': {'label': _('Helpdesk Team'), 'sequence': 30},
'stage_id': {'label': _('Stage'), 'sequence': 40},
'kanban_state': {'label': _('Status'), 'sequence': 50},
'partner_id': {'label': _('Customer'), 'sequence': 60},
}
def _ticket_get_search_domain(self, search_in, search):
if search_in == 'name':
return ['|', ('name', 'ilike', search), ('ticket_ref', 'ilike', search)]
elif search_in == 'user_id':
assignees = request.env['res.users'].sudo()._search([('name', 'ilike', search)])
return [('user_id', 'in', assignees)]
elif search_in in self._ticket_get_searchbar_inputs():
return [(search_in, 'ilike', search)]
else:
return FALSE_DOMAIN
def _prepare_my_tickets_values(self, page=1, date_begin=None, date_end=None, sortby=None, filterby='all', search=None, groupby='none', search_in='name'):
values = self._prepare_portal_layout_values()
domain = self._prepare_helpdesk_tickets_domain()
searchbar_sortings = {
'create_date desc': {'label': _('Newest')},
'id desc': {'label': _('Reference')},
'name': {'label': _('Subject')},
'user_id': {'label': _('Assigned to')},
'stage_id': {'label': _('Stage')},
'date_last_stage_update desc': {'label': _('Last Stage Update')},
}
searchbar_filters = {
'all': {'label': _('All'), 'domain': []},
'assigned': {'label': _('Assigned'), 'domain': [('user_id', '!=', False)]},
'unassigned': {'label': _('Unassigned'), 'domain': [('user_id', '=', False)]},
'open': {'label': _('Open'), 'domain': [('close_date', '=', False)]},
'closed': {'label': _('Closed'), 'domain': [('close_date', '!=', False)]},
}
searchbar_inputs = dict(sorted(self._ticket_get_searchbar_inputs().items(), key=lambda item: item[1]['sequence']))
searchbar_groupby = dict(sorted(self._ticket_get_searchbar_groupby().items(), key=lambda item: item[1]['sequence']))
# default sort by value
if not sortby:
sortby = 'create_date desc'
domain = AND([domain, searchbar_filters[filterby]['domain']])
if date_begin and date_end:
domain = AND([domain, [('create_date', '>', date_begin), ('create_date', '<=', date_end)]])
# search
if search and search_in:
domain = AND([domain, self._ticket_get_search_domain(search_in, search)])
# pager
tickets_count = request.env['helpdesk.ticket'].search_count(domain)
pager = portal_pager(
url="/my/tickets",
url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, 'search_in': search_in, 'search': search, 'groupby': groupby, 'filterby': filterby},
total=tickets_count,
page=page,
step=self._items_per_page
)
order = f'{groupby}, {sortby}' if groupby != 'none' else sortby
tickets = request.env['helpdesk.ticket'].search(domain, order=order, limit=self._items_per_page, offset=pager['offset'])
request.session['my_tickets_history'] = tickets.ids[:100]
if not tickets:
grouped_tickets = []
elif groupby != 'none':
grouped_tickets = [request.env['helpdesk.ticket'].concat(*g) for k, g in groupbyelem(tickets, itemgetter(groupby))]
else:
grouped_tickets = [tickets]
values.update({
'date': date_begin,
'grouped_tickets': grouped_tickets,
'page_name': 'ticket',
'default_url': '/my/tickets',
'pager': pager,
'searchbar_sortings': searchbar_sortings,
'searchbar_filters': searchbar_filters,
'searchbar_inputs': searchbar_inputs,
'searchbar_groupby': searchbar_groupby,
'sortby': sortby,
'groupby': groupby,
'search_in': search_in,
'search': search,
'filterby': filterby,
})
return values
@http.route(['/my/tickets', '/my/tickets/page/'], type='http', auth="user", website=True)
def my_helpdesk_tickets(self, page=1, date_begin=None, date_end=None, sortby=None, filterby='all', search=None, groupby='none', search_in='name', **kw):
values = self._prepare_my_tickets_values(page, date_begin, date_end, sortby, filterby, search, groupby, search_in)
return request.render("helpdesk.portal_helpdesk_ticket", values)
@http.route([
"/helpdesk/ticket/",
"/helpdesk/ticket//",
'/my/ticket/',
'/my/ticket//'
], type='http', auth="public", website=True)
def tickets_followup(self, ticket_id=None, access_token=None, **kw):
try:
ticket_sudo = self._document_check_access('helpdesk.ticket', ticket_id, access_token)
except (AccessError, MissingError):
return request.redirect('/my')
values = self._ticket_get_page_view_values(ticket_sudo, access_token, **kw)
return request.render("helpdesk.tickets_followup", values)
@http.route([
'/my/ticket/close/',
'/my/ticket/close//',
], type='http', auth="public", website=True)
def ticket_close(self, ticket_id=None, access_token=None, **kw):
try:
ticket_sudo = self._document_check_access('helpdesk.ticket', ticket_id, access_token)
except (AccessError, MissingError):
return request.redirect('/my')
if not ticket_sudo.team_id.allow_portal_ticket_closing:
raise UserError(_("The team does not allow ticket closing through portal"))
if not ticket_sudo.closed_by_partner:
closing_stage = ticket_sudo.team_id._get_closing_stage()
if ticket_sudo.stage_id != closing_stage:
ticket_sudo.write({'stage_id': closing_stage[0].id, 'closed_by_partner': True})
else:
ticket_sudo.write({'closed_by_partner': True})
body = _('Ticket closed by the customer')
ticket_sudo.with_context(mail_create_nosubscribe=True).message_post(body=body, message_type='comment', subtype_xmlid='mail.mt_note')
return request.redirect('/my/ticket/%s/%s?ticket_closed=1' % (ticket_id, access_token or ''))