diff --git a/modules/product_image/product.py b/modules/product_image/product.py index e620e5f8fc..87cad7ea25 100644 --- a/modules/product_image/product.py +++ b/modules/product_image/product.py @@ -64,7 +64,7 @@ class ImageURLMixin: 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 --git a/modules/product_image/tests/test_module.py b/modules/product_image/tests/test_module.py index ac6f618286..b8e83d3f83 100644 --- a/modules/product_image/tests/test_module.py +++ b/modules/product_image/tests/test_module.py @@ -116,7 +116,7 @@ class ProductImageTestCase(ModuleTestCase): ).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 --git a/modules/production_work/work.py b/modules/production_work/work.py index 5a80c52311..c9c05fd3a4 100644 --- a/modules/production_work/work.py +++ b/modules/production_work/work.py @@ -406,7 +406,7 @@ class WorkCycle(Workflow, ModelSQL, ModelView): 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 --git a/modules/web_user/user.py b/modules/web_user/user.py index df6f7cf2b4..ecc98632a3 100644 --- a/modules/web_user/user.py +++ b/modules/web_user/user.py @@ -442,8 +442,7 @@ class UserSession(ModelSQL): @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 --git a/trytond/CHANGELOG b/trytond/CHANGELOG index e8cda6a203..796cb83d31 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add timestamp field on ModelSQL for last modified * Support Function field without getter but SQL expression * Add support for column_ method * Use tables and Model as arguments for sql_column of the Field diff --git a/trytond/trytond/ir/message.xml b/trytond/trytond/ir/message.xml index 3f9fa48b3f..66f06839dd 100644 --- a/trytond/trytond/ir/message.xml +++ b/trytond/trytond/ir/message.xml @@ -65,6 +65,9 @@ this repository contains the full copyright notices and license terms. --> Edited at + + Last Modified at + Record Name diff --git a/trytond/trytond/ir/routes.py b/trytond/trytond/ir/routes.py index 75bcafda66..7db00886cf 100644 --- a/trytond/trytond/ir/routes.py +++ b/trytond/trytond/ir/routes.py @@ -17,7 +17,7 @@ from trytond.wsgi import app 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 --git a/trytond/trytond/ir/session.py b/trytond/trytond/ir/session.py index ad50b1fcd8..d7df0b2048 100644 --- a/trytond/trytond/ir/session.py +++ b/trytond/trytond/ir/session.py @@ -96,7 +96,7 @@ class Session(ModelSQL): 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 @@ class Session(ModelSQL): ('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 --git a/trytond/trytond/model/modelsql.py b/trytond/trytond/model/modelsql.py index 26ffbf5199..a4d1413847 100644 --- a/trytond/trytond/model/modelsql.py +++ b/trytond/trytond/model/modelsql.py @@ -16,7 +16,7 @@ from sql.operators import And, Concat, Equal, Exists, Operator, Or 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 @@ class ModelSQL(ModelStorage): _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) @@ -956,6 +959,11 @@ class ModelSQL(ModelStorage): raise ConcurrencyException( '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): diff --git a/trytond/trytond/res/user.py b/trytond/trytond/res/user.py index 32bebaf3ca..10b8abc7c5 100644 --- a/trytond/trytond/res/user.py +++ b/trytond/trytond/res/user.py @@ -325,8 +325,7 @@ class User(avatar_mixin(100, 'login'), DeactivableMixin, ModelSQL, ModelView): ], 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'))))