[IMP] sale, website_quote: allow portal payment after signature

Both features already existed but it was not possible to enable both.
This makes that possible. When both are enabled the quotation first has to be
signed, then it can be paid.

Purpose
=======
No way to combine digital signature and online payments to confirm orders.
A customer signature matters for big deals, in order to keep an official print
of customer agreement.

Specs
=====
- when the customer signs online, a new pdf is generated with the customer
  signature on it and posted as a note in the chatter, replacing the current
  signature jpg.

- show the signature in the portal view, with both sale_management and
  website_quote module

- change in settings: Digital Signature and Electronic Payment are now 2
  checkboxes instead of a radio button

- these settings must be company-specific (add company icon if multi-companies,
  like other company-specific settings)

- when both modes are activated, it first request the signature, then the
  payment

- the order is confirmed after payment

opw-1818837
Closes #24174
This commit is contained in:
Joren Van Onder
2018-06-28 18:47:51 +02:00
committed by Romain Derie
parent 803162537b
commit 2fa469c5e5
15 changed files with 180 additions and 90 deletions
+19 -10
View File
@@ -200,16 +200,11 @@ class CustomerPortal(CustomerPortal):
]
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')
def _portal_quote_user_can_accept(self, order):
return order.company_id.portal_confirmation_sign or order.company_id.portal_confirmation_pay
@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:
@@ -217,15 +212,29 @@ class CustomerPortal(CustomerPortal):
if order_sudo.state != 'sent':
return {'error': _('Order is not in a state requiring customer validation.')}
order_sudo.action_confirm()
if not self._portal_quote_user_can_accept(order_sudo):
return {'error': _('Operation not allowed')}
if not signature:
return {'error': _('Signature is missing.')}
success_message = _('Your order has been signed but still needs to be paid to be confirmed.')
if not order_sudo.has_to_be_paid():
order_sudo.action_confirm()
success_message = _('Your order has been confirmed.')
order_sudo.signature = signature
order_sudo.signed_by = partner_name
pdf = request.env.ref('sale.action_report_saleorder').sudo().render_qweb_pdf([order_sudo.id])[0]
_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 [],
attachments=[('%s.pdf' % order_sudo.name, pdf)],
**({'token': access_token} if access_token else {}))
return {
'success': _('Your Order has been confirmed.'),
'success': success_message,
'redirect_url': '/my/orders/%s?%s' % (order_sudo.id, access_token and 'access_token=%s' % order_sudo.access_token or ''),
}
+9 -1
View File
@@ -69,8 +69,16 @@
% set is_online = access_action and access_action['type'] == 'ir.actions.act_url'
% set doc_name = 'quotation' if object.state in ('draft', 'sent') else 'order confirmation'
% set pay_sign_name = object.get_portal_confirmation_action()
% set access_name = is_online and object.state in ('draft', 'sent') and pay_sign_name in ('pay', 'sign') and 'Accept and %s online' % pay_sign_name or 'View %s' % doc_name
% set access_url = is_online and object.get_mail_url() or ''
% set access_name = 'View %s' % doc_name
% if is_online and object.state in ('draft', 'sent') and pay_sign_name in ('pay', 'sign'):
% if pay_sign_name == 'sign' and object.has_to_be_paid():
% set access_name = 'Sign and pay online'
% else:
% set access_name = 'Accept and %s online' % pay_sign_name
% endif
% endif
% if object.partner_id.parent_id:
(<i>${object.partner_id.parent_id.name}</i>)
+2
View File
@@ -8,3 +8,5 @@ class ResCompany(models.Model):
_inherit = "res.company"
sale_note = fields.Text(string='Default Terms and Conditions', translate=True)
portal_confirmation_sign = fields.Boolean(string='Digital Signature', default=True)
portal_confirmation_pay = fields.Boolean(string='Electronic Payment')
+6 -17
View File
@@ -18,12 +18,9 @@ class ResConfigSettings(models.TransientModel):
module_sale_margin = fields.Boolean("Margins")
group_sale_layout = fields.Boolean("Sections on Sales Orders", implied_group='sale.group_sale_layout')
group_warning_sale = fields.Boolean("Sale Order Warnings", implied_group='sale.group_warning_sale')
portal_confirmation = fields.Boolean('Online Signature & Payment')
portal_confirmation_options = fields.Selection([
('sign', 'Signature'),
('pay', 'Payment')], string="Online Signature & Payment options",
config_parameter='sale.sale_portal_confirmation_options')
module_sale_payment = fields.Boolean("Sale Payment", help='Technical field implied by user choice of online_confirmation')
portal_confirmation_sign = fields.Boolean(related='company_id.portal_confirmation_sign', string='Digital Signature')
portal_confirmation_pay = fields.Boolean(related='company_id.portal_confirmation_pay', string='Electronic Payment')
module_sale_payment = fields.Boolean("Sale Payment", help='Technical field implied by user choice of portal_confirmation_pay.')
module_website_quote = fields.Boolean("Quotations Templates")
group_sale_delivery_address = fields.Boolean("Customer Addresses", implied_group='sale.group_delivery_invoice_address')
multi_sales_price = fields.Boolean("Multiple Sales Prices per Product")
@@ -131,16 +128,9 @@ class ResConfigSettings(models.TransientModel):
'group_pricelist_item': False,
})
@api.onchange('portal_confirmation')
def _onchange_portal_confirmation(self):
if not self.portal_confirmation:
self.portal_confirmation_options = False
elif not self.portal_confirmation_options:
self.portal_confirmation_options = 'sign'
@api.onchange('portal_confirmation_options')
def _onchange_portal_confirmation_options(self):
if self.portal_confirmation_options == 'pay':
@api.onchange('portal_confirmation_pay')
def _onchange_portal_confirmation_pay(self):
if self.portal_confirmation_pay:
self.module_sale_payment = True
@api.model
@@ -152,6 +142,5 @@ class ResConfigSettings(models.TransientModel):
multi_sales_price=sale_pricelist_setting in ['percentage', 'formula'],
multi_sales_price_method=sale_pricelist_setting in ['percentage', 'formula'] and sale_pricelist_setting or False,
sale_pricelist_setting=sale_pricelist_setting,
portal_confirmation=ICPSudo.get_param('sale.sale_portal_confirmation_options', default=False) in ('pay', 'sign'),
)
return res
+17 -1
View File
@@ -163,6 +163,9 @@ class SaleOrder(models.Model):
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('sale.order'))
team_id = fields.Many2one('crm.team', 'Sales Channel', change_default=True, default=_get_default_team, oldname='section_id')
signature = fields.Binary('Signature', help='Signature received through the portal.', copy=False, attachment=True)
signed_by = fields.Char('Signed by', help='Name of the person that signed the SO.', copy=False)
product_id = fields.Many2one('product.product', related='order_line.product_id', string='Product')
def _compute_portal_url(self):
@@ -488,6 +491,8 @@ class SaleOrder(models.Model):
orders = self.filtered(lambda s: s.state in ['cancel', 'sent'])
return orders.write({
'state': 'draft',
'signature': False,
'signed_by': False,
})
@api.multi
@@ -673,7 +678,18 @@ class SaleOrder(models.Model):
return self.get_share_url()
def get_portal_confirmation_action(self):
return self.env['ir.config_parameter'].sudo().get_param('sale.sale_portal_confirmation_options', default='none')
if self.company_id.portal_confirmation_sign and not self.signature:
return 'sign'
if self.company_id.portal_confirmation_pay:
return 'pay'
return 'none'
def has_to_be_signed(self):
return self.company_id.portal_confirmation_sign
def has_to_be_paid(self):
return self.company_id.portal_confirmation_pay
@api.multi
def _notify_get_groups(self, message, groups):
@@ -202,6 +202,19 @@
<strong>Fiscal Position Remark:</strong>
<span t-field="doc.fiscal_position_id.note"/>
</p>
<div t-if="doc.signature" class="row mt32" name="signature">
<div class="col-xs-offset-8 col-xs-4">
<strong>Signature</strong>
</div>
<div class="col-xs-offset-8 col-xs-4">
<img t-att-src="'data:image/png;base64,%s' % to_text(doc.signature)" style="max-height: 4cm; max-width: 8cm;"/>
</div>
<div class="col-xs-offset-8 col-xs-4 text-center">
<p t-field="doc.signed_by"/>
</div>
</div>
<div class="oe_structure"/>
</div>
</t>
+25 -18
View File
@@ -139,6 +139,31 @@
</div>
<h2>Quotations &amp; Orders</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" id="sale_config_online_confirmation_sign">
<div class="o_setting_left_pane">
<field name="portal_confirmation_sign"/>
</div>
<div class="o_setting_right_pane">
<label for="portal_confirmation_sign"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Request a digital signature to confirm orders
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="sale_config_online_confirmation_pay">
<div class="o_setting_left_pane">
<field name="portal_confirmation_pay"/>
</div>
<div class="o_setting_right_pane">
<field name="module_sale_payment" invisible="1"/>
<label for="portal_confirmation_pay"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Request an electronic payment to confirm orders
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
</div>
@@ -154,24 +179,6 @@
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="sale_config_online_confirmation">
<div class="o_setting_left_pane">
<field name="portal_confirmation"/>
</div>
<div class="o_setting_right_pane">
<field name="module_sale_payment" invisible="1"/>
<label for="portal_confirmation"/>
<div class="text-muted">
Let your customers sign &amp; pay online
</div>
<div class="content-group" attrs="{'invisible': [('portal_confirmation', '=', False)]}">
<div class="mt16">
<field name="portal_confirmation_options" class="o_light_label" widget="radio"
attrs="{'required': [('portal_confirmation', '=', True)]}"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_website_quote"/>
+2 -2
View File
@@ -5,8 +5,8 @@
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="sale.res_config_settings_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='portal_confirmation_options']/.." position="after">
<div attrs="{'invisible': [('portal_confirmation_options', '!=', 'pay')]}">
<xpath expr="//label[@for='portal_confirmation_pay']/.." position="inside">
<div attrs="{'invisible': [('portal_confirmation_pay', '=', False)]}">
<button name='%(payment.action_payment_acquirer)d' icon="fa-arrow-right" type="action" string="Payment Acquirers" class="btn-link"/>
</div>
</xpath>
+4 -5
View File
@@ -24,11 +24,10 @@ 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)
def _portal_quote_user_can_accept(self, order_id):
result = super(CustomerPortal, self)._portal_quote_user_can_accept(order_id)
order_sudo = request.env['sale.order'].sudo().browse(order_id)
def _portal_quote_user_can_accept(self, order):
result = super(CustomerPortal, self)._portal_quote_user_can_accept(order)
# either use quote template settings or fallback on default behavior
return not order_sudo.require_payment if order_sudo.template_id else result
return order.require_signature if order.template_id else result
class sale_quote(http.Controller):
@@ -85,7 +84,7 @@ class sale_quote(http.Controller):
'partner_id': order_sudo.partner_id.id,
}
if order_sudo.require_payment or values['need_payment']:
if order_sudo.has_to_be_paid() or values['need_payment']:
domain = expression.AND([
['&', ('website_published', '=', True), ('company_id', '=', order_sudo.company_id.id)],
['|', ('specific_countries', '=', False), ('country_ids', 'in', [order_sudo.partner_id.country_id.id])]
+42 -16
View File
@@ -6,6 +6,7 @@ from datetime import datetime, timedelta
from odoo import api, fields, models, _
from odoo.tools.translate import html_translate
from odoo.addons import decimal_precision as dp
from odoo.exceptions import ValidationError
from werkzeug.urls import url_encode
@@ -51,16 +52,19 @@ class SaleOrder(models.Model):
template = self.env.ref('website_quote.website_quote_template_default', raise_if_not_found=False)
return template and template.active and template or False
def _get_default_online_payment(self):
def _get_default_require_signature(self):
default_template = self._get_default_template()
if self.template_id:
return self.template_id.require_payment
elif default_template:
return default_template.require_payment
elif self.env['ir.config_parameter'].sudo().get_param('sale.sale_portal_confirmation_options', default='none') == 'pay':
return 1
if default_template:
return default_template.require_signature
else:
return 0
return False
def _get_default_require_payment(self):
default_template = self._get_default_template()
if default_template:
return default_template.require_payment
else:
return False
template_id = fields.Many2one(
'sale.quote.template', 'Quotation Template',
@@ -75,12 +79,12 @@ class SaleOrder(models.Model):
amount_undiscounted = fields.Float(
'Amount Before Discount', compute='_compute_amount_undiscounted', digits=0)
quote_viewed = fields.Boolean('Quotation Viewed')
require_payment = fields.Selection([
(0, 'Online Signature'),
(1, 'Online Payment')], default=_get_default_online_payment, string='Confirmation Mode',
help="Choose how you want to confirm an order to launch the delivery process. You can either "
"request a digital signature or an upfront payment. With a digital signature, you can "
"request the payment when issuing the invoice.")
require_signature = fields.Boolean('Digital Signature', default=_get_default_require_signature,
states={'sale': [('readonly', True)], 'done': [('readonly', True)]},
help='Request a digital signature to the customer in order to confirm orders automatically.')
require_payment = fields.Boolean('Electronic Payment', default=_get_default_require_payment,
states={'sale': [('readonly', True)], 'done': [('readonly', True)]},
help='Request an electronic payment to the customer in order to confirm orders automatically.')
@api.multi
@api.returns('self', lambda value: value.id)
@@ -113,6 +117,8 @@ class SaleOrder(models.Model):
@api.onchange('template_id')
def onchange_template_id(self):
if not self.template_id:
self.require_signature = False
self.require_payment = False
return
template = self.template_id.with_context(lang=self.partner_id.lang)
@@ -170,11 +176,18 @@ class SaleOrder(models.Model):
self.validity_date = fields.Date.to_string(datetime.now() + timedelta(template.number_of_days))
self.website_description = template.website_description
self.require_signature = template.require_signature
self.require_payment = template.require_payment
if template.note:
self.note = template.note
@api.constrains('template_id', 'require_signature', 'require_payment')
def _check_portal_confirmation(self):
for order in self.sudo().filtered('template_id'):
if not order.require_signature and not order.require_payment:
raise ValidationError(_('Please select a confirmation mode in Other Information: Digital Signature, Electronic Payment or both.'))
@api.multi
def open_quotation(self):
self.ensure_one()
@@ -209,10 +222,23 @@ class SaleOrder(models.Model):
def get_portal_confirmation_action(self):
""" Template override default behavior of pay / sign chosen in sales settings """
if self.require_payment is not None or self.require_payment is not False:
return 'pay' if self.require_payment == 1 else 'sign'
if self.template_id:
if self.require_signature and not self.signature:
return 'sign'
elif self.require_payment:
return 'pay'
else:
return 'none'
return super(SaleOrder, self).get_portal_confirmation_action()
def has_to_be_signed(self):
res = super(SaleOrder, self).has_to_be_signed()
return self.require_signature if self.template_id else res
def has_to_be_paid(self):
res = super(SaleOrder, self).has_to_be_paid()
return self.require_payment if self.template_id else res
@api.multi
def action_confirm(self):
res = super(SaleOrder, self).action_confirm()
+11 -8
View File
@@ -1,9 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import api, fields, models, _
from odoo.tools.translate import html_translate
from odoo.addons import decimal_precision as dp
from odoo.exceptions import ValidationError
class SaleQuoteTemplate(models.Model):
@@ -17,18 +18,20 @@ class SaleQuoteTemplate(models.Model):
options = fields.One2many('sale.quote.option', 'template_id', 'Optional Products Lines', copy=True)
number_of_days = fields.Integer('Quotation Duration',
help='Number of days for the validity date computation of the quotation')
require_payment = fields.Selection([
(0, 'Online Signature'),
(1, 'Online Payment')], default=0, string='Confirmation Mode',
help="Choose how you want to confirm an order to launch the delivery process. You can either "
"request a digital signature or an upfront payment. With a digital signature, you can "
"request the payment when issuing the invoice.")
require_signature = fields.Boolean('Digital Signature', default=True, help='Request a digital signature to the customer in order to confirm orders automatically.')
require_payment = fields.Boolean('Electronic Payment', help='Request an electronic payment to the customer in order to confirm orders automatically.')
mail_template_id = fields.Many2one(
'mail.template', 'Confirmation Mail',
domain=[('model', '=', 'sale.order')],
help="This e-mail template will be sent on confirmation. Leave empty to send nothing.")
active = fields.Boolean(default=True, help="If unchecked, it will allow you to hide the quotation template without removing it.")
@api.constrains('require_signature', 'require_payment')
def _check_confirmation(self):
for template in self:
if not template.require_signature and not template.require_payment:
raise ValidationError(_('Please select a confirmation mode in Confirmation: Digital Signature, Electronic Payment or both.'))
@api.multi
def open_template(self):
self.ensure_one()
@@ -37,7 +37,8 @@
</div>
</xpath>
<xpath expr="//field[@name='client_order_ref']" position="after">
<field name="require_payment" widget="radio"/>
<field name="require_signature" attrs="{'invisible': [('template_id', '=', False)]}"/>
<field name="require_payment" attrs="{'invisible': [('template_id', '=', False)]}"/>
<field name="website_description" invisible="1"/>
</xpath>
</field>
@@ -94,7 +94,8 @@
</page>
<page string="Confirmation">
<group>
<field name="require_payment" widget="radio"/>
<field name="require_signature"/>
<field name="require_payment"/>
<field name="mail_template_id"/>
</group>
</page>
@@ -145,6 +145,20 @@
</section>
</template>
<template id="signature" name="Signature">
<div t-if="quotation.signature" class="row mt64" name="signature">
<div class="col-xs-offset-8 col-xs-4">
<strong>Signature</strong>
</div>
<div class="col-xs-offset-8 col-xs-4">
<img t-att-src="'data:image/png;base64,%s' % to_text(quotation.signature)" style="max-height: 4cm; max-width: 8cm;"/>
</div>
<div class="col-xs-offset-8 col-xs-4 text-center">
<p t-field="quotation.signed_by"/>
</div>
</div>
</template>
<template id="change_quantity" inherit_id="website_quote.pricing" active="False" customize_show="True" name="Change Quantity">
<xpath expr="//div[@id='quote_qty']" position="replace">
<div class="input-group oe_website_spinner">
@@ -202,11 +216,11 @@
<div class="col-md-3 hidden-print">
<div class="bs-sidebar">
<div class="text-center" t-if="quotation.state in ('draft', 'sent', 'waiting_date')" style="padding: 10px">
<a role="button" t-if="order_valid and not quotation.require_payment" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept">
<a role="button" t-if="order_valid and not quotation.signature and quotation.has_to_be_signed()" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept">
<span class="fa fa-check"> Accept Order</span>
</a>
<a role="button" t-if="order_valid and quotation.require_payment" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept">
<span class="fa fa-check"> Accept &amp; Pay</span>
<a role="button" t-elif="order_valid and quotation.has_to_be_paid()" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept">
<span class="fa fa-check"> <t t-if="not quotation.signature">Accept &amp; </t>Pay</span>
</a>
<a role="button" t-if="not order_valid" href="#discussion" class="btn btn-info btn-block o_normalwhitespace">
<strong>This offer expired!</strong><br/>
@@ -218,7 +232,7 @@
</a>
</div>
</div>
<div class="text-center" t-if="need_payment or (quotation.state in ('sale') and quotation.require_payment and tx_state != 'done')" style="padding: 10px">
<div class="text-center" t-if="need_payment or (quotation.state in ('sale') and quotation.has_to_be_paid() and tx_state != 'done')" style="padding: 10px">
<a role="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept">
<span class="fa fa-check"> Pay Now</span>
</a>
@@ -301,7 +315,7 @@
<!-- modal relative to the actions Accept/Reject/Cancel -->
<div role="dialog" class="modal fade" id="modalaccept">
<div class="modal-dialog" t-if="not quotation.require_payment and not need_payment">
<div class="modal-dialog" t-if="not quotation.signature and quotation.has_to_be_signed()">
<form id="accept" method="POST" t-att-data-order-id="quotation.id" t-att-data-token="quotation.access_token" class="js_accept_json modal-content js_website_submit_form">
<input type="hidden" name="csrf_token" t-att-value="request.csrf_token()"/>
<header class="modal-header">
@@ -326,7 +340,7 @@
</form>
</div>
<div class="modal-dialog modal-content" t-if="quotation.require_payment or need_payment">
<div class="modal-dialog modal-content" t-if="not (quotation.has_to_be_signed() and not quotation.signature) and (quotation.has_to_be_paid() or need_payment)">
<header class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close">&amp;times;</button>
<h4 class="modal-title">Validate Order</h4>
@@ -386,17 +400,17 @@
<t t-call="website_quote.so_quotation_content"/>
<div class="text-center hidden-print mt16 mb16" id="quotation_toolbar" t-if="quotation.state in ('draft', 'sent', 'waiting_date')">
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="order_valid and not quotation.require_payment">
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="order_valid and not quotation.has_to_be_paid()">
<span class="fa fa-check"> Accept</span>
</a>
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="order_valid and quotation.require_payment">
<a role="button" class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="order_valid and quotation.has_to_be_paid()">
<span class="fa fa-check"> Accept &amp; Pay</span>
</a>
<a role="button" class="btn btn-danger" data-toggle="modal" data-target="#modaldecline">
<span class="fa fa-times"> Reject</span>
</a>
</div>
<div class="text-center" t-if="need_payment or (quotation.state in ('sale') and quotation.require_payment and tx_state != 'done')" style="padding: 10px">
<div class="text-center" t-if="need_payment or (quotation.state in ('sale') and quotation.has_to_be_paid() and tx_state != 'done')" style="padding: 10px">
<a role="button" class="btn btn-primary" data-toggle="modal" data-target="#modalaccept">
<span class="fa fa-check"> Pay Now</span>
</a>
@@ -535,6 +549,8 @@
<div class="oe_structure"/>
<!-- Pricing -->
<t t-call="website_quote.pricing"/>
<!-- Signature -->
<t t-call="website_quote.signature"/>
<!-- Options -->
<t t-call="website_quote.optional_products"/>
</template>
@@ -11,7 +11,7 @@
Show the automatic invoices setting in sales settings.
-->
<div class="col-xs-12 col-md-6 o_setting_box"
attrs="{'invisible': ['|', ('default_invoice_policy', '!=', 'order'), '&amp;', ('portal_confirmation_options', '!=', 'pay'), ('module_website_quote', '=', False)]}">
attrs="{'invisible': ['|', ('default_invoice_policy', '!=', 'order'), '&amp;', ('portal_confirmation_pay', '=', False), ('module_website_quote', '=', False)]}">
<div class="o_setting_left_pane">
<field name="automatic_invoice" nolabel="1"/>
</div>