mirror of https://github.com/OCA/social.git
[10.0] Improving the mail tracking value (#170)
Improve the tracking of changed values on database fields ("mail.tracking.value" functionality) by adding many2many and one2many support. As well as a more user friendly view to consult changes recorded.pull/677/head
parent
c564d483bd
commit
0c59eb2d59
|
@ -0,0 +1,77 @@
|
||||||
|
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
|
||||||
|
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
|
||||||
|
:alt: License: AGPL-3
|
||||||
|
|
||||||
|
==============================
|
||||||
|
Tracking value change improved
|
||||||
|
==============================
|
||||||
|
|
||||||
|
This module extends the mail_tracking_value functionality that records
|
||||||
|
value changes on predefined fields.
|
||||||
|
It adds support for many2many and one2many fields, which are not handled
|
||||||
|
well by default.
|
||||||
|
It also implements a new view (little bit more user friendly than the
|
||||||
|
existing one) to watch for changes
|
||||||
|
|
||||||
|
Installation
|
||||||
|
============
|
||||||
|
|
||||||
|
To install this module, you need to:
|
||||||
|
|
||||||
|
#. Just install the module
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
=============
|
||||||
|
|
||||||
|
To configure this module, you need to:
|
||||||
|
|
||||||
|
# No configuration is necessary
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=====
|
||||||
|
|
||||||
|
To access the new view displaying value changes :
|
||||||
|
|
||||||
|
Settings -> Technical -> Improved tracking values
|
||||||
|
|
||||||
|
|
||||||
|
Known issues / Roadmap
|
||||||
|
======================
|
||||||
|
|
||||||
|
* Improve rendering of values depending of type using qweb widgets
|
||||||
|
|
||||||
|
Bug Tracker
|
||||||
|
===========
|
||||||
|
|
||||||
|
Bugs are tracked on `GitHub Issues
|
||||||
|
<https://github.com/OCA/mail_improved_tracking_value/issues>`_. In case of trouble, please
|
||||||
|
check there if your issue has already been reported. If you spotted it first,
|
||||||
|
help us smash it by providing detailed and welcomed feedback.
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Images
|
||||||
|
------
|
||||||
|
|
||||||
|
* Odoo Community Association: `Icon <https://github.com/OCA/maintainer-tools/blob/master/template/module/static/description/icon.svg>`_.
|
||||||
|
|
||||||
|
Contributors
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Thierry Ducrest <thierry.ducrest@camptocamp.com>
|
||||||
|
|
||||||
|
Maintainer
|
||||||
|
----------
|
||||||
|
|
||||||
|
.. image:: https://odoo-community.org/logo.png
|
||||||
|
:alt: Odoo Community Association
|
||||||
|
:target: https://odoo-community.org
|
||||||
|
|
||||||
|
This module is maintained by the OCA.
|
||||||
|
|
||||||
|
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||||
|
mission is to support the collaborative development of Odoo features and
|
||||||
|
promote its widespread use.
|
||||||
|
|
||||||
|
To contribute to this module, please visit https://odoo-community.org.
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 Camptocamp SA
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from . import models
|
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 Camptocamp SA
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Improved tracking value change',
|
||||||
|
'version': '10.0.1.0.0',
|
||||||
|
'summary': 'Improves tracking changed values for certain type of fields.'
|
||||||
|
'Adds a user-friendly view to consult them.',
|
||||||
|
'author': 'Camptocamp, Odoo Community Association (OCA)',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'category': 'Tools',
|
||||||
|
'website': 'http://www.camptocamp.com',
|
||||||
|
'depends': [
|
||||||
|
'base',
|
||||||
|
'mail'
|
||||||
|
],
|
||||||
|
'data': [
|
||||||
|
'views/mail_tracking_value.xml'
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
}
|
|
@ -0,0 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 Camptocamp SA
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
from . import mail_tracking_value
|
|
@ -0,0 +1,78 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2017 Camptocamp SA
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class MailTrackingValue(models.Model):
|
||||||
|
|
||||||
|
_inherit = "mail.tracking.value"
|
||||||
|
|
||||||
|
new_value_formatted = fields.Char(compute='_compute_formatted_value',
|
||||||
|
string='New value')
|
||||||
|
old_value_formatted = fields.Char(compute='_compute_formatted_value',
|
||||||
|
string='Old value')
|
||||||
|
record_name = fields.Char(related='mail_message_id.record_name')
|
||||||
|
model = fields.Char(related='mail_message_id.model', store='True',
|
||||||
|
string='Model')
|
||||||
|
|
||||||
|
@api.depends('new_value_char', 'new_value_integer', 'new_value_float',
|
||||||
|
'new_value_text', 'new_value_datetime', 'new_value_monetary',
|
||||||
|
'old_value_char', 'old_value_integer', 'old_value_float',
|
||||||
|
'old_value_text', 'old_value_datetime', 'old_value_monetary')
|
||||||
|
def _compute_formatted_value(self):
|
||||||
|
""" Sets the value formatted field used in the view """
|
||||||
|
# Could be improved, by styling in the view depending on type of field
|
||||||
|
for record in self:
|
||||||
|
if record.field_type in ('many2many', 'one2many', 'char'):
|
||||||
|
record.new_value_formatted = record.new_value_char
|
||||||
|
record.old_value_formatted = record.old_value_char
|
||||||
|
elif record.field_type == 'integer':
|
||||||
|
record.new_value_formatted = str(record.new_value_integer)
|
||||||
|
record.old_value_formatted = str(record.old_value_integer)
|
||||||
|
elif record.field_type == 'float':
|
||||||
|
record.new_value_formatted = str(record.new_value_float)
|
||||||
|
record.old_value_formatted = str(record.old_value_float)
|
||||||
|
elif record.field_type == 'monetary':
|
||||||
|
record.new_value_formatted = str(record.new_value_monetary)
|
||||||
|
record.old_value_formatted = str(record.old_value_monetary)
|
||||||
|
elif record.field_type == 'datetime':
|
||||||
|
record.new_value_formatted = str(record.new_value_datetime)
|
||||||
|
record.old_value_formatted = str(record.old_value_datetime)
|
||||||
|
elif record.field_type == 'text':
|
||||||
|
record.new_value_formatted = record.new_value_text
|
||||||
|
record.old_value_formatted = record.old_value_text
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def create_tracking_values(self, old_value, new_value, col_name, col_info):
|
||||||
|
""" Add tacking capabilities for many2many and one2many fields """
|
||||||
|
if col_info['type'] in ('many2many', 'one2many'):
|
||||||
|
|
||||||
|
def get_values(source, prefix):
|
||||||
|
if source:
|
||||||
|
names = ', '.join(source.mapped('display_name'))
|
||||||
|
json_ids = json.dumps(source.ids)
|
||||||
|
else:
|
||||||
|
names = ''
|
||||||
|
json_ids = json.dumps([])
|
||||||
|
return {
|
||||||
|
'{}_value_char'.format(prefix): names,
|
||||||
|
'{}_value_text'.format(prefix): json_ids,
|
||||||
|
}
|
||||||
|
|
||||||
|
values = {
|
||||||
|
'field': col_name,
|
||||||
|
'field_desc': col_info['string'],
|
||||||
|
'field_type': col_info['type'],
|
||||||
|
}
|
||||||
|
values.update(get_values(old_value, 'old'))
|
||||||
|
values.update(get_values(new_value, 'new'))
|
||||||
|
return values
|
||||||
|
else:
|
||||||
|
return super(MailTrackingValue, self).create_tracking_values(
|
||||||
|
old_value, new_value,
|
||||||
|
col_name, col_info
|
||||||
|
)
|
|
@ -0,0 +1 @@
|
||||||
|
from . import test_improved_tracking_value
|
|
@ -0,0 +1,154 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018 Camptocamp SA
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)
|
||||||
|
|
||||||
|
import json
|
||||||
|
from odoo.tests.common import TransactionCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestImproveTrackingValue(TransactionCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestImproveTrackingValue, self).setUp()
|
||||||
|
self.model = self.env['mail.tracking.value']
|
||||||
|
self.msg = self.env['mail.message'].create({
|
||||||
|
'message_type': 'email'
|
||||||
|
})
|
||||||
|
self.mr = self.env.ref('base.res_partner_title_mister')
|
||||||
|
self.dr = self.env.ref('base.res_partner_title_doctor')
|
||||||
|
self.mm = self.env.ref('base.res_partner_title_madam')
|
||||||
|
self.pf = self.env.ref('base.res_partner_title_prof')
|
||||||
|
|
||||||
|
def test_change_one2many(self):
|
||||||
|
"""Test tracking one2many changes"""
|
||||||
|
tracking = self.model.create_tracking_values(
|
||||||
|
self.mr, self.dr, 'testing_col',
|
||||||
|
{'string': 'TestingField', 'type': 'one2many'})
|
||||||
|
self.assertEqual(tracking['old_value_char'], self.mr.display_name)
|
||||||
|
self.assertEqual(tracking['new_value_char'], self.dr.display_name)
|
||||||
|
self.assertEqual(tracking['old_value_text'], json.dumps([self.mr.id]))
|
||||||
|
self.assertEqual(tracking['new_value_text'], json.dumps([self.dr.id]))
|
||||||
|
|
||||||
|
def test_change_one2many_empty(self):
|
||||||
|
"""Test tracking one2many changes emtpy"""
|
||||||
|
tracking = self.model.create_tracking_values(
|
||||||
|
self.mr, None, 'testing_col',
|
||||||
|
{'string': 'TestingField', 'type': 'one2many'})
|
||||||
|
self.assertEqual(tracking['old_value_char'], self.mr.display_name)
|
||||||
|
self.assertEqual(tracking['new_value_char'], '')
|
||||||
|
self.assertEqual(tracking['old_value_text'], json.dumps([self.mr.id]))
|
||||||
|
self.assertEqual(tracking['new_value_text'], json.dumps([]))
|
||||||
|
|
||||||
|
def test_change_many2many(self):
|
||||||
|
"""Test tracking many2many changes"""
|
||||||
|
oldvalue = self.env['res.partner.title'].browse([self.mr.id,
|
||||||
|
self.dr.id])
|
||||||
|
newvalue = self.env['res.partner.title'].browse([self.mm.id,
|
||||||
|
self.pf.id])
|
||||||
|
tracking = self.model.create_tracking_values(
|
||||||
|
oldvalue, newvalue, 'testing_col',
|
||||||
|
{'string': 'TestingField', 'type': 'many2many'})
|
||||||
|
self.assertEqual(tracking['old_value_char'],
|
||||||
|
self.mr.display_name + ', ' + self.dr.display_name)
|
||||||
|
self.assertEqual(tracking['new_value_char'],
|
||||||
|
self.mm.display_name + ', ' + self.pf.display_name)
|
||||||
|
self.assertEqual(tracking['old_value_text'],
|
||||||
|
json.dumps(oldvalue.ids))
|
||||||
|
self.assertEqual(tracking['new_value_text'],
|
||||||
|
json.dumps(newvalue.ids))
|
||||||
|
|
||||||
|
def test_char_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'char',
|
||||||
|
'old_value_char': 'weakness',
|
||||||
|
'new_value_char': 'strength',
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, 'weakness')
|
||||||
|
self.assertEqual(r.new_value_formatted, 'strength')
|
||||||
|
|
||||||
|
def test_many2many_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'many2many',
|
||||||
|
'old_value_char': '123',
|
||||||
|
'new_value_char': '456',
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, '123')
|
||||||
|
self.assertEqual(r.new_value_formatted, '456')
|
||||||
|
|
||||||
|
def test_one2many_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'one2many',
|
||||||
|
'old_value_char': '123',
|
||||||
|
'new_value_char': '456',
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, '123')
|
||||||
|
self.assertEqual(r.new_value_formatted, '456')
|
||||||
|
|
||||||
|
def test_integer_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'integer',
|
||||||
|
'old_value_integer': 1,
|
||||||
|
'new_value_integer': 3,
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, str(1))
|
||||||
|
self.assertEqual(r.new_value_formatted, str(3))
|
||||||
|
|
||||||
|
def test_float_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'float',
|
||||||
|
'old_value_float': 1.1,
|
||||||
|
'new_value_float': 3.14159,
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, str(1.1))
|
||||||
|
self.assertEqual(r.new_value_formatted, str(3.14159))
|
||||||
|
|
||||||
|
def test_monetary_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'monetary',
|
||||||
|
'old_value_monetary': 3.45,
|
||||||
|
'new_value_monetary': 5.45,
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, str(3.45))
|
||||||
|
self.assertEqual(r.new_value_formatted, str(5.45))
|
||||||
|
|
||||||
|
def test_datetime_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'datetime',
|
||||||
|
'old_value_datetime': '2018-01-01',
|
||||||
|
'new_value_datetime': '2018-01-04',
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, '2018-01-01 00:00:00')
|
||||||
|
self.assertEqual(r.new_value_formatted, '2018-01-04 00:00:00')
|
||||||
|
|
||||||
|
def test_text_tracking_value(self):
|
||||||
|
r = self.model.create({
|
||||||
|
'mail_message_id': self.msg.id,
|
||||||
|
'field_type': 'text',
|
||||||
|
'old_value_text': 'previous',
|
||||||
|
'new_value_text': 'next',
|
||||||
|
'field': 'test',
|
||||||
|
'field_desc': 'desc',
|
||||||
|
})
|
||||||
|
self.assertEqual(r.old_value_formatted, 'previous')
|
||||||
|
self.assertEqual(r.new_value_formatted, 'next')
|
|
@ -0,0 +1,74 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<record id="tracking_change_list" model="ir.ui.view">
|
||||||
|
<field name="name">Tracking change list"</field>
|
||||||
|
<field name="model">mail.tracking.value</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Tracking change list" default_order="create_date desc"
|
||||||
|
create="false" edit="false" delete="false">
|
||||||
|
<field name="create_date" string="On"/>
|
||||||
|
<field name="record_name" string="For"/>
|
||||||
|
<field name="field_desc" string="Field changed"/>
|
||||||
|
<field name="old_value_formatted"/>
|
||||||
|
<field name="new_value_formatted"/>
|
||||||
|
<field name="model"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="tracking_change_form_view" model="ir.ui.view">
|
||||||
|
<field name="name">Tracking change form</field>
|
||||||
|
<field name="model">mail.tracking.value</field>
|
||||||
|
<field name="priority" eval="20"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<field name="create_date" class="oe-inline"/>
|
||||||
|
<group>
|
||||||
|
<group string="">
|
||||||
|
<field name="record_name" string="For"/>
|
||||||
|
<field name="field_desc" string="Field changed"/>
|
||||||
|
<field name="old_value_formatted"/>
|
||||||
|
<field name="new_value_formatted"/>
|
||||||
|
<field name="model"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="tracking_change_list_filter" model="ir.ui.view">
|
||||||
|
<field name="name">Tracking change list filter</field>
|
||||||
|
<field name="model">mail.tracking.value</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Search">
|
||||||
|
<group>
|
||||||
|
<field name="model"/>
|
||||||
|
</group>
|
||||||
|
<group expand="0" string="Group by">
|
||||||
|
<filter group_by="group_by_model" string="Model" context="{'group_by':'model'}"/>
|
||||||
|
</group>
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_view_tracking_changes" model="ir.actions.act_window">
|
||||||
|
<field name="name">View last tracked changes</field>
|
||||||
|
<field name="res_model">mail.tracking.value</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field name="view_ids" eval="[(5, 0, 0),
|
||||||
|
(0, 0, {'view_mode': 'tree', 'view_id': ref('tracking_change_list')}),
|
||||||
|
(0, 0, {'view_mode': 'form', 'view_id': ref('tracking_change_form_view')})]"
|
||||||
|
/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem action="action_view_tracking_changes"
|
||||||
|
id ="menu_action_view_tracking_changes"
|
||||||
|
name="Improved tracking values"
|
||||||
|
parent="base.menu_email"
|
||||||
|
sequence="10"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</odoo>
|
Loading…
Reference in New Issue