summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-02-14 18:09:24 +0100
committerChristophe Simonis <chs@odoo.com>2019-02-14 18:09:24 +0100
commitc1fe9f18d5a1f6c13548ea2af964bde40b3b6861 (patch)
tree811d2b54d9d6867741ffd6ce26e3baf292d4e1fb
parent18228f9c4e1642a3c0afb24223bccfb3354c928a (diff)
parent69719f598aad2eb8ff87687bc47900030688f0ce (diff)
[MERGE] forward port branch 10.0 up to 69719f598aa
-rw-r--r--addons/base_import_module/i18n/base_import_module.pot5
-rw-r--r--addons/base_import_module/views/base_import_module_view.xml1
-rw-r--r--addons/google_calendar/models/google_calendar.py4
-rw-r--r--addons/l10n_it/__manifest__.py2
-rw-r--r--addons/point_of_sale/models/pos_order.py10
-rw-r--r--addons/stock/models/product.py4
-rw-r--r--addons/stock/views/stock_picking_views.xml8
-rw-r--r--addons/web_editor/static/lib/summernote/src/js/EventHandler.js10
-rw-r--r--addons/web_editor/static/src/js/backend.js9
-rw-r--r--doc/cla/individual/naglis.md11
-rw-r--r--odoo/addons/base/ir/ir_model.py7
-rw-r--r--odoo/addons/test_new_api/tests/test_new_fields.py41
-rw-r--r--odoo/fields.py16
-rw-r--r--odoo/models.py7
14 files changed, 114 insertions, 21 deletions
diff --git a/addons/base_import_module/i18n/base_import_module.pot b/addons/base_import_module/i18n/base_import_module.pot
index 8af3356b3f4..b46e0ea5dfa 100644
--- a/addons/base_import_module/i18n/base_import_module.pot
+++ b/addons/base_import_module/i18n/base_import_module.pot
@@ -133,6 +133,11 @@ msgid "No file sent."
msgstr ""
#. module: base_import_module
+#: model:ir.ui.view,arch_db:base_import_module.view_base_module_import
+msgid "Note: you can only import data modules (.xml files and static assets)"
+msgstr ""
+
+#. module: base_import_module
#: code:addons/base_import_module/controllers/main.py:25
#, python-format
msgid "Only administrators can upload a module"
diff --git a/addons/base_import_module/views/base_import_module_view.xml b/addons/base_import_module/views/base_import_module_view.xml
index 60af0d3fc61..b132c7f3a83 100644
--- a/addons/base_import_module/views/base_import_module_view.xml
+++ b/addons/base_import_module/views/base_import_module_view.xml
@@ -8,6 +8,7 @@
<form string="Import module">
<field name="state" invisible="1"/>
<separator string="Import Module" colspan="4"/>
+ <p class="alert alert-warning">Note: you can only import data modules (.xml files and static assets)</p>
<group states="init" col="4">
<label string="Select module package to import (.zip file):" colspan="4"/>
<field name="module_file" colspan="4"/>
diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py
index b71f93fc87b..d2fa4839b1f 100644
--- a/addons/google_calendar/models/google_calendar.py
+++ b/addons/google_calendar/models/google_calendar.py
@@ -443,7 +443,9 @@ class GoogleCalendar(models.AbstractModel):
if google_attendee.get('found'):
continue
- attendee = ResPartner.search([('email', '=', google_attendee['email'])], limit=1)
+ attendee = ResPartner.search([('email', '=ilike', google_attendee['email']), ('user_ids', '!=', False)], limit=1)
+ if not attendee:
+ attendee = ResPartner.search([('email', '=ilike', google_attendee['email'])], limit=1)
if not attendee:
data = {
'email': partner_email,
diff --git a/addons/l10n_it/__manifest__.py b/addons/l10n_it/__manifest__.py
index 7d3eca9daab..7acf2ee6b1c 100644
--- a/addons/l10n_it/__manifest__.py
+++ b/addons/l10n_it/__manifest__.py
@@ -13,7 +13,7 @@ Piano dei conti italiano di un'impresa generica.
Italian accounting chart and localization.
""",
'category': 'Localization',
- 'website': 'http://www.openerp-italia.org/',
+ 'website': 'http://www.odoo.com/',
'data': [
'data/l10n_it_chart_data.xml',
'data/account.account.template.csv',
diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py
index dedbc839d85..4eb99789963 100644
--- a/addons/point_of_sale/models/pos_order.py
+++ b/addons/point_of_sale/models/pos_order.py
@@ -391,13 +391,11 @@ class PosOrder(models.Model):
aml = order.statement_ids.mapped('journal_entry_ids') | order.account_move.line_ids | order.invoice_id.move_id.line_ids
aml = aml.filtered(lambda r: not r.reconciled and r.account_id.internal_type == 'receivable' and r.partner_id == order.partner_id.commercial_partner_id)
- # Reconcile returns first
- # to avoid mixing up the credit of a payment and the credit of a return
- # in the receivable account
- aml_returns = aml.filtered(lambda l: (l.journal_id.type == 'sale' and l.credit) or (l.journal_id.type != 'sale' and l.debit))
try:
- aml_returns.reconcile()
- (aml - aml_returns).reconcile()
+ # Cash returns will be well reconciled
+ # Whereas freight returns won't be
+ # "c'est la vie..."
+ aml.reconcile()
except Exception:
# There might be unexpected situations where the automatic reconciliation won't
# work. We don't want the user to be blocked because of this, since the automatic
diff --git a/addons/stock/models/product.py b/addons/stock/models/product.py
index 7adcdb992e0..8192b4eec4c 100644
--- a/addons/stock/models/product.py
+++ b/addons/stock/models/product.py
@@ -262,7 +262,9 @@ class Product(models.Model):
# TODO: Still optimization possible when searching virtual quantities
ids = []
- for product in self.search([]):
+ # Order the search on `id` to prevent the default order on the product name which slows
+ # down the search because of the join on the translation table to get the translated names.
+ for product in self.search([], order='id'):
if OPERATORS[operator](product[field], value):
ids.append(product.id)
return [('id', 'in', ids)]
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index afbc82a6b30..3890b275819 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -55,8 +55,8 @@
<field name="use_existing_lots" groups="stock.group_production_lot"/>
</group>
<group string="Locations">
- <field name="default_location_src_id" attrs="{'required': [('code', '=', 'internal')]}"/>
- <field name="default_location_dest_id" attrs="{'required': [('code', 'in', ('internal', 'incoming'))]}"/>
+ <field name="default_location_src_id" attrs="{'required': [('code', '=', 'internal')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
+ <field name="default_location_dest_id" attrs="{'required': [('code', 'in', ('internal', 'incoming'))]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
</group>
</group>
</sheet>
@@ -229,8 +229,8 @@
<group>
<group>
<field name="partner_id" attrs="{'invisible': [('picking_type_code', '=', 'internal')]}"/>
- <field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}"/>
- <field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}"/>
+ <field name="location_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'outgoing')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
+ <field name="location_dest_id" groups="stock.group_stock_multi_locations" attrs="{'invisible': [('picking_type_code', '=', 'incoming')]}" domain="['|', ('company_id', '=', company_id), ('company_id', '=', False)]"/>
<field name="backorder_id" readonly="1" attrs="{'invisible': [('backorder_id','=',False)]}"/>
</group>
<group>
diff --git a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js
index 1cb9cd8e1c7..d8dcbe18a8b 100644
--- a/addons/web_editor/static/lib/summernote/src/js/EventHandler.js
+++ b/addons/web_editor/static/lib/summernote/src/js/EventHandler.js
@@ -207,11 +207,15 @@ define([
var layoutInfo = dom.makeLayoutInfo(target);
/* ODOO: (start_modification */
var $editable = layoutInfo.editable();
- if (!event.isDefaultPrevented()) {
- modules.editor.saveRange($editable);
+ if (event.setStyleInfoFromEditable) {
+ var styleInfo = modules.editor.styleFromNode($editable);
+ } else {
+ if (!event.isDefaultPrevented()) {
+ modules.editor.saveRange($editable);
+ }
+ var styleInfo = modules.editor.currentStyle(target);
}
/* ODOO: end_modification) */
- var styleInfo = modules.editor.currentStyle(target);
self.updateStyleInfo(styleInfo, layoutInfo);
}, 0);
};
diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js
index 1c5feb7a7d4..884ef68d674 100644
--- a/addons/web_editor/static/src/js/backend.js
+++ b/addons/web_editor/static/src/js/backend.js
@@ -57,7 +57,8 @@ var FieldTextHtmlSimple = widget.extend({
this.$translate.remove();
this.$translate = $();
// Triggers a mouseup to refresh the editor toolbar
- this.$content.trigger('mouseup');
+ var mouseupEvent = $.Event('mouseup', {'setStyleInfoFromEditable': true});
+ this.$content.trigger(mouseupEvent);
return def;
},
initialize_content: function () {
@@ -170,7 +171,11 @@ var FieldTextHtmlSimple = widget.extend({
transcoder.class_to_style(this.$content);
transcoder.font_to_img(this.$content);
}
- this.internal_set_value(this.$content.html());
+ var value = this.$content.html();
+ if (this.get('value') === false && value === '<p><br></p>') {
+ value = false;
+ }
+ this.internal_set_value(value);
},
destroy_content: function () {
$(".oe-view-manager-content").off("scroll");
diff --git a/doc/cla/individual/naglis.md b/doc/cla/individual/naglis.md
new file mode 100644
index 00000000000..dde081dfaa1
--- /dev/null
+++ b/doc/cla/individual/naglis.md
@@ -0,0 +1,11 @@
+Lithuania, 2019-02-09
+
+I hereby agree to the terms of the Odoo Individual Contributor License
+Agreement v1.0.
+
+I declare that I am authorized and able to make this agreement and sign this
+declaration.
+
+Signed,
+
+Naglis Jonaitis naglis@mailbox.org https://github.com/naglis
diff --git a/odoo/addons/base/ir/ir_model.py b/odoo/addons/base/ir/ir_model.py
index 911692cd780..521a94238f5 100644
--- a/odoo/addons/base/ir/ir_model.py
+++ b/odoo/addons/base/ir/ir_model.py
@@ -443,6 +443,13 @@ class IrModelFields(models.Model):
"""
return self.sudo().search([('model', '=', model_name), ('name', '=', name)])
+ @tools.ormcache('model_name', 'name')
+ def _get_id(self, model_name, name):
+ self.env.cr.execute("SELECT id FROM ir_model_fields WHERE model=%s AND name=%s",
+ (model_name, name))
+ result = self.env.cr.fetchone()
+ return result and result[0]
+
@api.multi
def _drop_column(self):
tables_to_drop = set()
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index 1d982c4bb9b..6dc80305c7f 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -276,6 +276,16 @@ class TestFields(common.TransactionCase):
self.assertEqual(record.bar, 'Ho')
self.assertEqual(record.counts, {'compute': 0, 'inverse': 1})
+ def test_13_inverse_access(self):
+ """ test access rights on inverse fields """
+ foo = self.env['test_new_api.category'].create({'name': 'Foo'})
+ user = self.env['res.users'].create({'name': 'Foo', 'login': 'foo'})
+ self.assertFalse(user.has_group('base.group_system'))
+ # add group on non-stored inverse field
+ self.patch(type(foo).display_name, 'groups', 'base.group_system')
+ with self.assertRaises(AccessError):
+ foo.sudo(user).display_name = 'Forbidden'
+
def test_14_search(self):
""" test search on computed fields """
discussion = self.env.ref('test_new_api.discussion_0')
@@ -572,6 +582,10 @@ class TestFields(common.TransactionCase):
self.env['ir.property'].create({'name': 'foo', 'fields_id': field.id,
'value': 'default', 'type': 'char'})
+ # assumption: users don't have access to 'ir.property'
+ accesses = self.env['ir.model.access'].search([('model_id.model', '=', 'ir.property')])
+ accesses.write(dict.fromkeys(['perm_read', 'perm_write', 'perm_create', 'perm_unlink'], False))
+
# create/modify a record, and check the value for each user
record = self.env['test_new_api.company'].create({'foo': 'main'})
record.invalidate_cache()
@@ -591,6 +605,13 @@ class TestFields(common.TransactionCase):
self.assertEqual(record.sudo(user1).foo, False)
self.assertEqual(record.sudo(user2).foo, 'default')
+ # set field with 'force_company' in context
+ record.sudo(user0).with_context(force_company=company1.id).foo = 'beta'
+ record.invalidate_cache()
+ self.assertEqual(record.sudo(user0).foo, 'main')
+ self.assertEqual(record.sudo(user1).foo, 'beta')
+ self.assertEqual(record.sudo(user2).foo, 'default')
+
# create company record and attribute
company_record = self.env['test_new_api.company'].create({'foo': 'ABC'})
attribute_record = self.env['test_new_api.company.attr'].create({
@@ -610,6 +631,26 @@ class TestFields(common.TransactionCase):
self.assertEqual(attribute_record.bar, 'DEFDEF')
self.assertFalse(self.env.has_todo())
+ # add group on company-dependent field
+ self.assertFalse(user0.has_group('base.group_system'))
+ self.patch(type(record).foo, 'groups', 'base.group_system')
+ with self.assertRaises(AccessError):
+ record.sudo(user0).foo = 'forbidden'
+
+ user0.write({'groups_id': [(4, self.env.ref('base.group_system').id)]})
+ record.sudo(user0).foo = 'yes we can'
+
+ # add ir.rule to prevent access on record
+ self.assertTrue(user0.has_group('base.group_user'))
+ model_id = self.env['ir.model'].search([('model', '=', record._name)]).id
+ rule = self.env['ir.rule'].create({
+ 'model_id': model_id,
+ 'groups': [self.env.ref('base.group_user').id],
+ 'domain_force': str([('id', '!=', record.id)]),
+ })
+ with self.assertRaises(AccessError):
+ record.sudo(user0).foo = 'forbidden'
+
def test_28_sparse(self):
""" test sparse fields. """
record = self.env['test_new_api.sparse'].create({})
diff --git a/odoo/fields.py b/odoo/fields.py
index a4f6bc7e27b..511fa0a3235 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -661,13 +661,25 @@ class Field(object):
return model.env['ir.property'].get(self.name, self.model_name)
def _compute_company_dependent(self, records):
- Property = records.env['ir.property']
+ # read property as superuser, as the current user may not have access
+ context = records.env.context
+ if 'force_company' not in context:
+ field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name)
+ company = records.env['res.company']._company_default_get(self.model_name, field_id)
+ context = dict(context, force_company=company.id)
+ Property = records.env(user=SUPERUSER_ID, context=context)['ir.property']
values = Property.get_multi(self.name, self.model_name, records.ids)
for record in records:
record[self.name] = values.get(record.id)
def _inverse_company_dependent(self, records):
- Property = records.env['ir.property']
+ # update property as superuser, as the current user may not have access
+ context = records.env.context
+ if 'force_company' not in context:
+ field_id = records.env['ir.model.fields']._get_id(self.model_name, self.name)
+ company = records.env['res.company']._company_default_get(self.model_name, field_id)
+ context = dict(context, force_company=company.id)
+ Property = records.env(user=SUPERUSER_ID, context=context)['ir.property']
values = {
record.id: self.convert_to_write(record[self.name], record)
for record in records
diff --git a/odoo/models.py b/odoo/models.py
index 92e6cd7c3d6..706fc3b2a7c 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -3533,10 +3533,15 @@ class BaseModel(object):
protected_fields = map(self._fields.get, new_vals)
with self.env.protecting(protected_fields, self):
# write old-style fields with (low-level) method _write
- if old_vals:
+ if old_vals or new_vals:
+ # if log_access is enabled, this updates 'write_date' and
+ # 'write_uid' and check access rules, even when old_vals is
+ # empty
self._write(old_vals)
if new_vals:
+ self.check_field_access_rights('write', list(new_vals))
+
self.modified(set(new_vals) - set(old_vals))
# put the values of fields into cache, and inverse them