odoo-addon-web-m2x-options-manager 16.0.1.0.0.3__py3-none-any.whl → 17.0.1.0.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odoo/addons/web_m2x_options_manager/README.rst +29 -18
- odoo/addons/web_m2x_options_manager/__init__.py +1 -3
- odoo/addons/web_m2x_options_manager/__manifest__.py +9 -2
- odoo/addons/web_m2x_options_manager/demo/res_partner_demo_view.xml +11 -10
- odoo/addons/web_m2x_options_manager/hooks.py +11 -0
- odoo/addons/web_m2x_options_manager/i18n/it.po +1 -16
- odoo/addons/web_m2x_options_manager/i18n/web_m2x_options_manager.pot +81 -33
- odoo/addons/web_m2x_options_manager/models/__init__.py +1 -3
- odoo/addons/web_m2x_options_manager/models/ir_model.py +46 -21
- odoo/addons/web_m2x_options_manager/models/ir_model_fields.py +53 -0
- odoo/addons/web_m2x_options_manager/models/ir_ui_view.py +9 -7
- odoo/addons/web_m2x_options_manager/models/m2x_create_edit_option.py +133 -76
- odoo/addons/web_m2x_options_manager/readme/CONTRIBUTORS.md +2 -0
- odoo/addons/web_m2x_options_manager/readme/USAGE.md +12 -0
- odoo/addons/web_m2x_options_manager/static/description/index.html +49 -32
- odoo/addons/web_m2x_options_manager/tests/__init__.py +2 -3
- odoo/addons/web_m2x_options_manager/tests/common.py +42 -0
- odoo/addons/web_m2x_options_manager/tests/test_ir_model.py +37 -0
- odoo/addons/web_m2x_options_manager/tests/test_ir_model_fields.py +99 -0
- odoo/addons/web_m2x_options_manager/tests/test_m2x_create_edit_option.py +67 -92
- odoo/addons/web_m2x_options_manager/tools.py +34 -0
- odoo/addons/web_m2x_options_manager/views/ir_model.xml +38 -18
- odoo/addons/web_m2x_options_manager/views/m2x_create_edit_option.xml +157 -0
- {odoo_addon_web_m2x_options_manager-16.0.1.0.0.3.dist-info → odoo_addon_web_m2x_options_manager-17.0.1.0.0.2.dist-info}/METADATA +37 -24
- odoo_addon_web_m2x_options_manager-17.0.1.0.0.2.dist-info/RECORD +30 -0
- {odoo_addon_web_m2x_options_manager-16.0.1.0.0.3.dist-info → odoo_addon_web_m2x_options_manager-17.0.1.0.0.2.dist-info}/WHEEL +1 -1
- odoo_addon_web_m2x_options_manager-17.0.1.0.0.2.dist-info/top_level.txt +1 -0
- odoo/addons/web_m2x_options_manager/readme/CONTRIBUTORS.rst +0 -4
- odoo/addons/web_m2x_options_manager/readme/USAGE.rst +0 -7
- odoo_addon_web_m2x_options_manager-16.0.1.0.0.3.dist-info/RECORD +0 -23
- odoo_addon_web_m2x_options_manager-16.0.1.0.0.3.dist-info/top_level.txt +0 -1
- /odoo/addons/web_m2x_options_manager/readme/{DESCRIPTION.rst → DESCRIPTION.md} +0 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# Copyright 2025 Camptocamp SA
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo_test_helper import FakeModelLoader
|
|
5
|
+
|
|
6
|
+
from odoo import fields, models
|
|
7
|
+
|
|
8
|
+
from .common import Common
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestIrModelFields(Common):
|
|
12
|
+
def test_field_can_have_options(self):
|
|
13
|
+
# M2O field
|
|
14
|
+
self.assertTrue(self._get_field("res.partner", "parent_id").can_have_options)
|
|
15
|
+
# M2M field
|
|
16
|
+
self.assertTrue(self._get_field("res.partner", "category_id").can_have_options)
|
|
17
|
+
# O2M field
|
|
18
|
+
self.assertFalse(self._get_field("res.partner", "user_ids").can_have_options)
|
|
19
|
+
# non-relational field
|
|
20
|
+
self.assertFalse(self._get_field("res.partner", "id").can_have_options)
|
|
21
|
+
|
|
22
|
+
def test_field_comodel_id(self):
|
|
23
|
+
# M2O field
|
|
24
|
+
self.assertEqual(
|
|
25
|
+
self._get_field("res.partner", "parent_id").comodel_id,
|
|
26
|
+
self._get_model("res.partner"),
|
|
27
|
+
)
|
|
28
|
+
# M2M field
|
|
29
|
+
self.assertEqual(
|
|
30
|
+
self._get_field("res.partner", "category_id").comodel_id,
|
|
31
|
+
self._get_model("res.partner.category"),
|
|
32
|
+
)
|
|
33
|
+
# O2M field
|
|
34
|
+
self.assertEqual(
|
|
35
|
+
self._get_field("res.partner", "user_ids").comodel_id,
|
|
36
|
+
self._get_model("res.users"),
|
|
37
|
+
)
|
|
38
|
+
# Non-relational field
|
|
39
|
+
self.assertFalse(self._get_field("res.partner", "id").comodel_id)
|
|
40
|
+
|
|
41
|
+
def test_field_search(self):
|
|
42
|
+
loader = FakeModelLoader(self.env, self.__module__)
|
|
43
|
+
loader.backup_registry()
|
|
44
|
+
|
|
45
|
+
class ResUsers(models.Model):
|
|
46
|
+
_inherit = "res.users"
|
|
47
|
+
|
|
48
|
+
test_field = fields.Many2one("res.groups", string="Abc")
|
|
49
|
+
|
|
50
|
+
class ResGroups(models.Model):
|
|
51
|
+
_inherit = "res.groups"
|
|
52
|
+
|
|
53
|
+
test_field_abc = fields.Many2one("res.users", string="Abc")
|
|
54
|
+
|
|
55
|
+
loader.update_registry((ResUsers, ResGroups))
|
|
56
|
+
|
|
57
|
+
test_field = self._get_field("res.users", "test_field")
|
|
58
|
+
test_field_abc = self._get_field("res.groups", "test_field_abc")
|
|
59
|
+
|
|
60
|
+
ir_model_fields = self.env["ir.model.fields"]
|
|
61
|
+
name = "ABC"
|
|
62
|
+
domain = [("model", "in", ("res.users", "res.groups"))]
|
|
63
|
+
|
|
64
|
+
# String "abc" is contained in both fields' description: basic ``name_search()``
|
|
65
|
+
# will return them both sorted by ID
|
|
66
|
+
ir_model_fields = ir_model_fields.with_context(search_by_technical_name=False)
|
|
67
|
+
self.assertEqual(
|
|
68
|
+
[r[0] for r in ir_model_fields.name_search(name, domain, limit=None)],
|
|
69
|
+
[test_field.id, test_field_abc.id],
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Use context key ``search_by_technical_name``: ``test_field_abc`` should now be
|
|
73
|
+
# returned first
|
|
74
|
+
ir_model_fields = ir_model_fields.with_context(search_by_technical_name=True)
|
|
75
|
+
self.assertEqual(
|
|
76
|
+
[r[0] for r in ir_model_fields.name_search(name, domain, limit=None)],
|
|
77
|
+
[test_field_abc.id, test_field.id],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
ir_model_fields.with_context(search_by_technical_name=False)
|
|
81
|
+
|
|
82
|
+
# Search again by mimicking the ``ir.model`` Form view for m2x options
|
|
83
|
+
users_model = self._get_model("res.users")
|
|
84
|
+
ir_model_fields = ir_model_fields.with_context(
|
|
85
|
+
o2m_list_view_m2x_domain=[("model_id", "=", users_model.id)]
|
|
86
|
+
)
|
|
87
|
+
self.assertEqual(
|
|
88
|
+
[r[0] for r in ir_model_fields.name_search(name, domain, limit=None)],
|
|
89
|
+
[test_field.id],
|
|
90
|
+
)
|
|
91
|
+
ir_model_fields = ir_model_fields.with_context(
|
|
92
|
+
o2m_list_view_m2x_domain=[("comodel_id", "=", users_model.id)]
|
|
93
|
+
)
|
|
94
|
+
self.assertEqual(
|
|
95
|
+
[r[0] for r in ir_model_fields.name_search(name, domain, limit=None)],
|
|
96
|
+
[test_field_abc.id],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
loader.restore_registry()
|
|
@@ -1,120 +1,95 @@
|
|
|
1
1
|
# Copyright 2021 Camptocamp SA
|
|
2
2
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
3
|
|
|
4
|
-
from lxml import etree
|
|
5
|
-
|
|
6
4
|
from odoo.exceptions import ValidationError
|
|
7
|
-
from odoo.tests.common import TransactionCase
|
|
8
|
-
from odoo.tools.safe_eval import safe_eval
|
|
9
5
|
|
|
6
|
+
from .common import Common
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestM2xCreateEditOption(Common):
|
|
10
|
+
def test_errors(self):
|
|
11
|
+
with self.assertRaises(ValidationError):
|
|
12
|
+
# Fails ``_check_field_type``: users_field is a One2many
|
|
13
|
+
self._create_opt("res.partner", "user_ids")
|
|
14
|
+
|
|
15
|
+
def test_apply_options(self):
|
|
16
|
+
# Check fields on res.partner form view before applying options
|
|
17
|
+
form_doc = self._get_test_view_parsed()
|
|
18
|
+
self.assertEqual(
|
|
19
|
+
self._eval_node_options(form_doc.xpath("//field[@name='title']")[0]), {}
|
|
20
|
+
)
|
|
21
|
+
self.assertEqual(
|
|
22
|
+
self._eval_node_options(form_doc.xpath("//field[@name='parent_id']")[0]),
|
|
23
|
+
{"create": False, "create_edit": False},
|
|
24
|
+
)
|
|
25
|
+
self.assertEqual(
|
|
26
|
+
self._eval_node_options(form_doc.xpath("//field[@name='category_id']")[0]),
|
|
27
|
+
{"create": False, "create_edit": False},
|
|
28
|
+
)
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
# View to be used
|
|
16
|
-
self.view = ref("web_m2x_options_manager.res_partner_demo_form_view")
|
|
17
|
-
# res.partner model and fields
|
|
18
|
-
self.res_partner_model = ref("base.model_res_partner")
|
|
19
|
-
self.categ_field = ref("base.field_res_partner__category_id")
|
|
20
|
-
self.title_field = ref("base.field_res_partner__title")
|
|
21
|
-
self.users_field = ref("base.field_res_partner__user_ids")
|
|
22
|
-
# res.users model and fields
|
|
23
|
-
self.res_users_model = ref("base.model_res_users")
|
|
24
|
-
self.company_field = ref("base.field_res_users__company_id")
|
|
25
|
-
# Options setup
|
|
26
|
-
self.title_opt = self.env["m2x.create.edit.option"].create(
|
|
30
|
+
# Create options, check view has been updated
|
|
31
|
+
self._create_opt(
|
|
32
|
+
"res.partner",
|
|
33
|
+
"title",
|
|
27
34
|
{
|
|
28
|
-
"field_id": self.title_field.id,
|
|
29
|
-
"model_id": self.res_partner_model.id,
|
|
30
35
|
"option_create": "set_true",
|
|
31
36
|
"option_create_edit": "set_true",
|
|
32
|
-
|
|
33
|
-
}
|
|
37
|
+
},
|
|
34
38
|
)
|
|
35
|
-
self.
|
|
39
|
+
self._create_opt(
|
|
40
|
+
"res.partner",
|
|
41
|
+
"parent_id",
|
|
36
42
|
{
|
|
37
|
-
"field_id": self.categ_field.id,
|
|
38
|
-
"model_id": self.res_partner_model.id,
|
|
39
43
|
"option_create": "set_true",
|
|
40
44
|
"option_create_edit": "set_true",
|
|
41
|
-
|
|
42
|
-
}
|
|
45
|
+
},
|
|
43
46
|
)
|
|
44
|
-
self.
|
|
47
|
+
self._create_opt(
|
|
48
|
+
"res.partner",
|
|
49
|
+
"category_id",
|
|
45
50
|
{
|
|
46
|
-
"field_id": self.company_field.id,
|
|
47
|
-
"model_id": self.res_users_model.id,
|
|
48
51
|
"option_create": "force_true",
|
|
49
|
-
"option_create_edit": "
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
+
"option_create_edit": "force_true",
|
|
53
|
+
},
|
|
52
54
|
)
|
|
53
|
-
|
|
54
|
-
def test_errors(self):
|
|
55
|
-
with self.assertRaises(ValidationError):
|
|
56
|
-
# Fails ``_check_field_in_model``: model is res.partner, field is
|
|
57
|
-
# res.users's company_id
|
|
58
|
-
self.env["m2x.create.edit.option"].create(
|
|
59
|
-
{
|
|
60
|
-
"field_id": self.company_field.id,
|
|
61
|
-
"model_id": self.res_partner_model.id,
|
|
62
|
-
"option_create": "set_true",
|
|
63
|
-
"option_create_edit": "set_true",
|
|
64
|
-
}
|
|
65
|
-
)
|
|
66
|
-
with self.assertRaises(ValidationError):
|
|
67
|
-
# Fails ``_check_field_type``: users_field is a One2many
|
|
68
|
-
self.env["m2x.create.edit.option"].create(
|
|
69
|
-
{
|
|
70
|
-
"field_id": self.users_field.id,
|
|
71
|
-
"model_id": self.res_partner_model.id,
|
|
72
|
-
"option_create": "set_true",
|
|
73
|
-
"option_create_edit": "set_true",
|
|
74
|
-
}
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
def test_apply_options(self):
|
|
78
|
-
res = self.env["res.partner"].get_view(self.view.id)
|
|
79
|
-
|
|
80
|
-
# Check fields on res.partner form view
|
|
81
|
-
form_arch = res["arch"]
|
|
82
|
-
form_doc = etree.XML(form_arch)
|
|
83
|
-
title_node = form_doc.xpath("//field[@name='title']")[0]
|
|
55
|
+
form_doc = self._get_test_view_parsed()
|
|
84
56
|
self.assertEqual(
|
|
85
|
-
|
|
57
|
+
self._eval_node_options(form_doc.xpath("//field[@name='title']")[0]),
|
|
86
58
|
{"create": True, "create_edit": True},
|
|
87
59
|
)
|
|
88
60
|
self.assertEqual(
|
|
89
|
-
(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
("true", "true"),
|
|
61
|
+
self._eval_node_options(form_doc.xpath("//field[@name='parent_id']")[0]),
|
|
62
|
+
# These remain the same because the options are defined w/ 'set_true':
|
|
63
|
+
# but the node already contains them, so no override is applied
|
|
64
|
+
{"create": False, "create_edit": False},
|
|
94
65
|
)
|
|
95
|
-
categ_node = form_doc.xpath("//field[@name='category_id']")[0]
|
|
96
66
|
self.assertEqual(
|
|
97
|
-
|
|
98
|
-
|
|
67
|
+
self._eval_node_options(form_doc.xpath("//field[@name='category_id']")[0]),
|
|
68
|
+
# These change values because the options are defined w/ 'force_true':
|
|
69
|
+
# options' values are overridden even if the node already contains them
|
|
70
|
+
{"create": True, "create_edit": True},
|
|
99
71
|
)
|
|
100
|
-
|
|
101
|
-
|
|
72
|
+
|
|
73
|
+
# Update options on ``res.partner.parent_id``, check its node has been updated
|
|
74
|
+
opt = self.env["m2x.create.edit.option"].get("res.partner", "parent_id")
|
|
75
|
+
opt.option_create = "force_true"
|
|
76
|
+
opt.option_create_edit = "force_true"
|
|
77
|
+
form_doc = self._get_test_view_parsed()
|
|
102
78
|
self.assertEqual(
|
|
103
|
-
|
|
79
|
+
self._eval_node_options(form_doc.xpath("//field[@name='parent_id']")[0]),
|
|
80
|
+
{"create": True, "create_edit": True},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
def test_m2x_option_name(self):
|
|
84
|
+
# Mostly to make Codecov happy...
|
|
85
|
+
opt = self._create_opt(
|
|
86
|
+
"res.partner",
|
|
87
|
+
"title",
|
|
104
88
|
{
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"create_edit": True,
|
|
108
|
-
"no_quick_create": True,
|
|
89
|
+
"option_create": "set_true",
|
|
90
|
+
"option_create_edit": "set_true",
|
|
109
91
|
},
|
|
110
92
|
)
|
|
111
|
-
|
|
112
|
-
self.
|
|
113
|
-
|
|
114
|
-
form_arch = res["arch"]
|
|
115
|
-
form_doc = etree.XML(form_arch)
|
|
116
|
-
title_node = form_doc.xpath("//field[@name='title']")[0]
|
|
117
|
-
self.assertEqual(
|
|
118
|
-
safe_eval(title_node.attrib.get("options"), nocopy=True),
|
|
119
|
-
{"create": True, "create_edit": False, "no_create_edit": True},
|
|
120
|
-
)
|
|
93
|
+
self.assertEqual(opt.name, "res.partner.title")
|
|
94
|
+
opt = opt.new({"field_id": self._get_field("res.partner", "parent_id").id})
|
|
95
|
+
self.assertEqual(opt.name, "res.partner.parent_id")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Copyright 2025 Camptocamp SA
|
|
2
|
+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
|
3
|
+
|
|
4
|
+
from odoo.tools.sql import column_exists, create_column
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def prepare_column_can_have_options(cr):
|
|
8
|
+
"""Creates column ``ir_model_fields.can_have_options`` and fills its values"""
|
|
9
|
+
if not column_exists(cr, "ir_model_fields", "can_have_options"):
|
|
10
|
+
create_column(cr, "ir_model_fields", "can_have_options", "boolean")
|
|
11
|
+
cr.execute(
|
|
12
|
+
"""
|
|
13
|
+
UPDATE ir_model_fields
|
|
14
|
+
SET can_have_options =
|
|
15
|
+
CASE
|
|
16
|
+
WHEN ttype in ('many2many', 'many2one') THEN true
|
|
17
|
+
ELSE false
|
|
18
|
+
END
|
|
19
|
+
"""
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def prepare_column_comodel_id(cr):
|
|
24
|
+
"""Creates column ``ir_model_fields.comodel_id`` and fills its values"""
|
|
25
|
+
if not column_exists(cr, "ir_model_fields", "comodel_id"):
|
|
26
|
+
create_column(cr, "ir_model_fields", "comodel_id", "int4")
|
|
27
|
+
cr.execute(
|
|
28
|
+
"""
|
|
29
|
+
UPDATE ir_model_fields
|
|
30
|
+
SET comodel_id = m.id
|
|
31
|
+
FROM ir_model m
|
|
32
|
+
WHERE relation = m.model
|
|
33
|
+
"""
|
|
34
|
+
)
|
|
@@ -8,28 +8,48 @@
|
|
|
8
8
|
<field name="arch" type="xml">
|
|
9
9
|
<xpath expr="//notebook/page[@name='fields']" position="after">
|
|
10
10
|
<page string="Create/Edit Options" name="create_edit_options">
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
<field name="id" invisible="1" />
|
|
12
|
+
<div class="alert alert-info" role="alert">
|
|
13
|
+
Here are displayed options for this model's fields<hr />
|
|
14
|
+
<div>
|
|
15
|
+
<button
|
|
16
|
+
name="button_fill_m2x_options"
|
|
17
|
+
type="object"
|
|
18
|
+
string="Fill"
|
|
19
|
+
/>
|
|
20
|
+
<button
|
|
21
|
+
name="button_empty_m2x_options"
|
|
22
|
+
type="object"
|
|
23
|
+
string="Empty"
|
|
24
|
+
/>
|
|
25
|
+
</div>
|
|
14
26
|
</div>
|
|
15
27
|
<field
|
|
16
|
-
name="
|
|
28
|
+
name="m2x_option_ids"
|
|
29
|
+
context="{'tree_view_ref': 'web_m2x_options_manager.m2x_create_edit_option_tree_o2m_filter_by_model'}"
|
|
17
30
|
nolabel="1"
|
|
18
|
-
|
|
19
|
-
>
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
/>
|
|
32
|
+
<div class="alert alert-info" role="alert">
|
|
33
|
+
Here are displayed options for other models' fields related to this model<hr
|
|
34
|
+
/>
|
|
35
|
+
<div>
|
|
36
|
+
<button
|
|
37
|
+
name="button_fill_m2x_comodels_options"
|
|
38
|
+
type="object"
|
|
39
|
+
string="Fill"
|
|
40
|
+
/>
|
|
41
|
+
<button
|
|
42
|
+
name="button_empty_m2x_comodels_options"
|
|
43
|
+
type="object"
|
|
44
|
+
string="Empty"
|
|
27
45
|
/>
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
<field
|
|
49
|
+
name="m2x_comodels_option_ids"
|
|
50
|
+
context="{'tree_view_ref': 'web_m2x_options_manager.m2x_create_edit_option_tree_o2m_filter_by_comodel'}"
|
|
51
|
+
nolabel="1"
|
|
52
|
+
/>
|
|
33
53
|
</page>
|
|
34
54
|
</xpath>
|
|
35
55
|
</field>
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" ?>
|
|
2
|
+
<odoo>
|
|
3
|
+
|
|
4
|
+
<record id="m2x_create_edit_option_search" model="ir.ui.view">
|
|
5
|
+
<field name="name">m2x.create.edit.option search view</field>
|
|
6
|
+
<field name="model">m2x.create.edit.option</field>
|
|
7
|
+
<field name="arch" type="xml">
|
|
8
|
+
<search>
|
|
9
|
+
<field name="field_id" />
|
|
10
|
+
<field name="model_id" />
|
|
11
|
+
<field name="comodel_id" />
|
|
12
|
+
<group expand="0" string="Group By">
|
|
13
|
+
<filter
|
|
14
|
+
name="groupby_model_id"
|
|
15
|
+
string="Model"
|
|
16
|
+
domain="[]"
|
|
17
|
+
context="{'group_by': 'model_id'}"
|
|
18
|
+
/>
|
|
19
|
+
<filter
|
|
20
|
+
name="groupby_comodel_id"
|
|
21
|
+
string="Comodel"
|
|
22
|
+
domain="[]"
|
|
23
|
+
context="{'group_by': 'comodel_id'}"
|
|
24
|
+
/>
|
|
25
|
+
<filter
|
|
26
|
+
name="groupby_option_create"
|
|
27
|
+
string="Create Option"
|
|
28
|
+
domain="[]"
|
|
29
|
+
context="{'group_by': 'option_create'}"
|
|
30
|
+
/>
|
|
31
|
+
<filter
|
|
32
|
+
name="groupby_option_create_edit"
|
|
33
|
+
string="Create & Edit Option"
|
|
34
|
+
domain="[]"
|
|
35
|
+
context="{'group_by': 'option_create_edit'}"
|
|
36
|
+
/>
|
|
37
|
+
</group>
|
|
38
|
+
</search>
|
|
39
|
+
</field>
|
|
40
|
+
</record>
|
|
41
|
+
|
|
42
|
+
<record id="m2x_create_edit_option_form" model="ir.ui.view">
|
|
43
|
+
<field name="name">m2x.create.edit.option form view</field>
|
|
44
|
+
<field name="model">m2x.create.edit.option</field>
|
|
45
|
+
<field name="arch" type="xml">
|
|
46
|
+
<form>
|
|
47
|
+
<sheet>
|
|
48
|
+
<group>
|
|
49
|
+
<group>
|
|
50
|
+
<field
|
|
51
|
+
name="field_id"
|
|
52
|
+
context="{'search_by_technical_name': True, 'display_technical_name': True}"
|
|
53
|
+
options="{'create': False, 'create_edit': False}"
|
|
54
|
+
/>
|
|
55
|
+
<field name="model_id" />
|
|
56
|
+
<field name="comodel_id" />
|
|
57
|
+
</group>
|
|
58
|
+
<group>
|
|
59
|
+
<field name="option_create" />
|
|
60
|
+
<field name="option_create_edit" />
|
|
61
|
+
</group>
|
|
62
|
+
</group>
|
|
63
|
+
</sheet>
|
|
64
|
+
</form>
|
|
65
|
+
</field>
|
|
66
|
+
</record>
|
|
67
|
+
|
|
68
|
+
<record id="m2x_create_edit_option_tree_base" model="ir.ui.view">
|
|
69
|
+
<field name="name">m2x.create.edit.option tree view - base</field>
|
|
70
|
+
<field name="model">m2x.create.edit.option</field>
|
|
71
|
+
<field name="arch" type="xml">
|
|
72
|
+
<tree default_order="model_name asc, field_name asc">
|
|
73
|
+
<field name="model_id" optional="show" />
|
|
74
|
+
<field name="model_name" optional="hide" />
|
|
75
|
+
<field name="field_id" optional="show" />
|
|
76
|
+
<field name="field_name" optional="hide" />
|
|
77
|
+
<field name="comodel_id" optional="show" />
|
|
78
|
+
<field name="comodel_name" optional="hide" />
|
|
79
|
+
<field name="option_create" />
|
|
80
|
+
<field name="option_create_edit" />
|
|
81
|
+
</tree>
|
|
82
|
+
</field>
|
|
83
|
+
</record>
|
|
84
|
+
|
|
85
|
+
<record id="m2x_create_edit_option_tree_editable" model="ir.ui.view">
|
|
86
|
+
<field name="name">m2x.create.edit.option tree view - editable</field>
|
|
87
|
+
<field name="model">m2x.create.edit.option</field>
|
|
88
|
+
<field name="mode">primary</field>
|
|
89
|
+
<field
|
|
90
|
+
name="inherit_id"
|
|
91
|
+
ref="web_m2x_options_manager.m2x_create_edit_option_tree_base"
|
|
92
|
+
/>
|
|
93
|
+
<field name="arch" type="xml">
|
|
94
|
+
<tree position="attributes">
|
|
95
|
+
<attribute name="editable">bottom</attribute>
|
|
96
|
+
</tree>
|
|
97
|
+
<field name="field_id" position="attributes">
|
|
98
|
+
<attribute
|
|
99
|
+
name="context"
|
|
100
|
+
>{'search_by_technical_name': True, 'display_technical_name': True}</attribute>
|
|
101
|
+
<attribute
|
|
102
|
+
name="options"
|
|
103
|
+
>{'create': False, 'create_edit': False}</attribute>
|
|
104
|
+
</field>
|
|
105
|
+
</field>
|
|
106
|
+
</record>
|
|
107
|
+
|
|
108
|
+
<record id="m2x_create_edit_option_tree_o2m_filter_by_model" model="ir.ui.view">
|
|
109
|
+
<field name="name">m2x.create.edit.option tree view - filter by model</field>
|
|
110
|
+
<field name="model">m2x.create.edit.option</field>
|
|
111
|
+
<field name="mode">primary</field>
|
|
112
|
+
<field
|
|
113
|
+
name="inherit_id"
|
|
114
|
+
ref="web_m2x_options_manager.m2x_create_edit_option_tree_editable"
|
|
115
|
+
/>
|
|
116
|
+
<field name="arch" type="xml">
|
|
117
|
+
<field name="field_id" position="attributes">
|
|
118
|
+
<attribute name="context" operation="update">{
|
|
119
|
+
'o2m_list_view_m2x_domain': [('model_id', '=', parent.id)],
|
|
120
|
+
}</attribute>
|
|
121
|
+
</field>
|
|
122
|
+
</field>
|
|
123
|
+
</record>
|
|
124
|
+
|
|
125
|
+
<record id="m2x_create_edit_option_tree_o2m_filter_by_comodel" model="ir.ui.view">
|
|
126
|
+
<field name="name">m2x.create.edit.option tree view - filter by comodel</field>
|
|
127
|
+
<field name="model">m2x.create.edit.option</field>
|
|
128
|
+
<field name="mode">primary</field>
|
|
129
|
+
<field
|
|
130
|
+
name="inherit_id"
|
|
131
|
+
ref="web_m2x_options_manager.m2x_create_edit_option_tree_editable"
|
|
132
|
+
/>
|
|
133
|
+
<field name="arch" type="xml">
|
|
134
|
+
<field name="field_id" position="attributes">
|
|
135
|
+
<attribute name="context" operation="update">{
|
|
136
|
+
'o2m_list_view_m2x_domain': [('comodel_id', '=', parent.id)],
|
|
137
|
+
}</attribute>
|
|
138
|
+
</field>
|
|
139
|
+
</field>
|
|
140
|
+
</record>
|
|
141
|
+
|
|
142
|
+
<record id="m2x_create_edit_option_action" model="ir.actions.act_window">
|
|
143
|
+
<field name="name">Fields' Create & Edit Options</field>
|
|
144
|
+
<field name="res_model">m2x.create.edit.option</field>
|
|
145
|
+
<field name="view_mode">tree,form</field>
|
|
146
|
+
</record>
|
|
147
|
+
|
|
148
|
+
<record id="m2x_create_edit_option_menu" model="ir.ui.menu">
|
|
149
|
+
<field name="name">Fields' Create & Edit Options</field>
|
|
150
|
+
<field name="parent_id" ref="base.next_id_9" />
|
|
151
|
+
<field
|
|
152
|
+
name="action"
|
|
153
|
+
ref="web_m2x_options_manager.m2x_create_edit_option_action"
|
|
154
|
+
/>
|
|
155
|
+
</record>
|
|
156
|
+
|
|
157
|
+
</odoo>
|