Files
odoo_source/addons/test_mail/tests/test_message_track.py
T
Thibault Delavallée 84e2d6ea55 [REF] mail: remove 'alias_user_id'
'alias_user_id' field allows to set a user when creating records through the
mail gateway. This is however quite wrong and eases spoofing. The alias owner
is not the creator of any record, nor responsible.

Current possible ways of being owner / responsible of records created through
the mailgateway

  * when you send an email to an alias: if you are recognized you are already
    set as creating user and logged message author;
  * it is possible to use alias_defaults notably to set fields like 'user_id'
    if you want to be notified / responsible of records created through this
    specific alias;

Those usages are therefore sufficient, no need to have another way to spoof
users. Moreover it is hidden in technical view of aliases, no model allows
to configure it by default. Moreover since odoo/odoo@3edf181 no default
value is given to alias_user_id as it adds more (ACLs / creator) issues than
really helping setting up mail gateway flows.

Task-3453482
Prepares Task-36879 (Mail: Multi-Domain Aliases)

closes odoo/odoo#138213

Related: odoo/upgrade#5259
Related: odoo/enterprise#48692
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
2023-10-11 17:02:14 +00:00

832 lines
36 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from unittest.mock import patch
from odoo import fields
from odoo.addons.mail.tests.common import MailCommon
from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE
from odoo.tests import Form, tagged, users
from odoo.tools import mute_logger
@tagged('mail_track')
class TestTracking(MailCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
record = cls.env['mail.test.ticket'].with_user(
cls.user_employee
).with_context(cls._test_context).create({
'name': 'Test',
})
cls.record = record.with_context(mail_notrack=False)
def test_message_track_author(self):
""" Check that the author of the log note matches the user at the time
of writing. """
with self.mock_mail_gateway():
self.record._track_set_author(self.partner_admin)
self.record.write({
'customer_id': self.partner_employee.id,
})
self.flush_tracking()
self.assertEqual(len(self.record.message_ids), 1)
self.assertEqual(len(self.record.message_ids.tracking_value_ids), 1)
self.assertEqual(self.record.message_ids.author_id, self.partner_admin)
def test_message_track_message_type(self):
"""Check that the right message type is applied for track templates."""
self.record.message_subscribe(
partner_ids=[self.user_admin.partner_id.id],
subtype_ids=[self.env.ref('mail.mt_comment').id]
)
mail_templates = self.env['mail.template'].create([{
'name': f'Template {n}',
'subject': f'Template {n}',
'model_id': self.env.ref('test_mail.model_mail_test_ticket').id,
'body_html': f'<p>Template {n}</p>',
} for n in range(2)])
def _track_subtype(self, init_values):
return self.env.ref('mail.mt_note')
self.patch(self.registry('mail.test.ticket'), '_track_subtype', _track_subtype)
def _track_template(self, changes):
if 'email_from' in changes:
return {'email_from': (mail_templates[0], {})}
elif 'container_id' in changes:
return {'container_id': (mail_templates[1], {'message_type': 'notification'})}
return {}
self.patch(self.registry('mail.test.ticket'), '_track_template', _track_template)
container = self.env['mail.test.container'].create({'name': 'Container'})
# default is auto_comment
with self.mock_mail_gateway():
self.record.email_from = 'test@test.lan'
self.flush_tracking()
first_message = self.record.message_ids.filtered(lambda message: message.subject == 'Template 0')
self.assertEqual(len(self.record.message_ids), 2, 'Should be one change message and one automated template')
self.assertEqual(first_message.message_type, 'auto_comment')
# auto_comment can be overriden by _track_template
with self.mock_mail_gateway(mail_unlink_sent=False):
self.record.container_id = container
self.flush_tracking()
second_message = self.record.message_ids.filtered(lambda message: message.subject == 'Template 1')
self.assertEqual(len(self.record.message_ids), 4, 'Should have added one change message and one automated template')
self.assertEqual(second_message.message_type, 'notification')
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.assertEqual(self.record.message_ids.author_id, self.partner_employee)
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_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.author_id, self.partner_employee)
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
])
@users('employee')
def test_message_track_no_tracking(self):
""" Update a set of non tracked fields -> no message, no tracking, or
use dedicated context key """
record = self.record.with_env(self.env)
record.write({
'name': 'Tracking or not',
'count': 32,
})
self.flush_tracking()
self.assertFalse(record.message_ids)
# check context key allowing to skip tracking
record.with_context(mail_notrack=True).write({'email_from': 'new.from@test.example.com'})
self.flush_tracking()
self.assertFalse(record.message_ids)
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.author_id, self.partner_employee)
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
])
@mute_logger('odoo.addons.mail.models.mail_mail')
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].author_id, self.partner_employee)
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
])
@mute_logger('odoo.addons.mail.models.mail_mail')
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].author_id, self.partner_employee)
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>')
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.addons.mail.models.mail_thread')
def test_message_track_template_at_create_from_message(self):
"""Make sure records created through aliasing show the original message before the template"""
# setup
test_model = self.env['ir.model']._get('mail.test.ticket')
original_sender = self.user_admin.partner_id
custom_values = {'name': 'Test', 'customer_id': original_sender.id,
'mail_template': self.env.ref('test_mail.mail_test_ticket_tracking_tpl').id}
self.env['mail.alias'].create({
'alias_name': 'groups',
'alias_model_id': test_model.id,
'alias_contact': 'everyone',
'alias_defaults': custom_values})
record = self.format_and_process(MAIL_TEMPLATE, '"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>',
'groups@test.mycompany.com', target_field='customer_id', subject=custom_values['customer_id'],
target_model='mail.test.ticket')
with self.mock_mail_gateway(mail_unlink_sent=False):
self.flush_tracking()
# Should be trigger message and response template
self.assertEqual(len(record.message_ids), 2)
messages = list(record.message_ids)
messages.sort(key=lambda msg: msg.id)
trigger = messages[0]
template = messages[1]
self.assertIn('Please call me as soon as possible this afternoon!', trigger.body)
self.assertIn(f"Hello {custom_values['name']}", template.body)
self.assertMailMail(
original_sender,
'sent',
author=self.env.ref('base.partner_root'),
email_values={
'body_content': f"<p>Hello {custom_values['name']}</p>",
}
)
@mute_logger('odoo.addons.mail.models.mail_mail')
def test_message_track_template_create_partner_multicompany(self):
""" Test partner created due to usage of a mail.template, triggered by
a tracking, in a multi company environment. """
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_source(template)
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_message_track_template_defaults(self):
""" 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.patch(self.registry('mail.test.container'), '_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.patch(self.registry('mail.test.container'), '_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})
@tagged('mail_track')
class TestTrackingInternals(MailCommon):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.record = cls.env['mail.test.ticket'].with_user(cls.user_employee).create({
'name': 'Test',
})
cls.test_partner = cls.env['res.partner'].create({
'country_id': cls.env.ref('base.be').id,
'email': 'test.partner@test.example.com',
'name': 'Test Partner',
'phone': '0456001122',
})
@users('employee')
def test_mail_track_2many(self):
""" Check result of tracking one2many and many2many fields. Current
usage is to aggregate names into value_char fields. """
# Create a record with an initially invalid selection value
test_tags = self.env['mail.test.track.all.m2m'].create([
{'name': 'Tag1',},
{'name': 'Tag2',},
{'name': 'Tag3',},
])
test_record = self.env['mail.test.track.all'].create({
'name': 'Test 2Many fields tracking',
})
self.flush_tracking()
# no tracked field, no tracking at create
last_message = test_record.message_ids[0]
self.assertFalse(last_message.tracking_value_ids)
# update m2m
test_record.write({
'many2many_field': [(4, test_tags[0].id), (4, test_tags[1].id)],
})
self.flush_tracking()
last_message = test_record.message_ids[0]
self.assertTracking(
last_message,
[('many2many_field', 'many2many', '', ', '.join(test_tags[:2].mapped('name')))]
)
# update m2m + o2m
test_record.write({
'many2many_field': [(3, test_tags[0].id), (4, test_tags[2].id)],
'one2many_field': [
(0, 0, {'name': 'Child1'}),
(0, 0, {'name': 'Child2'}),
(0, 0, {'name': 'Child3'}),
],
})
self.flush_tracking()
last_message = test_record.message_ids[0]
self.assertTracking(
last_message,
[
('many2many_field', 'many2many', ', '.join(test_tags[:2].mapped('name')), ', '.join((test_tags[1] + test_tags[2]).mapped('name'))),
('one2many_field', 'one2many', '', ', '.join(('Child1', 'Child2', 'Child3'))),
]
)
# remove from o2m
test_record.write({'one2many_field': [(3, test_record.one2many_field[0].id)]})
self.flush_tracking()
last_message = test_record.message_ids[0]
self.assertTracking(
last_message,
[('one2many_field', 'one2many', ', '.join(('Child1', 'Child2', 'Child3')), ', '.join(('Child2', 'Child3')))]
)
@users('employee')
def test_mail_track_all_no2many(self):
test_record = self.env['mail.test.track.all'].create({
'company_id': self.env.company.id,
})
self.flush_tracking()
self.assertEqual(test_record.currency_id, self.env.ref('base.USD'))
messages = test_record.message_ids
today = fields.Date.today()
today_dt = fields.Datetime.to_datetime(today)
now = fields.Datetime.now()
test_record.write({
'boolean_field': True,
'char_field': 'char_value',
'date_field': today,
'datetime_field': now,
'float_field': 3.22,
'html_field': '<p>Html Value</p>',
'integer_field': 42,
'many2one_field_id': self.test_partner.id,
'monetary_field': 42.42,
'selection_field': 'first',
'text_field': 'text_value',
})
self.flush_tracking()
new_message = test_record.message_ids - messages
self.assertEqual(len(new_message), 1,
'Should have generated a tracking value')
self.assertTracking(
new_message,
[
('boolean_field', 'boolean', 0, 1),
('char_field', 'char', False, 'char_value'),
('date_field', 'date', False, today_dt),
('datetime_field', 'datetime', False, now),
('float_field', 'float', 0, 3.22),
('integer_field', 'integer', 0, 42),
('many2one_field_id', 'many2one', self.env['res.partner'], self.test_partner),
('monetary_field', 'monetary', False, (42.42, self.env.ref('base.USD'))),
('selection_field', 'selection', '', 'FIRST'),
('text_field', 'text', False, 'text_value'),
],
strict=True
)
@users('employee')
def test_mail_track_compute(self):
""" Test tracking of computed fields """
# no tracking at creation
compute_record = self.env['mail.test.track.compute'].create({})
self.flush_tracking()
self.assertEqual(len(compute_record.message_ids), 1)
self.assertEqual(len(compute_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_su = self.env['res.partner'].sudo().create({
'name': 'Foo',
'email': 'foo@example.com',
'phone': '1234567890',
})
compute_record.partner_id = partner_su
self.flush_tracking()
self.assertEqual(len(compute_record.message_ids), 2)
self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 4)
self.assertEqual(compute_record.message_ids.author_id, self.partner_employee)
self.assertTracking(compute_record.message_ids[0], [
('partner_id', 'many2one', False, partner_su),
('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_su.write({'name': 'Fool'})
self.flush_tracking()
self.assertEqual(len(compute_record.message_ids), 3)
self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 1)
self.assertTracking(compute_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_su.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(compute_record.partner_phone, '0987654321')
self.flush_tracking()
self.assertEqual(len(compute_record.message_ids), 4)
self.assertEqual(len(compute_record.message_ids[0].tracking_value_ids), 2)
self.assertTracking(compute_record.message_ids[0], [
('partner_name', 'char', 'Fool', 'Bar'),
('partner_email', 'char', 'foo@example.com', 'bar@example.com'),
])
@users('employee')
def test_mail_track_monetary(self):
""" Update a record with a tracked monetary field """
monetary_record = self.env['mail.test.track.monetary'].with_user(self.user_employee).create({
'company_id': self.user_employee.company_id.id,
})
self.flush_tracking()
self.assertEqual(len(monetary_record.message_ids), 1)
# Check if the tracking value have the correct currency and values
monetary_record.write({
'revenue': 100,
})
self.flush_tracking()
self.assertEqual(len(monetary_record.message_ids), 2)
self.assertTracking(monetary_record.message_ids[0], [
('revenue', 'monetary', 0, (100, self.env.company.currency_id)),
])
# Check if the tracking value have the correct currency and values after changing the value and the company
monetary_record.write({
'revenue': 200,
'company_id': self.company_2.id,
})
self.flush_tracking()
self.assertEqual(len(monetary_record.message_ids), 3)
self.assertTracking(monetary_record.message_ids[0], [
('revenue', 'monetary', 100, (200, self.company_2.currency_id)),
('company_currency', 'many2one', self.user_employee.company_id.currency_id, self.company_2.currency_id)
])
@users('employee')
def test_mail_track_selection_invalid(self):
""" Check that initial invalid selection values are allowed when tracking """
# Create a record with an initially invalid selection value
invalid_value = 'I love writing tests!'
record = self.env['mail.test.track.selection'].create({
'name': 'Test Invalid Selection Values',
'selection_type': 'first',
})
self.flush_tracking()
self.env.cr.execute(
"""
UPDATE mail_test_track_selection
SET selection_type = %s
WHERE id = %s
""",
[invalid_value, record.id]
)
record.invalidate_recordset()
self.assertEqual(record.selection_type, invalid_value)
# Write a valid selection value
record.selection_type = "second"
self.flush_tracking()
self.assertTracking(record.message_ids, [
('selection_type', 'char', invalid_value, 'Second'),
])
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()
tracking_values = self.env['mail.tracking.value'].search([('mail_message_id', '=', self.record.message_ids[0].id)])
formattedTrackingValues = [{
'changedField': 'Email From',
'id': tracking_values[0]['id'],
'fieldName': 'email_from',
'fieldType': 'char',
'newValue': {
'currencyId': False,
'value': 'X',
},
'oldValue': {
'currencyId': False,
'value': False,
},
}]
self.assertEqual(msg_emp[0].get('trackingValues'), [], "should not have protected tracking values")
self.assertEqual(msg_sudo[0].get('trackingValues'), formattedTrackingValues, "should have protected tracking values")
msg_emp = self.record._notify_by_email_prepare_rendering_context(self.record.message_ids[0], {})
msg_sudo = self.record.sudo()._notify_by_email_prepare_rendering_context(self.record.message_ids[0], {})
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.env.invalidate_all()
self.env.registry.clear_cache()
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 they 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')
@users('employee')
def test_track_invalid(self):
""" Test invalid use cases: unknown field, unsupported type, ... """
test_record = self.env['mail.test.track.all'].create({
'company_id': self.env.company.id,
})
self.flush_tracking()
# raise on non existing field
with self.assertRaises(ValueError):
self.env['mail.tracking.value']._create_tracking_values(
'', 'Test',
'not_existing_field', {'string': 'Test', 'type': 'char'},
test_record,
)
# raise on unsupported field type
with self.assertRaises(NotImplementedError):
self.env['mail.tracking.value']._create_tracking_values(
'', '<p>Html</p>',
'html_field', {'string': 'HTML', 'type': 'html'},
test_record,
)
@users('employee')
def test_track_sequence(self):
""" Update some tracked fields and check that the mail.tracking.value
are ordered according to their tracking_sequence """
record = self.record.with_env(self.env)
self.assertEqual(len(record.message_ids), 1)
# order: user_id -> 1, customer_id -> 2, container_id -> True -> 100, email_from -> True -> 100
ordered_fnames = ['user_id', 'customer_id', 'container_id', 'email_from']
# Update tracked fields, should generate tracking values correctly ordered
record.write({
'container_id': self.env['mail.test.container'].with_context(mail_create_nosubscribe=True).create({'name': 'Container'}).id,
'customer_id': self.user_admin.partner_id.id,
'email_from': 'new.from@test.example.com',
'name': 'Zboub',
'user_id': self.user_admin.id,
})
self.flush_tracking()
self.assertEqual(len(record.message_ids), 2, 'should have 1 new tracking message')
tracking_values = self.env['mail.tracking.value'].sudo().search(
[('mail_message_id', '=', record.message_ids[0].id)]
)
self.assertEqual(
tracking_values.field_id.mapped('name'),
ordered_fnames,
'Track: order, based on ID DESC, should follow tracking sequence (or name) on field'
)
# Manually create trackings, format should be the fallback to reorder them
new_msg = record.message_post(
body='Manual Hack of tracking',
subtype_xmlid='mail.mt_note',
)
custom_order_fnames = ['container_id', 'customer_id', 'email_from', 'user_id']
field_ids = [
self.env['ir.model.fields']._get(record._name, fname).id
for fname in custom_order_fnames
]
self.env['mail.tracking.value'].sudo().create([
{
'field_id': field_id,
'mail_message_id': new_msg.id,
'old_value_char': 'unimportant',
'new_value_char': 'unimportant',
}
for field_id in field_ids
])
tracking_values = self.env['mail.tracking.value'].sudo().search(
[('mail_message_id', '=', record.message_ids[0].id)]
)
self.assertEqual(
tracking_values.field_id.mapped('name'),
list(reversed(custom_order_fnames)),
'Tracking model: order, based on ID DESC, following reverted insertion'
)
tracking_formatted = tracking_values._tracking_value_format()
self.assertEqual(
[t['fieldName'] for t in tracking_formatted],
ordered_fnames,
'Track: formatted order is correctly based on field sequence definition'
)
@users('employee')
def test_unlinked_field(self):
""" Check that removing a field removes its tracking values. """
record = self.record.with_env(self.env)
record.write({'email_from': 'new_value'}) # create a tracking value
record_other = self.env['mail.test.ticket'].create({})
self.flush_tracking()
record_other.write({'email_from': 'email.from.1@example.com'})
self.flush_tracking()
record_other.write({
'customer_id': self.test_partner.id,
'email_from': 'email.from.2@example.com',
'user_id': self.env.user.id,
})
self.flush_tracking()
self.assertTracking(
record.message_ids[0],
[('email_from', 'char', False, 'new_value')],
strict=True,
)
self.assertTracking(
record_other.message_ids[0],
[('customer_id', 'integer', False, self.test_partner.id),
('email_from', 'char', 'email.from.1@example.com', 'email.from.2@example.com'),
('user_id', 'integer', False, self.env.user.id)],
strict=True,
)
self.assertTracking(
record_other.message_ids[1],
[('email_from', 'char', False, 'email.from.1@example.com')],
strict=True,
)
# check display / format
trackings_all = (record + record_other).message_ids.sudo().tracking_value_ids
trackings_all_sorted = [
trackings_all.filtered(lambda t: t.field_id.name == 'user_id'), # tracking=1
trackings_all.filtered(lambda t: t.field_id.name == 'customer_id'), # tracking=2
trackings_all.filtered(lambda t: t.field_id.name == 'email_from')[0], # tracking=True -> 100
trackings_all.filtered(lambda t: t.field_id.name == 'email_from')[1], # tracking=True -> 100
trackings_all.filtered(lambda t: t.field_id.name == 'email_from')[2], # tracking=True -> 100
]
fields_info = [
('user_id', 'many2one', 'Responsible'),
('customer_id', 'many2one', 'Customer'),
('email_from', 'char', 'Email From'),
('email_from', 'char', 'Email From'),
('email_from', 'char', 'Email From'),
]
values_info = [
('', self.env.user.name),
('', self.test_partner.name),
(False, 'new_value'),
('email.from.1@example.com', 'email.from.2@example.com'),
(False, 'email.from.1@example.com'),
]
formatted = trackings_all._tracking_value_format()
self.assertEqual(
formatted,
[
{
'changedField': field_info[2],
'id': tracking.id,
'fieldName': field_info[0],
'fieldType': field_info[1],
'newValue': {
'currencyId': False,
'value': values[1],
},
'oldValue': {
'currencyId': False,
'value': values[0],
},
}
for tracking, field_info, values in zip(trackings_all_sorted, fields_info, values_info)
]
)
# remove fields
fields_toremove = self.env['ir.model.fields'].sudo().search([
('model', '=', 'mail.test.ticket'),
('name', 'in', ('email_from', 'user_id', 'datetime')) # also include a non tracked field
])
fields_toremove.with_context(_force_unlink=True).unlink()
self.assertEqual(len(trackings_all.exists()), 5)
# check display / format, even if field is removed
formatted = trackings_all._tracking_value_format()
self.assertEqual(
formatted,
[
{
'changedField': field_info[2],
'id': tracking.id,
'fieldName': field_info[0],
'fieldType': field_info[1],
'newValue': {
'currencyId': False,
'value': values[1],
},
'oldValue': {
'currencyId': False,
'value': values[0],
},
}
for tracking, field_info, values in zip(trackings_all_sorted, fields_info, values_info)
]
)