[IMP] mail: move message unread counter to channel

The field could have been removed from thread since https://github.com/odoo/odoo/commit/88b8cd058713bfad2942f7e7434c34b6c1a6e7da

task-2746109

closes odoo/odoo#83765

Related: odoo/upgrade#3222
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
This commit is contained in:
Didier (did)
2022-03-21 14:59:42 +01:00
parent 25fa0a3833
commit fc9e67bc6b
10 changed files with 87 additions and 45 deletions
+1 -4
View File
@@ -71,7 +71,6 @@
<field name="total_amount_company" optional="show" sum="Total Company Currency" widget='monetary' options="{'currency_field': 'company_currency_id'}" decoration-bf="True"/>
<field name="total_amount" optional="hide" decoration-bf="True" groups="base.group_multi_currency"/>
<field name="currency_id" optional="hide" groups="base.group_multi_currency"/>
<field name="message_unread" invisible="1"/>
<field name="state" optional="show" readonly="1" decoration-info="state == 'draft'" decoration-success="state in ['reported', 'approved', 'done']" decoration-danger="state in 'refused'" widget="badge"/>
</tree>
</field>
@@ -632,7 +631,6 @@
<field name="journal_id" optional="hide"/>
<field name="state" optional="show" decoration-info="state == 'draft'" decoration-success="state in ['submit', 'approve', 'post', 'done']" decoration-danger="state == 'cancel'" widget="badge"/>
<field name="payment_state" optional="show" decoration-info="payment_state in ('partial','in_payment')" decoration-success="payment_state == 'paid'" decoration-danger="payment_state in ('reversed','not_paid')" widget="badge"/>
<field name="message_unread" invisible="1"/>
</tree>
</field>
</record>
@@ -762,8 +760,7 @@
<field name="reference" optional="hide" readonly="True"/>
<field name="analytic_account_id" optional="show" domain="['|', ('company_id', '=', parent.company_id), ('company_id', '=', False)]" groups="analytic.group_analytic_accounting"/>
<field name="analytic_tag_ids" optional="hide" widget="many2many_tags" groups="analytic.group_analytic_tags"/>
<field name="account_id" optional="hide"/>
<field name="message_unread" invisible="1"/>
<field name="account_id" optional="hide" readonly="True"/>
<button name="action_get_attachment_view" type="object" icon="fa-paperclip" aria-label="View Attachments" title="View Attachments" class="float-right pr-0" readonly="True"/>
<field name="attachment_number" class="text-right pl-0 pr-4" width="1" nolabel="1"/>
<field name="product_has_cost" invisible="True"/>
+1 -1
View File
@@ -2,7 +2,7 @@
{
'name': 'Discuss',
'version': '1.7',
'version': '1.8',
'category': 'Productivity/Discuss',
'sequence': 145,
'summary': 'Chat, mail gateway and private channels',
+2 -3
View File
@@ -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
@@ -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]
-2
View File
@@ -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])
-27
View File
@@ -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:
@@ -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-') },
@@ -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)
])
+3 -7
View File
@@ -516,10 +516,9 @@
<field name="model">purchase.order</field>
<field name="priority" eval="1"/>
<field name="arch" type="xml">
<tree string="Purchase Order" decoration-bf="message_unread==True"
<tree string="Purchase Order"
decoration-muted="state=='cancel'" sample="1">
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="message_unread" invisible="1"/>
<field name="partner_ref" optional="hide"/>
<field name="name" string="Reference" readonly="1" decoration-info="state in ('draft','sent')"/>
<field name="date_order" invisible="not context.get('quotation_only', False)" optional="show"/>
@@ -548,13 +547,12 @@
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<tree string="Purchase Order" decoration-info="state in ['draft', 'sent']"
decoration-muted="state == 'cancel'" decoration-bf="message_unread==True"
decoration-muted="state == 'cancel'"
class="o_purchase_order" js_class="purchase_list_dashboard" sample="1">
<header>
<button name="action_create_invoice" type="object" string="Create Bills"/>
</header>
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="message_unread" invisible="1"/>
<field name="partner_ref" optional="hide"/>
<field name="name" string="Reference" readonly="1" decoration-bf="1"/>
<field name="date_approve" invisible="context.get('quotation_only', False)" optional="show"/>
@@ -581,8 +579,7 @@
<field name="name">purchase.order.view.tree</field>
<field name="model">purchase.order</field>
<field name="arch" type="xml">
<tree decoration-bf="message_unread==True"
decoration-muted="state == 'cancel'"
<tree decoration-muted="state == 'cancel'"
decoration-info="invoice_status == 'to invoice'"
string="Purchase Order"
class="o_purchase_order"
@@ -591,7 +588,6 @@
<button name="action_create_invoice" type="object" string="Create Bills"/>
</header>
<field name="priority" optional="show" widget="priority" nolabel="1"/>
<field name="message_unread" invisible="1"/>
<field name="partner_ref" optional="hide"/>
<field name="name" string="Reference" readonly="1" decoration-bf="1" decoration-info="state in ('draft','sent')"/>
<field name="date_approve" widget="date" invisible="context.get('quotation_only', False)" optional="show"/>
@@ -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, {