diff --git a/addons/crm_livechat/models/mail_channel.py b/addons/crm_livechat/models/mail_channel.py index 7747d6926f8..c051309c813 100644 --- a/addons/crm_livechat/models/mail_channel.py +++ b/addons/crm_livechat/models/mail_channel.py @@ -29,7 +29,7 @@ class MailChannel(models.Model): """ description = ''.join( '%s: %s\n' % (message.author_id.name or self.anonymous_name, message.body) - for message in self.channel_message_ids.sorted('id') + for message in self.message_ids.sorted('id') ) # if public user is part of the chat: consider lead to be linked to an # anonymous user whatever the participants. Otherwise keep only share diff --git a/addons/im_livechat/data/im_livechat_channel_demo.xml b/addons/im_livechat/data/im_livechat_channel_demo.xml index 59d2b3b1072..2e9bffd27f4 100644 --- a/addons/im_livechat/data/im_livechat_channel_demo.xml +++ b/addons/im_livechat/data/im_livechat_channel_demo.xml @@ -38,7 +38,6 @@ email You're welcome, have a nice day! - @@ -47,7 +46,6 @@ email Great! Thanks for the info - @@ -56,7 +54,6 @@ email Yes, you can use our Timesheets application and Awesome Timesheets to record your time efficiently! - @@ -65,7 +62,6 @@ email I'm looking for an application to record my timesheet, any tips? - @@ -74,7 +70,6 @@ email Hello, how may I help you? - @@ -99,7 +94,6 @@ email You're welcome, enjoy Odoo! - @@ -108,7 +102,6 @@ email Awesome, thanks! - @@ -117,7 +110,6 @@ email Yes, we just released a new application called Social Marketing that should fit your needs! Check it out :) - @@ -126,7 +118,6 @@ email I was wondering if Odoo has an application to easily manage social media for my business.. - @@ -135,7 +126,6 @@ email Hello, how may I help you? - @@ -160,7 +150,6 @@ email Oh :( - @@ -169,7 +158,6 @@ email Nope, sorry to disappoint :( - @@ -178,7 +166,6 @@ email Hello, are you single? - @@ -187,7 +174,6 @@ email Hello, how may I help you? - @@ -212,7 +198,6 @@ email Thanks for the info, I'll look into it! - @@ -221,7 +206,6 @@ email Hello Joel Willis, you're at the right place! You can customize Odoo using our Studio application in just a few clicks. - @@ -230,7 +214,6 @@ email Hello, I'm looking for a software that can be easily updated with my needs. - @@ -255,7 +238,6 @@ email Ok.. Will do, thanks - @@ -264,7 +246,6 @@ email Hi, if you need help with your database, feel free to contact our support via http://www.odoo.com/help - @@ -273,7 +254,6 @@ email Hello, it seems that I can't log in to my database. Can you help? - @@ -282,7 +262,6 @@ email Hello, how may I help you? - @@ -307,7 +286,6 @@ email Thanks! - @@ -316,7 +294,6 @@ email Yes, of course, you can find it here: https://www.odoo.com/documentation/user/14.0/ - @@ -325,7 +302,6 @@ email Hello, I'm a bit lost in the Invetory module, is there some documentation I could find? - @@ -334,7 +310,6 @@ email Hello, how may I help you? - @@ -359,7 +334,6 @@ email Good to hear, thanks! - @@ -368,7 +342,6 @@ email Joel Willis, you'll need our Inventory and Sales application to do so. You can try them for 15 days, FOR FREE :) - @@ -377,7 +350,6 @@ email Hi, I need a software to easily manage my stock, and generate sales orders. - @@ -386,7 +358,6 @@ email Hello, how may I help you? - @@ -411,7 +382,6 @@ email I'm great, thanks for asking! - @@ -420,7 +390,6 @@ email I'm fine, and you? - @@ -429,7 +398,6 @@ email Heeeey Marc, how are you? - @@ -438,7 +406,6 @@ email Hello, how may I help you? - @@ -458,7 +425,6 @@ Hi mail.channel - @@ -467,7 +433,6 @@ Hello, how may I help you? mail.channel - @@ -477,7 +442,6 @@ I would like to know more about the CRM application mail.channel - @@ -486,7 +450,6 @@ The CRM application helps you to track leads, close opportunities and get accurate forecasts. You can test it for free on our website. mail.channel - @@ -496,7 +459,6 @@ Great, thanks! mail.channel - @@ -506,7 +468,6 @@ Rating: :-) mail.channel - diff --git a/addons/im_livechat/models/im_livechat_channel.py b/addons/im_livechat/models/im_livechat_channel.py index a79734a92ef..8a8ac0fcdd8 100644 --- a/addons/im_livechat/models/im_livechat_channel.py +++ b/addons/im_livechat/models/im_livechat_channel.py @@ -76,7 +76,7 @@ class ImLivechatChannel(models.Model): def _compute_nbr_channel(self): data = self.env['mail.channel'].read_group([ ('livechat_channel_id', 'in', self._ids), - ('channel_message_ids', '!=', False)], ['__count'], ['livechat_channel_id'], lazy=False) + ('message_ids', '!=', False)], ['__count'], ['livechat_channel_id'], lazy=False) channel_count = {x['livechat_channel_id'][0]: x['__count'] for x in data} for record in self: record.nbr_channel = channel_count.get(record.id, 0) @@ -182,8 +182,7 @@ class ImLivechatChannel(models.Model): self.env.cr.execute("""SELECT COUNT(DISTINCT c.id), c.livechat_operator_id FROM mail_channel c - LEFT OUTER JOIN mail_message_mail_channel_rel r ON c.id = r.mail_channel_id - LEFT OUTER JOIN mail_message m ON r.mail_message_id = m.id + LEFT OUTER JOIN mail_message m ON c.id = m.res_id AND m.model = 'mail.channel' WHERE c.channel_type = 'livechat' AND c.livechat_operator_id in %s AND m.create_date > ((now() at time zone 'UTC') - interval '30 minutes') diff --git a/addons/im_livechat/models/mail_channel.py b/addons/im_livechat/models/mail_channel.py index 8b9f4e102cd..c77e0082702 100644 --- a/addons/im_livechat/models/mail_channel.py +++ b/addons/im_livechat/models/mail_channel.py @@ -131,8 +131,8 @@ class MailChannel(models.Model): FROM mail_channel C WHERE NOT EXISTS ( SELECT * - FROM mail_message_mail_channel_rel R - WHERE R.mail_channel_id = C.id + FROM mail_message M + WHERE M.res_id = C.id AND m.model = 'mail.channel' ) AND C.channel_type = 'livechat' AND livechat_channel_id IS NOT NULL AND COALESCE(write_date, create_date, (now() at time zone 'UTC'))::timestamp < ((now() at time zone 'UTC') - interval %s)""", ("%s hours" % hours,)) @@ -177,7 +177,7 @@ class MailChannel(models.Model): if self.livechat_active: self.livechat_active = False # avoid useless notification if the channel is empty - if not self.channel_message_ids: + if not self.message_ids: return # Notify that the visitor has left the conversation self.message_post(author_id=self.env.ref('base.partner_root').id, diff --git a/addons/im_livechat/report/im_livechat_report_channel.py b/addons/im_livechat/report/im_livechat_report_channel.py index 07c01110b8e..9e3d2c79cbb 100644 --- a/addons/im_livechat/report/im_livechat_report_channel.py +++ b/addons/im_livechat/report/im_livechat_report_channel.py @@ -59,9 +59,11 @@ class ImLivechatReportChannel(models.Model): count(distinct C.livechat_operator_id) as nbr_speaker, count(distinct M.id) as nbr_message, CASE - WHEN EXISTS (select distinct M.author_id FROM mail_message M, mail_message_mail_channel_rel R - WHERE M.author_id=C.livechat_operator_id AND R.mail_channel_id = C.id - AND R.mail_message_id = M.id and C.livechat_operator_id = M.author_id) + WHEN EXISTS (select distinct M.author_id FROM mail_message M + WHERE M.author_id=C.livechat_operator_id + AND M.res_id = C.id + AND M.model = 'mail.channel' + AND C.livechat_operator_id = M.author_id) THEN 0 ELSE 1 END as is_without_answer, @@ -88,10 +90,9 @@ class ImLivechatReportChannel(models.Model): END as is_unrated, C.livechat_operator_id as partner_id FROM mail_channel C - JOIN mail_message_mail_channel_rel R ON (C.id = R.mail_channel_id) - JOIN mail_message M ON (M.id = R.mail_message_id) + JOIN mail_message M ON (M.res_id = C.id AND m.model = 'mail.channel') JOIN im_livechat_channel L ON (L.id = C.livechat_channel_id) - LEFT JOIN mail_message MO ON (R.mail_message_id = MO.id AND MO.author_id = C.livechat_operator_id) + LEFT JOIN mail_message MO ON (M.res_id = C.id AND m.model = 'mail.channel' AND MO.author_id = C.livechat_operator_id) LEFT JOIN rating_rating Rate ON (Rate.res_id = C.id and Rate.res_model = 'mail.channel' and Rate.parent_res_model = 'im_livechat.channel') WHERE C.livechat_operator_id is not null GROUP BY C.livechat_operator_id, C.id, C.name, C.livechat_channel_id, L.name, C.create_date, C.uuid, Rate.rating diff --git a/addons/im_livechat/report/im_livechat_report_operator.py b/addons/im_livechat/report/im_livechat_report_operator.py index cafaf898da2..8dec7061b16 100644 --- a/addons/im_livechat/report/im_livechat_report_operator.py +++ b/addons/im_livechat/report/im_livechat_report_operator.py @@ -35,9 +35,8 @@ class ImLivechatReportOperator(models.Model): EXTRACT('epoch' FROM MAX(M.create_date) - MIN(M.create_date)) AS duration, EXTRACT('epoch' FROM MIN(MO.create_date) - MIN(M.create_date)) AS time_to_answer FROM mail_channel C - JOIN mail_message_mail_channel_rel R ON R.mail_channel_id = C.id - JOIN mail_message M ON R.mail_message_id = M.id - LEFT JOIN mail_message MO ON (R.mail_message_id = MO.id AND MO.author_id = C.livechat_operator_id) + JOIN mail_message M ON M.res_id = C.id AND M.model = 'mail.channel' + LEFT JOIN mail_message MO ON (MO.res_id = C.id AND M.model = 'mail.channel' AND MO.author_id = C.livechat_operator_id) WHERE C.livechat_channel_id IS NOT NULL GROUP BY C.id, C.livechat_operator_id ) diff --git a/addons/im_livechat/static/src/components/discuss/discuss_tests.js b/addons/im_livechat/static/src/components/discuss/discuss_tests.js index 4cfd207ff1c..36c38f6180a 100644 --- a/addons/im_livechat/static/src/components/discuss/discuss_tests.js +++ b/addons/im_livechat/static/src/components/discuss/discuss_tests.js @@ -264,8 +264,8 @@ QUnit.test('livechats are sorted by last message date in the sidebar: most recen assert.expect(7); this.data['mail.message'].records.push( - { id: 11, channel_ids: [11] }, // least recent message due to smaller id - { id: 12, channel_ids: [12] }, // most recent message due to higher id + { id: 11, model: "mail.channel", res_id: 11 }, // least recent message due to smaller id + { id: 12, model: "mail.channel", res_id: 12 }, // most recent message due to higher id ); this.data['mail.channel'].records.push( { @@ -345,7 +345,7 @@ QUnit.test('livechats with no messages are sorted by creation date in the sideba assert.expect(5); this.data['mail.message'].records.push( - { id: 13, channel_ids: [13] }, + { id: 13, model: "mail.channel", res_id: 13 }, ); this.data['mail.channel'].records.push( { diff --git a/addons/im_livechat/static/src/legacy/public_livechat.js b/addons/im_livechat/static/src/legacy/public_livechat.js index 951ebb9ca45..0d3fc1eac08 100644 --- a/addons/im_livechat/static/src/legacy/public_livechat.js +++ b/addons/im_livechat/static/src/legacy/public_livechat.js @@ -383,8 +383,9 @@ var LivechatButton = Widget.extend({ attachment_ids: [], author_id: this._livechat.getOperatorPID(), body: this.options.default_message, - channel_ids: [this._livechat.getID()], date: time.datetime_to_str(new Date()), + model: "mail.channel", + res_id: this._livechat.getID(), }, { prepend: true }); } }, diff --git a/addons/im_livechat/tests/test_get_mail_channel.py b/addons/im_livechat/tests/test_get_mail_channel.py index 30deee55d4a..fb31ab31121 100644 --- a/addons/im_livechat/tests/test_get_mail_channel.py +++ b/addons/im_livechat/tests/test_get_mail_channel.py @@ -87,8 +87,6 @@ class TestGetMailChannel(TransactionCase): mail_channel = self.livechat_channel._open_livechat_mail_channel('Anonymous') mail_channels.append(mail_channel) # send a message to mark this channel as 'active' - self.env['mail.channel'].browse(mail_channel['id']).write({ - 'channel_message_ids': [(0, 0, {'body': 'cc'})] - }) + self.env['mail.channel'].browse(mail_channel['id']).message_post(body='cc') return mail_channels diff --git a/addons/im_livechat/views/mail_channel_views.xml b/addons/im_livechat/views/mail_channel_views.xml index a9e03b819b3..b3245a8e270 100644 --- a/addons/im_livechat/views/mail_channel_views.xml +++ b/addons/im_livechat/views/mail_channel_views.xml @@ -24,7 +24,7 @@ - + @@ -129,7 +129,7 @@ Sessions mail.channel tree,form - [('livechat_channel_id', 'in', [active_id]), ('channel_message_ids', '!=', False)] + [('livechat_channel_id', 'in', [active_id]), ('message_ids', '!=', False)] { 'search_default_livechat_channel_id': [active_id], 'default_livechat_channel_id': active_id, diff --git a/addons/mail/data/mail_channel_data.xml b/addons/mail/data/mail_channel_data.xml index e99f9761f4e..7a8e4b0b628 100644 --- a/addons/mail/data/mail_channel_data.xml +++ b/addons/mail/data/mail_channel_data.xml @@ -11,7 +11,6 @@ mail.channel - email Welcome to Odoo! diff --git a/addons/mail/data/mail_channel_demo.xml b/addons/mail/data/mail_channel_demo.xml index a38123f80c7..5b1137089c6 100644 --- a/addons/mail/data/mail_channel_demo.xml +++ b/addons/mail/data/mail_channel_demo.xml @@ -23,7 +23,6 @@ Selling a training session and selling the products after the training session is more efficient than directly selling a pack with the training session and the products.

]]>
comment -
@@ -33,7 +32,6 @@ I noted I can not manage efficiently my pipeline when I have more than 50 opportunities in the qualification stage.

Any advice on this? How do you organize your activities with more than 50 opportunities?

]]>
comment -
@@ -44,7 +42,6 @@ comment - @@ -53,7 +50,6 @@ mail.channel - Great news!
@@ -70,7 +66,6 @@ mail.channel - Your monthly meal vouchers arrived. You can get them at the HR's office.

This month you also get 250 EUR of eco-vouchers if you have been in the company for more than a year.

]]>
comment @@ -81,7 +76,6 @@ mail.channel - Thanks! Could you please remind me where is Christine's office, if I may ask? I'm new here!

]]>
comment @@ -92,7 +86,6 @@ mail.channel - Building B3, second floor on the right :-).

]]>
comment @@ -158,7 +151,6 @@ ]]> comment -
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 521661a48c0..00f7b5de214 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -57,9 +57,6 @@ class Channel(models.Model): channel_last_seen_partner_ids = fields.One2many( 'mail.channel.partner', 'channel_id', string='Last Seen', groups='base.group_user') - channel_message_ids = fields.Many2many( - 'mail.message', 'mail_message_mail_channel_rel', - string='Channel Messages') is_member = fields.Boolean('Is Member', compute='_compute_is_member', compute_sudo=True) group_ids = fields.Many2many( 'res.groups', string='Auto Subscription', @@ -571,7 +568,6 @@ class Channel(models.Model): # When posting a message on a mail channel, manage moderation and postpone notify users if not msg_vals or msg_vals.get('moderation_status') != 'pending_moderation': # link message to channel - message.write({'channel_ids': [(4, cid) for cid in self.ids]}) rdata = super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs) message_format_values = message.message_format()[0] @@ -871,7 +867,7 @@ class Channel(models.Model): :rtype : list(dict) """ self.ensure_one() - domain = [("channel_ids", "in", self.ids)] + domain = ["&", ("model", "=", "mail.channel"), ("res_id", "in", self.ids)] if last_id: domain.append(("id", "<", last_id)) return self.env['mail.message'].message_fetch(domain=domain, limit=limit) @@ -990,7 +986,7 @@ class Channel(models.Model): kept only for compatibility reasons. """ self.ensure_one() - domain = [('channel_ids', 'in', self.ids)] + domain = ["&", ("model", "=", "mail.channel"), ("res_id", "in", self.ids)] if last_message_id: domain = expression.AND([domain, [('id', '<=', last_message_id)]]) last_message = self.env['mail.message'].search(domain, order="id DESC", limit=1) @@ -1032,14 +1028,13 @@ class Channel(models.Model): def channel_fetched(self): """ Broadcast the channel_fetched notification to channel members - :param channel_ids : list of channel id that has been fetched by current user """ for channel in self: - if not channel.channel_message_ids.ids: + if not channel.message_ids.ids: return if channel.channel_type != 'chat': return - last_message_id = channel.channel_message_ids.ids[0] # zero is the index of the last message + last_message_id = channel.message_ids.ids[0] # zero is the index of the last message channel_partner = self.env['mail.channel.partner'].search([('channel_id', '=', channel.id), ('partner_id', '=', self.env.user.partner_id.id)], limit=1) if channel_partner.fetched_message_id.id == last_message_id: # last message fetched by user is already up-to-date @@ -1202,10 +1197,10 @@ class Channel(models.Model): return [] self.flush() self.env.cr.execute(""" - SELECT mail_channel_id AS id, MAX(mail_message_id) AS message_id - FROM mail_message_mail_channel_rel - WHERE mail_channel_id IN %s - GROUP BY mail_channel_id + SELECT res_id AS id, MAX(id) AS message_id + FROM mail_message + WHERE model = 'mail.channel' AND res_id IN %s + GROUP BY res_id """, (tuple(self.ids),)) return self.env.cr.dictfetchall() @@ -1236,11 +1231,14 @@ class Channel(models.Model): def _send_transient_message(self, partner_to, content): """ Notifies partner_to that a message (not stored in DB) has been written in this channel """ - self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', partner_to.id), { - 'body': "" + content + "", - 'channel_ids': [self.id], - 'info': 'transient_message', - }) + self.env['bus.bus'].sendone( + (self._cr.dbname, 'res.partner', partner_to.id), + {'body': "" + content + "", + 'info': 'transient_message', + 'model': self._name, + 'res_id': self.id, + } + ) def _define_command_help(self): return {'help': _("Show a helper message")} diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index b9dd993437a..a586fd2bc3b 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -138,8 +138,6 @@ class Message(models.Model): has_error = fields.Boolean( 'Has error', compute='_compute_has_error', search='_search_has_error', help='Has error') - channel_ids = fields.Many2many( - 'mail.channel', 'mail_message_mail_channel_rel', string='Channels') # notifications notification_ids = fields.One2many( 'mail.notification', 'mail_message_id', 'Notifications', @@ -292,47 +290,35 @@ class Message(models.Model): return ids pid = self.env.user.partner_id.id - author_ids, partner_ids, channel_ids, allowed_ids = set([]), set([]), set([]), set([]) + author_ids, partner_ids, allowed_ids = set([]), set([]), set([]) model_ids = {} # check read access rights before checking the actual rules on the given ids super(Message, self.with_user(access_rights_uid or self._uid)).check_access_rights('read') - self.flush(['model', 'res_id', 'author_id', 'message_type', 'partner_ids', 'channel_ids']) + self.flush(['model', 'res_id', 'author_id', 'message_type', 'partner_ids']) self.env['mail.notification'].flush(['mail_message_id', 'res_partner_id']) - self.env['mail.channel'].flush(['channel_message_ids']) - self.env['mail.channel.partner'].flush(['channel_id', 'partner_id']) for sub_ids in self._cr.split_for_in_conditions(ids): self._cr.execute(""" SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.message_type, - COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id), - channel_partner.channel_id as channel_id + COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id) FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s LEFT JOIN "mail_notification" needaction_rel ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s - LEFT JOIN "mail_message_mail_channel_rel" channel_rel - ON channel_rel.mail_message_id = m.id - LEFT JOIN "mail_channel" channel - ON channel.id = channel_rel.mail_channel_id - LEFT JOIN "mail_channel_partner" channel_partner - ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s - WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=pid, ids=list(sub_ids))) - for id, rmod, rid, author_id, message_type, partner_id, channel_id in self._cr.fetchall(): + for msg_id, rmod, rid, author_id, message_type, partner_id in self._cr.fetchall(): if author_id == pid: - author_ids.add(id) + author_ids.add(msg_id) elif partner_id == pid: - partner_ids.add(id) - elif channel_id: - channel_ids.add(id) + partner_ids.add(msg_id) elif rmod and rid and message_type != 'user_notification': - model_ids.setdefault(rmod, {}).setdefault(rid, set()).add(id) + model_ids.setdefault(rmod, {}).setdefault(rid, set()).add(msg_id) allowed_ids = self._find_allowed_doc_ids(model_ids) - final_ids = author_ids | partner_ids | channel_ids | allowed_ids + final_ids = author_ids | partner_ids | allowed_ids if count: return len(final_ids) @@ -363,7 +349,6 @@ class Message(models.Model): - author_id == pid, uid is the author OR - uid is in the recipients (partner_ids) OR - uid has been notified (needaction) OR - - uid is member of a listern channel (channel_ids.partner_ids) OR - uid have read access to the related document if model, res_id - otherwise: raise - create: if @@ -417,31 +402,24 @@ class Message(models.Model): # Read mail_message.ids to have their values message_values = dict((message_id, {}) for message_id in self.ids) - self.flush(['model', 'res_id', 'author_id', 'parent_id', 'moderation_status', 'message_type', 'partner_ids', 'channel_ids']) + self.flush(['model', 'res_id', 'author_id', 'parent_id', 'moderation_status', 'message_type', 'partner_ids']) self.env['mail.notification'].flush(['mail_message_id', 'res_partner_id']) - self.env['mail.channel'].flush(['channel_message_ids', 'moderator_ids']) - self.env['mail.channel.partner'].flush(['channel_id', 'partner_id']) + self.env['mail.channel'].flush(['moderator_ids']) self.env['res.users'].flush(['moderation_channel_ids']) if operation == 'read': self._cr.execute(""" SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id), - channel_partner.channel_id as channel_id, m.moderation_status, + m.moderation_status, m.message_type as message_type FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s LEFT JOIN "mail_notification" needaction_rel ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s - LEFT JOIN "mail_message_mail_channel_rel" channel_rel - ON channel_rel.mail_message_id = m.id - LEFT JOIN "mail_channel" channel - ON channel.id = channel_rel.mail_channel_id - LEFT JOIN "mail_channel_partner" channel_partner - ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, ids=self.ids)) - for mid, rmod, rid, author_id, parent_id, partner_id, channel_id, moderation_status, message_type in self._cr.fetchall(): + for mid, rmod, rid, author_id, parent_id, partner_id, moderation_status, message_type in self._cr.fetchall(): message_values[mid] = { 'model': rmod, 'res_id': rid, @@ -449,32 +427,26 @@ class Message(models.Model): 'parent_id': parent_id, 'moderation_status': moderation_status, 'moderator_id': False, - 'notified': any((message_values[mid].get('notified'), partner_id, channel_id)), + 'notified': any((message_values[mid].get('notified'), partner_id)), 'message_type': message_type, } elif operation == 'write': self._cr.execute(""" SELECT DISTINCT m.id, m.model, m.res_id, m.author_id, m.parent_id, m.moderation_status, COALESCE(partner_rel.res_partner_id, needaction_rel.res_partner_id), - channel_partner.channel_id as channel_id, channel_moderator_rel.res_users_id as moderator_id, + channel_moderator_rel.res_users_id as moderator_id, m.message_type as message_type FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = %%(pid)s LEFT JOIN "mail_notification" needaction_rel ON needaction_rel.mail_message_id = m.id AND needaction_rel.res_partner_id = %%(pid)s - LEFT JOIN "mail_message_mail_channel_rel" channel_rel - ON channel_rel.mail_message_id = m.id - LEFT JOIN "mail_channel" channel - ON channel.id = channel_rel.mail_channel_id - LEFT JOIN "mail_channel_partner" channel_partner - ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = %%(pid)s LEFT JOIN "mail_channel" moderated_channel ON m.moderation_status = 'pending_moderation' AND m.res_id = moderated_channel.id LEFT JOIN "mail_channel_moderator_rel" channel_moderator_rel ON channel_moderator_rel.mail_channel_id = moderated_channel.id AND channel_moderator_rel.res_users_id = %%(uid)s WHERE m.id = ANY (%%(ids)s)""" % self._table, dict(pid=self.env.user.partner_id.id, uid=self.env.user.id, ids=self.ids)) - for mid, rmod, rid, author_id, parent_id, moderation_status, partner_id, channel_id, moderator_id, message_type in self._cr.fetchall(): + for mid, rmod, rid, author_id, parent_id, moderation_status, partner_id, moderator_id, message_type in self._cr.fetchall(): message_values[mid] = { 'model': rmod, 'res_id': rid, @@ -482,7 +454,7 @@ class Message(models.Model): 'parent_id': parent_id, 'moderation_status': moderation_status, 'moderator_id': moderator_id, - 'notified': any((message_values[mid].get('notified'), partner_id, channel_id)), + 'notified': any((message_values[mid].get('notified'), partner_id)), 'message_type': message_type, } elif operation == 'create': @@ -583,17 +555,11 @@ class Message(models.Model): # TDE: probably clean me parent_ids = [message.get('parent_id') for message in message_values.values() if message.get('parent_id')] - self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id, channel_partner.partner_id FROM "%s" m + self._cr.execute("""SELECT DISTINCT m.id, partner_rel.res_partner_id FROM "%s" m LEFT JOIN "mail_message_res_partner_rel" partner_rel ON partner_rel.mail_message_id = m.id AND partner_rel.res_partner_id = (%%s) - LEFT JOIN "mail_message_mail_channel_rel" channel_rel - ON channel_rel.mail_message_id = m.id - LEFT JOIN "mail_channel" channel - ON channel.id = channel_rel.mail_channel_id - LEFT JOIN "mail_channel_partner" channel_partner - ON channel_partner.channel_id = channel.id AND channel_partner.partner_id = (%%s) - WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, self.env.user.partner_id.id, parent_ids,)) - not_parent_ids = [mid[0] for mid in self._cr.fetchall() if any([mid[1], mid[2]])] + WHERE m.id = ANY (%%s)""" % self._table, (self.env.user.partner_id.id, parent_ids,)) + not_parent_ids = [mid[0] for mid in self._cr.fetchall() if mid[1]] notified_ids += [mid for mid, message in message_values.items() if message.get('parent_id') in not_parent_ids] @@ -774,30 +740,16 @@ class Message(models.Model): if not notifications: return - # notifies changes in messages through the bus. To minimize the number of - # notifications, we need to group the messages depending on their channel_ids - groups = [] - messages = notifications.mapped('mail_message_id') - current_channel_ids = messages[0].channel_ids - current_group = [] - for record in messages: - if record.channel_ids == current_channel_ids: - current_group.append(record.id) - else: - groups.append((current_group, current_channel_ids)) - current_group = [record.id] - current_channel_ids = record.channel_ids - - groups.append((current_group, current_channel_ids)) - current_group = [record.id] - current_channel_ids = record.channel_ids - notifications.write({'is_read': True}) - for (msg_ids, channel_ids) in groups: - # channel_ids in result is deprecated and will be removed in a future version - notification = {'type': 'mark_as_read', 'message_ids': msg_ids, 'channel_ids': [c.id for c in channel_ids], 'needaction_inbox_counter': self.env.user.partner_id.get_needaction_count()} - self.env['bus.bus'].sendone((self._cr.dbname, 'res.partner', partner_id.id), notification) + # notifies changes in messages through the bus. + self.env['bus.bus'].sendone( + (self._cr.dbname, 'res.partner', partner_id.id), + {'type': 'mark_as_read', + 'message_ids': notifications.mapped('mail_message_id').ids, + 'needaction_inbox_counter': self.env.user.partner_id.get_needaction_count() + } + ) @api.model def unstar_all(self): @@ -1127,7 +1079,6 @@ class Message(models.Model): 'author_id': (3, u'Administrator'), 'email_from': 'sacha@pokemon.com' # email address or False 'subtype_id': (1, u'Discussions'), - 'channel_ids': [], # list of channel ids 'date': '2015-06-30 08:22:33', 'partner_ids': [[7, "Sacha Du Bourg-Palette"]], # list of partner name_get 'message_type': u'comment', @@ -1164,7 +1115,7 @@ class Message(models.Model): 'id', 'body', 'date', 'author_id', 'email_from', # base message fields 'message_type', 'subtype_id', 'subject', # message specific 'model', 'res_id', 'record_name', # document related - 'channel_ids', 'partner_ids', # recipients + 'partner_ids', # recipients 'starred_partner_ids', # list of partner ids for whom the message is starred 'moderation_status', ] diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index 35afe3d737e..43447fa1902 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -160,10 +160,8 @@ class MailThread(models.AbstractModel): 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_message_mail_channel_rel rel - ON rel.mail_message_id = msg.id RIGHT JOIN mail_channel_partner cp - ON (cp.channel_id = rel.mail_channel_id AND cp.partner_id = %s AND + 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 diff --git a/addons/mail/static/src/components/chat_window_manager/chat_window_manager_tests.js b/addons/mail/static/src/components/chat_window_manager/chat_window_manager_tests.js index 0ca449a2828..d336f17235d 100644 --- a/addons/mail/static/src/components/chat_window_manager/chat_window_manager_tests.js +++ b/addons/mail/static/src/components/chat_window_manager/chat_window_manager_tests.js @@ -1013,7 +1013,8 @@ QUnit.test('[technical] chat window: scroll conservation on toggle home menu', a for (let i = 0; i < 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -1784,7 +1785,8 @@ QUnit.test('chat window with a thread: keep scroll position in message list on f for (let i = 0; i < 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -1866,7 +1868,8 @@ QUnit.test('chat window should scroll to the newly posted message just after pos for (let i = 0; i < 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -2023,7 +2026,8 @@ QUnit.test('[technical] chat window with a thread: keep scroll position in messa for (let i = 0; i < 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -2121,7 +2125,7 @@ QUnit.test('chat window does not fetch messages if hidden', async function (asse * 2 visible chat windows + hidden menu: * 10 + 325 + 5 + 325 + 10 + 200 + 5 = 875 < 900 */ - assert.expect(14); + assert.expect(17); // 3 channels are expected to be found in the messaging menu, each with a // random unique id that will be referenced in the test @@ -2153,10 +2157,13 @@ QUnit.test('chat window does not fetch messages if hidden', async function (asse }, mockRPC(route, args) { if (args.method === 'message_fetch') { - // domain should be like [['channel_id', 'in', [X]]] with X the channel id - const channel_ids = args.kwargs.domain[0][2]; - assert.strictEqual(channel_ids.length, 1, "messages should be fetched channel per channel"); - assert.step(`rpc:message_fetch:${channel_ids[0]}`); + // domain should be like [['message_type', '=', 'user_notification'], ['model', '=', 'mail.channel'], ['res_id', '=', X]] with X the channel id + const channel_model = args.kwargs.domain[1][2]; + const channel_id_field = args.kwargs.domain[2][0]; + const channel_id = args.kwargs.domain[2][2]; + assert.strictEqual(channel_model, "mail.channel", "messages should be on channel thread model"); + assert.strictEqual(channel_id_field, "res_id", "messages should be fetched channel per channel using res_id field"); + assert.step(`rpc:message_fetch:${channel_id}`); } return this._super(...arguments); }, @@ -2243,7 +2250,6 @@ QUnit.test('new message separator is shown in a chat window of a chat on receivi ]; this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [10], model: 'mail.channel', res_id: 10, }); @@ -2348,7 +2354,6 @@ QUnit.test('focusing a chat window of a chat should make new message separator d ); this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [10], model: 'mail.channel', res_id: 10, }); diff --git a/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js b/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js index 26431207467..92c2280a245 100644 --- a/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js +++ b/addons/mail/static/src/components/discuss/tests/discuss_domain_tests.js @@ -82,10 +82,12 @@ QUnit.test('discuss should keep filter domain on changing thread', async functio this.data['mail.channel'].records.push({ id: 20 }); this.data['mail.message'].records.push({ body: "test", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }, { body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); await this.start(); const channel = this.env.models['mail.thread'].findFromIdentifyingData({ diff --git a/addons/mail/static/src/components/discuss/tests/discuss_inbox_tests.js b/addons/mail/static/src/components/discuss/tests/discuss_inbox_tests.js index 63b524eb81b..2ebbd13b53b 100644 --- a/addons/mail/static/src/components/discuss/tests/discuss_inbox_tests.js +++ b/addons/mail/static/src/components/discuss/tests/discuss_inbox_tests.js @@ -555,7 +555,6 @@ QUnit.test('subject should not be shown when subject is the same as the thread n this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -579,7 +578,6 @@ QUnit.test('subject should not be shown when subject is the same as the thread n this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -603,7 +601,6 @@ QUnit.test('subject should not be shown when subject differs from thread name on this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -627,7 +624,6 @@ QUnit.test('subject should not be shown when subject differs from thread name on this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -651,7 +647,6 @@ QUnit.test('subject should be shown when the thread name has an extra prefix com this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -675,7 +670,6 @@ QUnit.test('subject should not be shown when subject differs from thread name on this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, @@ -699,7 +693,6 @@ QUnit.test('subject should not be shown when subject differs from thread name on this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, needaction: true, diff --git a/addons/mail/static/src/components/discuss/tests/discuss_moderation_tests.js b/addons/mail/static/src/components/discuss/tests/discuss_moderation_tests.js index c4c53cb568e..fc29d5ff331 100644 --- a/addons/mail/static/src/components/discuss/tests/discuss_moderation_tests.js +++ b/addons/mail/static/src/components/discuss/tests/discuss_moderation_tests.js @@ -988,7 +988,6 @@ QUnit.test('as moderator, new pending moderation message posted by someone else' const messageData = { author_id: [10, 'john doe'], // random id, different than current partner body: "not empty", - channel_ids: [], // server do NOT return channel_id of the message if pending moderation id: 1, // random unique id model: 'mail.channel', // expected value to link message to channel moderation_status: 'pending_moderation', // message is expected to be pending diff --git a/addons/mail/static/src/components/discuss/tests/discuss_tests.js b/addons/mail/static/src/components/discuss/tests/discuss_tests.js index dc2005e5ac7..2a02984bb61 100644 --- a/addons/mail/static/src/components/discuss/tests/discuss_tests.js +++ b/addons/mail/static/src/components/discuss/tests/discuss_tests.js @@ -520,8 +520,9 @@ QUnit.test('sidebar: channel rendering with needaction counter', async function // expected needaction message this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], // link message to channel id: 100, // random unique id, useful to link notification + model: "mail.channel", + res_id: 20, }); // expected needaction notification this.data['mail.notification'].records.push({ @@ -1194,7 +1195,6 @@ QUnit.test('load single message from channel initially', async function (assert) this.data['mail.channel'].records.push({ id: 20 }); this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], date: "2019-04-20 10:00:00", id: 100, model: 'mail.channel', @@ -1215,7 +1215,11 @@ QUnit.test('load single message from channel initially', async function (assert) ); assert.deepEqual( args.kwargs.domain, - [["channel_ids", "in", [20]]], + [ + ["message_type", "!=", "user_notification"], + ["model", "=", "mail.channel"], + ["res_id", "=", 20] + ], "should fetch messages from channel" ); } @@ -1288,7 +1292,6 @@ QUnit.test('basic rendering of message', async function (assert) { this.data['mail.message'].records.push({ author_id: 11, body: "

