diff options
| author | Damien Bouvy <dbo@odoo.com> | 2015-04-16 12:11:02 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2015-06-15 14:57:16 +0200 |
| commit | de8d90e12edfb9f965e7c145c48a6f0c86bb7d32 (patch) | |
| tree | 2e67c00fb10a0fe02cc94cacd1bf61747a538a34 | |
| parent | 3dafbe9aa8777e5f33c51eea8a72229b55f11249 (diff) | |
[IMP] website_quote: add payment management
Until now, you could only sign and confirm a sale order from the frontend.
From now on, you can also pay sale orders if the 'Immediate Payment'
option is checked on the sale order.
| -rw-r--r-- | addons/website_quote/__openerp__.py | 2 | ||||
| -rw-r--r-- | addons/website_quote/controllers/main.py | 111 | ||||
| -rw-r--r-- | addons/website_quote/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/website_quote/models/order.py | 15 | ||||
| -rw-r--r-- | addons/website_quote/models/payment.py | 28 | ||||
| -rw-r--r-- | addons/website_quote/static/src/js/website_quotation.js | 73 | ||||
| -rw-r--r-- | addons/website_quote/views/website_quotation.xml | 111 | ||||
| -rw-r--r-- | addons/website_quote/views/website_quotation_backend.xml | 2 |
8 files changed, 306 insertions, 37 deletions
diff --git a/addons/website_quote/__openerp__.py b/addons/website_quote/__openerp__.py index 35c35ff5fb6..9830a3ee15b 100644 --- a/addons/website_quote/__openerp__.py +++ b/addons/website_quote/__openerp__.py @@ -10,7 +10,7 @@ OpenERP Sale Quote Roller """, 'author': 'OpenERP SA', - 'depends': ['website', 'sale', 'mail', 'web_tip'], + 'depends': ['website', 'sale', 'mail', 'web_tip', 'payment', 'website_portal'], 'data': [ 'quotation_report.xml', 'views/website_quotation.xml', diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py index eeb085c67a7..c414f363a96 100644 --- a/addons/website_quote/controllers/main.py +++ b/addons/website_quote/controllers/main.py @@ -55,14 +55,41 @@ class sale_quote(http.Controller): pdf = report_obj.get_pdf(request.cr, SUPERUSER_ID, [order_id], 'website_quote.report_quote', data=None, context=request.context) pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))] return request.make_response(pdf, headers=pdfhttpheaders) + user = request.registry['res.users'].browse(request.cr, SUPERUSER_ID, request.uid, context=request.context) + tx_id = request.registry['payment.transaction'].search(request.cr, SUPERUSER_ID, [('reference', '=', order.name)], context=request.context) + tx = request.registry['payment.transaction'].browse(request.cr, SUPERUSER_ID, tx_id, context=request.context) if tx_id else False values = { 'quotation': order, 'message': message and int(message) or False, 'option': bool(filter(lambda x: not x.line_id, order.options)), 'order_valid': (not order.validity_date) or (now <= order.validity_date), 'days_valid': days, - 'action': action + 'action': action, + 'breadcrumb': user.partner_id == order.partner_id, + 'tx_id': tx_id, + 'tx_state': tx.state if tx else False, + 'tx_post_msg': tx.acquirer_id.post_msg if tx else False, + 'need_payment': not tx_id and order.state == 'manual' } + + if order.require_payment or (not tx_id and order.state == 'manual'): + payment_obj = request.registry.get('payment.acquirer') + acquirer_ids = payment_obj.search(request.cr, SUPERUSER_ID, [('website_published', '=', True), ('company_id', '=', order.company_id.id)], context=request.context) + values['acquirers'] = list(payment_obj.browse(request.cr, token and SUPERUSER_ID or request.uid, acquirer_ids, context=request.context)) + render_ctx = dict(request.context, submit_class='btn btn-primary', submit_txt=_('Pay & Confirm')) + for acquirer in values['acquirers']: + acquirer.button = payment_obj.render( + request.cr, SUPERUSER_ID, acquirer.id, + order.name, + order.amount_total, + order.pricelist_id.currency_id.id, + partner_id=order.partner_id.id, + tx_values={ + 'return_url': '/quote/' + str(order_id) + '/confirm', + 'type': 'form', + 'alias_usage': _('If we store your payment information on our server, subscription payments will be made automatically.') + }, + context=render_ctx) return request.website.render('website_quote.so_quotation', values) @http.route(['/quote/accept'], type='json', auth="public", website=True) @@ -71,6 +98,8 @@ class sale_quote(http.Controller): order = order_obj.browse(request.cr, SUPERUSER_ID, order_id) if token != order.access_token: return request.website.render('website.404') + if order.require_payment: + return request.website.render('website.404') attachments=sign and [('signature.png', sign.decode('base64'))] or [] order_obj.action_button_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context) message = _('Order signed by %s') % (signer,) @@ -175,3 +204,83 @@ class sale_quote(http.Controller): line = request.registry.get('sale.order.line').create(request.cr, SUPERUSER_ID, vals, context=request.context) option_obj.write(request.cr, SUPERUSER_ID, [option.id], {'line_id': line}, context=request.context) return werkzeug.utils.redirect("/quote/%s/%s#pricing" % (order.id, token)) + + # note dbo: website_sale code + @http.route(['/quote/<int:order_id>/transaction/<int:acquirer_id>'], type='json', auth="public", website=True) + def payment_transaction(self, acquirer_id, order_id): + """ Json method that creates a payment.transaction, used to create a + transaction when the user clicks on 'pay now' button. After having + created the transaction, the event continues and the user is redirected + to the acquirer website. + + :param int acquirer_id: id of a payment.acquirer record. If not set the + user is redirected to the checkout page + """ + cr, uid, context = request.cr, request.uid, request.context + transaction_obj = request.registry.get('payment.transaction') + order = request.registry.get('sale.order').browse(cr, SUPERUSER_ID, order_id, context=context) + + if not order or not order.order_line or acquirer_id is None: + return request.redirect("/quote/" + str(order_id)) + + # find an already existing transaction + tx_id = transaction_obj.search(cr, SUPERUSER_ID, [('reference', '=', order.name)], context=context) + tx = transaction_obj.browse(cr, SUPERUSER_ID, tx_id, context=context) + if tx: + if tx.state == 'draft': # button cliked but no more info -> rewrite on tx or create a new one ? + tx.write({ + 'acquirer_id': acquirer_id, + }) + tx_id = tx.id + else: + tx_id = transaction_obj.create(cr, SUPERUSER_ID, { + 'acquirer_id': acquirer_id, + 'type': 'form', + 'amount': order.amount_total, + 'currency_id': order.pricelist_id.currency_id.id, + 'partner_id': order.partner_id.id, + 'partner_country_id': order.partner_id.country_id.id, + 'reference': order.name, + 'sale_order_id': order.id, + }, context=context) + request.session['sale_transaction_id'] = tx_id + tx = transaction_obj.browse(cr, SUPERUSER_ID, tx_id, context=context) + + # confirm the quotation + if tx.acquirer_id.auto_confirm == 'at_pay_now': + request.registry['sale.order'].action_button_confirm(cr, SUPERUSER_ID, [order.id], context=request.context) + + return tx_id + + @http.route(['/quote/<int:order_id>/confirm'], type='http', auth="public", website=True) + def confirm(self, order_id): + cr, uid, context = request.cr, request.uid, request.context + transaction_obj = request.registry.get('payment.transaction') + sale_order_obj = request.registry.get('sale.order') + order = sale_order_obj.browse(cr, SUPERUSER_ID, order_id, context=context) + + if not order or not order.order_line: + return request.redirect("/quote/%s" % (order_id)) + + # find transaction + tx_id = transaction_obj.search(cr, SUPERUSER_ID, [('reference', '=', order.name)], context=context) + tx = transaction_obj.browse(cr, SUPERUSER_ID, tx_id, context=context) + # create draft invoice if transaction is ok + if tx and tx.state == 'pending': + sale_order_obj.action_button_confirm(cr, SUPERUSER_ID, [order_id], context=context) + sale_order_obj.signal_workflow(cr, SUPERUSER_ID, [order.id], 'manual_invoice', context=context) + message = _('Order payed by %s, waiting for payment confirmation') % (tx.partner_id.name,) + self.__message_post(message, order_id, type='comment', subtype='mt_comment') + elif tx and tx.state == 'done': + sale_order_obj.signal_workflow(cr, SUPERUSER_ID, [order.id], 'manual_invoice', context=context) + invoice = order.invoice_ids + request.registry['account.invoice'].signal_workflow(cr, SUPERUSER_ID, [invoice.id], 'invoice_open', context=context) + period_id = request.registry['account.period'].find(cr, SUPERUSER_ID, time.strftime('%Y-%m-%d'), context=context)[0] + message = _('Order payed by %s') % (tx.partner_id.name,) + self.__message_post(message, order_id, type='comment', subtype='mt_comment') + # if website_sale is installed, we need to clean the sale_order linked to the website + # otherwise the system will mix transactions on the next SO + if hasattr(request.website, 'sale_reset'): + request.website.sale_reset() + + return request.redirect("/quote/%s/%s" % (order.id, order.access_token)) diff --git a/addons/website_quote/models/__init__.py b/addons/website_quote/models/__init__.py index 49178edb61d..5649c941501 100644 --- a/addons/website_quote/models/__init__.py +++ b/addons/website_quote/models/__init__.py @@ -1 +1,2 @@ import order +import payment diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py index 380e83c76ed..a25030ad2ba 100644 --- a/addons/website_quote/models/order.py +++ b/addons/website_quote/models/order.py @@ -36,6 +36,7 @@ class sale_quote_template(osv.osv): 'note': fields.text('Terms and conditions'), '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.boolean('Immediate Payment', help="Require immediate payment by the customer when validating the order from the website quote"), } def open_template(self, cr, uid, quote_id, context=None): return { @@ -159,7 +160,8 @@ class sale_order(osv.osv): 'options' : fields.one2many('sale.order.option', 'order_id', 'Optional Products Lines', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, copy=True), 'amount_undiscounted': fields.function(_get_total, string='Amount Before Discount', type="float", digits_compute=dp.get_precision('Account')), - 'quote_viewed': fields.boolean('Quotation Viewed') + 'quote_viewed': fields.boolean('Quotation Viewed'), + 'require_payment': fields.boolean('Immediate Payment', help="Require immediate payment by the customer when validating the order from the website quote"), } def _get_template_id(self, cr, uid, context=None): @@ -180,7 +182,7 @@ class sale_order(osv.osv): return { 'type': 'ir.actions.act_url', 'target': 'self', - 'url': '/quote/%s' % (quote.id) + 'url': '/quote/%s/%s' % (quote.id, quote.access_token) } def onchange_template_id(self, cr, uid, ids, template_id, partner=False, fiscal_position=False, pricelist_id=False, context=None): @@ -233,7 +235,14 @@ class sale_order(osv.osv): date = False if quote_template.number_of_days > 0: date = (datetime.datetime.now() + datetime.timedelta(quote_template.number_of_days)).strftime("%Y-%m-%d") - data = {'order_line': lines, 'website_description': quote_template.website_description, 'note': quote_template.note, 'options': options, 'validity_date': date} + data = { + 'order_line': lines, + 'website_description': quote_template.website_description, + 'note': quote_template.note, + 'options': options, + 'validity_date': date, + 'require_payment': quote_template.require_payment + } return {'value': data} def recommended_products(self, cr, uid, ids, context=None): diff --git a/addons/website_quote/models/payment.py b/addons/website_quote/models/payment.py new file mode 100644 index 00000000000..991decaa3cb --- /dev/null +++ b/addons/website_quote/models/payment.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- + +from openerp import SUPERUSER_ID +from openerp.osv import orm, fields + + +class PaymentTransaction(orm.Model): + _inherit = 'payment.transaction' + + _columns = { + # link with the sale order + 'sale_order_id': fields.many2one('sale.order', 'Sale Order'), + } + + def form_feedback(self, cr, uid, data, acquirer_name, context=None): + """ Override to confirm the sale order, if defined, and if the transaction + is done. """ + tx = None + res = super(PaymentTransaction, self).form_feedback(cr, uid, data, acquirer_name, context=context) + + # fetch the tx, check its state, confirm the potential SO + tx_find_method_name = '_%s_form_get_tx_from_data' % acquirer_name + if hasattr(self, tx_find_method_name): + tx = getattr(self, tx_find_method_name)(cr, uid, data, context=context) + if tx and tx.state == 'done' and tx.acquirer_id.auto_confirm == 'at_pay_confirm' and tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']: + self.pool['sale.order'].action_button_confirm(cr, SUPERUSER_ID, [tx.sale_order_id.id], context=context) + + return res diff --git a/addons/website_quote/static/src/js/website_quotation.js b/addons/website_quote/static/src/js/website_quotation.js index feb1901677a..61d54e8a29a 100644 --- a/addons/website_quote/static/src/js/website_quotation.js +++ b/addons/website_quote/static/src/js/website_quotation.js @@ -57,34 +57,38 @@ this.$("#signature").jSignature('reset'); }, submitForm: function(ev){ - ev.preventDefault(); // extract data var self = this; var href = self.$el.find('form').attr("action"); - var order_id = href.match(/accept\/([0-9]+)/); + var action = href.match(/quote\/([a-z]+)/); + var order_id = href.match(/quote\/[a-z]+\/([0-9]+)/); var token = href.match(/token=(.*)/); if (token){ token = token[1]; } - // 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){ + + if (action[1]=='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){ + return false; + } + openerp.jsonRpc("/quote/"+action[1], 'call', { + 'order_id': parseInt(order_id[1]), + 'token': token, + 'signer': signer_name, + 'sign': signature?JSON.stringify(signature[1]):false, + }).then(function (data) { + self.$el.modal('hide'); + window.location.href = '/quote/'+order_id[1]+'/'+token+'?message=3'; + }); return false; } - openerp.jsonRpc("/quote/accept", 'call', { - 'order_id': parseInt(order_id[1]), - 'token': token, - 'signer': signer_name, - 'sign': signature?JSON.stringify(signature[1]):false, - }).then(function (data) { - self.$el.modal('hide'); - window.location.href = '/quote/'+order_id[1]+'/'+token+'?message=3'; - }); - return false; }, }); @@ -155,3 +159,34 @@ }); }()); + +// dbo note: website_sale code for payment +// if we standardize payment somehow, this should disappear +$(document).ready(function () { + + // When choosing an acquirer, display its Pay Now button + var $payment = $("#payment_method"); + $payment.on("click", "input[name='acquirer']", function (ev) { + var payment_id = $(ev.currentTarget).val(); + $("div.oe_quote_acquirer_button[data-id]", $payment).addClass("hidden"); + $("div.oe_quote_acquirer_button[data-id='"+payment_id+"']", $payment).removeClass("hidden"); + }) + .find("input[name='acquirer']:checked").click(); + + // When clicking on payment button: create the tx using json then continue to the acquirer + $payment.on("click", 'button[type="submit"],button[name="submit"]', function (ev) { + ev.preventDefault(); + ev.stopPropagation(); + var $form = $(ev.currentTarget).parents('form'); + var acquirer_id = $(ev.currentTarget).parents('div.oe_quote_acquirer_button').first().data('id'); + if (! acquirer_id) { + return false; + } + var href = $(location).attr("href"); + var order_id = href.match(/quote\/([0-9]+)/)[1]; + openerp.jsonRpc('/quote/' + order_id +'/transaction/' + acquirer_id, 'call', {}).then(function (data) { + $form.submit(); + }); + }); + +}); diff --git a/addons/website_quote/views/website_quotation.xml b/addons/website_quote/views/website_quotation.xml index 95f1c9ceedb..85ba2a73034 100644 --- a/addons/website_quote/views/website_quotation.xml +++ b/addons/website_quote/views/website_quotation.xml @@ -19,7 +19,7 @@ <th>Products</th> <th>Quantity</th> <th>Taxes</th> - <th></th> + <th><span t-if="True in [line.discount > 0 for line in quotation.order_line]">Discount</span></th> <th class="text-right">Unit Price</th> <th class="text-right">Price</th> <th></th> @@ -155,15 +155,15 @@ <template id="quotation_toolbar"> <div class="text-center hidden-print" t-if="quotation.state in ('draft', 'sent', 'waiting_date')"> - <a class="btn btn-success" data-toggle="modal" data-target="#modelaccept" + <a class="btn btn-success" data-toggle="modal" data-target="#modalaccept" t-if="days_valid >= 0"> <i class="fa fa-check"></i> Accept </a> - <a class="btn btn-info fa fa-comment" type="submit" href="#discussion"> - Feedback + <a class="btn btn-info" type="submit" href="#discussion"> + <i class="fa fa-comment"></i> Feedback </a> - <a class="btn btn-danger fa fa-times" data-toggle="modal" data-target="#modaldecline"> - Reject + <a class="btn btn-danger" data-toggle="modal" data-target="#modaldecline"> + <i class="fa fa-times"></i> Reject </a> </div> </template> @@ -178,8 +178,8 @@ <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 t-if="order_valid" class="btn btn-primary btn-block fa fa-check" data-toggle="modal" data-target="#modalaccept"> - Accept Order + <a t-if="order_valid" class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept"> + <i class="fa fa-check"></i> Accept Order </a> <a t-if="not order_valid" href="#discussion" class="btn btn-info btn-block"> <strong>This offer expired!</strong><br/> @@ -194,6 +194,11 @@ </a> </div> </div> + <div class="text-center" t-if="need_payment" style="padding: 10px"> + <a class="btn btn-primary btn-block" data-toggle="modal" data-target="#modalaccept"> + <i class="fa fa-check"></i> Pay Now + </a> + </div> <hr class="mt0 mb0"/> <t t-call="website_quote.navigation_menu"/> <hr class="mt0 mb0"/> @@ -201,7 +206,8 @@ <input type="hidden" t-att-value="quotation.validity_date" id="validity_date"/> <div class="mt8" t-if="days_valid > 0"> <strong>This offer expires in</strong> - <div class="fa fa-clock-o fa-2x day_counter mt8 mb16"> + <div class="day_counter mt8 mb16"> + <i class="fa fa-clock-o"></i> <t t-esc="days_valid"/> <t t-if="days_valid > 1"> days @@ -255,10 +261,20 @@ and do not hesitate to <a href="#discussion">contact us</a> for any question. </div> + <div class="alert alert-warning alert-dismissable" t-if="tx_id and tx_state == 'done'"> + <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> + Your payment has been received, thank you for your trust. + <span t-if='tx_post_msg' t-raw="tx_post_msg"/> + </div> + <div class="alert alert-warning alert-dismissable" t-if="tx_id and tx_state == 'pending'"> + <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> + Your transaction is waiting confirmation. + <span t-if='tx_post_msg' t-raw="tx_post_msg"/> + </div> <!-- modal relative to the actions Accept/Reject/Cancel --> <div class="modal fade" id="modalaccept" role="dialog" aria-hidden="true"> - <div class="modal-dialog"> + <div class="modal-dialog" t-if="not quotation.require_payment and not need_payment"> <form id="accept" method="POST" t-attf-action="/quote/accept/#{quotation.id}/?token=#{quotation.access_token}" class="js_accept_json modal-content"> <div class="modal-header"> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> @@ -278,11 +294,58 @@ </div> </div> <div class="modal-footer"> - <button type="submit" t-attf-class="btn btn-primary">Sign Order</button> or - <button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button> + <div t-if="not acquirers"> + <button type="submit" t-attf-class="btn btn-primary">Sign & Confirm</button> or + <button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button> + </div> </div> </form> </div> + + <div class="modal-dialog modal-content" t-if="quotation.require_payment or need_payment"> + <div class="modal-header"> + <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> + <h4 class="modal-title">Validate Order</h4> + </div> + <div class="modal-body" id="sign-dialog"> + <p> + <span>I agree that by paying this proposal, I + accept it on the behalf of </span><b t-field="quotation.company_id"/><span>, for an amount of </span> + <b data-id="total_amount" t-field="quotation.amount_total" + t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/> + <span>with payment terms: </span><b t-field="quotation.payment_term"/>. + </p> + </div> + <div class="modal-footer"> + <!-- begin website_sale code --> + <div t-if="acquirers" id="payment_method" class="row text-left"> + <div class="col-md-6"> + <h4>Payment Method:</h4> + <ul class="list-unstyled"> + <li t-foreach="acquirers or []" t-as="acquirer"> + <label t-if="acquirer.button"> + <input t-att-value="acquirer.id" type="radio" name="acquirer" t-att-checked="acquirers[0] == acquirer"/> + <img class="media-object" style="width: 60px; display: inline-block;" + t-att-title="acquirer.name" + t-att-src="'/payment_%s/static/src/img/%s_icon.png' % (acquirer.provider, acquirer.provider)"/> + <span t-field="acquirer.name"/> + </label> + </li> + </ul> + </div> + <div class="col-md-6"> + <t t-foreach="acquirers or []" t-as="acquirer"> + <div t-att-data-id="acquirer.id" class="oe_quote_acquirer_button hidden pull-right"> + <div t-raw="acquirer.button"/> + <div t-field="acquirer.pre_msg"/> + <button type="button" class="btn btn-link" data-dismiss="modal" style="padding: 0">Cancel</button> + </div> + </t> + </div> + </div> + <!-- end website_sale code --> + </div> + </div> </div> <div class="modal fade" id="modaldecline" role="dialog" aria-hidden="true"> @@ -448,7 +511,7 @@ <section t-if="quotation.options" id="options"> <h1 class="page-header">Options</h1> </section> - <section id="options"> + <section t-if="quotation.options" id="options"> <table class="table table-hover wq-table"> <thead> <tr> @@ -567,6 +630,28 @@ </xpath> </template> + <template id="quotations" inherit_id="website_portal.quotations"> + <xpath expr="//t[@t-foreach='quotations']" position="replace"> + <t t-foreach="quotations" t-as="quotation"> + <tr t-att-class="'hidden to_hide' if quotation_index > 4 else ''"> + <td> + <a t-att-href="'/quote/'+str(quotation.id)"><t t-esc="quotation.name"/></a> + </td> + <td><span t-field="quotation.validity_date"/></td> + <td> + <t t-if="quotation.state == 'cancel'"> + <span class="label label-default"><i class="fa fa-fw fa-remove"/> Cancelled</span> + </t> + <t t-if="quotation.validity_date and quotation.validity_date < date"> + <span class="label label-default"><i class="fa fa-fw fa-clock-o"/> Expired</span> + </t> + </td> + <td><span t-esc="quotation.amount_total" t-esc-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/> + </td> + </tr> + </t> + </xpath> + </template> </data> </openerp> diff --git a/addons/website_quote/views/website_quotation_backend.xml b/addons/website_quote/views/website_quotation_backend.xml index c42d76df29e..faaaf714d12 100644 --- a/addons/website_quote/views/website_quotation_backend.xml +++ b/addons/website_quote/views/website_quotation_backend.xml @@ -44,6 +44,7 @@ </xpath> <xpath expr="//field[@name='project_id']" position="before"> <field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position, pricelist_id)"/> + <field name="require_payment"/> </xpath> <xpath expr="//field[@name='client_order_ref']" position="after"> <field name="website_description" invisible="1"/> @@ -71,6 +72,7 @@ <div id="number_of_days"> <field name="number_of_days" class="oe_inline"/> days </div> + <field name="require_payment"/> </group> </group> <notebook> |
