[IMP] payment(_*): allow specifying extra allowed states
Some providers require additional allowed states due to their refund or transaction process justifying it. Until now, these extra states were specified in the `payment` module, which was not ideal as it allowed every provider in every flow to accept these additional states. With this commit, additional states are now specified only in the coresponding flow of a provider that requires them. task-2869678 closes odoo/odoo#107110 Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
@@ -89,14 +89,14 @@ class PaymentTransaction(models.Model):
|
||||
return separator.join(invoices.mapped('name'))
|
||||
return super()._compute_reference_prefix(provider_code, separator, **values)
|
||||
|
||||
def _set_canceled(self, state_message=None):
|
||||
def _set_canceled(self, state_message=None, **kwargs):
|
||||
""" Update the transactions' state to 'cancel'.
|
||||
|
||||
:param str state_message: The reason for which the transaction is set in 'cancel' state
|
||||
:return: updated transactions
|
||||
:rtype: `payment.transaction` recordset
|
||||
"""
|
||||
processed_txs = super()._set_canceled(state_message)
|
||||
processed_txs = super()._set_canceled(state_message, **kwargs)
|
||||
# Cancel the existing payments
|
||||
processed_txs.payment_id.action_cancel()
|
||||
return processed_txs
|
||||
|
||||
@@ -631,69 +631,89 @@ class PaymentTransaction(models.Model):
|
||||
"""
|
||||
self.ensure_one()
|
||||
|
||||
def _set_pending(self, state_message=None):
|
||||
def _set_pending(self, state_message=None, extra_allowed_states=()):
|
||||
""" Update the transactions' state to `pending`.
|
||||
|
||||
:param str state_message: The reason for setting the transactions in the state `pending`.
|
||||
:param tuple[str] extra_allowed_states: The extra states that should be considered allowed
|
||||
target states for the source state 'pending'.
|
||||
:return: The updated transactions.
|
||||
:rtype: recordset of `payment.transaction`
|
||||
"""
|
||||
allowed_states = ('draft',)
|
||||
target_state = 'pending'
|
||||
txs_to_process = self._update_state(allowed_states, target_state, state_message)
|
||||
txs_to_process = self._update_state(
|
||||
allowed_states + extra_allowed_states, target_state, state_message
|
||||
)
|
||||
txs_to_process._log_received_message()
|
||||
return txs_to_process
|
||||
|
||||
def _set_authorized(self, state_message=None):
|
||||
def _set_authorized(self, state_message=None, extra_allowed_states=()):
|
||||
""" Update the transactions' state to `authorized`.
|
||||
|
||||
:param str state_message: The reason for setting the transactions in the state `authorized`.
|
||||
:param tuple[str] extra_allowed_states: The extra states that should be considered allowed
|
||||
target states for the source state 'authorized'.
|
||||
:return: The updated transactions.
|
||||
:rtype: recordset of `payment.transaction`
|
||||
"""
|
||||
allowed_states = ('draft', 'pending')
|
||||
target_state = 'authorized'
|
||||
txs_to_process = self._update_state(allowed_states, target_state, state_message)
|
||||
txs_to_process = self._update_state(
|
||||
allowed_states + extra_allowed_states, target_state, state_message
|
||||
)
|
||||
txs_to_process._log_received_message()
|
||||
return txs_to_process
|
||||
|
||||
def _set_done(self, state_message=None):
|
||||
def _set_done(self, state_message=None, extra_allowed_states=()):
|
||||
""" Update the transactions' state to `done`.
|
||||
|
||||
:param str state_message: The reason for setting the transactions in the state `done`.
|
||||
:param tuple[str] extra_allowed_states: The extra states that should be considered allowed
|
||||
target states for the source state 'done'.
|
||||
:return: The updated transactions.
|
||||
:rtype: recordset of `payment.transaction`
|
||||
"""
|
||||
allowed_states = ('draft', 'pending', 'authorized', 'error', 'cancel') # 'cancel' for Payulatam
|
||||
allowed_states = ('draft', 'pending', 'authorized', 'error')
|
||||
target_state = 'done'
|
||||
txs_to_process = self._update_state(allowed_states, target_state, state_message)
|
||||
txs_to_process = self._update_state(
|
||||
allowed_states + extra_allowed_states, target_state, state_message
|
||||
)
|
||||
txs_to_process._log_received_message()
|
||||
return txs_to_process
|
||||
|
||||
def _set_canceled(self, state_message=None):
|
||||
def _set_canceled(self, state_message=None, extra_allowed_states=()):
|
||||
""" Update the transactions' state to `cancel`.
|
||||
|
||||
:param str state_message: The reason for setting the transactions in the state `cancel`.
|
||||
:param tuple[str] extra_allowed_states: The extra states that should be considered allowed
|
||||
target states for the source state 'canceled'.
|
||||
:return: The updated transactions.
|
||||
:rtype: recordset of `payment.transaction`
|
||||
"""
|
||||
allowed_states = ('draft', 'pending', 'authorized', 'done') # 'done' for Authorize refunds.
|
||||
allowed_states = ('draft', 'pending', 'authorized')
|
||||
target_state = 'cancel'
|
||||
txs_to_process = self._update_state(allowed_states, target_state, state_message)
|
||||
txs_to_process = self._update_state(
|
||||
allowed_states + extra_allowed_states, target_state, state_message
|
||||
)
|
||||
# Cancel the existing payments.
|
||||
txs_to_process._log_received_message()
|
||||
return txs_to_process
|
||||
|
||||
def _set_error(self, state_message):
|
||||
def _set_error(self, state_message, extra_allowed_states=()):
|
||||
""" Update the transactions' state to `error`.
|
||||
|
||||
:param str state_message: The reason for setting the transactions in the state `error`.
|
||||
:param tuple[str] extra_allowed_states: The extra states that should be considered allowed
|
||||
target states for the source state 'error'.
|
||||
:return: The updated transactions.
|
||||
:rtype: recordset of `payment.transaction`
|
||||
"""
|
||||
allowed_states = ('draft', 'pending', 'authorized', 'done') # 'done' for Stripe refunds.
|
||||
allowed_states = ('draft', 'pending', 'authorized')
|
||||
target_state = 'error'
|
||||
txs_to_process = self._update_state(allowed_states, target_state, state_message)
|
||||
txs_to_process = self._update_state(
|
||||
allowed_states + extra_allowed_states, target_state, state_message
|
||||
)
|
||||
txs_to_process._log_received_message()
|
||||
return txs_to_process
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo.tests import tagged
|
||||
from odoo.tools import mute_logger
|
||||
|
||||
from odoo.addons.payment.tests.common import PaymentCommon
|
||||
|
||||
@@ -80,3 +81,16 @@ class TestPaymentTransaction(PaymentCommon):
|
||||
msg="The amount of the refund transaction should be the negative value of the amount "
|
||||
"to refund."
|
||||
)
|
||||
|
||||
@mute_logger('odoo.addons.payment.models.payment_transaction')
|
||||
def test_update_state_to_illegal_target_state(self):
|
||||
tx = self._create_transaction('redirect', state='done')
|
||||
tx._update_state(['draft', 'pending', 'authorized'], 'cancel', None)
|
||||
self.assertEqual(tx.state, 'done')
|
||||
|
||||
def test_update_state_to_extra_allowed_state(self):
|
||||
tx = self._create_transaction('redirect', state='done')
|
||||
tx._update_state(
|
||||
['draft', 'pending', 'authorized', 'done'], 'cancel', None
|
||||
)
|
||||
self.assertEqual(tx.state, 'cancel')
|
||||
|
||||
@@ -108,7 +108,7 @@ class PaymentTransaction(models.Model):
|
||||
tx_status = tx_details.get('transaction', {}).get('transactionStatus')
|
||||
if tx_status in TRANSACTION_STATUS_MAPPING['voided']:
|
||||
# The payment has been voided from Authorize.net side before we could refund it.
|
||||
self._set_canceled()
|
||||
self._set_canceled(extra_allowed_states=('done',))
|
||||
elif tx_status in TRANSACTION_STATUS_MAPPING['refunded']:
|
||||
# The payment has been refunded from Authorize.net side before we could refund it. We
|
||||
# create a refund tx on Odoo to reflect the move of the funds.
|
||||
|
||||
@@ -129,7 +129,7 @@ class PaymentTransaction(models.Model):
|
||||
if status == 'PENDING':
|
||||
self._set_pending(state_message=state_message)
|
||||
elif status == 'APPROVED':
|
||||
self._set_done(state_message=state_message)
|
||||
self._set_done(state_message=state_message, extra_allowed_states=('cancel',))
|
||||
elif status in ('EXPIRED', 'DECLINED'):
|
||||
self._set_canceled(state_message=state_message)
|
||||
else:
|
||||
|
||||
@@ -452,7 +452,7 @@ class PaymentTransaction(models.Model):
|
||||
self._set_error(_(
|
||||
"The refund did not go through. Please log into your Stripe Dashboard to get "
|
||||
"more information on that matter, and address any accounting discrepancies."
|
||||
))
|
||||
), extra_allowed_states=('done',))
|
||||
else: # Classify unknown intent statuses as `error` tx state
|
||||
_logger.warning(
|
||||
"received invalid payment status (%s) for transaction with reference %s",
|
||||
|
||||
@@ -31,14 +31,14 @@ class PaymentTransaction(models.Model):
|
||||
for trans in self:
|
||||
trans.sale_order_ids_nbr = len(trans.sale_order_ids)
|
||||
|
||||
def _set_pending(self, state_message=None):
|
||||
def _set_pending(self, state_message=None, **kwargs):
|
||||
""" Override of `payment` to send the quotations automatically.
|
||||
|
||||
:param str state_message: The reason for which the transaction is set in 'pending' state.
|
||||
:return: updated transactions.
|
||||
:rtype: `payment.transaction` recordset.
|
||||
"""
|
||||
txs_to_process = super()._set_pending(state_message=state_message)
|
||||
txs_to_process = super()._set_pending(state_message=state_message, **kwargs)
|
||||
|
||||
for tx in txs_to_process: # Consider only transactions that are indeed set pending.
|
||||
sales_orders = tx.sale_order_ids.filtered(lambda so: so.state in ['draft', 'sent'])
|
||||
@@ -92,9 +92,9 @@ class PaymentTransaction(models.Model):
|
||||
)
|
||||
return confirmed_orders
|
||||
|
||||
def _set_authorized(self, state_message=None):
|
||||
def _set_authorized(self, state_message=None, **kwargs):
|
||||
""" Override of payment to confirm the quotations automatically. """
|
||||
super()._set_authorized(state_message=state_message)
|
||||
super()._set_authorized(state_message=state_message, **kwargs)
|
||||
confirmed_orders = self._check_amount_and_confirm_order()
|
||||
confirmed_orders._send_order_confirmation_mail()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user