[FIX] account: dashboard performance

When thousands of invoices are in a different currency than their
corresponding journals, a large number of calls are performed to
`_get_conversion_rate`, and therefore `_select_companies_rates`.

Despite the fact that the query itself is optimized, the impact of
performances on the dashboard loading time is not negligible.

In order to avoid unnecessary queries, we create a cache dictionary to
store the rate values. This way, we avoid any unnecessary query.

opw-1920188

closes odoo/odoo#30552
This commit is contained in:
Nicolas Martinelli
2019-01-28 12:51:50 +00:00
parent e7bbdf884f
commit c8ac1b40f2
@@ -189,9 +189,10 @@ class account_journal(models.Model):
query = """SELECT residual_signed as amount_total, currency_id AS currency, type, date_invoice, company_id FROM account_invoice WHERE journal_id = %s AND date <= %s AND state = 'open';"""
self.env.cr.execute(query, (self.id, today))
late_query_results = self.env.cr.dictfetchall()
(number_waiting, sum_waiting) = self._count_results_and_sum_amounts(query_results_to_pay, currency)
(number_draft, sum_draft) = self._count_results_and_sum_amounts(query_results_drafts, currency)
(number_late, sum_late) = self._count_results_and_sum_amounts(late_query_results, currency)
curr_cache = {}
(number_waiting, sum_waiting) = self._count_results_and_sum_amounts(query_results_to_pay, currency, curr_cache=curr_cache)
(number_draft, sum_draft) = self._count_results_and_sum_amounts(query_results_drafts, currency, curr_cache=curr_cache)
(number_late, sum_late) = self._count_results_and_sum_amounts(late_query_results, currency, curr_cache=curr_cache)
difference = currency.round(last_balance-account_sum) + 0.0
return {
@@ -239,20 +240,32 @@ class account_journal(models.Model):
FROM account_invoice inv
WHERE journal_id = %(journal_id)s AND state = 'draft';""", {'journal_id':self.id})
def _count_results_and_sum_amounts(self, results_dict, target_currency):
def _count_results_and_sum_amounts(self, results_dict, target_currency, curr_cache=None):
""" Loops on a query result to count the total number of invoices and sum
their amount_total field (expressed in the given target currency).
amount_total must be signed !
"""
rslt_count = 0
rslt_sum = 0.0
# Create a cache with currency rates to avoid unnecessary SQL requests. Do not copy
# curr_cache on purpose, so the dictionary is modified and can be re-used for subsequent
# calls of the method.
curr_cache = {} if curr_cache is None else curr_cache
for result in results_dict:
cur = self.env['res.currency'].browse(result.get('currency'))
company = self.env['res.company'].browse(result.get('company_id')) or self.env.user.company_id
rslt_count += 1
date = result.get('date_invoice') or fields.Date.today()
amount = result.get('amount_total', 0)
rslt_sum += cur._convert(
amount, target_currency, company, result.get('date_invoice') or fields.Date.today())
if cur != target_currency:
key = (cur, target_currency, company, date)
# Using setdefault will call _get_conversion_rate, so we explicitly check the
# existence of the key in the cache instead.
if key not in curr_cache:
curr_cache[key] = self.env['res.currency']._get_conversion_rate(*key)
amount *= curr_cache[key]
rslt_sum += target_currency.round(amount)
return (rslt_count, rslt_sum)
@api.multi