mirror of https://github.com/OCA/social.git
[IMP] mail_tracking: Buttons, discuss, empty mails
parent
e6d457f8fa
commit
096175532b
|
@ -2,6 +2,31 @@
|
|||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<!-- Message with CC -->
|
||||
<record id="mail_message_cc" model="mail.message">
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.partner_demo" />
|
||||
<field name="message_type">comment</field>
|
||||
<field name="subtype_id" ref="mail.mt_comment" />
|
||||
<field name="email_cc">acc@testmail.com,asusteK@yourcompany.example.com,thinkbig@yourcompany.example.com</field>
|
||||
<field name="mail_tracking_needs_action">1</field>
|
||||
<field name="body"><![CDATA[<p>This is a message with CC</p>]]></field>
|
||||
<field name="email_from">wood.corner26@example.com</field>
|
||||
<field name="author_id" ref="base.res_partner_1" />
|
||||
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||
<field name="subject">Message with CC</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_tracking_email_cc" model="mail.tracking.email">
|
||||
<field name="name">Message with CC</field>
|
||||
<field name="mail_message_id" ref="mail_message_cc" />
|
||||
<field name="partner_id" ref="base.res_partner_1" />
|
||||
<field name="recipient">asusteK@yourcompany.example.com</field>
|
||||
<field name="sender">demo@yourcompany.example.com</field>
|
||||
<field name="state">sent</field>
|
||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<!-- Failed Message A -->
|
||||
<record id="mail_message_failed" model="mail.message">
|
||||
<field name="model">res.partner</field>
|
||||
|
@ -20,8 +45,8 @@
|
|||
<field name="name">Failed Message</field>
|
||||
<field name="mail_message_id" ref="mail_message_failed" />
|
||||
<field name="partner_id" ref="base.res_partner_1" />
|
||||
<field name="recipient">res1@yourcompany.example.com</field>
|
||||
<field name="sender">demo@yourcompany.example.com</field>
|
||||
<field name="recipient">demo@yourcompany.example.com</field>
|
||||
<field name="sender">res1@yourcompany.example.com</field>
|
||||
<field name="state">error</field>
|
||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
@ -44,8 +69,32 @@
|
|||
<field name="name">Failed Message</field>
|
||||
<field name="mail_message_id" ref="mail_message_failed_b" />
|
||||
<field name="partner_id" ref="base.res_partner_10" />
|
||||
<field name="recipient">res10@yourcompany.example.com</field>
|
||||
<field name="sender">demo@yourcompany.example.com</field>
|
||||
<field name="recipient">demo@yourcompany.example.com</field>
|
||||
<field name="sender">res10@yourcompany.example.com</field>
|
||||
<field name="state">error</field>
|
||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
||||
<!-- Failed Message C -->
|
||||
<record id="mail_message_failed_c" model="mail.message">
|
||||
<field name="model">res.partner</field>
|
||||
<field name="res_id" ref="base.partner_demo" />
|
||||
<field name="message_type">comment</field>
|
||||
<field name="subtype_id" ref="mail.mt_comment" />
|
||||
<field name="mail_tracking_needs_action">1</field>
|
||||
<field name="body"><![CDATA[<p>This is another failed message</p>]]></field>
|
||||
<field name="email_from">admin@example.com</field>
|
||||
<field name="author_id" ref="base.partner_root" />
|
||||
<field name="partner_ids" eval="[(6, 0, [ref('base.partner_demo')])]" />
|
||||
<field name="subject">Failed Message</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_tracking_email_failed_c" model="mail.tracking.email">
|
||||
<field name="name">Failed Message</field>
|
||||
<field name="mail_message_id" ref="mail_message_failed_c" />
|
||||
<field name="partner_id" ref="base.partner_root" />
|
||||
<field name="recipient">demo@yourcompany.example.com</field>
|
||||
<field name="sender">admin@example.com</field>
|
||||
<field name="state">error</field>
|
||||
<field name="time" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
|
|
|
@ -12,8 +12,8 @@ class MailComposer(models.TransientModel):
|
|||
|
||||
@api.multi
|
||||
def send_mail(self, auto_commit=False):
|
||||
""" This method marks as reviewed the message when using the 'Retry'
|
||||
option in the mail_failed_message widget"""
|
||||
"""This method marks as reviewed the message when using the 'Retry'
|
||||
option in the mail_failed_message widget"""
|
||||
message = self.env['mail.message'].browse(
|
||||
self._context.get('message_id'))
|
||||
if message.exists():
|
||||
|
@ -29,7 +29,10 @@ class MailComposer(models.TransientModel):
|
|||
|
||||
@api.model
|
||||
def get_record_data(self, values):
|
||||
"""Overwrite 'partner_ids' if enable 'hide_followers' on mail
|
||||
composer"""
|
||||
values = super(MailComposer, self).get_record_data(values)
|
||||
# Only use selected partners without followers
|
||||
if self._context.get('default_hide_followers', False):
|
||||
values['partner_ids'] = [
|
||||
(6, 0, self._context.get('default_partner_ids', list()))
|
||||
|
|
|
@ -11,6 +11,7 @@ class MailMail(models.Model):
|
|||
_inherit = 'mail.mail'
|
||||
|
||||
def _tracking_email_prepare(self, partner, email):
|
||||
"""Prepare email.tracking.email record values"""
|
||||
ts = time.time()
|
||||
dt = datetime.utcfromtimestamp(ts)
|
||||
email_to_list = email.get('email_to', [])
|
||||
|
@ -27,6 +28,8 @@ class MailMail(models.Model):
|
|||
}
|
||||
|
||||
def send_get_email_dict(self, partner=None):
|
||||
"""Creates the mail.tracking.email record and adds the image tracking
|
||||
to the email"""
|
||||
email = super(MailMail, self).send_get_email_dict(partner=partner)
|
||||
vals = self._tracking_email_prepare(partner, email)
|
||||
tracking_email = self.env['mail.tracking.email'].sudo().create(vals)
|
||||
|
|
|
@ -22,12 +22,28 @@ class MailMessage(models.Model):
|
|||
" to filter tracking issues",
|
||||
default=False,
|
||||
)
|
||||
is_failed_message = fields.Boolean(compute="_compute_is_failed_message")
|
||||
|
||||
@api.model
|
||||
def get_failed_states(self):
|
||||
"""The 'failed' states of the message"""
|
||||
return {'error', 'rejected', 'spam', 'bounced', 'soft-bounced'}
|
||||
|
||||
@api.depends('mail_tracking_needs_action', 'author_id', 'partner_ids',
|
||||
'mail_tracking_ids')
|
||||
def _compute_is_failed_message(self):
|
||||
"""Compute 'is_failed_message' field for the active user"""
|
||||
failed_states = self.get_failed_states()
|
||||
for record in self:
|
||||
needs_action = record.mail_tracking_needs_action
|
||||
involves_me = record.env.user.partner_id.id in (
|
||||
record.author_id | record.partner_ids).ids
|
||||
has_failed_trackings = failed_states.intersection(
|
||||
record.mapped("mail_tracking_ids.state"))
|
||||
record.is_failed_message = bool(
|
||||
needs_action and involves_me and has_failed_trackings)
|
||||
|
||||
def _tracking_status_map_get(self):
|
||||
"""Map tracking states to be used in chatter"""
|
||||
return {
|
||||
'False': 'waiting',
|
||||
'error': 'error',
|
||||
|
@ -43,6 +59,7 @@ class MailMessage(models.Model):
|
|||
}
|
||||
|
||||
def _partner_tracking_status_get(self, tracking_email):
|
||||
"""Determine tracking status"""
|
||||
tracking_status_map = self._tracking_status_map_get()
|
||||
status = 'unknown'
|
||||
if tracking_email:
|
||||
|
@ -51,12 +68,23 @@ class MailMessage(models.Model):
|
|||
return status
|
||||
|
||||
def _partner_tracking_status_human_get(self, status):
|
||||
"""Translations for tracking statuses to be used on qweb"""
|
||||
statuses = {'waiting': _('Waiting'), 'error': _('Error'),
|
||||
'sent': _('Sent'), 'delivered': _('Delivered'),
|
||||
'opened': _('Opened'), 'unknown': _('Unknown')}
|
||||
return _("Status: %s") % statuses[status]
|
||||
|
||||
@api.model
|
||||
def _get_error_description(self, tracking):
|
||||
"""Translations for error descriptions to be used on qweb"""
|
||||
descriptions = {
|
||||
'no_recipient': _("The partner doesn't have a defined email"),
|
||||
}
|
||||
return descriptions.get(tracking.error_type,
|
||||
tracking.error_description)
|
||||
|
||||
def tracking_status(self):
|
||||
"""Generates a complete status tracking of the messages by partner"""
|
||||
res = {}
|
||||
for message in self:
|
||||
partner_trackings = []
|
||||
|
@ -79,6 +107,9 @@ class MailMessage(models.Model):
|
|||
'status': status,
|
||||
'status_human':
|
||||
self._partner_tracking_status_human_get(status),
|
||||
'error_type': tracking.error_type,
|
||||
'error_description':
|
||||
self._get_error_description(tracking),
|
||||
'tracking_id': tracking.id,
|
||||
'recipient': recipient,
|
||||
'partner_id': tracking.partner_id.id,
|
||||
|
@ -94,81 +125,80 @@ class MailMessage(models.Model):
|
|||
partners |= message.needaction_partner_ids
|
||||
# Remove recipients already included
|
||||
partners -= partners_already
|
||||
tracking_unkown_values = {
|
||||
'status': 'unknown',
|
||||
'status_human': self._partner_tracking_status_human_get(
|
||||
'unknown'),
|
||||
'error_type': False,
|
||||
'error_description': False,
|
||||
'tracking_id': False,
|
||||
}
|
||||
for partner in partners:
|
||||
# If there is partners not included, then status is 'unknown'
|
||||
# Because can be an Cc recipient
|
||||
# and perhaps a Cc recipient
|
||||
isCc = False
|
||||
if partner.email in email_cc_list:
|
||||
email_cc_list.discard(partner.email)
|
||||
isCc = True
|
||||
partner_trackings.append({
|
||||
'status': 'unknown',
|
||||
'status_human':
|
||||
self._partner_tracking_status_human_get('unknown'),
|
||||
'tracking_id': False,
|
||||
tracking_unkown_values.update({
|
||||
'recipient': partner.name,
|
||||
'partner_id': partner.id,
|
||||
'isCc': isCc,
|
||||
})
|
||||
partner_trackings.append(tracking_unkown_values.copy())
|
||||
for email in email_cc_list:
|
||||
# If there is Cc without partner
|
||||
partner_trackings.append({
|
||||
'status': 'unknown',
|
||||
'status_human':
|
||||
self._partner_tracking_status_human_get('unknown'),
|
||||
'tracking_id': False,
|
||||
tracking_unkown_values.update({
|
||||
'recipient': email,
|
||||
'partner_id': False,
|
||||
'isCc': True,
|
||||
})
|
||||
res[message.id] = partner_trackings
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def _get_failed_message(self):
|
||||
res = {}
|
||||
for message in self:
|
||||
res.update({
|
||||
message.id: message.mail_tracking_needs_action
|
||||
and bool(message.mail_tracking_ids.filtered(
|
||||
lambda x: x.state in self.get_failed_states()))
|
||||
})
|
||||
partner_trackings.append(tracking_unkown_values.copy())
|
||||
res[message.id] = {
|
||||
'partner_trackings': partner_trackings,
|
||||
'is_failed_message': message.is_failed_message,
|
||||
}
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _message_read_dict_postprocess(self, messages, message_tree):
|
||||
"""Preare values to be used by the chatter widget"""
|
||||
res = super(MailMessage, self)._message_read_dict_postprocess(
|
||||
messages, message_tree)
|
||||
mail_message_ids = {m.get('id') for m in messages if m.get('id')}
|
||||
mail_messages = self.browse(mail_message_ids)
|
||||
partner_trackings = mail_messages.tracking_status()
|
||||
failed_message = mail_messages._get_failed_message()
|
||||
tracking_statuses = mail_messages.tracking_status()
|
||||
for message_dict in messages:
|
||||
mail_message_id = message_dict.get('id', False)
|
||||
if mail_message_id:
|
||||
message_dict.update({
|
||||
'partner_trackings': partner_trackings[mail_message_id],
|
||||
'failed_message': failed_message[mail_message_id],
|
||||
})
|
||||
message_dict.update(tracking_statuses[mail_message_id])
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _prepare_dict_failed_message(self, message):
|
||||
"""Preare values to be used by the chatter widget"""
|
||||
failed_trackings = message.mail_tracking_ids.filtered(
|
||||
lambda x: x.state in self.get_failed_states())
|
||||
failed_partners = failed_trackings.mapped('partner_id')
|
||||
failed_recipients = failed_partners.name_get()
|
||||
if message.author_id:
|
||||
author = message.author_id.name_get()[0]
|
||||
else:
|
||||
author = (-1, _('-Unknown Author-'))
|
||||
return {
|
||||
'id': message.id,
|
||||
'date': message.date,
|
||||
'author_id': message.author_id.name_get()[0],
|
||||
'author': author,
|
||||
'body': message.body,
|
||||
'failed_recipients': failed_recipients,
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def get_failed_messages(self):
|
||||
return [self._prepare_dict_failed_message(msg) for msg in self]
|
||||
"""Returns the list of failed messages to be used by the
|
||||
failed_messages widget"""
|
||||
return [self._prepare_dict_failed_message(msg)
|
||||
for msg in self.sorted('date', reverse=True)]
|
||||
|
||||
@api.multi
|
||||
def toggle_tracking_status(self):
|
||||
|
@ -177,19 +207,25 @@ class MailMessage(models.Model):
|
|||
self.mail_tracking_needs_action = not self.mail_tracking_needs_action
|
||||
return self.mail_tracking_needs_action
|
||||
|
||||
@api.model
|
||||
def _get_failed_message_domain(self):
|
||||
return [
|
||||
('mail_tracking_ids.state', 'in', list(self.get_failed_states())),
|
||||
('mail_tracking_needs_action', '=', True)
|
||||
"""Returns the domain used to filter messages on discuss thread"""
|
||||
domain = self.env['mail.thread']._get_failed_message_domain()
|
||||
domain += [
|
||||
'|',
|
||||
('partner_ids', 'in', [self.env.user.partner_id.id]),
|
||||
('author_id', '=', self.env.user.partner_id.id),
|
||||
]
|
||||
return domain
|
||||
|
||||
@api.model
|
||||
def get_failed_count(self):
|
||||
""" Gets the number of failed messages """
|
||||
""" Gets the number of failed messages used on discuss mailbox item"""
|
||||
return self.search_count(self._get_failed_message_domain())
|
||||
|
||||
@api.model
|
||||
def message_fetch(self, domain, limit=20):
|
||||
"""Force domain to be used on discuss thread messages"""
|
||||
# HACK: Because can't modify the domain in discuss JS to search the
|
||||
# failed messages we force the change here to clean it of
|
||||
# not valid criterias
|
||||
|
@ -199,6 +235,7 @@ class MailMessage(models.Model):
|
|||
|
||||
@api.multi
|
||||
def message_format(self):
|
||||
"""Adds 'failed_recipients' to display on 'failed_messages' widget"""
|
||||
message_values = super().message_format()
|
||||
for message in message_values:
|
||||
message_id = self.browse(message['id'])
|
||||
|
@ -213,9 +250,9 @@ class MailMessage(models.Model):
|
|||
@api.multi
|
||||
def _notify(self, force_send=False, send_after_commit=True,
|
||||
user_signature=True):
|
||||
"""Force not adds followers to email (used when retry a message)"""
|
||||
self_sudo = self.sudo()
|
||||
hide_followers = self_sudo._context.get('default_hide_followers',
|
||||
False)
|
||||
hide_followers = self_sudo._context.get('default_hide_followers')
|
||||
if hide_followers:
|
||||
# HACK: Because Odoo uses subtype to found message followers
|
||||
# whe modify it to False to avoid include them.
|
||||
|
|
|
@ -14,14 +14,28 @@ class MailThread(models.AbstractModel):
|
|||
'mail.message', 'res_id', string='Failed Messages',
|
||||
domain=lambda self:
|
||||
[('model', '=', self._name)]
|
||||
+ self.env['mail.message']._get_failed_message_domain(),
|
||||
+ self._get_failed_message_domain(),
|
||||
auto_join=True)
|
||||
|
||||
def _get_failed_message_domain(self):
|
||||
"""Domain used to display failed messages on the 'failed_messages'
|
||||
widget"""
|
||||
failed_states = self.env['mail.message'].get_failed_states()
|
||||
return [
|
||||
('mail_tracking_needs_action', '=', True),
|
||||
('mail_tracking_ids.state', 'in', list(failed_states)),
|
||||
]
|
||||
|
||||
@api.multi
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def message_post(self, body='', subject=None, message_type='notification',
|
||||
subtype=None, parent_id=False, attachments=None,
|
||||
content_subtype='html', **kwargs):
|
||||
"""Adds CC recipient to the message.
|
||||
|
||||
Because Odoo implementation avoid store cc recipients we ensure that
|
||||
this information its written into the mail.message record.
|
||||
"""
|
||||
new_message = super().message_post(
|
||||
body=body, subject=subject, message_type=message_type,
|
||||
subtype=subtype, parent_id=parent_id, attachments=attachments,
|
||||
|
@ -69,7 +83,7 @@ class MailThread(models.AbstractModel):
|
|||
res = super().fields_view_get(
|
||||
view_id=view_id, view_type=view_type, toolbar=toolbar,
|
||||
submenu=submenu)
|
||||
if view_type != 'search' and view_type != 'form':
|
||||
if view_type not in {'search', 'form'}:
|
||||
return res
|
||||
doc = etree.XML(res['arch'])
|
||||
if view_type == 'search':
|
||||
|
|
|
@ -161,7 +161,9 @@ class MailTrackingEmail(models.Model):
|
|||
@api.depends('recipient')
|
||||
def _compute_recipient_address(self):
|
||||
for email in self:
|
||||
if email.recipient:
|
||||
is_empty_recipient = (not email.recipient
|
||||
or '<False>' in email.recipient)
|
||||
if not is_empty_recipient:
|
||||
matches = re.search(r'<(.*@.*)>', email.recipient)
|
||||
if matches:
|
||||
email.recipient_address = matches.group(1).lower()
|
||||
|
@ -215,12 +217,24 @@ class MailTrackingEmail(models.Model):
|
|||
|
||||
@api.multi
|
||||
def smtp_error(self, mail_server, smtp_server, exception):
|
||||
self.sudo().write({
|
||||
'error_smtp_server': tools.ustr(smtp_server),
|
||||
'error_type': exception.__class__.__name__,
|
||||
'error_description': tools.ustr(exception),
|
||||
values = {
|
||||
'state': 'error',
|
||||
})
|
||||
}
|
||||
IrMailServer = self.env['ir.mail_server']
|
||||
if str(exception) == IrMailServer.NO_VALID_RECIPIENT \
|
||||
and not self.recipient_address:
|
||||
values.update({
|
||||
'error_type': 'no_recipient',
|
||||
'error_description':
|
||||
"The partner doesn't have a defined email",
|
||||
})
|
||||
else:
|
||||
values.update({
|
||||
'error_smtp_server': tools.ustr(smtp_server),
|
||||
'error_type': exception.__class__.__name__,
|
||||
'error_description': tools.ustr(exception),
|
||||
})
|
||||
self.sudo().write(values)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
|
|
|
@ -31,6 +31,9 @@ These are all available status icons:
|
|||
.. |cc| image:: ../static/src/img/cc.png
|
||||
:width: 10px
|
||||
|
||||
.. |noemail| image:: ../static/src/img/no_email.png
|
||||
:width: 10px
|
||||
|
||||
|unknown| **Unknown**: No email tracking info available. Maybe this notified partner has 'Receive Inbox Notifications by Email' == 'Never'
|
||||
|
||||
|waiting| **Waiting**: Waiting to be sent
|
||||
|
@ -45,6 +48,8 @@ These are all available status icons:
|
|||
|
||||
|cc| **Cc**: It's a Carbon-Copy recipient. Can't know the status so is 'Unknown'
|
||||
|
||||
|noemail| **No Email**: The partner doesn't have a defined email
|
||||
|
||||
|
||||
When the message generates and 'error' status, it will apear on discuss 'Failed'
|
||||
channel. Any view that uses 'mail_thread' widget can show the failed messages
|
||||
|
@ -57,3 +62,10 @@ too.
|
|||
* Chatter
|
||||
|
||||
.. image:: ../static/img/failed_message_widget.png
|
||||
|
||||
You can use "Failed sent messages" filter present in all views to show records
|
||||
with messages in failed status and that needs an user action.
|
||||
|
||||
* Filter
|
||||
|
||||
.. image:: ../static/img/failed_message_filter.png
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 95 KiB |
Binary file not shown.
After Width: | Height: | Size: 935 B |
|
@ -126,7 +126,6 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
},
|
||||
|
||||
_openComposer: function (context) {
|
||||
var self = this;
|
||||
var failed_msg = chat_manager.get_message(context.message_id);
|
||||
this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
|
@ -138,11 +137,8 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
context: context,
|
||||
}, {
|
||||
on_close: function () {
|
||||
self.trigger('need_refresh');
|
||||
chat_manager.get_messages({
|
||||
model: failed_msg.model,
|
||||
res_id: failed_msg.res_id,
|
||||
});
|
||||
chat_manager.bus.trigger('update_message', failed_msg);
|
||||
core.bus.trigger('force_update_message', failed_msg);
|
||||
},
|
||||
}).then(this.trigger.bind(this, 'close_composer'));
|
||||
},
|
||||
|
@ -178,7 +174,7 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
event.preventDefault();
|
||||
var message_id = $(event.currentTarget).data('message-id');
|
||||
var failed_msg = chat_manager.get_message(message_id);
|
||||
this._rpc({
|
||||
return this._rpc({
|
||||
model: 'mail.message',
|
||||
method: 'toggle_tracking_status',
|
||||
args: [[message_id]],
|
||||
|
@ -233,7 +229,8 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
Object.defineProperties(msg, {
|
||||
is_failed: property_descr("channel_failed"),
|
||||
});
|
||||
msg.is_failed = data.failed_message;
|
||||
|
||||
msg.is_failed = data.is_failed_message;
|
||||
msg.failed_recipients = data.failed_recipients;
|
||||
return msg;
|
||||
};
|
||||
|
@ -291,7 +288,7 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
msg.date = moment(time.auto_str_to_date(msg.date));
|
||||
msg.hour = time_from_now(msg.date);
|
||||
});
|
||||
return _.sortBy(messages, 'date');
|
||||
return messages;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -331,7 +328,7 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.failed_messages = this.record.specialData[this.name];
|
||||
this.failed_messages = this.record.specialData[this.name] || [];
|
||||
},
|
||||
_render: function () {
|
||||
if (this.failed_messages.length) {
|
||||
|
@ -348,7 +345,7 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
},
|
||||
_reset: function (record) {
|
||||
this._super.apply(this, arguments);
|
||||
this.failed_messages = this.record.specialData[this.name];
|
||||
this.failed_messages = this.record.specialData[this.name] || [];
|
||||
this.res_id = record.res_id;
|
||||
},
|
||||
|
||||
|
@ -456,6 +453,7 @@ odoo.define('mail_tracking.FailedMessage', function (require) {
|
|||
keepChanges: true,
|
||||
});
|
||||
} else {
|
||||
// Workaround to avoid trigger reload event twice.
|
||||
this._super.apply(this, arguments);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<template>
|
||||
<templates>
|
||||
|
||||
<t t-name="mail.chat.SidebarFailed">
|
||||
<span class="o_mail_failed_message_refresh hidden"><i class="fa fa-refresh"/> Outdated</span>
|
||||
|
@ -22,21 +22,21 @@
|
|||
<t t-extend="mail.EmptyChannel">
|
||||
<t t-jquery="t:last-child" t-operation="after">
|
||||
<t t-if="options.channel_id==='channel_failed'">
|
||||
<div class="o_thread_title">Congratulations, doesn't have failed messages</div>
|
||||
<div class="o_thread_title">Congratulations, your failed mailbox is empty</div>
|
||||
<div>Failed messages appear here.</div>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
|
||||
<t t-extend="mail.ChatThread.Message">
|
||||
<t t-jquery="span[t-attf-class=o_thread_icons]" t-operation="append">
|
||||
<a t-if="message.is_failed && options.display_retry_button" href="#" class="btn btn-link btn-success text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-check"/> Mark Reviewed
|
||||
<a t-if="message.is_failed && options.display_retry_button" class="btn btn-link btn-success text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-check"/> Set as Reviewed
|
||||
</a>
|
||||
<a t-if="message.is_failed && options.display_reviewed_button" href="#" class="btn btn-link btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<a t-if="message.is_failed && options.display_reviewed_button" class="btn btn-link btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
</template>
|
||||
</templates>
|
||||
|
|
|
@ -4,20 +4,28 @@
|
|||
<div class="o_thread_message" style="margin-bottom: 10px">
|
||||
<div class="o_thread_message_sidebar">
|
||||
<div class="o_avatar_stack">
|
||||
<img t-attf-src="/web/image#{message.author_id[0] >= 0 ? ('/res.partner/' + message.author_id[0] + '/image_small') : ''}" class="o_thread_message_avatar img-circle mb8" t-att-title="message.author_id[1]"/>
|
||||
<img t-attf-src="/web/image/res.partner/#{message.author[0]}/image_small" class="o_thread_message_avatar img-circle mb8" t-att-title="message.author[1]"/>
|
||||
<i class="o_avatar_icon fa fa-exclamation bg-danger-full" title="Failed"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="o_thread_message_core">
|
||||
<div class="o_mail_info">
|
||||
<strong class="o_thread_author">
|
||||
<t t-esc="message.author_id[1]"/>
|
||||
<t t-esc="message.author[1]"/>
|
||||
</strong>
|
||||
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small><br/>
|
||||
- <small class="o_mail_timestamp" t-att-title="message.date.format(date_format)"><t t-esc="message.hour"/></small>
|
||||
<span t-attf-class="o_thread_icons">
|
||||
<a href="#" class="btn btn-link btn-success text-muted btn-sm o_failed_message_reviewed o_activity_link" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-check"/> Mark Reviewed
|
||||
</a>
|
||||
<a href="#" class="btn btn-link btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</span>
|
||||
<br/>
|
||||
<strong class="text-danger">Failed Recipients:</strong>
|
||||
<t t-set="first_recipient" t-value="true"/>
|
||||
<t t-foreach="message.failed_recipients" t-as="recipient">
|
||||
<t t-if="!first_recipient">
|
||||
<t t-if="!recipient_first">
|
||||
-
|
||||
</t>
|
||||
<a class="o_mail_action_tracking_partner"
|
||||
|
@ -25,20 +33,11 @@
|
|||
t-attf-href="#model=res.partner&id=#{recipient[0]}">
|
||||
<t t-esc="recipient[1]"/>
|
||||
</a>
|
||||
<t t-set="first_recipient" t-value="false"/>
|
||||
</t>
|
||||
</div>
|
||||
<div class="o_thread_message_note small">
|
||||
<t t-raw="message.body"/>
|
||||
</div>
|
||||
<div class="o_thread_message_tools btn-group">
|
||||
<a href="#" class="btn btn-link btn-success text-muted btn-sm o_failed_message_reviewed o_activity_link mr8" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-check"/> Mark Reviewed
|
||||
</a>
|
||||
<a href="#" class="btn btn-link btn-default text-muted btn-sm o_failed_message_retry" t-att-data-message-id="message.id">
|
||||
<i class="fa fa-retweet"/> Retry
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
|
|
@ -22,7 +22,8 @@
|
|||
</t>
|
||||
<t t-elif="tracking['status'] === 'error'">
|
||||
<span class="mail_tracking_error mail_tracking_pointer">
|
||||
<i class="fa fa-remove"></i>
|
||||
<i t-if="tracking['error_type'] === 'no_recipient'" class="fa fa-user-times"></i>
|
||||
<i t-else="" class="fa fa-remove"></i>
|
||||
</span>
|
||||
</t>
|
||||
<t t-elif="tracking['status'] === 'sent'">
|
||||
|
@ -64,9 +65,11 @@
|
|||
<t t-else="">
|
||||
<span t-attf-class="#{tracking['isCc'] ? 'o_mail_cc' : ''}"><t t-esc="tracking['recipient']"/></span>
|
||||
</t>
|
||||
<t t-if="tracking['status'] === 'error' && tracking['error_type'] === 'no_recipient'" t-set="title_status" t-value="tracking['error_description']" />
|
||||
<t t-else="" t-set="title_status" t-value="tracking['status_human']" />
|
||||
<span class="mail_tracking o_mail_action_tracking_status"
|
||||
t-att-data-tracking="tracking['tracking_id']"
|
||||
t-att-title="tracking['status_human']">
|
||||
t-att-title="title_status">
|
||||
<t t-call="mail.tracking.status"/>
|
||||
</span>
|
||||
</t>
|
||||
|
|
|
@ -115,6 +115,29 @@ class TestMailTracking(TransactionCase):
|
|||
tracking_email.event_create('open', metadata)
|
||||
self.assertEqual(tracking_email.state, 'opened')
|
||||
|
||||
def test_message_post_partner_no_email(self):
|
||||
# Create message with recipient without defined email
|
||||
self.recipient.write({'email': ''})
|
||||
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': '<p>This is a test message</p>',
|
||||
})
|
||||
# Search tracking created
|
||||
tracking_email = self.env['mail.tracking.email'].search([
|
||||
('mail_message_id', '=', message.id),
|
||||
('partner_id', '=', self.recipient.id),
|
||||
])
|
||||
# No email should generate a error state: no_recipient
|
||||
self.assertEqual(tracking_email.state, 'error')
|
||||
self.assertEqual(tracking_email.error_type, 'no_recipient')
|
||||
self.assertFalse(self.recipient.email_bounced)
|
||||
|
||||
def _check_partner_trackings(self, message):
|
||||
message_dict = message.message_format()[0]
|
||||
self.assertEqual(len(message_dict['partner_trackings']), 3)
|
||||
|
@ -192,6 +215,10 @@ class TestMailTracking(TransactionCase):
|
|||
self.assertFalse(tracking.mail_message_id.mail_tracking_needs_action)
|
||||
self.assertTrue(
|
||||
self.env['mail.message'].get_failed_count() < failed_count)
|
||||
# No author_id
|
||||
tracking.mail_message_id.author_id = False
|
||||
values = tracking.mail_message_id.get_failed_messages()[0]
|
||||
self.assertEqual(values['author'][0], -1)
|
||||
|
||||
def mail_send(self, recipient):
|
||||
mail = self.env['mail.mail'].create({
|
||||
|
|
Loading…
Reference in New Issue