[IMP] mail: display scheduled_datetime of delayed notify in chatter
Followup of odoo/odoo#95623 (introduce scheduled message notification) as well as odoo/odoo#99482 (support scheduled datetime from template to composer in both comment and mailing modes). Task-3093257 (Mail: The Composer Update) Part-of: odoo/odoo#107356
This commit is contained in:
@@ -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': {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
|
||||
<!-- Message with scheduled notification -->
|
||||
<record id="message_demo_partner_2_0" model="mail.message">
|
||||
<field name="author_id" ref="base.partner_demo"/>
|
||||
<field name="body" type="html"><p>Hello! Could you send us your address ?</p></field>
|
||||
<field name="date" eval="(DateTime.today() - timedelta(days=1)).strftime('%Y-%m-%d %H:%M:00')"/>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.res_partner_2"/>
|
||||
<field name="message_type">email</field>
|
||||
<field name="subtype_id" ref="mail.mt_comment"/>
|
||||
</record>
|
||||
<record id="message_demo_partner_1_5_notif_0" model="mail.message.schedule">
|
||||
<field name="mail_message_id" ref="message_demo_partner_2_0"/>
|
||||
<field name="scheduled_datetime" eval="(DateTime.today() + timedelta(days=2)).strftime('%Y-%m-%d %H:%M:00')"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -76,6 +76,11 @@
|
||||
on <a class="o_MessageView_originThreadLink fs-6" t-att-href="message.originThread.url" t-on-click="onClickOriginThread"><t t-if="message.originThread.displayName"><t t-esc="message.originThread.displayName"/></t><t t-else="">document</t></a>
|
||||
</t>
|
||||
</small>
|
||||
<div t-if="message.scheduledDatetime" class="o_MessageView_headerScheduledDatetime" t-att-class="{ 'ms-2': isInChatWindowAndIsAlignedRight }" t-att-title="scheduledFromNow">
|
||||
<span class="text-600 cursor-pointer">
|
||||
<i class="fa fa-calendar-o"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div t-if="messageListViewItemOwner and message.originThread and message.originThread === messageListViewItemOwner.messageListViewOwner.threadViewOwner.thread and message.notifications.length > 0" t-att-class="{ 'ms-2': isInChatWindowAndIsAlignedRight }">
|
||||
<span t-if="message.failureNotifications.length > 0" class="o_MessageView_notificationIconClickable o-error cursor-pointer text-danger" role="button" tabindex="0" t-on-click="onClickFailure">
|
||||
<i class="o_MessageView_notificationIcon" t-att-class="failureNotificationIconClassName" role="img" aria-label="Delivery failure"/> <span t-if="failureNotificationIconLabel" t-out="failureNotificationIconLabel"/>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user