details:   https://code.tryton.org/tryton/commit/1b56df748161
branch:    8.0
user:      Cédric Krier <[email protected]>
date:      Fri Oct 09 23:28:16 2026 +0200
description:
        Convert account move line check_modification into on_modification

        The method is calling validate_move which test the value in the 
database but
        check_modification is called before the update of the database for 
write.
        But on_modification is always called after the database modification.

        Closes #15157
        (grafted from a4629adf43a5dbf915a7eb031ae01e5763004396)
diffstat:

 modules/account/move.py              |  14 +++----
 modules/account/tests/test_module.py |  63 ++++++++++++++++++++++++++++++++++++
 2 files changed, 69 insertions(+), 8 deletions(-)

diffs (107 lines):

diff -r ff085e946456 -r 1b56df748161 modules/account/move.py
--- a/modules/account/move.py   Fri Oct 09 23:25:45 2026 +0200
+++ b/modules/account/move.py   Fri Oct 09 23:28:16 2026 +0200
@@ -1446,16 +1446,13 @@
                         }])
 
     @classmethod
-    def check_modification(cls, mode, lines, values=None, external=False):
+    def on_modification(cls, mode, lines, field_names=None):
         pool = Pool()
         Move = pool.get('account.move')
 
-        super().check_modification(
-            mode, lines, values=values, external=external)
-
-        if (mode in {'create', 'delete'}
-                or (mode == 'write'
-                    and values.keys() - cls._check_modify_exclude)):
+        super().on_modification(mode, lines, field_names=field_names)
+
+        if field_names is None or (field_names - cls._check_modify_exclude):
             journal_period_done = set()
             for line in lines:
                 if line.move.state == 'posted':
@@ -1476,7 +1473,8 @@
                             'account.msg_delete_line_reconciled',
                             line=line.rec_name))
         if (mode == 'write'
-                and values.keys() & cls._reconciliation_modify_disallow):
+                and field_names is not None
+                and field_names & cls._reconciliation_modify_disallow):
             for line in lines:
                 if line.reconciliation:
                     raise AccessError(gettext(
diff -r ff085e946456 -r 1b56df748161 modules/account/tests/test_module.py
--- a/modules/account/tests/test_module.py      Fri Oct 09 23:25:45 2026 +0200
+++ b/modules/account/tests/test_module.py      Fri Oct 09 23:28:16 2026 +0200
@@ -768,6 +768,69 @@
                 move.save()
 
     @with_transaction()
+    def test_move_validation(self):
+        "Test move validation"
+        pool = Pool()
+        Party = pool.get('party.party')
+        FiscalYear = pool.get('account.fiscalyear')
+        Journal = pool.get('account.journal')
+        Account = pool.get('account.account')
+        Move = pool.get('account.move')
+        Line = pool.get('account.move.line')
+
+        party = Party(name='Party')
+        party.save()
+
+        company = create_company()
+        with set_company(company):
+            fiscalyear = get_fiscalyear(company)
+            fiscalyear.save()
+            FiscalYear.create_period([fiscalyear])
+            period = fiscalyear.periods[0]
+            create_chart(company)
+
+            journal_revenue, = Journal.search([
+                    ('code', '=', 'REV'),
+                    ])
+            revenue, = Account.search([
+                    ('type.revenue', '=', True),
+                    ('closed', '=', False),
+                    ], limit=1)
+            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.lines = [
+                Line(account=revenue, credit=Decimal(100)),
+                ]
+            move.save()
+
+            line, = move.lines
+            self.assertEqual(line.state, 'draft')
+
+            move.lines += (
+                Line(account=receivable, debit=Decimal(100), party=party),
+                )
+            move.save()
+
+            self.assertEqual([l.state for l in move.lines], ['valid', 'valid'])
+
+            line = move.lines[-1]
+            line.credit = Decimal(50)
+            line.save()
+
+            self.assertEqual(line.state, 'draft')
+
+            line.credit = Decimal(100)
+            line.save()
+            self.assertEqual(line.state, 'valid')
+
+    @with_transaction()
     def test_find_best_reconciliation(self):
         "Test find best reconciliation"
         pool = Pool()

Reply via email to