summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/website/models/ir_module_module.py10
-rw-r--r--addons/website/models/ir_ui_view.py8
-rw-r--r--addons/website/tests/test_views.py124
-rw-r--r--odoo/addons/base/models/ir_ui_view.py23
4 files changed, 132 insertions, 33 deletions
diff --git a/addons/website/models/ir_module_module.py b/addons/website/models/ir_module_module.py
index 9ec53dcc519..62cddbf38a3 100644
--- a/addons/website/models/ir_module_module.py
+++ b/addons/website/models/ir_module_module.py
@@ -411,3 +411,13 @@ class IrModuleModule(models.Model):
'res_model': self._name,
'res_id': theme.id,
})
+
+ def _check(self):
+ super()._check()
+ View = self.env['ir.ui.view']
+ website_views_to_adapt = getattr(self.pool, 'website_views_to_adapt', [])
+ if website_views_to_adapt:
+ for view_replay in website_views_to_adapt:
+ cow_view = View.browse(view_replay[0])
+ View._load_records_write_on_cow(cow_view, view_replay[1], view_replay[2])
+ self.pool.website_views_to_adapt.clear()
diff --git a/addons/website/models/ir_ui_view.py b/addons/website/models/ir_ui_view.py
index 69022920a39..c1d3a1304e1 100644
--- a/addons/website/models/ir_ui_view.py
+++ b/addons/website/models/ir_ui_view.py
@@ -131,6 +131,14 @@ class View(models.Model):
return True
+ def _load_records_write_on_cow(self, cow_view, inherit_id, values):
+ inherit_id = self.search([
+ ('key', '=', self.browse(inherit_id).key),
+ ('website_id', 'in', (False, cow_view.website_id.id)),
+ ], order='website_id', limit=1).id
+ values['inherit_id'] = inherit_id
+ cow_view.with_context(no_cow=True).write(values)
+
def _create_all_specific_views(self, processed_modules):
""" When creating a generic child view, we should
also create that view under specific view trees (COW'd).
diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py
index f07dfa898ee..d1bfea11166 100644
--- a/addons/website/tests/test_views.py
+++ b/addons/website/tests/test_views.py
@@ -13,7 +13,18 @@ def attrs(**kwargs):
return {'data-oe-%s' % key: str(value) for key, value in kwargs.items()}
-class TestViewSaving(common.TransactionCase):
+class TestViewSavingCommon(common.TransactionCase):
+ def _create_imd(self, view):
+ xml_id = view.key.split('.')
+ return self.env['ir.model.data'].create({
+ 'module': xml_id[0],
+ 'name': xml_id[1],
+ 'model': view._name,
+ 'res_id': view.id,
+ })
+
+
+class TestViewSaving(TestViewSavingCommon):
def eq(self, a, b):
self.assertEqual(a.tag, b.tag)
@@ -43,6 +54,7 @@ class TestViewSaving(common.TransactionCase):
self.view_id = self.env['ir.ui.view'].create({
'name': "Test View",
'type': 'qweb',
+ 'key': 'website.test_view',
'arch': ET.tostring(self.arch, encoding='unicode')
})
@@ -138,12 +150,7 @@ class TestViewSaving(common.TransactionCase):
Company = self.env['res.company']
# create an xmlid for the view
- imd = self.env['ir.model.data'].create({
- 'module': 'website',
- 'name': 'test_view',
- 'model': self.view_id._name,
- 'res_id': self.view_id.id,
- })
+ imd = self._create_imd(self.view_id)
self.assertEqual(self.view_id.model_data_id, imd)
self.assertFalse(imd.noupdate)
@@ -276,7 +283,7 @@ class TestViewSaving(common.TransactionCase):
@tagged('-at_install', 'post_install')
-class TestCowViewSaving(common.TransactionCase):
+class TestCowViewSaving(TestViewSavingCommon):
def setUp(self):
super(TestCowViewSaving, self).setUp()
View = self.env['ir.ui.view']
@@ -773,6 +780,7 @@ class TestCowViewSaving(common.TransactionCase):
View._create_all_specific_views(['_website_sale_comparison'])
specific_view = Website.with_context(load_all_views=True, website_id=1).viewref('_website_sale.product')
+ self.assertEqual(self.base_view.key, specific_view.key, "Ensure it is equal as it should be for the rest of the test so we test the expected behaviors")
specific_view_arch = specific_view.read_combined(['arch'])['arch']
self.assertEqual(specific_view.website_id.id, 1, "Ensure we got specific view to perform the checks against")
self.assertEqual(specific_view_arch, '<p>COMPARE</p>', "When a module creates an inherited view (on a generic tree), it should also create that view in the specific COW'd tree.")
@@ -785,20 +793,33 @@ class TestCowViewSaving(common.TransactionCase):
self.assertEqual(specific_view_arch, '<p>COMPARE EDITED</p>', "When a module updates an inherited view (on a generic tree), it should also update the copies of that view (COW).")
# Test fields that should not be COW'd
- random_view_id = View.search([], limit=1).id
+ random_views = View.search([('key', '!=', None)], limit=2)
View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={
'website_id': None,
- 'inherit_id': random_view_id,
+ 'inherit_id': random_views[0].id,
})])
w1_specific_child_view = Website.with_context(load_all_views=True, website_id=1).viewref('_website_sale_comparison.product_add_to_compare')
generic_child_view = Website.with_context(load_all_views=True).viewref('_website_sale_comparison.product_add_to_compare')
self.assertEqual(w1_specific_child_view.website_id.id, 1, "website_id is a prohibited field when COWing views during _load_records")
- self.assertEqual(w1_specific_child_view.inherit_id.id != random_view_id, True, "inherit_id is a prohibited field when COWing views during _load_records")
- self.assertEqual(generic_child_view.inherit_id.id, random_view_id, "prohibited fields only concerned write on COW'd view. Generic should still considere these fields")
+ self.assertEqual(generic_child_view.inherit_id, random_views[0], "prohibited fields only concerned write on COW'd view. Generic should still considere these fields")
+ self.assertEqual(w1_specific_child_view.inherit_id, random_views[0], "inherit_id update should be repliacated on cow views during _load_records")
# Set back the generic view as parent for the rest of the test
generic_child_view.inherit_id = self.base_view
+ w1_specific_child_view.inherit_id = specific_view
+
+ # Don't update inherit_id if it was anually updated
+ w1_specific_child_view.inherit_id = random_views[1].id
+ View._load_records([dict(xml_id='_website_sale_comparison.product_add_to_compare', values={
+ 'inherit_id': random_views[0].id,
+ })])
+ self.assertEqual(w1_specific_child_view.inherit_id, random_views[1],
+ "inherit_id update should not be repliacated on cow views during _load_records if it was manually updated before")
+
+ # Set back the generic view as parent for the rest of the test
+ generic_child_view.inherit_id = self.base_view
+ w1_specific_child_view.inherit_id = specific_view
# Don't update fields from COW'd view if these fields have been modified from original view
new_website = Website.create({'name': 'New Website'})
@@ -813,12 +834,7 @@ class TestCowViewSaving(common.TransactionCase):
self.assertEqual(new_website_specific_child_view.priority, 6, "XML update should NOT be written on the specific view if the fields have been modified on that specific view")
# Simulate website_sale update on top level view
- self.env['ir.model.data'].create({
- 'module': '_website_sale',
- 'name': 'product',
- 'model': self.base_view._name,
- 'res_id': self.base_view.id,
- })
+ self._create_imd(self.base_view)
self.base_view.invalidate_cache()
View._load_records([dict(xml_id='_website_sale.product', values={
'website_meta_title': 'A bug got fixed by updating this field',
@@ -920,12 +936,7 @@ class TestCowViewSaving(common.TransactionCase):
'arch': '<div>base content</div>',
'key': 'website.base_view_v1',
}).with_context(load_all_views=True)
- self.env['ir.model.data'].create({
- 'module': 'website',
- 'name': 'base_view_v1',
- 'model': v1._name,
- 'res_id': v1.id,
- })
+ self._create_imd(v1)
# Extension
v2 = View.create({
@@ -935,12 +946,7 @@ class TestCowViewSaving(common.TransactionCase):
'arch': '<div position="inside"><ooo>extended content</ooo></div>',
'key': 'website.extension_view_v2',
})
- self.env['ir.model.data'].create({
- 'module': 'website',
- 'name': 'extension_view_v2',
- 'model': v2._name,
- 'res_id': v2.id,
- })
+ self._create_imd(v2)
# multiwebsite specific
v1.with_context(website_id=1).write({'name': 'Extension Specific'})
@@ -986,6 +992,64 @@ class TestCowViewSaving(common.TransactionCase):
self.assertEqual(specific_view.with_context(lang='en_US').arch, '<div>hi</div>',
"loading module translation copy translation from base to specific view")
+ def test_specific_view_module_update_inherit_change(self):
+ """ During a module update, if inherit_id is changed, we need to
+ replicate the change for cow views. """
+ # If D.inherit_id becomes B instead of A, after module update, we expect:
+ # CASE 1
+ # A A' B A A' B
+ # | | => / \
+ # D D' D D'
+ #
+ # CASE 2
+ # A A' B B' A A' B B'
+ # | | => | |
+ # D D' D D'
+ #
+ # CASE 3
+ # A B A B
+ # / \ => / \
+ # D D' D D'
+ #
+ # CASE 4
+ # A B B' A B B'
+ # / \ => | |
+ # D D' D D'
+
+ # 1. Setup following view trees
+ # A A' B
+ # | |
+ # D D'
+ View = self.env['ir.ui.view']
+ Website = self.env['website']
+ self._create_imd(self.inherit_view)
+ # invalidate cache to recompute xml_id, or it will still be empty
+ self.inherit_view.invalidate_cache()
+ base_view_2 = self.base_view.copy({'key': 'website.base_view2', 'arch': '<div>base2 content</div>'})
+ self.base_view.with_context(website_id=1).write({'arch': '<div>website 1 content</div>'})
+ specific_view = Website.with_context(load_all_views=True, website_id=1).viewref(self.base_view.key)
+ specific_view.inherit_children_ids.with_context(website_id=1).write({'arch': '<div position="inside">, extended content website 1</div>'})
+ specific_child_view = Website.with_context(load_all_views=True, website_id=1).viewref(self.inherit_view.key)
+ # 2. Ensure view trees are as expected
+ self.assertEqual(self.base_view.inherit_children_ids, self.inherit_view, "D should be under A")
+ self.assertEqual(specific_view.inherit_children_ids, specific_child_view, "D' should be under A'")
+ self.assertFalse(base_view_2.inherit_children_ids, "B should have no child")
+
+ # 3. Simulate module update, D.inherit_id is now B instead of A
+ View._load_records([dict(xml_id=self.inherit_view.key, values={
+ 'inherit_id': base_view_2.id,
+ })])
+
+ # 4. Ensure view trees is now
+ # A A' B
+ # / \
+ # D D'
+ self.assertTrue(len(self.base_view.inherit_children_ids) == len(specific_view.inherit_children_ids) == 0,
+ "Child views should now be under view B")
+ self.assertEqual(len(base_view_2.inherit_children_ids), 2, "D and D' should be under B")
+ self.assertTrue(self.inherit_view in base_view_2.inherit_children_ids, "D should be under B")
+ self.assertTrue(specific_child_view in base_view_2.inherit_children_ids, "D' should be under B")
+
@tagged('-at_install', 'post_install')
class Crawler(HttpCase):
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index c77316d4557..a042859dc82 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -1826,15 +1826,32 @@ actual arch.
noupdate behavior on views having an ir.model.data.
"""
if self.type == 'qweb':
- # Update also specific views
for cow_view in self._get_specific_views():
authorized_vals = {}
for key in values:
- if cow_view[key] == self[key]:
+ if key != 'inherit_id' and cow_view[key] == self[key]:
authorized_vals[key] = values[key]
- cow_view.write(authorized_vals)
+ # if inherit_id update, replicate change on cow view but
+ # only if that cow view inherit_id wasn't manually changed
+ inherit_id = values.get('inherit_id')
+ if inherit_id and self.inherit_id.id != inherit_id and \
+ cow_view.inherit_id.key == self.inherit_id.key:
+ self._load_records_write_on_cow(cow_view, inherit_id, authorized_vals)
+ else:
+ cow_view.with_context(no_cow=True).write(authorized_vals)
super(View, self)._load_records_write(values)
+ def _load_records_write_on_cow(self, cow_view, inherit_id, values):
+ # for modules updated before `website`, we need to
+ # store the change to replay later on cow views
+ if not hasattr(self.pool, 'website_views_to_adapt'):
+ self.pool.website_views_to_adapt = []
+ self.pool.website_views_to_adapt.append((
+ cow_view.id,
+ inherit_id,
+ values,
+ ))
+
class ResetViewArchWizard(models.TransientModel):
""" A wizard to compare and reset views architecture. """