[IMP] payment: post-process transactions immediately
Instead of monitoring a list of transactions to post-process in the session, we now track only the last processed transaction, thus allowing to drop the 10 min delay of the post-processing cron. task-3125913 Part-of: odoo/odoo#119473 Co-authored-by: mano-odoo <mano@odoo.com>
This commit is contained in:
committed by
Antoine Vandevenne (anv)
co-authored by
mano-odoo
parent
23faf6f7ef
commit
cf52e381bd
@@ -1,11 +1,10 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
|
||||
import psycopg2
|
||||
|
||||
from odoo import fields, http
|
||||
from odoo import http
|
||||
from odoo.http import request
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
@@ -22,7 +21,7 @@ class PaymentPostProcessing(http.Controller):
|
||||
their post-processing.
|
||||
"""
|
||||
|
||||
MONITORED_TX_IDS_KEY = '__payment_monitored_tx_ids__'
|
||||
MONITORED_TX_ID_KEY = '__payment_monitored_tx_id__'
|
||||
|
||||
@http.route('/payment/status', type='http', auth='public', website=True, sitemap=False)
|
||||
def display_status(self, **kwargs):
|
||||
@@ -36,104 +35,54 @@ class PaymentPostProcessing(http.Controller):
|
||||
|
||||
@http.route('/payment/status/poll', type='json', auth='public')
|
||||
def poll_status(self, **_kwargs):
|
||||
""" Fetch the transactions to display on the status page and finalize their post-processing.
|
||||
""" Fetch the transaction to display on the status page and finalize its post-processing.
|
||||
|
||||
:return: The post-processing values of the transactions
|
||||
:return: The post-processing values of the transaction.
|
||||
:rtype: dict
|
||||
"""
|
||||
# Retrieve recent user's transactions from the session
|
||||
limit_date = fields.Datetime.now() - timedelta(days=1)
|
||||
monitored_txs = request.env['payment.transaction'].sudo().search([
|
||||
('id', 'in', self.get_monitored_transaction_ids()),
|
||||
('last_state_change', '>=', limit_date)
|
||||
])
|
||||
if not monitored_txs: # The transaction was not correctly created
|
||||
return {
|
||||
'success': False,
|
||||
'error': 'no_tx_found',
|
||||
}
|
||||
# Retrieve the last user's transaction from the session.
|
||||
monitored_tx = request.env['payment.transaction'].sudo().browse(
|
||||
self.get_monitored_transaction_id()
|
||||
).exists()
|
||||
if not monitored_tx: # The session might have expired, or the tx has never existed.
|
||||
raise Exception('tx_not_found')
|
||||
|
||||
# Build the list of display values with the display message and post-processing values
|
||||
display_values_list = []
|
||||
for tx in monitored_txs:
|
||||
display_message = None
|
||||
if tx.state == 'pending':
|
||||
display_message = tx.provider_id.pending_msg
|
||||
elif tx.state == 'done':
|
||||
display_message = tx.provider_id.done_msg
|
||||
elif tx.state == 'cancel':
|
||||
display_message = tx.provider_id.cancel_msg
|
||||
display_values_list.append({
|
||||
'display_message': display_message,
|
||||
**tx._get_post_processing_values(),
|
||||
})
|
||||
# Finalize the post-processing of the transaction before redirecting the user to the landing
|
||||
# route and its document.
|
||||
if monitored_tx.state == 'done' and not monitored_tx.is_post_processed:
|
||||
try:
|
||||
monitored_tx._finalize_post_processing()
|
||||
except psycopg2.OperationalError: # The database cursor could not be committed.
|
||||
request.env.cr.rollback() # Rollback and try later.
|
||||
raise Exception('retry')
|
||||
except Exception as e:
|
||||
request.env.cr.rollback()
|
||||
_logger.exception(
|
||||
"Encountered an error while post-processing transaction with id %s:\n%s",
|
||||
monitored_tx.id, e
|
||||
)
|
||||
raise
|
||||
|
||||
# Stop monitoring already post-processed transactions
|
||||
post_processed_txs = monitored_txs.filtered('is_post_processed')
|
||||
self.remove_transactions(post_processed_txs)
|
||||
|
||||
# Finalize post-processing of transactions before displaying them to the user
|
||||
txs_to_post_process = (monitored_txs - post_processed_txs).filtered(
|
||||
lambda t: t.state == 'done'
|
||||
)
|
||||
success, error = True, None
|
||||
try:
|
||||
txs_to_post_process._finalize_post_processing()
|
||||
except psycopg2.OperationalError: # A collision of accounting sequences occurred
|
||||
request.env.cr.rollback() # Rollback and try later
|
||||
success = False
|
||||
error = 'tx_process_retry'
|
||||
except Exception as e:
|
||||
request.env.cr.rollback()
|
||||
success = False
|
||||
error = str(e)
|
||||
_logger.exception(
|
||||
"encountered an error while post-processing transactions with ids %s:\n%s",
|
||||
', '.join([str(tx_id) for tx_id in txs_to_post_process.ids]), e
|
||||
)
|
||||
|
||||
return {
|
||||
'success': success,
|
||||
'error': error,
|
||||
'display_values_list': display_values_list,
|
||||
}
|
||||
# Return the post-processing values to display the transaction summary to the customer.
|
||||
return monitored_tx._get_post_processing_values()
|
||||
|
||||
@classmethod
|
||||
def monitor_transactions(cls, transactions):
|
||||
""" Add the ids of the provided transactions to the list of monitored transaction ids.
|
||||
def monitor_transaction(cls, transaction):
|
||||
""" Make the provided transaction id monitored.
|
||||
|
||||
:param recordset transactions: The transactions to monitor, as a `payment.transaction`
|
||||
recordset
|
||||
:param payment.transaction transaction: The transaction to monitor.
|
||||
:return: None
|
||||
"""
|
||||
if transactions:
|
||||
monitored_tx_ids = request.session.get(cls.MONITORED_TX_IDS_KEY, [])
|
||||
request.session[cls.MONITORED_TX_IDS_KEY] = list(
|
||||
set(monitored_tx_ids).union(transactions.ids)
|
||||
)
|
||||
request.session[cls.MONITORED_TX_ID_KEY] = transaction.id
|
||||
|
||||
@classmethod
|
||||
def get_monitored_transaction_ids(cls):
|
||||
""" Return the ids of transactions being monitored.
|
||||
def get_monitored_transaction_id(cls):
|
||||
""" Return the id of transaction being monitored.
|
||||
|
||||
Only the ids and not the recordset itself is returned to allow the caller browsing the
|
||||
recordset with sudo privileges, and using the ids in a custom query.
|
||||
Only the id and not the recordset itself is returned to allow the caller browsing the
|
||||
recordset with sudo privileges, and using the id in a custom query.
|
||||
|
||||
:return: The ids of transactions being monitored
|
||||
:return: The id of transactions being monitored
|
||||
:rtype: list
|
||||
"""
|
||||
return request.session.get(cls.MONITORED_TX_IDS_KEY, [])
|
||||
|
||||
@classmethod
|
||||
def remove_transactions(cls, transactions):
|
||||
""" Remove the ids of the provided transactions from the list of monitored transaction ids.
|
||||
|
||||
:param recordset transactions: The transactions to remove, as a `payment.transaction`
|
||||
recordset
|
||||
:return: None
|
||||
"""
|
||||
if transactions:
|
||||
monitored_tx_ids = request.session.get(cls.MONITORED_TX_IDS_KEY, [])
|
||||
request.session[cls.MONITORED_TX_IDS_KEY] = [
|
||||
tx_id for tx_id in monitored_tx_ids if tx_id not in transactions.ids
|
||||
]
|
||||
return request.session.get(cls.MONITORED_TX_ID_KEY)
|
||||
|
||||
Reference in New Issue
Block a user