|
| 1 | +from collections import OrderedDict |
| 2 | +from markupsafe import Markup |
| 3 | +from operator import itemgetter |
| 4 | + |
| 5 | +from odoo import _, http |
| 6 | +from odoo.http import request |
| 7 | +from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager |
| 8 | +from odoo.osv.expression import OR, AND |
| 9 | +from odoo.tools import groupby as groupbyelem |
| 10 | + |
| 11 | + |
| 12 | +class CustomPortal(CustomerPortal): |
| 13 | + |
| 14 | + #-------------------------------------------------------------------------------# |
| 15 | + # Generalise filter based on super and sub branch |
| 16 | + #-------------------------------------------------------------------------------# |
| 17 | + def _get_searchbar_filters(self): |
| 18 | + partner = request.env.user.partner_id |
| 19 | + filters = OrderedDict({'all': {'label': _('All'), 'domain': []}}) |
| 20 | + |
| 21 | + partner_ids = request.env['res.partner'].search([ |
| 22 | + ('id', 'child_of', partner.id), |
| 23 | + ('is_company', '=', True) |
| 24 | + ]) |
| 25 | + |
| 26 | + for p in partner_ids: |
| 27 | + filters[str(p.id)] = { |
| 28 | + 'label': p.name, |
| 29 | + 'domain': [('partner_id', '=', p.id)], |
| 30 | + } |
| 31 | + return filters |
| 32 | + |
| 33 | + #-------------------------------------------------------------------------------# |
| 34 | + # Add filters for super-branch and sub-branch in each section |
| 35 | + # (sales orders, your invoices, our orders, tickets) |
| 36 | + #-------------------------------------------------------------------------------# |
| 37 | + |
| 38 | + #-------------------------------------------------------------------------------# |
| 39 | + # 1. invoices & bills |
| 40 | + def _get_account_searchbar_filters(self): |
| 41 | + branch_filters = self._get_searchbar_filters() |
| 42 | + filters = OrderedDict({ |
| 43 | + 'all': {'label': _('All'), 'domain': []}, |
| 44 | + 'invoices': {'label': _('Invoices'), |
| 45 | + 'domain': [('move_type', 'in', ('out_invoice', 'out_refund', 'out_receipt'))]}, |
| 46 | + 'bills': {'label': _('Bills'), |
| 47 | + 'domain': [('move_type', 'in', ('in_invoice', 'in_refund', 'in_receipt'))]}, |
| 48 | + }) |
| 49 | + filters.update(branch_filters) |
| 50 | + return filters |
| 51 | + |
| 52 | + #-------------------------------------------------------------------------------# |
| 53 | + # 2. your orders |
| 54 | + def _get_sale_searchbar_filters(self): |
| 55 | + return self._get_searchbar_filters() |
| 56 | + |
| 57 | + def _prepare_sale_portal_rendering_values( |
| 58 | + self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, quotation_page=False, **kwargs |
| 59 | + ): |
| 60 | + SaleOrder = request.env['sale.order'] |
| 61 | + |
| 62 | + if not sortby: |
| 63 | + sortby = 'date' |
| 64 | + |
| 65 | + partner = request.env.user.partner_id |
| 66 | + values = self._prepare_portal_layout_values() |
| 67 | + |
| 68 | + if quotation_page: |
| 69 | + url = "/my/quotes" |
| 70 | + domain = self._prepare_quotations_domain(partner) |
| 71 | + else: |
| 72 | + url = "/my/orders" |
| 73 | + domain = self._prepare_orders_domain(partner) |
| 74 | + |
| 75 | + searchbar_sortings = self._get_sale_searchbar_sortings() |
| 76 | + searchbar_filters = self._get_sale_searchbar_filters() |
| 77 | + if not filterby: |
| 78 | + filterby = 'all' |
| 79 | + domain += searchbar_filters[filterby]['domain'] |
| 80 | + |
| 81 | + sort_order = searchbar_sortings[sortby]['order'] |
| 82 | + |
| 83 | + if date_begin and date_end: |
| 84 | + domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)] |
| 85 | + |
| 86 | + pager_values = portal_pager( |
| 87 | + url=url, |
| 88 | + total=SaleOrder.search_count(domain), |
| 89 | + page=page, |
| 90 | + step=self._items_per_page, |
| 91 | + url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, 'filterby': filterby}, |
| 92 | + ) |
| 93 | + orders = SaleOrder.search(domain, order=sort_order, limit=self._items_per_page, offset=pager_values['offset']) |
| 94 | + |
| 95 | + values.update({ |
| 96 | + 'date': date_begin, |
| 97 | + 'quotations': orders.sudo() if quotation_page else SaleOrder, |
| 98 | + 'orders': orders.sudo() if not quotation_page else SaleOrder, |
| 99 | + 'page_name': 'quote' if quotation_page else 'order', |
| 100 | + 'pager': pager_values, |
| 101 | + 'default_url': url, |
| 102 | + 'searchbar_sortings': searchbar_sortings, |
| 103 | + 'sortby': sortby, |
| 104 | + 'searchbar_filters': searchbar_filters, |
| 105 | + 'filterby': filterby |
| 106 | + }) |
| 107 | + |
| 108 | + return values |
| 109 | + |
| 110 | + #-------------------------------------------------------------------------------# |
| 111 | + # 3. our orders |
| 112 | + @http.route( |
| 113 | + ['/my/purchase', '/my/purchase/page/<int:page>'], type='http', |
| 114 | + auth="user", website=True |
| 115 | + ) |
| 116 | + def portal_my_purchase_orders( |
| 117 | + self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw |
| 118 | + ): |
| 119 | + return self._render_portal( |
| 120 | + "purchase.portal_my_purchase_orders", |
| 121 | + page, date_begin, date_end, sortby, filterby, |
| 122 | + [], |
| 123 | + self._get_searchbar_filters(), |
| 124 | + 'all', |
| 125 | + "/my/purchase", |
| 126 | + 'my_purchases_history', |
| 127 | + 'purchase', |
| 128 | + 'orders' |
| 129 | + ) |
| 130 | + |
| 131 | + #-------------------------------------------------------------------------------# |
| 132 | + # 4. tickets |
| 133 | + def _prepare_my_tickets_values( |
| 134 | + self, page=1, date_begin=None, date_end=None, sortby=None, filterby='all', |
| 135 | + search=None, groupby='none', search_in='content' |
| 136 | + ): |
| 137 | + |
| 138 | + values = self._prepare_portal_layout_values() |
| 139 | + domain = self._prepare_helpdesk_tickets_domain() |
| 140 | + |
| 141 | + searchbar_sortings = { |
| 142 | + 'date': {'label': _('Newest'), 'order': 'create_date desc'}, |
| 143 | + 'reference': {'label': _('Reference'), 'order': 'id desc'}, |
| 144 | + 'name': {'label': _('Subject'), 'order': 'name'}, |
| 145 | + 'user': {'label': _('Assigned to'), 'order': 'user_id'}, |
| 146 | + 'stage': {'label': _('Stage'), 'order': 'stage_id'}, |
| 147 | + 'update': {'label': _('Last Stage Update'), 'order': 'date_last_stage_update desc'}, |
| 148 | + } |
| 149 | + searchbar_filters = self._get_searchbar_filters() |
| 150 | + searchbar_inputs = { |
| 151 | + 'content': { |
| 152 | + 'input': 'content', |
| 153 | + 'label': Markup(_('Search <span class="nolabel"> (in Content)</span>')) |
| 154 | + }, |
| 155 | + 'ticket_ref': {'input': 'ticket_ref', 'label': _('Search in Reference')}, |
| 156 | + 'message': {'input': 'message', 'label': _('Search in Messages')}, |
| 157 | + 'user': {'input': 'user', 'label': _('Search in Assigned to')}, |
| 158 | + 'status': {'input': 'status', 'label': _('Search in Stage')}, |
| 159 | + } |
| 160 | + searchbar_groupby = { |
| 161 | + 'none': {'input': 'none', 'label': _('None')}, |
| 162 | + 'stage': {'input': 'stage_id', 'label': _('Stage')}, |
| 163 | + 'user': {'input': 'user_id', 'label': _('Assigned to')}, |
| 164 | + } |
| 165 | + |
| 166 | + # default sort by value |
| 167 | + if not sortby: |
| 168 | + sortby = 'date' |
| 169 | + order = searchbar_sortings[sortby]['order'] |
| 170 | + if groupby in searchbar_groupby and groupby != 'none': |
| 171 | + order = f'{searchbar_groupby[groupby]["input"]}, {order}' |
| 172 | + |
| 173 | + if filterby in ['last_message_sup', 'last_message_cust']: |
| 174 | + discussion_subtype_id = request.env.ref('mail.mt_comment').id |
| 175 | + messages = request.env['mail.message'].search_read( |
| 176 | + [('model', '=', 'helpdesk.ticket'), ('subtype_id', '=', discussion_subtype_id)], |
| 177 | + fields=['res_id', 'author_id'], order='date desc' |
| 178 | + ) |
| 179 | + |
| 180 | + last_author_dict = {} |
| 181 | + for message in messages: |
| 182 | + if message['res_id'] not in last_author_dict: |
| 183 | + last_author_dict[message['res_id']] = message['author_id'][0] |
| 184 | + |
| 185 | + ticket_author_list = request.env['helpdesk.ticket'].search_read(fields=['id', 'partner_id']) |
| 186 | + ticket_author_dict = { |
| 187 | + ticket['id']: ticket['partner_id'][0] if ticket['partner_id'] else False |
| 188 | + for ticket in ticket_author_list |
| 189 | + } |
| 190 | + |
| 191 | + last_message_cust = [] |
| 192 | + last_message_sup = [] |
| 193 | + ticket_ids = set(last_author_dict.keys()) & set(ticket_author_dict.keys()) |
| 194 | + for ticket_id in ticket_ids: |
| 195 | + if last_author_dict[ticket_id] == ticket_author_dict[ticket_id]: |
| 196 | + last_message_cust.append(ticket_id) |
| 197 | + else: |
| 198 | + last_message_sup.append(ticket_id) |
| 199 | + |
| 200 | + if filterby == 'last_message_cust': |
| 201 | + domain = AND([domain, [('id', 'in', last_message_cust)]]) |
| 202 | + else: |
| 203 | + domain = AND([domain, [('id', 'in', last_message_sup)]]) |
| 204 | + |
| 205 | + else: |
| 206 | + domain = AND([domain, searchbar_filters[filterby]['domain']]) |
| 207 | + |
| 208 | + if date_begin and date_end: |
| 209 | + domain = AND([domain, [('create_date', '>', date_begin), ('create_date', '<=', date_end)]]) |
| 210 | + |
| 211 | + # Search |
| 212 | + if search and search_in: |
| 213 | + search_domain = [] |
| 214 | + if search_in == 'ticket_ref': |
| 215 | + search_domain = OR([search_domain, [('ticket_ref', 'ilike', search)]]) |
| 216 | + elif search_in == 'content': |
| 217 | + search_domain = OR([ |
| 218 | + search_domain, ['|', ('name', 'ilike', search), ('description', 'ilike', search)] |
| 219 | + ]) |
| 220 | + elif search_in == 'user': |
| 221 | + assignees = request.env['res.users'].sudo()._search([('name', 'ilike', search)]) |
| 222 | + search_domain = OR([search_domain, [('user_id', 'in', assignees)]]) |
| 223 | + elif search_in == 'message': |
| 224 | + discussion_subtype_id = request.env.ref('mail.mt_comment').id |
| 225 | + search_domain = OR([ |
| 226 | + search_domain, [('message_ids.body', 'ilike', search), |
| 227 | + ('message_ids.subtype_id', '=', discussion_subtype_id)] |
| 228 | + ]) |
| 229 | + elif search_in == 'status': |
| 230 | + search_domain = OR([search_domain, [('stage_id', 'ilike', search)]]) |
| 231 | + |
| 232 | + domain = AND([domain, search_domain]) |
| 233 | + |
| 234 | + # Pager |
| 235 | + tickets_count = request.env['helpdesk.ticket'].search_count(domain) |
| 236 | + pager = portal_pager( |
| 237 | + url="/my/tickets", |
| 238 | + url_args={ |
| 239 | + 'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, |
| 240 | + 'search_in': search_in, 'search': search, 'groupby': groupby, 'filterby': filterby |
| 241 | + }, |
| 242 | + total=tickets_count, page=page, step=self._items_per_page |
| 243 | + ) |
| 244 | + |
| 245 | + tickets = request.env['helpdesk.ticket'].search( |
| 246 | + domain, order=order, limit=self._items_per_page, offset=pager['offset'] |
| 247 | + ) |
| 248 | + request.session['my_tickets_history'] = tickets.ids[:100] |
| 249 | + |
| 250 | + # Group tickets if needed |
| 251 | + if not tickets: |
| 252 | + grouped_tickets = [] |
| 253 | + elif groupby != 'none': |
| 254 | + grouped_tickets = [ |
| 255 | + request.env['helpdesk.ticket'].concat(*g) |
| 256 | + for k, g in groupbyelem(tickets, itemgetter(searchbar_groupby[groupby]['input'])) |
| 257 | + ] |
| 258 | + else: |
| 259 | + grouped_tickets = [tickets] |
| 260 | + |
| 261 | + values.update({ |
| 262 | + 'date': date_begin, |
| 263 | + 'grouped_tickets': grouped_tickets, |
| 264 | + 'page_name': 'ticket', |
| 265 | + 'default_url': '/my/tickets', |
| 266 | + 'pager': pager, |
| 267 | + 'searchbar_sortings': searchbar_sortings, |
| 268 | + 'searchbar_filters': searchbar_filters, |
| 269 | + 'searchbar_inputs': searchbar_inputs, |
| 270 | + 'searchbar_groupby': searchbar_groupby, |
| 271 | + 'sortby': sortby, |
| 272 | + 'groupby': groupby, |
| 273 | + 'search_in': search_in, |
| 274 | + 'search': search, |
| 275 | + 'filterby': filterby, |
| 276 | + }) |
| 277 | + return values |
0 commit comments