Add cache on invoice's amounts

Having cache on the invoice's amounts will allow to make search more efficient
for #11919
This commit is contained in:
Nicolas Évrard
2023-05-25 16:21:49 +02:00
parent df3aca4022
commit d894c1f442
4 changed files with 56 additions and 58 deletions
+1
View File
@@ -1,3 +1,4 @@
* Add cache on invoice's amounts
Version 6.8.0 - 2023-05-01
--------------------------
+50 -55
View File
@@ -6,7 +6,7 @@ from decimal import Decimal
from itertools import chain, combinations, groupby
from sql.aggregate import Sum
from sql.conditionals import Case, Coalesce
from sql.conditionals import Coalesce
from sql.functions import CharLength, Round
from trytond import backend
@@ -218,12 +218,18 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
untaxed_amount = fields.Function(Monetary(
"Untaxed", currency='currency', digits='currency'),
'get_amount', searcher='search_untaxed_amount')
untaxed_amount_cache = fields.Numeric(
"Untaxed Cache", digits='currency', readonly=True)
tax_amount = fields.Function(Monetary(
"Tax", currency='currency', digits='currency'),
'get_amount', searcher='search_tax_amount')
tax_amount_cache = fields.Numeric(
"Tax Cache", digits='currency', readonly=True)
total_amount = fields.Function(Monetary(
"Total", currency='currency', digits='currency'),
'get_amount', searcher='search_total_amount')
total_amount_cache = fields.Numeric(
"Total Cache", digits='currency', readonly=True)
reconciled = fields.Function(fields.Date('Reconciled',
states={
'invisible': ~Eval('reconciled'),
@@ -302,6 +308,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
'state', 'alternative_payees', 'payment_lines',
'move', 'cancel_move', 'additional_moves',
'invoice_report_cache', 'invoice_report_format',
'total_amount_cache', 'tax_amount_cache', 'untaxed_amount_cache',
'lines'}
cls._order = [
('number', 'DESC NULLS FIRST'),
@@ -597,18 +604,28 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
def get_amount(cls, invoices, names):
pool = Pool()
InvoiceTax = pool.get('account.invoice.tax')
Move = pool.get('account.move')
MoveLine = pool.get('account.move.line')
cursor = Transaction().connection.cursor()
untaxed_amount = dict((i.id, Decimal(0)) for i in invoices)
tax_amount = dict((i.id, Decimal(0)) for i in invoices)
total_amount = dict((i.id, Decimal(0)) for i in invoices)
untaxed_amount = {i.id: i.currency.round(Decimal(0)) for i in invoices}
tax_amount = untaxed_amount.copy()
total_amount = untaxed_amount.copy()
invoices_no_cache = []
for invoice in invoices:
if (invoice.total_amount_cache is not None
and invoice.untaxed_amount_cache is not None
and invoice.tax_amount_cache is not None):
total_amount[invoice.id] = invoice.total_amount_cache
untaxed_amount[invoice.id] = invoice.untaxed_amount_cache
tax_amount[invoice.id] = invoice.tax_amount_cache
else:
invoices_no_cache.append(invoice.id)
invoices_no_cache = cls.browse(invoices_no_cache)
type_name = cls.tax_amount._field.sql_type().base
tax = InvoiceTax.__table__()
to_round = False
for sub_ids in grouped_slice(invoices):
for sub_ids in grouped_slice(invoices_no_cache):
red_sql = reduce_ids(tax.invoice, sub_ids)
cursor.execute(*tax.select(tax.invoice,
Coalesce(Sum(tax.amount), 0).as_(type_name),
@@ -626,56 +643,11 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
tax_amount[invoice.id] = invoice.currency.round(
tax_amount[invoice.id])
invoices_move = set()
invoices_no_move = set()
for invoice in invoices:
if invoice.move:
invoices_move.add(invoice.id)
else:
invoices_no_move.add(invoice.id)
invoices_move = cls.browse(invoices_move)
invoices_no_move = cls.browse(invoices_no_move)
type_name = cls.total_amount._field.sql_type().base
invoice = cls.__table__()
move = Move.__table__()
line = MoveLine.__table__()
to_round = False
for sub_ids in grouped_slice(invoices_move):
red_sql = reduce_ids(invoice.id, sub_ids)
cursor.execute(*invoice.join(move,
condition=invoice.move == move.id
).join(line, condition=move.id == line.move
).select(invoice.id,
Coalesce(Sum(
Case((
line.second_currency == invoice.currency,
line.amount_second_currency),
else_=line.debit - line.credit)),
0).cast(type_name),
where=(invoice.account == line.account) & red_sql,
group_by=invoice.id))
for invoice_id, sum_ in cursor:
# SQLite uses float for SUM
if not isinstance(sum_, Decimal):
sum_ = Decimal(str(sum_))
to_round = True
total_amount[invoice_id] = sum_
for invoice in invoices_move:
if invoice.type == 'in':
total_amount[invoice.id] *= -1
# Float amount must be rounded to get the right precision
if to_round:
total_amount[invoice.id] = invoice.currency.round(
total_amount[invoice.id])
untaxed_amount[invoice.id] = (
total_amount[invoice.id] - tax_amount[invoice.id])
for invoice in invoices_no_move:
for invoice in invoices_no_cache:
zero = invoice.currency.round(Decimal(0))
untaxed_amount[invoice.id] = sum(
(line.amount for line in invoice.lines
if line.type == 'line'), Decimal(0))
if line.type == 'line'), zero)
total_amount[invoice.id] = (
untaxed_amount[invoice.id] + tax_amount[invoice.id])
@@ -1310,6 +1282,9 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
default.setdefault('invoice_date', None)
default.setdefault('accounting_date', None)
default.setdefault('payment_term_date', None)
default.setdefault('total_amount_cache', None)
default.setdefault('untaxed_amount_cache', None)
default.setdefault('tax_amount_cache', None)
return super(Invoice, cls).copy(invoices, default=default)
@classmethod
@@ -1561,12 +1536,29 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
cls.cancel(invoices)
return new_invoices
@classmethod
def _store_cache(cls, invoices):
for invoice in invoices:
if (invoice.untaxed_amount == invoice.untaxed_amount_cache
and invoice.tax_amount == invoice.tax_amount_cache
and invoice.total_amount == invoice.total_amount_cache):
continue
invoice.untaxed_amount_cache = invoice.untaxed_amount
invoice.tax_amount_cache = invoice.tax_amount
invoice.total_amount_cache = invoice.total_amount
cls.save(invoices)
@classmethod
@ModelView.button
@Workflow.transition('draft')
def draft(cls, invoices):
Move = Pool().get('account.move')
cls.write(invoices, {
'tax_amount_cache': None,
'untaxed_amount_cache': None,
'total_amount_cache': None,
})
moves = []
for invoice in invoices:
if invoice.move:
@@ -1588,6 +1580,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
cls.set_number(invoices_in)
cls._store_cache(invoices)
cls._check_similar(invoices)
moves = []
for invoice in invoices_in:
@@ -1656,6 +1649,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
context = transaction.context
cls.set_number(invoices)
cls._store_cache(invoices)
moves = []
for invoice in invoices:
move = invoice.get_move()
@@ -1806,6 +1800,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
cancel_moves.extend(additional_cancel_moves)
if cancel_moves:
Move.save(cancel_moves)
cls._store_cache(invoices)
cls.save(to_save)
if delete_moves:
Move.delete(delete_moves)
@@ -346,10 +346,12 @@ Create some complex invoice and test its taxes base rounding::
Decimal('0.00')
>>> invoice.taxes[0].base == invoice.untaxed_amount
True
>>> found_invoice, = Invoice.find([('untaxed_amount', '=', Decimal(0))])
>>> empty_invoice, found_invoice = Invoice.find(
... [('untaxed_amount', '=', Decimal(0))], order=[('id', 'ASC')])
>>> found_invoice.id == invoice.id
True
>>> found_invoice, = Invoice.find([('total_amount', '=', Decimal(0))])
>>> empty_invoice, found_invoice = Invoice.find(
... [('total_amount', '=', Decimal(0))], order=[('id', 'ASC')])
>>> found_invoice.id == invoice.id
True
@@ -91,7 +91,7 @@ Post a supplier invoice with 0% deductible::
(Decimal('550.00'), Decimal('0.00'), Decimal('550.00'))
>>> invoice.click('post')
>>> invoice.untaxed_amount, invoice.tax_amount, invoice.total_amount
(Decimal('550.00'), Decimal('0'), Decimal('550.00'))
(Decimal('550.00'), Decimal('0.00'), Decimal('550.00'))
>>> len(invoice.taxes)
0