From 0da67bce2173a7d828d4397e4c812841e61811f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Fri, 26 Dec 2025 10:57:44 +0100 Subject: [PATCH] Deprecate reduce_ids for SQL_OPERATORS['in'] Closes #12989 #13897 --- modules/account/account.py | 29 ++++++----- modules/account/company.py | 5 +- modules/account/journal.py | 6 +-- modules/account/move.py | 21 ++++---- modules/account/party.py | 5 +- modules/account_budget/account.py | 11 +++-- modules/account_deposit/party.py | 5 +- modules/account_invoice/invoice.py | 13 ++--- modules/account_invoice_history/account.py | 4 +- modules/account_payment/account.py | 5 +- modules/account_payment/payment.py | 8 ++-- modules/account_payment_clearing/payment.py | 5 +- modules/account_payment_sepa/payment.py | 12 +++-- modules/account_statement/journal.py | 10 ++-- modules/analytic_budget/analytic_account.py | 3 +- modules/commission/commission.py | 5 +- .../marketing_automation.py | 12 +++-- modules/marketing_email/marketing.py | 5 +- modules/product_price_list_cache/product.py | 15 +++--- modules/production_work/work.py | 5 +- modules/project_invoice/invoice.py | 4 +- modules/project_invoice/project.py | 11 +++-- modules/project_revenue/work.py | 8 ++-- modules/purchase/product.py | 5 +- modules/purchase_history/purchase.py | 5 +- modules/sale/sale_reporting.py | 8 ++-- modules/sale_history/sale.py | 5 +- modules/sale_point/sale.py | 8 ++-- modules/sale_promotion_coupon/sale.py | 5 +- modules/stock/move.py | 11 +++-- modules/stock/product.py | 14 ++++-- modules/stock/stock_reporting_margin.py | 5 +- modules/stock_forecast/forecast.py | 6 +-- modules/stock_quantity_issue/stock.py | 3 +- modules/stock_shipment_measurements/stock.py | 8 ++-- modules/stock_supply/purchase_request.py | 4 +- modules/stock_supply/shipment.py | 4 +- modules/timesheet/work.py | 6 +-- modules/web_shortener/web.py | 4 +- trytond/CHANGELOG | 1 + trytond/trytond/ir/cron.py | 4 +- trytond/trytond/ir/note.py | 4 +- trytond/trytond/ir/trigger.py | 11 +++-- trytond/trytond/model/modelsql.py | 1 - trytond/trytond/tests/test_tools.py | 48 +------------------ trytond/trytond/tools/misc.py | 48 +++---------------- 46 files changed, 191 insertions(+), 234 deletions(-) diff --git a/modules/account/account.py b/modules/account/account.py index 344dbd5012..6f17f816ac 100644 --- a/modules/account/account.py +++ b/modules/account/account.py @@ -22,8 +22,8 @@ from trytond.pool import Pool from trytond.pyson import Bool, Eval, Id, If, PYSONEncoder from trytond.report import Report from trytond.tools import ( - grouped_slice, is_full_text, lstrip_wildcard, pair, reduce_ids, - sql_pairing, sqlite_apply_types) + grouped_slice, is_full_text, lstrip_wildcard, pair, sql_pairing, + sqlite_apply_types) from trytond.transaction import Transaction, check_access, inactive_records from trytond.wizard import ( Button, StateAction, StateTransition, StateView, Wizard) @@ -1019,7 +1019,7 @@ class Account( with transaction.set_context(company=company.id): line_query, fiscalyear_ids = MoveLine.query_get(line) for sub_ids in grouped_slice(ids): - red_sql = reduce_ids(table_a.id, sub_ids) + red_sql = fields.SQL_OPERATORS['in'](table_a.id, sub_ids) if context.get('flat_balance'): query = (table_a .join(line, condition=line.account == table_a.id)) @@ -1092,7 +1092,7 @@ class Account( Sum(Coalesce(Column(line, name), 0)).as_(name)) types.append('NUMERIC') for sub_ids in grouped_slice(ids): - red_sql = reduce_ids(table.id, sub_ids) + red_sql = fields.SQL_OPERATORS['in'](table.id, sub_ids) query = (table.join(line, 'LEFT', condition=line.account == table.id ).select(*columns, @@ -1531,9 +1531,11 @@ class AccountParty(ActivePeriodMixin, ModelSQL): with transaction.set_context(company=company.id): line_query, fiscalyear_ids = MoveLine.query_get(line) for sub_account_ids in grouped_slice(account_ids): - account_sql = reduce_ids(table_a.id, sub_account_ids) + account_sql = fields.SQL_OPERATORS['in']( + table_a.id, sub_account_ids) for sub_party_ids in grouped_slice(party_ids): - party_sql = reduce_ids(line.party, sub_party_ids) + party_sql = fields.SQL_OPERATORS['in']( + line.party, sub_party_ids) if context.get('flat_balance'): query = (table_a .join(line, condition=line.account == table_a.id)) @@ -1608,9 +1610,11 @@ class AccountParty(ActivePeriodMixin, ModelSQL): line_query, fiscalyear_ids = MoveLine.query_get(line) for sub_account_ids in grouped_slice(account_ids): - account_sql = reduce_ids(table.id, sub_account_ids) + account_sql = fields.SQL_OPERATORS['in']( + table.id, sub_account_ids) for sub_party_ids in grouped_slice(party_ids): - party_sql = reduce_ids(line.party, sub_party_ids) + party_sql = fields.SQL_OPERATORS['in']( + line.party, sub_party_ids) query = (table.join(line, 'LEFT', condition=line.account == table.id ).select(*columns, @@ -1750,7 +1754,8 @@ class AccountDeferral(ModelSQL, ModelView): balances = defaultdict(Decimal) for sub_deferrals in grouped_slice(deferrals): - red_sql = reduce_ids(table.id, [d.id for d in sub_deferrals]) + red_sql = fields.SQL_OPERATORS['in']( + table.id, [d.id for d in sub_deferrals]) query = (table .join(account, condition=table.account == account.id) .join(account_child, @@ -2487,10 +2492,12 @@ class GeneralLedgerLine(DescriptionOriginMixin, ModelSQL, ModelView): query = account_party.select( account_party.account, account_party.party, account_party.id) for sub_account_ids in grouped_slice(account_ids): - account_where = reduce_ids(account_party.account, sub_account_ids) + account_where = fields.SQL_OPERATORS['in']( + account_party.account, sub_account_ids) for sub_party_ids in grouped_slice(party_ids): query.where = (account_where - & reduce_ids(account_party.party, sub_party_ids)) + & fields.SQL_OPERATORS['in']( + account_party.party, sub_party_ids)) cursor.execute(*query) for account, party, id_ in cursor: key = (account, party) diff --git a/modules/account/company.py b/modules/account/company.py index 0d1fff085c..8eaad4a99e 100644 --- a/modules/account/company.py +++ b/modules/account/company.py @@ -12,7 +12,7 @@ from trytond.model import fields from trytond.model.exceptions import AccessError from trytond.modules.currency.fields import Monetary from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction @@ -67,7 +67,8 @@ class Company(metaclass=PoolMeta): today_where = Literal(True) for sub_companies in grouped_slice(companies): sub_ids = [p.id for p in sub_companies] - company_where = reduce_ids(account.company, sub_ids) + company_where = fields.SQL_OPERATORS['in']( + account.company, sub_ids) query = (line .join(account, condition=account.id == line.account) diff --git a/modules/account/journal.py b/modules/account/journal.py index e1e09d0783..29c388f71f 100644 --- a/modules/account/journal.py +++ b/modules/account/journal.py @@ -14,8 +14,7 @@ from trytond.modules.currency.fields import Monetary from trytond.pool import Pool from trytond.pyson import Eval from trytond.tools import ( - grouped_slice, is_full_text, lstrip_wildcard, reduce_ids, - sqlite_apply_types) + grouped_slice, is_full_text, lstrip_wildcard, sqlite_apply_types) from trytond.transaction import Transaction STATES = { @@ -115,7 +114,8 @@ class Journal( & (move.company == company.id)) for sub_journals in grouped_slice(journals): sub_journals = list(sub_journals) - red_sql = reduce_ids(move.journal, [j.id for j in sub_journals]) + red_sql = fields.SQL_OPERATORS['in']( + move.journal, [j.id for j in sub_journals]) query = line.join(move, 'LEFT', condition=line.move == move.id ).join(account, 'LEFT', condition=line.account == account.id ).join(account_type, 'LEFT', diff --git a/modules/account/move.py b/modules/account/move.py index 23567f3473..c88dac1b31 100644 --- a/modules/account/move.py +++ b/modules/account/move.py @@ -21,8 +21,7 @@ from trytond.pool import Pool from trytond.pyson import Bool, Eval, If, PYSONEncoder from trytond.report import Report from trytond.rpc import RPC -from trytond.tools import ( - firstline, grouped_slice, reduce_ids, sqlite_apply_types) +from trytond.tools import firstline, grouped_slice, sqlite_apply_types from trytond.transaction import Transaction, check_access from trytond.wizard import ( Button, StateAction, StateTransition, StateView, Wizard) @@ -363,7 +362,8 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView): for company, moves in groupby(moves, key=lambda m: m.company): currency = company.currency for sub_moves in grouped_slice(list(moves)): - red_sql = reduce_ids(line.move, [m.id for m in sub_moves]) + red_sql = fields.SQL_OPERATORS['in']( + line.move, [m.id for m in sub_moves]) valid_move_query = line.select( line.move, @@ -465,7 +465,8 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView): cursor.execute(*move.select( move.id, - where=reduce_ids(move.id, sub_moves_ids) + where=fields.SQL_OPERATORS['in']( + move.id, sub_moves_ids) & ~Exists(line.select( line.move, where=line.move == move.id)))) @@ -480,7 +481,8 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView): cursor.execute(*line.select( line.move, - where=reduce_ids(line.move, sub_moves_ids), + where=fields.SQL_OPERATORS['in']( + line.move, sub_moves_ids), group_by=line.move, having=Abs(Round( Sum(line.debit - line.credit), @@ -496,7 +498,8 @@ class Move(DescriptionOriginMixin, ModelSQL, ModelView): cursor.execute(*line.select( line.id, - where=reduce_ids(line.move, sub_moves_ids) + where=fields.SQL_OPERATORS['in']( + line.move, sub_moves_ids) & (line.debit == Decimal(0)) & (line.credit == Decimal(0)) & (line.reconciliation == Null) @@ -864,7 +867,8 @@ class MoveLineMixin: parties = {l.party for l in sub_lines} if None in parties: party_where |= line.party == parties.discard(None) - party_where |= reduce_ids(line.party, map(int, parties)) + party_where |= fields.SQL_OPERATORS['in']( + line.party, map(int, parties)) where &= party_where query = (line @@ -878,7 +882,8 @@ class MoveLineMixin: where=where)) query = query.select( query.id, query.balance.as_('balance'), - where=reduce_ids(query.id, [l.id for l in sub_lines])) + where=fields.SQL_OPERATORS['in']( + query.id, [l.id for l in sub_lines])) if backend.name == 'sqlite': sqlite_apply_types(query, [None, 'NUMERIC']) cursor.execute(*query) diff --git a/modules/account/party.py b/modules/account/party.py index 0594d29967..0b082fa4d6 100644 --- a/modules/account/party.py +++ b/modules/account/party.py @@ -16,7 +16,7 @@ from trytond.modules.currency.fields import Monetary from trytond.modules.party.exceptions import EraseError from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval, If -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.tools import timezone as tz from trytond.transaction import Transaction @@ -165,7 +165,8 @@ class Party(CompanyMultiValueMixin, metaclass=PoolMeta): columns.append(Coalesce(expressions[name], Decimal()).as_(name)) if parties is not None: - party_where = reduce_ids(move_line.party, [p.id for p in parties]) + party_where = fields.SQL_OPERATORS['in']( + move_line.party, [p.id for p in parties]) else: party_where = Literal(True) diff --git a/modules/account_budget/account.py b/modules/account_budget/account.py index 5ef7cb3b95..77858c0b0e 100644 --- a/modules/account_budget/account.py +++ b/modules/account_budget/account.py @@ -17,7 +17,7 @@ from trytond.modules.currency.fields import Monetary from trytond.pool import Pool from trytond.pyson import Bool, Eval, If from trytond.rpc import RPC -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction from trytond.wizard import ( Button, StateAction, StateTransition, StateView, Wizard) @@ -213,7 +213,7 @@ class BudgetLineMixin( sqlite_apply_types(query, [None, 'NUMERIC']) for sub_ids in grouped_slice(ids): - query.where = reduce_ids(table.id, sub_ids) + query.where = fields.SQL_OPERATORS['in'](table.id, sub_ids) cursor.execute(*query) amounts.update(cursor) @@ -549,7 +549,7 @@ class BudgetLine(BudgetLineMixin, ModelSQL, ModelView): line = Line.__table__() amount = Sum(Coalesce(line.credit, 0) - Coalesce(line.debit, 0)) - red_sql = reduce_ids(table.id, [r.id for r in records]) + red_sql = fields.SQL_OPERATORS['in'](table.id, [r.id for r in records]) periods = Transaction().context.get('periods') if not periods: periods = [p.id for p in Period.search([ @@ -693,7 +693,7 @@ class BudgetLinePeriod(AmountMixin, ModelSQL, ModelView): line = MoveLine.__table__() amount = Sum(Coalesce(line.credit, 0) - Coalesce(line.debit, 0)) - red_sql = reduce_ids(table.id, [r.id for r in records]) + red_sql = fields.SQL_OPERATORS['in'](table.id, [r.id for r in records]) periods = Transaction().context.get('periods') if not periods: periods = [p.id for p in Period.search([ @@ -737,7 +737,8 @@ class BudgetLinePeriod(AmountMixin, ModelSQL, ModelView): for sub_ids in grouped_slice({p.budget_line.id for p in periods}): cursor.execute(*table.select( table.budget_line, - where=reduce_ids(table.budget_line, sub_ids), + where=fields.SQL_OPERATORS['in']( + table.budget_line, sub_ids), group_by=table.budget_line, having=Sum(table.ratio) > 1, limit=1)) diff --git a/modules/account_deposit/party.py b/modules/account_deposit/party.py index cb5514561d..2a69430f7b 100644 --- a/modules/account_deposit/party.py +++ b/modules/account_deposit/party.py @@ -12,7 +12,7 @@ from trytond.model import fields from trytond.modules.currency.fields import Monetary from trytond.modules.party.exceptions import EraseError from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction @@ -46,7 +46,8 @@ class Party(metaclass=PoolMeta): line_clause, _ = MoveLine.query_get(line) for sub_parties in grouped_slice(parties): - party_clause = reduce_ids(line.party, [p.id for p in sub_parties]) + party_clause = fields.SQL_OPERATORS['in']( + line.party, [p.id for p in sub_parties]) query = (line .join(account, condition=account.id == line.account) .join(account_type, condition=account.type == account_type.id) diff --git a/modules/account_invoice/invoice.py b/modules/account_invoice/invoice.py index 1bab8b40ba..31169292ca 100644 --- a/modules/account_invoice/invoice.py +++ b/modules/account_invoice/invoice.py @@ -32,8 +32,7 @@ from trytond.pyson import Bool, Eval, Id, If from trytond.report import Report from trytond.rpc import RPC from trytond.tools import ( - cached_property, firstline, grouped_slice, reduce_ids, slugify, - sqlite_apply_types) + cached_property, firstline, grouped_slice, slugify, sqlite_apply_types) from trytond.transaction import Transaction from trytond.wizard import ( Button, StateAction, StateReport, StateTransition, StateView, Wizard) @@ -829,8 +828,9 @@ class Invoice( type_name = cls.tax_amount._field.sql_type().base tax = InvoiceTax.__table__() - for sub_ids in grouped_slice(invoices_no_cache): - red_sql = reduce_ids(tax.invoice, sub_ids) + for sub_invoices in grouped_slice(invoices_no_cache): + red_sql = fields.SQL_OPERATORS['in']( + tax.invoice, map(int, sub_invoices)) query = (tax.select(tax.invoice, Coalesce(Sum(tax.amount), 0).as_(type_name), where=red_sql, @@ -885,7 +885,8 @@ class Invoice( invoice = cls.__table__() additional_move = AdditionalMove.__table__() - red_sql = reduce_ids(invoice.id, invoices) + red_sql = fields.SQL_OPERATORS['in']( + invoice.id, map(int, invoices)) query = (invoice .join(line, condition=((invoice.move == line.move) @@ -1152,7 +1153,7 @@ class Invoice( for sub_invoices in grouped_slice(invoices): sub_ids = map(int, sub_invoices) cursor.execute(*table.select(table.id, has_cache, - where=reduce_ids(table.id, sub_ids))) + where=fields.SQL_OPERATORS['in'](table.id, sub_ids))) result.update(cursor) return result diff --git a/modules/account_invoice_history/account.py b/modules/account_invoice_history/account.py index 743b3c0876..ad26dcd29c 100644 --- a/modules/account_invoice_history/account.py +++ b/modules/account_invoice_history/account.py @@ -7,7 +7,7 @@ from sql.functions import CurrentTimestamp from trytond import backend from trytond.model import ModelView, Workflow, fields from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction @@ -55,7 +55,7 @@ class Invoice(metaclass=PoolMeta): Greatest(table.numbered_at, party.create_date, address.create_date, identifier.create_date, payment_term.create_date).as_('history_datetime'), - where=reduce_ids(table.id, ids) + where=fields.SQL_OPERATORS['in'](table.id, ids) & (table.numbered_at != Null) & (table.state.in_(cls._history_states())))) if backend.name == 'sqlite': diff --git a/modules/account_payment/account.py b/modules/account_payment/account.py index 0bb63deb73..5c5078ca0e 100644 --- a/modules/account_payment/account.py +++ b/modules/account_payment/account.py @@ -19,7 +19,7 @@ from trytond.modules.company.model import CompanyValueMixin from trytond.modules.currency.fields import Monetary from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, Id, If -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction, check_access, without_check_access from trytond.wizard import ( Button, StateAction, StateTransition, StateView, Wizard) @@ -195,7 +195,8 @@ class MoveLine(metaclass=PoolMeta): for sub_lines in grouped_slice(lines): query.where = ( table.account.in_(accounts) - & reduce_ids(table.id, map(int, sub_lines))) + & fields.SQL_OPERATORS['in']( + table.id, map(int, sub_lines))) cursor.execute(*query) else: cursor.execute(*query) diff --git a/modules/account_payment/payment.py b/modules/account_payment/payment.py index c167e5b893..f3b32db5c2 100644 --- a/modules/account_payment/payment.py +++ b/modules/account_payment/payment.py @@ -22,8 +22,7 @@ from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval, If from trytond.rpc import RPC from trytond.tools import ( - cursor_dict, grouped_slice, reduce_ids, sortable_values, - sqlite_apply_types) + cursor_dict, grouped_slice, sortable_values, sqlite_apply_types) from trytond.transaction import Transaction from trytond.wizard import StateAction, Wizard @@ -202,9 +201,10 @@ class Group(ModelSQL, ModelView, ChatMixin): ).as_('payment_not_complete'), ] - for sub_ids in grouped_slice(groups): + for sub_groups in grouped_slice(groups): query = payment.select(*columns, - where=reduce_ids(payment.group, sub_ids), + where=fields.SQL_OPERATORS['in']( + payment.group, map(int, sub_groups)), group_by=payment.group) if backend.name == 'sqlite': sqlite_apply_types( diff --git a/modules/account_payment_clearing/payment.py b/modules/account_payment_clearing/payment.py index 6fd02cd778..d21c5323da 100644 --- a/modules/account_payment_clearing/payment.py +++ b/modules/account_payment_clearing/payment.py @@ -13,7 +13,7 @@ from trytond.model import ModelView, Workflow, fields from trytond.modules.account.exceptions import AccountMissing from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, If, TimeDelta -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard @@ -447,7 +447,8 @@ class Group(metaclass=PoolMeta): for sub_groups in grouped_slice(groups): cursor.execute(*payment.select( payment.group, column, - where=reduce_ids(payment.group, sub_groups), + where=fields.SQL_OPERATORS['in']( + payment.group, map(int, sub_groups)), group_by=payment.group)) result.update(cursor) return result diff --git a/modules/account_payment_sepa/payment.py b/modules/account_payment_sepa/payment.py index a315397ea8..dcaa0513a4 100644 --- a/modules/account_payment_sepa/payment.py +++ b/modules/account_payment_sepa/payment.py @@ -26,7 +26,7 @@ from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval, If from trytond.report import Report from trytond.tools import ( - cached_property, grouped_slice, is_full_text, lstrip_wildcard, reduce_ids, + cached_property, grouped_slice, is_full_text, lstrip_wildcard, sortable_values) from trytond.transaction import Transaction @@ -786,8 +786,9 @@ class Mandate(Workflow, ModelSQL, ModelView): cursor = Transaction().connection.cursor() has_payments = dict.fromkeys([m.id for m in mandates], False) - for sub_ids in grouped_slice(mandates): - red_sql = reduce_ids(payment.sepa_mandate, sub_ids) + for sub_mandates in grouped_slice(mandates): + red_sql = fields.SQL_OPERATORS['in']( + payment.sepa_mandate, map(int, sub_mandates)) cursor.execute(*payment.select(payment.sepa_mandate, Literal(True), where=red_sql, group_by=payment.sepa_mandate)) @@ -803,8 +804,9 @@ class Mandate(Workflow, ModelSQL, ModelView): cursor = Transaction().connection.cursor() is_first = dict.fromkeys([m.id for m in mandates], True) - for sub_ids in grouped_slice(mandates): - red_sql = reduce_ids(payment.sepa_mandate, sub_ids) + for sub_mandates in grouped_slice(mandates): + red_sql = fields.SQL_OPERATORS['in']( + payment.sepa_mandate, map(int, sub_mandates)) cursor.execute(*payment.select( payment.sepa_mandate, Literal(False), where=red_sql diff --git a/modules/account_statement/journal.py b/modules/account_statement/journal.py index e1ee2e81e7..8dec0a7cc7 100644 --- a/modules/account_statement/journal.py +++ b/modules/account_statement/journal.py @@ -10,8 +10,7 @@ from trytond.modules.currency.fields import Monetary from trytond.pool import Pool from trytond.pyson import Eval from trytond.rpc import RPC -from trytond.tools import ( - cursor_dict, grouped_slice, reduce_ids, sqlite_apply_types) +from trytond.tools import cursor_dict, grouped_slice, sqlite_apply_types from trytond.transaction import Transaction @@ -145,9 +144,10 @@ class Journal(DeactivableMixin, ModelSQL, ModelView): columns.append( FirstValue(statement.date, window=w).as_('last_date')) query = statement.select(*columns, - distinct=True, - where=reduce_ids(statement.journal, sub_ids) - & (statement.state != 'cancelled')) + distinct=True, + where=(fields.SQL_OPERATORS['in']( + statement.journal, sub_ids) + & (statement.state != 'cancelled'))) if backend.name == 'sqlite': types = [None] if 'last_amount' in names: diff --git a/modules/analytic_budget/analytic_account.py b/modules/analytic_budget/analytic_account.py index d5a839cd02..7a27c2a5ca 100644 --- a/modules/analytic_budget/analytic_account.py +++ b/modules/analytic_budget/analytic_account.py @@ -8,7 +8,6 @@ from trytond.modules.account_budget import ( BudgetLineMixin, BudgetMixin, CopyBudgetMixin, CopyBudgetStartMixin) from trytond.pool import Pool from trytond.pyson import Eval -from trytond.tools import reduce_ids from trytond.transaction import Transaction from trytond.wizard import Button, StateAction, StateView, Wizard @@ -179,7 +178,7 @@ class BudgetLine(BudgetLineMixin, ModelSQL, ModelView): children = cls.__table__() balance = Sum(Coalesce(line.credit, 0) - Coalesce(line.debit, 0)) - red_sql = reduce_ids(table.id, [r.id for r in records]) + red_sql = fields.SQL_OPERATORS['in'](table.id, [r.id for r in records]) with Transaction().set_context(context): query_where = Line.query_get(line) return (table diff --git a/modules/commission/commission.py b/modules/commission/commission.py index 6191735305..f4956e3fff 100644 --- a/modules/commission/commission.py +++ b/modules/commission/commission.py @@ -21,8 +21,7 @@ from trytond.modules.currency.fields import Monetary from trytond.modules.product import price_digits, round_price from trytond.pool import Pool from trytond.pyson import Bool, Eval, Id, If -from trytond.tools import ( - decistmt, grouped_slice, reduce_ids, sqlite_apply_types) +from trytond.tools import decistmt, grouped_slice, sqlite_apply_types from trytond.transaction import Transaction, check_access from trytond.wizard import Button, StateAction, StateView, Wizard @@ -129,7 +128,7 @@ class Agent(DeactivableMixin, ModelSQL, ModelView): ids = [a.id for a in agents] amounts = dict.fromkeys(ids, None) for sub_ids in grouped_slice(ids): - where = reduce_ids(commission.agent, sub_ids) + where = fields.SQL_OPERATORS['in'](commission.agent, sub_ids) where &= commission.invoice_line == Null query = commission.select( commission.agent, Sum(commission.amount).as_('pending_amount'), diff --git a/modules/marketing_automation/marketing_automation.py b/modules/marketing_automation/marketing_automation.py index 0a6c7c4444..d8a40eccbc 100644 --- a/modules/marketing_automation/marketing_automation.py +++ b/modules/marketing_automation/marketing_automation.py @@ -26,7 +26,7 @@ from trytond.pool import Pool from trytond.pyson import Eval, If, PYSONDecoder, TimeDelta from trytond.report import Report, html_to_text, mjml_to_html from trytond.sendmail import SMTPDataManager, send_message_transactional -from trytond.tools import grouped_slice, pairwise_longest, reduce_ids +from trytond.tools import grouped_slice, pairwise_longest from trytond.tools.chart import sparkline from trytond.tools.email_ import format_address, has_rcpt, set_from_header from trytond.transaction import Transaction @@ -178,12 +178,13 @@ class Scenario( others.append(scenario) count = {name: defaultdict(int) for name in names} - for sub in grouped_slice(others): + for sub_others in grouped_slice(others): cursor.execute(*record.select( record.scenario, Count(), Count(filter_=record.blocked), - where=reduce_ids(record.scenario, sub), + where=fields.SQL_OPERATORS['in']( + record.scenario, map(int, sub_others)), group_by=record.scenario)) for id_, all_, blocked in cursor: if 'record_count' in count: @@ -521,13 +522,14 @@ class Activity( cursor = Transaction().connection.cursor() count = {name: defaultdict(int) for name in names} - for sub in grouped_slice(activities): + for sub_activities in grouped_slice(activities): cursor.execute(*record_activity.select( record_activity.activity, Count(filter_=record_activity.state == 'done'), Count(filter_=record_activity.email_opened), Count(filter_=record_activity.email_clicked), - where=reduce_ids(record_activity.activity, sub), + where=fields.SQL_OPERATORS['in']( + record_activity.activity, map(int, sub_activities)), group_by=record_activity.activity)) for id_, all_, email_opened, email_clicked in cursor: if 'record_count' in count: diff --git a/modules/marketing_email/marketing.py b/modules/marketing_email/marketing.py index 392adca194..210673d159 100644 --- a/modules/marketing_email/marketing.py +++ b/modules/marketing_email/marketing.py @@ -22,7 +22,7 @@ from trytond.pool import Pool from trytond.pyson import Eval from trytond.report import Report, get_email, html_to_text, mjml_to_html from trytond.sendmail import SMTPDataManager, send_message_transactional -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.tools.email_ import ( EmailNotValidError, format_address, normalize_email, set_from_header, validate_email) @@ -259,7 +259,8 @@ class EmailList(DeactivableMixin, ModelSQL, ModelView): email.list_, Count(), group_by=[email.list_]) for sub_lists in grouped_slice(lists): query.where = ( - reduce_ids(email.list_, sub_lists) + fields.SQL_OPERATORS['in']( + email.list_, map(int, sub_lists)) & email.active) cursor.execute(*query) subscribed.update(cursor) diff --git a/modules/product_price_list_cache/product.py b/modules/product_price_list_cache/product.py index 149cfb921f..93c47c2523 100644 --- a/modules/product_price_list_cache/product.py +++ b/modules/product_price_list_cache/product.py @@ -10,7 +10,7 @@ from trytond.cache import Cache, freeze from trytond.model import ModelSQL, dualmethod, fields from trytond.pool import Pool, PoolMeta from trytond.protocols.jsonrpc import JSONDecoder, JSONEncoder -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction dumps = partial( @@ -164,20 +164,21 @@ class PriceListCache(ModelSQL): cursor.execute(*cache.delete()) elif price_lists and products is None: for sub_price_lists in grouped_slice(price_lists): - cursor.execute(*cache.delete(where=reduce_ids( + cursor.execute(*cache.delete(where=fields.SQL_OPERATORS['in']( cache.price_list, [ p.id for p in sub_price_lists]))) elif price_lists is None and products: for sub_products in grouped_slice(products): - cursor.execute(*cache.delete(where=reduce_ids( + cursor.execute(*cache.delete(where=fields.SQL_OPERATORS['in']( cache.product, [p.id for p in sub_products]))) else: for sub_products in grouped_slice(products): for sub_price_lists in grouped_slice(price_lists): - cursor.execute(*cache.delete(where=reduce_ids( - cache.price_list, [ - p.id for p in sub_price_lists]) - & reduce_ids( + cursor.execute(*cache.delete( + where=fields.SQL_OPERATORS['in']( + cache.price_list, [ + p.id for p in sub_price_lists]) + & fields.SQL_OPERATORS['in']( cache.product, [p.id for p in sub_products]))) @classmethod diff --git a/modules/production_work/work.py b/modules/production_work/work.py index c9c05fd3a4..0207d0d08b 100644 --- a/modules/production_work/work.py +++ b/modules/production_work/work.py @@ -19,7 +19,7 @@ from trytond.modules.company.model import employee_field, set_employee from trytond.modules.product import price_digits, round_price from trytond.pool import Pool from trytond.pyson import Bool, Eval, If, TimeDelta -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction from .exceptions import PickerError @@ -250,7 +250,8 @@ class Work(sequence_ordered(), ModelSQL, ModelView, ChatMixin): costs = defaultdict(Decimal) for sub_works in grouped_slice(works): - red_sql = reduce_ids(cycle.work, [w.id for w in sub_works]) + red_sql = fields.SQL_OPERATORS['in']( + cycle.work, [w.id for w in sub_works]) query = cycle.select( cycle.work, Sum(Coalesce(cycle.cost, 0)).as_('cost'), where=red_sql & (cycle.state == 'done'), diff --git a/modules/project_invoice/invoice.py b/modules/project_invoice/invoice.py index 314a4975ad..3a5bd5ae86 100644 --- a/modules/project_invoice/invoice.py +++ b/modules/project_invoice/invoice.py @@ -10,7 +10,7 @@ from trytond.model import fields from trytond.modules.account_invoice.exceptions import ( InvoiceLineValidationError) from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -92,7 +92,7 @@ class InvoiceLine(metaclass=PoolMeta): ts_line.invoice_line, Sum(ts_line.duration), group_by=ts_line.invoice_line) for sub_lines in grouped_slice(lines): - query.where = reduce_ids( + query.where = fields.SQL_OPERATORS['in']( ts_line.invoice_line, map(int, sub_lines)) cursor.execute(*query) diff --git a/modules/project_invoice/project.py b/modules/project_invoice/project.py index cca22943be..4491616a33 100644 --- a/modules/project_invoice/project.py +++ b/modules/project_invoice/project.py @@ -18,7 +18,7 @@ from trytond.model.exceptions import AccessError from trytond.modules.currency.fields import Monetary from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, Id, If -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction from trytond.wizard import StateAction, Wizard @@ -114,7 +114,7 @@ class Progress: quantities = {} for sub_works in grouped_slice(works): sub_works = list(sub_works) - where = reduce_ids( + where = fields.SQL_OPERATORS['in']( table.id, [x.id for x in sub_works if x.invoice_unit_price]) cursor.execute(*table.join(progress, condition=progress.work == table.id @@ -162,7 +162,7 @@ class Progress: work2currency = {} ids2work = dict((w.id, w) for w in works) for sub_ids in grouped_slice(ids2work.keys()): - where = reduce_ids(table.id, sub_ids) + where = fields.SQL_OPERATORS['in'](table.id, sub_ids) query = (table.join(progress, condition=progress.work == table.id ).join(invoice_line, @@ -275,7 +275,8 @@ class Timesheet: group_by=line.work) for upto, tworks in upto2tworks.items(): for sub_ids in grouped_slice(tworks): - query.where = (reduce_ids(line.work, sub_ids) + query.where = ( + fields.SQL_OPERATORS['in'](line.work, sub_ids) & (line.invoice_line == Null)) if upto: query.where &= (line.date <= upto) @@ -318,7 +319,7 @@ class Timesheet: work2currency = {} work_ids = [w.id for w in works] for sub_ids in grouped_slice(work_ids): - where = reduce_ids(table.id, sub_ids) + where = fields.SQL_OPERATORS['in'](table.id, sub_ids) cursor.execute(*table.join(timesheet_work, condition=( Concat(cls.__name__ + ',', table.id) diff --git a/modules/project_revenue/work.py b/modules/project_revenue/work.py index 33089da127..28cb08f4ef 100644 --- a/modules/project_revenue/work.py +++ b/modules/project_revenue/work.py @@ -13,7 +13,7 @@ from trytond.modules.currency.fields import Monetary from trytond.modules.product import price_digits, round_price from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -95,13 +95,13 @@ class Work(metaclass=PoolMeta): work_ids = [w.id for w in works] for sub_ids in grouped_slice(work_ids): - red_sql = reduce_ids(table.id, sub_ids) + where = fields.SQL_OPERATORS['in'](table.id, sub_ids) cursor.execute(*table.join(work, condition=( Concat(cls.__name__ + ',', table.id) == work.origin) ).join(line, condition=line.work == work.id ).select(table.id, Sum(line.cost_price * line.duration), - where=red_sql, + where=where, group_by=[table.id])) for work_id, cost in cursor: # SQLite stores timedelta as float @@ -135,7 +135,7 @@ class Work(metaclass=PoolMeta): work2currency = {} iline2work = {} for sub_ids in grouped_slice(work_ids): - where = reduce_ids(table.id, sub_ids) + where = fields.SQL_OPERATORS['in'](table.id, sub_ids) cursor.execute(*table.join(purchase_line, condition=purchase_line.work == table.id ).join(invoice_line, diff --git a/modules/purchase/product.py b/modules/purchase/product.py index 9b92f3b416..07d240717c 100644 --- a/modules/purchase/product.py +++ b/modules/purchase/product.py @@ -13,8 +13,7 @@ from trytond.modules.product import ( ProductDeactivatableMixin, price_digits, round_price) from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, If, TimeDelta -from trytond.tools import ( - grouped_slice, is_full_text, lstrip_wildcard, reduce_ids) +from trytond.tools import grouped_slice, is_full_text, lstrip_wildcard from trytond.transaction import Transaction from .exceptions import PurchaseUOMWarning @@ -160,7 +159,7 @@ class Product(metaclass=PoolMeta): .join(purchase, condition=line.purchase == purchase.id) .select( Max(line.id), - where=where & reduce_ids( + where=where & fields.SQL_OPERATORS['in']( line.product, map(int, sub_products)), group_by=[line.product])) cursor.execute(*query) diff --git a/modules/purchase_history/purchase.py b/modules/purchase_history/purchase.py index 38a95ccbbe..f4f0dbc7f7 100644 --- a/modules/purchase_history/purchase.py +++ b/modules/purchase_history/purchase.py @@ -4,7 +4,7 @@ from trytond.model import ModelView, Workflow, fields from trytond.pool import PoolMeta from trytond.pyson import Eval -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -49,7 +49,8 @@ class Purchase(metaclass=PoolMeta): cursor.execute(*table.update( [table.revision], [table.revision + 1], - where=reduce_ids(table.id, sub_purchases))) + where=fields.SQL_OPERATORS['in']( + table.id, map(int, sub_purchases)))) super().draft(purchases) diff --git a/modules/sale/sale_reporting.py b/modules/sale/sale_reporting.py index 4161d4cddb..ace2085f00 100644 --- a/modules/sale/sale_reporting.py +++ b/modules/sale/sale_reporting.py @@ -15,7 +15,7 @@ from trytond.model import ModelSQL, ModelView, UnionMixin, fields, sum_tree from trytond.modules.currency.fields import Monetary from trytond.pool import Pool from trytond.pyson import Eval, If -from trytond.tools import grouped_slice, pairwise_longest, reduce_ids +from trytond.tools import grouped_slice, pairwise_longest from trytond.tools.chart import sparkline from trytond.transaction import Transaction from trytond.wizard import StateAction, StateTransition, Wizard @@ -517,7 +517,8 @@ class CustomerCategoryTree(ModelSQL, ModelView): reporting_customer_categories = [] for sub_ids in grouped_slice(ids): sub_ids = list(sub_ids) - where = reduce_ids(reporting_product_category.id, sub_ids) + where = fields.SQL_OPERATORS['in']( + reporting_product_category.id, sub_ids) cursor.execute( *reporting_product_category.select( reporting_product_category.id, where=where)) @@ -758,7 +759,8 @@ class ProductCategoryTree(ModelSQL, ModelView): reporting_product_categories = [] for sub_ids in grouped_slice(ids): sub_ids = list(sub_ids) - where = reduce_ids(reporting_product_category.id, sub_ids) + where = fields.SQL_OPERATORS['in']( + reporting_product_category.id, sub_ids) cursor.execute( *reporting_product_category.select( reporting_product_category.id, where=where)) diff --git a/modules/sale_history/sale.py b/modules/sale_history/sale.py index 40922fbc80..dd7864757f 100644 --- a/modules/sale_history/sale.py +++ b/modules/sale_history/sale.py @@ -4,7 +4,7 @@ from trytond.model import ModelView, Workflow, fields from trytond.pool import PoolMeta from trytond.pyson import Eval -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -45,7 +45,8 @@ class RevisionMixin: cursor.execute(*table.update( [table.revision], [table.revision + 1], - where=reduce_ids(table.id, sub_records))) + where=fields.SQL_OPERATORS['in']( + table.id, map(int, sub_records)))) super().draft(records) diff --git a/modules/sale_point/sale.py b/modules/sale_point/sale.py index 70f638699a..2c626014a3 100644 --- a/modules/sale_point/sale.py +++ b/modules/sale_point/sale.py @@ -21,7 +21,7 @@ from trytond.modules.currency.fields import Monetary from trytond.modules.product import price_digits, round_price from trytond.pool import Pool from trytond.pyson import Bool, Eval, Id, If -from trytond.tools import grouped_slice, reduce_ids, sqlite_apply_types +from trytond.tools import grouped_slice, sqlite_apply_types from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard @@ -865,12 +865,14 @@ class POSCashSession(Workflow, ModelSQL, ModelView): payment.select( payment.session.as_('session'), Sum(payment.amount).as_('amount'), - where=reduce_ids(payment.session, sub_ids), + where=fields.SQL_OPERATORS['in']( + payment.session, sub_ids), group_by=[payment.session]), transfer.select( transfer.session.as_('session'), Sum(transfer.amount).as_('amount'), - where=reduce_ids(transfer.session, sub_ids), + where=fields.SQL_OPERATORS['in']( + transfer.session, sub_ids), group_by=[transfer.session]), all_=True) query = query.select( diff --git a/modules/sale_promotion_coupon/sale.py b/modules/sale_promotion_coupon/sale.py index 242efea6aa..48cd4ff08c 100644 --- a/modules/sale_promotion_coupon/sale.py +++ b/modules/sale_promotion_coupon/sale.py @@ -15,7 +15,7 @@ from trytond.pool import Pool, PoolMeta from trytond.pyson import Eval, If from trytond.sql.functions import DateRange from trytond.sql.operators import RangeOverlap -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from .exceptions import PromotionCouponNumberDatesError @@ -302,7 +302,8 @@ class PromotionCouponNumber(DeactivableMixin, ModelSQL, ModelView): result = {} for sub_numbers in grouped_slice(numbers): - query.where = reduce_ids(table.id, map(int, sub_numbers)) + query.where = fields.SQL_OPERATORS['in']( + table.id, map(int, sub_numbers)) cursor.execute(*query) result.update(dict(cursor)) return result diff --git a/modules/stock/move.py b/modules/stock/move.py index f6ac9f9d87..5781892d16 100644 --- a/modules/stock/move.py +++ b/modules/stock/move.py @@ -19,7 +19,7 @@ from trytond.model.exceptions import AccessError from trytond.modules.product import price_digits, round_price from trytond.pool import Pool from trytond.pyson import Bool, Eval, Id, If, TimeDelta -from trytond.tools import cached_property, grouped_slice, reduce_ids +from trytond.tools import cached_property, grouped_slice from trytond.transaction import Transaction, without_check_access from .exceptions import MoveFutureWarning, MoveOriginWarning @@ -1331,9 +1331,9 @@ class Move(Workflow, ModelSQL, ModelView): sub_location_ids = list(sub_location_ids) table = cls.__table__() query = table.select(Literal(1), - where=(reduce_ids( + where=(fields.SQL_OPERATORS['in']( table.to_location, sub_location_ids) - | reduce_ids( + | fields.SQL_OPERATORS['in']( table.from_location, sub_location_ids)) & table.product.in_(product_ids) & (table.company == company_id), @@ -1635,9 +1635,10 @@ class Move(Workflow, ModelSQL, ModelView): if PeriodCache: cache_column = Column(period_cache, fieldname) if isinstance(grouping_ids[0], (int, float, Decimal)): - where &= reduce_ids(column, grouping_ids) + where &= fields.SQL_OPERATORS['in'](column, grouping_ids) if PeriodCache: - where_period &= reduce_ids(cache_column, grouping_ids) + where_period &= fields.SQL_OPERATORS['in']( + cache_column, grouping_ids) else: where &= column.in_(grouping_ids) if PeriodCache: diff --git a/modules/stock/product.py b/modules/stock/product.py index dccc6bdf5d..b83301f37c 100644 --- a/modules/stock/product.py +++ b/modules/stock/product.py @@ -17,7 +17,7 @@ from trytond.model.exceptions import AccessError from trytond.modules.product import price_digits, round_price from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, If, PYSONEncoder -from trytond.tools import decistmt, grouped_slice, reduce_ids +from trytond.tools import decistmt, grouped_slice from trytond.tools import timezone as tz from trytond.transaction import Transaction, without_check_access from trytond.wizard import ( @@ -679,7 +679,8 @@ class ProductQuantitiesByWarehouse(ModelSQL, ModelView): raise AccessError(gettext( 'stock.msg_product_quantities_max', max=transaction.database.IN_MAX)) - product_clause = reduce_ids(product.template, product_template) + product_clause = fields.SQL_OPERATORS['in']( + product.template, product_template) else: product_clause = product.template == Null product_column = Concat('product.template,', product.template) @@ -695,7 +696,8 @@ class ProductQuantitiesByWarehouse(ModelSQL, ModelView): raise AccessError(gettext( 'stock.msg_product_quantities_max', max=transaction.database.IN_MAX)) - product_clause = reduce_ids(move.product, product) + product_clause = fields.SQL_OPERATORS['in']( + move.product, product) else: product_clause = move.product == Null product_column = Concat('product.product,', move.product) @@ -945,7 +947,8 @@ class ProductQuantitiesByWarehouseMove(ModelSQL, ModelView): raise AccessError(gettext( 'stock.msg_product_quantities_max', max=transaction.database.IN_MAX)) - product_clause = reduce_ids(product.template, product_template) + product_clause = fields.SQL_OPERATORS['in']( + product.template, product_template) else: product_clause = product.template == Null product_column = Concat('product.template,', product.template) @@ -960,7 +963,8 @@ class ProductQuantitiesByWarehouseMove(ModelSQL, ModelView): raise AccessError(gettext( 'stock.msg_product_quantities_max', max=transaction.database.IN_MAX)) - product_clause = reduce_ids(move.product, product) + product_clause = fields.SQL_OPERATORS['in']( + move.product, product) else: product_clause = move.product == Null product_column = Concat('product.product,', move.product) diff --git a/modules/stock/stock_reporting_margin.py b/modules/stock/stock_reporting_margin.py index f446dc6cf4..a587f406b6 100644 --- a/modules/stock/stock_reporting_margin.py +++ b/modules/stock/stock_reporting_margin.py @@ -15,7 +15,7 @@ from trytond.model import ModelSQL, ModelView, fields, sum_tree from trytond.modules.currency.fields import Monetary from trytond.pool import Pool from trytond.pyson import Eval, If -from trytond.tools import grouped_slice, pairwise_longest, reduce_ids +from trytond.tools import grouped_slice, pairwise_longest from trytond.tools.chart import sparkline from trytond.transaction import Transaction @@ -596,7 +596,8 @@ class CategoryTree(ModelSQL, ModelView): reporting_categories = [] for sub_ids in grouped_slice(ids): sub_ids = list(sub_ids) - where = reduce_ids(reporting_category.id, sub_ids) + where = fields.SQL_OPERATORS['in']( + reporting_category.id, sub_ids) cursor.execute( *reporting_category.select(reporting_category.id, where=where)) reporting_categories.extend(r for r, in cursor) diff --git a/modules/stock_forecast/forecast.py b/modules/stock_forecast/forecast.py index 7a0cf9fe83..e5cb1d18c4 100644 --- a/modules/stock_forecast/forecast.py +++ b/modules/stock_forecast/forecast.py @@ -18,7 +18,7 @@ from trytond.pool import Pool from trytond.pyson import Bool, Eval, If from trytond.sql.functions import DateRange from trytond.sql.operators import RangeOverlap -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from trytond.wizard import Button, StateTransition, StateView, Wizard @@ -357,13 +357,13 @@ class ForecastLine(ModelSQL, ModelView): product2line = dict((line.product.id, line) for line in lines) product_ids = product2line.keys() for sub_ids in grouped_slice(product_ids): - red_sql = reduce_ids(move.product, sub_ids) + where = fields.SQL_OPERATORS['in'](move.product, sub_ids) cursor.execute(*move.join(location_from, condition=move.from_location == location_from.id ).join(location_to, condition=move.to_location == location_to.id ).select(move.product, Sum(move.internal_quantity), - where=red_sql + where=where & (location_from.left >= forecast.warehouse.left) & (location_from.right <= forecast.warehouse.right) & (location_to.left >= forecast.destination.left) diff --git a/modules/stock_quantity_issue/stock.py b/modules/stock_quantity_issue/stock.py index fa1893e5f8..1932477c7a 100644 --- a/modules/stock_quantity_issue/stock.py +++ b/modules/stock_quantity_issue/stock.py @@ -249,12 +249,11 @@ class QuantityIssue( issue.issue_products = [] issues[issue.origin] = issue - # Order by id to speedup reduce_ids products = Product.search([ ('type', 'in', ['goods', 'assets']), ('consumable', '=', False), ], - order=[('id', 'ASC')]) + order=[]) for product in products: for warehouse in warehouses: diff --git a/modules/stock_shipment_measurements/stock.py b/modules/stock_shipment_measurements/stock.py index fafaf60cda..e924e19f13 100644 --- a/modules/stock_shipment_measurements/stock.py +++ b/modules/stock_shipment_measurements/stock.py @@ -13,7 +13,7 @@ from trytond.model import Index, ModelSQL, ModelView, Workflow, fields from trytond.modules.company.model import CompanyValueMixin from trytond.pool import Pool, PoolMeta from trytond.pyson import Bool, Eval, Id -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -285,7 +285,9 @@ class MeasurementsMixin(object): where = query.where for sub_shipments in grouped_slice(shipments): query.where = ( - where & reduce_ids(table.id, map(int, sub_shipments))) + where + & fields.SQL_OPERATORS['in']( + table.id, map(int, sub_shipments))) cursor.execute(*query) else: cursor.execute(*query) @@ -331,7 +333,7 @@ class MeasurementsMixin(object): if s.state not in states or s.internal_weight is None or s.internal_volume is None)): - query.where = reduce_ids( + query.where = fields.SQL_OPERATORS['in']( table.id, [s.id for s in sub_shipments]) cursor.execute(*query) for id_, weight, volume in cursor: diff --git a/modules/stock_supply/purchase_request.py b/modules/stock_supply/purchase_request.py index 2ad2c6d45a..3bf710c881 100644 --- a/modules/stock_supply/purchase_request.py +++ b/modules/stock_supply/purchase_request.py @@ -65,12 +65,12 @@ class PurchaseRequest(metaclass=PoolMeta): if products is None: # fetch goods and assets - # ordered by ids to speedup reduce_ids in products_by_location products = Product.search([ ('type', 'in', ['goods', 'assets']), ('consumable', '=', False), ('purchasable', '=', True), - ], order=[('id', 'ASC')]) + ], + order=[]) # aggregate product by minimum supply date date2products = defaultdict(list) for product in products: diff --git a/modules/stock_supply/shipment.py b/modules/stock_supply/shipment.py index 741580e0d1..ad36965cb5 100644 --- a/modules/stock_supply/shipment.py +++ b/modules/stock_supply/shipment.py @@ -89,11 +89,11 @@ class ShipmentInternal(ModelSQL, ModelView): location_ids, with_childs=True, grouping_filter=grouping_filter) - # ordered by ids to speedup reduce_ids in products_by_location if implicit_locations: products = Product.search([ ('type', 'in', ['goods', 'assets']), - ], order=[('id', 'ASC')]) + ], + order=[]) product_ids = [p.id for p in products] pbl = get_pbl(None, today, None) else: diff --git a/modules/timesheet/work.py b/modules/timesheet/work.py index a89939aa62..1cd273e446 100644 --- a/modules/timesheet/work.py +++ b/modules/timesheet/work.py @@ -10,7 +10,7 @@ from trytond.i18n import gettext from trytond.model import ModelSQL, ModelStorage, ModelView, Unique, fields from trytond.pool import Pool from trytond.pyson import Bool, Eval, If -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from .exceptions import CompanyValidationError @@ -106,9 +106,9 @@ class Work(ModelSQL, ModelView): condition=line.work == table_w.id) for sub_ids in grouped_slice(ids): - red_sql = reduce_ids(table_w.id, sub_ids) cursor.execute(*query_table.select(table_w.id, Sum(line.duration), - where=red_sql & where, + where=where + & fields.SQL_OPERATORS['in'](table_w.id, sub_ids), group_by=table_w.id)) for work_id, duration in cursor: # SQLite uses float for SUM diff --git a/modules/web_shortener/web.py b/modules/web_shortener/web.py index 61bb60d9c0..0e26cb7c8f 100644 --- a/modules/web_shortener/web.py +++ b/modules/web_shortener/web.py @@ -10,7 +10,7 @@ from sql.aggregate import Count import trytond.config as config from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from trytond.url import http_host from trytond.wsgi import Base64Converter @@ -56,7 +56,7 @@ class ShortenedURL(ModelSQL, ModelView): for sub_ids in grouped_slice(shortened_urls): cursor.execute(*access.select( access.url, Count(access.id), - where=reduce_ids(access.url, sub_ids), + where=fields.SQL_OPERATORS['in'](access.url, sub_ids), group_by=[access.url])) counts.update(cursor) return counts diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 15b1a34ee2..b53127ab52 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Deprecate ``reduce_ids`` for ``SQL_OPERATORS['in']`` * Use array for ``in`` operators * Update to Psycopg 3 * Add support for Python 3.14 diff --git a/trytond/trytond/ir/cron.py b/trytond/trytond/ir/cron.py index 180d8d58b7..8d29f98b1f 100644 --- a/trytond/trytond/ir/cron.py +++ b/trytond/trytond/ir/cron.py @@ -22,7 +22,7 @@ from trytond.model import ( from trytond.pool import Pool from trytond.pyson import Eval from trytond.status import processing -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.tools import timezone as tz from trytond.transaction import Transaction, TransactionError from trytond.worker import run_task @@ -143,7 +143,7 @@ class Cron(DeactivableMixin, ModelSQL, ModelView): ids = [c.id for c in sub_crons] query = table.select( table.id, - where=reduce_ids(table.id, ids), + where=fields.SQL_OPERATORS['in'](table.id, ids), for_=For('UPDATE')) cursor.execute(*query) not_running = {i for i, in cursor} diff --git a/trytond/trytond/ir/note.py b/trytond/trytond/ir/note.py index 897e4d895e..197d6429a0 100644 --- a/trytond/trytond/ir/note.py +++ b/trytond/trytond/ir/note.py @@ -9,7 +9,7 @@ from trytond.i18n import lazy_gettext from trytond.model import ModelSQL, ModelView, fields from trytond.pool import Pool from trytond.pyson import Eval -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction from .resource import ResourceMixin, resource_copy @@ -52,7 +52,7 @@ class Note(ResourceMixin, ModelSQL, ModelView): unread = {} for sub_ids in grouped_slice(ids): - where = reduce_ids(table.id, sub_ids) + where = fields.SQL_OPERATORS['in'](table.id, sub_ids) query = table.join(read, 'LEFT', condition=(table.id == read.note) & (read.user == user_id) diff --git a/trytond/trytond/ir/trigger.py b/trytond/trytond/ir/trigger.py index 4d25a22ca8..4fb661339f 100644 --- a/trytond/trytond/ir/trigger.py +++ b/trytond/trytond/ir/trigger.py @@ -15,7 +15,7 @@ from trytond.model import ( from trytond.model.exceptions import ValidationError from trytond.pool import Pool from trytond.pyson import Eval, If, PYSONDecoder, TimeDelta -from trytond.tools import grouped_slice, reduce_ids +from trytond.tools import grouped_slice from trytond.transaction import Transaction @@ -206,10 +206,10 @@ class Trigger(DeactivableMixin, ModelSQL, ModelView): new_ids = [] for sub_ids in grouped_slice(ids): sub_ids = list(sub_ids) - red_sql = reduce_ids(trigger_log.record_id, sub_ids) + where = fields.SQL_OPERATORS['in'](trigger_log.record_id, sub_ids) cursor.execute(*trigger_log.select( trigger_log.record_id, Count(), - where=red_sql & (trigger_log.trigger == self.id), + where=where & (trigger_log.trigger == self.id), group_by=trigger_log.record_id)) number = dict(cursor) for record_id in sub_ids: @@ -231,10 +231,11 @@ class Trigger(DeactivableMixin, ModelSQL, ModelView): now = datetime.datetime.fromisoformat(now) for sub_ids in grouped_slice(ids): sub_ids = list(sub_ids) - red_sql = reduce_ids(trigger_log.record_id, sub_ids) + where = fields.SQL_OPERATORS['in']( + trigger_log.record_id, sub_ids) cursor.execute(*trigger_log.select( trigger_log.record_id, Max(trigger_log.create_date), - where=(red_sql & (trigger_log.trigger == self.id)), + where=where & (trigger_log.trigger == self.id), group_by=trigger_log.record_id)) delay = dict(cursor) for record_id in sub_ids: diff --git a/trytond/trytond/model/modelsql.py b/trytond/trytond/model/modelsql.py index a711c6382b..ad488f3728 100644 --- a/trytond/trytond/model/modelsql.py +++ b/trytond/trytond/model/modelsql.py @@ -383,7 +383,6 @@ class ModelSQL(ModelStorage): @classmethod def __setup_indexes__(cls): pool = Pool() - # Define Range index to optimise with reduce_ids for field_name, field in cls._fields.items(): Targets = [] if isinstance(field, fields.Many2One): diff --git a/trytond/trytond/tests/test_tools.py b/trytond/trytond/tests/test_tools.py index 461b853dd9..a429659933 100644 --- a/trytond/trytond/tests/test_tools.py +++ b/trytond/trytond/tests/test_tools.py @@ -6,7 +6,6 @@ import datetime as dt import doctest import email.message import os -import sys import unittest from copy import deepcopy from decimal import Decimal @@ -29,7 +28,7 @@ from trytond.tests.test_tryton import TestCase from trytond.tools import ( cached_property, decimal_, email_, escape_wildcard, file_open, firstline, grouped_slice, is_full_text, is_instance_method, likify, lstrip_wildcard, - pair, pairwise_longest, reduce_domain, reduce_ids, remove_forbidden_chars, + pair, pairwise_longest, reduce_domain, remove_forbidden_chars, rstrip_wildcard, slugify, sortable_values, sqlite_apply_types, strip_wildcard, timezone, unescape_wildcard, unpair) from trytond.tools.chart import sparkline @@ -56,51 +55,6 @@ class ToolsTestCase(TestCase): 'Test tools' table = sql.Table('test') - def test_reduce_ids_empty(self): - 'Test reduce_ids empty list' - self.assertEqual(reduce_ids(self.table.id, []), sql.Literal(False)) - - def test_reduce_ids_continue(self): - 'Test reduce_ids continue list' - self.assertEqual(reduce_ids(self.table.id, list(range(10))), - sql.operators.Or(((self.table.id >= 0) & (self.table.id <= 9),))) - - def test_reduce_ids_one_hole(self): - 'Test reduce_ids continue list with one hole' - self.assertEqual(reduce_ids( - self.table.id, list(range(10)) + list(range(20, 30))), - ((self.table.id >= 0) & (self.table.id <= 9)) - | ((self.table.id >= 20) & (self.table.id <= 29))) - - def test_reduce_ids_short_continue(self): - 'Test reduce_ids short continue list' - self.assertEqual(reduce_ids(self.table.id, list(range(4))), - sql.operators.Or((self.table.id.in_(list(range(4))),))) - - def test_reduce_ids_complex(self): - 'Test reduce_ids complex list' - self.assertEqual(reduce_ids(self.table.id, - list(range(10)) + list(range(25, 30)) + list(range(15, 20))), - (((self.table.id >= 0) & (self.table.id <= 14)) - | (self.table.id.in_(list(range(25, 30)))))) - - def test_reduce_ids_complex_small_continue(self): - 'Test reduce_ids complex list with small continue' - self.assertEqual(reduce_ids(self.table.id, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 18, 19, 21]), - (((self.table.id >= 1) & (self.table.id <= 12)) - | (self.table.id.in_([15, 18, 19, 21])))) - - @unittest.skipIf(sys.flags.optimize, "assert removed by optimization") - def test_reduce_ids_float(self): - 'Test reduce_ids with integer as float' - self.assertEqual(reduce_ids(self.table.id, - [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, - 15.0, 18.0, 19.0, 21.0]), - (((self.table.id >= 1.0) & (self.table.id <= 12.0)) - | (self.table.id.in_([15.0, 18.0, 19.0, 21.0])))) - self.assertRaises(AssertionError, reduce_ids, self.table.id, [1.1]) - def test_reduce_domain(self): 'Test reduce_domain' clause = ('x', '=', 'x') diff --git a/trytond/trytond/tools/misc.py b/trytond/trytond/tools/misc.py index b9bb13c2b3..24d95afe49 100644 --- a/trytond/trytond/tools/misc.py +++ b/trytond/trytond/tools/misc.py @@ -12,14 +12,12 @@ import re import types import unicodedata import warnings -from array import array from collections.abc import Iterable, Sized from functools import cache, wraps from itertools import chain, islice, tee, zip_longest -from sql import As, Cast, Literal, Select +from sql import As, Cast, Select from sql.conditionals import Case -from sql.operators import Or from trytond.const import MODULES_GROUP, OPERATORS @@ -111,45 +109,11 @@ def get_smtp_server(): def reduce_ids(field, ids): - ''' - Return a small SQL expression for the list of ids and the sql column - ''' - if __debug__: - def strict_int(value): - assert not isinstance(value, float) or value.is_integer(), \ - "ids must be integer" - return int(value) - else: - strict_int = int - ids = list(map(strict_int, ids)) - if not ids: - return Literal(False) - ids.sort() - prev = ids.pop(0) - continue_list = [prev, prev] - discontinue_list = array('l') - sql = Or() - for i in ids: - if i == prev: - continue - if i != prev + 1: - if continue_list[-1] - continue_list[0] < 5: - discontinue_list.extend([continue_list[0] + x for x in - range(continue_list[-1] - continue_list[0] + 1)]) - else: - sql.append((field >= continue_list[0]) - & (field <= continue_list[-1])) - continue_list = [] - continue_list.append(i) - prev = i - if continue_list[-1] - continue_list[0] < 5: - discontinue_list.extend([continue_list[0] + x for x in - range(continue_list[-1] - continue_list[0] + 1)]) - else: - sql.append((field >= continue_list[0]) & (field <= continue_list[-1])) - if discontinue_list: - sql.append(field.in_(discontinue_list)) - return sql + from trytond.model.fields import SQL_OPERATORS + warnings.warn( + "reduce_ids is deprecated use trytond.fields.SQL_OPERATORS['in']", + DeprecationWarning) + return SQL_OPERATORS['in'](field, ids) def reduce_domain(domain):