diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index c22586eb837..7a046cccd42 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -71,7 +71,6 @@
-
@@ -632,7 +631,6 @@
-
@@ -762,8 +760,7 @@
-
-
+
diff --git a/addons/mail/__manifest__.py b/addons/mail/__manifest__.py
index d5b0f118b71..a87b132c357 100644
--- a/addons/mail/__manifest__.py
+++ b/addons/mail/__manifest__.py
@@ -2,7 +2,7 @@
{
'name': 'Discuss',
- 'version': '1.7',
+ 'version': '1.8',
'category': 'Productivity/Discuss',
'sequence': 145,
'summary': 'Chat, mail gateway and private channels',
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py
index 07bd82af11f..322ba2c8f98 100644
--- a/addons/mail/models/mail_channel.py
+++ b/addons/mail/models/mail_channel.py
@@ -770,11 +770,10 @@ class Channel(models.Model):
# find the channel partner state, if logged user
if self.env.user and self.env.user.partner_id:
info['message_needaction_counter'] = channel.message_needaction_counter
- info['message_unread_counter'] = channel.message_unread_counter
- partner_channel = member_of_current_user_by_channel.get(channel, self.env['mail.channel.partner'])
+ partner_channel = member_of_current_user_by_channel.get(channel, self.env['mail.channel.partner']).with_prefetch([m.id for m in member_of_current_user_by_channel.values()])
if partner_channel:
- partner_channel = partner_channel[0]
info['state'] = partner_channel.fold_state or 'open'
+ info['message_unread_counter'] = partner_channel.message_unread_counter
info['is_minimized'] = partner_channel.is_minimized
info['seen_message_id'] = partner_channel.seen_message_id.id
info['custom_channel_name'] = partner_channel.custom_channel_name
diff --git a/addons/mail/models/mail_channel_partner.py b/addons/mail/models/mail_channel_partner.py
index 6711e7c7a6c..38b0476c218 100644
--- a/addons/mail/models/mail_channel_partner.py
+++ b/addons/mail/models/mail_channel_partner.py
@@ -23,6 +23,7 @@ class ChannelPartner(models.Model):
custom_channel_name = fields.Char('Custom channel name')
fetched_message_id = fields.Many2one('mail.message', string='Last Fetched')
seen_message_id = fields.Many2one('mail.message', string='Last Seen')
+ message_unread_counter = fields.Integer('Unread Messages Counter', compute='_compute_message_unread', compute_sudo=True, help='Number of unread messages')
fold_state = fields.Selection([('open', 'Open'), ('folded', 'Folded'), ('closed', 'Closed')], string='Conversation Fold State', default='open')
is_minimized = fields.Boolean("Conversation is minimized")
is_pinned = fields.Boolean("Is pinned on the interface", default=True)
@@ -31,6 +32,28 @@ class ChannelPartner(models.Model):
rtc_session_ids = fields.One2many(string="RTC Sessions", comodel_name='mail.channel.rtc.session', inverse_name='channel_partner_id')
rtc_inviting_session_id = fields.Many2one('mail.channel.rtc.session', string='Ringing session')
+ @api.depends('channel_id.message_ids', 'seen_message_id')
+ def _compute_message_unread(self):
+ self.flush()
+ self.env.cr.execute("""
+ SELECT count(mail_message.id) AS count,
+ mail_channel_partner.id
+ FROM mail_message
+ INNER JOIN mail_channel_partner
+ ON mail_channel_partner.channel_id = mail_message.res_id
+ WHERE mail_message.model = 'mail.channel'
+ AND mail_message.message_type NOT IN ('notification', 'user_notification')
+ AND (
+ mail_message.id > mail_channel_partner.seen_message_id
+ OR mail_channel_partner.seen_message_id IS NULL
+ )
+ AND mail_channel_partner.id IN %(ids)s
+ GROUP BY mail_channel_partner.id
+ """, {'ids': tuple(self.ids)})
+ unread_counter_by_channel_partner = {res['id']: res['count'] for res in self.env.cr.dictfetchall()}
+ for channel_partner in self:
+ channel_partner.message_unread_counter = unread_counter_by_channel_partner.get(channel_partner.id)
+
def name_get(self):
return [(record.id, record.partner_id.name or record.guest_id.name) for record in self]
diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py
index a0403666a97..1d7189a83e8 100644
--- a/addons/mail/models/mail_message.py
+++ b/addons/mail/models/mail_message.py
@@ -1088,8 +1088,6 @@ class Message(models.Model):
if issubclass(self.pool[model], self.pool['mail.thread']):
self.env[model].invalidate_cache(fnames=[
'message_ids',
- 'message_unread',
- 'message_unread_counter',
'message_needaction',
'message_needaction_counter',
], ids=[res_id])
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 7b82d1006bf..ef2fe1c7631 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -89,12 +89,6 @@ class MailThread(models.AbstractModel):
'mail.message', 'res_id', string='Messages',
domain=lambda self: [('message_type', '!=', 'user_notification')], auto_join=True)
has_message = fields.Boolean(compute="_compute_has_message", search="_search_has_message", store=False)
- message_unread = fields.Boolean(
- 'Unread Messages', compute='_compute_message_unread',
- help="If checked, new messages require your attention.")
- message_unread_counter = fields.Integer(
- 'Unread Messages Counter', compute='_compute_message_unread',
- help="Number of unread messages")
message_needaction = fields.Boolean(
'Action Needed',
compute='_compute_message_needaction', search='_search_message_needaction',
@@ -176,27 +170,6 @@ class MailThread(models.AbstractModel):
operator_new = 'not inselect'
return [('id', operator_new, ("SELECT res_id FROM mail_message WHERE model=%s", [self._name]))]
- def _compute_message_unread(self):
- partner_id = self.env.user.partner_id.id
- res = dict.fromkeys(self.ids, 0)
- if self.ids:
- # search for unread messages, directly in SQL to improve performances
- self._cr.execute(""" SELECT msg.res_id FROM mail_message msg
- RIGHT JOIN mail_channel_partner cp
- ON (cp.channel_id = msg.res_id AND cp.partner_id = %s AND
- (cp.seen_message_id IS NULL OR cp.seen_message_id < msg.id))
- WHERE msg.model = %s AND msg.res_id = ANY(%s) AND
- msg.message_type != 'user_notification' AND
- (msg.author_id IS NULL OR msg.author_id != %s) AND
- (msg.message_type not in ('notification', 'user_notification') OR msg.model != 'mail.channel')""",
- (partner_id, self._name, list(self.ids), partner_id,))
- for result in self._cr.fetchall():
- res[result[0]] += 1
-
- for record in self:
- record.message_unread_counter = res.get(record._origin.id, 0)
- record.message_unread = bool(record.message_unread_counter)
-
def _compute_message_needaction(self):
res = dict.fromkeys(self.ids, 0)
if self.ids:
diff --git a/addons/mail/static/tests/helpers/model_definitions_setup.js b/addons/mail/static/tests/helpers/model_definitions_setup.js
index b80e51a6544..2097d2cbd76 100644
--- a/addons/mail/static/tests/helpers/model_definitions_setup.js
+++ b/addons/mail/static/tests/helpers/model_definitions_setup.js
@@ -53,6 +53,7 @@ insertModelFields('mail.channel', {
string: "Members",
type: 'many2many'
},
+ message_unread_counter: { string: 'Unread counter', type: 'integer' },
seen_message_id: { relation: 'mail.message', string: "Last Seen", type: 'many2one' },
state: { default: 'open', string: "FoldState", type: "char" },
uuid: { default: () => _.uniqueId('mail.channel_uuid-') },
diff --git a/addons/mail/tests/test_mail_channel_partner.py b/addons/mail/tests/test_mail_channel_partner.py
index ab89753d124..8f6e8a66424 100644
--- a/addons/mail/tests/test_mail_channel_partner.py
+++ b/addons/mail/tests/test_mail_channel_partner.py
@@ -30,6 +30,10 @@ class TestMailChannelMembers(MailCommon):
cls.env, login='user_2',
name='User 2',
groups='base.group_user,mail.secret_group')
+ cls.user_3 = mail_new_test_user(
+ cls.env, login='user_3',
+ name='User 3',
+ groups='base.group_user,mail.secret_group')
cls.user_portal = mail_new_test_user(
cls.env, login='user_portal',
name='User Portal',
@@ -222,3 +226,54 @@ class TestMailChannelMembers(MailCommon):
self.public_channel.with_user(self.user_portal).add_members(self.user_portal.partner_id.ids)
with self.assertRaises(AccessError):
self.public_channel.with_user(self.user_public).add_members(self.user_public.partner_id.ids)
+
+ # ------------------------------------------------------------
+ # UNREAD COUNTER TESTS
+ # ------------------------------------------------------------
+
+ def test_unread_counter_with_message_post(self):
+ channel_as_user_1 = self.env['mail.channel'].with_user(self.user_1).create({
+ 'name': 'Secret channel',
+ 'public': 'public',
+ 'channel_type': 'channel',
+ })
+ channel_as_user_1.with_user(self.user_1).add_members(self.user_1.partner_id.ids)
+ channel_as_user_1.with_user(self.user_1).add_members(self.user_2.partner_id.ids)
+ channel_1_rel_user_2 = self.env['mail.channel.partner'].search([
+ ('channel_id', '=', channel_as_user_1.id),
+ ('partner_id', '=', self.user_2.partner_id.id)
+ ])
+ self.assertEqual(channel_1_rel_user_2.message_unread_counter, 0, "should not have unread message initially as notification type is ignored")
+
+ channel_as_user_1.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
+ channel_1_rel_user_2 = self.env['mail.channel.partner'].search([
+ ('channel_id', '=', channel_as_user_1.id),
+ ('partner_id', '=', self.user_2.partner_id.id)
+ ])
+ self.assertEqual(channel_1_rel_user_2.message_unread_counter, 1, "should have 1 unread message after someone else posted a message")
+
+ def test_unread_counter_with_message_post_multi_channel(self):
+ channel_1_as_user_1 = self.env['mail.channel'].with_user(self.user_1).create({
+ 'name': 'wololo channel',
+ 'public': 'public',
+ 'channel_type': 'channel',
+ })
+ channel_2_as_user_2 = self.env['mail.channel'].with_user(self.user_2).create({
+ 'name': 'walala channel',
+ 'public': 'public',
+ 'channel_type': 'channel',
+ })
+ channel_1_as_user_1.add_members(self.user_2.partner_id.ids)
+ channel_2_as_user_2.add_members(self.user_1.partner_id.ids)
+ channel_2_as_user_2.add_members(self.user_3.partner_id.ids)
+ channel_1_as_user_1.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
+ channel_1_as_user_1.message_post(body='Test 2', message_type='comment', subtype_xmlid='mail.mt_comment')
+ channel_2_as_user_2.message_post(body='Test', message_type='comment', subtype_xmlid='mail.mt_comment')
+ members = self.env['mail.channel.partner'].search([('channel_id', 'in', (channel_1_as_user_1 + channel_2_as_user_2).ids)], order="id")
+ self.assertEqual(members.mapped('message_unread_counter'), [
+ 0, # channel 1 user 1: posted last message
+ 0, # channel 2 user 2: posted last message
+ 2, # channel 1 user 2: received 2 messages (from message post)
+ 1, # channel 2 user 1: received 1 message (from message post)
+ 1, # channel 2 user 3: received 1 message (from message post)
+ ])
diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml
index d1f2e4e599a..15739b3e612 100644
--- a/addons/purchase/views/purchase_views.xml
+++ b/addons/purchase/views/purchase_views.xml
@@ -516,10 +516,9 @@
purchase.order
-
-
@@ -548,13 +547,12 @@
-
@@ -581,8 +579,7 @@
purchase.order.view.tree
purchase.order
-
-
diff --git a/addons/test_discuss_full/tests/test_performance.py b/addons/test_discuss_full/tests/test_performance.py
index 6bd8230774c..5e74ef874ee 100644
--- a/addons/test_discuss_full/tests/test_performance.py
+++ b/addons/test_discuss_full/tests/test_performance.py
@@ -99,7 +99,7 @@ class TestDiscussFullPerformance(TransactionCase):
self.maxDiff = None
self.users[0].flush()
self.users[0].invalidate_cache()
- with self.assertQueryCount(emp=85): # ent: 84
+ with self.assertQueryCount(emp=86):
init_messaging = self.users[0].with_user(self.users[0])._init_messaging()
self.assertEqual(init_messaging, {