summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-08-19 14:43:31 +0200
committerChristophe Simonis <chs@odoo.com>2019-08-19 14:43:31 +0200
commit121034f79a6a4e57ed936c8672fc5f8472ca2cfd (patch)
tree18924689c1b4b90a36d130930d08155172c441ec
parent76a52bdbfbcfe1fc9411e09c122711ce3a102809 (diff)
parent1e4de19cd49c005ebe3b14558e8eca6a1f55e72f (diff)
[MERGE] forward port branch 11.0 up to 1e4de19cd49
-rw-r--r--addons/account/models/account_invoice.py2
-rw-r--r--addons/mail/models/mail_thread.py2
-rw-r--r--addons/stock/models/stock_production_lot.py2
-rw-r--r--addons/website_sale_comparison/controllers/main.py11
-rw-r--r--addons/website_sale_comparison/models/website_sale_comparison.py2
-rw-r--r--doc/reference/qweb.rst8
-rw-r--r--odoo/addons/base/models/ir_rule.py4
-rw-r--r--odoo/addons/test_access_rights/ir.model.access.csv1
-rw-r--r--odoo/addons/test_access_rights/models.py12
-rw-r--r--odoo/addons/test_access_rights/tests/test_ir_rules.py38
10 files changed, 66 insertions, 16 deletions
diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py
index ce7229533e0..3a5d6ff40dc 100644
--- a/addons/account/models/account_invoice.py
+++ b/addons/account/models/account_invoice.py
@@ -1673,7 +1673,7 @@ class AccountInvoiceLine(models.Model):
return
if not self.product_id:
fpos = self.invoice_id.fiscal_position_id
- self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids, partner=self.partner_id).ids
+ self.invoice_line_tax_ids = fpos.map_tax(self.account_id.tax_ids, partner=self.partner_id)
elif not self.price_unit:
self._set_taxes()
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index e1127b6c44b..33515993594 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -1509,7 +1509,7 @@ class MailThread(models.AbstractModel):
# Very unusual situation, be we should be fault-tolerant here
message_id = "<%s@localhost>" % time.time()
_logger.debug('Parsing Message without message-id, generating a random one: %s', message_id)
- msg_dict['message_id'] = message_id
+ msg_dict['message_id'] = message_id.strip()
if message.get('Subject'):
msg_dict['subject'] = tools.decode_smtp_header(message.get('Subject'))
diff --git a/addons/stock/models/stock_production_lot.py b/addons/stock/models/stock_production_lot.py
index 002a750f024..cbcf3416c83 100644
--- a/addons/stock/models/stock_production_lot.py
+++ b/addons/stock/models/stock_production_lot.py
@@ -40,7 +40,7 @@ class ProductionLot(models.Model):
@api.multi
def write(self, vals):
if 'product_id' in vals:
- move_lines = self.env['stock.move.line'].search([('lot_id', 'in', self.ids)])
+ move_lines = self.env['stock.move.line'].search([('lot_id', 'in', self.ids), ('product_id', '!=', vals['product_id'])])
if move_lines:
raise UserError(_(
'You are not allowed to change the product linked to a serial or lot number ' +
diff --git a/addons/website_sale_comparison/controllers/main.py b/addons/website_sale_comparison/controllers/main.py
index 29b605a9837..a54d9763e51 100644
--- a/addons/website_sale_comparison/controllers/main.py
+++ b/addons/website_sale_comparison/controllers/main.py
@@ -18,14 +18,15 @@ class WebsiteSaleProductComparison(WebsiteSale):
products = request.env['product.product'].search([('id', 'in', product_ids)])
values['products'] = products.with_context(display_default_code=False)
- res = {}
+ res = OrderedDict()
+ attrs = products.mapped('attribute_line_ids.attribute_id').filtered(lambda x: x.create_variant)
+ for attr in attrs.sorted(lambda att: (att.category_id.sequence, att.sequence)):
+ cat_name = attr.category_id.name or _('Uncategorized')
+ res.setdefault(cat_name, OrderedDict()).setdefault(attr.name, [' - '] * len(products))
for num, product in enumerate(products):
- for var in product.attribute_line_ids.sorted(lambda x: x.attribute_id.sequence):
+ for var in product.attribute_line_ids.filtered(lambda x: x.attribute_id.create_variant):
cat_name = var.attribute_id.category_id.name or _('Uncategorized')
att_name = var.attribute_id.name
- if not var.attribute_id.create_variant:
- continue
- res.setdefault(cat_name, OrderedDict()).setdefault(att_name, [' - '] * len(products))
val = product.attribute_value_ids.filtered(lambda x: x.attribute_id == var.attribute_id)
res[cat_name][att_name][num] = val[0].name
values['specs'] = res
diff --git a/addons/website_sale_comparison/models/website_sale_comparison.py b/addons/website_sale_comparison/models/website_sale_comparison.py
index f19692efe81..88be2499c0a 100644
--- a/addons/website_sale_comparison/models/website_sale_comparison.py
+++ b/addons/website_sale_comparison/models/website_sale_comparison.py
@@ -26,6 +26,6 @@ class ProductTemplate(models.Model):
def get_variant_groups(self):
res = OrderedDict()
- for var in self.attribute_line_ids.sorted(lambda x: x.attribute_id.sequence):
+ for var in self.attribute_line_ids.sorted(lambda x: (x.attribute_id.category_id.sequence, x.attribute_id.sequence)):
res.setdefault(var.attribute_id.category_id.name or _('Uncategorized'), []).append(var)
return res
diff --git a/doc/reference/qweb.rst b/doc/reference/qweb.rst
index c1c7ec8e7c4..cd44ae28235 100644
--- a/doc/reference/qweb.rst
+++ b/doc/reference/qweb.rst
@@ -455,12 +455,16 @@ template inheritance
''''''''''''''''''''
Template inheritance is used to alter existing templates in-place, e.g. to
-add information to templates created by an other modules.
+add information to templates created by other modules.
Template inheritance is performed via the ``t-extend`` directive which takes
the name of the template to alter as parameter.
-The alteration is then performed with any number of ``t-jquery``
+When ``t-extend`` is combined with ``t-name`` a new template with the given name
+is created. In this case the extended template is not altered, instead the
+directives define how to create the new template.
+
+In both cases the alteration is then performed with any number of ``t-jquery``
sub-directives::
<t t-extend="base.template">
diff --git a/odoo/addons/base/models/ir_rule.py b/odoo/addons/base/models/ir_rule.py
index a319b74a1a2..c1174915451 100644
--- a/odoo/addons/base/models/ir_rule.py
+++ b/odoo/addons/base/models/ir_rule.py
@@ -42,7 +42,9 @@ class IrRule(models.Model):
def _eval_context(self):
"""Returns a dictionary to use as evaluation context for
ir.rule domains."""
- return {'user': self.env.user, 'time': time}
+ # use an empty context for 'user' to make the domain evaluation
+ # independent from the context
+ return {'user': self.env.user.with_context({}), 'time': time}
@api.depends('groups')
def _compute_global(self):
diff --git a/odoo/addons/test_access_rights/ir.model.access.csv b/odoo/addons/test_access_rights/ir.model.access.csv
index eda40f960d9..4bbc5b37c59 100644
--- a/odoo/addons/test_access_rights/ir.model.access.csv
+++ b/odoo/addons/test_access_rights/ir.model.access.csv
@@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_test_access_right_some_obj,access_test_access_right_some_obj,model_test_access_right_some_obj,,1,1,1,1
access_test_access_right_container,access_test_access_right_container,model_test_access_right_container,,1,1,1,1
+access_test_access_right_obj_categ,access_test_access_right_obj_categ,model_test_access_right_obj_categ,,1,1,1,1
diff --git a/odoo/addons/test_access_rights/models.py b/odoo/addons/test_access_rights/models.py
index ee4b7ffb216..04d73128335 100644
--- a/odoo/addons/test_access_rights/models.py
+++ b/odoo/addons/test_access_rights/models.py
@@ -7,9 +7,21 @@ class SomeObj(models.Model):
_name = 'test_access_right.some_obj'
val = fields.Integer()
+ categ_id = fields.Many2one('test_access_right.obj_categ')
class Container(models.Model):
_name = 'test_access_right.container'
some_ids = fields.Many2many('test_access_right.some_obj', 'test_access_right_rel', 'container_id', 'some_id')
+
+
+class ObjCateg(models.Model):
+ _name = 'test_access_right.obj_categ'
+
+ name = fields.Char(required=True)
+
+ def search(self, args, **kwargs):
+ if self.env.context.get('only_media'):
+ args += [('name', '=', 'Media')]
+ return super(ObjCateg, self).search(args, **kwargs)
diff --git a/odoo/addons/test_access_rights/tests/test_ir_rules.py b/odoo/addons/test_access_rights/tests/test_ir_rules.py
index c1ce6cc6b80..dc74e3ee59d 100644
--- a/odoo/addons/test_access_rights/tests/test_ir_rules.py
+++ b/odoo/addons/test_access_rights/tests/test_ir_rules.py
@@ -8,10 +8,11 @@ class TestRules(TransactionCase):
def setUp(self):
super(TestRules, self).setUp()
- self.id1 = self.env['test_access_right.some_obj']\
- .create({'val': 1}).id
- self.id2 = self.env['test_access_right.some_obj']\
- .create({'val': -1}).id
+ ObjCateg = self.env['test_access_right.obj_categ']
+ SomeObj = self.env['test_access_right.some_obj']
+ self.categ1 = ObjCateg.create({'name': 'Food'}).id
+ self.id1 = SomeObj.create({'val': 1, 'categ_id': self.categ1}).id
+ self.id2 = SomeObj.create({'val': -1, 'categ_id': self.categ1}).id
# create a global rule forbidding access to records with a negative
# (or zero) val
self.env['ir.rule'].create({
@@ -19,6 +20,13 @@ class TestRules(TransactionCase):
'model_id': self.browse_ref('test_access_rights.model_test_access_right_some_obj').id,
'domain_force': "[('val', '>', 0)]"
})
+ # create a global rule that forbid access to records without
+ # categories, the search is part of the test
+ self.env['ir.rule'].create({
+ 'name': 'See all categories',
+ 'model_id': self.browse_ref('test_access_rights.model_test_access_right_some_obj').id,
+ 'domain_force': "[('categ_id', 'in', user.env['test_access_right.obj_categ'].search([]).ids)]"
+ })
def test_basic_access(self):
env = self.env(user=self.browse_ref('base.public_user'))
@@ -56,3 +64,25 @@ class TestRules(TransactionCase):
container_user.write({'some_ids': [(5,)]})
self.assertItemsEqual(container_user.some_ids.ids, [])
self.assertItemsEqual(container_admin.some_ids.ids, [self.id2])
+
+ def test_no_context_in_ir_rules(self):
+ """ The context should not impact the ir rules. """
+ env = self.env(user=self.browse_ref('base.public_user'))
+ ObjCateg = self.env['test_access_right.obj_categ']
+ SomeObj = self.env['test_access_right.some_obj']
+
+ # validate the effect of context on category search, there are
+ # no existing media category
+ self.assertTrue(ObjCateg.search([]))
+ self.assertFalse(ObjCateg.with_context(only_media=True).search([]))
+
+ # record1 is food and is accessible with an empy context
+ ObjCateg.clear_caches()
+ records = SomeObj.search([('id', '=', self.id1)])
+ self.assertTrue(records)
+
+ # it should also be accessible as the context is not used when
+ # searching for SomeObjs
+ ObjCateg.clear_caches()
+ records = SomeObj.with_context(only_media=True).search([('id', '=', self.id1)])
+ self.assertTrue(records)