From 088b9c23980b3e2700cb3354f710f321fb42be45 Mon Sep 17 00:00:00 2001 From: "Moises Lopez - https://www.vauxoo.com/" Date: Tue, 11 Jan 2022 20:29:59 +0000 Subject: [PATCH] [REF] account_check_printing: Speed-up payment creation The method constraint to validate the Check Number is slow 1. Analyzing the following query: ```sql SELECT payment.check_number, move.journal_id FROM account_payment payment JOIN account_move move ON move.id = payment.move_id JOIN account_journal journal ON journal.id = move.journal_id, account_payment other_payment JOIN account_move other_move ON other_move.id = other_payment.move_id WHERE payment.check_number::integer = other_payment.check_number::integer AND move.journal_id = other_move.journal_id AND payment.id != other_payment.id AND payment.id IN (1085159) AND move.state = 'posted' AND other_move.state = 'posted'; ``` The output is: Planning Time: 3.354 ms Execution Time: 2514.660 ms Discarding null values ```diff AND other_move.state = 'posted'; + AND payment.check_number IS NOT NULL + AND other_payment.check_number IS NOT NULL ``` The output is Planning Time: 3.216 ms Execution Time: 0.140 ms 2. The constraint is computed even if the payment is not a check (check_number is empty) Returning early save useless extra computating It is not needed to compare falsy values for duplicated for whole table 3. The validation to check is it not a number is not optimal It is transforming the string -> integer -> string to check if the string is not a number but it is enough using only string -> integer not needed to transform to string again python3 -m timeit -u msec -s "check_numbers = [str(i) for i in range(1000000)]" "[str(int(i)) for i in check_numbers]" > 1 loop, best of 5: 323 msec per loop python3 -m timeit -u msec -s "check_numbers = [str(i) for i in range(1000000)]" "[int(i) for i in check_numbers]" > 2 loops, best of 5: 135 msec per loop It is better but not enough, using `str.isdigit` method is 5x faster than original approach python3 -m timeit -u msec -s "check_numbers = [str(i) for i in range(1000000)]" "[i.isdecimal() for i in check_numbers]" > 5 loops, best of 5: 64 msec per loop closes odoo/odoo#83851 X-original-commit: 31e0ed8c957e8a6af7f6bd5d449b450967206eab Signed-off-by: Olivier Colson --- .../models/account_payment.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/account_check_printing/models/account_payment.py b/addons/account_check_printing/models/account_payment.py index 94c4953097e..c19c879712e 100644 --- a/addons/account_check_printing/models/account_payment.py +++ b/addons/account_check_printing/models/account_payment.py @@ -46,12 +46,12 @@ class AccountPayment(models.Model): @api.constrains('check_number', 'journal_id') def _constrains_check_number(self): - if not self: + payment_checks = self.filtered('check_number') + if not payment_checks: return - try: - self.mapped(lambda p: str(int(p.check_number))) - except ValueError: - raise ValidationError(_('Check numbers can only consist of digits')) + for payment_check in payment_checks: + if not payment_check.check_number.isdecimal(): + raise ValidationError(_('Check numbers can only consist of digits')) self.flush() self.env.cr.execute(""" SELECT payment.check_number, move.journal_id @@ -66,8 +66,10 @@ class AccountPayment(models.Model): AND payment.id IN %(ids)s AND move.state = 'posted' AND other_move.state = 'posted' + AND payment.check_number IS NOT NULL + AND other_payment.check_number IS NOT NULL """, { - 'ids': tuple(self.ids), + 'ids': tuple(payment_checks.ids), }) res = self.env.cr.dictfetchall() if res: