details:   https://code.tryton.org/tryton/commit/18b7206f9f6c
branch:    default
user:      Cédric Krier <[email protected]>
date:      Fri Mar 13 11:52:40 2026 +0100
description:
        Commit after each reconciliation of the automatic reconciliation 
scheduled task

        This avoid having a long running transaction on large database but keep 
sharing
        most of the code with the wizard.

        Closes #14674
diffstat:

 modules/account/move.py            |  239 +++++++++++++++++++++---------------
 modules/account_deposit/account.py |    7 +-
 2 files changed, 140 insertions(+), 106 deletions(-)

diffs (329 lines):

diff -r 8eb8b9f3f1f7 -r 18b7206f9f6c modules/account/move.py
--- a/modules/account/move.py   Sun Mar 29 08:57:01 2026 +0200
+++ b/modules/account/move.py   Fri Mar 13 11:52:40 2026 +0100
@@ -1865,19 +1865,34 @@
         return self.maturity_date or self.date
 
     @classmethod
-    def reconcile_automatic(cls, data=None):
+    def reconcile_automatic(cls):
         pool = Pool()
         Reconcile = pool.get('account.reconcile', type='wizard')
-
-        data = data.copy() if data is not None else {}
-        data['start'] = {
-            'automatic': True,
-            'only_balanced': True,
-            }
-
-        session_id, _, _ = Reconcile.create()
-        Reconcile.execute(session_id, data, 'setup')
-        Reconcile.delete(session_id)
+        transaction = Transaction()
+        context = transaction.context
+
+        company_id = context.get('company')
+
+        def lines_to_reconcile(account, party, currency):
+            lines, _ = cls.find_best_reconciliation(
+                Reconcile.to_reconcile(account, party, currency),
+                currency)
+            return lines
+
+        accounts = Reconcile.accounts_to_reconcile(company=company_id)
+        for account in accounts:
+            if account.party_required:
+                parties = Reconcile.parties_to_reconcile(account)
+            else:
+                parties = [None]
+            for party in parties:
+                currencies = Reconcile.currencies_to_reconcile(
+                    account, party)
+                for currency in currencies:
+                    while lines := lines_to_reconcile(
+                            account, party, currency):
+                        Line.reconcile(lines)
+                        transaction.commit()
 
 
 class LineReceivablePayableContext(ModelView):
@@ -2234,8 +2249,8 @@
             ])
     reconcile = StateTransition()
 
-    def get_accounts(self):
-        'Return a list of account id to reconcile'
+    @classmethod
+    def accounts_to_reconcile(cls, company=None, with_rule=False):
         pool = Pool()
         Rule = pool.get('ir.rule')
         Line = pool.get('account.move.line')
@@ -2244,25 +2259,25 @@
         AccountType = pool.get('account.account.type')
         account = Account.__table__()
         account_type = AccountType.__table__()
-        cursor = Transaction().connection.cursor()
-        account_rule = Rule.query_get(Account.__name__)
-
-        if self.model and self.model.__name__ == 'account.move.line':
-            lines = [l for l in self.records if not l.reconciliation]
-            return list({l.account for l in lines if l.account.reconcile})
+        transaction = Transaction()
+        cursor = transaction.connection.cursor()
 
         balance = line.debit - line.credit
-        cursor.execute(*line.join(account,
+        query = (line
+            .join(account,
                 condition=line.account == account.id)
             .join(account_type, condition=account.type == account_type.id)
             .select(
                 account.id,
-                where=((line.reconciliation == Null)
+                where=(
+                    (line.reconciliation == Null)
                     & (line.state == 'valid')
-                    & account.reconcile
-                    & account.id.in_(account_rule)),
-                group_by=[account.id,
-                    account_type.receivable, account_type.payable],
+                    & account.reconcile),
+                group_by=[
+                    account.id,
+                    account_type.receivable,
+                    account_type.payable,
+                    ],
                 having=((
                         Sum(Case((balance > 0, 1), else_=0)) > 0)
                     & (Sum(Case((balance < 0, 1), else_=0)) > 0)
@@ -2275,74 +2290,112 @@
                             Sum(balance) > 0),
                         else_=False)
                     )))
