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)pull/2916/head
parent
56312632a0
commit
be22968e91
|
@ -10,7 +10,7 @@
|
||||||
"category": "Tools",
|
"category": "Tools",
|
||||||
"website": "https://github.com/OCA/server-tools",
|
"website": "https://github.com/OCA/server-tools",
|
||||||
"author": "Akretion, Odoo Community Association (OCA)",
|
"author": "Akretion, Odoo Community Association (OCA)",
|
||||||
"maintainers": ["Kev-Roche"],
|
"maintainers": ["Kev-Roche", "sebastienbeau"],
|
||||||
"license": "AGPL-3",
|
"license": "AGPL-3",
|
||||||
"application": False,
|
"application": False,
|
||||||
"installable": True,
|
"installable": True,
|
||||||
|
@ -19,9 +19,8 @@
|
||||||
"mail",
|
"mail",
|
||||||
],
|
],
|
||||||
"data": [
|
"data": [
|
||||||
"views/ir_model.xml",
|
|
||||||
"views/ir_model_fields.xml",
|
"views/ir_model_fields.xml",
|
||||||
|
"views/ir_model.xml",
|
||||||
"views/message_template.xml",
|
"views/message_template.xml",
|
||||||
"security/ir.model.access.csv",
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
from . import mail_thread
|
from . import mail_thread
|
||||||
from . import tracking_model
|
|
||||||
from . import ir_model
|
from . import ir_model
|
||||||
|
|
|
@ -2,116 +2,129 @@
|
||||||
# @author Kévin Roche <kevin.roche@akretion.com>
|
# @author Kévin Roche <kevin.roche@akretion.com>
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from ast import literal_eval
|
||||||
|
|
||||||
from odoo import api, fields, models
|
from odoo import api, fields, models
|
||||||
|
|
||||||
|
|
||||||
class IrModel(models.Model):
|
class IrModel(models.Model):
|
||||||
_inherit = "ir.model"
|
_inherit = "ir.model"
|
||||||
|
|
||||||
o2m_model_ids = fields.One2many(
|
active_custom_tracking = fields.Boolean(default=False)
|
||||||
string="One2many Models",
|
tracked_field_count = fields.Integer(compute="_compute_tracked_field_count")
|
||||||
comodel_name="ir.model",
|
automatic_custom_tracking = fields.Boolean(
|
||||||
compute="_compute_o2m_model_ids",
|
compute="_compute_automatic_custom_tracking",
|
||||||
readonly=True,
|
readonly=False,
|
||||||
)
|
|
||||||
custom_tracking_field_ids = fields.One2many(
|
|
||||||
comodel_name="tracking.model.field",
|
|
||||||
inverse_name="model_id",
|
|
||||||
string="Custom Tracked Fields",
|
|
||||||
compute="_compute_custom_tracking_fields",
|
|
||||||
store=True,
|
store=True,
|
||||||
)
|
|
||||||
field_count = fields.Integer(compute="_compute_tracked_field_count")
|
|
||||||
apply_custom_tracking = fields.Boolean(
|
|
||||||
"Apply custom tracking on fields",
|
|
||||||
default=False,
|
default=False,
|
||||||
help="""Add tracking on all this model fields if they
|
help=("If tick new field will be automatically tracked " "if the domain match"),
|
||||||
are not readonly True, neither computed.""",
|
)
|
||||||
|
automatic_custom_tracking_domain = fields.Char(
|
||||||
|
compute="_compute_automatic_custom_tracking_domain",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
@api.depends("field_id", "apply_custom_tracking")
|
@api.depends("active_custom_tracking")
|
||||||
def _compute_custom_tracking_fields(self):
|
def _compute_automatic_custom_tracking(self):
|
||||||
for rec in self:
|
for record in self:
|
||||||
fields = rec.env["ir.model.fields"].search(
|
record.automatic_custom_tracking = False
|
||||||
[("model_id.model", "=", rec.model)]
|
|
||||||
|
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 update_custom_tracking(self):
|
||||||
def _compute_o2m_model_ids(self):
|
for record in self:
|
||||||
for rec in self:
|
fields = record.field_id.filtered("trackable").filtered_domain(
|
||||||
o2m_model_list = []
|
literal_eval(record.automatic_custom_tracking_domain)
|
||||||
if rec.apply_custom_tracking:
|
)
|
||||||
for field in rec.field_id:
|
fields.write({"custom_tracking": True})
|
||||||
if field.ttype == "one2many":
|
untrack_fields = record.field_id - fields
|
||||||
o2m_relation_id = self.search(
|
untrack_fields.write({"custom_tracking": False})
|
||||||
[("model", "=", field.relation)], limit=1
|
|
||||||
)
|
|
||||||
o2m_model_list.append(o2m_relation_id.id)
|
|
||||||
rec.o2m_model_ids = [(6, 0, o2m_model_list)]
|
|
||||||
|
|
||||||
@api.depends(
|
@api.depends("field_id.custom_tracking")
|
||||||
"custom_tracking_field_ids", "custom_tracking_field_ids.custom_tracking"
|
|
||||||
)
|
|
||||||
def _compute_tracked_field_count(self):
|
def _compute_tracked_field_count(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.field_count = sum(
|
rec.tracked_field_count = len(rec.field_id.filtered("custom_tracking"))
|
||||||
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)],
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class IrModelFields(models.Model):
|
class IrModelFields(models.Model):
|
||||||
_inherit = "ir.model.fields"
|
_inherit = "ir.model.fields"
|
||||||
|
|
||||||
custom_tracking_id = fields.Many2one(
|
custom_tracking = fields.Boolean(
|
||||||
comodel_name="tracking.model.field",
|
compute="_compute_custom_tracking",
|
||||||
string="Technical Custom Tracking Field",
|
store=True,
|
||||||
compute="_compute_custom_tracking_field_id",
|
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")
|
@api.depends("native_tracking")
|
||||||
def _compute_custom_tracking_field_id(self):
|
def _compute_custom_tracking(self):
|
||||||
for rec in self:
|
for record in self:
|
||||||
if rec.model_id.apply_custom_tracking:
|
if record.model_id.automatic_custom_tracking:
|
||||||
rec.custom_tracking_id = self.env["tracking.model.field"].search(
|
domain = literal_eval(record.model_id.automatic_custom_tracking_domain)
|
||||||
[("tracking_field_id", "=", rec.id), ("model_id", "=", rec._name)],
|
record.custom_tracking = bool(record.filtered_domain(domain))
|
||||||
limit=1,
|
|
||||||
)
|
|
||||||
else:
|
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)
|
||||||
|
|
|
@ -8,6 +8,7 @@ from odoo import models, tools
|
||||||
class MailThread(models.AbstractModel):
|
class MailThread(models.AbstractModel):
|
||||||
_inherit = "mail.thread"
|
_inherit = "mail.thread"
|
||||||
|
|
||||||
|
# TODO invalidate ormcache
|
||||||
@tools.ormcache("self.env.uid", "self.env.su")
|
@tools.ormcache("self.env.uid", "self.env.su")
|
||||||
def _get_tracked_fields(self):
|
def _get_tracked_fields(self):
|
||||||
res = super()._get_tracked_fields()
|
res = super()._get_tracked_fields()
|
||||||
|
@ -21,12 +22,12 @@ class MailThread(models.AbstractModel):
|
||||||
self.env["ir.model"]
|
self.env["ir.model"]
|
||||||
.sudo()
|
.sudo()
|
||||||
.search(
|
.search(
|
||||||
[("model", "=", self._name), ("apply_custom_tracking", "=", True)],
|
[("model", "=", self._name), ("active_custom_tracking", "=", True)],
|
||||||
limit=1,
|
limit=1,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if tracking_model:
|
if tracking_model:
|
||||||
track_fields = tracking_model.custom_tracking_field_ids.filtered(
|
track_fields = tracking_model.field_id.filtered(
|
||||||
lambda f: f.custom_tracking
|
lambda f: f.custom_tracking
|
||||||
).mapped("name")
|
).mapped("name")
|
||||||
return track_fields and set(self.fields_get(track_fields))
|
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
|
# tracking (apply_custom_tracking = True), we track
|
||||||
# the changes only for fields with custom_tracking.
|
# the changes only for fields with custom_tracking.
|
||||||
if (
|
if (
|
||||||
line_model_id.apply_custom_tracking
|
line_model_id.active_custom_tracking
|
||||||
and not line_field_id.custom_tracking_id.custom_tracking
|
and not line_field_id.custom_tracking
|
||||||
):
|
):
|
||||||
old = new = False
|
old = new = False
|
||||||
elif line_field_id.ttype in ["one2many", "many2one", "many2many"]:
|
elif line_field_id.ttype in ["one2many", "many2one", "many2many"]:
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
# Copyright (C) 2022 Akretion (<http://www.akretion.com>).
|
|
||||||
# @author Kévin Roche <kevin.roche@akretion.com>
|
|
||||||
# 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
|
|
|
@ -1 +1,2 @@
|
||||||
* Kévin Roche <kevin.roche@akretion.com>
|
* Kévin Roche <kevin.roche@akretion.com>
|
||||||
|
* Sébastien BEAU <sebastien.beau@akretion.com>
|
||||||
|
|
|
@ -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
|
|
|
|
@ -28,35 +28,40 @@ class TestTrackingManager(SavepointCase):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_1_ir_model_config(self):
|
def _active_tracking(self, fields_list):
|
||||||
self.partner_model.apply_custom_tracking = True
|
self.partner_model.active_custom_tracking = True
|
||||||
# custom_tracking_field_ids
|
for field in self._get_fields(fields_list):
|
||||||
self.assertEqual(
|
field.custom_tracking = True
|
||||||
len(self.partner_model.field_id),
|
|
||||||
len(self.partner_model.custom_tracking_field_ids),
|
def _get_fields(self, fields_list):
|
||||||
)
|
return self.env["ir.model.fields"].search(
|
||||||
# o2m_model_ids
|
[
|
||||||
self.assertEqual(
|
("model_id.model", "=", "res.partner"),
|
||||||
len(self.partner_model.o2m_model_ids),
|
("name", "in", fields_list),
|
||||||
len(self.partner_model.field_id.filtered(lambda x: x.ttype == "one2many")),
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
def test_2_tracking_model_field_config(self):
|
def test_not_tracked(self):
|
||||||
self.partner_2 = self.env.ref("base.res_partner_2")
|
self.partner_model.active_custom_tracking = True
|
||||||
self.partner_1_w_parent = self.env.ref("base.res_partner_address_1")
|
field = self._get_fields(["category_id"])[0]
|
||||||
self.partner_model.apply_custom_tracking = True
|
self.assertFalse(field.native_tracking)
|
||||||
# Readonly, related are not tracked
|
self.assertFalse(field.custom_tracking)
|
||||||
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_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
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_1.category_id = [
|
self.partner_1.category_id = [
|
||||||
(4, self.env.ref("base.res_partner_category_3").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
|
after_msg = self.partner_1.message_ids
|
||||||
self.assertEqual(len(initial_msg), len(after_msg))
|
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 = [
|
self.partner_1.category_id = [
|
||||||
(4, self.env.ref("base.res_partner_category_8").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.assertEqual(len(initial_msg) + 1, len(after_msg))
|
||||||
self.assertTrue("New" in after_msg[0].body)
|
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
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_1.category_id = [(3, self.partner_1.category_id[0].id)]
|
self.partner_1.category_id = [(3, self.partner_1.category_id[0].id)]
|
||||||
after_msg = self.partner_1.message_ids
|
after_msg = self.partner_1.message_ids
|
||||||
self.assertEqual(len(initial_msg), len(after_msg))
|
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)]
|
self.partner_1.category_id = [(3, self.partner_1.category_id[0].id)]
|
||||||
after_msg = self.partner_1.message_ids
|
after_msg = self.partner_1.message_ids
|
||||||
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
||||||
self.assertTrue("Delete" in after_msg[0].body)
|
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
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_model.apply_custom_tracking = True
|
self._active_tracking(["category_id"])
|
||||||
self.partner_1.category_id = [
|
self.partner_1.category_id = [
|
||||||
(3, self.partner_1.category_id[0].id),
|
(3, self.partner_1.category_id[0].id),
|
||||||
(4, self.env.ref("base.res_partner_category_8").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("New"), 2)
|
||||||
self.assertEqual(after_msg[0].body.count("Delete"), 1)
|
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
|
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)]
|
self.partner_1.bank_ids = [(4, self.bank_partner_2.id)]
|
||||||
after_msg = self.partner_1.message_ids
|
after_msg = self.partner_1.message_ids
|
||||||
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
||||||
self.assertTrue("New" in after_msg[0].body)
|
self.assertTrue("New" in after_msg[0].body)
|
||||||
|
|
||||||
def test_7_o2m_delete(self):
|
def test_o2m_delete(self):
|
||||||
self.partner_model.apply_custom_tracking = True
|
self._active_tracking(["bank_ids"])
|
||||||
initial_msg = self.partner_1.message_ids
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_1.write({"bank_ids": [(3, self.partner_1.bank_ids[0].id)]})
|
self.partner_1.write({"bank_ids": [(3, self.partner_1.bank_ids[0].id)]})
|
||||||
after_msg = self.partner_1.message_ids
|
after_msg = self.partner_1.message_ids
|
||||||
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
self.assertEqual(len(initial_msg) + 1, len(after_msg))
|
||||||
self.assertTrue("Delete" in after_msg[0].body)
|
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)]
|
self.partner_1.bank_ids = [(6, 0, self.bank_partner_2.id)]
|
||||||
initial_msg = self.partner_1.message_ids
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_model.apply_custom_tracking = True
|
self._active_tracking(["bank_ids"])
|
||||||
self.partner_1.write(
|
self.partner_1.write(
|
||||||
{
|
{
|
||||||
"bank_ids": [(1, self.partner_1.bank_ids.id, {"acc_number": "123"})],
|
"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(
|
bank_model = self.env["ir.model"].search(
|
||||||
[("model", "=", self.bank_partner_2._name)], limit=1
|
[("model", "=", self.bank_partner_2._name)], limit=1
|
||||||
)
|
)
|
||||||
bank_model.apply_custom_tracking = True
|
bank_model.active_custom_tracking = True
|
||||||
acc_number = bank_model.custom_tracking_field_ids.filtered(
|
acc_number = bank_model.field_id.filtered(lambda x: x.name == "acc_number")
|
||||||
lambda x: x.name == "acc_number"
|
|
||||||
)
|
|
||||||
acc_number.custom_tracking = False
|
acc_number.custom_tracking = False
|
||||||
self.partner_1.write(
|
self.partner_1.write(
|
||||||
{
|
{
|
||||||
|
@ -142,9 +145,9 @@ class TestTrackingManager(SavepointCase):
|
||||||
after_msg_2 = self.partner_1.message_ids
|
after_msg_2 = self.partner_1.message_ids
|
||||||
self.assertEqual(len(after_msg), len(after_msg_2))
|
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
|
initial_msg = self.partner_1.message_ids
|
||||||
self.partner_model.apply_custom_tracking = True
|
self._active_tracking(["bank_ids"])
|
||||||
self.partner_1.bank_ids = [
|
self.partner_1.bank_ids = [
|
||||||
(3, self.partner_1.bank_ids[0].id),
|
(3, self.partner_1.bank_ids[0].id),
|
||||||
(4, self.bank_partner_2.id),
|
(4, self.bank_partner_2.id),
|
||||||
|
|
|
@ -10,40 +10,50 @@
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<xpath expr="//sheet/group[1]" position="after">
|
<xpath expr="//sheet/group[1]" position="after">
|
||||||
|
|
||||||
<group string="Tracking">
|
<group string="Custom Tracking">
|
||||||
<group>
|
<group>
|
||||||
<field name="apply_custom_tracking" />
|
<field name="active_custom_tracking" string="Active" />
|
||||||
<button
|
<field
|
||||||
name="show_custom_tracked_field"
|
name="automatic_custom_tracking"
|
||||||
string="Custom Tracked fields"
|
string="Automatic configuration"
|
||||||
|
attrs="{'invisible': [('active_custom_tracking', '=', False)]}"
|
||||||
|
/>
|
||||||
|
<field
|
||||||
|
name="automatic_custom_tracking_domain"
|
||||||
|
string="Domain"
|
||||||
|
attrs="{
|
||||||
|
'invisible': [('automatic_custom_tracking', '=', False)],
|
||||||
|
'required': [('automatic_custom_tracking', '=', True)],
|
||||||
|
}"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="update_custom_tracking"
|
||||||
|
string="Update fields configuration"
|
||||||
|
attrs="{'invisible': [('automatic_custom_tracking', '=', False)]}"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
name="update_custom_tracking"
|
||||||
|
string="Update"
|
||||||
|
icon="fa-refresh"
|
||||||
type="object"
|
type="object"
|
||||||
class="btn-secondary"
|
class="btn-secondary"
|
||||||
attrs="{'invisible': [('apply_custom_tracking', '=', False)]}"
|
attrs="{'invisible': [('automatic_custom_tracking', '=', False)]}"
|
||||||
/>
|
/>
|
||||||
<button
|
|
||||||
name="show_o2m_models"
|
|
||||||
string="One2many related models"
|
|
||||||
type="object"
|
|
||||||
class="btn-secondary"
|
|
||||||
attrs="{'invisible': [('apply_custom_tracking', '=', False)]}"
|
|
||||||
/>
|
|
||||||
</group>
|
|
||||||
</group>
|
</group>
|
||||||
|
</group>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
<xpath expr="//sheet/group[1]" position="before">
|
<xpath expr="//sheet/group[1]" position="before">
|
||||||
<div
|
<div class="oe_button_box" name="button_box">
|
||||||
class="oe_button_box"
|
|
||||||
name="button_box"
|
|
||||||
attrs="{'invisible': [('apply_custom_tracking', '=', False)]}"
|
|
||||||
>
|
|
||||||
<button
|
<button
|
||||||
name="show_custom_tracked_field"
|
name="%(ir_model_fields_action)d"
|
||||||
type="object"
|
type="action"
|
||||||
class="oe_stat_button"
|
class="oe_stat_button"
|
||||||
icon="fa-server"
|
icon="fa-server"
|
||||||
|
attrs="{'invisible': [('active_custom_tracking', '=', False)]}"
|
||||||
>
|
>
|
||||||
<field
|
<field
|
||||||
name="field_count"
|
name="tracked_field_count"
|
||||||
widget="statinfo"
|
widget="statinfo"
|
||||||
string="Tracked Fields"
|
string="Tracked Fields"
|
||||||
/>
|
/>
|
||||||
|
@ -53,15 +63,4 @@
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="custom_tracking_view_tree" model="ir.ui.view">
|
|
||||||
<field name="model">ir.model</field>
|
|
||||||
<field name="name">tracking.ir.model.tree</field>
|
|
||||||
<field name="inherit_id" ref="base.view_model_tree" />
|
|
||||||
<field name="arch" type="xml">
|
|
||||||
<xpath expr="//tree/field[@name='transient']" position="after">
|
|
||||||
<field name="apply_custom_tracking" readonly="True" />
|
|
||||||
</xpath>
|
|
||||||
</field>
|
|
||||||
</record>
|
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
|
@ -3,17 +3,37 @@
|
||||||
@author Kévin Roche <kevin.roche@akretion.com>
|
@author Kévin Roche <kevin.roche@akretion.com>
|
||||||
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||||
<odoo>
|
<odoo>
|
||||||
<record id="custom_tracking_field_view_tree" model="ir.ui.view">
|
|
||||||
<field name="model">tracking.model.field</field>
|
<record model="ir.actions.act_window" id="ir_model_fields_action">
|
||||||
<field name="name">tracking.model.field.tree</field>
|
<field name="name">Trackable Fields</field>
|
||||||
|
<field name="type">ir.actions.act_window</field>
|
||||||
|
<field name="res_model">ir.model.fields</field>
|
||||||
|
<field name="view_mode">tree,form</field>
|
||||||
|
<field
|
||||||
|
name="domain"
|
||||||
|
>[("trackable", "=", True), ("model_id", "=", context['active_id'])]</field>
|
||||||
|
<field name="context">{}</field>
|
||||||
|
<field name="target">current</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="ir_model_fields_view_tree_custom_tracking" model="ir.ui.view">
|
||||||
|
<field name="model">ir.model.fields</field>
|
||||||
<field name="arch" type="xml">
|
<field name="arch" type="xml">
|
||||||
<tree editable="bottom" create="false">
|
<tree editable="bottom" create="0" delete="0" duplicate="0">
|
||||||
<field name="name" readonly="True" />
|
<field name="name" readonly="True" />
|
||||||
|
<field name="field_description" readonly="True" />
|
||||||
|
<field name="ttype" readonly="True" />
|
||||||
|
<field name="native_tracking" readonly="True" />
|
||||||
<field name="custom_tracking" widget="boolean_toggle" />
|
<field name="custom_tracking" widget="boolean_toggle" />
|
||||||
<field name="native_tracking_field" readonly="True" />
|
|
||||||
<field name="type_field" readonly="True" />
|
|
||||||
<field name="model_id" readonly="True" />
|
|
||||||
</tree>
|
</tree>
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
<record id="ir_model_fields_action_view" model="ir.actions.act_window.view">
|
||||||
|
<field name="sequence" eval="2" />
|
||||||
|
<field name="view_mode">tree</field>
|
||||||
|
<field name="view_id" ref="ir_model_fields_view_tree_custom_tracking" />
|
||||||
|
<field name="act_window_id" ref="ir_model_fields_action" />
|
||||||
|
</record>
|
||||||
|
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|
Loading…
Reference in New Issue