From 03655a335a7fd994d0cd1bbb888e60964e8a48f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 11 Mar 2026 10:01:51 +0100 Subject: [PATCH] Add notify_user to ModelStorage Closes #14667 --- trytond/CHANGELOG | 1 + trytond/doc/ref/models.rst | 5 +++++ trytond/doc/ref/transaction.rst | 2 ++ trytond/trytond/model/modelstorage.py | 20 ++++++++++++++++++ trytond/trytond/res/notification.py | 2 +- trytond/trytond/tests/test_modelstorage.py | 24 ++++++++++++++++++++++ trytond/trytond/transaction.py | 18 ++++++++++++++++ 7 files changed, 71 insertions(+), 1 deletion(-) diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index aa3e6f7b01..52c4aff214 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Add notify_user to ModelStorage * Check button states when testing access * Remove the files of Binary fields from the Filestore * Add delete and delete_many to Filestore diff --git a/trytond/doc/ref/models.rst b/trytond/doc/ref/models.rst index d22a2fd918..3ba2a9c88c 100644 --- a/trytond/doc/ref/models.rst +++ b/trytond/doc/ref/models.rst @@ -356,6 +356,11 @@ Class methods: Log event for records. + +.. classmethod:: ModelStorage.notify_user(records, icon, label[, description[, user[, \**extra]]]) + + Notify user about records. + .. classmethod:: ModelStorage.preprocess_values(cls, mode, values) Returns a copy of ``values`` to be used for :meth:`~ModelStorage.create` and diff --git a/trytond/doc/ref/transaction.rst b/trytond/doc/ref/transaction.rst index 376c0b9726..5c2b26d53d 100644 --- a/trytond/doc/ref/transaction.rst +++ b/trytond/doc/ref/transaction.rst @@ -54,6 +54,8 @@ Transaction .. attribute:: Transaction.log_records +.. attribute:: Transaction.user_notifications + .. attribute:: Transaction.check_warnings The dictionary of warnings already checked with user and warning diff --git a/trytond/trytond/model/modelstorage.py b/trytond/trytond/model/modelstorage.py index ef41b02d79..464d691bfe 100644 --- a/trytond/trytond/model/modelstorage.py +++ b/trytond/trytond/model/modelstorage.py @@ -5,6 +5,7 @@ import base64 import csv import datetime import decimal +import json import math import random import time @@ -245,6 +246,25 @@ class ModelStorage(Model): resource=record, event=event, target=target, user=user, **extra)) + @dualmethod + def notify_user( + cls, records, icon, label, description=None, user=None, **extra): + pool = Pool() + Notification = pool.get('res.notification') + for transaction, sub_records in groupby( + records, lambda r: r._transaction): + with Transaction().set_current_transaction(transaction): + if user is None: + user = transaction.user + transaction.user_notifications.append(Notification( + user=user, + icon=icon, + label=label, + description=description, + model=cls.__name__, + records=json.dumps([r.id for r in records]), + **extra)) + @classmethod def preprocess_values(cls, mode, values): assert mode in {'create', 'write'} diff --git a/trytond/trytond/res/notification.py b/trytond/trytond/res/notification.py index 7b2ca741c7..26d2e2b73f 100644 --- a/trytond/trytond/res/notification.py +++ b/trytond/trytond/res/notification.py @@ -54,7 +54,7 @@ class Notification( @classmethod def __setup__(cls): super().__setup__() - + cls._order = [('id', 'DESC')] cls.__rpc__.update({ 'get': RPC(), 'get_count': RPC(), diff --git a/trytond/trytond/tests/test_modelstorage.py b/trytond/trytond/tests/test_modelstorage.py index b025c13b95..8f18dc3831 100644 --- a/trytond/trytond/tests/test_modelstorage.py +++ b/trytond/trytond/tests/test_modelstorage.py @@ -743,6 +743,30 @@ class ModelStorageTestCase(TestCase): Model.delete([record]) + @with_transaction() + def test_notify_user(self): + "Test notify user" + pool = Pool() + Model = pool.get('test.modelstorage') + Notification = pool.get('res.notification') + transaction = Transaction() + + record = Model(name="Foo") + record.save() + + record.notify_user( + 'tryton-notification', + "Test notification", + "Long description") + transaction._store_user_notifications() + + notification, = Notification.search([]) + + self.assertEqual(notification.user.id, transaction.user) + self.assertEqual(notification.icon, 'tryton-notification') + self.assertEqual(notification.label, "Test notification") + self.assertEqual(notification.description, "Long description") + class EvalEnvironmentTestCase(TestCase): "Test EvalEnvironment" diff --git a/trytond/trytond/transaction.py b/trytond/trytond/transaction.py index ae4f3bb340..644877330e 100644 --- a/trytond/trytond/transaction.py +++ b/trytond/trytond/transaction.py @@ -130,6 +130,7 @@ class Transaction(object): instance.delete_records = None instance.trigger_records = None instance.log_records = None + instance.user_notifications = None instance.check_warnings = None instance.timestamp = None instance.started_at = None @@ -198,6 +199,7 @@ class Transaction(object): self.delete_records = defaultdict(set) self.trigger_records = defaultdict(set) self.log_records = [] + self.user_notifications = [] self.check_warnings = defaultdict(set) self.timestamp = {} self.counter = 0 @@ -265,6 +267,7 @@ class Transaction(object): self.delete_records = None self.trigger_records = None self.log_records = None + self.user_notifications = None self.timestamp = None self._datamanagers = [] @@ -340,6 +343,19 @@ class Transaction(object): if self.log_records: self.log_records.clear() + def _store_user_notifications(self): + from trytond.pool import Pool + if self.user_notifications: + pool = Pool() + Notification = pool.get('res.notification') + with without_check_access(): + Notification.save(self.user_notifications) + self._clear_user_notifications() + + def _clear_user_notifications(self): + if self.user_notifications: + self.user_notifications.clear() + def _remove_warnings(self): from trytond.pool import Pool if self.check_warnings: @@ -358,6 +374,7 @@ class Transaction(object): from trytond.cache import Cache try: self._store_log_records() + self._store_user_notifications() self._remove_warnings() if self._datamanagers: for datamanager in self._datamanagers: @@ -391,6 +408,7 @@ class Transaction(object): datamanager.tpc_abort(self) Cache.rollback(self) self._clear_log_records() + self._clear_user_notifications() self._clear_warnings() if self.connection: self.connection.rollback()