-        return [a for a, in cursor]
+        if with_rule:
+            account_rule = Rule.query_get(Account.__name__)
+            query.where &= account.id.in_(account_rule)
+        if company:
+            query.where &= account.company == int(company)
+        cursor.execute(*query)
+        return Account.browse([a for a, in cursor])
+
+    @classmethod
+    def parties_to_reconcile(cls, account):
+        pool = Pool()
+        Party = pool.get('party.party')
+        Line = pool.get('account.move.line')
+        line = Line.__table__()
+        transaction = Transaction()
+        cursor = transaction.connection.cursor()
+
+        query = (line
+            .select(line.party,
+                where=(
+                    (line.reconciliation == Null)
+                    & (line.state == 'valid')
+                    & (line.account == int(account))
+                    & (line.party != Null)),
+                group_by=line.party))
+        cursor.execute(*query)
+        return Party.browse([p for p, in cursor])
+
+    @classmethod
+    def currencies_to_reconcile(cls, account, party, _balanced=False):
+        pool = Pool()
+        Currency = pool.get('currency.currency')
+        Line = pool.get('account.move.line')
+        line = Line.__table__()
+        transaction = Transaction()
+        cursor = transaction.connection.cursor()
+
+        balance = Case(
+            (line.second_currency != Null, line.amount_second_currency),
+            else_=line.debit - line.credit)
+        currency_expr = Coalesce(line.second_currency, account.currency.id)
+        query = (line
+            .select(
+                currency_expr,
+                where=((line.reconciliation == Null)
+                    & (line.state == 'valid')
+                    & (line.account == int(account))
+                    & (line.party == (int(party) if party else None))),
+                group_by=line.second_currency))
+        if _balanced:
+            query.having = Sum(balance) == 0
+        else:
+            query.having = ((
+                    Sum(Case((balance > 0, 1), else_=0)) > 0)
+                & (Sum(Case((balance < 0, 1), else_=0)) > 0)
+                | Case((account.type.receivable, Sum(balance) < 0),
+                    else_=False)
+                | Case((account.type.payable, Sum(balance) > 0),
+                    else_=False))
+        cursor.execute(*query)
+        return Currency.browse([p for p, in cursor])
+
+    @classmethod
+    def to_reconcile(cls, account, party, currency):
+        pool = Pool()
+        Line = pool.get('account.move.line')
+        return Line.search([
+                ('account', '=', account),
+                ('party', '=', party),
+                ('reconciliation', '=', None),
+                ['OR',
+                    [
+                        ('currency', '=', currency),
+                        ('second_currency', '=', None),
+                        ],
+                    ('second_currency', '=', currency),
+                    ],
+                ('state', '=', 'valid'),
+                ],
+            order=[])
+
+    def get_accounts(self):
+        'Return a list of account id to reconcile'
+        if self.model and self.model.__name__ == 'account.move.line':
+            lines = [l for l in self.records if not l.reconciliation]
+            return list({l.account for l in lines if l.account.reconcile})
+        return self.accounts_to_reconcile(with_rule=True)
 
     def get_parties(self, account, party=None):
         'Return a list party to reconcile for the account'
-        pool = Pool()
-        Line = pool.get('account.move.line')
-        line = Line.__table__()
-        cursor = Transaction().connection.cursor()
-
         if self.model and self.model.__name__ == 'account.move.line':
             lines = [l for l in self.records if not l.reconciliation]
             return list({
                     l.party for l in lines
                     if l.account == account and l.party})
-
-        where = ((line.reconciliation == Null)
-            & (line.state == 'valid')
-            & (line.account == account.id))
-        if party:
-            where &= (line.party == party.id)
-        else:
-            where &= (line.party != Null)
-        cursor.execute(*line.select(line.party,
-                where=where,
-                group_by=line.party))
-        return [p for p, in cursor]
-
-    def get_currencies(self, account, party, currency=None, _balanced=False):
+        return self.parties_to_reconcile(account)
+
+    def get_currencies(self, account, party):
         "Return a list of currencies to reconcile for the account and party"
