diff options
| author | Thibault Delavallée <tde@odoo.com> | 2019-05-22 15:10:54 +0000 |
|---|---|---|
| committer | Thibault Delavallée <tde@odoo.com> | 2019-07-12 14:54:11 +0000 |
| commit | 488a5d5af5418ed5c223af5b61aea0b3100508c1 (patch) | |
| tree | 796bf1e455b263f39800040b7f9f3d7c7756dc16 /addons | |
| parent | fd15ca19183a765e1307c4d197bf0dda30b73ab7 (diff) | |
[IMP] mail: prepare new SMS model by updating mail.notification
This commit prepares SMS refactoring by updating some mail models. Purpose
is to be able to store SMS notification information inside existing mail
flow and models.
Main changes
* mail.notification: define a notification_type field to store the medium
used to notify people. In mail two ways exist: inbox and email. It will
ease introduction of SMS notification mechanism. This field replaces the
is_email boolean field;
* mail.notification: make res_partner_id field not required. This means
notifications could be linked to something else than a partner. An SMS
for example. For Inbox and email notifications partner is still required
and a constraint is added accordingly;
* mail.thread: let notification methods handle the creation and update of
their notification instead of creating them in _notify_thread and tweaking
them in sub notification methods;
* mail.thread: clearly move inbox-style notification in its own method like
notify by email;
Some lighter code changes
* propagate message_type to notification recipient computation. It will
allow for example to be more precise when computing a notification type
depending on the message_type. For example, send a notification by SMS
when the message_type is SMS;
* ease inheritance of ``_notify_thread`` by returning computed recipients
data. It will allow to work on it without having to re-compute it;
* propagate kwargs from message_post and message_notify to notify methods.
This allow to avoid depending on context and set explicit parameters.
Drawback is that message_post and notify must separate kwargs used to
create a message and those that are propagated to notification methods;
* update various notification check to ensure they work on email
notifications, notably the resend and cancel wizards;
One side effect of this commit is that notifications are created by the
relevant notification method. Previously all notifications were created
by writing on needaction_partner_ids fields then updated according to the
notification process. Notably there could be too much notification created
when sending emails due to _notify_customize_recipients not being correctly
synchronized with notification. This issue is now solved as only really
sent emails create notifications.
Migration tips
* notification_type: notification.is_email and 'email' else 'inbox';
* remove is_email;
Related to task 1922163
Linked to PR #33510
Diffstat (limited to 'addons')
| -rw-r--r-- | addons/mail/models/mail_channel.py | 9 | ||||
| -rw-r--r-- | addons/mail/models/mail_followers.py | 3 | ||||
| -rw-r--r-- | addons/mail/models/mail_mail.py | 18 | ||||
| -rw-r--r-- | addons/mail/models/mail_message.py | 88 | ||||
| -rw-r--r-- | addons/mail/models/mail_notification.py | 21 | ||||
| -rw-r--r-- | addons/mail/models/mail_thread.py | 210 | ||||
| -rw-r--r-- | addons/mail/views/mail_message_views.xml | 4 | ||||
| -rw-r--r-- | addons/mail/wizard/invite.py | 14 | ||||
| -rw-r--r-- | addons/mail/wizard/mail_resend_cancel.py | 8 | ||||
| -rw-r--r-- | addons/mail/wizard/mail_resend_message.py | 32 | ||||
| -rw-r--r-- | addons/test_mail/tests/common.py | 4 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_mail_race.py | 23 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_mail_resend.py | 2 | ||||
| -rw-r--r-- | addons/test_mail/tests/test_performance.py | 26 | ||||
| -rw-r--r-- | addons/website_blog/models/website_blog.py | 9 | ||||
| -rw-r--r-- | addons/website_forum/models/forum.py | 9 |
16 files changed, 266 insertions, 214 deletions
diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 7912a856ca4..cbc548825d9 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -503,15 +503,10 @@ class Channel(models.Model): notifications.append([(self._cr.dbname, 'res.partner', partner.id), channel_info]) return notifications - def _notify_thread(self, message, msg_vals=False, model_description=False, mail_auto_delete=True): + def _notify_thread(self, message, msg_vals=False, **kwargs): # 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': - super(Channel, self)._notify_thread( - message, - msg_vals=msg_vals, - model_description=model_description, - mail_auto_delete=mail_auto_delete, - ) + super(Channel, self)._notify_thread(message, msg_vals=msg_vals, **kwargs) else: message._notify_pending_by_chat() diff --git a/addons/mail/models/mail_followers.py b/addons/mail/models/mail_followers.py index 1b5c94972c2..417b83e59f5 100644 --- a/addons/mail/models/mail_followers.py +++ b/addons/mail/models/mail_followers.py @@ -81,7 +81,7 @@ class Followers(models.Model): # Private tools methods to fetch followers data # -------------------------------------------------- - def _get_recipient_data(self, records, subtype_id, pids=None, cids=None): + def _get_recipient_data(self, records, message_type, subtype_id, pids=None, cids=None): """ Private method allowing to fetch recipients data based on a subtype. Purpose of this method is to fetch all data necessary to notify recipients in a single query. It fetches data from @@ -92,6 +92,7 @@ class Followers(models.Model): * channels if cids is given; :param records: fetch data from followers of records that follow subtype_id; + :param message_type: mail.message.message_type in order to allow custom behavior depending on it (SMS for example); :param subtype_id: mail.message.subtype to check against followers; :param pids: additional set of partner IDs from which to fetch recipient data; :param cids: additional set of channel IDs from which to fetch recipient data; diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py index f97d0a52cde..f9069021307 100644 --- a/addons/mail/models/mail_mail.py +++ b/addons/mail/models/mail_mail.py @@ -154,24 +154,24 @@ class MailMail(models.Model): notif_mails_ids = [mail.id for mail in self if mail.notification] if notif_mails_ids: notifications = self.env['mail.notification'].search([ - ('is_email', '=', True), + ('notification_type', '=', 'email'), ('mail_id', 'in', notif_mails_ids), - ('email_status', 'not in', ('sent', 'canceled')) + ('notification_status', 'not in', ('sent', 'canceled')) ]) if notifications: - #find all notification linked to a failure + # find all notification linked to a failure failed = self.env['mail.notification'] if failure_type: failed = notifications.filtered(lambda notif: notif.res_partner_id not in success_pids) failed.sudo().write({ - 'email_status': 'exception', + 'notification_status': 'exception', 'failure_type': failure_type, 'failure_reason': failure_reason, }) messages = notifications.mapped('mail_message_id').filtered(lambda m: m.is_thread_message()) - messages._notify_failure_update() # notify user that we have a failure + messages._notify_mail_failure_update() # notify user that we have a failure (notifications - failed).sudo().write({ - 'email_status': 'sent', + 'notification_status': 'sent', 'failure_type': '', 'failure_reason': '', }) @@ -335,14 +335,14 @@ class MailMail(models.Model): # update in case an email bounces while sending all emails related to current # mail record. notifs = self.env['mail.notification'].search([ - ('is_email', '=', True), + ('notification_type', '=', 'email'), ('mail_id', 'in', mail.ids), - ('email_status', 'not in', ('sent', 'canceled')) + ('notification_status', 'not in', ('sent', 'canceled')) ]) if notifs: notif_msg = _('Error without exception. Probably due do concurrent access update of notification records. Please see with an administrator.') notifs.sudo().write({ - 'email_status': 'exception', + 'notification_status': 'exception', 'failure_type': 'UNKNOWN', 'failure_reason': notif_msg, }) diff --git a/addons/mail/models/mail_message.py b/addons/mail/models/mail_message.py index e90249e9533..45b71613a85 100644 --- a/addons/mail/models/mail_message.py +++ b/addons/mail/models/mail_message.py @@ -5,6 +5,7 @@ import logging import re from binascii import Error as binascii_error +from collections import defaultdict from operator import itemgetter from email.utils import formataddr from openerp.http import request @@ -148,15 +149,15 @@ class Message(models.Model): def _compute_has_error(self): error_from_notification = self.env['mail.notification'].sudo().search([ ('mail_message_id', 'in', self.ids), - ('email_status', 'in', ('bounce', 'exception'))]).mapped('mail_message_id') + ('notification_status', 'in', ('bounce', 'exception'))]).mapped('mail_message_id') for message in self: message.has_error = message in error_from_notification @api.multi def _search_has_error(self, operator, operand): if operator == '=' and operand: - return [('notification_ids.email_status', 'in', ('bounce', 'exception'))] - return ['!', ('notification_ids.email_status', 'in', ('bounce', 'exception'))] # this wont work and will be equivalent to "not in" beacause of orm restrictions. Dont use "has_error = False" + return [('notification_ids.notification_status', 'in', ('bounce', 'exception'))] + return ['!', ('notification_ids.notification_status', 'in', ('bounce', 'exception'))] # this wont work and will be equivalent to "not in" beacause of orm restrictions. Dont use "has_error = False" @api.depends('starred_partner_ids') def _get_starred(self): @@ -373,25 +374,22 @@ class Message(models.Model): partner_ids = [] if message.subtype_id: partner_ids = [partner_tree[partner.id] for partner in message.partner_ids - if partner.id in partner_tree] + if partner.id in partner_tree] else: partner_ids = [partner_tree[partner.id] for partner in message.partner_ids - if partner.id in partner_tree] + if partner.id in partner_tree] # we read customer_email_status before filtering inactive user because we don't want to miss a red enveloppe customer_email_status = ( - (all(n.email_status == 'sent' for n in message.notification_ids) and 'sent') or - (any(n.email_status == 'exception' for n in message.notification_ids) and 'exception') or - (any(n.email_status == 'bounce' for n in message.notification_ids) and 'bounce') or + (all(n.notification_status == 'sent' for n in message.notification_ids if n.notification_type == 'email') and 'sent') or + (any(n.notification_status == 'exception' for n in message.notification_ids if n.notification_type == 'email') and 'exception') or + (any(n.notification_status == 'bounce' for n in message.notification_ids if n.notification_type == 'email') and 'bounce') or 'ready' ) customer_email_data = [] - def filter_notification(notif): - return ( - (notif.email_status in ('bounce', 'exception', 'canceled') or notif.res_partner_id.partner_share) and - notif.res_partner_id.active - ) - for notification in message.notification_ids.filtered(filter_notification): - customer_email_data.append((partner_tree[notification.res_partner_id.id][0], partner_tree[notification.res_partner_id.id][1], notification.email_status)) + for notification in message.notification_ids.filtered( + lambda n: n.notification_type == 'email' and n.res_partner_id.active and + (n.notification_status in ('bounce', 'exception', 'canceled') or n.res_partner_id.partner_share)): + customer_email_data.append((partner_tree[notification.res_partner_id.id][0], partner_tree[notification.res_partner_id.id][1], notification.notification_status)) has_access_to_model = message.model and self.env[message.model].check_access_rights('read', raise_exception=False) if message.attachment_ids and message.res_id and issubclass(self.pool[message.model], self.pool['mail.thread']) and has_access_to_model: @@ -512,7 +510,7 @@ class Message(models.Model): # fetch notification status notif_dict = {} - notifs = self.env['mail.notification'].sudo().search([('mail_message_id', 'in', list(mid for mid in message_tree)), ('is_read', '=', False)]) + notifs = self.env['mail.notification'].sudo().search([('mail_message_id', 'in', list(mid for mid in message_tree)), ('res_partner_id', '!=', False), ('is_read', '=', False)]) for notif in notifs: mid = notif.mail_message_id.id if not notif_dict.get(mid): @@ -540,14 +538,46 @@ class Message(models.Model): 'moderation_status', ] + def _get_mail_failure_dict(self): + return { + 'message_id': self.id, + 'record_name': self.record_name, + 'model_name': self.env['ir.model']._get(self.model).display_name, + 'uuid': self.message_id, + 'res_id': self.res_id, + 'model': self.model, + 'last_message_date': self.date, + 'module_icon': '/mail/static/src/img/smiley/mailfailure.jpg', + } + @api.multi def _format_mail_failures(self): - """ - A shorter message to notify a failure update - """ + """ A shorter message to notify a failure update """ failures_infos = [] + + # prepare notifications computation in batch + all_notifications = self.env['mail.notification'].sudo().search([ + ('mail_message_id', 'in', self.ids) + ]) + msgid_to_notif = defaultdict(lambda: self.env['mail.notification'].sudo()) + for notif in all_notifications: + msgid_to_notif[notif.mail_message_id.id] += notif + # for each channel, build the information header and include the logged partner information for message in self: + notifications = msgid_to_notif[message.id] + if not any(notification.notification_type == 'email' for notification in notifications): + continue + info = dict(message._get_mail_failure_dict(), + failure_type='mail', + notifications=dict((notif.res_partner_id.id, (notif.notification_status, notif.res_partner_id.name)) for notif in notifications)) + failures_infos.append(info) + return failures_infos + + @api.multi + def _notify_mail_failure_update(self): + messages = self.env['mail.message'] + for message in self: # Check if user has access to the record before displaying a notification about it. # In case the user switches from one company to another, it might happen that he doesn't # have access to the record related to the notification. In this case, we skip it. @@ -558,24 +588,10 @@ class Message(models.Model): record.check_access_rule('read') except AccessError: continue - info = { - 'message_id': message.id, - 'record_name': message.record_name, - 'model_name': self.env['ir.model']._get(message.model).display_name, - 'uuid': message.message_id, - 'res_id': message.res_id, - 'model': message.model, - 'last_message_date': message.date, - 'module_icon': '/mail/static/src/img/smiley/mailfailure.jpg', - 'notifications': dict((notif.res_partner_id.id, (notif.email_status, notif.res_partner_id.name)) for notif in message.notification_ids.sudo()) - } - failures_infos.append(info) - return failures_infos + else: + messages |= message - @api.multi - def _notify_failure_update(self): - authors = {} - for author, author_messages in groupby(self, itemgetter('author_id')): + for author, author_messages in groupby(messages, itemgetter('author_id')): self.env['bus.bus'].sendone( (self._cr.dbname, 'res.partner', author.id), {'type': 'mail_failure', 'elements': self.env['mail.message'].concat(*author_messages)._format_mail_failures()} diff --git a/addons/mail/models/mail_notification.py b/addons/mail/models/mail_notification.py index 3397342a818..5cf42a2dc72 100644 --- a/addons/mail/models/mail_notification.py +++ b/addons/mail/models/mail_notification.py @@ -14,10 +14,12 @@ class Notification(models.Model): mail_message_id = fields.Many2one( 'mail.message', 'Message', index=True, ondelete='cascade', required=True) res_partner_id = fields.Many2one( - 'res.partner', 'Needaction Recipient', index=True, ondelete='cascade', required=True) + 'res.partner', 'Needaction Recipient', index=True, ondelete='cascade', required=False) is_read = fields.Boolean('Is Read', index=True) - is_email = fields.Boolean('Sent by Email', index=True) - email_status = fields.Selection([ + notification_type = fields.Selection([ + ('inbox', 'Inbox'), ('email', 'Email')], string='Notification Type', + default='inbox', index=True, required=True) + notification_status = fields.Selection([ ('ready', 'Ready to Send'), ('sent', 'Sent'), ('bounce', 'Bounced'), @@ -37,10 +39,17 @@ class Notification(models.Model): ], string='Failure type') failure_reason = fields.Text('Failure reason', copy=False) + _sql_constraints = [ + # email notification;: partner is required + ('notification_partner_required', + "CHECK(notification_type in ('email', 'inbox') AND res_partner_id IS NOT NULL)", + 'Customer is required for inbox / email notification'), + ] + def init(self): - self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_res_partner_id_is_read_email_status_mail_message_id',)) + self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', ('mail_notification_res_partner_id_is_read_notification_status_mail_message_id',)) if not self._cr.fetchone(): - self._cr.execute('CREATE INDEX mail_notification_res_partner_id_is_read_email_status_mail_message_id ON mail_message_res_partner_needaction_rel (res_partner_id, is_read, email_status, mail_message_id)') + self._cr.execute('CREATE INDEX mail_notification_res_partner_id_is_read_notification_status_mail_message_id ON mail_message_res_partner_needaction_rel (res_partner_id, is_read, notification_status, mail_message_id)') @api.multi def format_failure_reason(self): @@ -49,5 +58,3 @@ class Notification(models.Model): return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error')) else: return _("Unknown error") + ": %s" % (self.failure_reason or '') - - diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index c1feb9ba820..432c0657b42 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -226,7 +226,7 @@ class MailThread(models.AbstractModel): if self.ids: self._cr.execute(""" SELECT msg.res_id, COUNT(msg.res_id) FROM mail_message msg RIGHT JOIN mail_message_res_partner_needaction_rel rel - ON rel.mail_message_id = msg.id AND rel.email_status in ('exception','bounce') + ON rel.mail_message_id = msg.id AND rel.notification_status in ('exception','bounce') WHERE msg.author_id = %s AND msg.model = %s AND msg.res_id in %s AND msg.message_type != 'user_notification' GROUP BY msg.res_id""", (self.env.user.partner_id.id, self._name, tuple(self.ids),)) @@ -915,7 +915,7 @@ class MailThread(models.AbstractModel): ('mail_message_id', '=', mail_message.id), ('res_partner_id', 'in', partners.ids)]) notifications.write({ - 'email_status': 'bounce' + 'notification_status': 'bounce' }) if bounced_model in self.env and hasattr(self.env[bounced_model], '_message_receive_bounce') and bounced_thread_id: @@ -1679,7 +1679,7 @@ class MailThread(models.AbstractModel): email_from=False, author_id=None, parent_id=False, subtype_id=False, subtype=None, partner_ids=None, channel_ids=None, attachments=None, attachment_ids=None, - add_sign=True, model_description=False, mail_auto_delete=True, record_name=False, + add_sign=True, record_name=False, **kwargs): """ Post a new message in an existing thread, returning the new mail.message ID. @@ -1707,11 +1707,14 @@ class MailThread(models.AbstractModel): :return int: ID of newly created mail.message """ self.ensure_one() # should always be posted on a record, use message_notify if no record + # split message additional values from notify additional values + msg_kwargs = dict((key, val) for key, val in kwargs.items() if key in self.env['mail.message']._fields) + notif_kwargs = dict((key, val) for key, val in kwargs.items() if key not in msg_kwargs) if self._name == 'mail.thread' or not self.id or message_type == 'user_notification': raise ValueError('message_post should only be call to post message on record. Use message_notify instead') - if 'model' in kwargs or 'res_id' in kwargs: + if 'model' in msg_kwargs or 'res_id' in msg_kwargs: raise ValueError("message_post doesn't support model and res_id parameters anymore. Please call message_post on record") self = self.with_lang() # add lang to context imediatly since it will be usefull in various flows latter. @@ -1746,7 +1749,7 @@ class MailThread(models.AbstractModel): # parent_message searched in sudo for performance, only used for id. # Note that with sudo we will match message with internal subtypes. parent_id = parent_message.id if parent_message else False - elif parent_id: + elif parent_id: old_parent_id = parent_id parent_message = MailMessage_sudo.search([('id', '=', parent_id), ('parent_id', '!=', False)], limit=1) # avoid loops when finding ancestors @@ -1757,7 +1760,8 @@ class MailThread(models.AbstractModel): processed_list.append(new_parent_id) parent_message = parent_message.parent_id parent_id = parent_message.id - values = dict(kwargs) + + values = dict(msg_kwargs) values.update({ 'author_id': author_id, 'model': self._name, @@ -1776,19 +1780,19 @@ class MailThread(models.AbstractModel): attachments = attachments or [] attachment_ids = attachment_ids or [] attachement_values = self._message_post_process_attachments(attachments, attachment_ids, values) - values.update(attachement_values) # attachement_ids, [body] + values.update(attachement_values) # attachement_ids, [body] - new_message= self._message_create(values) + new_message = self._message_create(values) # Set main attachment field if necessary self._message_set_main_attachment_id(values['attachment_ids']) if values['author_id'] and values['message_type'] != 'notification' and not self._context.get('mail_create_nosubscribe'): - #if self.env['res.partner'].browse(values['author_id']).active: # we dont want to add odoobot/inactive as a follower + # if self.env['res.partner'].browse(values['author_id']).active: # we dont want to add odoobot/inactive as a follower self._message_subscribe([values['author_id']]) self._message_post_after_hook(new_message, values) - self._notify_thread(new_message, values, model_description=model_description, mail_auto_delete=mail_auto_delete) + self._notify_thread(new_message, values, **notif_kwargs) return new_message def _message_set_main_attachment_id(self, attachment_ids): # todo move this out of mail.thread @@ -1863,14 +1867,15 @@ class MailThread(models.AbstractModel): return composer.send_mail() def message_notify(self, partner_ids=False, parent_id=False, model=False, res_id=False, - author_id=False, body='', subject=False, model_description=False, - mail_auto_delete=True, **kwargs): + author_id=False, body='', subject=False, **kwargs): """ Shortcut allowing to notify partners of messages that shouldn't be displayed on a document. It pushes notifications on inbox or by email depending on the user configuration, like other notifications. """ - if self: self.ensure_one() + # split message additional values from notify additional values + msg_kwargs = dict((key, val) for key, val in kwargs.items() if key in self.env['mail.message']._fields) + notif_kwargs = dict((key, val) for key, val in kwargs.items() if key not in msg_kwargs) if author_id: author = self.env['res.partner'].sudo().browse(author_id) @@ -1914,9 +1919,9 @@ class MailThread(models.AbstractModel): 'reply_to': MailThread._notify_get_reply_to(default=email_from, records=None)[False], 'message_id': tools.generate_tracking_message_id('message-notify'), } - values.update(kwargs) + values.update(msg_kwargs) new_message = MailThread._message_create(values) - MailThread._notify_thread(new_message, values, model_description=model_description, mail_auto_delete=mail_auto_delete) + MailThread._notify_thread(new_message, values, **notif_kwargs) return new_message def _message_log(self, body='', author_id=None, subject=False, message_type='notification', **kwargs): @@ -1972,24 +1977,27 @@ class MailThread(models.AbstractModel): # ------------------------------------------------------ @api.multi - def _notify_thread(self, message, msg_vals=False, model_description=False, mail_auto_delete=True): + def _notify_thread(self, message, msg_vals=False, **kwargs): """ Main notification method. This method basically does two things + * call ``_notify_compute_recipients`` that computes recipients to notify based on message record or message creation values if given (to optimize performance if we already have data computed); - * performs the notification process; - Can be overridden to intercept and postpone notification mecanism (mail.channel moderation) - :param message: posted message; + * performs the notification process by calling the various notification + methods implemented; + + This method cnn be overridden to intercept and postpone notification + mechanism like mail.channel moderation. + + :param message: mail.message record to notify; :param msg_vals: dictionary of values used to create the message. If given - it is used instead of accessing ``self`` to lesen query count in some + it is used instead of accessing ``self`` to lessen query count in some simple cases where no notification is actually required; - :param force_send: tells whether to send notification emails within the - current transaction or to use the email queue; - :param model_description: optional data used in notification process (see - notification templates); - :param mail_auto_delete: delete notification emails once sent; - """ + Kwargs allow to pass various parameters that are given to sub notification + methods. See those methods for more details about the additional parameters. + Parameters used for email-style notifications + """ msg_vals = msg_vals if msg_vals else {} rdata = self._notify_compute_recipients(message, msg_vals) if not rdata: @@ -1998,59 +2006,78 @@ class MailThread(models.AbstractModel): message_values = {} if rdata['channels']: message_values['channel_ids'] = [(6, 0, [r['id'] for r in rdata['channels']])] - if rdata['partners']: - message_values['needaction_partner_ids'] = [(6, 0, [r['id'] for r in rdata['partners'] if r['type'] != 'channel_email'])] - # change of behavior to check: since email_cids partner are added in _notify_compute_recipients, - # they will be added to needaction_partner_ids to. - # we may want to filter them (example with channel_email, a cleaner solution may be great) - # -> instead of using _notify_customize_recipients, we could add a flag on rdata - # (would work for needactions, not if we want to erase partner_ids, ids) - # (could also be interesting for, we could add partners with r['notif'] = 'ocn_client' and r['needaction']=False) - # then override a notify_recipients (as it was before) to effectively send ocn notifications. - # envelope will contain more needaction, those for the member of a email channel. - if message_values and self: - message_values.update(self._notify_customize_recipients(message, msg_vals)) - if message_values: - message.write(message_values) - - inbox_pids = [r['id'] for r in rdata['partners'] if r['notif'] == 'inbox'] - partner_email_rdata = [r for r in rdata['partners'] if r['notif'] == 'email'] - channel_ids = [r['id'] for r in rdata['channels']] - - notifications = [] + + self._notify_record_by_inbox(message, rdata, msg_vals=msg_vals, **kwargs) + self._notify_record_by_email(message, rdata, msg_vals=msg_vals, **kwargs) + + return rdata + + def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs): + """ Notification method: inbox. Do two main things + + * create an inbox notification for users; + * create channel / message link (channel_ids field of mail.message); + * send bus notifications; + + TDE/XDO TODO: flag rdata directly, with for example r['notif'] = 'ocn_client' and r['needaction']=False + and correctly override notify_recipients + """ + channel_ids = [r['id'] for r in recipients_data['channels']] + if channel_ids: + message.write({'channel_ids': [(6, 0, channel_ids)]}) + + inbox_pids = [r['id'] for r in recipients_data['partners'] if r['notif'] == 'inbox'] + if inbox_pids: + notif_create_values = [{ + 'mail_message_id': message.id, + 'res_partner_id': pid, + 'notification_type': 'inbox', + } for pid in inbox_pids] + self.env['mail.notification'].sudo().create(notif_create_values) + + bus_notifications = [] if inbox_pids or channel_ids: - message_values = False + message_format_values = False if inbox_pids: - message_values = message.message_format()[0] + message_format_values = message.message_format()[0] for partner in self.env['res.partner'].browse(inbox_pids): - notifications.append([(self._cr.dbname, 'ir.needaction', partner), dict(message_values)]) + bus_notifications.append([(self._cr.dbname, 'ir.needaction', partner), dict(message_format_values)]) if channel_ids: - notifications += self.env['mail.channel'].sudo().browse(channel_ids)._channel_message_notifications(message, message_values) - if partner_email_rdata: - self._notify_record_by_email(message, partner_email_rdata, msg_vals=msg_vals, model_description=model_description, mail_auto_delete=mail_auto_delete) - if notifications: - self.env['bus.bus'].sudo().sendmany(notifications) - return True - - def _notify_record_by_email(self, message, partners_data, msg_vals=False, model_description=False, mail_auto_delete=True, send_after_commit=True): + bus_notifications += self.env['mail.channel'].sudo().browse(channel_ids)._channel_message_notifications(message, message_format_values) + if bus_notifications: + self.env['bus.bus'].sudo().sendmany(bus_notifications) + + def _notify_record_by_email(self, message, recipients_data, msg_vals=False, + model_description=False, mail_auto_delete=True, check_existing=False, + force_send=True, send_after_commit=True, + **kwargs): """ Method to send email linked to notified messages. + :param message: mail.message record to notify; - :param partners_data: partner to notify by email coming from _notify_compute_recipients - :param msg_vals: message creation values if available + :param recipients_data: see ``_notify_thread``; + :param msg_vals: see ``_notify_thread``; + + :param model_description: model description used in email notification process + (computed if not given); + :param mail_auto_delete: delete notification emails once sent; + :param check_existing: check for existing notifications to update based on + mailed recipient, otherwise create new notifications; + + :param force_send: send emails directly instead of using queue; :param send_after_commit: if force_send, tells whether to send emails after the transaction has been committed using a post-commit hook; - :param model_description: optional data used in notification process (see - notification templates); - :param mail_auto_delete: delete notification emails once sent; """ + partners_data = [r for r in recipients_data['partners'] if r['notif'] == 'email'] + if not partners_data: + return True + model = msg_vals.get('model') if msg_vals else message.model model_name = model_description or (self.with_lang().env['ir.model']._get(model).display_name if model else False) # one query for display name recipients_groups_data = self._notify_classify_recipients(partners_data, model_name) if not recipients_groups_data: return True - - force_send = self.env.context.get('mail_notify_force_send', True) + force_send = self.env.context.get('mail_notify_force_send', force_send) template_values = self._notify_prepare_template_context(message, msg_vals, model_description=model_description) # 10 queries @@ -2062,7 +2089,6 @@ class MailThread(models.AbstractModel): _logger.warning('QWeb template %s not found when sending notification emails. Sending without layouting.' % (template_xmlid)) base_template = False - mail_subject = message.subject or (message.record_name and 'Re: %s' % message.record_name) # in cache, no queries # prepare notification mail values base_mail_values = { @@ -2080,6 +2106,7 @@ class MailThread(models.AbstractModel): emails = self.env['mail.mail'].sudo() # loop on groups (customer, portal, user, ... + model specific like group_sale_salesman) + notif_create_values = [] recipients_max = 50 for recipients_group_data in recipients_groups_data: # generate notification email content @@ -2093,7 +2120,8 @@ class MailThread(models.AbstractModel): else: mail_body = message.body mail_body = self._replace_local_links(mail_body) - # send email + + # create email for recipients_ids_chunk in split_every(recipients_max, recipients_ids): recipient_values = self._notify_email_recipient_values(recipients_ids_chunk) email_to = recipient_values['email_to'] @@ -2110,22 +2138,32 @@ class MailThread(models.AbstractModel): email = Mail.create(create_values) if email and recipient_ids: - notifications = self.env['mail.notification'].sudo().search([ - ('mail_message_id', '=', email.mail_message_id.id), - ('res_partner_id', 'in', list(recipient_ids)) # not sure to check. - # TODO XDO what if recipient_ids are empty because of _notify_email_recipient_values - # should we use recipients_ids_chunk? - # should we unlink recipients_ids_chunk - recipient_ids ? - # should we avoid to create needation? by calling _notify_email_recipient_values at the same place _notify_customize_recipients does? (but no chubnk at this step) - ]) - notifications.write({ - 'is_email': True, + tocreate_recipient_ids = list(recipient_ids) + if check_existing: + existing_notifications = self.env['mail.notification'].sudo().search([ + ('mail_message_id', '=', message.id), + ('notification_type', '=', 'email'), + ('res_partner_id', 'in', tocreate_recipient_ids) + ]) + if existing_notifications: + tocreate_recipient_ids = [rid for rid in recipient_ids if rid not in existing_notifications.mapped('res_partner_id.id')] + existing_notifications.write({ + 'notification_status': 'ready', + 'mail_id': email.id, + }) + notif_create_values += [{ + 'mail_message_id': message.id, + 'res_partner_id': recipient_id, + 'notification_type': 'email', 'mail_id': email.id, - 'is_read': True, # handle by email discards Inbox notification - 'email_status': 'ready', - }) + 'is_read': True, # discard Inbox notification + 'notification_status': 'ready', + } for recipient_id in tocreate_recipient_ids] emails |= email + if notif_create_values: + self.env['mail.notification'].sudo().create(notif_create_values) + # NOTE: # 1. for more than 50 followers, use the queue system # 2. do not send emails immediately if the registry is not loaded, @@ -2158,7 +2196,7 @@ class MailThread(models.AbstractModel): author = message.env['res.partner'].browse(msg_vals.get('author_id')) if msg_vals else message.author_id model = msg_vals.get('model') if msg_vals else message.model add_sign = msg_vals.get('add_sign') if msg_vals else message.add_sign - subtype_id = msg_vals.get('subtype_id') if msg_vals else message.subtype_id.id + subtype_id = msg_vals.get('subtype_id') if msg_vals else message.subtype_id.id message_id = message.id record_name = msg_vals.get('record_name') if msg_vals else message.record_name author_user = user if user.partner_id == author else author.user_ids[0] if author and author.user_ids else False @@ -2222,13 +2260,14 @@ class MailThread(models.AbstractModel): # get values from msg_vals or from message if msg_vals doen't exists pids = msg_vals.get('partner_ids', []) if msg_vals else msg_sudo.partner_ids.ids cids = msg_vals.get('channel_ids', []) if msg_vals else msg_sudo.channel_ids.ids + message_type = msg_vals.get('message_type') if msg_vals else msg_sudo.message_type subtype_id = msg_vals.get('subtype_id') if msg_vals else msg_sudo.subtype_id.id # is it possible to have record but no subtype_id ? recipient_data = { 'partners': [], 'channels': [], } - res = self.env['mail.followers']._get_recipient_data(self, subtype_id, pids, cids) + res = self.env['mail.followers']._get_recipient_data(self, message_type, subtype_id, pids, cids) if not res: return recipient_data @@ -2243,11 +2282,11 @@ class MailThread(models.AbstractModel): if notif == 'inbox': recipient_data['partners'].append(dict(pdata, notif=notif, type='user')) elif not pshare and notif: # has an user and is not shared, is therefore user - recipient_data['partners'].append(dict(pdata, notif='email', type='user')) + recipient_data['partners'].append(dict(pdata, notif=notif, type='user')) elif pshare and notif: # has an user but is shared, is therefore portal - recipient_data['partners'].append(dict(pdata, notif='email', type='portal')) + recipient_data['partners'].append(dict(pdata, notif=notif, type='portal')) else: # has no user, is therefore customer - recipient_data['partners'].append(dict(pdata, notif='email', type='customer')) + recipient_data['partners'].append(dict(pdata, notif=notif if notif else 'email', type='customer')) elif cid: recipient_data['channels'].append({'id': cid, 'notif': notif, 'type': ctype}) @@ -2514,9 +2553,6 @@ class MailThread(models.AbstractModel): 'email_to': False, 'recipient_ids': recipient_ids, } - @api.multi - def _notify_customize_recipients(self, message, msg_vals): - return {} # ------------------------------------------------------ # Followers API diff --git a/addons/mail/views/mail_message_views.xml b/addons/mail/views/mail_message_views.xml index 70657edd08c..14c48dc4738 100644 --- a/addons/mail/views/mail_message_views.xml +++ b/addons/mail/views/mail_message_views.xml @@ -75,8 +75,8 @@ <tree> <field name="res_partner_id"/> <field name="is_read"/> - <field name="is_email"/> - <field name="email_status"/> + <field name="notification_type"/> + <field name="notification_status"/> </tree> </field> </group> diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index 5a899e1a7c6..5c7889b6a76 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -72,13 +72,13 @@ class Invite(models.TransientModel): 'no_auto_thread': True, 'add_sign': True, }) - partners_data = [{ - 'id': pid, - 'share': True, - 'notif': 'email', - 'type': 'customer', + recipients_data = {'partners': [{ + 'id': pid, + 'share': True, + 'notif': 'email', + 'type': 'customer', 'groups': [] - } for pid in new_partners.ids] - document._notify_record_by_email(message, partners_data, send_after_commit=False) + } for pid in new_partners.ids]} + document._notify_record_by_email(message, recipients_data, send_after_commit=False) message.unlink() return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mail/wizard/mail_resend_cancel.py b/addons/mail/wizard/mail_resend_cancel.py index 277ed8b96db..f3bb8307754 100644 --- a/addons/mail/wizard/mail_resend_cancel.py +++ b/addons/mail/wizard/mail_resend_cancel.py @@ -4,7 +4,7 @@ from odoo import _, api, fields, models -class MailCancelResend(models.TransientModel): +class MailResendCancel(models.TransientModel): _name = 'mail.resend.cancel' _description = 'Dismiss notification for resend by model' @@ -26,7 +26,7 @@ class MailCancelResend(models.TransientModel): FROM mail_message_res_partner_needaction_rel notif JOIN mail_message mes ON notif.mail_message_id = mes.id - WHERE notif.email_status IN ('bounce', 'exception') + WHERE notif.notification_status IN ('bounce', 'exception') AND mes.model = %s AND mes.author_id = %s """, (wizard.model, author_id)) @@ -34,6 +34,6 @@ class MailCancelResend(models.TransientModel): notif_ids = [row[0] for row in res] messages_ids = list(set([row[1] for row in res])) if notif_ids: - self.env["mail.notification"].browse(notif_ids).sudo().write({'email_status': 'canceled'}) - self.env["mail.message"].browse(messages_ids)._notify_failure_update() + self.env["mail.notification"].browse(notif_ids).sudo().write({'notification_status': 'canceled'}) + self.env["mail.message"].browse(messages_ids)._notify_mail_failure_update() return {'type': 'ir.actions.act_window_close'} diff --git a/addons/mail/wizard/mail_resend_message.py b/addons/mail/wizard/mail_resend_message.py index e5e4c069df1..9c72944961b 100644 --- a/addons/mail/wizard/mail_resend_message.py +++ b/addons/mail/wizard/mail_resend_message.py @@ -28,16 +28,14 @@ class MailResendMessage(models.TransientModel): message_id = self._context.get('mail_message_to_resend') if message_id: mail_message_id = self.env['mail.message'].browse(message_id) - notification_ids = mail_message_id.notification_ids.filtered(lambda notif: notif.email_status in ('exception', 'bounce')) - partner_ids = [(0, 0, - { - "partner_id": notif.res_partner_id.id, - "name": notif.res_partner_id.name, - "email": notif.res_partner_id.email, - "resend": True, - "message": notif.format_failure_reason(), - } - ) for notif in notification_ids] + notification_ids = mail_message_id.notification_ids.filtered(lambda notif: notif.notification_type == 'email' and notif.notification_status in ('exception', 'bounce')) + partner_ids = [(0, 0, { + "partner_id": notif.res_partner_id.id, + "name": notif.res_partner_id.name, + "email": notif.res_partner_id.email, + "resend": True, + "message": notif.format_failure_reason(), + }) for notif in notification_ids] has_user = any([notif.res_partner_id.user_ids for notif in notification_ids]) if has_user: partner_readonly = not self.env['res.users'].check_access_rights('write', raise_exception=False) @@ -59,14 +57,14 @@ class MailResendMessage(models.TransientModel): "If a partner disappeared from partner list, we cancel the notification" to_cancel = wizard.partner_ids.filtered(lambda p: not p.resend).mapped("partner_id") to_send = wizard.partner_ids.filtered(lambda p: p.resend).mapped("partner_id") - notif_to_cancel = wizard.notification_ids.filtered(lambda notif: notif.res_partner_id in to_cancel and notif.email_status in ('exception', 'bounce')) - notif_to_cancel.sudo().write({'email_status': 'canceled'}) + notif_to_cancel = wizard.notification_ids.filtered(lambda notif: notif.notification_type == 'email' and notif.res_partner_id in to_cancel and notif.notification_status in ('exception', 'bounce')) + notif_to_cancel.sudo().write({'notification_status': 'canceled'}) if to_send: message = wizard.mail_message_id record = self.env[message.model].browse(message.res_id) if message.is_thread_message() else self.env['mail.thread'] email_partners_data = [] - for pid, cid, active, pshare, ctype, notif, groups in self.env['mail.followers']._get_recipient_data(None, False, pids=to_send.ids): + for pid, cid, active, pshare, ctype, notif, groups in self.env['mail.followers']._get_recipient_data(None, 'comment', False, pids=to_send.ids): if pid and notif == 'email' or not notif: pdata = {'id': pid, 'share': pshare, 'active': active, 'notif': 'email', 'groups': groups or []} if not pshare and notif: # has an user and is not shared, is therefore user @@ -76,17 +74,17 @@ class MailResendMessage(models.TransientModel): else: # has no user, is therefore customer email_partners_data.append(dict(pdata, type='customer')) - record._notify_record_by_email(message, email_partners_data, send_after_commit=False) + record._notify_record_by_email(message, {'partners': email_partners_data}, check_existing=True, send_after_commit=False) - self.mail_message_id._notify_failure_update() + self.mail_message_id._notify_mail_failure_update() return {'type': 'ir.actions.act_window_close'} @api.multi def cancel_mail_action(self): for wizard in self: for notif in wizard.notification_ids: - notif.filtered(lambda notif: notif.email_status in ('exception', 'bounce')).sudo().write({'email_status': 'canceled'}) - wizard.mail_message_id._notify_failure_update() + notif.filtered(lambda notif: notif.notification_type == 'email' and notif.notification_status in ('exception', 'bounce')).sudo().write({'notification_status': 'canceled'}) + wizard.mail_message_id._notify_mail_failure_update() return {'type': 'ir.actions.act_window_close'} diff --git a/addons/test_mail/tests/common.py b/addons/test_mail/tests/common.py index 7df3aa0c96a..9881d631977 100644 --- a/addons/test_mail/tests/common.py +++ b/addons/test_mail/tests/common.py @@ -86,13 +86,13 @@ class BaseFunctionalTest(common.SavepointCase): self.assertEqual(expected, real, 'Invalid number of notification for %s: %s instead of %s' % (partner.name, real, expected)) if partner_notif: - self.assertTrue(all(n.is_email == (notif_type == 'email') for n in partner_notif)) + self.assertTrue(all(n.notification_type == notif_type for n in partner_notif)) self.assertTrue(all(n.is_read == (notif_read == 'read') for n in partner_notif), 'Invalid read status for %s' % partner.name) # for simplification, limitate to single message asserts if hasattr(self, 'assertEmails') and len(new_messages) == 1: - self.assertEmails(new_messages.author_id, new_notifications.filtered(lambda n: n.is_email).mapped('res_partner_id')) + self.assertEmails(new_messages.author_id, new_notifications.filtered(lambda n: n.notification_type == 'email').mapped('res_partner_id')) def assertBusNotification(self, channels, message_items=None, init=True): """ Check for bus notifications. Basic check is about used channels. diff --git a/addons/test_mail/tests/test_mail_race.py b/addons/test_mail/tests/test_mail_race.py index 943a1b30922..310051a7d68 100644 --- a/addons/test_mail/tests/test_mail_race.py +++ b/addons/test_mail/tests/test_mail_race.py @@ -1,13 +1,14 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from contextlib import closing import psycopg2 + from odoo import api from odoo.addons.test_mail.tests import common as mail_common from odoo.tests import common from odoo.tools import mute_logger + class TestMailRace(common.TransactionCase, mail_common.MockEmails): @mute_logger('odoo.addons.mail.models.mail_mail') @@ -26,15 +27,15 @@ class TestMailRace(common.TransactionCase, mail_common.MockEmails): 'subject': 'S', 'body': 'B', 'subtype_id': self.ref('mail.mt_comment'), - 'needaction_partner_ids': [(6, 0, [self.partner.id])], + 'notification_ids': [(0, 0, { + 'res_partner_id': self.partner.id, + 'mail_id': mail.id, + 'notification_type': 'email', + 'is_read': True, + 'notification_status': 'ready', + })], }) notif = self.env['mail.notification'].search([('res_partner_id', '=', self.partner.id)]) - notif.write({ - 'mail_id': mail.id, - 'is_email': True, - 'is_read': True, - 'email_status': 'ready', - }) # we need to commit transaction or cr will keep the lock on notif self.cr.commit() @@ -46,7 +47,7 @@ class TestMailRace(common.TransactionCase, mail_common.MockEmails): with this.registry.cursor() as cr, mute_logger('odoo.sql_db'): try: # try ro aquire lock (no wait) on notification (should fail) - cr.execute("SELECT email_status FROM mail_message_res_partner_needaction_rel WHERE id = %s FOR UPDATE NOWAIT", [notif.id]) + cr.execute("SELECT notification_status FROM mail_message_res_partner_needaction_rel WHERE id = %s FOR UPDATE NOWAIT", [notif.id]) except psycopg2.OperationalError: # record already locked by send, all good bounce_deferred.append(True) @@ -55,14 +56,14 @@ class TestMailRace(common.TransactionCase, mail_common.MockEmails): # Only here to simulate the initial use case # If the record is lock, this line would create a deadlock since we are in the same thread # In practice, the update will wait the end of the send() transaction and set the notif as bounce, as expeced - cr.execute("UPDATE mail_message_res_partner_needaction_rel SET email_status='bounce' WHERE id = %s", [notif.id]) + cr.execute("UPDATE mail_message_res_partner_needaction_rel SET notification_status='bounce' WHERE id = %s", [notif.id]) return message['Message-Id'] self.env['ir.mail_server']._patch_method('send_email', send_email) mail.send() self.assertTrue(bounce_deferred, "The bounce should have been deferred") - self.assertEqual(notif.email_status, 'sent') + self.assertEqual(notif.notification_status, 'sent') # some cleaning since we commited the cr notif.unlink() diff --git a/addons/test_mail/tests/test_mail_resend.py b/addons/test_mail/tests/test_mail_resend.py index e14a5a8784f..a2c94ae65e1 100644 --- a/addons/test_mail/tests/test_mail_resend.py +++ b/addons/test_mail/tests/test_mail_resend.py @@ -50,7 +50,7 @@ class TestMailResend(common.BaseFunctionalTest, common.MockEmails): def assertNotifStates(self, states, message):
notif = self.env['mail.notification'].search([('mail_message_id', '=', message.id)], order="res_partner_id asc")
- self.assertEquals(tuple(notif.mapped('email_status')), states)
+ self.assertEquals(tuple(notif.mapped('notification_status')), states)
return notif
def assertBusMessage(self, partners):
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py index 882f1c48b13..666728d53dd 100644 --- a/addons/test_mail/tests/test_performance.py +++ b/addons/test_mail/tests/test_performance.py @@ -200,7 +200,7 @@ class TestAdvMailPerformance(BaseMailPerformance): def test_message_assignation_email(self): self.user_test.write({'notification_type': 'email'}) record = self.env['mail.test.track'].create({'name': 'Test'}) - with self.assertQueryCount(__system__=55, emp=58): # com runbot: 55 - 58 // test_mail only: 55 - 58 + with self.assertQueryCount(__system__=52, emp=54): # com runbot: 52 - 54 // test_mail only: 52 - 54 record.write({ 'user_id': self.user_test.id, }) @@ -209,7 +209,7 @@ class TestAdvMailPerformance(BaseMailPerformance): @warmup def test_message_assignation_inbox(self): record = self.env['mail.test.track'].create({'name': 'Test'}) - with self.assertQueryCount(__system__=33, emp=39): # test_mail only: 33 - 39 + with self.assertQueryCount(__system__=31, emp=36): # test_mail only: 31 - 36 record.write({ 'user_id': self.user_test.id, }) @@ -253,7 +253,7 @@ class TestAdvMailPerformance(BaseMailPerformance): def test_message_post_one_email_notification(self): record = self.env['mail.test.simple'].create({'name': 'Test'}) - with self.assertQueryCount(__system__=47, emp=51): # com runbot: 47 - 51 // test_mail only: 47 - 51 + with self.assertQueryCount(__system__=44, emp=47): # com runbot: 44 - 47 // test_mail only: 44 - 47 record.message_post( body='<p>Test Post Performances with an email ping</p>', partner_ids=self.customer.ids, @@ -265,7 +265,7 @@ class TestAdvMailPerformance(BaseMailPerformance): def test_message_post_one_inbox_notification(self): record = self.env['mail.test.simple'].create({'name': 'Test'}) - with self.assertQueryCount(__system__=30, emp=36): # com runbot 30 - 36 // test_mail only: 30 - 36 + with self.assertQueryCount(__system__=28, emp=33): # com runbot 28 - 33 // test_mail only: 28 - 33 record.message_post( body='<p>Test Post Performances with an inbox ping</p>', partner_ids=self.user_test.partner_id.ids, @@ -373,7 +373,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): self.umbrella.message_subscribe(self.user_portal.partner_id.ids) record = self.umbrella.with_user(self.env.user) - with self.assertQueryCount(__system__=78, emp=82): # com runbot: 78 - 82 // test_mail only: 78 - 82 + with self.assertQueryCount(__system__=82, emp=85): # com runbot: 82 - 85 // test_mail only: 82 - 85 record.message_post( body='<p>Test Post Performances</p>', message_type='comment', @@ -390,7 +390,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): record = self.umbrella.with_user(self.env.user) template_id = self.env.ref('test_mail.mail_test_tpl').id - with self.assertQueryCount(__system__=93, emp=99): # com runbot: 93 - 99 // test_mail only: 93 - 99 + with self.assertQueryCount(__system__=98, emp=103): # com runbot: 98 - 103 // test_mail only: 98 - 103 record.message_post_with_template(template_id, message_type='comment', composition_mode='comment') self.assertEqual(record.message_ids[0].body, '<p>Adding stuff on %s</p>' % record.name) @@ -459,7 +459,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): 'user_id': self.env.uid, }) self.assertEqual(rec.message_partner_ids, self.partners | self.env.user.partner_id) - with self.assertQueryCount(__system__=54, emp=57): # com runbot: 54 - 57 // test_mail only: 54 - 57 + with self.assertQueryCount(__system__=51, emp=53): # com runbot: 51 - 53 // test_mail only: 51 - 53 rec.write({'user_id': self.user_portal.id}) self.assertEqual(rec.message_partner_ids, self.partners | self.env.user.partner_id | self.user_portal.partner_id) # write tracking message @@ -479,7 +479,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): customer_id = self.customer.id user_id = self.user_portal.id - with self.assertQueryCount(__system__=145, emp=149): # com runbot: 145 - 149 // test_mail only: 145 - 149 + with self.assertQueryCount(__system__=146, emp=149): # com runbot: 146 - 149 // test_mail only: 146 - 149 rec = self.env['mail.test.full'].create({ 'name': 'Test', 'umbrella_id': umbrella_id, @@ -506,7 +506,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): }) self.assertEqual(rec.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id) self.assertEqual(len(rec.message_ids), 1) - with self.assertQueryCount(__system__=95, emp=100): # com runbot: 95 - 100 // test_mail only: 95 - 100 + with self.assertQueryCount(__system__=99, emp=103): # com runbot: 99 -103 // test_mail only: 99 - 103 rec.write({ 'name': 'Test2', 'umbrella_id': self.umbrella.id, @@ -542,7 +542,7 @@ class TestHeavyMailPerformance(BaseMailPerformance): }) self.assertEqual(rec.message_partner_ids, self.user_portal.partner_id | self.env.user.partner_id) - with self.assertQueryCount(__system__=100, emp=105): # test_mail only: 100 - 105 + with self.assertQueryCount(__system__=104, emp=108): # test_mail only: 104 - 108 rec.write({ 'name': 'Test2', 'umbrella_id': umbrella_id, @@ -704,14 +704,14 @@ class TestMailPerformancePost(BaseMailPerformance): partner_ids = [self.user_inbox.partner_id.id, self.user_email.partner_id.id, self.partner.id] channel_ids = [self.channel_inbox.id, self.channel_email.id] record = self.record.with_user(self.env.user) - attachements = [ # not linear on number of attachements + attachements = [ # not linear on number of attachements ('attach tuple 1', "attachement tupple content 1"), ('attach tuple 2', "attachement tupple content 2", {'cid': 'cid1'}), ('attach tuple 3', "attachement tupple content 3", {'cid': 'cid2'}), ] - self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals) #-> 163-> 165 query + self.attachements = self.env['ir.attachment'].with_user(self.env.user).create(self.vals) attachement_ids = self.attachements.ids - with self.assertQueryCount(emp=175): # com runbot 154 // test_mail only: 133 + with self.assertQueryCount(emp=175): # com runbot 154 // test_mail only: 132 self.cr.sql_log = self.warm and self.cr.sql_log_count record.with_context({}).message_post( body='<p>Test body <img src="cid:cid1"> <img src="cid:cid2"></p>', diff --git a/addons/website_blog/models/website_blog.py b/addons/website_blog/models/website_blog.py index 6ddb0cb06a6..da71c1b3194 100644 --- a/addons/website_blog/models/website_blog.py +++ b/addons/website_blog/models/website_blog.py @@ -259,14 +259,13 @@ class BlogPost(models.Model): return groups @api.multi - def _notify_customize_recipients(self, message, msg_vals): + def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs): """ Override to avoid keeping all notified recipients of a comment. We avoid tracking needaction on post comments. Only emails should be sufficient. """ - msg_type = msg_vals.get('message_type') or message.message_type - if msg_type == 'comment': - return {'needaction_partner_ids': []} - return super(BlogPost, self)._notify_customize_recipients(message, msg_vals) + if msg_vals.get('message_type', message.message_type) == 'comment': + return + return super(BlogPost, self)._notify_record_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs) def _default_website_meta(self): res = super(BlogPost, self)._default_website_meta() diff --git a/addons/website_forum/models/forum.py b/addons/website_forum/models/forum.py index e668fc173ee..5a98dc12a41 100644 --- a/addons/website_forum/models/forum.py +++ b/addons/website_forum/models/forum.py @@ -830,14 +830,13 @@ class Post(models.Model): return super(Post, self).message_post(message_type=message_type, **kwargs) @api.multi - def _notify_customize_recipients(self, message, msg_vals): + def _notify_record_by_inbox(self, message, recipients_data, msg_vals=False, **kwargs): """ Override to avoid keeping all notified recipients of a comment. We avoid tracking needaction on post comments. Only emails should be sufficient. """ - msg_type = msg_vals.get('message_type') or message.message_type - if msg_type == 'comment': - return {'needaction_partner_ids': [], 'partner_ids': []} - return super(Post, self)._notify_customize_recipients(message, msg_vals) + if msg_vals.get('message_type', message.message_type) == 'comment': + return + return super(Post, self)._notify_record_by_inbox(message, recipients_data, msg_vals=msg_vals, **kwargs) class PostReason(models.Model): |
