[FIX] mail: fix tracking formatting when having several models

In odoo/odoo@9c9552fd20 a support of tracking value belonging to another model
than the message has been added. In such cases the formatting method written
at odoo/odoo#124182 fails as it expects a single model for all tracking values
it receives. When field is removed 'model' is False, and we fallback on
mail_message model.

Part-of: odoo/odoo#156571
This commit is contained in:
Alvaro Fuentes
2024-03-05 19:21:39 +00:00
committed by Thibault Delavallée
parent b1d403c4b4
commit 0db60bf1c2
5 changed files with 140 additions and 19 deletions
+9 -2
View File
@@ -2605,8 +2605,8 @@ msgstr ""
#. module: mail
#: model:ir.model.fields,help:mail.field_mail_alias_domain__default_from
msgid ""
"Default from when it does not match outgoing server filters. Can be either a "
"local-part e.g. 'notifications' either a complete email address e.g. "
"Default from when it does not match outgoing server filters. Can be either a"
" local-part e.g. 'notifications' either a complete email address e.g. "
"'notifications@example.com' to override all outgoing emails."
msgstr ""
@@ -9402,6 +9402,13 @@ msgstr ""
msgid "Unit of delay"
msgstr ""
#. module: mail
#. odoo-python
#: code:addons/mail/models/mail_tracking_value.py:0
#, python-format
msgid "Unknown"
msgstr ""
#. module: mail
#. odoo-python
#: code:addons/mail/models/mail_notification.py:0
+42 -17
View File
@@ -3,7 +3,7 @@
from datetime import datetime
from odoo import api, fields, models
from odoo import api, fields, models, _
class MailTracking(models.Model):
@@ -112,34 +112,59 @@ class MailTracking(models.Model):
to display tracking values. Order it according to asked display, aka
ascending sequence (and field name).
:return list: for each tracking value in self, their formatted display
values given as a dict;
"""
model_map = {}
for tracking in self:
model = tracking.field_id.model or tracking.mail_message_id.model
model_map.setdefault(model, self.browse())
model_map[model] += tracking
formatted = []
for model, trackings in model_map.items():
formatted += trackings._tracking_value_format_model(model)
return formatted
def _tracking_value_format_model(self, model):
""" Return structure and formatted data structure to be used by chatter
to display tracking values. Order it according to asked display, aka
ascending sequence (and field name).
:return list: for each tracking value in self, their formatted display
values given as a dict;
"""
if not self:
return []
field_models = self.field_id.mapped('model')
if len(set(field_models)) != 1:
raise ValueError('All tracking value should belong to the same model.')
TrackedModel = self.env[field_models[0]]
tracked_fields = TrackedModel.fields_get(self.field_id.mapped('name'), attributes={'string', 'type'})
# fetch model-based information
if model:
TrackedModel = self.env[model]
tracked_fields = TrackedModel.fields_get(self.field_id.mapped('name'), attributes={'string', 'type'})
model_sequence_info = dict(TrackedModel._mail_track_order_fields(tracked_fields)) if model else {}
else:
tracked_fields, model_sequence_info = {}, {}
# generate sequence of trackings
fields_sequence_map = dict(
{
tracking.field_info['name']: tracking.field_info.get('sequence', 100)
for tracking in self.filtered('field_info')
},
**model_sequence_info,
)
# generate dict of field information, if available
fields_col_info = (
tracked_fields.get(tracking.field_id.name) or {
'string': tracking.field_info['desc'],
'type': tracking.field_info['type'],
}
for tracking in self
)
fields_sequence_map = dict(
{tracking.field_info['name']: tracking.field_info.get('sequence', 100)
for tracking in self.filtered('field_info')},
**dict(TrackedModel._mail_track_order_fields(tracked_fields))
'string': tracking.field_info['desc'] if tracking.field_info else _('Unknown'),
'type': tracking.field_info['type'] if tracking.field_info else 'char',
} for tracking in self
)
formatted = [
{
'changedField': col_info['string'],
'id': tracking.id,
'fieldName': tracking.field_id.name or tracking.field_info['name'],
'fieldName': tracking.field_id.name or (tracking.field_info['name'] if tracking.field_info else 'unknown'),
'fieldType': col_info['type'],
'newValue': {
'currencyId': tracking.currency_id.id,
@@ -153,7 +178,7 @@ class MailTracking(models.Model):
for tracking, col_info in zip(self, fields_col_info)
]
formatted.sort(
key=lambda info: (fields_sequence_map[info['fieldName']], info['fieldName']),
key=lambda info: (fields_sequence_map.get(info['fieldName'], 100), info['fieldName']),
reverse=False,
)
return formatted
@@ -152,6 +152,16 @@ class MailTestTrackCompute(models.Model):
partner_phone = fields.Char(related='partner_id.phone', tracking=True)
class MailTestTrackGroups(models.Model):
_name = 'mail.test.track.groups'
_description = "Test tracking with groups"
_inherit = ['mail.thread']
name = fields.Char(tracking=1)
partner_id = fields.Many2one('res.partner', tracking=2, groups="base.group_user")
secret = fields.Char(tracking=3, groups="base.group_user")
class MailTestTrackMonetary(models.Model):
_name = 'mail.test.track.monetary'
_description = 'Test tracking monetary field'
@@ -50,6 +50,7 @@ access_mail_test_track_all,mail.test.track.all,model_mail_test_track_all,base.gr
access_mail_test_track_all_m2m,mail.test.track.all.m2m,model_mail_test_track_all_m2m,base.group_user,1,1,1,1
access_mail_test_track_all_o2m,mail.test.track.all.o2m,model_mail_test_track_all_o2m,base.group_user,1,1,1,1
access_mail_test_track_compute,mail.test.track.compute,model_mail_test_track_compute,base.group_user,1,1,1,1
access_mail_test_track_groups,mail.test.track.groups,model_mail_test_track_groups,base.group_user,1,1,1,1
access_mail_test_track_monetary,mail.test.track.monetary,model_mail_test_track_monetary,base.group_user,1,1,1,1
access_mail_test_track_selection_portal,mail.test.track.selection.portal,model_mail_test_track_selection,base.group_portal,0,0,0,0
access_mail_test_track_selection_user,mail.test.track.selection.user,model_mail_test_track_selection,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
50 access_mail_test_track_all_m2m mail.test.track.all.m2m model_mail_test_track_all_m2m base.group_user 1 1 1 1
51 access_mail_test_track_all_o2m mail.test.track.all.o2m model_mail_test_track_all_o2m base.group_user 1 1 1 1
52 access_mail_test_track_compute mail.test.track.compute model_mail_test_track_compute base.group_user 1 1 1 1
53 access_mail_test_track_groups mail.test.track.groups model_mail_test_track_groups base.group_user 1 1 1 1
54 access_mail_test_track_monetary mail.test.track.monetary model_mail_test_track_monetary base.group_user 1 1 1 1
55 access_mail_test_track_selection_portal mail.test.track.selection.portal model_mail_test_track_selection base.group_portal 0 0 0 0
56 access_mail_test_track_selection_user mail.test.track.selection.user model_mail_test_track_selection base.group_user 1 1 1 1
@@ -746,6 +746,84 @@ class TestTrackingInternals(MailCommon):
test_record,
)
@users('employee')
def test_track_multi_models(self):
""" Some models track value coming from another model e.g. when having
a sub model (lines) on which some value should be tracked on a parent
model. Test there is no model mismatch. """
main_track = self.env['mail.test.track.all'].create({
'name': 'Multi Models Tracking',
'char_field': 'char_value',
})
self.flush_tracking()
self.assertEqual(len(main_track.message_ids), 1)
self.assertFalse(main_track.message_ids.tracking_value_ids)
sub_track = self.env['mail.test.track.groups'].create({
'name': 'Groups',
'secret': 'secret',
})
# some custom code generates tracking values on main_track
main_track.message_post(
body='Custom Log with Tracking',
tracking_value_ids=[
(0, 0, {
'field_id': self.env['ir.model.fields']._get(sub_track._name, 'secret').id,
'new_value_char': 'secret',
'old_value_char': False,
}),
(0, 0, {
'field_id': False,
'new_value_integer': self.env.uid,
'old_value_integer': False,
}),
(0, 0, {
'field_id': False,
'field_info': {
'desc': 'Old integer',
'name': 'Removed',
'sequence': 35,
'type': 'integer',
},
'new_value_integer': 35,
'old_value_integer': 30,
}),
],
)
trackings = main_track.message_ids.sudo().tracking_value_ids
self.assertEqual(len(trackings), 3)
# check formatting, as it fetches info on model
formatted = trackings._tracking_value_format()
self.assertEqual(
formatted,
[
{
'changedField': 'Secret',
'id': trackings[0].id,
'fieldName': 'secret',
'fieldType': 'char',
'newValue': {'currencyId': False, 'value': 'secret'},
'oldValue': {'currencyId': False, 'value': False}
}, {
'changedField': 'Old integer',
'id': trackings[2].id,
'fieldName': 'Removed',
'fieldType': 'integer',
'newValue': {'currencyId': False, 'value': 35},
'oldValue': {'currencyId': False, 'value': 30}
}, {
'changedField': 'Unknown',
'id': trackings[1].id,
'fieldName': 'unknown',
'fieldType': 'char',
'newValue': {'currencyId': False, 'value': False},
'oldValue': {'currencyId': False, 'value': False}
}
]
)
@users('employee')
def test_track_sequence(self):
""" Update some tracked fields and check that the mail.tracking.value