diff --git a/addons/contacts/__manifest__.py b/addons/contacts/__manifest__.py
index 0a910c80538..0e7702f34ad 100644
--- a/addons/contacts/__manifest__.py
+++ b/addons/contacts/__manifest__.py
@@ -15,6 +15,9 @@ You can track your vendors, customers and other contacts.
'data': [
'views/contact_views.xml',
],
+ 'demo': [
+ 'data/mail_demo.xml',
+ ],
'application': True,
'license': 'LGPL-3',
'assets': {
diff --git a/addons/contacts/data/mail_demo.xml b/addons/contacts/data/mail_demo.xml
new file mode 100644
index 00000000000..12eb254cbd0
--- /dev/null
+++ b/addons/contacts/data/mail_demo.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Hello! Could you send us your address ?
+
+ res.partner
+
+ email
+
+
+
+
+
+
+
+
diff --git a/addons/im_livechat/tests/test_message.py b/addons/im_livechat/tests/test_message.py
index 3957660a45c..79a8a2c4b7d 100644
--- a/addons/im_livechat/tests/test_message.py
+++ b/addons/im_livechat/tests/test_message.py
@@ -73,6 +73,7 @@ class TestImLivechatMessage(TransactionCase):
'recipients': [],
'record_name': "test1 Ernest Employee",
'res_id': channel_livechat_1.id,
+ 'scheduledDatetime': False,
'sms_ids': [],
'starred_partner_ids': [],
'subject': False,
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index 03456375469..4ef8133eaef 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -904,10 +904,17 @@ class Message(models.Model):
domain = expression.AND([domain, [('id', '>', min_id)]])
return self.search(domain, limit=limit)
- def message_format(self, format_reply=True):
- """ Get the message values in the format for web client. Since message values can be broadcasted,
- computed fields MUST NOT BE READ and broadcasted.
- :returns list(dict).
+ def message_format(self, format_reply=True, msg_vals=None):
+ """ Get the message values in the format for web client. Since message
+ values can be broadcasted, computed fields MUST NOT BE READ and
+ broadcasted.
+
+ :param msg_vals: dictionary of values used to create the message. If
+ given it may be used to access values related to ``message`` without
+ accessing it directly. It lessens query count in some optimized use
+ cases by avoiding access message content in db;
+
+ :returns list(dict).
Example :
{
'body': HTML content of the message
@@ -958,6 +965,19 @@ class Message(models.Model):
com_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_comment')
note_id = self.env['ir.model.data']._xmlid_to_res_id('mail.mt_note')
+
+ # fetch scheduled notifications once, only if msg_vals is not given to
+ # avoid useless queries when notifying Inbox right after a message_post
+ scheduled_dt_by_msg_id = {}
+ if msg_vals:
+ scheduled_dt_by_msg_id = {msg.id: msg_vals.get('scheduled_date') for msg in self}
+ elif self:
+ schedulers = self.env['mail.message.schedule'].sudo().search([
+ ('mail_message_id', 'in', self.ids)
+ ])
+ for scheduler in schedulers:
+ scheduled_dt_by_msg_id[scheduler.mail_message_id.id] = scheduler.scheduled_datetime
+
for vals in vals_list:
message_sudo = self.browse(vals['id']).sudo().with_prefetch(self.ids)
notifs = message_sudo.notification_ids.filtered(lambda n: n.res_partner_id)
@@ -969,6 +989,7 @@ class Message(models.Model):
'subtype_description': message_sudo.subtype_id.description,
'is_notification': vals['message_type'] == 'user_notification',
'recipients': [{'id': p.id, 'name': p.name} for p in message_sudo.partner_ids],
+ 'scheduledDatetime': scheduled_dt_by_msg_id.get(vals['id'], False),
})
if vals['model'] and self.env[vals['model']]._original_module:
vals['module_icon'] = modules.module.get_module_icon(self.env[vals['model']]._original_module)
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index f65807673d2..dd60edc395a 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -2857,7 +2857,7 @@ class MailThread(models.AbstractModel):
} for pid in inbox_pids]
self.env['mail.notification'].sudo().create(notif_create_values)
- message_format_values = message.message_format()[0]
+ message_format_values = message.message_format(msg_vals=msg_vals)[0]
for partner_id in inbox_pids:
bus_notifications.append((self.env['res.partner'].browse(partner_id), 'mail.message/inbox', dict(message_format_values)))
self.env['bus.bus'].sudo()._sendmany(bus_notifications)
diff --git a/addons/mail/static/src/components/message/message.xml b/addons/mail/static/src/components/message/message.xml
index ed5c98dfcfd..e96eef8fa04 100644
--- a/addons/mail/static/src/components/message/message.xml
+++ b/addons/mail/static/src/components/message/message.xml
@@ -76,6 +76,11 @@
on document
+
diff --git a/addons/mail/static/src/models/message.js b/addons/mail/static/src/models/message.js
index 17bae319e1b..70d2e56d8f1 100644
--- a/addons/mail/static/src/models/message.js
+++ b/addons/mail/static/src/models/message.js
@@ -99,6 +99,9 @@ Model({
if ("recipients" in data) {
data2.recipients = data.recipients;
}
+ if ("scheduledDatetime" in data) {
+ data2.scheduledDatetime = data.scheduledDatetime;
+ }
if ("starred_partner_ids" in data && this.messaging.currentPartner) {
data2.isStarred = data.starred_partner_ids.includes(
this.messaging.currentPartner.id
@@ -698,6 +701,23 @@ Model({
},
}),
recipients: many("Partner"),
+ /**
+ * In case of delayed notification, a scheduled datetime linked to the
+ * message scheduler is given in order to display it new to the post
+ * datetime.
+ */
+ scheduledDatetime: attr(),
+ scheduledMomentDate: attr({
+ compute() {
+ if (!this.scheduledDatetime) {
+ return clear();
+ }
+ if (!moment.isMoment(this.scheduledDatetime)) {
+ return moment(str_to_datetime(this.scheduledDatetime));
+ }
+ return this.scheduledDatetime;
+ },
+ }),
shortTime: attr({
compute() {
if (!this.momentDate) {
diff --git a/addons/mail/static/src/models/message_view.js b/addons/mail/static/src/models/message_view.js
index d488451344c..b685547f642 100644
--- a/addons/mail/static/src/models/message_view.js
+++ b/addons/mail/static/src/models/message_view.js
@@ -713,5 +713,22 @@ Model({
return this.env._t("Read More");
},
}),
+ /**
+ * Scheduled for sending
+ */
+ scheduledFromNow: attr({
+ compute() {
+ if (!this.message) {
+ return clear();
+ }
+ if (!this.message.scheduledMomentDate) {
+ return clear();
+ }
+ if (!this.clockWatcher.clock.date) {
+ return clear();
+ }
+ return this.message.scheduledMomentDate.fromNow();
+ },
+ }),
},
});
diff --git a/addons/rating/models/mail_message.py b/addons/rating/models/mail_message.py
index dcd72234c3b..5a8419af34a 100644
--- a/addons/rating/models/mail_message.py
+++ b/addons/rating/models/mail_message.py
@@ -26,8 +26,8 @@ class MailMessage(models.Model):
])
return [('id', 'in', ratings.mapped('message_id').ids)]
- def message_format(self, format_reply=True):
- message_values = super().message_format(format_reply=format_reply)
+ def message_format(self, format_reply=True, msg_vals=None):
+ message_values = super().message_format(format_reply=format_reply, msg_vals=msg_vals)
rating_mixin_messages = self.filtered(lambda message:
message.model
and message.res_id
diff --git a/addons/sms/models/mail_message.py b/addons/sms/models/mail_message.py
index e3a4c67c5df..65906097a84 100644
--- a/addons/sms/models/mail_message.py
+++ b/addons/sms/models/mail_message.py
@@ -33,13 +33,13 @@ class MailMessage(models.Model):
return ['&', ('notification_ids.notification_status', '=', 'exception'), ('notification_ids.notification_type', '=', 'sms')]
raise NotImplementedError()
- def message_format(self, format_reply=True):
+ def message_format(self, format_reply=True, msg_vals=None):
""" Override in order to retrieves data about SMS (recipient name and
SMS status)
TDE FIXME: clean the overall message_format thingy
"""
- message_values = super(MailMessage, self).message_format(format_reply=format_reply)
+ message_values = super(MailMessage, self).message_format(format_reply=format_reply, msg_vals=msg_vals)
all_sms_notifications = self.env['mail.notification'].sudo().search([
('mail_message_id', 'in', [r['id'] for r in message_values]),
('notification_type', '=', 'sms')
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index 914f0542f45..ba1b99a8ecd 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -1187,7 +1187,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
]
}])
- with self.assertQueryCount(employee=6):
+ with self.assertQueryCount(employee=7):
res = messages.message_format()
self.assertEqual(len(res), 2)
for message in res:
@@ -1196,7 +1196,7 @@ class TestMailComplexPerformance(BaseMailPerformance):
self.env.flush_all()
self.env.invalidate_all()
- with self.assertQueryCount(employee=19):
+ with self.assertQueryCount(employee=20):
res = messages.message_format()
self.assertEqual(len(res), 2)
for message in res:
@@ -1217,14 +1217,14 @@ class TestMailComplexPerformance(BaseMailPerformance):
'res_id': record.id
} for record in records])
- with self.assertQueryCount(employee=5):
+ with self.assertQueryCount(employee=6):
res = messages.message_format()
self.assertEqual(len(res), 6)
self.env.flush_all()
self.env.invalidate_all()
- with self.assertQueryCount(employee=14):
+ with self.assertQueryCount(employee=15):
res = messages.message_format()
self.assertEqual(len(res), 6)