Files
odoo_source/addons/test_mail/tests/test_message_track.py
T
Lucas Lefèvre 8d2d41068d [IMP] mail: Delete tracking values when uninstalling module
Purpose
=======
mail.tracking.values should be deleted when the corresponding
field is deleted (when the module which defined it is uninstalled).

Firstly because we don't want useless data in db.

Secondly because the groups associated with the field can no longer
be checked if it was deleted. As we don't know to whom the value
was restricted, the value should not be displayed anyway.
Note: this case was fixed in saas-12.2 by b9e96b7 but the proper
way to fix it is to delete the tracking values.

Specification
=============

Delete `mail.tracking.value` when the associated
`ir.model.fields` is deleted.

Two alternatives were considered:

1. Change the `field` field of `mail.tracking.value` from Char
to a m2o to `ir.model.fields` which allows to use delete
oncascade. This implies to modify existing code, but more
importantly it adds database queries.

2. Override the unlink method of `ir.model.fields` to
first unlink associated tracking values.

The first method is probably cleaner but the second method
was nonetheless chosen as we don't want to impact
performance in the main tracking flow only to better
support module uninstalls which happens rarely.

Note: when a module is uninstalled, `ir.model.fields`
are unlinked one by one (in a for loop). Tracking values are
therefore also unlinked field by field. Batchifying field
deletion would greatly reduce the amount of queries.
2019-04-17 17:05:17 +02:00

180 lines
9.0 KiB
Python

