details:   https://code.tryton.org/tryton/commit/5c2f445c099e
branch:    default
user:      Cédric Krier <[email protected]>
date:      Wed Feb 25 09:04:03 2026 +0100
description:
        Add timestamp field on ModelStorage for last modified

        Closes #14202
diffstat:

 modules/product_image/product.py           |   2 +-
 modules/product_image/tests/test_module.py |   2 +-
 modules/production_work/work.py            |   2 +-
 modules/web_user/user.py                   |   3 +--
 trytond/CHANGELOG                          |   1 +
 trytond/trytond/ir/message.xml             |   3 +++
 trytond/trytond/ir/routes.py               |   2 +-
 trytond/trytond/ir/session.py              |   4 ++--
 trytond/trytond/model/modelsql.py          |  10 +++++++++-
 trytond/trytond/res/user.py                |   3 +--
 10 files changed, 21 insertions(+), 11 deletions(-)

diffs (150 lines):

diff -r 453652aa7ac8 -r 5c2f445c099e modules/product_image/product.py
--- a/modules/product_image/product.py  Thu Feb 12 11:14:07 2026 +0100
+++ b/modules/product_image/product.py  Wed Feb 25 09:04:03 2026 +0100
@@ -64,7 +64,7 @@
                     args['h'] = height
                 if index is not None:
                     args['i'] = index
-            timestamp = int((self.write_date or self.create_date).timestamp())
+            timestamp = int(self.last_modified_at.timestamp())
             args['t'] = (
                 base64.urlsafe_b64encode(timestamp.to_bytes(8, 'big'))
                 .decode().rstrip('='))
diff -r 453652aa7ac8 -r 5c2f445c099e modules/product_image/tests/test_module.py
--- a/modules/product_image/tests/test_module.py        Thu Feb 12 11:14:07 
2026 +0100
+++ b/modules/product_image/tests/test_module.py        Wed Feb 25 09:04:03 
2026 +0100
@@ -116,7 +116,7 @@
                 ).read()
             image.save()
 
-        template.write_date = dt.datetime(2025, 1, 1)  # fix timestamp
+        template.last_modified_at = dt.datetime(2025, 1, 1)  # fix timestamp
 
         self.assertRegex(
             template.get_image_url(i=2),
diff -r 453652aa7ac8 -r 5c2f445c099e modules/production_work/work.py
--- a/modules/production_work/work.py   Thu Feb 12 11:14:07 2026 +0100
+++ b/modules/production_work/work.py   Wed Feb 25 09:04:03 2026 +0100
@@ -406,7 +406,7 @@
 
     def set_duration(self, now):
         if self.duration is None:
-            self.duration = now - (self.write_date or self.create_date)
+            self.duration = now - self.last_modified_at
 
     def set_cost(self):
         if self.cost is None:
diff -r 453652aa7ac8 -r 5c2f445c099e modules/web_user/user.py
--- a/modules/web_user/user.py  Thu Feb 12 11:14:07 2026 +0100
+++ b/modules/web_user/user.py  Wed Feb 25 09:04:03 2026 +0100
@@ -442,8 +442,7 @@
     @property
     def expired(self):
         now = datetime.datetime.now()
-        timestamp = self.write_date or self.create_date
-        return abs(timestamp - now) > self.timeout()
+        return abs(self.last_modified_at - now) > self.timeout()
 
     @classmethod
     def reset(cls, session):
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/CHANGELOG
--- a/trytond/CHANGELOG Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/CHANGELOG Wed Feb 25 09:04:03 2026 +0100
@@ -1,3 +1,4 @@
+* Add timestamp field on ModelSQL for last modified
 * Support Function field without getter but SQL expression
 * Add support for column_<field name> method
 * Use tables and Model as arguments for sql_column of the Field
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/trytond/ir/message.xml
--- a/trytond/trytond/ir/message.xml    Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/trytond/ir/message.xml    Wed Feb 25 09:04:03 2026 +0100
@@ -65,6 +65,9 @@
         <record model="ir.message" id="msg_edited_at">
             <field name="text">Edited at</field>
         </record>
+        <record model="ir.message" id="msg_last_modified_at">
+            <field name="text">Last Modified at</field>
+        </record>
         <record model="ir.message" id="msg_record_name">
             <field name="text">Record Name</field>
         </record>
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/trytond/ir/routes.py
--- a/trytond/trytond/ir/routes.py      Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/trytond/ir/routes.py      Wed Feb 25 09:04:03 2026 +0100
@@ -17,7 +17,7 @@
 
 
 def get_token(record):
-    return str((record.write_date or record.create_date).timestamp())
+    return str(record.last_modified_at.timestamp())
 
 
 def get_config(names, section='html', default=None):
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/trytond/ir/session.py
--- a/trytond/trytond/ir/session.py     Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/trytond/ir/session.py     Wed Feb 25 09:04:03 2026 +0100
@@ -96,7 +96,7 @@
             if abs(session.create_date - now) < timeout:
                 if compare_digest(session.key, key):
                     find = True
-                    last_reset = session.write_date or session.create_date
+                    last_reset = session.last_modified_at
             else:
                 if find is None and compare_digest(session.key, key):
                     find = False
@@ -120,7 +120,7 @@
                 ('key', '=', key),
                 domain or [],
                 ], limit=1)
-        timestamp = session.write_date or session.create_date
+        timestamp = session.last_modified_at
         valid = abs(timestamp - now) < timeout
         if not valid:
             cls.delete([session])
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/trytond/model/modelsql.py
--- a/trytond/trytond/model/modelsql.py Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/trytond/model/modelsql.py Wed Feb 25 09:04:03 2026 +0100
@@ -16,7 +16,7 @@
 from trytond import backend, config
 from trytond.cache import freeze
 from trytond.exceptions import ConcurrencyException
-from trytond.i18n import gettext
+from trytond.i18n import gettext, lazy_gettext
 from trytond.pool import Pool
 from trytond.pyson import PYSONDecoder, PYSONEncoder
 from trytond.rpc import RPC
@@ -322,6 +322,9 @@
     _history = False
     table_query = None
 
+    last_modified_at = fields.Function(fields.Timestamp(
+            lazy_gettext('ir.msg_last_modified_at')))
+
     @classmethod
     def __setup__(cls):
         cls._table = config.get('table', cls.__name__, default=cls._table)
@@ -957,6 +960,11 @@
                         'Records were modified in the meanwhile')
 
     @classmethod
+    def column_last_modified_at(cls, tables):
+        table, _ = tables[None]
+        return Coalesce(table.write_date, table.create_date)
+
+    @classmethod
     @no_table_query
     def create(cls, vlist):
         transaction = Transaction()
diff -r 453652aa7ac8 -r 5c2f445c099e trytond/trytond/res/user.py
--- a/trytond/trytond/res/user.py       Thu Feb 12 11:14:07 2026 +0100
+++ b/trytond/trytond/res/user.py       Wed Feb 25 09:04:03 2026 +0100
@@ -325,8 +325,7 @@
                         ], order=[('create_uid', 'ASC')])
 
                 def filter_(session):
-                    timestamp = session.write_date or session.create_date
-                    return abs(timestamp - now) < timeout
+                    return abs(session.last_modified_at - now) < timeout
                 result.update(dict((i, len(list(g)))
                         for i, g in groupby(filter(filter_, sessions),
                             attrgetter('create_uid.id'))))

Reply via email to