# -*- coding: utf-8 -*- # Part of Odoo, Flectra. See LICENSE file for full copyright and licensing details. import base64 from flectra import http, _ from flectra.exceptions import AccessError from flectra.http import request from flectra.tools import consteq from flectra.addons.portal.controllers.mail import _message_post_helper from flectra.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager, get_records_pager class CustomerPortal(CustomerPortal): def _prepare_portal_layout_values(self): values = super(CustomerPortal, self)._prepare_portal_layout_values() partner = request.env.user.partner_id SaleOrder = request.env['sale.order'] quotation_count = SaleOrder.search_count([ ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), ('state', 'in', ['sent', 'cancel']) ]) order_count = SaleOrder.search_count([ ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), ('state', 'in', ['sale', 'done']) ]) values.update({ 'quotation_count': quotation_count, 'order_count': order_count, }) return values # # Quotations and Sales Orders # def _order_check_access(self, order_id, access_token=None): order = request.env['sale.order'].browse([order_id]) order_sudo = order.sudo() try: order.check_access_rights('read') order.check_access_rule('read') except AccessError: if not access_token or not consteq(order_sudo.access_token, access_token): raise return order_sudo def _order_get_page_view_values(self, order, access_token, **kwargs): order_invoice_lines = {il.product_id.id: il.invoice_id for il in order.invoice_ids.mapped('invoice_line_ids')} values = { 'order': order, 'order_invoice_lines': order_invoice_lines, } if access_token: values['no_breadcrumbs'] = True values['access_token'] = access_token values['portal_confirmation'] = request.env['ir.config_parameter'].sudo().get_param('sale.sale_portal_confirmation_options', default='none') if kwargs.get('error'): values['error'] = kwargs['error'] if kwargs.get('warning'): values['warning'] = kwargs['warning'] if kwargs.get('success'): values['success'] = kwargs['success'] history = request.session.get('my_orders_history', []) values.update(get_records_pager(history, order)) return values @http.route(['/my/quotes', '/my/quotes/page/'], type='http', auth="user", website=True) def portal_my_quotes(self, page=1, date_begin=None, date_end=None, sortby=None, **kw): values = self._prepare_portal_layout_values() partner = request.env.user.partner_id SaleOrder = request.env['sale.order'] domain = [ ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), ('state', 'in', ['sent', 'cancel']) ] searchbar_sortings = { 'date': {'label': _('Order Date'), 'order': 'date_order desc'}, 'name': {'label': _('Reference'), 'order': 'name'}, 'stage': {'label': _('Stage'), 'order': 'state'}, } # default sortby order if not sortby: sortby = 'date' sort_order = searchbar_sortings[sortby]['order'] archive_groups = self._get_archive_groups('sale.order', domain) if date_begin and date_end: domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)] # count for pager quotation_count = SaleOrder.search_count(domain) # make pager pager = portal_pager( url="/my/quotes", url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby}, total=quotation_count, page=page, step=self._items_per_page ) # search the count to display, according to the pager data quotations = SaleOrder.search(domain, order=sort_order, limit=self._items_per_page, offset=pager['offset']) request.session['my_quotes_history'] = quotations.ids[:100] values.update({ 'date': date_begin, 'quotations': quotations.sudo(), 'page_name': 'quote', 'pager': pager, 'archive_groups': archive_groups, 'default_url': '/my/quotes', 'searchbar_sortings': searchbar_sortings, 'sortby': sortby, }) return request.render("sale.portal_my_quotations", values) @http.route(['/my/orders', '/my/orders/page/'], type='http', auth="user", website=True) def portal_my_orders(self, page=1, date_begin=None, date_end=None, sortby=None, **kw): values = self._prepare_portal_layout_values() partner = request.env.user.partner_id SaleOrder = request.env['sale.order'] domain = [ ('message_partner_ids', 'child_of', [partner.commercial_partner_id.id]), ('state', 'in', ['sale', 'done']) ] searchbar_sortings = { 'date': {'label': _('Order Date'), 'order': 'date_order desc'}, 'name': {'label': _('Reference'), 'order': 'name'}, 'stage': {'label': _('Stage'), 'order': 'state'}, } # default sortby order if not sortby: sortby = 'date' sort_order = searchbar_sortings[sortby]['order'] archive_groups = self._get_archive_groups('sale.order', domain) if date_begin and date_end: domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)] # count for pager order_count = SaleOrder.search_count(domain) # pager pager = portal_pager( url="/my/orders", url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby}, total=order_count, page=page, step=self._items_per_page ) # content according to pager and archive selected orders = SaleOrder.search(domain, order=sort_order, limit=self._items_per_page, offset=pager['offset']) request.session['my_orders_history'] = orders.ids[:100] values.update({ 'date': date_begin, 'orders': orders.sudo(), 'page_name': 'order', 'pager': pager, 'archive_groups': archive_groups, 'default_url': '/my/orders', 'searchbar_sortings': searchbar_sortings, 'sortby': sortby, }) return request.render("sale.portal_my_orders", values) @http.route(['/my/orders/'], type='http', auth="public", website=True) def portal_order_page(self, order=None, access_token=None, **kw): try: order_sudo = self._order_check_access(order, access_token=access_token) except AccessError: return request.redirect('/my') values = self._order_get_page_view_values(order_sudo, access_token, **kw) return request.render("sale.portal_order_page", values) @http.route(['/my/orders/pdf/'], type='http', auth="public", website=True) def portal_order_report(self, order_id, access_token=None, **kw): try: order_sudo = self._order_check_access(order_id, access_token) except AccessError: return request.redirect('/my') # print report as sudo, since it require access to taxes, payment term, ... and portal # does not have those access rights. pdf = request.env.ref('sale.action_report_saleorder').sudo().render_qweb_pdf([order_sudo.id])[0] pdfhttpheaders = [ ('Content-Type', 'application/pdf'), ('Content-Length', len(pdf)), ] return request.make_response(pdf, headers=pdfhttpheaders) def _portal_quote_user_can_accept(self, order_id): return request.env['ir.config_parameter'].sudo().get_param('sale.sale_portal_confirmation_options', default='none') in ('pay', 'sign') @http.route(['/my/quotes/accept'], type='json', auth="public", website=True) def portal_quote_accept(self, res_id, access_token=None, partner_name=None, signature=None): if not self._portal_quote_user_can_accept(res_id): return {'error': _('Operation not allowed')} if not signature: return {'error': _('Signature is missing.')} try: order_sudo = self._order_check_access(res_id, access_token=access_token) except AccessError: return {'error': _('Invalid order')} if order_sudo.state != 'sent': return {'error': _('Order is not in a state requiring customer validation.')} order_sudo.action_confirm() _message_post_helper( res_model='sale.order', res_id=order_sudo.id, message=_('Order signed by %s') % (partner_name,), attachments=[('signature.png', base64.b64decode(signature))] if signature else [], **({'token': access_token} if access_token else {})) return { 'success': _('Your Order has been confirmed.'), 'redirect_url': '/my/orders/%s?%s' % (order_sudo.id, access_token and 'access_token=%s' % order_sudo.access_token or ''), }