summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorDamien Bouvy <dbo@odoo.com>2016-08-29 15:42:25 +0200
committerDamien Bouvy <dbo@odoo.com>2016-09-01 15:58:41 +0200
commita2d249f1a4d4c64c01e2c8c3f1145b817b7544e3 (patch)
treefdcad5c0c2f478cab0bd50e7ca8b9ef3a054ee84 /addons
parent406611967b4c8eca711e666b9f707b93c7faf19e (diff)
[IMP] website_(portal_sale|sale|quote), payment: add authorization payment flow steps
This allows using a modern e-commerce flow where amounts are first authorized at checkout then captured when the picking is in fact shipped instead of immediately capturing the amount. To access this flow, a new selection value is available on the payment acquirer: "Authorize the amount and confirm the SO on acquirer confirmation". If this value is selected, s2s/form transactions should only require authorization to the acquirer; the user can then capture/void the payment manually from the Odoo backend transaction form view by simply using buttons.
Diffstat (limited to 'addons')
-rw-r--r--addons/payment/models/payment_acquirer.py29
-rw-r--r--addons/payment/views/payment_views.xml2
-rw-r--r--addons/website_portal_sale/models/payment.py3
-rw-r--r--addons/website_quote/views/website_quote_templates.xml5
-rw-r--r--addons/website_sale/controllers/main.py5
-rw-r--r--addons/website_sale/views/sale_order.xml2
-rw-r--r--addons/website_sale/views/templates.xml5
7 files changed, 46 insertions, 5 deletions
diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py
index 45b159f692c..aed9564d234 100644
--- a/addons/payment/models/payment_acquirer.py
+++ b/addons/payment/models/payment_acquirer.py
@@ -71,6 +71,7 @@ class PaymentAcquirer(models.Model):
help="Make this payment acquirer available (Customer invoices, etc.)")
auto_confirm = fields.Selection([
('none', 'No automatic confirmation'),
+ ('authorize', 'Authorize the amount and confirm the SO on acquirer confirmation'),
('confirm_so', 'Confirm the SO on acquirer confirmation'),
('generate_and_pay_invoice', 'On acquirer confirmation, confirm the SO, generate the invoice and pay it')],
string='Order Confirmation', default='confirm_so', required=True)
@@ -374,6 +375,7 @@ class PaymentTransaction(models.Model):
state = fields.Selection([
('draft', 'Draft'),
('pending', 'Pending'),
+ ('authorized', 'Authorized'),
('done', 'Done'),
('error', 'Error'),
('cancel', 'Canceled')], 'Status',
@@ -559,14 +561,39 @@ class PaymentTransaction(models.Model):
return getattr(self, custom_method_name)(**kwargs)
@api.multi
+ def s2s_capture_transaction(self, **kwargs):
+ custom_method_name = '%s_s2s_capture_transaction' % self.acquirer_id.provider
+ if hasattr(self, custom_method_name):
+ return getattr(self, custom_method_name)(**kwargs)
+
+ @api.multi
+ def s2s_void_transaction(self, **kwargs):
+ custom_method_name = '%s_s2s_void_transaction' % self.acquirer_id.provider
+ if hasattr(self, custom_method_name):
+ return getattr(self, custom_method_name)(**kwargs)
+
+ @api.multi
def s2s_get_tx_status(self):
""" Get the tx status. """
invalid_param_method_name = '_%s_s2s_get_tx_status' % self.acquirer_id.provider
if hasattr(self, invalid_param_method_name):
return getattr(self, invalid_param_method_name)()
-
return True
+ @api.multi
+ def action_capture(self):
+ if any(self.mapped(lambda tx: tx.state != 'authorized')):
+ raise ValidationError('Only transactions in the Authorized status can be captured.')
+ for tx in self:
+ tx.s2s_capture_transaction()
+
+ @api.multi
+ def action_void(self):
+ if any(self.mapped(lambda tx: tx.state != 'authorized')):
+ raise ValidationError('Only transactions in the Authorized status can be voided.')
+ for tx in self:
+ tx.s2s_void_transaction()
+
class PaymentToken(models.Model):
_name = 'payment.token'
diff --git a/addons/payment/views/payment_views.xml b/addons/payment/views/payment_views.xml
index 448de81273b..51bf831a719 100644
--- a/addons/payment/views/payment_views.xml
+++ b/addons/payment/views/payment_views.xml
@@ -208,6 +208,8 @@
<field name="arch" type="xml">
<form string="Payment Transactions">
<header>
+ <button type="object" name="action_capture" states="authorized" string="Capture Transaction" class="oe_highlight"/>
+ <button type="object" name="action_void" states="authorized" string="Void Transaction"/>
<field name="state" widget="statusbar" clickable="True"/>
</header>
<sheet>
diff --git a/addons/website_portal_sale/models/payment.py b/addons/website_portal_sale/models/payment.py
index 2b2ceeaa6f1..8170cc37139 100644
--- a/addons/website_portal_sale/models/payment.py
+++ b/addons/website_portal_sale/models/payment.py
@@ -52,6 +52,9 @@ class PaymentTransaction(models.Model):
# verify SO/TX match, excluding tx.fees which are currently not included in SO
amount_matches = float_compare(tx.amount, tx.sale_order_id.amount_total, 2) == 0
if amount_matches:
+ if tx.state == 'authorized' and tx.acquirer_id.auto_confirm == 'authorize':
+ _logger.info('<%s> transaction authorized, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
+ tx.sale_order_id.with_context(send_email=True).action_confirm()
if tx.state == 'done' and tx.acquirer_id.auto_confirm in ['confirm_so', 'generate_and_pay_invoice']:
_logger.info('<%s> transaction completed, auto-confirming order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id)
tx.sale_order_id.with_context(send_email=True).action_confirm()
diff --git a/addons/website_quote/views/website_quote_templates.xml b/addons/website_quote/views/website_quote_templates.xml
index 4e1e1a20a33..7505867f21d 100644
--- a/addons/website_quote/views/website_quote_templates.xml
+++ b/addons/website_quote/views/website_quote_templates.xml
@@ -271,6 +271,11 @@
Your transaction is waiting confirmation.
<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 == 'authorized'">
+ <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
+ Your payment has been authorized.
+ <span t-if='tx_post_msg' t-raw="tx_post_msg"/>
+ </div>
<div class="alert alert-warning alert-dismissable" t-if="message==4 and quotation.state != 'sent'">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&amp;times;</button>
This order
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index a756173183e..6db1998fcde 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -748,7 +748,8 @@ class WebsiteSale(http.Controller):
tx.partner_id == tx.sale_order_id.partner_id):
try:
s2s_result = tx.s2s_do_transaction()
- if not s2s_result or tx.state != 'done':
+ valid_state = 'authorized' if tx.acquirer_id.auto_confirm == 'authorize' else 'done'
+ if not s2s_result or tx.state != valid_state:
return dict(success=False, error=_("Payment transaction failed (%s)") % tx.state_message)
return dict(success=True, url='/shop/payment/validate')
except Exception, e:
@@ -884,7 +885,7 @@ class WebsiteSale(http.Controller):
if not order or (order.amount_total and not tx):
return request.redirect('/shop')
- if (not order.amount_total and not tx) or tx.state in ['pending', 'done']:
+ if (not order.amount_total and not tx) or tx.state in ['pending', 'done', 'authorized']:
if (not order.amount_total and not tx):
# Orders are confirmed by payment transactions, but there is none for free orders,
# (e.g. free events), so confirm immediately
diff --git a/addons/website_sale/views/sale_order.xml b/addons/website_sale/views/sale_order.xml
index a77bc74de35..0040ac33fe3 100644
--- a/addons/website_sale/views/sale_order.xml
+++ b/addons/website_sale/views/sale_order.xml
@@ -8,7 +8,7 @@
<field name="arch" type="xml">
<xpath expr="//field[@name='amount_total']" position="after">
<field name="payment_acquirer_id" readonly="1" attrs="{'invisible': [('payment_acquirer_id', '=', False)]}" options='{"no_open":True}'/>
- <field name="payment_tx_id" readonly="1" attrs="{'invisible': [('payment_acquirer_id', '=', False)]}" groups="base.group_no_one"/>
+ <field name="payment_tx_id" readonly="1" attrs="{'invisible': [('payment_acquirer_id', '=', False)]}"/>
</xpath>
</field>
</record>
diff --git a/addons/website_sale/views/templates.xml b/addons/website_sale/views/templates.xml
index 3f54c9eb319..f3782fdb109 100644
--- a/addons/website_sale/views/templates.xml
+++ b/addons/website_sale/views/templates.xml
@@ -1535,6 +1535,9 @@
<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>
@@ -1547,7 +1550,7 @@
</div>
</t>
</t>
- <t t-if="tx_ids and state not in ['done', 'cancel', 'pending']">
+ <t t-if="tx_ids and state not in ['done', 'cancel', 'pending', 'authorized']">
<p>Your transaction is waiting confirmation.</p>
</t>
</template>