'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>
832 lines
36 KiB
Python
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)
|
|
]
|
|
)
|