PURPOSE Global purpose is to rename some methods and add some docstrings to clean API of notification methods used in mail thread. Notably use _notify_thread prefix for main methods, and _notify_by_'mean' for tool sub-methods. SPECIFICATIONS Remove unused arguments coming from old implementation and usage. They were introduced notably for performance reason when cache was more often invalidated which is not the case anymore. Anyway when parameters are unused it is always better to remove them. Remove ``notify_by_email`` parameter in ``_notify_thread``. It is only used in channels to avoid notifying people of some automated notifications. The same behavior has been cleanly implemented at odoo/odoo@018820d . Rename methods, starting with ``_notify(_records)`` to ease their grouping and understanding. Add some additional prefixes like ``_notify_by_'mean'`` and ``_notify_get_recipients`` for recipients related computation. Add some docstrings, notably when parameters usage is not clear. Some linting is also performed in updated actions, just to lessen styling issues notably on runbot. This commit should not change anything functionally as it contains only some renaming and docstrings updates as well as some outdated parameters removal. Task-2710804 (Mail: Clean Mail.Thread API) Part-of: odoo/odoo#82167
394 lines
17 KiB
Python
394 lines
17 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from unittest.mock import patch
|
|
|
|
from odoo.addons.test_mail.tests.common import TestMailCommon
|
|
from odoo.tests.common import tagged
|
|
from odoo.tests import Form
|
|
|
|
|
|
@tagged('mail_track')
|
|
class TestTracking(TestMailCommon):
|
|
|
|
def setUp(self):
|
|
super(TestTracking, self).setUp()
|
|
|
|
record = self.env['mail.test.ticket'].with_user(self.user_employee).with_context(self._test_context).create({
|
|
'name': 'Test',
|
|
})
|
|
self.flush_tracking()
|
|
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.flush_tracking()
|
|
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'})
|
|
with self.mock_mail_gateway():
|
|
self.record.write({
|
|
'name': 'Test2',
|
|
'customer_id': customer.id,
|
|
})
|
|
self.flush_tracking()
|
|
|
|
# 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.notified_partner_ids, self.env['res.partner'])
|
|
self.assertNotSentEmail()
|
|
|
|
# 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_ticket_container_upd').id]
|
|
)
|
|
|
|
container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'})
|
|
self.record.write({
|
|
'name': 'Test2',
|
|
'email_from': 'noone@example.com',
|
|
'container_id': container.id,
|
|
})
|
|
self.flush_tracking()
|
|
# 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_ticket_container_upd'))
|
|
|
|
# no specific recipients except those following container
|
|
self.assertEqual(self.record.message_ids.partner_ids, self.env['res.partner'])
|
|
self.assertEqual(self.record.message_ids.notified_partner_ids, self.user_admin.partner_id)
|
|
|
|
# verify tracked value
|
|
self.assertTracking(
|
|
self.record.message_ids,
|
|
[('container_id', 'many2one', False, container) # 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_ticket_tracking_tpl').id})
|
|
self.assertEqual(self.record.message_ids, self.env['mail.message'])
|
|
|
|
with self.mock_mail_gateway():
|
|
self.record.write({
|
|
'name': 'Test2',
|
|
'customer_id': self.user_admin.partner_id.id,
|
|
})
|
|
self.flush_tracking()
|
|
|
|
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.assertSentEmail(self.record.env.user.partner_id, [self.partner_admin], 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.ticket'].with_user(self.user_employee).with_context(self._test_context)
|
|
Model = Model.with_context(mail_notrack=False)
|
|
with self.mock_mail_gateway():
|
|
record = Model.create({
|
|
'name': 'Test',
|
|
'customer_id': self.user_admin.partner_id.id,
|
|
'mail_template': self.env.ref('test_mail.mail_test_ticket_tracking_tpl').id,
|
|
})
|
|
self.flush_tracking()
|
|
|
|
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.assertSentEmail(self.record.env.user.partner_id, [self.partner_admin], body='<p>Hello Test</p>')
|
|
|
|
def test_create_partner_from_tracking_multicompany(self):
|
|
company1 = self.env['res.company'].create({'name': 'company1'})
|
|
self.env.user.write({'company_ids': [(4, company1.id, False)]})
|
|
self.assertNotEqual(self.env.company, company1)
|
|
|
|
email_new_partner = "diamonds@rust.com"
|
|
Partner = self.env['res.partner']
|
|
self.assertFalse(Partner.search([('email', '=', email_new_partner)]))
|
|
|
|
template = self.env['mail.template'].create({
|
|
'model_id': self.env['ir.model']._get('mail.test.track').id,
|
|
'name': 'AutoTemplate',
|
|
'subject': 'autoresponse',
|
|
'email_from': self.env.user.email_formatted,
|
|
'email_to': "{{ object.email_from }}",
|
|
'body_html': "<div>A nice body</div>",
|
|
})
|
|
|
|
def patched_message_track_post_template(*args, **kwargs):
|
|
if args[0]._name == "mail.test.track":
|
|
args[0].message_post_with_template(template.id)
|
|
return True
|
|
|
|
with patch('odoo.addons.mail.models.mail_thread.MailThread._message_track_post_template', patched_message_track_post_template):
|
|
self.env['mail.test.track'].create({
|
|
'email_from': email_new_partner,
|
|
'company_id': company1.id,
|
|
'user_id': self.env.user.id, # trigger track template
|
|
})
|
|
self.flush_tracking()
|
|
|
|
new_partner = Partner.search([('email', '=', email_new_partner)])
|
|
self.assertTrue(new_partner)
|
|
self.assertEqual(new_partner.company_id, company1)
|
|
|
|
def test_track_template(self):
|
|
# Test: Check that default_* keys are not taken into account in _message_track_post_template
|
|
magic_code = 'Up-Up-Down-Down-Left-Right-Left-Right-Square-Triangle'
|
|
|
|
mt_name_changed = self.env['mail.message.subtype'].create({
|
|
'name': 'MAGIC CODE WOOP WOOP',
|
|
'description': 'SPECIAL CONTENT UNLOCKED'
|
|
})
|
|
self.env['ir.model.data'].create({
|
|
'name': 'mt_name_changed',
|
|
'model': 'mail.message.subtype',
|
|
'module': 'mail',
|
|
'res_id': mt_name_changed.id
|
|
})
|
|
mail_template = self.env['mail.template'].create({
|
|
'name': 'SPECIAL CONTENT UNLOCKED',
|
|
'subject': 'SPECIAL CONTENT UNLOCKED',
|
|
'model_id': self.env.ref('test_mail.model_mail_test_container').id,
|
|
'auto_delete': True,
|
|
'body_html': '''<div>WOOP WOOP</div>''',
|
|
})
|
|
|
|
def _track_subtype(self, init_values):
|
|
if 'name' in init_values and init_values['name'] == magic_code:
|
|
return 'mail.mt_name_changed'
|
|
return False
|
|
self.registry('mail.test.container')._patch_method('_track_subtype', _track_subtype)
|
|
|
|
def _track_template(self, changes):
|
|
res = {}
|
|
if 'name' in changes:
|
|
res['name'] = (mail_template, {'composition_mode': 'mass_mail'})
|
|
return res
|
|
self.registry('mail.test.container')._patch_method('_track_template', _track_template)
|
|
|
|
cls = type(self.env['mail.test.container'])
|
|
self.assertFalse(hasattr(getattr(cls, 'name'), 'track_visibility'))
|
|
getattr(cls, 'name').track_visibility = 'always'
|
|
|
|
@self.addCleanup
|
|
def cleanup():
|
|
del getattr(cls, 'name').track_visibility
|
|
|
|
test_mail_record = self.env['mail.test.container'].create({
|
|
'name': 'Zizizatestmailname',
|
|
'description': 'Zizizatestmaildescription',
|
|
})
|
|
test_mail_record.with_context(default_parent_id=2147483647).write({'name': magic_code})
|
|
|
|
def test_message_track_multiple(self):
|
|
""" check that multiple updates generate a single tracking message """
|
|
container = self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'})
|
|
self.record.name = 'Zboub'
|
|
self.record.customer_id = self.user_admin.partner_id
|
|
self.record.user_id = self.user_admin
|
|
self.record.container_id = container
|
|
self.flush_tracking()
|
|
|
|
# should have a single message with all tracked fields
|
|
self.assertEqual(len(self.record.message_ids), 1, 'should have 1 tracking message')
|
|
self.assertTracking(self.record.message_ids[0], [
|
|
('customer_id', 'many2one', False, self.user_admin.partner_id),
|
|
('user_id', 'many2one', False, self.user_admin),
|
|
('container_id', 'many2one', False, container),
|
|
])
|
|
|
|
def test_tracked_compute(self):
|
|
# no tracking at creation
|
|
record = self.env['mail.test.track.compute'].create({})
|
|
self.flush_tracking()
|
|
self.assertEqual(len(record.message_ids), 1)
|
|
self.assertEqual(len(record.message_ids[0].tracking_value_ids), 0)
|
|
|
|
# assign partner_id: one tracking message for the modified field and all
|
|
# the stored and non-stored computed fields on the record
|
|
partner = self.env['res.partner'].create({
|
|
'name': 'Foo',
|
|
'email': 'foo@example.com',
|
|
'phone': '1234567890',
|
|
})
|
|
record.partner_id = partner
|
|
self.flush_tracking()
|
|
self.assertEqual(len(record.message_ids), 2)
|
|
self.assertEqual(len(record.message_ids[0].tracking_value_ids), 4)
|
|
self.assertTracking(record.message_ids[0], [
|
|
('partner_id', 'many2one', False, partner),
|
|
('partner_name', 'char', False, 'Foo'),
|
|
('partner_email', 'char', False, 'foo@example.com'),
|
|
('partner_phone', 'char', False, '1234567890'),
|
|
])
|
|
|
|
# modify partner: one tracking message for the only recomputed field
|
|
partner.write({'name': 'Fool'})
|
|
self.flush_tracking()
|
|
self.assertEqual(len(record.message_ids), 3)
|
|
self.assertEqual(len(record.message_ids[0].tracking_value_ids), 1)
|
|
self.assertTracking(record.message_ids[0], [
|
|
('partner_name', 'char', 'Foo', 'Fool'),
|
|
])
|
|
|
|
# modify partner: one tracking message for both stored computed fields;
|
|
# the non-stored computed fields have no tracking
|
|
partner.write({
|
|
'name': 'Bar',
|
|
'email': 'bar@example.com',
|
|
'phone': '0987654321',
|
|
})
|
|
# force recomputation of 'partner_phone' to make sure it does not
|
|
# generate tracking values
|
|
self.assertEqual(record.partner_phone, '0987654321')
|
|
self.flush_tracking()
|
|
self.assertEqual(len(record.message_ids), 4)
|
|
self.assertEqual(len(record.message_ids[0].tracking_value_ids), 2)
|
|
self.assertTracking(record.message_ids[0], [
|
|
('partner_name', 'char', 'Fool', 'Bar'),
|
|
('partner_email', 'char', 'foo@example.com', 'bar@example.com'),
|
|
])
|
|
|
|
@tagged('mail_track')
|
|
class TestTrackingMonetary(TestMailCommon):
|
|
|
|
def setUp(self):
|
|
super(TestTrackingMonetary, self).setUp()
|
|
|
|
self._activate_multi_company()
|
|
|
|
record = self.env['mail.test.track.monetary'].with_user(self.user_employee).with_context(self._test_context).create({
|
|
'company_id': self.user_employee.company_id.id,
|
|
})
|
|
self.flush_tracking()
|
|
self.record = record.with_context(mail_notrack=False)
|
|
|
|
def test_message_track_monetary(self):
|
|
""" Update a record with a tracked monetary field """
|
|
|
|
# Check if the tracking value have the correct currency and values
|
|
self.record.write({
|
|
'revenue': 100,
|
|
})
|
|
self.flush_tracking()
|
|
self.assertEqual(len(self.record.message_ids), 1)
|
|
|
|
self.assertTracking(self.record.message_ids[0], [
|
|
('revenue', 'monetary', 0, 100),
|
|
])
|
|
|
|
# Check if the tracking value have the correct currency and values after changing the value and the company
|
|
self.record.write({
|
|
'revenue': 200,
|
|
'company_id': self.company_2.id,
|
|
})
|
|
self.flush_tracking()
|
|
self.assertEqual(len(self.record.message_ids), 2)
|
|
|
|
self.assertTracking(self.record.message_ids[0], [
|
|
('revenue', 'monetary', 100, 200),
|
|
('company_currency', 'many2one', self.user_employee.company_id.currency_id, self.company_2.currency_id)
|
|
])
|
|
|
|
@tagged('mail_track')
|
|
class TestTrackingInternals(TestMailCommon):
|
|
|
|
def setUp(self):
|
|
super(TestTrackingInternals, self).setUp()
|
|
|
|
record = self.env['mail.test.ticket'].with_user(self.user_employee).with_context(self._test_context).create({
|
|
'name': 'Test',
|
|
})
|
|
self.flush_tracking()
|
|
self.record = record.with_context(mail_notrack=False)
|
|
|
|
def test_track_groups(self):
|
|
field = self.record._fields['email_from']
|
|
self.addCleanup(setattr, field, 'groups', field.groups)
|
|
field.groups = 'base.group_erp_manager'
|
|
|
|
self.record.sudo().write({'email_from': 'X'})
|
|
self.flush_tracking()
|
|
|
|
msg_emp = self.record.message_ids.message_format()
|
|
msg_sudo = self.record.sudo().message_ids.message_format()
|
|
self.assertFalse(msg_emp[0].get('tracking_value_ids'), "should not have protected tracking values")
|
|
self.assertTrue(msg_sudo[0].get('tracking_value_ids'), "should have protected tracking values")
|
|
|
|
msg_emp = self.record._notify_by_email_prepare_rendering_context(self.record.message_ids, {})
|
|
msg_sudo = self.record.sudo()._notify_by_email_prepare_rendering_context(self.record.message_ids, {})
|
|
self.assertFalse(msg_emp.get('tracking_values'), "should not have protected tracking values")
|
|
self.assertTrue(msg_sudo.get('tracking_values'), "should have protected tracking values")
|
|
|
|
# test editing the record with user not in the group of the field
|
|
self.record.invalidate_cache()
|
|
self.record.clear_caches()
|
|
record_form = Form(self.record.with_user(self.user_employee))
|
|
record_form.name = 'TestDoNoCrash'
|
|
# the employee user must be able to save the fields on which he can write
|
|
# if we fetch all the tracked fields, ignoring the group of the current user
|
|
# it will crash and it shouldn't
|
|
record = record_form.save()
|
|
self.assertEqual(record.name, 'TestDoNoCrash')
|
|
|
|
def test_track_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,
|
|
'container_id': self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'}).id
|
|
})
|
|
self.flush_tracking()
|
|
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_unlinked_field(self):
|
|
record_sudo = self.record.sudo()
|
|
record_sudo.write({'email_from': 'new_value'}) # create a tracking value
|
|
self.flush_tracking()
|
|
self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 1)
|
|
ir_model_field = self.env['ir.model.fields'].search([
|
|
('model', '=', 'mail.test.ticket'),
|
|
('name', '=', 'email_from')])
|
|
ir_model_field.with_context(_force_unlink=True).unlink()
|
|
self.assertEqual(len(record_sudo.message_ids.tracking_value_ids), 0)
|