body

", - channel_ids: [20], date: "2019-04-20 10:00:00", id: 100, model: 'mail.channel', @@ -1390,7 +1393,6 @@ QUnit.test('basic rendering of squashed message', async function (assert) { { author_id: 11, // must be same author as other message body: "

body1

", // random body, set for consistency - channel_ids: [20], // to link message to channel date: "2019-04-20 10:00:00", // date must be within 1 min from other message id: 100, // random unique id, will be referenced in the test message_type: 'comment', // must be a squash-able type- @@ -1400,7 +1402,6 @@ QUnit.test('basic rendering of squashed message', async function (assert) { { author_id: 11, // must be same author as other message body: "

body2

", // random body, will be asserted in the test - channel_ids: [20], // to link message to channel date: "2019-04-20 10:00:30", // date must be within 1 min from other message id: 101, // random unique id, will be referenced in the test message_type: 'comment', // must be a squash-able type @@ -1498,7 +1499,6 @@ QUnit.test('inbox messages are never squashed', async function (assert) { { author_id: 11, // must be same author as other message body: "

body1

", // random body, set for consistency - channel_ids: [20], // to link message to channel date: "2019-04-20 10:00:00", // date must be within 1 min from other message id: 100, // random unique id, will be referenced in the test message_type: 'comment', // must be a squash-able type- @@ -1510,7 +1510,6 @@ QUnit.test('inbox messages are never squashed', async function (assert) { { author_id: 11, // must be same author as other message body: "

