diff --git a/mail_tracking/__manifest__.py b/mail_tracking/__manifest__.py index 5914214..f088398 100644 --- a/mail_tracking/__manifest__.py +++ b/mail_tracking/__manifest__.py @@ -7,14 +7,14 @@ { "name": "Email tracking", "summary": "Email tracking system for all mails sent", - "version": "12.0.2.0.1", + "version": "13.0.1.0.0", "category": "Social Network", "website": "http://github.com/OCA/social", "author": ("Tecnativa, " "Odoo Community Association (OCA)"), "license": "AGPL-3", "application": False, "installable": True, - "depends": ["decimal_precision", "mail"], + "depends": ["mail"], "data": [ "data/tracking_data.xml", "security/mail_tracking_email_security.xml", diff --git a/mail_tracking/hooks.py b/mail_tracking/hooks.py index 5693ebc..6605482 100644 --- a/mail_tracking/hooks.py +++ b/mail_tracking/hooks.py @@ -5,20 +5,11 @@ import logging from psycopg2.extensions import AsIs +from odoo.tools import column_exists + _logger = logging.getLogger(__name__) -def column_exists(cr, table, column): - cr.execute( - """ - SELECT column_name - FROM information_schema.columns - WHERE table_name = %s AND column_name = %s""", - (table, column), - ) - return bool(cr.fetchall()) - - def column_add_with_value(cr, table, column, field_type, value): if not column_exists(cr, table, column): cr.execute( diff --git a/mail_tracking/migrations/12.0.2.0.0/post-migrate.py b/mail_tracking/migrations/12.0.2.0.0/post-migrate.py deleted file mode 100644 index 6b2993c..0000000 --- a/mail_tracking/migrations/12.0.2.0.0/post-migrate.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2019 Alexandre Díaz -# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). - - -from openupgradelib.openupgrade import migrate - - -@migrate() -def migrate(env, version): - cr = env.cr - cr.execute("UPDATE mail_tracking_email SET token = NULL") diff --git a/mail_tracking/models/mail_bounced_mixin.py b/mail_tracking/models/mail_bounced_mixin.py index d63b445..488831a 100644 --- a/mail_tracking/models/mail_bounced_mixin.py +++ b/mail_tracking/models/mail_bounced_mixin.py @@ -12,11 +12,10 @@ class MailBouncedMixin(models.AbstractModel): _name = "mail.bounced.mixin" _description = "Mail bounced mixin" - _primary_email = ["email"] + _primary_email = "email" email_bounced = fields.Boolean(index=True) - @api.multi def email_bounced_set(self, tracking_emails, reason, event=None): """Inherit this method to make any other actions to the model that inherit the mixin""" @@ -28,7 +27,7 @@ class MailBouncedMixin(models.AbstractModel): return partners.write({"email_bounced": True}) def write(self, vals): - [email_field] = self._primary_email + email_field = self._primary_email if email_field not in vals: return super().write(vals) email = vals[email_field].lower() if vals[email_field] else False diff --git a/mail_tracking/models/mail_message.py b/mail_tracking/models/mail_message.py index f709c33..c0c818a 100644 --- a/mail_tracking/models/mail_message.py +++ b/mail_tracking/models/mail_message.py @@ -132,8 +132,8 @@ class MailMessage(models.Model): # Search all recipients for this message if message.partner_ids: partners |= message.partner_ids - if message.needaction_partner_ids: - partners |= message.needaction_partner_ids + if message.notified_partner_ids: + partners |= message.notified_partner_ids # Remove recipients already included partners -= partners_already tracking_unkown_values = { @@ -179,7 +179,6 @@ class MailMessage(models.Model): message_dict.update(tracking_statuses[mail_message_id]) return res - @api.multi def _prepare_dict_failed_message(self): """Preare values to be used by the chatter widget""" self.ensure_one() @@ -200,7 +199,6 @@ class MailMessage(models.Model): "failed_recipients": failed_recipients, } - @api.multi def get_failed_messages(self): """Returns the list of failed messages to be used by the failed_messages widget""" @@ -209,7 +207,6 @@ class MailMessage(models.Model): for msg in self.sorted("date", reverse=True) ] - @api.multi def set_need_action_done(self): """Set message tracking action as done diff --git a/mail_tracking/models/mail_resend_message.py b/mail_tracking/models/mail_resend_message.py index 1ba8c19..4c7dad2 100644 --- a/mail_tracking/models/mail_resend_message.py +++ b/mail_tracking/models/mail_resend_message.py @@ -36,7 +36,6 @@ class MailResendMessage(models.TransientModel): rec["partner_ids"].extend(partner_ids) return rec - @api.multi def resend_mail_action(self): for wizard in self: to_send = wizard.partner_ids.filtered("resend").mapped("partner_id") diff --git a/mail_tracking/models/mail_thread.py b/mail_tracking/models/mail_thread.py index 3f6525f..3682ea3 100644 --- a/mail_tracking/models/mail_thread.py +++ b/mail_tracking/models/mail_thread.py @@ -29,7 +29,6 @@ class MailThread(models.AbstractModel): ("mail_tracking_ids.state", "in", list(failed_states)), ] - @api.multi @api.returns("self", lambda value: value.id) def message_post(self, *args, **kwargs): """Adds CC recipient to the message. @@ -43,13 +42,12 @@ class MailThread(models.AbstractModel): new_message.sudo().write({"email_cc": email_cc}) return new_message - @api.multi - def message_get_suggested_recipients(self): + def _message_get_suggested_recipients(self): """Adds email Cc recipients as suggested recipients. If the recipient has a res.partner, use it. """ - res = super().message_get_suggested_recipients() + res = super()._message_get_suggested_recipients() ResPartnerObj = self.env["res.partner"] email_cc_formated_list = [] for record in self: @@ -65,7 +63,7 @@ class MailThread(models.AbstractModel): if not partner_id: record._message_add_suggested_recipient(res, email=cc, reason=_("Cc")) else: - partner = ResPartnerObj.browse(partner_id, self._prefetch) + partner = ResPartnerObj.browse(partner_id) record._message_add_suggested_recipient( res, partner=partner, reason=_("Cc") ) diff --git a/mail_tracking/models/mail_tracking_email.py b/mail_tracking/models/mail_tracking_email.py index 37eda13..aa7e1c2 100644 --- a/mail_tracking/models/mail_tracking_email.py +++ b/mail_tracking/models/mail_tracking_email.py @@ -8,8 +8,7 @@ import urllib.parse import uuid from datetime import datetime -from odoo import models, api, fields, tools -import odoo.addons.decimal_precision as dp +from odoo import api, fields, models, tools _logger = logging.getLogger(__name__) @@ -36,8 +35,8 @@ class MailTrackingEmail(models.Model): compute="_compute_tracking_display_name", ) timestamp = fields.Float( - string='UTC timestamp', readonly=True, - digits=dp.get_precision('MailTracking Timestamp')) + string="UTC timestamp", readonly=True, digits="MailTracking Timestamp" + ) time = fields.Datetime(string="Time", readonly=True, index=True) date = fields.Date( string="Date", readonly=True, compute="_compute_date", store=True @@ -125,7 +124,6 @@ class MailTrackingEmail(models.Model): ).write({"mail_tracking_needs_action": True}) return records - @api.multi def write(self, vals): super().write(vals) state = vals.get("state") @@ -255,7 +253,6 @@ class MailTrackingEmail(models.Model): % {"url": track_url, "tracking_email_id": self.id} ) - @api.multi def _partners_email_bounced_set(self, reason, event=None): recipients = [] if event and event.recipient_address: @@ -267,7 +264,6 @@ class MailTrackingEmail(models.Model): [("email", "=ilike", recipient)] ).email_bounced_set(self, reason, event=event) - @api.multi def smtp_error(self, mail_server, smtp_server, exception): values = {"state": "error"} IrMailServer = self.env["ir.mail_server"] @@ -292,7 +288,6 @@ class MailTrackingEmail(models.Model): self.sudo()._partners_email_bounced_set("error") self.sudo().write(values) - @api.multi def tracking_img_add(self, email): self.ensure_one() tracking_url = self._get_mail_tracking_img() @@ -311,20 +306,18 @@ class MailTrackingEmail(models.Model): if not self.mail_message_id.exists(): # pragma: no cover return True mail_message = self.mail_message_id - partners = ( - mail_message.needaction_partner_ids | mail_message.partner_ids) - if (self.partner_id and self.partner_id not in partners): + partners = mail_message.notified_partner_ids | mail_message.partner_ids + if self.partner_id and self.partner_id not in partners: # If mail_message haven't tracking partner, then # add it in order to see his tracking status in chatter if mail_message.subtype_id: - mail_message.sudo().write({ - 'needaction_partner_ids': [(4, self.partner_id.id)], - }) + mail_message.sudo().write( + {"notified_partner_ids": [(4, self.partner_id.id)]} + ) else: mail_message.sudo().write({"partner_ids": [(4, self.partner_id.id)]}) return True - @api.multi def _tracking_sent_prepare(self, mail_server, smtp_server, message, message_id): self.ensure_one() ts = time.time() @@ -368,7 +361,6 @@ class MailTrackingEmail(models.Model): concurrent_event_ids = m_event.search(domain) return concurrent_event_ids - @api.multi def event_create(self, event_type, metadata): event_ids = self.env["mail.tracking.event"] for tracking_email in self: diff --git a/mail_tracking/models/mail_tracking_event.py b/mail_tracking/models/mail_tracking_event.py index b7f91df..e6af3e1 100644 --- a/mail_tracking/models/mail_tracking_event.py +++ b/mail_tracking/models/mail_tracking_event.py @@ -5,8 +5,7 @@ import re import time from datetime import datetime -from odoo import models, api, fields -import odoo.addons.decimal_precision as dp +from odoo import api, fields, models class MailTrackingEvent(models.Model): @@ -24,8 +23,8 @@ class MailTrackingEvent(models.Model): index=True, ) timestamp = fields.Float( - string='UTC timestamp', readonly=True, - digits=dp.get_precision('MailTracking Timestamp')) + string="UTC timestamp", readonly=True, digits="MailTracking Timestamp" + ) time = fields.Datetime(string="Time", readonly=True) date = fields.Date( string="Date", readonly=True, compute="_compute_date", store=True @@ -81,7 +80,6 @@ class MailTrackingEvent(models.Model): else: email.recipient_address = False - @api.multi @api.depends("time") def _compute_date(self): for email in self: diff --git a/mail_tracking/models/res_partner.py b/mail_tracking/models/res_partner.py index 9a72af8..1133a79 100644 --- a/mail_tracking/models/res_partner.py +++ b/mail_tracking/models/res_partner.py @@ -11,23 +11,20 @@ class ResPartner(models.Model): # tracking_emails_count and email_score are non-store fields in order # to improve performance tracking_emails_count = fields.Integer( - compute="_compute_tracking_emails_count", readonly=True + compute="_compute_email_score_and_count", readonly=True ) - email_score = fields.Float(compute="_compute_email_score", readonly=True) + email_score = fields.Float(compute="_compute_email_score_and_count", readonly=True) @api.depends("email") - def _compute_email_score(self): - for partner in self.filtered('email'): - partner.email_score = self.env['mail.tracking.email'].\ - email_score_from_email(partner.email) - - @api.multi - @api.depends('email') - def _compute_tracking_emails_count(self): - for partner in self: - count = 0 - if partner.email: - count = self.env["mail.tracking.email"].search_count( - [("recipient_address", "=", partner.email.lower())] - ) - partner.tracking_emails_count = count + def _compute_email_score_and_count(self): + partners_mail = self.filtered("email") + mail_tracking_obj = self.env["mail.tracking.email"] + for partner in partners_mail: + partner.email_score = self.env[ + "mail.tracking.email" + ].email_score_from_email(partner.email) + partner.tracking_emails_count = mail_tracking_obj.search_count( + [("recipient_address", "=", partner.email.lower())] + ) + partners_no_mail = self - partners_mail + partners_no_mail.update({"email_score": 50.0, "tracking_emails_count": 0}) diff --git a/mail_tracking/static/src/js/failed_message/discuss.js b/mail_tracking/static/src/js/failed_message/discuss.js index 847443d..6deb131 100644 --- a/mail_tracking/static/src/js/failed_message/discuss.js +++ b/mail_tracking/static/src/js/failed_message/discuss.js @@ -169,9 +169,9 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) { /** * Render 'failed' mailbox menu entry in Discuss + * - Initial render * - * @private - * @returns {jQueryElementt} + * @override */ _renderSidebar: function () { var $sidebar = this._super.apply(this, arguments); @@ -184,6 +184,19 @@ odoo.define('mail_tracking.FailedMessageDiscuss', function (require) { return $sidebar; }, + /** + * Render 'failed' mailbox menu entry in Discuss + * - Update status render (not called if the mailbox is empty) + * + * @override + */ + _renderSidebarMailboxes: function () { + this._super.apply(this, arguments); + this.$('.o_mail_discuss_sidebar_mailboxes').append( + QWeb.render('mail_tracking.SidebarFailed', + this._sidebarQWebParams())); + }, + /** * Overrides to listen click on 'Set all as reviewed' button * diff --git a/mail_tracking/static/src/xml/failed_message/discuss.xml b/mail_tracking/static/src/xml/failed_message/discuss.xml index 45fb1f5..8f56388 100644 --- a/mail_tracking/static/src/xml/failed_message/discuss.xml +++ b/mail_tracking/static/src/xml/failed_message/discuss.xml @@ -8,13 +8,13 @@ -
- Failed - - -
-
+
+ Failed + + +
+ diff --git a/mail_tracking/tests/test_mail_tracking.py b/mail_tracking/tests/test_mail_tracking.py index 208f4cd..dd84926 100644 --- a/mail_tracking/tests/test_mail_tracking.py +++ b/mail_tracking/tests/test_mail_tracking.py @@ -77,17 +77,19 @@ class TestMailTracking(TransactionCase): def test_message_post(self): # This message will generate a notification for recipient - message = self.env['mail.message'].create({ - 'subject': 'Message test', - 'author_id': self.sender.id, - 'email_from': self.sender.email, - 'message_type': 'comment', - 'model': 'res.partner', - 'res_id': self.recipient.id, - 'partner_ids': [(4, self.recipient.id)], - 'body': '

This is a test message

', - }) - message._notify(message, {}, force_send=True) + message = self.env["mail.message"].create( + { + "subject": "Message test", + "author_id": self.sender.id, + "email_from": self.sender.email, + "message_type": "comment", + "model": "res.partner", + "res_id": self.recipient.id, + "partner_ids": [(4, self.recipient.id)], + "body": "

This is a test message

", + } + ) + message._moderate_accept() # Search tracking created tracking_email = self.env["mail.tracking.email"].search( [ @@ -102,7 +104,7 @@ class TestMailTracking(TransactionCase): message_dict = message.message_format()[0] self.assertTrue(len(message_dict["partner_ids"]) > 0) # First partner is recipient - partner_id = message_dict["partner_ids"][0][0] + partner_id = message_dict["partner_ids"][0] self.assertEqual(partner_id, self.recipient.id) status = message_dict["partner_trackings"][0] # Tracking status must be sent and @@ -137,7 +139,7 @@ class TestMailTracking(TransactionCase): "body": "

This is a test message

", } ) - message._notify(message, {}, force_send=True) + message._moderate_accept() # Search tracking created tracking_email = self.env["mail.tracking.email"].search( [ @@ -201,7 +203,7 @@ class TestMailTracking(TransactionCase): "body": "

This is another test message

", } ) - message._notify(message, {}, force_send=True) + message._moderate_accept() recipients = self.recipient._message_get_suggested_recipients() self.assertEqual(len(recipients[self.recipient.id][0]), 3) self._check_partner_trackings(message) @@ -233,6 +235,45 @@ class TestMailTracking(TransactionCase): values = tracking.mail_message_id.get_failed_messages()[0] self.assertEqual(values["author"][0], -1) + def test_resend_failed_message(self): + # This message will generate a notification for recipient + message = self.env["mail.message"].create( + { + "subject": "Message test", + "author_id": self.sender.id, + "email_from": self.sender.email, + "message_type": "comment", + "model": "res.partner", + "res_id": self.recipient.id, + "partner_ids": [(4, self.recipient.id)], + "body": "

This is a test message

", + } + ) + message._moderate_accept() + # Search tracking created + tracking_email = self.env["mail.tracking.email"].search( + [ + ("mail_message_id", "=", message.id), + ("partner_id", "=", self.recipient.id), + ] + ) + # Force error state + tracking_email.state = "error" + # Create resend mail wizard + wizard = ( + self.env["mail.resend.message"] + .sudo() + .with_context({"mail_message_to_resend": message.id}) + .create({}) + ) + # Check failed recipient)s + self.assertTrue(any(wizard.partner_ids)) + self.assertEqual(self.recipient.email, wizard.partner_ids[0].email) + # Resend message + wizard.resend_mail_action() + # Check tracking reset + self.assertFalse(tracking_email.state) + def mail_send(self, recipient): mail = self.env["mail.mail"].create( { diff --git a/mail_tracking/views/mail_tracking_email_view.xml b/mail_tracking/views/mail_tracking_email_view.xml index fc97c76..57a28b3 100644 --- a/mail_tracking/views/mail_tracking_email_view.xml +++ b/mail_tracking/views/mail_tracking_email_view.xml @@ -42,7 +42,10 @@