[IMP] sale: support quote confirmation and sign in customer portal

Purpose is to allow customers to accept and confirm orders on the
customer portal without having to rely on advanced website_quote
(online quotation) features. Including :

 * add quote confirmation route;
 * add support in customer portal using the newly-introduced signature
   widget;
 * add action button on the order page view;
This commit is contained in:
Thibault Delavallée
2017-09-06 10:39:05 +02:00
parent 3275aacb27
commit a3e048a001
3 changed files with 64 additions and 6 deletions
+35 -1
View File
@@ -1,11 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import base64
from odoo import http, _
from odoo.exceptions import AccessError
from odoo.http import request
from odoo.tools import consteq
from odoo.addons.portal.controllers.mail import _message_post_helper
from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager, get_records_pager
@@ -54,6 +56,8 @@ class CustomerPortal(CustomerPortal):
}
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']
@@ -64,6 +68,7 @@ class CustomerPortal(CustomerPortal):
history = request.session.get('my_orders_history', [])
values.update(get_records_pager(history, order))
return values
@http.route(['/my/quotes', '/my/quotes/page/<int:page>'], type='http', auth="user", website=True)
@@ -192,3 +197,32 @@ class CustomerPortal(CustomerPortal):
('Content-Length', len(pdf)),
]
return request.make_response(pdf, headers=pdfhttpheaders)
@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 request.env['ir.config_parameter'].sudo().get_param(
'sale.sale_portal_confirmation_options', default='none') not in ('pay', 'sign'):
return False
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, 'token_field': '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 ''),
}
+3 -3
View File
@@ -7,8 +7,8 @@
</template>
<template id="assets_frontend" inherit_id="web.assets_frontend" name="Sale frontend assets">
<xpath expr="." position="inside">
<link rel='stylesheet' href='/sale/static/src/less/sale_portal.less'/>
</xpath>
<xpath expr="." position="inside">
<link rel='stylesheet' href='/sale/static/src/less/sale_portal.less'/>
</xpath>
</template>
</odoo>
+26 -2
View File
@@ -131,6 +131,8 @@
<template id="portal_order_page" name="Sales Order">
<t t-call="portal.portal_layout">
<t t-set="sales_order" t-value="order"/> <!-- rename to ensure breadcrumb compatibility (differentiate with purchase orders) -->
<t t-set="invoices" t-value="[i for i in order.invoice_ids if i.state not in ['draft', 'cancel']]"/>
<t t-set="invoices_amount" t-value="sum(i.amount_total for i in invoices)"/>
<div id="optional_placeholder"></div>
<div class="container">
<div class="panel panel-default">
@@ -145,7 +147,17 @@
</h4>
</div>
<div class="col-md-6 col-sm-6 col-xs-12 hidden-print" id="o_portal_order_actions">
<a target="_blank" t-att-href="'/my/orders/pdf/%s?%s' % (order.id, keep_query())" class="pull-right" title="Download">
<t t-if="order.state == 'sent' and portal_confirmation == 'sign'" name="portal_confirmation_sign">
<a class="btn btn-primary ml8 pull-right" href="#portal_sign">
<i class="fa fa-arrow-circle-right"/> Accept &amp; Sign
</a>
</t>
<t t-if="order.state == 'sale' and portal_confirmation == 'sign'">
<a class="btn btn-success ml8 pull-right" disabled="disabled">
<i class="fa fa-check-circle"/> Signed
</a>
</t>
<a target="_blank" t-att-href="'/my/orders/pdf/%s?%s' % (order.id, keep_query())" class="ml8 pull-right" title="Download">
<i class="fa fa-download fa-2x" aria-hidden="true"/>
</a>
</div>
@@ -165,7 +177,6 @@
<div>
<address t-field="order.partner_invoice_id" t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'/>
</div>
<t t-set="invoices" t-value="[i for i in order.invoice_ids if i.state not in ['draft', 'cancel']]"/>
<t t-if="invoices">
<div>
<strong>Invoices</strong>
@@ -277,6 +288,19 @@
</div>
</div>
</div>
<div class="panel-body">
<div class="row" t-if="order.state == 'sent' and portal_confirmation == 'sign'" id="portal_sign">
<div class="col-md-offset-3 col-md-6">
<h4>Accept order</h4>
<t t-call="portal.portal_signature">
<t t-set="object" t-value="order"/>
<t t-set="partner_name" t-value="order.partner_id.name"/>
<t t-set="callUrl" t-value="'/my/quotes/accept'"/>
<t t-set="accessToken" t-value="order.access_token"/>
</t>
</div>
</div>
</div>
</div>
</div>
<div class="oe_structure mb32"/>