diff --git a/addons/hr_timesheet/models/ir_http.py b/addons/hr_timesheet/models/ir_http.py index 51e929b62af..cc687a513c4 100644 --- a/addons/hr_timesheet/models/ir_http.py +++ b/addons/hr_timesheet/models/ir_http.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models +from odoo import api, models +from odoo.http import request class Http(models.AbstractModel): @@ -9,13 +10,35 @@ class Http(models.AbstractModel): def session_info(self): """ The widget 'timesheet_uom' needs to know which UoM conversion factor and which javascript - widget to apply, depending on th ecurrent company. + widget to apply, depending on the current company. """ result = super(Http, self).session_info() if self.env.user.has_group('base.group_user'): - company = self.env.company - encoding_uom = company.timesheet_encode_uom_id + company_ids = request.env.user.company_ids - result['timesheet_uom'] = encoding_uom.read(['name', 'rounding', 'timesheet_widget'])[0] - result['timesheet_uom_factor'] = company.project_time_mode_id._compute_quantity(1.0, encoding_uom, round=False) # convert encoding uom into stored uom to get conversion factor + for company in company_ids: + result["user_companies"]["allowed_companies"][company.id].update({ + "timesheet_uom_id": company.timesheet_encode_uom_id.id, + "timesheet_uom_factor": company.project_time_mode_id._compute_quantity( + 1.0, + company.timesheet_encode_uom_id, + round=False + ), + }) + result["uom_ids"] = self.get_timesheet_uoms() return result + + @api.model + def get_timesheet_uoms(self): + company_ids = request.env.user.company_ids + uom_ids = company_ids.mapped('timesheet_encode_uom_id') | \ + company_ids.mapped('project_time_mode_id') + return { + uom.id: + { + 'id': uom.id, + 'name': uom.name, + 'rounding': uom.rounding, + 'timesheet_widget': uom.timesheet_widget, + } for uom in uom_ids + } diff --git a/addons/hr_timesheet/static/src/js/timesheet_graph.js b/addons/hr_timesheet/static/src/js/timesheet_graph.js index 490812de103..ca09fefb9dc 100644 --- a/addons/hr_timesheet/static/src/js/timesheet_graph.js +++ b/addons/hr_timesheet/static/src/js/timesheet_graph.js @@ -12,10 +12,14 @@ odoo.define('hr_timesheet.GraphView', function (require) { _processData: function (originIndex, rawData) { this._super.apply(this, arguments); const session = this.getSession(); - if (this.chart.measure === 'unit_amount' && session.timesheet_uom_factor !== 1) { + const currentCompanyId = session.user_context.allowed_company_ids[0]; + const currentCompany = session.user_companies.allowed_companies[currentCompanyId]; + const currentCompanyTimesheetUOMFactor = currentCompany.timesheet_uom_factor || 1; + + if (this.chart.measure === 'unit_amount' && currentCompanyTimesheetUOMFactor !== 1) { // recalculate the Duration values according to the timesheet_uom_factor this.chart.dataPoints.forEach(function (dataPt) { - dataPt.value *= session.timesheet_uom_factor; + dataPt.value *= currentCompanyTimesheetUOMFactor; }); } }, diff --git a/addons/hr_timesheet/static/src/js/timesheet_uom.js b/addons/hr_timesheet/static/src/js/timesheet_uom.js index 982caa462ab..6b68e3741f7 100644 --- a/addons/hr_timesheet/static/src/js/timesheet_uom.js +++ b/addons/hr_timesheet/static/src/js/timesheet_uom.js @@ -11,16 +11,26 @@ const fieldRegistry = require('web.field_registry'); require('web._field_registry'); const session = require('web.session'); +const AbstractWebClient = require('web.AbstractWebClient'); + +const TimesheetUOMMultiCompanyMixin = { + init: function(parent, name, record, options) { + this._super(parent, name, record, options); + const currentCompanyId = session.user_context.allowed_company_ids[0]; + const currentCompany = session.user_companies.allowed_companies[currentCompanyId]; + this.currentCompanyTimesheetUOMFactor = currentCompany.timesheet_uom_factor || 1; + } +}; /** * Extend the float factor widget to set default value for timesheet * use case. The 'factor' is forced to be the UoM timesheet * conversion from the session info. **/ -const FieldTimesheetFactor = basicFields.FieldFloatFactor.extend({ +const FieldTimesheetFactor = basicFields.FieldFloatFactor.extend(TimesheetUOMMultiCompanyMixin).extend({ formatType: 'float_factor', /** - * Override init to tweak options depending on the session_info + * Override init to tweak options depending on the session info * * @constructor * @override @@ -29,10 +39,8 @@ const FieldTimesheetFactor = basicFields.FieldFloatFactor.extend({ this._super(parent, name, record, options); // force factor in format and parse options - if (session.timesheet_uom_factor) { - this.nodeOptions.factor = session.timesheet_uom_factor; - this.parseOptions.factor = session.timesheet_uom_factor; - } + this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor; + this.parseOptions.factor = this.currentCompanyTimesheetUOMFactor; }, }); @@ -43,10 +51,10 @@ const FieldTimesheetFactor = basicFields.FieldFloatFactor.extend({ * native widget, and the 'factor' is forced to be the UoM timesheet * conversion. **/ -const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend({ +const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend(TimesheetUOMMultiCompanyMixin).extend({ formatType: 'float_factor', /** - * Override init to tweak options depending on the session_info + * Override init to tweak options depending on the session info * * @constructor * @override @@ -66,7 +74,7 @@ const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend({ if (!hasRange) { this.nodeOptions.range = [0.00, 1.00, 0.50]; } - this.nodeOptions.factor = session.timesheet_uom_factor; + this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor; }, }); @@ -74,91 +82,92 @@ const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend({ /** * Extend float time widget */ -const FieldTimesheetTime = basicFields.FieldFloatTime.extend({ +const FieldTimesheetTime = basicFields.FieldFloatTime.extend(TimesheetUOMMultiCompanyMixin).extend({ init: function () { this._super.apply(this, arguments); - - if (session.timesheet_uom_factor) { - this.nodeOptions.factor = session.timesheet_uom_factor; - this.parseOptions.factor = session.timesheet_uom_factor; - } + this.nodeOptions.factor = this.currentCompanyTimesheetUOMFactor; + this.parseOptions.factor = this.currentCompanyTimesheetUOMFactor; } }); +AbstractWebClient.include({ + init: function () { + this._super(...arguments); + /** + * Binding depending on Company Preference + * + * determine wich widget will be the timesheet one. + * Simply match the 'timesheet_uom' widget key with the correct + * implementation (float_time, float_toggle, ...). The default + * value will be 'float_factor'. + **/ + const widgetName = this.currentCompanyTimesheetUOM && + this.currentCompanyTimesheetUOM.timesheet_widget || + 'float_factor'; -/** - * Binding depending on Company Preference - * - * determine wich widget will be the timesheet one. - * Simply match the 'timesheet_uom' widget key with the correct - * implementation (float_time, float_toggle, ...). The default - * value will be 'float_factor'. -**/ -const widgetName = 'timesheet_uom' in session ? - session.timesheet_uom.timesheet_widget : 'float_factor'; + let FieldTimesheetUom = null; -let FieldTimesheetUom = null; + if (widgetName === 'float_toggle') { + FieldTimesheetUom = FieldTimesheetToggle; + } else if (widgetName === 'float_time') { + FieldTimesheetUom = FieldTimesheetTime; + } else { + FieldTimesheetUom = ( + fieldRegistry.get(widgetName) && + fieldRegistry.get(widgetName).extend({ }) + ) || FieldTimesheetFactor; + } + fieldRegistry.add('timesheet_uom', FieldTimesheetUom); -if (widgetName === 'float_toggle') { - FieldTimesheetUom = FieldTimesheetToggle; -} else if (widgetName === 'float_time') { - FieldTimesheetUom = FieldTimesheetTime; -} else { - FieldTimesheetUom = ( - fieldRegistry.get(widgetName) && - fieldRegistry.get(widgetName).extend({}) - ) || FieldTimesheetFactor; -} -fieldRegistry.add('timesheet_uom', FieldTimesheetUom); - -// widget timesheet_uom_no_toggle is the same as timesheet_uom but without toggle. -// We can modify easly huge amount of days. -let FieldTimesheetUomWithoutToggle = null; -if (widgetName === 'float_toggle') { - FieldTimesheetUomWithoutToggle = FieldTimesheetFactor; -} else { - FieldTimesheetUomWithoutToggle = FieldTimesheetTime; -} -fieldRegistry.add('timesheet_uom_no_toggle', FieldTimesheetUomWithoutToggle); + // widget timesheet_uom_no_toggle is the same as timesheet_uom but without toggle. + // We can modify easly huge amount of days. + let FieldTimesheetUomWithoutToggle = null; + if (widgetName === 'float_toggle') { + FieldTimesheetUomWithoutToggle = FieldTimesheetFactor; + } else { + FieldTimesheetUomWithoutToggle = FieldTimesheetTime; + } + fieldRegistry.add('timesheet_uom_no_toggle', FieldTimesheetUomWithoutToggle); -// bind the formatter and parser method, and tweak the options -const _tweak_options = function(options) { - if (!_.contains(options, 'factor')) { - options.factor = session.timesheet_uom_factor; - } - return options; -}; + // bind the formatter and parser method, and tweak the options + const _tweak_options = (options) => { + if (!_.contains(options, 'factor')) { + options.factor = this.currentCompanyTimesheetUOMFactor; + } + return options; + }; -fieldUtils.format.timesheet_uom = function(value, field, options) { - options = _tweak_options(options || {}); - const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType]; - return formatter(value, field, options); -}; + fieldUtils.format.timesheet_uom = function(value, field, options) { + options = _tweak_options(options || { }); + const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType]; + return formatter(value, field, options); + }; -fieldUtils.parse.timesheet_uom = function(value, field, options) { - options = _tweak_options(options || {}); - const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType]; - return parser(value, field, options); -}; + fieldUtils.parse.timesheet_uom = function(value, field, options) { + options = _tweak_options(options || { }); + const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType]; + return parser(value, field, options); + }; -fieldUtils.format.timesheet_uom_no_toggle = function(value, field, options) { - options = _tweak_options(options || {}); - const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType]; - return formatter(value, field, options); -}; + fieldUtils.format.timesheet_uom_no_toggle = function(value, field, options) { + options = _tweak_options(options || { }); + const formatter = fieldUtils.format[FieldTimesheetUom.prototype.formatType]; + return formatter(value, field, options); + }; -fieldUtils.parse.timesheet_uom_no_toggle = function(value, field, options) { - options = _tweak_options(options || {}); - const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType]; - return parser(value, field, options); -}; + fieldUtils.parse.timesheet_uom_no_toggle = function(value, field, options) { + options = _tweak_options(options || { }); + const parser = fieldUtils.parse[FieldTimesheetUom.prototype.formatType]; + return parser(value, field, options); + }; + }, +}); return { - FieldTimesheetUom, FieldTimesheetFactor, FieldTimesheetTime, - FieldTimesheetToggle + FieldTimesheetToggle, }; }); diff --git a/addons/hr_timesheet/static/tests/timesheet_uom_tests.js b/addons/hr_timesheet/static/tests/timesheet_uom_tests.js new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/partner_autocomplete/static/src/js/web_company_autocomplete.js b/addons/partner_autocomplete/static/src/js/web_company_autocomplete.js index dbd413b1de9..53c5029adfe 100644 --- a/addons/partner_autocomplete/static/src/js/web_company_autocomplete.js +++ b/addons/partner_autocomplete/static/src/js/web_company_autocomplete.js @@ -8,7 +8,7 @@ return AbstractWebClient.include({ start: function () { if (session.iap_company_enrich) { - const current_company_id = session.user_companies.current_company[0]; + const current_company_id = session.user_companies.current_company; this._rpc({ model: 'res.company', method: 'iap_enrich_auto', diff --git a/addons/web/models/ir_http.py b/addons/web/models/ir_http.py index 748e469ff28..fd83e877969 100644 --- a/addons/web/models/ir_http.py +++ b/addons/web/models/ir_http.py @@ -60,7 +60,15 @@ class Http(models.AbstractModel): } session_info.update({ # current_company should be default_company - "user_companies": {'current_company': (user.company_id.id, user.company_id.name), 'allowed_companies': [(comp.id, comp.name) for comp in user.company_ids]}, + "user_companies": { + 'current_company': user.company_id.id, + 'allowed_companies': { + comp.id: { + 'id': comp.id, + 'name': comp.name, + } for comp in user.company_ids + }, + }, "currencies": self.get_currencies(), "show_effect": True, "display_switch_company_menu": user.has_group('base.group_multi_company') and len(user.company_ids) > 1, diff --git a/addons/web/static/src/js/chrome/abstract_web_client.js b/addons/web/static/src/js/chrome/abstract_web_client.js index 032b8706bc5..d7f3af00357 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -104,6 +104,19 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { this.set('title_part', {"zopenerp": "Odoo"}); this.env = env; this.env.bus.on('set_title_part', this, this._onSetTitlePart); + // Only manage the multi company state if a session exists (in order not to interfere during js tests) + if (session.uid && Number.isInteger(session.uid) && session.uid > 0) { + this._manageMultiCompanyState(); + } + if (session.user_context && + session.user_context.allowed_company_ids && + session.user_context.allowed_company_ids.length) { + this.currentCompanyId = session.user_context.allowed_company_ids[0]; + const currentCompany = session.user_companies.allowed_companies[this.currentCompanyId]; + const currentCompanyTimesheetUOMId = currentCompany.timesheet_uom_id || false; + this.currentCompanyTimesheetUOMFactor = currentCompany.timesheet_uom_factor || 1; + this.currentCompanyTimesheetUOM = currentCompanyTimesheetUOMId && session.uom_ids[currentCompanyTimesheetUOMId] || false; + } }, /** * @override @@ -120,28 +133,8 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { this.on("change:title_part", this, this._title_changed); this._title_changed(); - var state = $.bbq.getState(); - // If not set on the url, retrieve cids from the local storage - // of from the default company on the user - var current_company_id = session.user_companies.current_company[0] - if (!state.cids) { - state.cids = utils.get_cookie('cids') !== null ? utils.get_cookie('cids') : String(current_company_id); - } - // If a key appears several times in the hash, it is available in the - // bbq state as an array containing all occurrences of that key - const cids = Array.isArray(state.cids) ? state.cids[0] : state.cids; - let stateCompanyIDS = cids.split(',').map(cid => parseInt(cid, 10)); - var userCompanyIDS = _.map(session.user_companies.allowed_companies, function(company) {return company[0]}); - // Check that the user has access to all the companies - if (!_.isEmpty(_.difference(stateCompanyIDS, userCompanyIDS))) { - state.cids = String(current_company_id); - stateCompanyIDS = [current_company_id] - } - // Update the user context with this configuration - session.user_context.allowed_company_ids = stateCompanyIDS; - $.bbq.pushState(state); // Update favicon - $("link[type='image/x-icon']").attr('href', '/web/image/res.company/' + String(stateCompanyIDS[0]) + '/favicon/') + $("link[type='image/x-icon']").attr('href', '/web/image/res.company/' + String(this.currentCompanyId) + '/favicon/'); return session.is_bound .then(function () { @@ -237,6 +230,28 @@ var AbstractWebClient = Widget.extend(KeyboardNavigationMixin, { }); this.$el.children().remove(); }, + _manageMultiCompanyState: function () { + let state = $.bbq.getState(); + // If not set on the url, retrieve cids from the local storage + // or from the default company on the user + const current_company_id = session.user_companies.current_company; + if (!state.cids) { + state.cids = utils.get_cookie('cids') !== null ? utils.get_cookie('cids') : String(current_company_id); + } + // If a key appears several times in the hash, it is available in the + // bbq state as an array containing all occurrences of that key + const cids = Array.isArray(state.cids) ? state.cids[0] : state.cids; + let stateCompanyIDS = cids.split(',').map(cid => parseInt(cid, 10)); + const userCompanyIDS = Object.keys(session.user_companies.allowed_companies).map(id => parseInt(id, 10)); + // Check that the user has access to all the companies + if (!_.isEmpty(_.difference(stateCompanyIDS, userCompanyIDS))) { + state.cids = String(current_company_id); + stateCompanyIDS = [current_company_id]; + } + // Update the user context with this configuration + session.user_context.allowed_company_ids = stateCompanyIDS; + $.bbq.pushState(state); + }, // -------------------------------------------------------------- // Window title handling // -------------------------------------------------------------- diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index cfeb1e8c305..2524ff2ab02 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -3710,10 +3710,10 @@ var BasicModel = AbstractModel.extend({ // in general, the actual "company_id" field of the form should be used for m2o domains, not this fallback let current_company_id; if (session.user_context.allowed_company_ids) { - current_company_id = session.user_context.allowed_company_ids[0]; + current_company_id = Object.keys(session.user_context.allowed_company_ids)[0]; } else { current_company_id = session.user_companies ? - session.user_companies.current_company[0] : + session.user_companies.current_company : false; } return Object.assign( diff --git a/addons/web/static/src/js/widgets/switch_company_menu.js b/addons/web/static/src/js/widgets/switch_company_menu.js index 06d33862d57..d8eab231c63 100644 --- a/addons/web/static/src/js/widgets/switch_company_menu.js +++ b/addons/web/static/src/js/widgets/switch_company_menu.js @@ -38,15 +38,12 @@ var SwitchCompanyMenu = Widget.extend({ * @override */ willStart: function () { - var self = this; this.allowed_company_ids = String(session.user_context.allowed_company_ids) .split(',') .map(function (id) {return parseInt(id);}); this.user_companies = session.user_companies.allowed_companies; this.current_company = this.allowed_company_ids[0]; - this.current_company_name = _.find(session.user_companies.allowed_companies, function (company) { - return company[0] === self.current_company; - })[1]; + this.current_company_name = session.user_companies.allowed_companies[this.current_company]['name']; return this._super.apply(this, arguments); }, diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 781eed08395..8dcde9c934e 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1734,11 +1734,11 @@