[IMP] website_sale: improved checkout process
- confirmation is now a page displaying a sale order, which polls on the related transaction to have its status - when receiving an incoming notification thorugh the acquirer controller the sale order is updated, then the confirmation page is displayed - this allows to have a confirmation page that is independant from the rest of the checkout process - cleaned confirmation page, removed order details bzr revid: tde@openerp.com-20131121120902-fm8b7u94gmepohgi
This commit is contained in:
parent
3ee84c32d2
commit
2693636989
|
@ -448,9 +448,15 @@ class Ecommerce(http.Controller):
|
|||
|
||||
@website.route(['/shop/mycart/'], type='http', auth="public", multilang=True)
|
||||
def mycart(self, **post):
|
||||
order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
prod_obj = request.registry.get('product.product')
|
||||
|
||||
# must have a draft sale order with lines at this point, otherwise reset
|
||||
order = context.get('website_sale_order')
|
||||
if order and order.state != 'draft':
|
||||
request.registry['website'].sale_reset_order(cr, uid, context=context)
|
||||
return request.redirect('/shop/')
|
||||
|
||||
if 'promo' in post:
|
||||
self.change_pricelist(post.get('promo'))
|
||||
|
||||
|
@ -462,7 +468,7 @@ class Ecommerce(http.Controller):
|
|||
product_ids.append(line.product_id.id)
|
||||
suggested_ids = list(set(suggested_ids) - set(product_ids))
|
||||
if suggested_ids:
|
||||
suggested_ids = prod_obj.search(request.cr, request.uid, [('id', 'in', suggested_ids)], context=request.context)
|
||||
suggested_ids = prod_obj.search(cr, uid, [('id', 'in', suggested_ids)], context=context)
|
||||
|
||||
# select 3 random products
|
||||
suggested_products = []
|
||||
|
@ -473,7 +479,7 @@ class Ecommerce(http.Controller):
|
|||
values = {
|
||||
'int': int,
|
||||
'get_categories': self.get_categories,
|
||||
'suggested_products': prod_obj.browse(request.cr, request.uid, suggested_products, request.context),
|
||||
'suggested_products': prod_obj.browse(cr, uid, suggested_products, context),
|
||||
}
|
||||
return request.website.render("website_sale.mycart", values)
|
||||
|
||||
|
@ -498,10 +504,15 @@ class Ecommerce(http.Controller):
|
|||
def checkout(self, **post):
|
||||
cr, uid, context, registry = request.cr, request.uid, request.context, request.registry
|
||||
|
||||
order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
|
||||
# must have a draft sale order with lines at this point, otherwise reset
|
||||
order = context.get('website_sale_order')
|
||||
if not order or order.state != 'draft' or not order.order_line:
|
||||
return self.mycart(**post)
|
||||
request.registry['website'].sale_reset_order(cr, uid, context=context)
|
||||
return request.redirect('/shop/')
|
||||
# if transaction pending / done: redirect to confirmation
|
||||
tx = context.get('website_sale_transaction')
|
||||
if tx and tx.state != 'draft':
|
||||
return request.redirect('/shop/payment/confirmation/%s' % order.id)
|
||||
|
||||
orm_partner = registry.get('res.partner')
|
||||
orm_user = registry.get('res.users')
|
||||
|
@ -549,10 +560,15 @@ class Ecommerce(http.Controller):
|
|||
def confirm_order(self, **post):
|
||||
cr, uid, context, registry = request.cr, request.uid, request.context, request.registry
|
||||
|
||||
# must have a draft sale order with lines at this point, otherwise redirect to shop
|
||||
order = request.registry['website'].get_current_order(request.cr, request.uid, context=request.context)
|
||||
|
||||
if not order or order.state != 'draft' or not order.order_line:
|
||||
return self.mycart(**post)
|
||||
request.registry['website'].sale_reset_order(cr, uid, context=context)
|
||||
return request.redirect('/shop/')
|
||||
# if transaction pending / done: redirect to confirmation
|
||||
tx = context.get('website_sale_transaction')
|
||||
if tx and tx.state != 'draft':
|
||||
return request.redirect('/shop/payment/confirmation/%s' % order.id)
|
||||
|
||||
orm_parter = registry.get('res.partner')
|
||||
orm_user = registry.get('res.users')
|
||||
|
@ -636,19 +652,29 @@ class Ecommerce(http.Controller):
|
|||
return request.redirect("/shop/payment/")
|
||||
|
||||
@website.route(['/shop/payment/'], type='http', auth="public", multilang=True)
|
||||
def payment(self, payment_acquirer_id=None, **post):
|
||||
def payment(self, **post):
|
||||
""" Payment step. This page proposes several payment means based on available
|
||||
payment.acquirer. State at this point :
|
||||
|
||||
- a draft sale order with lines; otherwise, clean context / session and
|
||||
back to the shop
|
||||
- no transaction in context / session, or only a draft one, if the customer
|
||||
did go to a payment.acquirer website but closed the tab without
|
||||
paying / canceling
|
||||
"""
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
payment_obj = request.registry.get('payment.acquirer')
|
||||
|
||||
# if no sale order at this stage: back to checkout beginning
|
||||
order = context.get('website_sale_order')
|
||||
if not order or not order.order_line:
|
||||
return request.redirect("/shop/checkout/")
|
||||
|
||||
if not order or not order.state == 'draft' or not order.order_line:
|
||||
request.registry['website'].sale_reset_order(cr, uid, context=context)
|
||||
return request.redirect("/shop/")
|
||||
# alread a transaction: forward to confirmation
|
||||
tx = context.get('website_sale_transaction')
|
||||
if tx and not tx.state == 'draft':
|
||||
return request.redirect('/shop/confirmation')
|
||||
print 'embetatn'
|
||||
# return request.redirect('/shop/confirmation/%s' % order.id)
|
||||
|
||||
partner_id = False
|
||||
shipping_partner_id = False
|
||||
|
@ -661,7 +687,6 @@ class Ecommerce(http.Controller):
|
|||
|
||||
values = {
|
||||
'partner': partner_id,
|
||||
'payment_acquirer_id': payment_acquirer_id,
|
||||
'order': order
|
||||
}
|
||||
values.update(request.registry.get('sale.order')._get_website_data(cr, uid, order, context))
|
||||
|
@ -680,7 +705,7 @@ class Ecommerce(http.Controller):
|
|||
order.pricelist_id.currency_id,
|
||||
partner_id=shipping_partner_id,
|
||||
tx_custom_values={
|
||||
'return_url': '/shop/confirmation',
|
||||
'return_url': '/shop/payment/validate',
|
||||
},
|
||||
context=context)
|
||||
|
||||
|
@ -688,7 +713,7 @@ class Ecommerce(http.Controller):
|
|||
|
||||
@website.route(['/shop/payment/transaction/<int:acquirer_id>'],
|
||||
type='http', methods=['POST'], auth="public")
|
||||
def payment_transaction(self, acquirer_id=None, **post):
|
||||
def payment_transaction(self, acquirer_id, **post):
|
||||
""" Hook method that creates a payment.transaction and redirect to the
|
||||
acquirer, using post values to re-create the post action.
|
||||
|
||||
|
@ -717,6 +742,7 @@ class Ecommerce(http.Controller):
|
|||
'currency_id': order.pricelist_id.currency_id.id,
|
||||
'partner_id': order.partner_id.id,
|
||||
'reference': order.name,
|
||||
'sale_order_id': order.id,
|
||||
}, context=context)
|
||||
request.httprequest.session['website_sale_transaction_id'] = tx_id
|
||||
elif tx and tx.state == 'draft': # button cliked but no more info -> rewrite on tx or create a new one ?
|
||||
|
@ -728,30 +754,47 @@ class Ecommerce(http.Controller):
|
|||
acquirer_total_url = '%s?%s' % (acquirer_form_post_url, urllib.urlencode(post))
|
||||
return request.redirect(acquirer_total_url)
|
||||
|
||||
@website.route('/shop/payment/confirm', type='json', auth="public", multilang=True)
|
||||
def payment_confirm(self, transaction_id=None, **post):
|
||||
@website.route('/shop/payment/get_status/<model("sale.order"):order>', type='json', auth="public", multilang=True)
|
||||
def payment_get_status(self, order, **post):
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
payment_obj = request.registry.get('payment.transaction')
|
||||
if transaction_id:
|
||||
tx = payment_obj.browse(cr, uid, transaction_id, context=context)
|
||||
else:
|
||||
tx = context.get('website_sale_transaction')
|
||||
if not order:
|
||||
return {
|
||||
'state': 'error',
|
||||
}
|
||||
|
||||
tx_ids = request.registry['payment.transaction'].search(
|
||||
cr, uid, [
|
||||
'|', ('sale_order_id', '=', order.id), ('reference', '=', order.name)
|
||||
], context=context)
|
||||
if not tx_ids:
|
||||
return {
|
||||
'state': 'error'
|
||||
}
|
||||
tx = request.registry['payment.transaction'].browse(cr, uid, tx_ids[0], context=context)
|
||||
return {
|
||||
'state': tx.state,
|
||||
}
|
||||
|
||||
@website.route('/shop/payment/validate', type='json', auth="public", multilang=True)
|
||||
def payment_validate(self):
|
||||
@website.route('/shop/payment/validate/', type='http', auth="public", multilang=True)
|
||||
def payment_validate(self, transaction_id=None, sale_order_id=None, **post):
|
||||
""" Method that should be called by the server when receiving an update
|
||||
for a transaction. State at this point :
|
||||
|
||||
- UDPATE ME
|
||||
"""
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
email_act = None
|
||||
sale_order_obj = request.registry['sale.order']
|
||||
|
||||
order = context.get('website_sale_order')
|
||||
tx = context.get('website_sale_transaction')
|
||||
if transaction_id is None:
|
||||
tx = context.get('website_sale_transaction')
|
||||
else:
|
||||
tx = request.registry['payment.transaction'].browse(cr, uid, transaction_id, context=context)
|
||||
|
||||
if not order or not tx:
|
||||
return request.redirect("/shop/checkout/")
|
||||
if sale_order_id is None:
|
||||
order = context.get('website_sale_order')
|
||||
else:
|
||||
order = request.registry['sale.order'].browse(cr, uid, sale_order_id, context=context)
|
||||
|
||||
if tx.state == 'done':
|
||||
# confirm the quotation
|
||||
|
@ -770,26 +813,23 @@ class Ecommerce(http.Controller):
|
|||
compose_id = request.registry['mail.compose.message'].create(cr, uid, {}, context=create_ctx)
|
||||
request.registry['mail.compose.message'].send_mail(cr, uid, [compose_id], context=create_ctx)
|
||||
|
||||
return True
|
||||
# clean context and session, then redirect to the confirmation page
|
||||
request.registry['website'].sale_reset_order(cr, uid, context=context)
|
||||
|
||||
@website.route(['/shop/confirmation/'], type='http', auth="public", multilang=True)
|
||||
def payment_confirmation(self, **post):
|
||||
context = request.context
|
||||
return request.redirect('/shop/confirmation/%s' % order.id)
|
||||
|
||||
# if no sale order at this stage: back to shop
|
||||
order = context.get('website_sale_order')
|
||||
if not order or not order.order_line:
|
||||
return request.redirect("/shop/")
|
||||
@website.route(['/shop/confirmation/<model("sale.order"):order>'], type='http', auth="public", multilang=True)
|
||||
def payment_confirmation(self, order, **post):
|
||||
""" End of checkout process controller. Confirmation is basically seing
|
||||
the status of a sale.order. State at this point :
|
||||
|
||||
# no transaction: back to payment
|
||||
tx = context.get('website_sale_transaction')
|
||||
if not tx:
|
||||
return request.redirect('/shop/payment')
|
||||
- should not have any context / session info: clean them
|
||||
- take a sale.order id, because we request a sale.order and are not
|
||||
session dependant anymore
|
||||
"""
|
||||
cr, uid, context = request.cr, request.uid, request.context
|
||||
|
||||
res = request.website.render("website_sale.confirmation", {'order': order})
|
||||
# request.httprequest.session['ecommerce_order_id'] = False
|
||||
# request.httprequest.session['ecommerce_pricelist'] = False
|
||||
return res
|
||||
return request.website.render("website_sale.confirmation", {'order': order})
|
||||
|
||||
@website.route(['/shop/change_sequence/'], type='json', auth="public")
|
||||
def change_sequence(self, id, top):
|
||||
|
|
|
@ -65,6 +65,17 @@ class Website(osv.Model):
|
|||
return tx
|
||||
return False
|
||||
|
||||
def sale_reset_order(self, cr, uid, context=None):
|
||||
request.httprequest.session.update({
|
||||
'ecommerce_order_id': False,
|
||||
'ecommerce_pricelist': False,
|
||||
'website_sale_transaction_id': False,
|
||||
})
|
||||
request.context.update({
|
||||
'website_sale_order': False,
|
||||
'website_sale_transaction': False,
|
||||
})
|
||||
|
||||
def preprocess_request(self, cr, uid, ids, request, context=None):
|
||||
request.context.update({
|
||||
'website_sale_order': self.get_current_order(cr, uid, context=context),
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
from openerp.osv import orm, fields
|
||||
from openerp.addons.web import http
|
||||
|
||||
|
||||
class Website(orm.Model):
|
||||
_inherit = 'website'
|
||||
|
||||
|
@ -16,3 +17,12 @@ class Website(orm.Model):
|
|||
'pricelist_id': fields.function(
|
||||
_get_pricelist, type='many2one', obj='product.pricelist')
|
||||
}
|
||||
|
||||
|
||||
class PaymentTransaction(orm.Model):
|
||||
_inherit = 'payment.transaction'
|
||||
|
||||
_columns = {
|
||||
# link with the sale order
|
||||
'sale_order_id': fields.many2one('sale.order', 'Sale Order'),
|
||||
}
|
||||
|
|
|
@ -2,46 +2,35 @@ $(document).ready(function () {
|
|||
|
||||
var _poll_nbr = 0;
|
||||
|
||||
function payment_transaction_validate() {
|
||||
return openerp.jsonRpc('/shop/payment/validate', 'call', {});
|
||||
}
|
||||
|
||||
function payment_transaction_poll_status() {
|
||||
return openerp.jsonRpc('/shop/payment/confirm', 'call', {
|
||||
}).done(function (result) {
|
||||
// console.log('--done', result);
|
||||
var order_node = $('div.oe_website_sale_tx_status');
|
||||
if (! order_node || order_node.data('orderId') === undefined) {
|
||||
return;
|
||||
}
|
||||
var order_id = order_node.data('orderId');
|
||||
return openerp.jsonRpc('/shop/payment/get_status/' + order_id, 'call', {
|
||||
}).then(function (result) {
|
||||
var tx_node = $('div.oe_website_sale_tx_status');
|
||||
var txt = '<h3>Your transaction is waiting confirmation.</h3>';
|
||||
_poll_nbr += 1;
|
||||
if (result.state == 'done') {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>Thanks for your order</h2>";
|
||||
});
|
||||
if (result.state == 'pending' && _poll_nbr <= 5) {
|
||||
txt = "<h3>Your transaction is waiting confirmation.</h3>";
|
||||
setTimeout(function () {
|
||||
payment_transaction_poll_status();
|
||||
}, 1000);
|
||||
}
|
||||
else if (result.state == 'done') {
|
||||
txt = "<h3>Your payment has been received.</h3>";
|
||||
}
|
||||
else if (result.state == 'pending') {
|
||||
if (_poll_nbr <= 2) {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>Waiting validation ...</h2>";
|
||||
});
|
||||
setTimeout(function () {
|
||||
return inner_done = payment_transaction_poll_status();
|
||||
}, 1000);
|
||||
// return inner_done;
|
||||
}
|
||||
else {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>You payment is currently under review. Please come back later.</h2>";
|
||||
});
|
||||
}
|
||||
txt = "<h3>Your transaction is waiting confirmation. You may try to refresh this page.</h3>";
|
||||
}
|
||||
else if (result.state == 'cancel') {
|
||||
$('div.oe_website_sale_confirmation').html(function() {
|
||||
return "<h2>The payment seems to have been canceled.</h2>";
|
||||
});
|
||||
txt = "<h3>The payment seems to have been canceled.</h3>";
|
||||
}
|
||||
tx_node.html(txt);
|
||||
});
|
||||
}
|
||||
|
||||
payment_transaction_poll_status().done(function (result) {
|
||||
// console.log('finished', result);
|
||||
payment_transaction_validate();
|
||||
});
|
||||
payment_transaction_poll_status();
|
||||
});
|
||||
|
|
|
@ -925,83 +925,28 @@
|
|||
<li class="text-muted">Payment<span class="chevron"></span></li>
|
||||
<li class="text-primary">Confirmation<span class="chevron"></span></li>
|
||||
</ul>
|
||||
<h1 class="mb32">Validate Order</h1>
|
||||
<h1 class="mb32">Order Confirmed</h1>
|
||||
<div class="row">
|
||||
<div class="col-md-8 oe_mycart">
|
||||
<table class='table table-striped table-condensed' id="mycart_products" t-if="website_sale_order and website_sale_order.order_line">
|
||||
<colgroup>
|
||||
<col width="80"/>
|
||||
<col/>
|
||||
<col width="100"/>
|
||||
<col width="120"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th colspan="2">Product</th>
|
||||
<th>Price</th>
|
||||
<th>Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr t-foreach="website_sale_order.order_line" t-as="line">
|
||||
<td colspan="2" t-if="not line.product_id.product_tmpl_id"></td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<a t-href="/shop/product/#{ line.product_id.product_tmpl_id.id }/">
|
||||
<span t-field="line.product_id.image_small"
|
||||
t-field-options='{"widget": "image", "class": "img-rounded"}'/>
|
||||
</a>
|
||||
</td>
|
||||
<td t-if="line.product_id.product_tmpl_id">
|
||||
<strong t-field="line.product_id.name"/>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span t-field="line.price_unit" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/>
|
||||
</td>
|
||||
<td>
|
||||
<div t-esc="line.product_uom_qty"/>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class='pull-right mb16' id="mycart_total">
|
||||
<colgroup>
|
||||
<col width="100"/>
|
||||
<col width="120"/>
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr style="border-top: 1px solid #000">
|
||||
<th><h3>Total:</h3></th>
|
||||
<th class="text-right"><h3><span t-field="website_sale_order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></h3></th>
|
||||
</tr>
|
||||
<tr class="text-muted">
|
||||
<td><abbr title="Taxes may be updated after providing shipping address">Incl. Taxes:</abbr></td>
|
||||
<td class="text-right"><span t-field="website_sale_order.amount_tax" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/></td>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
<div class="clearfix"/>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
|
||||
<h4>Bill To:</h4>
|
||||
<div t-field="website_sale_order.partner_invoice_id"/>
|
||||
<div class="col-md-8 oe_mycart">
|
||||
<h2>Thank you for your order.</h2>
|
||||
<div class="oe_website_sale_tx_status" t-att-data-order-id="order.id">
|
||||
</div>
|
||||
<div class="clearfix"/>
|
||||
<div class="oe_structure"/>
|
||||
</div>
|
||||
<div class="col-md-3 col-md-offset-1 text-muted" id="right_column">
|
||||
<h4>Bill To:</h4>
|
||||
<div t-field="order.partner_invoice_id"/>
|
||||
|
||||
<h4 class="mt32">Ship To:</h4>
|
||||
<div t-field="website_sale_order.partner_shipping_id"/>
|
||||
</div>
|
||||
</div>
|
||||
<h4 class="mt32">Ship To:</h4>
|
||||
<div t-field="order.partner_shipping_id"/>
|
||||
|
||||
<div class="oe_website_sale_confirmation">
|
||||
<h2>Tanks you for your order.</h2>
|
||||
<h4 class="mt32">Amount:</h4>
|
||||
<!-- <div t-field="order.amount_total" t-field-options='{
|
||||
"widget": "monetary",
|
||||
"display_currency": "website.pricelist_id.currency_id"
|
||||
}'/> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue