Files
odoo_source/addons/mail/models/mail_notification.py
T
Pierre-Yves Dufays aa32818c30 [IMP] [test_]mail[_full]: improves message recipients visibility
All the recipients of message were not always displayed in discuss and some
users were wondering if the message has been sent to everybody concerned.
This solves the problem by allowing to specify whether to display all
recipients based on the message sub type and defining for which sub type it
must.

Technical note:
- The added query count in some tests is due to the fact that the inbox
notification can now be displayed on the client. Before they were discarded
right away. Now additional check must be done. The added query is due to the
test self.res_partner_id.partner_share which is now also done on inbox
notifications (This has been determined by testing locally for all tests except
one: TestMailHeavyPerformancePost.test_complete_message_post, but it is likely
for the same reason).
- A speed optimization could be to add mail_notification.display as stored
computed field in order to avoid that extra read but it would add a little more
needed storage.

Task-2429708

closes odoo/odoo#90203

Related: odoo/upgrade#3487
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2022-06-01 09:57:47 +02:00

135 lines
5.9 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from dateutil.relativedelta import relativedelta
from odoo import api, fields, models
from odoo.exceptions import AccessError
from odoo.tools.translate import _
class MailNotification(models.Model):
_name = 'mail.notification'
_table = 'mail_notification'
_rec_name = 'res_partner_id'
_log_access = False
_description = 'Message Notifications'
# origin
author_id = fields.Many2one('res.partner', 'Author', ondelete='set null')
mail_message_id = fields.Many2one('mail.message', 'Message', index=True, ondelete='cascade', required=True)
mail_mail_id = fields.Many2one('mail.mail', 'Mail', index=True, help='Optional mail_mail ID. Used mainly to optimize searches.')
# recipient
res_partner_id = fields.Many2one('res.partner', 'Recipient', index=True, ondelete='cascade')
# status
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'),
('exception', 'Exception'),
('canceled', 'Canceled')
], string='Status', default='ready', index=True)
is_read = fields.Boolean('Is Read', index=True)
read_date = fields.Datetime('Read Date', copy=False)
failure_type = fields.Selection(selection=[
# generic
("unknown", "Unknown error"),
# mail
("mail_email_invalid", "Invalid email address"),
("mail_email_missing", "Missing email address"),
("mail_smtp", "Connection failed (outgoing mail server problem)"),
], string='Failure type')
failure_reason = fields.Text('Failure reason', copy=False)
_sql_constraints = [
# email notification: partner is required
('notification_partner_required',
"CHECK(notification_type NOT IN ('email', 'inbox') OR res_partner_id IS NOT NULL)",
'Customer is required for inbox / email notification'),
]
# ------------------------------------------------------------
# CRUD
# ------------------------------------------------------------
def init(self):
self._cr.execute("""
CREATE INDEX IF NOT EXISTS mail_notification_res_partner_id_is_read_notification_status_mail_message_id
ON mail_notification (res_partner_id, is_read, notification_status, mail_message_id);
CREATE INDEX IF NOT EXISTS mail_notification_author_id_notification_status_failure
ON mail_notification (author_id, notification_status)
WHERE notification_status IN ('bounce', 'exception');
""")
self.env.cr.execute(
"""CREATE UNIQUE INDEX IF NOT EXISTS unique_mail_message_id_res_partner_id_if_set
ON %s (mail_message_id, res_partner_id)
WHERE res_partner_id IS NOT NULL""" % self._table
)
@api.model_create_multi
def create(self, vals_list):
messages = self.env['mail.message'].browse(vals['mail_message_id'] for vals in vals_list)
messages.check_access_rights('read')
messages.check_access_rule('read')
for vals in vals_list:
if vals.get('is_read'):
vals['read_date'] = fields.Datetime.now()
return super(MailNotification, self).create(vals_list)
def write(self, vals):
if ('mail_message_id' in vals or 'res_partner_id' in vals) and not self.env.is_admin():
raise AccessError(_("Can not update the message or recipient of a notification."))
if vals.get('is_read'):
vals['read_date'] = fields.Datetime.now()
return super(MailNotification, self).write(vals)
@api.model
def _gc_notifications(self, max_age_days=180):
domain = [
('is_read', '=', True),
('read_date', '<', fields.Datetime.now() - relativedelta(days=max_age_days)),
('res_partner_id.partner_share', '=', False),
('notification_status', 'in', ('sent', 'canceled'))
]
return self.search(domain).unlink()
# ------------------------------------------------------------
# TOOLS
# ------------------------------------------------------------
def format_failure_reason(self):
self.ensure_one()
if self.failure_type != 'unknown':
return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error'))
else:
return _("Unknown error") + ": %s" % (self.failure_reason or '')
# ------------------------------------------------------------
# DISCUSS
# ------------------------------------------------------------
def _filtered_for_web_client(self):
"""Returns only the notifications to show on the web client."""
def _filter_unimportant_notifications(notif):
if notif.notification_status in ['bounce', 'exception', 'canceled'] \
or notif.res_partner_id.partner_share:
return True
subtype = notif.mail_message_id.subtype_id
return not subtype or subtype.track_recipients
return self.filtered(_filter_unimportant_notifications)
def _notification_format(self):
"""Returns the current notifications in the format expected by the web
client."""
return [{
'id': notif.id,
'notification_type': notif.notification_type,
'notification_status': notif.notification_status,
'failure_type': notif.failure_type,
'res_partner_id': [notif.res_partner_id.id, notif.res_partner_id.display_name] if notif.res_partner_id else False,
} for notif in self]