[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.
This commit is contained in:
committed by
Christophe Simonis
parent
3dafbe9aa8
commit
de8d90e12e
@@ -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',
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
import order
|
||||
import payment
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user