[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:
@@ -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"/>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
{
|
||||
'name': 'Discuss',
|
||||
'version': '1.7',
|
||||
'version': '1.8',
|
||||
'category': 'Productivity/Discuss',
|
||||
'sequence': 145,
|
||||
'summary': 'Chat, mail gateway and private channels',
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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)
|
||||
])
|
||||
|
||||
@@ -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, {
|
||||
|
||||
Reference in New Issue
Block a user