body2

", // random body, will be asserted in the test - channel_ids: [20], // to link message to channel date: "2019-04-20 10:00:30", // date must be within 1 min from other message id: 101, // random unique id, will be referenced in the test message_type: 'comment', // must be a squash-able type @@ -1563,7 +1562,6 @@ QUnit.test('load all messages from channel initially, less than fetch limit (29 for (let i = 28; i >= 0; i--) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], date: "2019-04-20 10:00:00", model: 'mail.channel', res_id: 20, @@ -1623,7 +1621,6 @@ QUnit.test('load more messages from channel', async function (assert) { for (let i = 0; i < 40; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], date: "2019-04-20 10:00:00", model: 'mail.channel', res_id: 20, @@ -1695,7 +1692,6 @@ QUnit.test('auto-scroll to bottom of thread', async function (assert) { for (let i = 1; i <= 25; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], model: 'mail.channel', res_id: 20, }); @@ -1743,7 +1739,6 @@ QUnit.test('load more messages from channel (auto-load on scroll)', async functi for (let i = 0; i < 40; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], model: 'mail.channel', res_id: 20, }); @@ -1831,7 +1826,6 @@ QUnit.test('new messages separator [REQUIRE FOCUS]', async function (assert) { for (let i = 1; i <= 25; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], id: 100 + i, // for setting proper value for seen_message_id model: 'mail.channel', res_id: 20, @@ -1956,7 +1950,6 @@ QUnit.test('restore thread scroll position', async function (assert) { for (let i = 1; i <= 25; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [11], model: 'mail.channel', res_id: 11, }); @@ -1964,7 +1957,6 @@ QUnit.test('restore thread scroll position', async function (assert) { for (let i = 1; i <= 24; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [12], model: 'mail.channel', res_id: 12, }); @@ -2114,200 +2106,6 @@ QUnit.test('restore thread scroll position', async function (assert) { ); }); -QUnit.test('message origin redirect to channel', async function (assert) { - assert.expect(15); - - // channels expected to be rendered, with random unique id that will be referenced in the test - this.data['mail.channel'].records.push({ id: 11 }, { id: 12 }); - this.data['mail.message'].records.push( - { - body: "not empty", - channel_ids: [11, 12], - id: 100, - model: 'mail.channel', - record_name: "channel11", - res_id: 11, - }, - { - body: "not empty", - channel_ids: [11, 12], - id: 101, - model: 'mail.channel', - record_name: "channel12", - res_id: 12, - } - ); - await this.start({ - discuss: { - params: { - default_active_id: 'mail.channel_11', - }, - }, - }); - assert.strictEqual( - document.querySelectorAll('.o_Discuss_thread .o_Message').length, - 2, - "should have 2 messages" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - `).length, - 1, - "should have message1 (Id 100)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - `).length, - 1, - "should have message2 (Id 101)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - .o_Message_originThread - `).length, - 0, - "message1 should not have origin part in channel11 (same origin as channel)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - .o_Message_originThread - `).length, - 1, - "message2 should have origin part (origin is channel12 !== channel11)" - ); - assert.strictEqual( - document.querySelector(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - .o_Message_originThread - `).textContent.trim(), - "(from #channel12)", - "message2 should display name of origin channel" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - .o_Message_originThreadLink - `).length, - 1, - "message2 should have link to redirect to origin" - ); - - // click on origin link of message2 (= channel12) - await afterNextRender(() => - document.querySelector(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - .o_Message_originThreadLink - `).click() - ); - assert.ok( - document.querySelector(` - .o_DiscussSidebar_groupChannel - .o_DiscussSidebar_item[data-thread-local-id="${ - this.env.models['mail.thread'].findFromIdentifyingData({ - id: 12, - model: 'mail.channel', - }).localId - }"] - .o_DiscussSidebarItem_activeIndicator - `).classList.contains('o-item-active'), - "channel12 should be active channel on redirect from discuss app" - ); - assert.strictEqual( - document.querySelectorAll(`.o_Discuss_thread .o_Message`).length, - 2, - "should have 2 messages" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - `).length, - 1, - "should have message1 (Id 100)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - `).length, - 1, - "should have message2 (Id 101)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - .o_Message_originThread - `).length, - 1, - "message1 should have origin thread part (= channel11 !== channel12)" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 101 }).localId - }"] - .o_Message_originThread - `).length, - 0, - "message2 should not have origin thread part in channel12 (same as current channel)" - ); - assert.strictEqual( - document.querySelector(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - .o_Message_originThread - `).textContent.trim(), - "(from #channel11)", - "message1 should display name of origin channel" - ); - assert.strictEqual( - document.querySelectorAll(` - .o_Discuss_thread - .o_Message[data-message-local-id="${ - this.env.models['mail.message'].findFromIdentifyingData({ id: 100 }).localId - }"] - .o_Message_originThreadLink - `).length, - 1, - "message1 should have link to redirect to origin channel" - ); -}); - QUnit.test('redirect to author (open chat)', async function (assert) { assert.expect(7); @@ -2333,7 +2131,6 @@ QUnit.test('redirect to author (open chat)', async function (assert) { { author_id: 7, body: "not empty", - channel_ids: [1], id: 100, model: 'mail.channel', res_id: 1, @@ -2572,7 +2369,6 @@ QUnit.test('inbox: mark all messages as read', async function (assert) { // first expected message { body: "not empty", - channel_ids: [20], // link message to channel id: 100, // random unique id, useful to link notification model: 'mail.channel', // needaction needs to be set here for message_fetch domain, because @@ -2583,7 +2379,6 @@ QUnit.test('inbox: mark all messages as read', async function (assert) { // second expected message { body: "not empty", - channel_ids: [20], // link message to channel id: 101, // random unique id, useful to link notification model: 'mail.channel', // needaction needs to be set here for message_fetch domain, because @@ -2742,7 +2537,6 @@ QUnit.test('toggle_star message', async function (assert) { this.data['mail.channel'].records.push({ id: 20 }); this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], id: 100, model: 'mail.channel', res_id: 20, @@ -3326,7 +3120,6 @@ QUnit.test('mark channel as seen on last message visible [REQUIRE FOCUS]', async this.data['mail.message'].records.push({ id: 12, body: "not empty", - channel_ids: [10], model: 'mail.channel', res_id: 10, }); @@ -3645,7 +3438,6 @@ QUnit.test('load recent messages from thread (already loaded some old messages)' for (let i = 0; i < 50; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], // id of related channel id: 100 + i, // random unique id, will be referenced in the test model: 'mail.channel', // expected value to link message to channel // needaction needs to be set here for message_fetch domain, because @@ -4105,7 +3897,6 @@ QUnit.test('receive new chat message: out of odoo focus (notification, channel)' // simulate receiving a new message with odoo focused await afterNextRender(() => { const messageData = { - channel_ids: [20], id: 126, model: 'mail.channel', res_id: 20, @@ -4144,7 +3935,6 @@ QUnit.test('receive new chat message: out of odoo focus (notification, chat)', a // simulate receiving a new message with odoo focused await afterNextRender(() => { const messageData = { - channel_ids: [10], id: 126, model: 'mail.channel', res_id: 10, @@ -4196,7 +3986,6 @@ QUnit.test('receive new chat messages: out of odoo focus (tab title)', async fun // simulate receiving a new message in chat 20 with odoo focused await afterNextRender(() => { const messageData1 = { - channel_ids: [20], id: 126, model: 'mail.channel', res_id: 20, @@ -4209,7 +3998,6 @@ QUnit.test('receive new chat messages: out of odoo focus (tab title)', async fun // simulate receiving a new message in chat 10 with odoo focused await afterNextRender(() => { const messageData2 = { - channel_ids: [10], id: 127, model: 'mail.channel', res_id: 10, @@ -4222,7 +4010,6 @@ QUnit.test('receive new chat messages: out of odoo focus (tab title)', async fun // simulate receiving another new message in chat 10 with odoo focused await afterNextRender(() => { const messageData3 = { - channel_ids: [10], id: 128, model: 'mail.channel', res_id: 10, @@ -4355,12 +4142,14 @@ QUnit.test('mark channel as seen if last message is visible when switching chann ); this.data['mail.message'].records.push({ body: 'oldest message', - channel_ids: [10], id: 10, + model: "mail.channel", + res_id: 10, }, { body: 'newest message', - channel_ids: [11], id: 11, + model: "mail.channel", + res_id: 11, }); await this.start({ discuss: { diff --git a/addons/mail/static/src/components/messaging_menu/messaging_menu_tests.js b/addons/mail/static/src/components/messaging_menu/messaging_menu_tests.js index d049ab7a5b3..af66bbba236 100644 --- a/addons/mail/static/src/components/messaging_menu/messaging_menu_tests.js +++ b/addons/mail/static/src/components/messaging_menu/messaging_menu_tests.js @@ -504,7 +504,6 @@ QUnit.test('channel preview: basic rendering', async function (assert) { this.data['mail.message'].records.push({ author_id: 7, // not current partner, will be asserted in the test body: "

test

", // random body, will be asserted in the test - channel_ids: [20], // id of related channel model: 'mail.channel', // necessary to link message to channel res_id: 20, // id of related channel }); @@ -597,12 +596,10 @@ QUnit.test('filtered previews', async function (assert) { ); this.data['mail.message'].records.push( { - channel_ids: [10], // id of related channel model: 'mail.channel', // to link message to channel res_id: 10, // id of related channel }, { - channel_ids: [20], // id of related channel model: 'mail.channel', // to link message to channel res_id: 20, // id of related channel }, @@ -781,7 +778,8 @@ QUnit.test('no code injection in message body preview', async function (assert) this.data['mail.channel'].records.push({ id: 11 }); this.data['mail.message'].records.push({ body: "

&shoulnotberaised

", - channel_ids: [11], + model: "mail.channel", + res_id: 11, }); await this.start(); @@ -822,7 +820,8 @@ QUnit.test('no code injection in message body preview from sanitized message', a this.data['mail.channel'].records.push({ id: 11 }); this.data['mail.message'].records.push({ body: "

<em>&shoulnotberaised</em><script>throw new Error('CodeInjectionError');</script>

", - channel_ids: [11], + model: "mail.channel", + res_id: 11, }); await this.start(); @@ -863,7 +862,8 @@ QUnit.test('
tags in message body preview are transformed in spaces', async this.data['mail.channel'].records.push({ id: 11 }); this.data['mail.message'].records.push({ body: "

a
b
c
d

", - channel_ids: [11], + model: "mail.channel", + res_id: 11, }); await this.start(); diff --git a/addons/mail/static/src/components/notification_list/notification_list_tests.js b/addons/mail/static/src/components/notification_list/notification_list_tests.js index 24df5b22cdf..3cf756d4fe2 100644 --- a/addons/mail/static/src/components/notification_list/notification_list_tests.js +++ b/addons/mail/static/src/components/notification_list/notification_list_tests.js @@ -53,14 +53,12 @@ QUnit.test('marked as read thread notifications are ordered by last message date ); this.data['mail.message'].records.push( { - channel_ids: [100], date: "2019-01-01 00:00:00", id: 42, model: 'mail.channel', res_id: 100, }, { - channel_ids: [200], date: "2020-01-01 00:00:00", id: 43, model: 'mail.channel', @@ -97,14 +95,12 @@ QUnit.test('thread notifications are re-ordered on receiving a new message', asy ); this.data['mail.message'].records.push( { - channel_ids: [100], date: "2019-01-01 00:00:00", id: 42, model: 'mail.channel', res_id: 100, }, { - channel_ids: [200], date: "2020-01-01 00:00:00", id: 43, model: 'mail.channel', @@ -124,7 +120,6 @@ QUnit.test('thread notifications are re-ordered on receiving a new message', asy const messageData = { author_id: [7, "Demo User"], body: "

New message !

", - channel_ids: [100], date: "2020-03-23 10:00:00", id: 44, message_type: 'comment', diff --git a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js index ca1fe22c785..3051f318ea9 100644 --- a/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js +++ b/addons/mail/static/src/components/thread_needaction_preview/thread_needaction_preview_tests.js @@ -361,49 +361,6 @@ QUnit.test('preview should display last needaction message preview even if there ); }); -QUnit.test('needaction preview should only show on its origin thread', async function (assert) { - assert.expect(2); - - this.data['mail.channel'].records.push({ id: 12 }); - this.data['mail.message'].records.push({ - channel_ids: [12], - id: 21, - model: 'res.partner', - needaction: true, - needaction_partner_ids: [this.data.currentPartnerId], - res_id: 11, - }); - this.data['mail.notification'].records.push({ - mail_message_id: 21, - notification_status: 'sent', - notification_type: 'inbox', - res_partner_id: this.data.currentPartnerId, - }); - await this.start({ hasMessagingMenu: true }); - await afterNextRender(() => this.afterEvent({ - eventName: 'o-thread-cache-loaded-messages', - func: () => document.querySelector('.o_MessagingMenu_toggler').click(), - message: "should wait until inbox loaded initial needaction messages", - predicate: ({ threadCache }) => { - return threadCache.thread.model === 'mail.box' && threadCache.thread.id === 'inbox'; - }, - })); - assert.containsOnce( - document.body, - '.o_ThreadNeedactionPreview', - "should have only one preview" - ); - const thread = this.env.models['mail.thread'].findFromIdentifyingData({ - id: 11, - model: 'res.partner', - }); - assert.containsOnce( - document.body, - `.o_ThreadNeedactionPreview[data-thread-local-id="${thread.localId}"]`, - "preview should be on the origin thread" - ); -}); - QUnit.test('chat window header should not have unread counter for non-channel thread', async function (assert) { assert.expect(2); diff --git a/addons/mail/static/src/components/thread_preview/thread_preview_tests.js b/addons/mail/static/src/components/thread_preview/thread_preview_tests.js index 981abf6bca6..bc33f7cd0f8 100644 --- a/addons/mail/static/src/components/thread_preview/thread_preview_tests.js +++ b/addons/mail/static/src/components/thread_preview/thread_preview_tests.js @@ -47,7 +47,6 @@ QUnit.test('mark as read', async function (assert) { message_unread_counter: 1, }); this.data['mail.message'].records.push({ - channel_ids: [11], id: 100, model: 'mail.channel', res_id: 11, diff --git a/addons/mail/static/src/components/thread_view/thread_view_tests.js b/addons/mail/static/src/components/thread_view/thread_view_tests.js index 87d33c89548..a3e1f15eb1a 100644 --- a/addons/mail/static/src/components/thread_view/thread_view_tests.js +++ b/addons/mail/static/src/components/thread_view/thread_view_tests.js @@ -106,7 +106,6 @@ QUnit.test('message list desc order', async function (assert) { for (let i = 0; i <= 60; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, }); @@ -204,7 +203,6 @@ QUnit.test('message list asc order', async function (assert) { for (let i = 0; i <= 60; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, }); @@ -466,7 +464,6 @@ QUnit.test('show message subject if thread is mailing channel', async function ( }); this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [100], model: 'mail.channel', res_id: 100, subject: "Salutations, voyageur", @@ -513,8 +510,9 @@ QUnit.test('[technical] new messages separator on posting message', async functi }]; this.data['mail.message'].records.push({ body: "first message", - channel_ids: [20], id: 10, + model: "mail.channel", + res_id: 20, }); await this.start(); const thread = this.env.models['mail.thread'].findFromIdentifyingData({ @@ -582,8 +580,9 @@ QUnit.test('new messages separator on receiving new message [REQUIRE FOCUS]', as }); this.data['mail.message'].records.push({ body: "blah", - channel_ids: [20], id: 1, + model: "mail.channel", + res_id: 20, }); await this.start(); const thread = this.env.models['mail.thread'].findFromIdentifyingData({ @@ -724,7 +723,6 @@ QUnit.test('basic rendering of canceled notification', async function (assert) { this.data['res.partner'].records.push({ id: 12, name: "Someone" }); this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [11], id: 10, message_type: 'email', model: 'mail.channel', @@ -825,7 +823,8 @@ QUnit.test('should scroll to bottom on receiving new message if the list is init for (let i = 0; i <= 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -896,7 +895,8 @@ QUnit.test('should not scroll on receiving new message if the list is initially for (let i = 0; i <= 10; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [20], + model: "mail.channel", + res_id: 20, }); } await this.start(); @@ -973,8 +973,9 @@ QUnit.test("delete all attachments of message without content should no longer d this.data['mail.message'].records.push( { attachment_ids: [143], - channel_ids: [11], id: 101, + model: "mail.channel", + res_id: 11, } ); await this.start(); @@ -1031,8 +1032,9 @@ QUnit.test('delete all attachments of a message with some text content should st { attachment_ids: [143], body: "Some content", - channel_ids: [11], id: 101, + model: "mail.channel", + res_id: 11, }, ); await this.start(); @@ -1088,8 +1090,9 @@ QUnit.test('delete all attachments of a message with tracking fields should stil this.data['mail.message'].records.push( { attachment_ids: [143], - channel_ids: [11], id: 101, + model: "mail.channel", + res_id: 11, tracking_value_ids: [6] }, ); @@ -1573,8 +1576,9 @@ QUnit.test('show empty placeholder when thread contains only empty messages', as this.data['mail.channel'].records.push({ id: 11 }); this.data['mail.message'].records.push( { - channel_ids: [11], id: 101, + model: "mail.channel", + res_id: 11, }, ); await this.start(); @@ -1621,8 +1625,9 @@ QUnit.test('message with subtype should be displayed (and not considered as empt }); this.data['mail.message'].records.push( { - channel_ids: [11], id: 101, + model: "mail.channel", + res_id: 11, subtype_id: 10, }, ); @@ -1673,12 +1678,14 @@ QUnit.test('[technical] message list with a full page of empty messages should s for (let i = 0; i <= 30; i++) { this.data['mail.message'].records.push({ body: "not empty", - channel_ids: [11], + model: "mail.channel", + res_id: 11, }); } for (let i = 0; i <= 30; i++) { this.data['mail.message'].records.push({ - channel_ids: [11], + model: "mail.channel", + res_id: 11, }); } await this.start(); @@ -1900,8 +1907,9 @@ QUnit.test('failure on loading more messages should not alter message list displ this.data['mail.message'].records.push(...[...Array(60).keys()].map(id => { return { body: 'coucou', - channel_ids: [20], id, + model: "mail.channel", + res_id: 20, }; })); await this.start({ @@ -1950,8 +1958,9 @@ QUnit.test('failure on loading more messages should display error and prompt ret this.data['mail.message'].records.push(...[...Array(60).keys()].map(id => { return { body: 'coucou', - channel_ids: [20], id, + model: "mail.channel", + res_id: 20, }; })); await this.start({ @@ -2009,8 +2018,9 @@ QUnit.test('Retry loading more messages on failed load more messages should load this.data['mail.message'].records = [...Array(90).keys()].map(id => { return { body: 'coucou', - channel_ids: [20], id, + model: "mail.channel", + res_id: 20, }; }); await this.start({ diff --git a/addons/mail/static/src/models/composer/composer.js b/addons/mail/static/src/models/composer/composer.js index 2c0813ec50c..301ec2c9967 100644 --- a/addons/mail/static/src/models/composer/composer.js +++ b/addons/mail/static/src/models/composer/composer.js @@ -292,7 +292,6 @@ function factory(dependencies) { let postData = { attachment_ids: this.attachments.map(attachment => attachment.id), body, - channel_ids: this.mentionedChannels.map(channel => channel.id), message_type: 'comment', partner_ids: this.recipients.map(partner => partner.id), }; diff --git a/addons/mail/static/src/models/message/message.js b/addons/mail/static/src/models/message/message.js index a3cf9f20d24..7c0fdb8cb18 100644 --- a/addons/mail/static/src/models/message/message.js +++ b/addons/mail/static/src/models/message/message.js @@ -63,16 +63,6 @@ function factory(dependencies) { if ('body' in data) { data2.body = data.body; } - if ('channel_ids' in data && data.channel_ids) { - const channels = data.channel_ids - .map(channelId => - this.env.models['mail.thread'].findFromIdentifyingData({ - id: channelId, - model: 'mail.channel', - }) - ).filter(channel => !!channel); - data2.serverChannels = [['replace', channels]]; - } if ('date' in data && data.date) { data2.date = moment(str_to_datetime(data.date)); } @@ -545,7 +535,7 @@ function factory(dependencies) { * @returns {mail.thread[]} */ _computeThreads() { - const threads = [...this.serverChannels]; + const threads = []; if (this.isHistory) { threads.push(this.env.messaging.history); } @@ -794,20 +784,12 @@ function factory(dependencies) { 'messagingModeration', 'messagingStarred', 'originThread', - 'serverChannels', ], inverse: 'messages', }), tracking_value_ids: attr({ default: [], }), - /** - * All channels containing this message on the server. - * Equivalent of python field `channel_ids`. - */ - serverChannels: many2many('mail.thread', { - inverse: 'messagesAsServerChannel', - }), }; Message.modelName = 'mail.message'; diff --git a/addons/mail/static/src/models/messaging_notification_handler/messaging_notification_handler.js b/addons/mail/static/src/models/messaging_notification_handler/messaging_notification_handler.js index dcd8b09f688..0614984b974 100644 --- a/addons/mail/static/src/models/messaging_notification_handler/messaging_notification_handler.js +++ b/addons/mail/static/src/models/messaging_notification_handler/messaging_notification_handler.js @@ -540,11 +540,10 @@ function factory(dependencies) { /** * @private * @param {Object} param0 - * @param {integer[]} [param0.channel_ids * @param {integer[]} [param0.message_ids=[]] * @param {integer} [param0.needaction_inbox_counter] */ - _handleNotificationPartnerMarkAsRead({ channel_ids, message_ids = [], needaction_inbox_counter }) { + _handleNotificationPartnerMarkAsRead({ message_ids = [], needaction_inbox_counter }) { for (const message_id of message_ids) { // We need to ignore all not yet known messages because we don't want them // to be shown partially as they would be linked directly to mainCache diff --git a/addons/mail/static/src/models/thread/thread.js b/addons/mail/static/src/models/thread/thread.js index 6b2c68f06ec..b02f4a2739e 100644 --- a/addons/mail/static/src/models/thread/thread.js +++ b/addons/mail/static/src/models/thread/thread.js @@ -139,9 +139,7 @@ function factory(dependencies) { * @return {Object} */ static convertData(data) { - const data2 = { - messagesAsServerChannel: [], - }; + const data2 = {}; if ('model' in data) { data2.model = data.model; } @@ -171,12 +169,20 @@ function factory(dependencies) { data2.isServerPinned = data.is_pinned; } if ('last_message' in data && data.last_message) { - data2.messagesAsServerChannel.push(['insert', { id: data.last_message.id }]); - data2.serverLastMessageId = data.last_message.id; + const messageData = this.env.models['mail.message'].convertData({ + id: data.last_message.id, + model: data2.model, + res_id: data2.id, + }); + data2.serverLastMessage = [['insert', messageData]]; } if ('last_message_id' in data && data.last_message_id) { - data2.messagesAsServerChannel.push(['insert', { id: data.last_message_id }]); - data2.serverLastMessageId = data.last_message_id; + const messageData = this.env.models['mail.message'].convertData({ + id: data.last_message_id, + model: data2.model, + res_id: data2.id, + }); + data2.serverLastMessage = [['insert', messageData]]; } if ('mass_mailing' in data) { data2.mass_mailing = data.mass_mailing; @@ -1247,7 +1253,7 @@ function factory(dependencies) { // By default trust the server up to the last message it used // because it's not possible to do better. let baseCounter = this.serverMessageUnreadCounter; - let countFromId = this.serverLastMessageId; + let countFromId = this.serverLastMessage ? this.serverLastMessage.id : 0; // But if the client knows the last seen message that the server // returned (and by assumption all the messages that come after), // the counter can be computed fully locally, ignoring potentially @@ -1843,7 +1849,7 @@ function factory(dependencies) { 'lastSeenByCurrentPartnerMessageId', 'messagingCurrentPartner', 'orderedMessages', - 'serverLastMessageId', + 'serverLastMessage', 'serverMessageUnreadCounter', ], }), @@ -1893,13 +1899,6 @@ function factory(dependencies) { messagesAsOriginThreadIsNeedaction: attr({ related: 'messagesAsOriginThread.isNeedaction', }), - /** - * All messages that are contained on this channel on the server. - * Equivalent to the inverse of python field `channel_ids`. - */ - messagesAsServerChannel: many2many('mail.message', { - inverse: 'serverChannels', - }), /** * Contains the message fetched/seen indicators for all messages of this thread. * FIXME This field should be readonly once task-2336946 is done. @@ -2077,15 +2076,13 @@ function factory(dependencies) { default: 'closed', }), /** - * Last message id considered by the server. + * Last message considered by the server. * * Useful to compute localMessageUnreadCounter field. * * @see localMessageUnreadCounter */ - serverLastMessageId: attr({ - default: 0, - }), + serverLastMessage: many2one('mail.message'), /** * Message unread counter coming from server. * diff --git a/addons/mail/static/src/models/thread_cache/thread_cache.js b/addons/mail/static/src/models/thread_cache/thread_cache.js index f97222307da..da0752f5f46 100644 --- a/addons/mail/static/src/models/thread_cache/thread_cache.js +++ b/addons/mail/static/src/models/thread_cache/thread_cache.js @@ -253,9 +253,7 @@ function factory(dependencies) { */ _extendMessageDomain(domain) { const thread = this.thread; - if (thread.model === 'mail.channel') { - return domain.concat([['channel_ids', 'in', [thread.id]]]); - } else if (thread === this.env.messaging.inbox) { + if (thread === this.env.messaging.inbox) { return domain.concat([['needaction', '=', true]]); } else if (thread === this.env.messaging.starred) { return domain.concat([ diff --git a/addons/mail/static/tests/helpers/mock_models.js b/addons/mail/static/tests/helpers/mock_models.js index 70da8ce0393..bd1a06b6ad9 100644 --- a/addons/mail/static/tests/helpers/mock_models.js +++ b/addons/mail/static/tests/helpers/mock_models.js @@ -125,7 +125,6 @@ class MockModels { attachment_ids: { string: "Attachments", type: 'many2many', relation: 'ir.attachment', default: [] }, author_id: { string: "Author", type: 'many2one', relation: 'res.partner', default() { return this.currentPartnerId; } }, body: { string: "Contents", type: 'html', default: "

" }, - channel_ids: { string: "Channels", type: 'many2many', relation: 'mail.channel' }, date: { string: "Date", type: 'datetime' }, email_from: { string: "From", type: 'char' }, history_partner_ids: { string: "Partners with History", type: 'many2many', relation: 'res.partner' }, diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 1ce1b3c8766..c358a548e7a 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -598,7 +598,10 @@ MockServer.include({ _mockMailChannelChannelFetched(ids) { const channels = this._getRecords('mail.channel', [['id', 'in', ids]]); for (const channel of channels) { - const channelMessages = this._getRecords('mail.message', [['channel_ids', 'in', channel.id]]); + const channelMessages = this._getRecords('mail.message', [ + ['model', '=', 'mail.channel'], + ['res_id', '=', channel.id], + ]); const lastMessage = channelMessages.reduce((lastMessage, message) => { if (message.id > lastMessage.id) { return message; @@ -634,7 +637,10 @@ MockServer.include({ _mockMailChannelChannelFetchPreview(ids) { const channels = this._getRecords('mail.channel', [['id', 'in', ids]]); return channels.map(channel => { - const channelMessages = this._getRecords('mail.message', [['channel_ids', 'in', channel.id]]); + const channelMessages = this._getRecords('mail.message', [ + ['model', '=', 'mail.channel'], + ['res_id', '=', channel.id], + ]); const lastMessage = channelMessages.reduce((lastMessage, message) => { if (message.id > lastMessage.id) { return message; @@ -727,7 +733,8 @@ MockServer.include({ return channels.map(channel => { const members = channel.members.map(partnerId => partnerInfos[partnerId]); const messages = this._getRecords('mail.message', [ - ['channel_ids', 'in', [channel.id]], + ['model', '=', 'mail.channel'], + ['res_id', '=', channel.id], ]); const lastMessageId = messages.reduce((lastMessageId, message) => { if (!lastMessageId || message.id > lastMessageId) { @@ -804,8 +811,9 @@ MockServer.include({ } const channel = this._getRecords('mail.channel', [['id', '=', channel_id]])[0]; const messagesBeforeGivenLastMessage = this._getRecords('mail.message', [ - ['channel_ids', 'in', [channel.id]], ['id', '<=', last_message_id], + ['model', '=', 'mail.channel'], + ['res_id', '=', channel.id], ]); if (!messagesBeforeGivenLastMessage || messagesBeforeGivenLastMessage.length === 0) { return; @@ -880,8 +888,9 @@ MockServer.include({ ["dbName", 'res.partner', this.currentPartnerId], { 'body': `${message}`, - 'channel_ids': [channel.id], 'info': 'transient_message', + 'model': 'mail.channel', + 'res_id': channel.id, } ]; this._widget.call('bus_service', 'trigger', 'notification', [notification]); @@ -1267,7 +1276,7 @@ MockServer.include({ this._mockWrite('mail.message', [[message.id], { moderation_status: 'accepted', }]); - this._mockMailThread_NotifyThread(model, message.channel_ids, message.id); + this._mockMailThread_NotifyThread(model, [message.res_id], message.id); } } }, @@ -1333,9 +1342,7 @@ MockServer.include({ ), }, ]); - // NOTE server is sending grouped notifications per channel_ids but - // this optimization is not needed here. - const data = { type: 'mark_as_read', message_ids: [message.id], channel_ids: message.channel_ids, needaction_inbox_counter: this._mockResPartnerGetNeedactionCount() }; + const data = { type: 'mark_as_read', message_ids: [message.id], needaction_inbox_counter: this._mockResPartnerGetNeedactionCount() }; const busNotifications = [[[false, 'res.partner', this.currentPartnerId], data]]; this._widget.call('bus_service', 'trigger', 'notification', busNotifications); } @@ -1636,7 +1643,7 @@ MockServer.include({ notifications.push([[false, 'res.partner', message.author_id], notificationData]); } // members - const channels = this._getRecords('mail.channel', [['id', 'in', message.channel_ids]]); + const channels = this._getRecords('mail.channel', [['id', '=', message.res_id]]); for (const channel of channels) { notifications.push([[false, 'mail.channel', channel.id], messageFormat]); } diff --git a/addons/mail/tests/test_mail_channel.py b/addons/mail/tests/test_mail_channel.py index e36272c1a50..2c3b138e965 100644 --- a/addons/mail/tests/test_mail_channel.py +++ b/addons/mail/tests/test_mail_channel.py @@ -206,7 +206,8 @@ class TestChannelInternals(MailCommon): with self.mock_mail_gateway(): new_msg = channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment') self.assertNotSentEmail() - self.assertEqual(new_msg.channel_ids, channel) + self.assertEqual(new_msg.model, self.test_channel._name) + self.assertEqual(new_msg.res_id, self.test_channel.id) self.assertEqual(new_msg.partner_ids, self.env['res.partner']) self.assertEqual(new_msg.notified_partner_ids, self.env['res.partner']) @@ -223,7 +224,8 @@ class TestChannelInternals(MailCommon): channel = self.env['mail.channel'].browse(self.test_channel.ids) new_msg = channel.message_post(body="Test", message_type='comment', subtype_xmlid='mail.mt_comment') self.assertNotSentEmail() - self.assertEqual(new_msg.channel_ids, self.test_channel) + self.assertEqual(new_msg.model, self.test_channel._name) + self.assertEqual(new_msg.res_id, self.test_channel.id) self.assertEqual(new_msg.partner_ids, self.env['res.partner']) self.assertEqual(new_msg.notified_partner_ids, self.env['res.partner']) @@ -320,12 +322,8 @@ class TestChannelInternals(MailCommon): channel.write({'channel_type': 'chat'}) channel.action_follow() - msg_1 = self._add_messages( - self.test_channel, 'Body1', author=self.user_employee.partner_id, - channel_ids=[self.test_channel.id]) - msg_2 = self._add_messages( - self.test_channel, 'Body2', author=self.user_employee.partner_id, - channel_ids=[self.test_channel.id]) + msg_1 = self._add_messages(self.test_channel, 'Body1', author=self.user_employee.partner_id) + msg_2 = self._add_messages(self.test_channel, 'Body2', author=self.user_employee.partner_id) self.test_channel.channel_seen(msg_2.id) self.assertEqual( @@ -440,12 +438,10 @@ class TestChannelModeration(MailCommon): msg_c1_emplo2 = self._add_messages(self.channel, 'Body21', author=self.partner_employee_2, moderation_status='pending_moderation') self._reset_bus() - self.assertFalse(msg_c1_admin1.channel_ids | msg_c1_admin2.channel_ids | msg_c1_emplo2.channel_ids) # accept with self.assertBus([(self.cr.dbname, 'mail.channel', self.channel.id)]): msg_c1_admin1.with_user(self.user_employee)._moderate('accept') - self.assertEqual(msg_c1_admin1.channel_ids, self.channel) self.assertEqual(msg_c1_admin1.moderation_status, 'accepted') self.assertEqual(msg_c1_admin2.moderation_status, 'pending_moderation') @@ -455,9 +451,6 @@ class TestChannelModeration(MailCommon): (self.cr.dbname, 'mail.channel', self.channel.id), (self.cr.dbname, 'mail.channel', self.channel.id)]): (msg_c1_admin1 | msg_c1_emplo2).with_user(self.user_employee)._moderate('allow') - self.assertEqual(msg_c1_admin1.channel_ids, self.channel) - self.assertEqual(msg_c1_admin2.channel_ids, self.channel) - self.assertEqual(msg_c1_emplo2.channel_ids, self.channel) self.assertEqual(msg_c1_admin1.moderation_status, 'accepted') self.assertEqual(msg_c1_admin2.moderation_status, 'accepted') self.assertEqual(msg_c1_emplo2.moderation_status, 'accepted') @@ -470,8 +463,6 @@ class TestChannelModeration(MailCommon): msg_c1_portal = self._add_messages(self.channel, '

Body12

', author=self.partner_portal, moderation_status='pending_moderation') id2, id4 = msg_c1_admin2.id, msg_c1_portal.id # save ids because unlink will discard them - self.assertFalse(msg_c1_admin1.channel_ids | msg_c1_admin2.channel_ids | msg_c1_emplo2.channel_ids | msg_c1_portal.channel_ids) - # test reject: should also send a rejection email with self.mock_mail_gateway(): (msg_c1_admin1 | msg_c1_emplo2).with_user(self.user_employee)._moderate('reject', title='RejectTitle', comment='RejectComment') @@ -670,5 +661,3 @@ class TestChannelModeration(MailCommon): self.assertFalse(msg_email1) self.assertEqual(msg_admin, pending_messages) self.assertEqual(accepted_messages, msg_moderator | msg_email2 | msg_notif) - self.assertFalse(msg_admin.channel_ids) - self.assertEqual(msg_email2.channel_ids, channel) diff --git a/addons/mail/views/mail_message_views.xml b/addons/mail/views/mail_message_views.xml index df65876ee14..401c4131e2c 100644 --- a/addons/mail/views/mail_message_views.xml +++ b/addons/mail/views/mail_message_views.xml @@ -64,7 +64,6 @@ - diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index 09370bd9ade..db0c27812dd 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -128,7 +128,6 @@ class TestMessagePost(TestMailCommon, TestRecipients): self.assertEqual(msg.body, _body) self.assertEqual(msg.partner_ids, self.partner_1 | self.partner_2) self.assertEqual(msg.notified_partner_ids, self.user_admin.partner_id | self.partner_1 | self.partner_2) - self.assertEqual(msg.channel_ids, self.env['mail.channel']) # notifications emails should have been deleted self.assertFalse(self.env['mail.mail'].sudo().search([('mail_message_id', '=', msg.id)]), diff --git a/addons/website_crm_livechat/models/crm_lead.py b/addons/website_crm_livechat/models/crm_lead.py index 1b2622aa8a8..9751c5bdb97 100644 --- a/addons/website_crm_livechat/models/crm_lead.py +++ b/addons/website_crm_livechat/models/crm_lead.py @@ -17,5 +17,5 @@ class Lead(models.Model): def action_redirect_to_livechat_sessions(self): visitors = self.visitor_ids action = self.env["ir.actions.actions"]._for_xml_id("website_livechat.website_visitor_livechat_session_action") - action['domain'] = [('livechat_visitor_id', 'in', visitors.ids), ('channel_message_ids', '!=', False)] + action['domain'] = [('livechat_visitor_id', 'in', visitors.ids), ('message_ids', '!=', False)] return action diff --git a/addons/website_livechat/models/mail_channel.py b/addons/website_livechat/models/mail_channel.py index 6137dcc8e23..589027a70fa 100644 --- a/addons/website_livechat/models/mail_channel.py +++ b/addons/website_livechat/models/mail_channel.py @@ -18,7 +18,7 @@ class MailChannel(models.Model): delete mail_channel as not useful to keep empty chat """ super(MailChannel, self)._execute_channel_pin(pinned) - if self.livechat_active and not self.channel_message_ids: + if self.livechat_active and not self.message_ids: self.unlink() def channel_info(self, extra_info=False): diff --git a/addons/website_livechat/models/website.py b/addons/website_livechat/models/website.py index a4bd8b1513e..95874357ecb 100644 --- a/addons/website_livechat/models/website.py +++ b/addons/website_livechat/models/website.py @@ -41,7 +41,7 @@ class Website(models.Model): ('livechat_visitor_id', '=', visitor.id), ('livechat_channel_id', '=', self.channel_id.id), ('livechat_active', '=', True), - ('channel_message_ids', '!=', False) + ('message_ids', '!=', False) ], order='create_date desc', limit=1) if chat_request_channel: return { diff --git a/addons/website_livechat/models/website_visitor.py b/addons/website_livechat/models/website_visitor.py index a70145abeff..ef387225894 100644 --- a/addons/website_livechat/models/website_visitor.py +++ b/addons/website_livechat/models/website_visitor.py @@ -31,7 +31,7 @@ class WebsiteVisitor(models.Model): def _compute_session_count(self): sessions = self.env['mail.channel'].search([('livechat_visitor_id', 'in', self.ids)]) session_count = dict.fromkeys(self.ids, 0) - for session in sessions.filtered(lambda c: c.channel_message_ids): + for session in sessions.filtered(lambda c: c.message_ids): session_count[session.livechat_visitor_id.id] += 1 for visitor in self: visitor.session_count = session_count.get(visitor.id, 0) diff --git a/addons/website_livechat/views/website_visitor_views.xml b/addons/website_livechat/views/website_visitor_views.xml index 595e7b6b5a3..1787d1fdd21 100644 --- a/addons/website_livechat/views/website_visitor_views.xml +++ b/addons/website_livechat/views/website_visitor_views.xml @@ -5,7 +5,7 @@ mail.channel tree,form - [('livechat_visitor_id', '=', active_id), ('channel_message_ids', '!=', False)] + [('livechat_visitor_id', '=', active_id), ('message_ids', '!=', False)] { 'search_default_livechat_visitor_id': [active_id], 'default_livechat_visitor_id': active_id,