# -*- coding: utf-8 -*-
from email.utils import formataddr
from odoo.addons.test_mail.tests import common
class TestTracking(common.BaseFunctionalTest, common.MockEmails):
def assertTracking(self, message, data):
tracking_values = message.sudo().tracking_value_ids
for field_name, value_type, old_value, new_value in data:
tracking = tracking_values.filtered(lambda track: track.field == field_name)
self.assertEqual(len(tracking), 1)
if value_type in ('char', 'integer'):
self.assertEqual(tracking.old_value_char, old_value)
self.assertEqual(tracking.new_value_char, new_value)
elif value_type in ('many2one'):
self.assertEqual(tracking.old_value_integer, old_value and old_value.id or False)
self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False)
self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '')
self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '')
else:
self.assertEqual(1, 0)
def setUp(self):
super(TestTracking, self).setUp()
record = self.env['mail.test.full'].sudo(self.user_employee).with_context(common.BaseFunctionalTest._test_context).create({
'name': 'Test',
})
self.record = record.with_context(mail_notrack=False)
def test_message_track_no_tracking(self):
""" Update a set of non tracked fields -> no message, no tracking """
self.record.write({
'name': 'Tracking or not',
'count': 32,
})
self.assertEqual(self.record.message_ids, self.env['mail.message'])
def test_message_track_no_subtype(self):
""" Update some tracked fields not linked to some subtype -> message with onchange """
customer = self.env['res.partner'].create({'name': 'Customer', 'email': 'cust@example.com'})
self.record.write({
'name': 'Test2',
'customer_id': customer.id,
})
# one new message containing tracking; without subtype linked to tracking, a note is generated
self.assertEqual(len(self.record.message_ids), 1)
self.assertEqual(self.record.message_ids.subtype_id, self.env.ref('mail.mt_note'))
# no specific recipients except those following notes, no email
self.assertEqual(self.record.message_ids.partner_ids, self.env['res.partner'])
self.assertEqual(self.record.message_ids.needaction_partner_ids, self.env['res.partner'])
self.assertEqual(self._mails, [])
# verify tracked value
self.assertTracking(
self.record.message_ids,
[('customer_id', 'many2one', False, customer) # onchange tracked field
])
def test_message_track_subtype(self):
""" Update some tracked fields linked to some subtype -> message with onchange """
self.record.message_subscribe(
partner_ids=[self.user_admin.partner_id.id],
subtype_ids=[self.env.ref('test_mail.st_mail_test_full_umbrella_upd').id]
)
umbrella = self.env['mail.test'].with_context(mail_create_nosubscribe=True).create({'name': 'Umbrella'})
self.record.write({
'name': 'Test2',
'email_from': 'noone@example.com',
'umbrella_id': umbrella.id,
})
# one new message containing tracking; subtype linked to tracking
self.assertEqual(len(self.record.message_ids), 1)
self.assertEqual(self.record.message_ids.subtype_id, self.env.ref('test_mail.st_mail_test_full_umbrella_upd'))
# no specific recipients except those following umbrella
self.assertEqual(self.record.message_ids.partner_ids, self.env['res.partner'])
self.assertEqual(self.record.message_ids.needaction_partner_ids, self.user_admin.partner_id)
# verify tracked value
self.assertTracking(
self.record.message_ids,
[('umbrella_id', 'many2one', False, umbrella) # onchange tracked field
])
def test_message_track_template(self):
""" Update some tracked fields linked to some template -> message with onchange """
self.record.write({'mail_template': self.env.ref('test_mail.mail_test_full_tracking_tpl').id})
self.assertEqual(self.record.message_ids, self.env['mail.message'])
self.record.write({
'name': 'Test2',
'customer_id': self.user_admin.partner_id.id,
})
self.assertEqual(len(self.record.message_ids), 2, 'should have 2 new messages: one for tracking, one for template')
# one new message containing the template linked to tracking
self.assertEqual(self.record.message_ids[0].subject, 'Test Template')
self.assertEqual(self.record.message_ids[0].body, '<p>Hello Test2</p>')
# one email send due to template
self.assertEqual(len(self._mails), 1)
self.assertEqual(set(self._mails[0]['email_to']), set([formataddr((self.user_admin.name, self.user_admin.email))]))
self.assertHtmlEqual(self._mails[0]['body'], '<p>Hello Test2</p>')
# one new message containing tracking; without subtype linked to tracking
self.assertEqual(self.record.message_ids[1].subtype_id, self.env.ref('mail.mt_note'))
self.assertTracking(
self.record.message_ids[1],
[('customer_id', 'many2one', False, self.user_admin.partner_id) # onchange tracked field
])
def test_message_track_template_at_create(self):
""" Create a record with tracking template on create, template should be sent."""
Model = self.env['mail.test.full'].sudo(self.user_employee).with_context(common.BaseFunctionalTest._test_context)
Model = Model.with_context(mail_notrack=False)
record = Model.create({
'name': 'Test',
'customer_id': self.user_admin.partner_id.id,
'mail_template': self.env.ref('test_mail.mail_test_full_tracking_tpl').id,
})
self.assertEqual(len(record.message_ids), 1, 'should have 1 new messages for template')
# one new message containing the template linked to tracking
self.assertEqual(record.message_ids[0].subject, 'Test Template')
self.assertEqual(record.message_ids[0].body, '<p>Hello Test</p>')
# one email send due to template
self.assertEqual(len(self._mails), 1)
def test_message_tracking_sequence(self):
""" Update some tracked fields and check that the mail.tracking.value are ordered according to their tracking_sequence"""
self.record.write({
'name': 'Zboub',
'customer_id': self.user_admin.partner_id.id,
'user_id': self.user_admin.id,
'umbrella_id': self.env['mail.test'].with_context(mail_create_nosubscribe=True).create({'name': 'Umbrella'}).id
})
self.assertEqual(len(self.record.message_ids), 1, 'should have 1 tracking message')
tracking_values = self.env['mail.tracking.value'].search([('mail_message_id', '=', self.record.message_ids.id)])
self.assertEqual(tracking_values[0].tracking_sequence, 1)
self.assertEqual(tracking_values[1].tracking_sequence, 2)
self.assertEqual(tracking_values[2].tracking_sequence, 100)
def test_track_groups(self):
self.record._fields['email_from'].groups = 'base.group_erp_manager' # patch the group attribute
self.record.sudo().write({'email_from': 'X'})
msg_emp = self.record.message_ids.message_format()
msg_admin = self.record.message_ids.sudo(self.user_admin).message_format()
self.assertFalse(msg_emp[0].get('tracking_value_ids'), "should not have protected tracking values")
self.assertTrue(msg_admin[0].get('tracking_value_ids'), "should have protected tracking values")
def test_notify_track_groups(self):
self.record._fields['email_from'].groups = 'base.group_erp_manager' # patch the group attribute
self.record.sudo().write({'email_from': 'X'})
msg_emp = self.partner_employee.sudo(self.user_employee)._notify_prepare_template_context(self.record.message_ids, self.record)
msg_admin = self.partner_admin.sudo(self.user_admin)._notify_prepare_template_context(self.record.message_ids, self.record)
self.assertFalse(msg_emp.get('tracking_values'), "should not have protected tracking values")
self.assertTrue(msg_admin.get('tracking_values'), "should have protected tracking values")
def test_unlinked_field(self):
record_sudo = self.record.sudo()
record_sudo.write({'email_from': 'X'}) # create a tracking value
self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 1)
ir_model_field = self.env['ir.model.fields'].search([
('model', '=', 'mail.test.full'),
('name', '=', 'email_from')])
ir_model_field.with_context(_force_unlink=True).unlink()
self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 0)