From 91ed1cddc1d1520ef427376ea9f3d40b8fa2fec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicolas=20=C3=89vrard?= Date: Sat, 27 May 2023 09:53:47 +0200 Subject: [PATCH] Use indexes and cache to make search on amounts faster This optimization relies on the idea that few invoices should stay in the states where the amount cache fields are empty. It splits the search query in two parts: one on the cached fields, the other on the computation with the invoice lines (and tax lines when it applies). Closes #11919 --- modules/account_invoice/invoice.py | 52 +++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/modules/account_invoice/invoice.py b/modules/account_invoice/invoice.py index 6fcd0d38be..e87e8b0224 100644 --- a/modules/account_invoice/invoice.py +++ b/modules/account_invoice/invoice.py @@ -5,9 +5,11 @@ from collections import defaultdict, namedtuple from decimal import Decimal from itertools import chain, combinations, groupby +from sql import Literal, Null from sql.aggregate import Sum from sql.conditionals import Coalesce from sql.functions import CharLength, Round +from sql.operators import Exists from trytond import backend from trytond.config import config @@ -303,6 +305,24 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): t, (t.state, Index.Equality()), where=t.state.in_(['draft', 'validated', 'posted'])), + Index(t, (t.total_amount_cache, Index.Range())), + Index( + t, + (t.total_amount_cache, Index.Equality()), + include=[t.id], + where=t.total_amount_cache == Null), + Index(t, (t.untaxed_amount_cache, Index.Range())), + Index( + t, + (t.untaxed_amount_cache, Index.Equality()), + include=[t.id], + where=t.untaxed_amount_cache == Null), + Index(t, (t.tax_amount_cache, Index.Range())), + Index( + t, + (t.tax_amount_cache, Index.Equality()), + include=[t.id], + where=t.tax_amount_cache == Null), }) cls._check_modify_exclude = { 'state', 'alternative_payees', 'payment_lines', @@ -799,12 +819,22 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): Coalesce(Sum(Round((line.quantity * line.unit_price).cast( type_name), currency.digits)), 0).as_('total_amount'), - where=line.invoice.in_(invoice_query), + where=(line.invoice.in_(invoice_query) + & (invoice.total_amount_cache == Null)), group_by=line.invoice) | tax.select(tax.invoice.as_('invoice'), Coalesce(Sum(tax.amount), 0).as_('total_amount'), - where=tax.invoice.in_(invoice_query), + where=(tax.invoice.in_(invoice_query) + & ~Exists(invoice.select(Literal(1), + where=(invoice.total_amount_cache == Null) + & (invoice.id == tax.invoice)))), group_by=tax.invoice)) + union |= invoice.select( + invoice.id.as_('invoice'), + invoice.total_amount_cache.as_('total_amount'), + where=(invoice.id.in_(invoice_query) + & (invoice.total_amount_cache != Null) + & Operator(invoice.total_amount_cache.cast(type_name), value))) query = union.select(union.invoice, group_by=union.invoice, having=Operator(Sum(union.total_amount).cast(type_name), value)) @@ -834,13 +864,18 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): ).join(currency, condition=(currency.id == invoice.currency) ).select(line.invoice, - where=line.invoice.in_(invoice_query), + where=(line.invoice.in_(invoice_query) + & (invoice.untaxed_amount_cache == Null)), group_by=line.invoice, having=Operator(Coalesce(Sum( Round((line.quantity * line.unit_price).cast( type_name), currency.digits)), 0).cast(type_name), value)) + query |= invoice.select(invoice.id, + where=invoice.id.in_(invoice_query) + & (invoice.untaxed_amount_cache != Null) + & Operator(invoice.untaxed_amount_cache.cast(type_name), value)) return [('id', 'in', query)] @classmethod @@ -848,8 +883,10 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): pool = Pool() Rule = pool.get('ir.rule') Tax = pool.get('account.invoice.tax') + Invoice = pool.get('account.invoice') type_name = cls.tax_amount._field.sql_type().base tax = Tax.__table__() + invoice = Invoice.__table__() _, operator, value = clause invoice_query = Rule.query_get('account.invoice') @@ -859,10 +896,17 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin): value = float(value) query = tax.select(tax.invoice, - where=tax.invoice.in_(invoice_query), + where=(tax.invoice.in_(invoice_query) + & ~Exists(invoice.select(Literal(1), + where=(invoice.tax_amount_cache == Null) + & (invoice.id == tax.invoice)))), group_by=tax.invoice, having=Operator(Coalesce(Sum(tax.amount), 0).cast(type_name), value)) + query |= invoice.select(invoice.id, + where=invoice.id.in_(invoice_query) + & (invoice.tax_amount_cache != Null) + & Operator(invoice.tax_amount_cache.cast(type_name), value)) return [('id', 'in', query)] def get_allow_cancel(self, name):