1# -*- coding: utf-8 -*- 2# Part of Odoo. See LICENSE file for full copyright and licensing details. 3 4from dateutil.relativedelta import relativedelta 5 6from odoo import api, fields, models 7from odoo.exceptions import AccessError 8from odoo.tools.translate import _ 9 10 11class MailNotification(models.Model): 12 _name = 'mail.notification' 13 _table = 'mail_message_res_partner_needaction_rel' 14 _rec_name = 'res_partner_id' 15 _log_access = False 16 _description = 'Message Notifications' 17 18 # origin 19 mail_message_id = fields.Many2one('mail.message', 'Message', index=True, ondelete='cascade', required=True) 20 mail_id = fields.Many2one('mail.mail', 'Mail', index=True, help='Optional mail_mail ID. Used mainly to optimize searches.') 21 # recipient 22 res_partner_id = fields.Many2one('res.partner', 'Recipient', index=True, ondelete='cascade') 23 # status 24 notification_type = fields.Selection([ 25 ('inbox', 'Inbox'), ('email', 'Email') 26 ], string='Notification Type', default='inbox', index=True, required=True) 27 notification_status = fields.Selection([ 28 ('ready', 'Ready to Send'), 29 ('sent', 'Sent'), 30 ('bounce', 'Bounced'), 31 ('exception', 'Exception'), 32 ('canceled', 'Canceled') 33 ], string='Status', default='ready', index=True) 34 is_read = fields.Boolean('Is Read', index=True) 35 read_date = fields.Datetime('Read Date', copy=False) 36 failure_type = fields.Selection(selection=[ 37 ("SMTP", "Connection failed (outgoing mail server problem)"), 38 ("RECIPIENT", "Invalid email address"), 39 ("BOUNCE", "Email address rejected by destination"), 40 ("UNKNOWN", "Unknown error"), 41 ], string='Failure type') 42 failure_reason = fields.Text('Failure reason', copy=False) 43 44 _sql_constraints = [ 45 # email notification;: partner is required 46 ('notification_partner_required', 47 "CHECK(notification_type NOT IN ('email', 'inbox') OR res_partner_id IS NOT NULL)", 48 'Customer is required for inbox / email notification'), 49 ] 50 51 def init(self): 52 self._cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = %s', 53 ('mail_notification_res_partner_id_is_read_notification_status_mail_message_id',)) 54 if not self._cr.fetchone(): 55 self._cr.execute(""" 56 CREATE INDEX mail_notification_res_partner_id_is_read_notification_status_mail_message_id 57 ON mail_message_res_partner_needaction_rel (res_partner_id, is_read, notification_status, mail_message_id) 58 """) 59 60 @api.model_create_multi 61 def create(self, vals_list): 62 messages = self.env['mail.message'].browse(vals['mail_message_id'] for vals in vals_list) 63 messages.check_access_rights('read') 64 messages.check_access_rule('read') 65 for vals in vals_list: 66 if vals.get('is_read'): 67 vals['read_date'] = fields.Datetime.now() 68 return super(MailNotification, self).create(vals_list) 69 70 def write(self, vals): 71 if ('mail_message_id' in vals or 'res_partner_id' in vals) and not self.env.is_admin(): 72 raise AccessError(_("Can not update the message or recipient of a notification.")) 73 if vals.get('is_read'): 74 vals['read_date'] = fields.Datetime.now() 75 return super(MailNotification, self).write(vals) 76 77 def format_failure_reason(self): 78 self.ensure_one() 79 if self.failure_type != 'UNKNOWN': 80 return dict(type(self).failure_type.selection).get(self.failure_type, _('No Error')) 81 else: 82 return _("Unknown error") + ": %s" % (self.failure_reason or '') 83 84 @api.model 85 def _gc_notifications(self, max_age_days=180): 86 domain = [ 87 ('is_read', '=', True), 88 ('read_date', '<', fields.Datetime.now() - relativedelta(days=max_age_days)), 89 ('res_partner_id.partner_share', '=', False), 90 ('notification_status', 'in', ('sent', 'canceled')) 91 ] 92 return self.search(domain).unlink() 93 94 def _filtered_for_web_client(self): 95 """Returns only the notifications to show on the web client.""" 96 return self.filtered(lambda n: 97 n.notification_type != 'inbox' and 98 (n.notification_status in ['bounce', 'exception', 'canceled'] or n.res_partner_id.partner_share) 99 ) 100 101 def _notification_format(self): 102 """Returns the current notifications in the format expected by the web 103 client.""" 104 return [{ 105 'id': notif.id, 106 'notification_type': notif.notification_type, 107 'notification_status': notif.notification_status, 108 'failure_type': notif.failure_type, 109 'res_partner_id': [notif.res_partner_id.id, notif.res_partner_id.display_name] if notif.res_partner_id else False, 110 } for notif in self] 111