summaryrefslogtreecommitdiff
path: root/odoo
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2019-04-25 15:42:05 +0000
committerYannick Tivisse <yti@odoo.com>2019-05-13 08:57:49 +0000
commita5b6f31cf28e5381e1c85f66730bcdb55998e643 (patch)
tree0a2261c2f7fc3b38e19692d555c40883e483f6c1 /odoo
parente1424d49b08283eac170e80ce59b7d5be5ff1392 (diff)
[IMP] base: Contextualize the multi company
Purpose ======= Allow the user to select the allowed companies for which he wants to see records on top of selecting his current company. It is confusing for users to see the records from the company he is connected to and the records of the children companies. Instead of using the hierarchy of companies to access records across companies, the user can now select (from his set of allowed companies) the companies for which he wants to access records. /!\ This means that the user will interact with records from company A when in company B. Example: a SO has been created and confirmed in A. When in B, I create the invoice from it. Specifications ============== 1/ Deprecate the parent/children hierarchy on the res.company model. The fields are kept on the res.company model to ensure the retro-compatibility, but won't be used accross the standard code anymore. The only functional usage for this mechanism was to allow to see records from several companies by creating a virtual parent company, which will be possible with the new mechanism. 2/ By default, a user will only see the records of the company he is connected to (or records without a company). (It is still editable by the user if needed). For that, put this information in the user context, to allow having different configurations on different browser tabs. Instead of having domains like ['|', ('company_id', '=', False), ('company_id', 'child_of', user.company_id.id)] you'll have something like ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] Note that the 'company_ids' is a value that is passed in the evaluation context on the record rule, as we already have user, or time. company_ids is a list of the ids of all the enabled companies in the user's context. 3/ Out of the generic improvements brought by this task, this will illustrate issues that could exist since several versions. For example, it should not be possible to create a scrap order for the company A with a package of the company B, or it should not be possible to create an invoice on the company A with payment terms from the company B. Before the version 12.0, it was easy to encounter this kind of issues as the admin was the SUPERUSER_ID. A positive side effect of the fact that the SUPERUSER_ID has become an inactive user was to make it more difficult to introduce mismatch on the records, but haven't solved the issue, as it was still possible to do it with parent companies configuration. Some of these issues have been fixed in this commit, but all the business flows should be re-tested to check if an ir.rule should be introduced (eg: a multi company rule for stock.quand.package), if the company of a record is correctly transfered to another record created from the first record (eg: From a SO, create an invoice and a payment, the company of the sales order should be transfered on the invoice and the payment, even if the company of the sales order is A and I'm logged into the company B with the company A enabled. 4/ Currently, if I click on a button on a notification email (example 'View Task'), I face a traceback if I'm not logged into the company of the record. Now, if you click on a button and if you have access to the record, the correct company will be automatically set. 5/ If I display a kanban view with several records from several companies (and an image), all the images should be displayed. 6/ Currently if you copy paste an url, this will crash if you're not in the correct company. This won't be fixed because it's quite impossible to do it in a clean way. This task brings a workaround. Copy/Paste -> Traceback -> Log into the correct company, re-copy/paste -> Ok. 7/ 2 property methods have been added on the environment to retrieve the company on which the user is logged in and the companies the user enabled, on a specific tab. That way, when creating a record, instead of doing default=lambda self: self.env.user.company_id do default=lambda self: self.env.company_id On the other hand, to retrieve the enabled companies, do companies = self.env.company_ids 8/ Modify the Company Switcher widget to allow to log into another company WITHOUT writing on the res.users (and thus bringing cache invalidation issues and so on). Also allow to enable several companies and see records from several companies, and independantly of the other browser's tabs. 9/ When focusing on a tab, save the current company configuration on the local storage. That way, when doing 'CTRL+T' or a middle click, the context is propagated to the new tab. 10/ Improve the error message in case of multi company access errors. Now, when the user is in debug mode, display the related names of the records and the name of the user who brings the issue. 11/ Remove the context erasing when writing on a res.users This is probably coming from the migration to new API of the base module. The context was not propagated at this moment, which was a common mistake at that time. When migrating the module, probably by using the 'black box' method, as the context was not propagated, it was erased on the new version. This is now an issue because the context (i.e. the enabled companies) was erased when writing on a res.users, leading to tracebacks. See: https://github.com/odoo/odoo/commit/7eab8e26d3d46c53f4be924d6a34e80a66e74960#diff-4c2e738ee8f64f11806c889ea097b5e7R624 12/ Fix the crash manager on redirect warnings. The issue is the following - Create an invoice on a company without a configured CoA. - Set a partner - On the onchange_partner_id, a redirect warning is raised to propose you to configure a CoA - Click on 'Go to the configuration panel' - A generic warning says something like 'Do you want to discard your changes?' - Click on yes, the page refreshes, but not on the redirect action. Now, set correctly the action on the hash, and reload instead. The breadcrumb is lost for example, but you reach the correct action at least. 13/ Introduce a res.group to enable/disable the multi company per tab feature. 14/ To help the users to know which tab is in which company, add the possibility to have a favicon per company. When creating a company, the classical 'O' icon is colored by default in a random color. 15/ Remove the company switcher on the frontend. This was mainly there to allow a user to swicth to the company linked to the website. This behavior is now transparent to the user. If the website A is activated, then the company set on the context is the company of the website. 16/ Deprecated the _company_default_get method on the res.company model. Remove the method _get_company on the res.users model. 17/ Add 'allowed_company_ids' and 'current_company_id' on the pyeval context. You can now use those variables on domains in the views to access directly to the activated company.ies on the current tab. TaskID: 1960971 closes odoo/odoo#32341 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Diffstat (limited to 'odoo')
-rw-r--r--odoo/addons/base/__manifest__.py1
-rw-r--r--odoo/addons/base/data/ir_module_category_data.xml4
-rw-r--r--odoo/addons/base/data/res_company_data.xml1
-rw-r--r--odoo/addons/base/data/res_groups_demo.xml8
-rw-r--r--odoo/addons/base/models/ir_actions_report.py6
-rw-r--r--odoo/addons/base/models/ir_attachment.py2
-rw-r--r--odoo/addons/base/models/ir_default.py4
-rw-r--r--odoo/addons/base/models/ir_module.py7
-rw-r--r--odoo/addons/base/models/ir_property.py12
-rw-r--r--odoo/addons/base/models/ir_qweb_fields.py2
-rw-r--r--odoo/addons/base/models/ir_rule.py28
-rw-r--r--odoo/addons/base/models/ir_sequence.py4
-rw-r--r--odoo/addons/base/models/ir_ui_view.py4
-rw-r--r--odoo/addons/base/models/res_bank.py2
-rw-r--r--odoo/addons/base/models/res_company.py78
-rw-r--r--odoo/addons/base/models/res_currency.py8
-rw-r--r--odoo/addons/base/models/res_lang.py2
-rw-r--r--odoo/addons/base/models/res_partner.py4
-rw-r--r--odoo/addons/base/models/res_users.py12
-rw-r--r--odoo/addons/base/security/base_groups.xml4
-rw-r--r--odoo/addons/base/security/base_security.xml32
-rw-r--r--odoo/addons/base/tests/test_acl.py2
-rw-r--r--odoo/addons/base/tests/test_expression.py1
-rw-r--r--odoo/addons/base/tests/test_ir_default.py2
-rw-r--r--odoo/addons/base/views/ir_rule_views.xml2
-rw-r--r--odoo/addons/base/views/res_company_views.xml8
-rw-r--r--odoo/addons/base/views/res_users_views.xml2
-rw-r--r--odoo/addons/test_access_rights/tests/test_feedback.py18
-rw-r--r--odoo/addons/test_new_api/tests/test_new_fields.py4
-rw-r--r--odoo/api.py33
-rw-r--r--odoo/fields.py6
-rw-r--r--odoo/tools/misc.py14
32 files changed, 185 insertions, 132 deletions
diff --git a/odoo/addons/base/__manifest__.py b/odoo/addons/base/__manifest__.py
index d47214a5ce9..f32d529a487 100644
--- a/odoo/addons/base/__manifest__.py
+++ b/odoo/addons/base/__manifest__.py
@@ -80,6 +80,7 @@ The kernel of Odoo, needed for all installation.
'data/res_bank_demo.xml',
'data/res_partner_demo.xml',
'data/res_partner_image_demo.xml',
+ 'data/res_groups_demo.xml',
],
'test': [],
'installable': True,
diff --git a/odoo/addons/base/data/ir_module_category_data.xml b/odoo/addons/base/data/ir_module_category_data.xml
index d99fed12077..7b06f7437d6 100644
--- a/odoo/addons/base/data/ir_module_category_data.xml
+++ b/odoo/addons/base/data/ir_module_category_data.xml
@@ -110,5 +110,9 @@
<field name="category_id" ref="module_category_usability"/>
</record>
+ <record id="group_toggle_company" model="res.groups">
+ <field name="category_id" ref="module_category_hidden"/>
+ </record>
+
</data>
</odoo>
diff --git a/odoo/addons/base/data/res_company_data.xml b/odoo/addons/base/data/res_company_data.xml
index 3759ae13143..50c19767c40 100644
--- a/odoo/addons/base/data/res_company_data.xml
+++ b/odoo/addons/base/data/res_company_data.xml
@@ -5,6 +5,7 @@
<field name="name">My Company</field>
<field name="partner_id" ref="main_partner"/>
<field name="currency_id" ref="base.EUR"/>
+ <field name="favicon" model="res.company" eval="obj()._get_default_favicon(original=True)"/>
</record>
<record id="main_partner" model="res.partner">
diff --git a/odoo/addons/base/data/res_groups_demo.xml b/odoo/addons/base/data/res_groups_demo.xml
new file mode 100644
index 00000000000..66d728ff776
--- /dev/null
+++ b/odoo/addons/base/data/res_groups_demo.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<odoo>
+ <data noupdate="1">
+ <record id="group_toggle_company" model="res.groups">
+ <field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
+ </record>
+ </data>
+</odoo>
diff --git a/odoo/addons/base/models/ir_actions_report.py b/odoo/addons/base/models/ir_actions_report.py
index 515387f672d..27fa49da4f9 100644
--- a/odoo/addons/base/models/ir_actions_report.py
+++ b/odoo/addons/base/models/ir_actions_report.py
@@ -202,7 +202,7 @@ class IrActionsReport(models.Model):
@api.model
def get_paperformat(self):
- return self.paperformat_id or self.env.user.company_id.paperformat_id
+ return self.paperformat_id or self.env.company_id.paperformat_id
@api.model
def _build_wkhtmltopdf_args(
@@ -760,7 +760,7 @@ class IrActionsReport(models.Model):
:param report_name: Name of the template to generate an action for
"""
discard_logo_check = self.env.context.get('discard_logo_check')
- if (self.env.user._is_admin()) and ((not self.env.user.company_id.external_report_layout_id) or (not discard_logo_check and not self.env.user.company_id.logo)) and config:
+ if (self.env.user._is_admin()) and ((not self.env.company_id.external_report_layout_id) or (not discard_logo_check and not self.env.company_id.logo)) and config:
template = self.env.ref('base.view_company_report_form_with_print') if self.env.context.get('from_transient_model', False) else self.env.ref('base.view_company_report_form')
return {
'name': _('Choose Your Document Layout'),
@@ -768,7 +768,7 @@ class IrActionsReport(models.Model):
'context': {'default_report_name': self.report_name, 'discard_logo_check': True},
'view_type': 'form',
'view_mode': 'form',
- 'res_id': self.env.user.company_id.id,
+ 'res_id': self.env.company_id.id,
'res_model': 'res.company',
'views': [(template.id, 'form')],
'view_id': template.id,
diff --git a/odoo/addons/base/models/ir_attachment.py b/odoo/addons/base/models/ir_attachment.py
index b0944e5ebd4..03b29aaacec 100644
--- a/odoo/addons/base/models/ir_attachment.py
+++ b/odoo/addons/base/models/ir_attachment.py
@@ -282,7 +282,7 @@ class IrAttachment(models.Model):
res_field = fields.Char('Resource Field', readonly=True)
res_id = fields.Integer('Resource ID', readonly=True, help="The record id this is attached to.")
company_id = fields.Many2one('res.company', string='Company', change_default=True,
- default=lambda self: self.env['res.company']._company_default_get('ir.attachment'))
+ default=lambda self: self.env.company_id)
type = fields.Selection([('url', 'URL'), ('binary', 'File')],
string='Type', required=True, default='binary', change_default=True,
help="You can either upload a file from your computer or copy/paste an internet link to your file.")
diff --git a/odoo/addons/base/models/ir_default.py b/odoo/addons/base/models/ir_default.py
index 38ab488b94e..282e12d211e 100644
--- a/odoo/addons/base/models/ir_default.py
+++ b/odoo/addons/base/models/ir_default.py
@@ -57,7 +57,7 @@ class IrDefault(models.Model):
if user_id is True:
user_id = self.env.uid
if company_id is True:
- company_id = self.env.user.company_id.id
+ company_id = self.env.company_id.id
# check consistency of model_name, field_name, and value
try:
@@ -107,7 +107,7 @@ class IrDefault(models.Model):
if user_id is True:
user_id = self.env.uid
if company_id is True:
- company_id = self.env.user.company_id.id
+ company_id = self.env.company_id.id
field = self.env['ir.model.fields']._get(model_name, field_name)
default = self.search([
diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py
index e2870d74b3c..1afe44bb05c 100644
--- a/odoo/addons/base/models/ir_module.py
+++ b/odoo/addons/base/models/ir_module.py
@@ -439,6 +439,13 @@ class Module(models.Model):
:rtype: dict[str, object]
"""
_logger.info('User #%d triggered module installation', self.env.uid)
+ # We use here the request object (which is thread-local) as a kind of
+ # "global" env because the env is not usable in the following use case.
+ # When installing a Chart of Account, I would like to send the
+ # allowed companies to configure it on the correct company.
+ # Otherwise, the SUPERUSER won't be aware of that and will try to
+ # configure the CoA on his own company, which makes no sense.
+ request.allowed_company_ids = self.env.company_ids.ids
return self._button_immediate_function(type(self).button_install)
@assert_log_admin_access
diff --git a/odoo/addons/base/models/ir_property.py b/odoo/addons/base/models/ir_property.py
index 0690080670f..c605d027b1a 100644
--- a/odoo/addons/base/models/ir_property.py
+++ b/odoo/addons/base/models/ir_property.py
@@ -178,10 +178,8 @@ class Property(models.Model):
return False
# only cache Property.get(res_id=False) as that's
- # sub-optimally, we can only call _company_default_get without a field
- # unless we want to create a more complete helper which does the
- # returning-a-company-id-from-a-model-and-name
- COMPANY_KEY = "self.env.context.get('force_company') or self.env['res.company']._company_default_get(model).id"
+ # sub-optimally.
+ COMPANY_KEY = "self.env.context.get('force_company') or self.env.company_id.id"
@ormcache(COMPANY_KEY, 'name', 'model')
def _get_default_property(self, name, model):
prop = self._get_property(name, model, res_id=False)
@@ -205,7 +203,7 @@ class Property(models.Model):
res = self._cr.fetchone()
if not res:
return None
- company_id = self._context.get('force_company') or self.env['res.company']._company_default_get(model, res[0]).id
+ company_id = self._context.get('force_company') or self.env.company_id.id
return [('fields_id', '=', res[0]), ('company_id', 'in', [company_id, False])]
@api.model
@@ -221,7 +219,7 @@ class Property(models.Model):
field_id = self.env['ir.model.fields']._get(model, name).id
company_id = (
self._context.get('force_company')
- or self.env['res.company']._company_default_get(model, field_id).id
+ or self.env.company_id.id
)
if field.type == 'many2one':
@@ -301,7 +299,7 @@ class Property(models.Model):
# retrieve the properties corresponding to the given record ids
self._cr.execute("SELECT id FROM ir_model_fields WHERE name=%s AND model=%s", (name, model))
field_id = self._cr.fetchone()[0]
- company_id = self.env.context.get('force_company') or self.env['res.company']._company_default_get(model, field_id).id
+ company_id = self.env.context.get('force_company') or self.env.company_id.id
refs = {('%s,%s' % (model, id)): id for id in values}
props = self.search([
('fields_id', '=', field_id),
diff --git a/odoo/addons/base/models/ir_qweb_fields.py b/odoo/addons/base/models/ir_qweb_fields.py
index c71427b7647..8209ebc81ce 100644
--- a/odoo/addons/base/models/ir_qweb_fields.py
+++ b/odoo/addons/base/models/ir_qweb_fields.py
@@ -420,7 +420,7 @@ class MonetaryConverter(models.AbstractModel):
if company_id:
company = self.env['res.company'].browse(company_id)
else:
- company = self.env.user.company_id
+ company = self.env.company_id
value = options['from_currency']._convert(value, display_currency, company, date)
lang = self.user_lang()
diff --git a/odoo/addons/base/models/ir_rule.py b/odoo/addons/base/models/ir_rule.py
index 092641e7f0d..a832795e5a9 100644
--- a/odoo/addons/base/models/ir_rule.py
+++ b/odoo/addons/base/models/ir_rule.py
@@ -43,8 +43,16 @@ class IrRule(models.Model):
@api.model
def _eval_context(self):
"""Returns a dictionary to use as evaluation context for
- ir.rule domains."""
- return {'user': self.env.user, 'time': time}
+ ir.rule domains.
+ Note: company_ids contains the ids of the activated companies
+ by the user with the switch company menu. These companies are
+ filtered and trusted.
+ """
+ return {
+ 'user': self.env.user,
+ 'time': time,
+ 'company_ids': self.env.company_ids.ids,
+ }
@api.depends('groups')
def _compute_global(self):
@@ -64,7 +72,7 @@ class IrRule(models.Model):
def _compute_domain_keys(self):
""" Return the list of context keys to use for caching ``_compute_domain``. """
- return []
+ return ['allowed_company_ids']
def _get_failing(self, for_records, mode='read'):
""" Returns the rules for the mode for the current user which fail on
@@ -196,22 +204,26 @@ class IrRule(models.Model):
'document_kind': description,
'document_model': model,
'operation': operation,
- })
+ })
- # debug mode, provide more info
+ # This extended AccessError is only displayed in debug mode.
+ # Note that by default, public and portal users do not have
+ # the group "base.group_no_one", even if debug mode is enabled,
+ # so it is relatively safe here to include the list of rules and
+ # record names.
rules = self._get_failing(records, mode=operation).sudo()
return AccessError(_("""The requested operation ("%(operation)s" on "%(document_kind)s" (%(document_model)s)) was rejected because of the following rules:
%(rules_list)s
%(multi_company_warning)s
-(records: %(example_records)s, uid: %(user_id)d)""") % {
+(Records: %(example_records)s, User: %(user_id)s)""") % {
'operation': operation,
'document_kind': description,
'document_model': model,
'rules_list': '\n'.join('- %s' % rule.name for rule in rules),
'multi_company_warning': ('\n' + _('Note: this might be a multi-company issue.') + '\n') if any(
'company_id' in r.domain_force for r in rules) else '',
- 'example_records': list(records.ids[:6]),
- 'user_id': self.env.user.id,
+ 'example_records': ' - '.join(['%s (id=%s)' % (rec.display_name, rec.id) for rec in records[:6].sudo()]),
+ 'user_id': '%s (id=%s)' % (self.env.user.name, self.env.user.id),
})
#
diff --git a/odoo/addons/base/models/ir_sequence.py b/odoo/addons/base/models/ir_sequence.py
index 7e97e2fa05c..e08729fe62a 100644
--- a/odoo/addons/base/models/ir_sequence.py
+++ b/odoo/addons/base/models/ir_sequence.py
@@ -136,7 +136,7 @@ class IrSequence(models.Model):
help="Odoo will automatically adds some '0' on the left of the "
"'Next Number' to get the required padding size.")
company_id = fields.Many2one('res.company', string='Company',
- default=lambda s: s.env['res.company']._company_default_get('ir.sequence'))
+ default=lambda s: s.env.company_id)
use_date_range = fields.Boolean(string='Use subsequences per date_range')
date_range_ids = fields.One2many('ir.sequence.date_range', 'sequence_id', string='Subsequences')
@@ -277,7 +277,7 @@ class IrSequence(models.Model):
self.check_access_rights('read')
force_company = self._context.get('force_company')
if not force_company:
- force_company = self.env.user.company_id.id
+ force_company = self.env.company_id.id
seq_ids = self.search([('code', '=', sequence_code), ('company_id', 'in', [force_company, False])], order='company_id')
if not seq_ids:
_logger.debug("No ir.sequence has been found for code '%s'. Please make sure a sequence is set for current company." % sequence_code)
diff --git a/odoo/addons/base/models/ir_ui_view.py b/odoo/addons/base/models/ir_ui_view.py
index ddae4f21642..e60af844439 100644
--- a/odoo/addons/base/models/ir_ui_view.py
+++ b/odoo/addons/base/models/ir_ui_view.py
@@ -1016,6 +1016,8 @@ actual arch.
'context_today',
'active_id',
'active_ids',
+ 'allowed_company_ids',
+ 'current_company_id',
'active_model',
'time',
'datetime',
@@ -1351,7 +1353,7 @@ actual arch.
qcontext = dict(
env=self.env,
user_id=self.env["res.users"].browse(self.env.user.id),
- res_company=self.env.user.company_id.sudo(),
+ res_company=self.env.company_id.sudo(),
keep_query=keep_query,
request=request, # might be unbound if we're not in an httprequest context
debug=request.debug if request else False,
diff --git a/odoo/addons/base/models/res_bank.py b/odoo/addons/base/models/res_bank.py
index d9a0433ecc2..414d2aed444 100644
--- a/odoo/addons/base/models/res_bank.py
+++ b/odoo/addons/base/models/res_bank.py
@@ -86,7 +86,7 @@ class ResPartnerBank(models.Model):
bank_bic = fields.Char(related='bank_id.bic', readonly=False)
sequence = fields.Integer(default=10)
currency_id = fields.Many2one('res.currency', string='Currency')
- company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.user.company_id, ondelete='cascade')
+ company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env.company_id, ondelete='cascade')
qr_code_valid = fields.Boolean(string="Has all required arguments", compute="_validate_qr_code_arguments")
_sql_constraints = [
diff --git a/odoo/addons/base/models/res_company.py b/odoo/addons/base/models/res_company.py
index db64a8265a3..9dfbf9bdced 100644
--- a/odoo/addons/base/models/res_company.py
+++ b/odoo/addons/base/models/res_company.py
@@ -1,11 +1,20 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
import base64
+import io
+import logging
import os
import re
from odoo import api, fields, models, tools, _
from odoo.exceptions import ValidationError, UserError
+from odoo.modules.module import get_resource_path
+
+from random import randrange
+from PIL import Image
+
+_logger = logging.getLogger(__name__)
class Company(models.Model):
@@ -29,6 +38,29 @@ class Company(models.Model):
currency_id = self.env['res.users'].browse(self._uid).company_id.currency_id
return currency_id or self._get_euro()
+ def _get_default_favicon(self, original=False):
+ img_path = get_resource_path('web', 'static/src/img/favicon.ico')
+ with tools.file_open(img_path, 'rb') as f:
+ if original:
+ return base64.b64encode(f.read())
+ # Modify the source image to change the color of the 'O'.
+ # This could seem overkill to modify the pixels 1 by 1, but
+ # Pillow doesn't provide an easy way to do it, and this
+ # is acceptable for a 16x16 image.
+ color = (randrange(32, 224, 24), randrange(32, 224, 24), randrange(32, 224, 24))
+ original = Image.open(f)
+ new_image = Image.new('RGBA', original.size)
+ for y in range(original.size[1]):
+ for x in range(original.size[0]):
+ pixel = original.getpixel((x, y))
+ if pixel[0] == 0 and pixel[1] == 0 and pixel[2] == 0:
+ new_image.putpixel((x, y), (0, 0, 0, 0))
+ else:
+ new_image.putpixel((x, y), (color[0], color[1], color[2], pixel[3]))
+ stream = io.BytesIO()
+ new_image.save(stream, format="ICO")
+ return base64.b64encode(stream.getvalue())
+
name = fields.Char(related='partner_id.name', string='Company Name', required=True, store=True, readonly=False)
sequence = fields.Integer(help='Used to order Companies in the company switcher', default=10)
parent_id = fields.Many2one('res.company', string='Parent Company', index=True)
@@ -57,12 +89,15 @@ class Company(models.Model):
company_registry = fields.Char()
paperformat_id = fields.Many2one('report.paperformat', 'Paper format', default=lambda self: self.env.ref('base.paperformat_euro', raise_if_not_found=False))
external_report_layout_id = fields.Many2one('ir.ui.view', 'Document Template')
+ base_onboarding_company_state = fields.Selection([
+ ('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")], string="State of the onboarding company step", default='not_done')
+ favicon = fields.Binary(string="Company Favicon", help="This field holds the image used to display a favicon for a given company.", default=_get_default_favicon)
+
+
_sql_constraints = [
('name_uniq', 'unique (name)', 'The company name must be unique !')
]
- base_onboarding_company_state = fields.Selection([
- ('not_done', "Not done"), ('just_done', "Just done"), ('done', "Done")], string="State of the onboarding company step", default='not_done')
@api.model_cr
def init(self):
@@ -154,7 +189,7 @@ class Company(models.Model):
# select only the currently visible companies (according to rules,
# which are probably to allow to see the child companies) even if
# she belongs to some other companies.
- companies = self.env.user.company_id + self.env.user.company_ids
+ companies = self.env.user.company_ids
args = (args or []) + [('id', 'in', companies.ids)]
newself = newself.sudo()
return super(Company, newself.with_context(context))._name_search(name=name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid)
@@ -162,36 +197,11 @@ class Company(models.Model):
@api.model
@api.returns('self', lambda value: value.id)
def _company_default_get(self, object=False, field=False):
- """ Returns the default company (usually the user's company).
- The 'object' and 'field' arguments are ignored but left here for
- backward compatibility and potential override.
+ """ Returns the user's company
+ - Deprecated
"""
- return self.env['res.users']._get_company()
-
- @api.model
- @tools.ormcache('self.env.uid', 'company')
- def _get_company_children(self, company=None):
- if not company:
- return []
- return self.search([('parent_id', 'child_of', [company])]).ids
-
- @api.multi
- def _get_partner_hierarchy(self):
- self.ensure_one()
- parent = self.parent_id
- if parent:
- return parent._get_partner_hierarchy()
- else:
- return self._get_partner_descendance([])
-
- @api.multi
- def _get_partner_descendance(self, descendance):
- self.ensure_one()
- descendance.append(self.partner_id.id)
- for child_id in self._get_company_children(self.id):
- if child_id != self.id:
- descendance = self.browse(child_id)._get_partner_descendance(descendance)
- return descendance
+ _logger.warning(_("The method '_company_default_get' on res.company is deprecated and shouldn't be used anymore"))
+ return self.env.company_id
# deprecated, use clear_caches() instead
def cache_restart(self):
@@ -199,6 +209,8 @@ class Company(models.Model):
@api.model
def create(self, vals):
+ if not vals.get('favicon'):
+ vals['favicon'] = self._get_default_favicon()
if not vals.get('name') or vals.get('partner_id'):
self.clear_caches()
return super(Company, self).create(vals)
@@ -266,7 +278,7 @@ class Company(models.Model):
def action_open_base_onboarding_company(self):
""" Onboarding step for company basic information. """
action = self.env.ref('base.action_open_base_onboarding_company').read()[0]
- action['res_id'] = self.env.user.company_id.id
+ action['res_id'] = self.env.company_id.id
return action
def set_onboarding_step_done(self, step_name):
diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py
index 09a6d019f38..a7ecad0a1ca 100644
--- a/odoo/addons/base/models/res_currency.py
+++ b/odoo/addons/base/models/res_currency.py
@@ -62,7 +62,7 @@ class Currency(models.Model):
@api.depends('rate_ids.rate')
def _compute_current_rate(self):
date = self._context.get('date') or fields.Date.today()
- company = self.env['res.company'].browse(self._context.get('company_id')) or self.env['res.users']._get_company()
+ company = self.env['res.company'].browse(self._context.get('company_id')) or self.env.company_id
# the subquery selects the last rate before 'date' for the given currency/company
currency_rates = self._get_rates(company, date)
for currency in self:
@@ -209,14 +209,14 @@ class Currency(models.Model):
def _compute(self, from_currency, to_currency, from_amount, round=True):
_logger.warning('The `_compute` method is deprecated. Use `_convert` instead')
date = self._context.get('date') or fields.Date.today()
- company = self.env['res.company'].browse(self._context.get('company_id')) or self.env['res.users']._get_company()
+ company = self.env['res.company'].browse(self._context.get('company_id')) or self.env.company_id
return from_currency._convert(from_amount, to_currency, company, date)
@api.multi
def compute(self, from_amount, to_currency, round=True):
_logger.warning('The `compute` method is deprecated. Use `_convert` instead')
date = self._context.get('date') or fields.Date.today()
- company = self.env['res.company'].browse(self._context.get('company_id')) or self.env['res.users']._get_company()
+ company = self.env['res.company'].browse(self._context.get('company_id')) or self.env.company_id
return self._convert(from_amount, to_currency, company, date)
def _select_companies_rates(self):
@@ -247,7 +247,7 @@ class CurrencyRate(models.Model):
rate = fields.Float(digits=(12, 6), default=1.0, help='The rate of the currency to the currency of rate 1')
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True)
company_id = fields.Many2one('res.company', string='Company',
- default=lambda self: self.env.user.company_id)
+ default=lambda self: self.env.company_id)
_sql_constraints = [
('unique_name_per_day', 'unique (name,currency_id,company_id)', 'Only one currency rate per day allowed!'),
diff --git a/odoo/addons/base/models/res_lang.py b/odoo/addons/base/models/res_lang.py
index 534c8cb248c..8d5990efbf6 100644
--- a/odoo/addons/base/models/res_lang.py
+++ b/odoo/addons/base/models/res_lang.py
@@ -175,7 +175,7 @@ class Lang(models.Model):
if default_value is None:
IrDefault.set('res.partner', 'lang', lang_code)
# set language of main company, created directly by db bootstrap SQL
- partner = self.env.user.company_id.partner_id
+ partner = self.env.company_id.partner_id
if not partner.lang:
partner.write({'lang': lang_code})
return True
diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py
index fa23086b16a..2bc8144f128 100644
--- a/odoo/addons/base/models/res_partner.py
+++ b/odoo/addons/base/models/res_partner.py
@@ -48,7 +48,7 @@ class FormatAddressMixin(models.AbstractModel):
def _fields_view_get_address(self, arch):
# consider the country of the user, not the country of the partner we want to display
- address_view_id = self.env.user.company_id.country_id.address_view_id
+ address_view_id = self.env.company_id.country_id.address_view_id
if address_view_id and not self._context.get('no_address_format'):
#render the partner address accordingly to address_view_id
doc = etree.fromstring(arch)
@@ -139,7 +139,7 @@ class Partner(models.Model):
return self.env['res.partner.category'].browse(self._context.get('category_id'))
def _default_company(self):
- return self.env['res.company']._company_default_get('res.partner')
+ return self.env.company_id
def _split_street_with_params(self, street_raw, street_format):
return {'street': street_raw}
diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py
index 0cfb40b0ffd..256e89cbb7d 100644
--- a/odoo/addons/base/models/res_users.py
+++ b/odoo/addons/base/models/res_users.py
@@ -240,17 +240,13 @@ class Users(models.Model):
companies_count = fields.Integer(compute='_compute_companies_count', string="Number of Companies", default=_companies_count)
tz_offset = fields.Char(compute='_compute_tz_offset', string='Timezone offset', invisible=True)
- @api.model
- def _get_company(self):
- return self.env.user.company_id
-
# Special behavior for this field: res.company.search() will only return the companies
# available to the current user (should be the user's companies?), when the user_preference
# context is set.
- company_id = fields.Many2one('res.company', string='Company', required=True, default=_get_company,
+ company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.company_id.id,
help='The company this user is currently working for.', context={'user_preference': True})
company_ids = fields.Many2many('res.company', 'res_company_users_rel', 'user_id', 'cid',
- string='Companies', default=_get_company)
+ string='Companies', default=lambda self: self.env.company_id.ids)
# overridden inherited fields to bypass access rights, in case you have
# access to the user but not its corresponding partner
@@ -787,7 +783,7 @@ class Users(models.Model):
@api.model
def get_company_currency_id(self):
- return self.env.user.company_id.currency_id.id
+ return self.env.company_id.currency_id.id
def _crypt_context(self):
""" Passlib CryptContext instance used to encrypt and verify
@@ -982,7 +978,7 @@ class UsersImplied(models.Model):
res = super(UsersImplied, self).write(values)
if values.get('groups_id'):
# add implied groups for all users
- for user in self.with_context({}):
+ for user in self:
if not user.has_group('base.group_user'):
vals = {'groups_id': [(5, 0, 0)] + values['groups_id']}
super(UsersImplied, user).write(vals)
diff --git a/odoo/addons/base/security/base_groups.xml b/odoo/addons/base/security/base_groups.xml
index 3bdf8ee3928..10541a1a4df 100644
--- a/odoo/addons/base/security/base_groups.xml
+++ b/odoo/addons/base/security/base_groups.xml
@@ -41,6 +41,10 @@
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record>
+ <record id="group_toggle_company" model="res.groups">
+ <field name="name">Allow to toggle company visibility in the company switch bar</field>
+ </record>
+
<record model="res.groups" id="group_partner_manager">
<field name="name">Contact Creation</field>
<field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
diff --git a/odoo/addons/base/security/base_security.xml b/odoo/addons/base/security/base_security.xml
index 21401d88b47..88872f49407 100644
--- a/odoo/addons/base/security/base_security.xml
+++ b/odoo/addons/base/security/base_security.xml
@@ -15,12 +15,7 @@
<record model="ir.rule" id="res_partner_rule">
<field name="name">res.partner company</field>
<field name="model_id" ref="base.model_res_partner"/>
- <!--
- Show partners from ancestors and descendants companies
- (or company-less), this is usually a better default for
- multicompany setups.
- -->
- <field name="domain_force">['|','|',('company_id.child_ids','child_of',[user.company_id.id]),('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
+ <field name="domain_force">['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
<field name="active" eval="False"/>
</record>
@@ -57,20 +52,17 @@
<field name="domain_force">[('user_id','=',user.id)]</field>
</record>
- <record id="res_partner_bank_rule" model="ir.rule">
- <field name="name">Partner bank company rule</field>
- <field name="model_id" ref="model_res_partner_bank"/>
- <field name="global" eval="True"/>
- <!--
- Same as res_partner above.
- -->
- <field name="domain_force">['|','|',('company_id.child_ids','child_of',[user.company_id.id]),('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
- </record>
+ <record id="res_partner_bank_rule" model="ir.rule">
+ <field name="name">Partner bank company rule</field>
+ <field name="model_id" ref="model_res_partner_bank"/>
+ <field name="global" eval="True"/>
+ <field name="domain_force">['|', ('company_id', 'in', company_ids), ('company_id', '=', False)]</field>
+ </record>
<record id="res_currency_rate_rule" model="ir.rule">
<field name="name">multi-company currency rate rule</field>
<field name="model_id" ref="model_res_currency_rate"/>
- <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
+ <field name="domain_force">['|',('company_id','=',False),('company_id', 'in', company_ids)]</field>
</record>
<!-- Security restriction for private addresses -->
@@ -147,21 +139,21 @@
<field name="model_id" ref="model_res_company"/>
<field eval="False" name="global"/>
<field name="groups" eval="[(6, 0, [ref('base.group_portal')])]"/>
- <field name="domain_force">[('id','child_of',[user.company_id.id])]</field>
+ <field name="domain_force">[('id','in', company_ids)]</field>
</record>
<record id="res_company_rule_employee" model="ir.rule">
<field name="name">company rule employee</field>
<field name="model_id" ref="model_res_company"/>
<field eval="False" name="global"/>
<field name="groups" eval="[(6, 0, [ref('base.group_user')])]"/>
- <field name="domain_force">[('id','child_of',[user.company_id.id])]</field>
+ <field name="domain_force">[('id','in', company_ids)]</field>
</record>
<record id="res_company_rule_public" model="ir.rule">
<field name="name">company rule public</field>
<field name="model_id" ref="model_res_company"/>
<field eval="False" name="global"/>
<field name="groups" eval="[(6, 0, [ref('base.group_public')])]"/>
- <field name="domain_force">[('id','child_of',[user.company_id.id])]</field>
+ <field name="domain_force">[('id','in', company_ids)]</field>
</record>
<record id="res_company_rule_erp_manager" model="ir.rule">
<field name="name">company rule erp manager</field>
@@ -176,7 +168,7 @@
<field name="name">user rule</field>
<field name="model_id" ref="model_res_users"/>
<field eval="True" name="global"/>
- <field name="domain_force">[('company_ids','child_of',[user.company_id.id])]</field>
+ <field name="domain_force">[('company_ids', 'in', company_ids)]</field>
</record>
</data>
</odoo>
diff --git a/odoo/addons/base/tests/test_acl.py b/odoo/addons/base/tests/test_acl.py
index ee7708f2b67..c16b6753c09 100644
--- a/odoo/addons/base/tests/test_acl.py
+++ b/odoo/addons/base/tests/test_acl.py
@@ -214,7 +214,7 @@ class TestIrRule(TransactionCase):
# modify the global rule on res_company which triggers a recursive check
# of the rules on company
global_rule = self.env.ref('base.res_company_rule_employee')
- global_rule.domain_force = "[('id','child_of',[user.company_id.id])]"
+ global_rule.domain_force = "[('id','in', company_ids)]"
# read as demo user (exercising the global company rule)
partners = partners_demo.search([])
diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py
index b40035dadd6..adae9a6fe01 100644
--- a/odoo/addons/base/tests/test_expression.py
+++ b/odoo/addons/base/tests/test_expression.py
@@ -574,7 +574,6 @@ class TestExpression(TransactionCase):
# To test the 64 characters limit for table aliases in PostgreSQL
self.patch_order('res.users', 'partner_id')
self.patch_order('res.partner', 'commercial_partner_id,company_id,name')
- self.patch_order('res.company', 'parent_id')
self.env['res.users'].search([('name', '=', 'test')])
@mute_logger('odoo.sql_db')
diff --git a/odoo/addons/base/tests/test_ir_default.py b/odoo/addons/base/tests/test_ir_default.py
index be5ce7d7236..d53c0b18914 100644
--- a/odoo/addons/base/tests/test_ir_default.py
+++ b/odoo/addons/base/tests/test_ir_default.py
@@ -9,7 +9,7 @@ class TestIrDefault(TransactionCase):
def test_defaults(self):
""" check the mechanism of user-defined defaults """
- companyA = self.env.user.company_id
+ companyA = self.env.company_id
companyB = companyA.create({'name': 'CompanyB'})
user1 = self.env.user
user2 = user1.create({'name': 'u2', 'login': 'u2'})
diff --git a/odoo/addons/base/views/ir_rule_views.xml b/odoo/addons/base/views/ir_rule_views.xml
index 8fa3ec0c635..e598ab524e4 100644
--- a/odoo/addons/base/views/ir_rule_views.xml
+++ b/odoo/addons/base/views/ir_rule_views.xml
@@ -100,7 +100,7 @@
<field name="name">Property multi-company</field>
<field name="model_id" ref="model_ir_property"/>
<field eval="True" name="global"/>
- <field name="domain_force">['|',('company_id','child_of',[user.company_id.id]),('company_id','=',False)]</field>
+ <field name="domain_force">['|',('company_id', 'in', company_ids),('company_id','=',False)]</field>
</record>
diff --git a/odoo/addons/base/views/res_company_views.xml b/odoo/addons/base/views/res_company_views.xml
index 8a0773e98da..c0138c94529 100644
--- a/odoo/addons/base/views/res_company_views.xml
+++ b/odoo/addons/base/views/res_company_views.xml
@@ -38,6 +38,7 @@
<field name="currency_id" options="{'no_create': True, 'no_open': True}" id="company_currency" context="{'active_test': False}"/>
<field name="parent_id" groups="base.group_multi_company"/>
<field name="sequence" invisible="1"/>
+ <field name="favicon" widget="image" class="float-left oe_avatar"/>
</group>
<group name="social_media"/>
</group>
@@ -50,7 +51,6 @@
<record id="view_company_tree" model="ir.ui.view">
<field name="name">res.company.tree</field>
<field name="model">res.company</field>
- <field name="field_parent">child_ids</field>
<field name="arch" type="xml">
<tree string="Companies">
<field name="sequence" widget="handle"/>
@@ -137,7 +137,7 @@
<field name="name"/>
</h2>
</xpath>
- <field name="parent_id" groups="base.group_multi_company" position="replace" invisible="1"/>
+ <field name="parent_id" position="replace"/>
<xpath expr="//sheet" position="inside">
<hr />
<div class="row mt16">
@@ -147,7 +147,7 @@
}"/>
</div>
<footer>
- <button string="Save" type="object" class="oe_highlight" name="write"/>
+ <button string="Save" type="object" class="oe_highlight" special="save"/>
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</xpath>
@@ -161,7 +161,7 @@
<field name="inherit_id" ref="base.view_company_report_form" />
<field name="priority">1024</field>
<field name="arch" type="xml">
- <xpath expr="//button[@name='write']" position="before" >
+ <xpath expr="//button[@special='save']" position="before" >
<button string="Save and Print" type="object" class="oe_highlight" name="write_company_and_print_report"/>
</xpath>
</field>
diff --git a/odoo/addons/base/views/res_users_views.xml b/odoo/addons/base/views/res_users_views.xml
index 62ae356a73c..d77cee157ba 100644
--- a/odoo/addons/base/views/res_users_views.xml
+++ b/odoo/addons/base/views/res_users_views.xml
@@ -209,7 +209,7 @@
<page name="access_rights" string="Access Rights">
<group string="Multi Companies" attrs="{'invisible': [('companies_count', '&lt;=', 1)]}">
<field string="Allowed Companies" name="company_ids" widget="many2many_tags" options="{'no_create': True}"/>
- <field string="Current Company" name="company_id" context="{'user_preference': 0}"/>
+ <field string="Default Company" name="company_id" context="{'user_preference': 0}"/>
<field string="Companies count" name="companies_count" invisible="1"/>
</group>
<field name="groups_id"/>
diff --git a/odoo/addons/test_access_rights/tests/test_feedback.py b/odoo/addons/test_access_rights/tests/test_feedback.py
index 660dd068199..56207f9ecbc 100644
--- a/odoo/addons/test_access_rights/tests/test_feedback.py
+++ b/odoo/addons/test_access_rights/tests/test_feedback.py
@@ -115,7 +115,7 @@ class TestIRRuleFeedback(Feedback):
"""The requested operation ("write" on "Object For Test Access Right" (test_access_right.some_obj)) was rejected because of the following rules:
- rule 0
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
@@ -140,7 +140,7 @@ class TestIRRuleFeedback(Feedback):
- rule 0
- rule 1
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
def test_globals_all(self):
@@ -156,7 +156,7 @@ class TestIRRuleFeedback(Feedback):
- rule 0
- rule 1
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
def test_globals_any(self):
@@ -174,7 +174,7 @@ class TestIRRuleFeedback(Feedback):
"""The requested operation ("write" on "Object For Test Access Right" (test_access_right.some_obj)) was rejected because of the following rules:
- rule 0
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
def test_combination(self):
@@ -193,7 +193,7 @@ class TestIRRuleFeedback(Feedback):
- rule 2
- rule 3
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
def test_warn_company(self):
@@ -202,7 +202,7 @@ class TestIRRuleFeedback(Feedback):
"""
self.env.ref('base.group_no_one').write(
{'users': [(4, self.user.id)]})
- self._make_rule('rule 0', "[('company_id', 'child_of', user.company_id.id)]")
+ self._make_rule('rule 0', "[('company_id', '=', user.company_id.id)]")
self._make_rule('rule 1', '[("val", "=", 0)]', global_=True)
with self.assertRaises(AccessError) as ctx:
self.record.write({'val': 1})
@@ -213,7 +213,7 @@ class TestIRRuleFeedback(Feedback):
Note: this might be a multi-company issue.
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
def test_read(self):
@@ -222,7 +222,7 @@ Note: this might be a multi-company issue.
"""
self.env.ref('base.group_no_one').write(
{'users': [(4, self.user.id)]})
- self._make_rule('rule 0', "[('company_id', 'child_of', user.company_id.id)]", attr='read')
+ self._make_rule('rule 0', "[('company_id', '=', user.company_id.id)]", attr='read')
self._make_rule('rule 1', '[("val", "=", 1)]', global_=True, attr='read')
with self.assertRaises(AccessError) as ctx:
_ = self.record.val
@@ -234,7 +234,7 @@ Note: this might be a multi-company issue.
Note: this might be a multi-company issue.
-(records: [%s], uid: %d)""" % (self.record.id, self.user.id)
+(Records: %s (id=%s), User: %s (id=%s))""" % (self.record.display_name, self.record.id, self.user.name, self.user.id)
)
p = self.env['test_access_right.parent'].create({'obj_id': self.record.id})
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 d9253aaba69..578ad04d6f1 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -771,8 +771,8 @@ class TestFields(common.TransactionCase):
""" test company-dependent fields. """
# consider three companies
company0 = self.env.ref('base.main_company')
- company1 = self.env['res.company'].create({'name': 'A', 'parent_id': company0.id})
- company2 = self.env['res.company'].create({'name': 'B', 'parent_id': company1.id})
+ company1 = self.env['res.company'].create({'name': 'A'})
+ company2 = self.env['res.company'].create({'name': 'B'})
# create one user per company
user0 = self.env['res.users'].create({'name': 'Foo', 'login': 'foo',
diff --git a/odoo/api.py b/odoo/api.py
index 4a177e668dc..94b02961063 100644
--- a/odoo/api.py
+++ b/odoo/api.py
@@ -855,6 +855,39 @@ class Environment(Mapping):
return self(user=SUPERUSER_ID)['res.users'].browse(self.uid)
@property
+ def company_id(self):
+ """ return the company in which the user is logged in (as an instance) """
+ try:
+ company_id = int(self.context.get('allowed_company_ids')[0])
+ if company_id in self.user.company_ids.ids:
+ return self['res.company'].browse(company_id)
+ return self.user.company_id
+ except Exception:
+ return self.user.company_id
+
+ @property
+ def company_ids(self):
+ """ return a recordset of the enabled companies by the user """
+ try: # In case the user tries to bidouille the url (eg: cids=1,foo,bar)
+ allowed_company_ids = self.context.get('allowed_company_ids')
+ # Prevent the user to enable companies for which he doesn't have any access
+ users_company_ids = self.user.company_ids.ids
+ allowed_company_ids = [company_id for company_id in allowed_company_ids if company_id in users_company_ids]
+ except Exception:
+ # By setting the default companies to all user companies instead of the main one
+ # we save a lot of potential trouble in all "out of context" calls, such as
+ # /mail/redirect or /web/image, etc. And it is not unsafe because the user does
+ # have access to these other companies. The risk of exposing foreign records
+ # (wrt to the context) is low because all normal RPCs will have a proper
+ # allowed_company_ids.
+ # Examples:
+ # - when printing a report for several records from several companies
+ # - when accessing to a record from the notification email template
+ # - when loading an binary image on a template
+ allowed_company_ids = self.user.company_ids.ids
+ return self['res.company'].browse(allowed_company_ids)
+
+ @property
def lang(self):
""" return the current language code """
return self.context.get('lang')
diff --git a/odoo/fields.py b/odoo/fields.py
index 2f8e64459a1..bc5f5e7d44f 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -657,8 +657,7 @@ class Field(MetaField('DummyField', (object,), {})):
# 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)
+ company = records.env.company_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)
@@ -669,8 +668,7 @@ class Field(MetaField('DummyField', (object,), {})):
# 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)
+ company = records.env.company_id
context = dict(context, force_company=company.id)
Property = records.env(user=SUPERUSER_ID, context=context)['ir.property']
values = {
diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py
index 671fd09e148..3d34c608fd4 100644
--- a/odoo/tools/misc.py
+++ b/odoo/tools/misc.py
@@ -422,20 +422,6 @@ def scan_languages():
return sorted(result or [('en_US', u'English')], key=itemgetter(1))
-def get_user_companies(cr, user):
- def _get_company_children(cr, ids):
- if not ids:
- return []
- cr.execute('SELECT id FROM res_company WHERE parent_id IN %s', (tuple(ids),))
- res = [x[0] for x in cr.fetchall()]
- res.extend(_get_company_children(cr, res))
- return res
- cr.execute('SELECT company_id FROM res_users WHERE id=%s', (user,))
- user_comp = cr.fetchone()[0]
- if not user_comp:
- return []
- return [user_comp] + _get_company_children(cr, [user_comp])
-
def mod10r(number):
"""
Input number : account or invoice number