From b64ac1f4541a09f9ddee5673cca9d77b9aef7bd9 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Wed, 7 Dec 2016 13:01:20 +0100 Subject: [PATCH] [FIX] website_sale, website_portal_sale: SO auto-confirm 1. Setup authorize.net acquirer Published on website Order Confirmation: capture manually Store card: always 2. Buy something on the store => SO gets confirmed 3. Buy again but select the saved card 4. You get a confirmation screen on the website, but the SO is not actually confirmed in the backend (but it should) The issue is that the confirmation of the SO is located in the `form_feedback` method. When paying with a saved credit card, there is no form filled in since we use a S2S transaction. Therefore, the method is never called, and the SO never confirmed. Fixes #14007 opw-693517 --- addons/website_portal_sale/models/payment.py | 62 ++++++++++++-------- addons/website_sale/controllers/main.py | 5 +- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/addons/website_portal_sale/models/payment.py b/addons/website_portal_sale/models/payment.py index d52085ee8f4..3b77d74b51e 100644 --- a/addons/website_portal_sale/models/payment.py +++ b/addons/website_portal_sale/models/payment.py @@ -41,32 +41,42 @@ class PaymentTransaction(models.Model): tx = None res = super(PaymentTransaction, self).form_feedback(data, acquirer_name) - # fetch the tx, check its state, confirm the potential SO - try: - 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)(data) - _logger.info('<%s> transaction processed: tx ref:%s, tx amount: %s', acquirer_name, tx.reference if tx else 'n/a', tx.amount if tx else 'n/a') + # fetch the tx + 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)(data) + _logger.info('<%s> transaction processed: tx ref:%s, tx amount: %s', acquirer_name, tx.reference if tx else 'n/a', tx.amount if tx else 'n/a') - if tx and tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']: - # 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() - - if tx.acquirer_id.auto_confirm == 'generate_and_pay_invoice': - self._generate_and_pay_invoice(tx, acquirer_name) - elif tx.state not in ['cancel', 'error'] and tx.sale_order_id.state == 'draft': - _logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) - tx.sale_order_id.force_quotation_send() - else: - _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) - except Exception: - _logger.exception('Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '') + if tx: + # Auto-confirm SO if necessary + tx._confirm_so(acquirer_name=acquirer_name) return res + + def _confirm_so(self, acquirer_name=False): + for tx in self: + try: + # check tx state, confirm the potential SO + if tx.sale_order_id and tx.sale_order_id.state in ['draft', 'sent']: + # 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 not acquirer_name: + acquirer_name = tx.sale_order_id.payment_acquirer_id.provider or 'unknown' + 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() + + if tx.acquirer_id.auto_confirm == 'generate_and_pay_invoice': + self._generate_and_pay_invoice(tx, acquirer_name) + elif tx.state not in ['cancel', 'error'] and tx.sale_order_id.state == 'draft': + _logger.info('<%s> transaction pending/to confirm manually, sending quote email for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) + tx.sale_order_id.force_quotation_send() + else: + _logger.warning('<%s> transaction MISMATCH for order %s (ID %s)', acquirer_name, tx.sale_order_id.name, tx.sale_order_id.id) + + except Exception: + _logger.exception('Fail to confirm the order or send the confirmation email%s', tx and ' for the transaction %s' % tx.reference or '') diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 592a3e43c74..6e2c06b3fd1 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -753,7 +753,10 @@ class WebsiteSale(http.Controller): 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') + else: + # Auto-confirm SO if necessary + tx._confirm_so() + return dict(success=True, url='/shop/payment/validate') except Exception, e: _logger.warning(_("Payment transaction (%s) failed : <%s>") % (tx.id, str(e))) return dict(success=False, error=_("Payment transaction failed (Contact Administrator)"))