diff options
| author | Damien Bouvy <dbo@odoo.com> | 2016-08-29 15:42:25 +0200 |
|---|---|---|
| committer | Damien Bouvy <dbo@odoo.com> | 2016-09-01 15:58:41 +0200 |
| commit | a2d249f1a4d4c64c01e2c8c3f1145b817b7544e3 (patch) | |
| tree | fdcad5c0c2f478cab0bd50e7ca8b9ef3a054ee84 | |
| parent | 406611967b4c8eca711e666b9f707b93c7faf19e (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.
| -rw-r--r-- | addons/payment/models/payment_acquirer.py | 29 | ||||
| -rw-r--r-- | addons/payment/views/payment_views.xml | 2 | ||||
| -rw-r--r-- | addons/website_portal_sale/models/payment.py | 3 | ||||
| -rw-r--r-- | addons/website_quote/views/website_quote_templates.xml | 5 | ||||
| -rw-r--r-- | addons/website_sale/controllers/main.py | 5 | ||||
| -rw-r--r-- | addons/website_sale/views/sale_order.xml | 2 | ||||
| -rw-r--r-- | addons/website_sale/views/templates.xml | 5 |
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">&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">&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> |
