[IMP] website_quote: use generic portal signature widget
Online quote can now use the generic signature widget already used on the standard sale customer portal. We therefore remove the custom widget build in website_quote. A route already exists to sign quotes; the old route is removed. The existing route is overridden so that quotations with template requiring payment cannot be signed. This is the expected behavior.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import base64
|
||||
|
||||
import werkzeug
|
||||
|
||||
@@ -23,6 +22,19 @@ class CustomerPortal(CustomerPortal):
|
||||
return request.redirect('/quote/%s/%s' % (order, access_token or ''))
|
||||
return super(CustomerPortal, self).portal_order_page(order=order, access_token=access_token, **kw)
|
||||
|
||||
@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):
|
||||
try:
|
||||
order_sudo = self._order_check_access(res_id, access_token=access_token)
|
||||
except exceptions.AccessError:
|
||||
pass
|
||||
else:
|
||||
if order_sudo.require_payment:
|
||||
return {
|
||||
'error': _('Order is not in a state requiring customer validation.')
|
||||
}
|
||||
return super(CustomerPortal, self).portal_quote_accept(res_id, access_token=access_token, partner_name=partner_name, signature=signature)
|
||||
|
||||
|
||||
class sale_quote(http.Controller):
|
||||
|
||||
@@ -102,19 +114,6 @@ class sale_quote(http.Controller):
|
||||
values.update(get_records_pager(history, order_sudo))
|
||||
return request.render('website_quote.so_quotation', values)
|
||||
|
||||
@http.route(['/quote/accept'], type='json', auth="public", website=True)
|
||||
def accept(self, order_id, token=None, signer=None, sign=None, **post):
|
||||
Order = request.env['sale.order'].sudo().browse(order_id)
|
||||
if token != Order.access_token or Order.require_payment:
|
||||
return request.render('website.404')
|
||||
if Order.state != 'sent':
|
||||
return False
|
||||
attachments = [('signature.png', base64.b64decode(sign))] if sign else []
|
||||
Order.action_confirm()
|
||||
message = _('Order signed by %s') % (signer,)
|
||||
_message_post_helper(message=message, res_id=order_id, res_model='sale.order', attachments=attachments, **({'token': token, 'token_field': 'access_token'} if token else {}))
|
||||
return True
|
||||
|
||||
@http.route(['/quote/<int:order_id>/<token>/decline'], type='http', auth="public", methods=['POST'], website=True)
|
||||
def decline(self, order_id, token, **post):
|
||||
Order = request.env['sale.order'].sudo().browse(order_id)
|
||||
|
||||
@@ -46,77 +46,6 @@ if(!$('.o_website_quote').length) {
|
||||
update_button_list.push(button);
|
||||
});
|
||||
|
||||
// Accept Modal, with jSignature
|
||||
var AcceptModal = Widget.extend({
|
||||
events: {
|
||||
'shown.bs.modal': 'initSignature',
|
||||
'click #sign_clean': 'clearSignature',
|
||||
'submit #accept': 'submitForm',
|
||||
},
|
||||
initSignature: function(ev){
|
||||
this.$("#signature").empty().jSignature({'decor-color' : '#D1D0CE', 'color': '#000', 'background-color': '#fff'});
|
||||
this.empty_sign = this.$("#signature").jSignature("getData",'image');
|
||||
},
|
||||
clearSignature: function(ev){
|
||||
this.$("#signature").jSignature('reset');
|
||||
},
|
||||
submitForm: function(ev){
|
||||
// extract data
|
||||
var self = this;
|
||||
var $confirm_btn = self.$el.find('button[type="submit"]');
|
||||
|
||||
// Support 2 routes:
|
||||
// - <form id="accept" method="POST" t-attf-action="/quote/accept/#{quotation.id}/?token=#{quotation.access_token}" ...>
|
||||
// - <form id="accept" method="POST" t-att-data-order-id="quotation.id" t-att-data-token="quotation.access_token" ...>
|
||||
// The first route is deprecated but might still be used if the template is not updated
|
||||
var href = self.$el.find('form').attr("action");
|
||||
var action, order_id, token;
|
||||
if (href) {
|
||||
action = href.match(/quote\/([a-z]+)/)[1];
|
||||
order_id = parseInt(href.match(/quote\/[a-z]+\/([0-9]+)/)[1]);
|
||||
token = href.match(/token=(.*)/) && href.match(/token=(.*)/)[1];
|
||||
}
|
||||
else {
|
||||
action = 'accept';
|
||||
order_id = self.$el.find('form').data("order-id");
|
||||
token = self.$el.find('form').data("token");
|
||||
}
|
||||
|
||||
if (action === 'accept') {
|
||||
ev.preventDefault();
|
||||
// process : display errors, or submit
|
||||
var signer_name = self.$("#name").val();
|
||||
var signature = self.$("#signature").jSignature("getData",'image');
|
||||
var is_empty = signature ? this.empty_sign[1] === signature[1] : false;
|
||||
self.$('#signer').toggleClass('has-error', !signer_name);
|
||||
self.$('#drawsign').toggleClass('panel-danger', is_empty).toggleClass('panel-default', !is_empty);
|
||||
if (is_empty || ! signer_name){
|
||||
setTimeout(function () {
|
||||
self.$('button[type="submit"], a.a-submit').removeAttr('data-loading-text').button('reset');
|
||||
});
|
||||
return false;
|
||||
}
|
||||
$confirm_btn.prepend('<i class="fa fa-spinner fa-spin"></i> ');
|
||||
$confirm_btn.attr('disabled', true);
|
||||
ajax.jsonRpc("/quote/"+action, 'call', {
|
||||
'order_id': order_id,
|
||||
'token': token,
|
||||
'signer': signer_name,
|
||||
'sign': signature?JSON.stringify(signature[1]):false,
|
||||
}).then(function (data) {
|
||||
var message_id = (data) ? 3 : 4;
|
||||
self.$el.modal('hide');
|
||||
window.location.href = '/quote/'+order_id.toString()+'/'+token+'?message='+message_id;
|
||||
});
|
||||
return false;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
var accept_modal = new AcceptModal();
|
||||
accept_modal.setElement($('#modalaccept'));
|
||||
accept_modal.start();
|
||||
|
||||
// Nav Menu ScrollSpy
|
||||
var NavigationSpyMenu = Widget.extend({
|
||||
start: function(watched_selector){
|
||||
|
||||
@@ -292,23 +292,19 @@
|
||||
<h4 class="modal-title">Validate Order</h4>
|
||||
</div>
|
||||
<div class="modal-body" id="sign-dialog">
|
||||
<p>
|
||||
<span>I agree that by signing this proposal, I
|
||||
accept it on the behalf of </span><b t-field="quotation.partner_id.commercial_partner_id"/><span>, for an amount of </span>
|
||||
<b data-id="total_amount" t-field="quotation.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
|
||||
<span>with payment terms: </span><b t-field="quotation.payment_term_id"/>.
|
||||
</p>
|
||||
<div id="signer" class="form-group">
|
||||
<label class="control-label" for="name">Your Name:</label>
|
||||
<input type="text" name="signer" id="name" class="form-control"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div t-if="not (pms or s2s_acquirers or form_acquirers)">
|
||||
<button type="submit" t-attf-class="btn btn-primary">Sign & Confirm</button>
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
|
||||
</div>
|
||||
<p>
|
||||
<span>I agree that by signing this proposal, I
|
||||
accept it on the behalf of </span><b t-field="quotation.partner_id.commercial_partner_id"/><span>, for an amount of </span>
|
||||
<b data-id="total_amount" t-field="quotation.amount_total"
|
||||
t-options='{"widget": "monetary", "display_currency": quotation.pricelist_id.currency_id}'/>
|
||||
<span>with payment terms: </span><b t-field="quotation.payment_term_id"/>.
|
||||
</p>
|
||||
<t t-call="portal.portal_signature">
|
||||
<t t-set="object" t-value="quotation"/>
|
||||
<t t-set="partner_name" t-value="quotation.partner_id.name"/>
|
||||
<t t-set="callUrl" t-value="'/my/quotes/accept'"/>
|
||||
<t t-set="accessToken" t-value="quotation.access_token"/>
|
||||
</t>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@@ -534,21 +530,6 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- Options:Quotation Signature -->
|
||||
<template id="opt_quotation_signature" name="Ask Signature" customize_show="True" inherit_id="website_quote.so_quotation">
|
||||
<xpath expr="//div[@id='sign-dialog']" position="inside">
|
||||
<div class="panel panel-default mt16 mb0" id="drawsign">
|
||||
<div class="panel-heading">
|
||||
<div class="pull-right">
|
||||
<a id="sign_clean" class="btn btn-xs">Clear</a>
|
||||
</div>
|
||||
<strong>Draw your signature</strong>
|
||||
</div>
|
||||
<div id="signature" class="panel-body" style="padding: 0"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="optional_products">
|
||||
<t t-if="any([(not option.line_id) for option in quotation.options])">
|
||||
<section>
|
||||
|
||||
Reference in New Issue
Block a user