From 9b073040e13a42af92c88eb766dff83a51814946 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Logan=20G=C3=B4net?= Date: Mon, 6 Jan 2025 09:42:21 +0100 Subject: [PATCH] [13.0][Fix #25] Fixing validation date offset (#34) [Fix #25] Fixing validation date offset Currently, validation dates on transactions are stored in the French timezone, but in Odoo the timezone is expected to be in UTC. A minor fix is needed in the _payfip_evaluate_data method to convert the timezone into a UTC one. Migrate all previous dates validations to fix the timezone issue. All transactions are processed in chunk of one hundred. Signed-off-by: Logan Gonet --- payment_payfip/__manifest__.py | 10 +-- ...rench-tz-to-utc-payfip-transaction-date.py | 40 +++++++++ .../models/inherited_payment_transaction.py | 85 +++++++++++-------- 3 files changed, 92 insertions(+), 43 deletions(-) create mode 100644 payment_payfip/controllers/migrations/13.0.24.12.31/pre-french-tz-to-utc-payfip-transaction-date.py diff --git a/payment_payfip/__manifest__.py b/payment_payfip/__manifest__.py index 4e0c643..402c8b5 100644 --- a/payment_payfip/__manifest__.py +++ b/payment_payfip/__manifest__.py @@ -1,6 +1,6 @@ { 'name': "Intermédiaire de paiement PayFIP", - 'version': '13.0.1.0.0', + 'version': '13.0.24.12.31', 'summary': """Intermédiaire de paiement : Implémentation de PayFIP""", 'author': "Horanet", 'website': "https://www.horanet.com/", @@ -11,9 +11,7 @@ 'openupgradelib', ] }, - 'depends': [ - 'payment' - ], + 'depends': ['payment'], 'qweb': [], 'init_xml': [], 'update_xml': [], @@ -21,13 +19,11 @@ # Views must be before data to avoid loading issues 'views/payment_payfip_templates.xml', 'views/payment_views.xml', - 'data/payment_acquirer.xml', 'data/draft_payments_recovered_mail.xml', 'data/cron_check_drafts.xml', ], - 'demo': [ - ], + 'demo': [], 'application': False, 'auto_install': False, 'installable': True, diff --git a/payment_payfip/controllers/migrations/13.0.24.12.31/pre-french-tz-to-utc-payfip-transaction-date.py b/payment_payfip/controllers/migrations/13.0.24.12.31/pre-french-tz-to-utc-payfip-transaction-date.py new file mode 100644 index 0000000..c7b1acd --- /dev/null +++ b/payment_payfip/controllers/migrations/13.0.24.12.31/pre-french-tz-to-utc-payfip-transaction-date.py @@ -0,0 +1,40 @@ +import logging +import pytz + +from openupgradelib import openupgrade + +from odoo import fields + +_logger = logging.getLogger(__name__) + + +@openupgrade.migrate(use_env=True) +def migrate(env, version): + """Migrate Payfip transaction confirmation date to UTC date instead of french timezone.""" + # Payfip timezone + payfip_tz = pytz.timezone('Europe/Paris') + # Retreive all payfip transaction with a confirmation date + payfip_transactions = env['payment.transaction'].search( + [ + ('acquirer_id.provider', '=', 'payfip'), + ('date_validate', '!=', False), + ] + ) + + _logger.info(f"Number of Payfip transaction to migrate : {len(payfip_transactions)}") + chunk_size = 100 + # Chunk transactions for processing + payfip_transactions_chunked = [ + payfip_transactions[i : i + chunk_size] for i in range(0, len(payfip_transactions), chunk_size) + ] + + for idx, payfip_transaction_chunk in enumerate(payfip_transactions_chunked, start=1): + _logger.info(f"Payfip transaction processing chunk : {idx}/{len(payfip_transactions_chunked)}") + for payfip_transaction in payfip_transaction_chunk: + # Validate date to datetime object + date_validate = fields.Datetime.from_string(payfip_transaction.date_validate) + # Localize datetime and transform into an UTC one + utc_date_validate = payfip_tz.localize(date_validate).astimezone(pytz.UTC).replace(tzinfo=None) + # Set UTC value into datetime + payfip_transaction.date_validate = utc_date_validate + _logger.info(f"Payfip transaction all chunks have been processed.") diff --git a/payment_payfip/models/inherited_payment_transaction.py b/payment_payfip/models/inherited_payment_transaction.py index 8d3c2f0..f10ac16 100644 --- a/payment_payfip/models/inherited_payment_transaction.py +++ b/payment_payfip/models/inherited_payment_transaction.py @@ -81,6 +81,7 @@ def create(self, vals): def action_payfip_check_transaction(self): self.ensure_one() self._payfip_check_transactions() + # endregion # region Model methods @@ -149,45 +150,52 @@ def _payfip_evaluate_data(self, data=False): hour = int(payfip_datetime[:2]) minute = int(payfip_datetime[2:4]) td_minute = timedelta(minutes=1) + payfip_tz = pytz.timezone('Europe/Paris') date_validate = datetime(year, month, day, hour=hour, minute=minute) + td_minute - - self.write({ - 'state': 'done', - 'payfip_state': result, - 'date': date_validate, - 'payfip_amount': payfip_amount, - }) + date_validate = payfip_tz.localize(date_validate).astimezone(pytz.UTC).replace(tzinfo=None) + + self.write( + { + 'state': 'done', + 'payfip_state': result, + 'date': date_validate, + 'payfip_amount': payfip_amount, + } + ) return True elif result in ['A']: message = 'Received notification for PayFIP payment %s: set as canceled' % self.reference _logger.info(message) - self.write({ - 'state': 'cancel', - 'payfip_state': result, - 'payfip_amount': payfip_amount, - }) + self.write( + { + 'state': 'cancel', + 'payfip_state': result, + 'payfip_amount': payfip_amount, + } + ) return True elif result in ['R', 'Z']: message = 'Received notification for PayFIP payment %s: set as error' % self.reference _logger.info(message) - self.write({ - 'state': 'error', - 'payfip_state': result, - 'state_message': message, - 'payfip_amount': payfip_amount, - }) + self.write( + { + 'state': 'error', + 'payfip_state': result, + 'state_message': message, + 'payfip_amount': payfip_amount, + } + ) return True else: - message = 'Received unrecognized status for PayFIP payment %s: %s, set as error' % ( - self.reference, - result - ) + message = 'Received unrecognized status for PayFIP payment %s: %s, set as error' % (self.reference, result) _logger.error(message) - self.write({ - 'state': 'error', - 'payfip_state': 'U', - 'state_message': message, - }) + self.write( + { + 'state': 'error', + 'payfip_state': 'U', + 'state_message': message, + } + ) return False @api.model @@ -212,15 +220,19 @@ def payfip_cron_check_draft_payment_transactions(self, options=None): transaction_model = self.env['payment.transaction'] acquirer_model = self.env['payment.acquirer'] - payfip_acquirers = acquirer_model.search([ - ('provider', '=', 'payfip'), - ]) - transactions = transaction_model.search([ - ('acquirer_id', 'in', payfip_acquirers.ids), - ('state', 'in', ['draft', 'pending']), - ('payfip_operation_identifier', 'not in', [False, '']), - ('create_date', '>=', date_from), - ]) + payfip_acquirers = acquirer_model.search( + [ + ('provider', '=', 'payfip'), + ] + ) + transactions = transaction_model.search( + [ + ('acquirer_id', 'in', payfip_acquirers.ids), + ('state', 'in', ['draft', 'pending']), + ('payfip_operation_identifier', 'not in', [False, '']), + ('create_date', '>=', date_from), + ] + ) for tx in transactions: self.env['payment.transaction'].form_feedback(tx.payfip_operation_identifier, 'payfip') @@ -228,4 +240,5 @@ def payfip_cron_check_draft_payment_transactions(self, options=None): if send_summary: mail_template = self.env.ref('payment_payfip.mail_template_draft_payments_recovered') mail_template.with_context(transactions=transactions).send_mail(self.env.user.id) + # endregion