From be22968e91d7402134af946925ae31a813538612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20BEAU?= Date: Mon, 9 Jan 2023 09:57:21 +0100 Subject: [PATCH] tracking_manager: refactor code, remove tracking_model - remove tracking_model (less model, less code, less issue) - add automatic rule for default config (this avoid missing tracked field after module installation) --- tracking_manager/__manifest__.py | 5 +- tracking_manager/models/__init__.py | 1 - tracking_manager/models/ir_model.py | 193 ++++++++++-------- tracking_manager/models/mail_thread.py | 9 +- tracking_manager/models/tracking_model.py | 52 ----- tracking_manager/readme/CONTRIBUTORS.rst | 1 + tracking_manager/security/ir.model.access.csv | 2 - .../tests/test_tracking_manager.py | 89 ++++---- tracking_manager/views/ir_model.xml | 67 +++--- tracking_manager/views/ir_model_fields.xml | 34 ++- 10 files changed, 217 insertions(+), 236 deletions(-) delete mode 100644 tracking_manager/models/tracking_model.py delete mode 100644 tracking_manager/security/ir.model.access.csv diff --git a/tracking_manager/__manifest__.py b/tracking_manager/__manifest__.py index 9d4800b0a..d3820f0ad 100644 --- a/tracking_manager/__manifest__.py +++ b/tracking_manager/__manifest__.py @@ -10,7 +10,7 @@ "category": "Tools", "website": "https://github.com/OCA/server-tools", "author": "Akretion, Odoo Community Association (OCA)", - "maintainers": ["Kev-Roche"], + "maintainers": ["Kev-Roche", "sebastienbeau"], "license": "AGPL-3", "application": False, "installable": True, @@ -19,9 +19,8 @@ "mail", ], "data": [ - "views/ir_model.xml", "views/ir_model_fields.xml", + "views/ir_model.xml", "views/message_template.xml", - "security/ir.model.access.csv", ], } diff --git a/tracking_manager/models/__init__.py b/tracking_manager/models/__init__.py index 3e5ef9b11..430245fe7 100644 --- a/tracking_manager/models/__init__.py +++ b/tracking_manager/models/__init__.py @@ -1,3 +1,2 @@ from . import mail_thread -from . import tracking_model from . import ir_model diff --git a/tracking_manager/models/ir_model.py b/tracking_manager/models/ir_model.py index 5cc13fb07..ea1df8b07 100644 --- a/tracking_manager/models/ir_model.py +++ b/tracking_manager/models/ir_model.py @@ -2,116 +2,129 @@ # @author Kévin Roche # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from ast import literal_eval + from odoo import api, fields, models class IrModel(models.Model): _inherit = "ir.model" - o2m_model_ids = fields.One2many( - string="One2many Models", - comodel_name="ir.model", - compute="_compute_o2m_model_ids", - readonly=True, - ) - custom_tracking_field_ids = fields.One2many( - comodel_name="tracking.model.field", - inverse_name="model_id", - string="Custom Tracked Fields", - compute="_compute_custom_tracking_fields", + active_custom_tracking = fields.Boolean(default=False) + tracked_field_count = fields.Integer(compute="_compute_tracked_field_count") + automatic_custom_tracking = fields.Boolean( + compute="_compute_automatic_custom_tracking", + readonly=False, store=True, - ) - field_count = fields.Integer(compute="_compute_tracked_field_count") - apply_custom_tracking = fields.Boolean( - "Apply custom tracking on fields", default=False, - help="""Add tracking on all this model fields if they - are not readonly True, neither computed.""", + help=("If tick new field will be automatically tracked " "if the domain match"), + ) + automatic_custom_tracking_domain = fields.Char( + compute="_compute_automatic_custom_tracking_domain", + store=True, + readonly=False, ) - @api.depends("field_id", "apply_custom_tracking") - def _compute_custom_tracking_fields(self): - for rec in self: - fields = rec.env["ir.model.fields"].search( - [("model_id.model", "=", rec.model)] + @api.depends("active_custom_tracking") + def _compute_automatic_custom_tracking(self): + for record in self: + record.automatic_custom_tracking = False + + def _default_automatic_custom_tracking_domain_rules(self): + return { + "product.product": [ + "|", + ("ttype", "!=", "one2many"), + ("name", "in", ["barcode_ids"]), + ], + "sale.order": [ + "|", + ("ttype", "!=", "one2many"), + ("name", "in", ["line_ids"]), + ], + "account.move": [ + "|", + ("ttype", "!=", "one2many"), + ("name", "in", ["invoice_line_ids"]), + ], + "default_automatic_rule": [("ttype", "!=", "one2many")], + } + + @api.depends("automatic_custom_tracking") + def _compute_automatic_custom_tracking_domain(self): + rules = self._default_automatic_custom_tracking_domain_rules() + for record in self: + record.automatic_custom_tracking_domain = str( + rules.get(record.model) or rules.get("default_automatic_rule") ) - fields_ids = [] - for field in fields: - values = {} - values["tracking_field_id"] = field.id - values["model_id"] = rec.id - fields_ids.append((0, 0, values)) - rec.custom_tracking_field_ids = [(6, 0, [])] - rec.custom_tracking_field_ids = fields_ids - @api.depends("field_id", "apply_custom_tracking") - def _compute_o2m_model_ids(self): - for rec in self: - o2m_model_list = [] - if rec.apply_custom_tracking: - for field in rec.field_id: - if field.ttype == "one2many": - o2m_relation_id = self.search( - [("model", "=", field.relation)], limit=1 - ) - o2m_model_list.append(o2m_relation_id.id) - rec.o2m_model_ids = [(6, 0, o2m_model_list)] + def update_custom_tracking(self): + for record in self: + fields = record.field_id.filtered("trackable").filtered_domain( + literal_eval(record.automatic_custom_tracking_domain) + ) + fields.write({"custom_tracking": True}) + untrack_fields = record.field_id - fields + untrack_fields.write({"custom_tracking": False}) - @api.depends( - "custom_tracking_field_ids", "custom_tracking_field_ids.custom_tracking" - ) + @api.depends("field_id.custom_tracking") def _compute_tracked_field_count(self): for rec in self: - rec.field_count = sum( - rec.custom_tracking_field_ids.mapped("custom_tracking") - ) - - def show_custom_tracked_field(self): - return { - "name": "Custom tracked fields", - "type": "ir.actions.act_window", - "res_id": self.id, - "view_mode": "tree", - "res_model": "tracking.model.field", - "view_id": self.env.ref( - "tracking_manager.custom_tracking_field_view_tree" - ).id, - "target": "current", - "domain": [("id", "in", self.custom_tracking_field_ids.ids)], - } - - def show_o2m_models(self): - return { - "name": "o2m models", - "type": "ir.actions.act_window", - "res_id": self.id, - "view_mode": "tree,form", - "res_model": "ir.model", - "views": [ - (self.env.ref("base.view_model_tree").id, "tree"), - (self.env.ref("base.view_model_form").id, "form"), - ], - "target": "current", - "domain": [("id", "in", self.o2m_model_ids.ids)], - } + rec.tracked_field_count = len(rec.field_id.filtered("custom_tracking")) class IrModelFields(models.Model): _inherit = "ir.model.fields" - custom_tracking_id = fields.Many2one( - comodel_name="tracking.model.field", - string="Technical Custom Tracking Field", - compute="_compute_custom_tracking_field_id", + custom_tracking = fields.Boolean( + compute="_compute_custom_tracking", + store=True, + readonly=False, + ) + native_tracking = fields.Boolean( + compute="_compute_native_tracking", + store=True, + ) + trackable = fields.Boolean( + compute="_compute_trackable", + store=True, ) - @api.depends("model_id.apply_custom_tracking") - def _compute_custom_tracking_field_id(self): - for rec in self: - if rec.model_id.apply_custom_tracking: - rec.custom_tracking_id = self.env["tracking.model.field"].search( - [("tracking_field_id", "=", rec.id), ("model_id", "=", rec._name)], - limit=1, - ) + @api.depends("native_tracking") + def _compute_custom_tracking(self): + for record in self: + if record.model_id.automatic_custom_tracking: + domain = literal_eval(record.model_id.automatic_custom_tracking_domain) + record.custom_tracking = bool(record.filtered_domain(domain)) else: - rec.custom_tracking_id = False + record.custom_tracking = record.native_tracking + + @api.depends("tracking") + def _compute_native_tracking(self): + for record in self: + record.native_tracking = bool(record.tracking) + + @api.depends("readonly", "related", "store") + def _compute_trackable(self): + blacklists = [ + "activity_ids", + "message_ids", + "message_last_post", + "message_main_attachment", + ] + for rec in self: + rec.trackable = ( + rec.name not in blacklists + and rec.store + and not rec.readonly + and not rec.related + ) + + def write(self, vals): + custom_tracking = None + if "custom_tracking" in vals: + self.check_access_rights("write") + custom_tracking = vals.pop("custom_tracking") + self._write({"custom_tracking": custom_tracking}) + self.invalidate_cache(["custom_tracking"]) + return super().write(vals) diff --git a/tracking_manager/models/mail_thread.py b/tracking_manager/models/mail_thread.py index 37ee8a125..f976467ee 100644 --- a/tracking_manager/models/mail_thread.py +++ b/tracking_manager/models/mail_thread.py @@ -8,6 +8,7 @@ from odoo import models, tools class MailThread(models.AbstractModel): _inherit = "mail.thread" + # TODO invalidate ormcache @tools.ormcache("self.env.uid", "self.env.su") def _get_tracked_fields(self): res = super()._get_tracked_fields() @@ -21,12 +22,12 @@ class MailThread(models.AbstractModel): self.env["ir.model"] .sudo() .search( - [("model", "=", self._name), ("apply_custom_tracking", "=", True)], + [("model", "=", self._name), ("active_custom_tracking", "=", True)], limit=1, ) ) if tracking_model: - track_fields = tracking_model.custom_tracking_field_ids.filtered( + track_fields = tracking_model.field_id.filtered( lambda f: f.custom_tracking ).mapped("name") return track_fields and set(self.fields_get(track_fields)) @@ -112,8 +113,8 @@ class MailThread(models.AbstractModel): # tracking (apply_custom_tracking = True), we track # the changes only for fields with custom_tracking. if ( - line_model_id.apply_custom_tracking - and not line_field_id.custom_tracking_id.custom_tracking + line_model_id.active_custom_tracking + and not line_field_id.custom_tracking ): old = new = False elif line_field_id.ttype in ["one2many", "many2one", "many2many"]: diff --git a/tracking_manager/models/tracking_model.py b/tracking_manager/models/tracking_model.py deleted file mode 100644 index c88893976..000000000 --- a/tracking_manager/models/tracking_model.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2022 Akretion (). -# @author Kévin Roche -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models - - -class TrackingModelField(models.Model): - _name = "tracking.model.field" - _description = "Tracking Model Field" - - name = fields.Char(related="tracking_field_id.name") - tracking_field_id = fields.Many2one( - "ir.model.fields", - "Field", - ) - model_id = fields.Many2one( - comodel_name="ir.model", - string="Tracking Model Field", - ) - custom_tracking = fields.Boolean( - string="Custom Tracking", - compute="_compute_custom_tracking", - readonly=False, - store=True, - ) - native_tracking_field = fields.Integer( - string="Native Tracking", related="tracking_field_id.tracking" - ) - type_field = fields.Selection( - string="Kind Field", related="tracking_field_id.ttype" - ) - - @api.depends( - "tracking_field_id", - "tracking_field_id.readonly", - "tracking_field_id.related", - "tracking_field_id.store", - ) - def _compute_custom_tracking(self): - # No tracking on compute / readonly fields - # Custom tracking include native tracking attribute. - for rec in self: - field_id = rec.tracking_field_id - if ( - field_id.readonly - or field_id.related - or (field_id.compute and field_id.store and not field_id.readonly) - ) and not field_id.tracking: - rec.custom_tracking = False - else: - rec.custom_tracking = True diff --git a/tracking_manager/readme/CONTRIBUTORS.rst b/tracking_manager/readme/CONTRIBUTORS.rst index dcae277c8..7ee0b5b5e 100644 --- a/tracking_manager/readme/CONTRIBUTORS.rst +++ b/tracking_manager/readme/CONTRIBUTORS.rst @@ -1 +1,2 @@ * Kévin Roche +* Sébastien BEAU diff --git a/tracking_manager/security/ir.model.access.csv b/tracking_manager/security/ir.model.access.csv deleted file mode 100644 index b2739dd87..000000000 --- a/tracking_manager/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_custom_tracking_model_field,tracking.model.field.user,model_tracking_model_field,base.group_user,1,1,1,1 diff --git a/tracking_manager/tests/test_tracking_manager.py b/tracking_manager/tests/test_tracking_manager.py index 09ca4abce..e91a9d39c 100644 --- a/tracking_manager/tests/test_tracking_manager.py +++ b/tracking_manager/tests/test_tracking_manager.py @@ -28,35 +28,40 @@ class TestTrackingManager(SavepointCase): } ) - def test_1_ir_model_config(self): - self.partner_model.apply_custom_tracking = True - # custom_tracking_field_ids - self.assertEqual( - len(self.partner_model.field_id), - len(self.partner_model.custom_tracking_field_ids), - ) - # o2m_model_ids - self.assertEqual( - len(self.partner_model.o2m_model_ids), - len(self.partner_model.field_id.filtered(lambda x: x.ttype == "one2many")), + def _active_tracking(self, fields_list): + self.partner_model.active_custom_tracking = True + for field in self._get_fields(fields_list): + field.custom_tracking = True + + def _get_fields(self, fields_list): + return self.env["ir.model.fields"].search( + [ + ("model_id.model", "=", "res.partner"), + ("name", "in", fields_list), + ] ) - def test_2_tracking_model_field_config(self): - self.partner_2 = self.env.ref("base.res_partner_2") - self.partner_1_w_parent = self.env.ref("base.res_partner_address_1") - self.partner_model.apply_custom_tracking = True - # Readonly, related are not tracked - parent_name = self.partner_model.custom_tracking_field_ids.filtered( - lambda x: x.name == "parent_name" - ) - self.assertFalse(parent_name.custom_tracking) - # Other fields are tracked - street = self.partner_model.custom_tracking_field_ids.filtered( - lambda x: x.name == "street" - ) - self.assertTrue(street.custom_tracking) + def test_not_tracked(self): + self.partner_model.active_custom_tracking = True + field = self._get_fields(["category_id"])[0] + self.assertFalse(field.native_tracking) + self.assertFalse(field.custom_tracking) - def test_3_m2m_add_line(self): + def test_native_tracked(self): + self.partner_model.active_custom_tracking = True + field = self._get_fields(["email"])[0] + self.assertTrue(field.native_tracking) + self.assertTrue(field.custom_tracking) + + def test_update_tracked(self): + self.partner_model.active_custom_tracking = True + field = self._get_fields(["category_id"])[0] + self.assertFalse(field.native_tracking) + self.partner_model.automatic_custom_tracking = True + self.partner_model.update_custom_tracking() + self.assertTrue(field.custom_tracking) + + def test_m2m_add_line(self): initial_msg = self.partner_1.message_ids self.partner_1.category_id = [ (4, self.env.ref("base.res_partner_category_3").id) @@ -64,7 +69,7 @@ class TestTrackingManager(SavepointCase): after_msg = self.partner_1.message_ids self.assertEqual(len(initial_msg), len(after_msg)) - self.partner_model.apply_custom_tracking = True + self._active_tracking(["category_id"]) self.partner_1.category_id = [ (4, self.env.ref("base.res_partner_category_8").id) ] @@ -72,21 +77,21 @@ class TestTrackingManager(SavepointCase): self.assertEqual(len(initial_msg) + 1, len(after_msg)) self.assertTrue("New" in after_msg[0].body) - def test_4_m2m_delete_line(self): + def test_m2m_delete_line(self): initial_msg = self.partner_1.message_ids self.partner_1.category_id = [(3, self.partner_1.category_id[0].id)] after_msg = self.partner_1.message_ids self.assertEqual(len(initial_msg), len(after_msg)) - self.partner_model.apply_custom_tracking = True + self._active_tracking(["category_id"]) self.partner_1.category_id = [(3, self.partner_1.category_id[0].id)] after_msg = self.partner_1.message_ids self.assertEqual(len(initial_msg) + 1, len(after_msg)) self.assertTrue("Delete" in after_msg[0].body) - def test_5_m2m_multi_line(self): + def test_m2m_multi_line(self): initial_msg = self.partner_1.message_ids - self.partner_model.apply_custom_tracking = True + self._active_tracking(["category_id"]) self.partner_1.category_id = [ (3, self.partner_1.category_id[0].id), (4, self.env.ref("base.res_partner_category_8").id), @@ -97,26 +102,26 @@ class TestTrackingManager(SavepointCase): self.assertEqual(after_msg[0].body.count("New"), 2) self.assertEqual(after_msg[0].body.count("Delete"), 1) - def test_6_o2m_add(self): + def test_o2m_add(self): initial_msg = self.partner_1.message_ids - self.partner_model.apply_custom_tracking = True + self._active_tracking(["bank_ids"]) self.partner_1.bank_ids = [(4, self.bank_partner_2.id)] after_msg = self.partner_1.message_ids self.assertEqual(len(initial_msg) + 1, len(after_msg)) self.assertTrue("New" in after_msg[0].body) - def test_7_o2m_delete(self): - self.partner_model.apply_custom_tracking = True + def test_o2m_delete(self): + self._active_tracking(["bank_ids"]) initial_msg = self.partner_1.message_ids self.partner_1.write({"bank_ids": [(3, self.partner_1.bank_ids[0].id)]}) after_msg = self.partner_1.message_ids self.assertEqual(len(initial_msg) + 1, len(after_msg)) self.assertTrue("Delete" in after_msg[0].body) - def test_8_o2m_change_in_line(self): + def test_o2m_change_in_line(self): self.partner_1.bank_ids = [(6, 0, self.bank_partner_2.id)] initial_msg = self.partner_1.message_ids - self.partner_model.apply_custom_tracking = True + self._active_tracking(["bank_ids"]) self.partner_1.write( { "bank_ids": [(1, self.partner_1.bank_ids.id, {"acc_number": "123"})], @@ -129,10 +134,8 @@ class TestTrackingManager(SavepointCase): bank_model = self.env["ir.model"].search( [("model", "=", self.bank_partner_2._name)], limit=1 ) - bank_model.apply_custom_tracking = True - acc_number = bank_model.custom_tracking_field_ids.filtered( - lambda x: x.name == "acc_number" - ) + bank_model.active_custom_tracking = True + acc_number = bank_model.field_id.filtered(lambda x: x.name == "acc_number") acc_number.custom_tracking = False self.partner_1.write( { @@ -142,9 +145,9 @@ class TestTrackingManager(SavepointCase): after_msg_2 = self.partner_1.message_ids self.assertEqual(len(after_msg), len(after_msg_2)) - def test_9_o2m_multi_line(self): + def test_o2m_multi_line(self): initial_msg = self.partner_1.message_ids - self.partner_model.apply_custom_tracking = True + self._active_tracking(["bank_ids"]) self.partner_1.bank_ids = [ (3, self.partner_1.bank_ids[0].id), (4, self.bank_partner_2.id), diff --git a/tracking_manager/views/ir_model.xml b/tracking_manager/views/ir_model.xml index 3506602f1..7cb5cade8 100644 --- a/tracking_manager/views/ir_model.xml +++ b/tracking_manager/views/ir_model.xml @@ -10,40 +10,50 @@ - - - -