Store in the cache all the lines to reconcile when searching for the best reconciliation

When the last line is pop from the debit/credit list, the access to the balance
may invalidate the cache of other records to fill the cache of the last one.
This may become very inefficient on large list of lines.
So by setting the record cache size to the size of the list of lines, we ensure
that no cache is invalidated during the calculation.

Closes #14230
This commit is contained in:
Cédric Krier
2025-09-15 13:18:39 +02:00
parent 8b283d14a0
commit c9980988cc
2 changed files with 116 additions and 46 deletions
+2 -1
View File
@@ -1834,7 +1834,8 @@ class Line(DescriptionOriginMixin, MoveLineMixin, ModelSQL, ModelView):
else:
return 0
lines = sorted(lines, key=cls._reconciliation_sort_key)
with Transaction().set_context(_record_cache_size=max(len(lines), 1)):
lines = cls.browse(sorted(lines, key=cls._reconciliation_sort_key))
debit, credit = [], []
remaining = -amount
for line in list(lines):
+114 -45
View File
@@ -769,66 +769,135 @@ class AccountTestCase(
def test_find_best_reconciliation(self):
"Test find best reconciliation"
pool = Pool()
Account = pool.get('account.account')
FiscalYear = pool.get('account.fiscalyear')
Journal = pool.get('account.journal')
Line = pool.get('account.move.line')
Move = pool.get('account.move')
Party = pool.get('party.party')
currency = create_currency('USD')
party = Party(name='Party')
party.save()
line1 = Line(
debit=Decimal(10), credit=0, currency=currency,
second_currency=None, account=None,
maturity_date=datetime.date(2024, 1, 1))
line2 = Line(
debit=0, credit=Decimal(10), currency=currency,
second_currency=None, account=None,
maturity_date=datetime.date(2024, 1, 2))
line3 = Line(
debit=0, credit=Decimal(5), currency=currency,
second_currency=None, account=None,
maturity_date=datetime.date(2024, 1, 3))
company = create_company()
with set_company(company):
create_chart(company)
fiscalyear = get_fiscalyear(company)
fiscalyear.save()
FiscalYear.create_period([fiscalyear])
period = fiscalyear.periods[0]
journal_revenue, = Journal.search([
('code', '=', 'REV'),
])
receivable, = Account.search([
('type.receivable', '=', True),
('closed', '=', False),
], limit=1)
currency = company.currency
for (lines, amount, best, remaining) in [
([line1], 0, [line1], Decimal('10')),
([line1], Decimal(10), [line1], Decimal(0)),
([line1], Decimal(3), [line1], Decimal(7)),
([line1, line2], 0, [line1, line2], Decimal(0)),
([line1, line2], Decimal(10), [line1], Decimal(0)),
([line1, line2], Decimal(-10), [line2], Decimal(0)),
([line1, line2], Decimal(20), [line1], Decimal(-10)),
([line1, line2], Decimal(-3), [line1, line2], Decimal(3)),
([line1, line2], Decimal(-9), [line2], Decimal(-1)),
([line1, line3], 0, [line1, line3], Decimal(5)),
([line1, line2, line3], 0, [line1, line2], Decimal(0)),
]:
with self.subTest(lines=lines, amount=amount):
self.assertEqual(
Line.find_best_reconciliation(lines, currency, amount),
(best, remaining))
move = Move()
move.period = period
move.journal = journal_revenue
move.date = period.start_date
move.save()
line1 = Line(
move=move, party=party,
debit=Decimal(10), credit=0,
account=receivable,
maturity_date=datetime.date(2024, 1, 1))
line2 = Line(
move=move, party=party,
debit=0, credit=Decimal(10),
account=receivable,
maturity_date=datetime.date(2024, 1, 2))
line3 = Line(
move=move, party=party,
debit=0, credit=Decimal(5),
account=receivable,
maturity_date=datetime.date(2024, 1, 3))
Line.save([line1, line2, line3])
for (lines, amount, best, remaining) in [
([line1], 0, [line1], Decimal('10')),
([line1], Decimal(10), [line1], Decimal(0)),
([line1], Decimal(3), [line1], Decimal(7)),
([line1, line2], 0, [line1, line2], Decimal(0)),
([line1, line2], Decimal(10), [line1], Decimal(0)),
([line1, line2], Decimal(-10), [line2], Decimal(0)),
([line1, line2], Decimal(20), [line1], Decimal(-10)),
([line1, line2], Decimal(-3), [line1, line2], Decimal(3)),
([line1, line2], Decimal(-9), [line2], Decimal(-1)),
([line1, line3], 0, [line1, line3], Decimal(5)),
([line1, line2, line3], 0, [line1, line2], Decimal(0)),
]:
with self.subTest(lines=lines, amount=amount):
self.assertEqual(
Line.find_best_reconciliation(lines, currency, amount),
(best, remaining))
@with_transaction()
def test_find_best_reconciliation_currency(self):
"Test find best reconciliation with different currencies"
pool = Pool()
Account = pool.get('account.account')
FiscalYear = pool.get('account.fiscalyear')
Journal = pool.get('account.journal')
Line = pool.get('account.move.line')
Move = pool.get('account.move')
Party = pool.get('party.party')
currency1 = create_currency('USD')
currency2 = create_currency('EUR')
line1 = Line(
debit=Decimal(10), credit=0, currency=currency1,
second_currency=None, account=None,
maturity_date=datetime.date(2024, 1, 1))
line2 = Line(
debit=0, credit=Decimal(20), currency=currency2,
second_currency=currency1, amount_second_currency=Decimal('-10'),
account=None, maturity_date=datetime.date(2024, 1, 1))
line3 = Line(
debit=0, credit=Decimal(5), currency=None,
second_currency=None, account=None,
maturity_date=datetime.date(2024, 1, 3))
party = Party(name='Party')
party.save()
self.assertEqual(
Line.find_best_reconciliation([line1, line2, line3], currency1),
([line1, line2], Decimal(0)))
company = create_company(currency=currency1)
with set_company(company):
create_chart(company)
fiscalyear = get_fiscalyear(company)
fiscalyear.save()
FiscalYear.create_period([fiscalyear])
period = fiscalyear.periods[0]
journal_revenue, = Journal.search([
('code', '=', 'REV'),
])
receivable, = Account.search([
('type.receivable', '=', True),
('closed', '=', False),
], limit=1)
move = Move()
move.period = period
move.journal = journal_revenue
move.date = period.start_date
move.save()
line1 = Line(
move=move, party=party,
debit=Decimal(10), credit=0,
second_currency=currency2,
amount_second_currency=Decimal(10),
account=receivable,
maturity_date=datetime.date(2024, 1, 1))
line2 = Line(
move=move, party=party,
debit=0, credit=Decimal(20),
second_currency=currency2,
amount_second_currency=Decimal('-10'),
account=receivable, maturity_date=datetime.date(2024, 1, 1))
line3 = Line(
move=move, party=party,
debit=0, credit=Decimal(5),
second_currency=None, account=receivable,
maturity_date=datetime.date(2024, 1, 3))
Line.save([line1, line2, line3])
self.assertEqual(
Line.find_best_reconciliation(
[line1, line2, line3], currency2),
([line1, line2], Decimal(0)))
@with_transaction()
def test_tax_compute(self):