-        pool = Pool()
-        Line = pool.get('account.move.line')
-        line = Line.__table__()
-        cursor = Transaction().connection.cursor()
-
         if self.model and self.model.__name__ == 'account.move.line':
             lines = [l for l in self.records if not l.reconciliation]
             return list({
-                    (l.second_currency or l.currency).id
+                    l.second_currency or l.currency
                     for l in lines
                     if l.account == account and l.party == party})
-
-        balance = Case(
-            (line.second_currency != Null, line.amount_second_currency),
-            else_=line.debit - line.credit)
-        if _balanced:
-            having = Sum(balance) == 0
-        else:
-            having = ((
-                    Sum(Case((balance > 0, 1), else_=0)) > 0)
-                & (Sum(Case((balance < 0, 1), else_=0)) > 0)
-                | Case((account.type.receivable, Sum(balance) < 0),
-                    else_=False)
-                | Case((account.type.payable, Sum(balance) > 0),
-                    else_=False)
-                )
-        where = ((line.reconciliation == Null)
-            & (line.state == 'valid')
-            & (line.account == account.id)
-            & (line.party == (party.id if party else None)))
-        currency_expr = Coalesce(line.second_currency, account.currency.id)
-        if currency:
-            where &= (currency_expr == currency.id)
-        cursor.execute(*line.select(
-                currency_expr,
-                where=where,
-                group_by=line.second_currency,
-                having=having))
-        return [p for p, in cursor]
+        return self.currencies_to_reconcile(account, party)
 
     def transition_setup(self):
         return self.transition_next_(first=True)
@@ -2436,29 +2489,6 @@
             defaults['date'] = Date.today()
         return defaults
 
-    def _all_lines(self):
-        'Return all lines to reconcile for the current state'
-        pool = Pool()
-        Line = pool.get('account.move.line')
-        return Line.search([
-                ('account', '=', self.show.account.id),
-                ('party', '=',
-                    self.show.party.id if self.show.party else None),
-                ('reconciliation', '=', None),
-                ['OR',
-                    [
-                        ('currency', '=', self.show.currency.id),
-                        ('second_currency', '=', None),
-                        ],
-                    ('second_currency', '=', self.show.currency.id),
-                    ],
-                ('state', '=', 'valid'),
-                ],
-            order=[])
-
-    def _line_sort_key(self, line):
-        return [line.maturity_date or line.date]
-
     def _default_lines(self):
         'Return the larger list of lines which can be reconciled'
         pool = Pool()
@@ -2472,10 +2502,13 @@
                 and (l.second_currency or l.currency) == self.show.currency}
         else:
             requested = []
+
+        account = self.show.account
         currency = self.show.currency
+        party = self.show.party
 
         lines, remaining = Line.find_best_reconciliation(
-            self._all_lines(), currency)
+            self.to_reconcile(account, party, currency), currency)
         if remaining:
             return requested
         else:
diff -r 8eb8b9f3f1f7 -r 18b7206f9f6c modules/account_deposit/account.py
--- a/modules/account_deposit/account.py        Sun Mar 29 08:57:01 2026 +0200
+++ b/modules/account_deposit/account.py        Fri Mar 13 11:52:40 2026 +0100
@@ -46,11 +46,12 @@
 class Reconcile(metaclass=PoolMeta):
     __name__ = 'account.reconcile'
 
-    def get_currencies(self, account, party, currency=None, _balanced=False):
+    @classmethod
+    def currencies_to_reconcile(cls, account, party, _balanced=False):
         if account.type.deposit:
             _balanced = True
-        return super().get_currencies(
-            account, party, currency=None, _balanced=_balanced)
+        return super().currencies_to_reconcile(
+            account, party, _balanced=_balanced)
 
 
 class Payment(metaclass=PoolMeta):

Reply via email to