# -*- 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 OR, AND 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'].check_access_rights('read', raise_exception=False) 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 _prepare_my_tickets_values(self, page=1, date_begin=None, date_end=None, sortby=None, filterby='all', search=None, groupby='none', search_in='content'): values = self._prepare_portal_layout_values() domain = self._prepare_helpdesk_tickets_domain() searchbar_sortings = { 'date': {'label': _('Newest'), 'order': 'create_date desc'}, 'reference': {'label': _('Reference'), 'order': 'id desc'}, 'name': {'label': _('Subject'), 'order': 'name'}, 'user': {'label': _('Assigned to'), 'order': 'user_id'}, 'stage': {'label': _('Stage'), 'order': 'stage_id'}, 'update': {'label': _('Last Stage Update'), 'order': 'date_last_stage_update desc'}, } 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 = { 'content': {'input': 'content', 'label': Markup(_('Search (in Content)'))}, 'ticket_ref': {'input': 'ticket_ref', 'label': _('Search in Reference')}, 'message': {'input': 'message', 'label': _('Search in Messages')}, 'user': {'input': 'user', 'label': _('Search in Assigned to')}, 'status': {'input': 'status', 'label': _('Search in Stage')}, } searchbar_groupby = { 'none': {'input': 'none', 'label': _('None')}, 'stage': {'input': 'stage_id', 'label': _('Stage')}, 'user': {'input': 'user_id', 'label': _('Assigned to')}, } # default sort by value if not sortby: sortby = 'date' order = searchbar_sortings[sortby]['order'] if groupby in searchbar_groupby and groupby != 'none': order = f'{searchbar_groupby[groupby]["input"]}, {order}' if filterby in ['last_message_sup', 'last_message_cust']: discussion_subtype_id = request.env.ref('mail.mt_comment').id messages = request.env['mail.message'].search_read([('model', '=', 'helpdesk.ticket'), ('subtype_id', '=', discussion_subtype_id)], fields=['res_id', 'author_id'], order='date desc') last_author_dict = {} for message in messages: if message['res_id'] not in last_author_dict: last_author_dict[message['res_id']] = message['author_id'][0] ticket_author_list = request.env['helpdesk.ticket'].search_read(fields=['id', 'partner_id']) ticket_author_dict = dict([(ticket_author['id'], ticket_author['partner_id'][0] if ticket_author['partner_id'] else False) for ticket_author in ticket_author_list]) last_message_cust = [] last_message_sup = [] ticket_ids = set(last_author_dict.keys()) & set(ticket_author_dict.keys()) for ticket_id in ticket_ids: if last_author_dict[ticket_id] == ticket_author_dict[ticket_id]: last_message_cust.append(ticket_id) else: last_message_sup.append(ticket_id) if filterby == 'last_message_cust': domain = AND([domain, [('id', 'in', last_message_cust)]]) else: domain = AND([domain, [('id', 'in', last_message_sup)]]) else: 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: search_domain = [] if search_in == 'ticket_ref': search_domain = OR([search_domain, [('ticket_ref', 'ilike', search)]]) if search_in == 'content': search_domain = OR([search_domain, ['|', ('name', 'ilike', search), ('description', 'ilike', search)]]) if search_in == 'user': assignees = request.env['res.users'].sudo()._search([('name', 'ilike', search)]) search_domain = OR([search_domain, [('user_id', 'in', assignees)]]) if search_in == 'message': discussion_subtype_id = request.env.ref('mail.mt_comment').id search_domain = OR([search_domain, [('message_ids.body', 'ilike', search), ('message_ids.subtype_id', '=', discussion_subtype_id)]]) if search_in == 'status': search_domain = OR([search_domain, [('stage_id', 'ilike', search)]]) domain = AND([domain, search_domain]) # 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 ) 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(searchbar_groupby[groupby]['input']))] 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='content', **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 ''))