[MERGE] payment, website_sale(_delivery): simplify payment configuration

This commit proposes to simplify payment acquirers configuration in
backend and frontend. Includes mainly

 * configuration boxes for website admins at checkout process;
 * better and fixed display of transaction status at checkout;
 * hide unnecessary fields for transfer provider;

This commit merges 'dev/master-payment-tde'.
This commit is contained in:
Thibault Delavallée
2017-05-16 15:41:26 +02:00
12 changed files with 166 additions and 190 deletions
+1 -1
View File
@@ -353,7 +353,7 @@ class PaymentAcquirer(models.Model):
return True
@api.multi
def toggle_enviroment_value(self):
def toggle_environment_value(self):
prod = self.filtered(lambda acquirer: acquirer.environment == 'prod')
prod.write({'environment': 'test'})
(self-prod).write({'environment': 'prod'})
+11 -3
View File
@@ -30,13 +30,21 @@
<field name="module_id" invisible="1"/>
<field name="module_state" invisible="1"/>
<div class="oe_button_box" name="button_box" attrs="{'invisible': ['&amp;', ('module_id', '!=', False), ('module_state', '!=', 'installed')]}">
<button attrs="{'invisible': [('environment', '=', 'test')]}" class="oe_stat_button" name="toggle_enviroment_value" type="object" icon="fa-play">
<!-- transfer referenced here due to view inheritance issue in current master (post-saas-16) -->
<button name="toggle_environment_value"
attrs="{'invisible': ['|', ('environment', '=', 'test'), ('provider', '=', 'transfer')]}"
class="oe_stat_button"
type="object" icon="fa-play">
<div class="o_stat_info o_field_widget">
<span class="text-success">Production</span>
<span class="o_stat_text">Environment</span>
</div>
</button>
<button attrs="{'invisible': [('environment', '=', 'prod')]}" class="oe_stat_button" name="toggle_enviroment_value" type="object" icon="fa-stop">
<!-- transfer referenced here due to view inheritance issue in current master (post-saas-16) -->
<button name="toggle_environment_value"
attrs="{'invisible': ['|', ('environment', '=', 'prod'), ('provider', '=', 'transfer')]}"
class="oe_stat_button"
type="object" icon="fa-stop">
<div class="o_stat_info o_field_widget">
<span class="o_warning_text">Test</span>
<span class="o_stat_text">Environment</span>
@@ -63,7 +71,7 @@
</group>
</group>
<notebook attrs="{'invisible': ['&amp;', ('module_id', '!=', False), ('module_state', '!=', 'installed')]}">
<page string="Credentials">
<page string="Credentials" name="acquirer_credentials">
<group name="acquirer">
</group>
</page>
+1
View File
@@ -8,6 +8,7 @@
'description': """Transfer Payment Acquirer""",
'depends': ['payment'],
'data': [
'views/payment_views.xml',
'views/payment_transfer_templates.xml',
'data/payment_acquirer_data.xml',
],
@@ -46,6 +46,13 @@ class TransferPaymentAcquirer(models.Model):
values['post_msg'] = self._format_transfer_data()
return super(TransferPaymentAcquirer, self).create(values)
@api.multi
def write(self, values):
""" Hook in write to create a default post_msg. See create(). """
if all(not acquirer.post_msg and acquirer.provider != 'transfer' for acquirer in self) and values.get('provider') == 'transfer':
values['post_msg'] = self._format_transfer_data()
return super(TransferPaymentAcquirer, self).write(values)
class TransferPaymentTransaction(models.Model):
_inherit = 'payment.transaction'
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="payment_acquirer_view_form_inherit_transfer" model="ir.ui.view">
<field name="name">payment.acquirer.view.form.inherit.transfer</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<page name="acquirer_credentials" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</page>
<field name="done_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
<field name="cancel_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
<field name="error_msg" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
<field name="auto_confirm" position="attributes">
<attribute name="attrs">{'invisible': [('provider', '=', 'transfer')]}</attribute>
</field>
</field>
</record>
</odoo>
+46 -62
View File
@@ -696,6 +696,40 @@ class WebsiteSale(http.Controller):
# Payment
# ------------------------------------------------------
def _get_shop_payment_values(self, order, **kwargs):
shipping_partner_id = False
if order:
shipping_partner_id = order.partner_shipping_id.id or order.partner_invoice_id.id
values = dict(
website_sale_order=order,
errors=[],
partner=order.partner_id.id,
order=order,
payment_action_id=request.env.ref('payment.action_payment_acquirer').id,
)
acquirers = request.env['payment.acquirer'].search(
[('website_published', '=', True), ('company_id', '=', order.company_id.id)]
)
values['acquirers'] = []
for acquirer in acquirers:
acquirer_button = acquirer.with_context(submit_class='btn btn-primary', submit_txt=_('Pay Now')).sudo().render(
'/',
order.amount_total,
order.pricelist_id.currency_id.id,
values={
'return_url': '/shop/payment/validate',
'partner_id': shipping_partner_id,
'billing_partner_id': order.partner_invoice_id.id,
}
)
acquirer.button = acquirer_button
values['acquirers'].append(acquirer)
values['tokens'] = request.env['payment.token'].search([('partner_id', '=', order.partner_id.id), ('acquirer_id', 'in', acquirers.ids)])
return values
@http.route(['/shop/payment'], type='http', auth="public", website=True)
def payment(self, **post):
""" Payment step. This page proposes several payment means based on available
@@ -707,48 +741,18 @@ class WebsiteSale(http.Controller):
did go to a payment.acquirer website but closed the tab without
paying / canceling
"""
SaleOrder = request.env['sale.order']
order = request.website.sale_get_order()
redirection = self.checkout_redirection(order)
if redirection:
return redirection
shipping_partner_id = False
if order:
if order.partner_shipping_id.id:
shipping_partner_id = order.partner_shipping_id.id
else:
shipping_partner_id = order.partner_invoice_id.id
render_values = self._get_shop_payment_values(order, **post)
values = {
'website_sale_order': order
}
values['errors'] = SaleOrder._get_errors(order)
values.update(SaleOrder._get_website_data(order))
if not values['errors']:
acquirers = request.env['payment.acquirer'].search(
[('website_published', '=', True), ('company_id', '=', order.company_id.id)]
)
values['acquirers'] = []
for acquirer in acquirers:
acquirer_button = acquirer.with_context(submit_class='btn btn-primary', submit_txt=_('Pay Now')).sudo().render(
'/',
order.amount_total,
order.pricelist_id.currency_id.id,
values={
'return_url': '/shop/payment/validate',
'partner_id': shipping_partner_id,
'billing_partner_id': order.partner_invoice_id.id,
}
)
acquirer.button = acquirer_button
values['acquirers'].append(acquirer)
if render_values['errors']:
render_values.pop('acquirers', '')
render_values.pop('tokens', '')
values['tokens'] = request.env['payment.token'].search([('partner_id', '=', order.partner_id.id), ('acquirer_id', 'in', acquirers.ids)])
return request.render("website_sale.payment", values)
return request.render("website_sale.payment", render_values)
@http.route(['/shop/payment/transaction_token/confirm'], type='json', auth="public", website=True)
def payment_transaction_token_confirm(self, tx, **kwargs):
@@ -856,35 +860,15 @@ class WebsiteSale(http.Controller):
@http.route('/shop/payment/get_status/<int:sale_order_id>', type='json', auth="public", website=True)
def payment_get_status(self, sale_order_id, **post):
order = request.env['sale.order'].sudo().browse(sale_order_id)
order = request.env['sale.order'].sudo().browse(sale_order_id).exists()
assert order.id == request.session.get('sale_last_order_id')
values = {}
flag = False
if not order:
values.update({'not_order': True, 'state': 'error'})
else:
tx = request.env['payment.transaction'].sudo().search(
['|', ('sale_order_id', '=', order.id), ('reference', '=', order.name)], limit=1
)
if not tx:
if order.amount_total:
values.update({'tx_ids': False, 'state': 'error'})
else:
values.update({'tx_ids': False, 'state': 'done', 'validation': None})
else:
state = tx.state
flag = state == 'pending'
values.update({
'tx_ids': True,
'state': state,
'acquirer_id': tx.acquirer_id,
'validation': tx.acquirer_id.auto_confirm == 'none',
'tx_post_msg': tx.acquirer_id.post_msg or None
})
return {'recall': flag, 'message': request.env['ir.ui.view'].render_template("website_sale.order_state_message", values)}
return {
'recall': order.payment_tx_id.state == 'pending',
'message': request.env['ir.ui.view'].render_template("website_sale.payment_confirmation_status", {
'order': order
})
}
@http.route('/shop/payment/validate', type='http', auth="public", website=True)
def payment_validate(self, transaction_id=None, sale_order_id=None, **post):
-11
View File
@@ -31,17 +31,6 @@ class SaleOrder(models.Model):
order.cart_quantity = int(sum(order.mapped('website_order_line.product_uom_qty')))
order.only_services = all(l.product_id.type in ('service', 'digital') for l in order.website_order_line)
@api.model
def _get_errors(self, order):
return []
@api.model
def _get_website_data(self, order):
return {
'partner': order.partner_id.id,
'order': order
}
@api.multi
def _cart_find_product_line(self, product_id=None, line_id=None, **kwargs):
self.ensure_one()
@@ -12,7 +12,7 @@ $(document).ready(function () {
if (! order_node || order_node.data('order-id') === undefined) {
return;
}
var order_id = order_node.data('order-id');
var order_id = parseInt(order_node.data('order-id'));
return ajax.jsonRpc('/shop/payment/get_status/' + order_id, 'call', {
}).then(function (result) {
_poll_nbr += 1;
+30 -66
View File
@@ -1369,8 +1369,8 @@
<div class="clearfix" />
<div class="oe_structure" />
<div class="js_payment mb64 col-md-12 row" t-if="acquirers and website_sale_order.amount_total" id="payment_method">
<div class="col-lg-5 col-sm-6">
<h4>Payment Method:</h4>
<div class="col-lg-7 col-sm-8">
<h4>Choose a payment method</h4>
<ul class="list-unstyled">
<t t-set="payment_methods_available" t-value="False"/>
<t t-set="partner_country" t-value="website_sale_order and website_sale_order.partner_invoice_id.country_id"/>
@@ -1411,8 +1411,12 @@
</div>
</li>
</ul>
<a class="btn btn-default btn-block"
t-attf-href="/web#return_label=Website&amp;action=#{payment_action_id}">
<i class="fa fa-plus-square" aria-hidden="true"></i> Setup payment methods
</a>
</div>
<div class="col-lg-5 col-lg-offset-2 col-sm-6 text-right">
<div class="col-lg-5 col-sm-4 text-right">
<t t-foreach="acquirers or []" t-as="acquirer" id="acquirers_list">
<div t-att-data-id="acquirer.id" class="oe_sale_acquirer_button hidden pull-right">
<div t-raw="acquirer.button" />
@@ -1471,9 +1475,8 @@
<h1 class="mb32">Order <em t-field="order.name" /> Confirmed</h1>
<div class="thanks_msg">
<h2>Thank you for your order.
<a class="btn btn-primary pull-right hidden-xs" href="/shop/print" target="_blank"><i class="fa fa-print"></i> Print
</a></h2>
<div class="oe_website_sale_tx_status" t-att-data-order-id="order.id"></div>
<a class="btn btn-primary pull-right hidden-xs" href="/shop/print" target="_blank"><i class="fa fa-print"></i> Print</a>
</h2>
</div>
</div>
</div>
@@ -1586,40 +1589,6 @@
</t>
</template>
<template id="order_state_message" name="Order State Message">
<t groups="base.group_system">
<a class="btn btn-primary pull-right" target="_blank" t-att-href="'/web#model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % ('payment.acquirer',acquirer_id.id, 'payment.action_payment_acquirer')">
<i class="fa fa-cog"></i> Configure Transfer Details</a>
</t>
<t t-if="(not_order and state == 'error') or (not tx_ids and state == 'error')">
<p>There seems to be an error with your request.</p>
</t>
<t t-if="not tx_ids and state == 'done'">
<p>Your order has been confirmed, thank you for your loyalty.</p>
</t>
<t t-if="tx_ids and state == 'done'">
<p>Your payment has been received.</p>
</t>
<t t-if="tx_ids and state == 'authorized'">
<p>Your payment has been authorized.</p>
</t>
<t t-if="tx_ids and state == 'cancel'">
<p>The payment seems to have been canceled.</p>
</t>
<t t-if="tx_ids and state == 'pending' and validation">
<p>Your transaction is waiting a manual confirmation.</p>
<br />
<t t-if="tx_post_msg">
<div class="hidden-print">
<t t-raw="tx_post_msg" />
</div>
</t>
</t>
<t t-if="tx_ids and state not in ['done', 'cancel', 'pending', 'authorized']">
<p>Your transaction is waiting confirmation.</p>
</t>
</template>
<template id="total">
<div id="cart_total" t-att-class="extra_class or ''" t-if="website_sale_order">
<div class="row" id="order_total_untaxed">
@@ -1644,36 +1613,31 @@
</template>
<template id="payment_confirmation_status">
<t t-if="order.payment_acquirer_id.auto_confirm == 'none'">
<div class="panel panel-info">
<div class="panel-heading" t-raw="order.payment_acquirer_id.pending_msg" />
<div class="oe_website_sale_tx_status" t-att-data-order-id="order.id">
<div t-att-class="' panel ' + (order.payment_tx_id.state == 'pending' and 'panel-info') or (order.payment_tx_id.state == 'done' and 'panel-success') or 'panel-danger'">
<a groups="base.group_system" class="btn btn-primary pull-right" target="_blank"
t-att-href="'/web#model=%s&amp;id=%s&amp;action=%s&amp;view_type=form' % ('payment.acquirer', order.payment_acquirer_id.id, 'payment.action_payment_acquirer')">
<i class="fa fa-cog"></i>
</a>
<div class="panel-heading">
<t t-if="order.payment_tx_id.state == 'pending'">
<t t-raw="order.payment_acquirer_id.pending_msg"/>
</t>
<t t-if="order.payment_tx_id.state == 'done'">
<t t-raw="order.payment_acquirer_id.done_msg"/>
</t>
<t t-if="order.payment_tx_id.state == 'cancel'">
<t t-raw="order.payment_acquirer_id.cancel_msg"/>
</t>
<t t-if="order.payment_tx_id.state == 'error'">
<t t-raw="order.payment_acquirer_id.error_msg"/>
</t>
</div>
<div class="panel-body" t-if="order.payment_acquirer_id.post_msg">
<t t-raw="order.payment_acquirer_id.post_msg" />
</div>
</div>
</t>
<t t-if="order.payment_acquirer_id.auto_confirm != 'none'">
<t t-if="order.payment_tx_id.state == 'pending'">
<div class="alert alert-info" role="alert">
<t t-raw="order.payment_acquirer_id.pending_msg" />
</div>
</t>
<t t-if="order.payment_tx_id.state == 'done'">
<div class="alert alert-success" role="alert">
<t t-raw="order.payment_acquirer_id.done_msg" />
</div>
</t>
<t t-if="order.payment_tx_id.state == 'cancel'">
<div class="alert alert-danger" role="alert">
<t t-raw="order.payment_acquirer_id.cancel_msg" />
</div>
</t>
<t t-if="order.payment_tx_id.state == 'error'">
<div class="alert alert-danger" role="alert">
<t t-raw="order.payment_acquirer_id.error_msg" />
</div>
</t>
</t>
</div>
</template>
<template id="payment_token_form">
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import http
from odoo import http, _
from odoo.http import request
from odoo.addons.website_sale.controllers.main import WebsiteSale
@@ -32,3 +33,19 @@ class WebsiteSaleDelivery(WebsiteSale):
if line.is_delivery:
ret['transaction']['shipping'] = line.price_unit
return ret
def _get_shop_payment_values(self, order, **kwargs):
values = super(WebsiteSaleDelivery, self)._get_shop_payment_values(order, **kwargs)
if not order._get_delivery_methods():
values['errors'].append(
(_('Sorry, we are unable to ship your order'),
_('No shipping method is available for your current order and shipping address. '
'Please contact us for more information.')))
has_stockable_products = any(line.product_id.type in ['consu', 'product'] for line in order.order_line)
if has_stockable_products:
delivery_carriers = order._get_delivery_methods()
values['deliveries'] = delivery_carriers.sudo().with_context(order_id=order.id)
values['delivery_action_id'] = request.env.ref('delivery.action_delivery_carrier_form').id
return values
@@ -90,29 +90,6 @@ class SaleOrder(models.Model):
_logger.debug("Carrier #%s removed from e-commerce carrier list. %s" % (carrier_id, e))
return available_carriers
@api.model
def _get_errors(self, order):
errors = super(SaleOrder, self)._get_errors(order)
if not order._get_delivery_methods():
errors.append(
(_('Sorry, we are unable to ship your order'),
_('No shipping method is available for your current order and shipping address. '
'Please contact us for more information.')))
return errors
@api.model
def _get_website_data(self, order):
""" Override to add delivery-related website data. """
values = super(SaleOrder, self)._get_website_data(order)
# We need a delivery only if we have stockable products
has_stockable_products = any(order.order_line.filtered(lambda line: line.product_id.type in ['consu', 'product']))
if not has_stockable_products:
return values
delivery_carriers = order._get_delivery_methods()
values['deliveries'] = delivery_carriers.sudo().with_context(order_id=order.id)
return values
@api.multi
def _cart_update(self, product_id=None, line_id=None, add_qty=0, set_qty=0, **kwargs):
""" Override to update carrier quotation if quantity changed """
@@ -23,29 +23,33 @@
<template id="payment_delivery" name="Delivery Costs" inherit_id="website_sale.payment">
<xpath expr="//div[@id='payment_method']" position="before">
<div t-if="deliveries" class="row" id="delivery_carrier">
<div t-if="deliveries" class="col-md-12 row" id="delivery_carrier">
<div class="col-lg-7 col-sm-8">
<h4>Choose your Delivery Method</h4>
<ul class="list-unstyled">
<li t-foreach="deliveries" t-as="delivery">
<t t-if="delivery.available">
<div id="delivery_method">
<label>
<input t-att-value="delivery.id" type="radio" name="delivery_type"
t-att-checked="order.carrier_id and order.carrier_id.id == delivery.id and 'checked' or False"/>
<abbr t-att-title="delivery.website_description"><span t-field="delivery.name"/></abbr>
<span class="badge" t-field="delivery.price"
t-options='{
"widget": "monetary",
"from_currency": delivery.product_id.company_id.currency_id,
"display_currency": website_sale_order.currency_id
}'/>
<div t-field="delivery.website_description" class="text-muted"/>
</label>
</div>
</t>
</li>
</ul>
<h4>Choose a delivery method</h4>
<ul class="list-unstyled">
<li t-foreach="deliveries" t-as="delivery">
<t t-if="delivery.available">
<div id="delivery_method">
<label>
<input t-att-value="delivery.id" type="radio" name="delivery_type"
t-att-checked="order.carrier_id and order.carrier_id.id == delivery.id and 'checked' or False"/>
<abbr t-att-title="delivery.website_description"><span t-field="delivery.name"/></abbr>
<span class="badge" t-field="delivery.price"
t-options='{
"widget": "monetary",
"from_currency": delivery.product_id.company_id.currency_id,
"display_currency": website_sale_order.currency_id
}'/>
<div t-field="delivery.website_description" class="text-muted"/>
</label>
</div>
</t>
</li>
</ul>
<a class="btn btn-default btn-block"
t-attf-href="/web#return_label=Website&amp;action=#{delivery_action_id}">
<i class="fa fa-plus-square" aria-hidden="true"></i> Setup delivery methods
</a>
</div>
</div>
</xpath>