diff options
| author | Sébastien Theys <seb@odoo.com> | 2020-08-28 12:53:38 +0000 |
|---|---|---|
| committer | Sébastien Theys <seb@odoo.com> | 2020-08-28 16:15:15 +0000 |
| commit | 17611f4adc5e950b0a75a2a6615b1da3c956387f (patch) | |
| tree | ede6cbc0bc67c1dceed3b5ca6c1bb3e08389675c /addons/hr/static/src | |
| parent | 5ba64925fd846e9a9a4884913e3d1004d05ecdf6 (diff) | |
[FIX] mail, *: fix opens and redirects, adapt messaging menu close
task-2244279
task-2262862
task-2274993
task-2277537
task-2282238
closes odoo/odoo#56745
X-original-commit: 109f202
Related: odoo/enterprise#12761
Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/hr/static/src')
| -rw-r--r-- | addons/hr/static/src/bugfix/bugfix.js | 10 | ||||
| -rw-r--r-- | addons/hr/static/src/bugfix/bugfix.scss | 6 | ||||
| -rw-r--r-- | addons/hr/static/src/bugfix/bugfix.xml | 11 | ||||
| -rw-r--r-- | addons/hr/static/src/bugfix/bugfix_tests.js | 18 | ||||
| -rw-r--r-- | addons/hr/static/src/js/chat.js | 43 | ||||
| -rw-r--r-- | addons/hr/static/src/js/many2one_avatar_employee.js | 36 | ||||
| -rw-r--r-- | addons/hr/static/src/models/employee/employee.js | 204 | ||||
| -rw-r--r-- | addons/hr/static/src/models/messaging/messaging.js | 36 | ||||
| -rw-r--r-- | addons/hr/static/src/models/partner/partner.js | 63 | ||||
| -rw-r--r-- | addons/hr/static/src/models/user/user.js | 18 |
10 files changed, 378 insertions, 67 deletions
diff --git a/addons/hr/static/src/bugfix/bugfix.js b/addons/hr/static/src/bugfix/bugfix.js new file mode 100644 index 00000000000..0351046fab3 --- /dev/null +++ b/addons/hr/static/src/bugfix/bugfix.js @@ -0,0 +1,10 @@ +/** + * This file allows introducing new JS modules without contaminating other files. + * This is useful when bug fixing requires adding such JS modules in stable + * versions of Odoo. Any module that is defined in this file should be isolated + * in its own file in master. + */ +odoo.define('hr/static/src/bugfix/bugfix.js', function (require) { +'use strict'; + +}); diff --git a/addons/hr/static/src/bugfix/bugfix.scss b/addons/hr/static/src/bugfix/bugfix.scss new file mode 100644 index 00000000000..c4272e527b4 --- /dev/null +++ b/addons/hr/static/src/bugfix/bugfix.scss @@ -0,0 +1,6 @@ +/** +* This file allows introducing new styles without contaminating other files. +* This is useful when bug fixing requires adding new components for instance in +* stable versions of Odoo. Any style that is defined in this file should be isolated +* in its own file in master. +*/ diff --git a/addons/hr/static/src/bugfix/bugfix.xml b/addons/hr/static/src/bugfix/bugfix.xml new file mode 100644 index 00000000000..c17906f75bb --- /dev/null +++ b/addons/hr/static/src/bugfix/bugfix.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="UTF-8"?> +<templates xml:space="preserve"> + +<!-- + This file allows introducing new static templates without contaminating other files. + This is useful when bug fixing requires adding new components for instance in stable + versions of Odoo. Any template that is defined in this file should be isolated + in its own file in master. +--> + +</templates> diff --git a/addons/hr/static/src/bugfix/bugfix_tests.js b/addons/hr/static/src/bugfix/bugfix_tests.js new file mode 100644 index 00000000000..8b23d37282d --- /dev/null +++ b/addons/hr/static/src/bugfix/bugfix_tests.js @@ -0,0 +1,18 @@ +odoo.define('hr/static/src/bugfix/bugfix_tests.js', function (require) { +'use strict'; + +/** + * This file allows introducing new QUnit test modules without contaminating + * other test files. This is useful when bug fixing requires adding new + * components for instance in stable versions of Odoo. Any test that is defined + * in this file should be isolated in its own file in master. + */ +QUnit.module('hr', {}, function () { +QUnit.module('bugfix', {}, function () { +QUnit.module('bugfix_tests.js', { + +}); +}); +}); + +}); diff --git a/addons/hr/static/src/js/chat.js b/addons/hr/static/src/js/chat.js index b9985e3929f..0489ccceac0 100644 --- a/addons/hr/static/src/js/chat.js +++ b/addons/hr/static/src/js/chat.js @@ -22,11 +22,7 @@ odoo.define('hr.employee_chat', function (require) { var self = this; return this._super.apply(this, arguments).then(function () { var $chat_button = self.$el.find('.o_employee_chat_btn'); - if (self.state.context.uid === self.state.data.user_id.res_id) { // Hide the button for yourself - $chat_button.hide(); - } else { - $chat_button.off('click').on('click', self._onOpenChat.bind(self)); - } + $chat_button.off('click').on('click', self._onOpenChat.bind(self)); }); }, @@ -38,9 +34,8 @@ odoo.define('hr.employee_chat', function (require) { _onOpenChat: function (ev) { ev.preventDefault(); ev.stopImmediatePropagation(); - this.trigger_up('open_chat', { - partner_id: this.state.data.user_partner_id.res_id - }); + const env = Component.env; + env.messaging.openChat({ employeeId: this.state.data.id }); return true; }, }; @@ -48,23 +43,9 @@ odoo.define('hr.employee_chat', function (require) { // USAGE OF CHAT MIXIN IN FORM VIEWS var EmployeeFormRenderer = FormRenderer.extend(ChatMixin); - var EmployeeFormController = FormController.extend({ - custom_events: _.extend({}, FormController.prototype.custom_events, { - open_chat: '_onOpenChat' - }), - - _onOpenChat: function (ev) { - const env = Component.env; - const partner = env.models['mail.partner'].insert({ - id: ev.data.partner_id, - }); - partner.openChat(); - }, - }); - var EmployeeFormView = FormView.extend({ config: _.extend({}, FormView.prototype.config, { - Controller: EmployeeFormController, + Controller: FormController, Renderer: EmployeeFormRenderer }), }); @@ -80,23 +61,9 @@ odoo.define('hr.employee_chat', function (require) { }), }); - var EmployeeKanbanController = KanbanController.extend({ - custom_events: _.extend({}, KanbanController.prototype.custom_events, { - open_chat: '_onOpenChat' - }), - - _onOpenChat: function (ev) { - const env = Component.env; - const partner = env.models['mail.partner'].insert({ - id: ev.data.partner_id, - }); - partner.openChat(); - }, - }); - var EmployeeKanbanView = KanbanView.extend({ config: _.extend({}, KanbanView.prototype.config, { - Controller: EmployeeKanbanController, + Controller: KanbanController, Renderer: EmployeeKanbanRenderer }), }); diff --git a/addons/hr/static/src/js/many2one_avatar_employee.js b/addons/hr/static/src/js/many2one_avatar_employee.js index 5be29801ea9..0d5a245aaa2 100644 --- a/addons/hr/static/src/js/many2one_avatar_employee.js +++ b/addons/hr/static/src/js/many2one_avatar_employee.js @@ -8,48 +8,26 @@ odoo.define('hr.Many2OneAvatarEmployee', function (require) { // Usage: // <field name="employee_id" widget="many2one_avatar_employee"/> - const { _t } = require('web.core'); const fieldRegistry = require('web.field_registry'); const { Many2OneAvatarUser, KanbanMany2OneAvatarUser } = require('mail.Many2OneAvatarUser'); - const session = require('web.session'); + const { Component } = owl; const Many2OneAvatarEmployeeMixin = { - supportedModel: 'hr.employee', - - /** - * Set the field to read on 'hr.employee' to get the partner id. - * - * @override - */ - init() { - this._super(...arguments); - this.partnerField = 'user_partner_id'; - }, + supportedModels: ['hr.employee', 'hr.employee.public'], //---------------------------------------------------------------------- // Private //---------------------------------------------------------------------- /** - * Display a warning if the user clicked on himself, or on an employee - * not associated with any user. - * * @override - * @param {number} [partnerId] the id of the clicked partner */ - _displayWarning(partnerId) { - if (partnerId !== session.partner_id) { - // this is not ourself, so if we get here it means that the - // employee is not associated with any user - this.displayNotification({ - message: _t('You can only chat with employees that have a dedicated user'), - type: 'info', - }); - } else { - this._super(...arguments); - } - }, + async _onAvatarClicked(ev) { + ev.stopPropagation(); // in list view, prevent from opening the record + const env = Component.env; + await env.messaging.openChat({ employeeId: this.value.res_id }); + } }; const Many2OneAvatarEmployee = Many2OneAvatarUser.extend(Many2OneAvatarEmployeeMixin); diff --git a/addons/hr/static/src/models/employee/employee.js b/addons/hr/static/src/models/employee/employee.js new file mode 100644 index 00000000000..0cf877baab6 --- /dev/null +++ b/addons/hr/static/src/models/employee/employee.js @@ -0,0 +1,204 @@ +odoo.define('hr/static/src/models/employee/employee.js', function (require) { +'use strict'; + +const { registerNewModel } = require('mail/static/src/model/model_core.js'); +const { attr, one2one } = require('mail/static/src/model/model_field.js'); + +function factory(dependencies) { + + class Employee extends dependencies['mail.model'] { + + //---------------------------------------------------------------------- + // Public + //---------------------------------------------------------------------- + + /** + * @static + * @param {Object} data + * @returns {Object} + */ + static convertData(data) { + const data2 = {}; + if ('id' in data) { + data2.id = data.id; + } + if ('user_id' in data) { + data2.hasCheckedUser = true; + if (!data.user_id) { + data2.user = [['unlink']]; + } else { + const partnerNameGet = data['user_partner_id']; + const partnerData = { + display_name: partnerNameGet[1], + id: partnerNameGet[0], + }; + const userNameGet = data['user_id']; + const userData = { + id: userNameGet[0], + partner: [['insert', partnerData]], + partnerDisplayName: userNameGet[1], + }; + data2.user = [['insert', userData]]; + } + } + return data2; + } + + /** + * Performs the `read` RPC on the `hr.employee.public`. + * + * @static + * @param {Object} param0 + * @param {Object} param0.context + * @param {string[]} param0.fields + * @param {integer[]} param0.ids + */ + static async performRpcRead({ context, fields, ids }) { + const employeesData = await this.env.services.rpc({ + model: 'hr.employee.public', + method: 'read', + args: [ids], + kwargs: { + context, + fields, + }, + }); + this.env.models['hr.employee'].insert(employeesData.map(employeeData => + this.env.models['hr.employee'].convertData(employeeData) + )); + } + + /** + * Performs the `search_read` RPC on `hr.employee.public`. + * + * @static + * @param {Object} param0 + * @param {Object} param0.context + * @param {Array[]} param0.domain + * @param {string[]} param0.fields + */ + static async performRpcSearchRead({ context, domain, fields }) { + const employeesData = await this.env.services.rpc({ + model: 'hr.employee.public', + method: 'search_read', + kwargs: { + context, + domain, + fields, + }, + }); + this.env.models['hr.employee'].insert(employeesData.map(employeeData => + this.env.models['hr.employee'].convertData(employeeData) + )); + } + + /** + * Checks whether this employee has a related user and partner and links + * them if applicable. + */ + async checkIsUser() { + return this.env.models['hr.employee'].performRpcRead({ + ids: [this.id], + fields: ['user_id', 'user_partner_id'], + context: { active_test: false }, + }); + } + + /** + * Gets the chat between the user of this employee and the current user. + * + * If a chat is not appropriate, a notification is displayed instead. + * + * @returns {mail.thread|undefined} + */ + async getChat() { + if (!this.user && !this.hasCheckedUser) { + await this.async(() => this.checkIsUser()); + } + // prevent chatting with non-users + if (!this.user) { + this.env.services['notification'].notify({ + message: this.env._t("You can only chat with employees that have a dedicated user."), + type: 'info', + }); + return; + } + return this.user.getChat(); + } + + /** + * Opens a chat between the user of this employee and the current user + * and returns it. + * + * If a chat is not appropriate, a notification is displayed instead. + * + * @param {Object} [options] forwarded to @see `mail.thread:open()` + * @returns {mail.thread|undefined} + */ + async openChat(options) { + const chat = await this.async(() => this.getChat()); + if (!chat) { + return; + } + await this.async(() => chat.open(options)); + return chat; + } + + /** + * Opens the most appropriate view that is a profile for this employee. + */ + async openProfile() { + return this.env.messaging.openDocument({ + id: this.id, + model: 'hr.employee.public', + }); + } + + //---------------------------------------------------------------------- + // Private + //---------------------------------------------------------------------- + + /** + * @override + */ + static _createRecordLocalId(data) { + return `${this.modelName}_${data.id}`; + } + + } + + Employee.fields = { + /** + * Whether an attempt was already made to fetch the user corresponding + * to this employee. This prevents doing the same RPC multiple times. + */ + hasCheckedUser: attr({ + default: false, + }), + /** + * Unique identifier for this employee. + */ + id: attr(), + /** + * Partner related to this employee. + */ + partner: one2one('mail.partner', { + inverse: 'employee', + related: 'user.partner', + }), + /** + * User related to this employee. + */ + user: one2one('mail.user', { + inverse: 'employee', + }), + }; + + Employee.modelName = 'hr.employee'; + + return Employee; +} + +registerNewModel('hr.employee', factory); + +}); diff --git a/addons/hr/static/src/models/messaging/messaging.js b/addons/hr/static/src/models/messaging/messaging.js new file mode 100644 index 00000000000..435761a91ef --- /dev/null +++ b/addons/hr/static/src/models/messaging/messaging.js @@ -0,0 +1,36 @@ +odoo.define('hr/static/src/models/messaging/messaging.js', function (require) { +'use strict'; + +const { + registerInstancePatchModel, +} = require('mail/static/src/model/model_core.js'); + +registerInstancePatchModel('mail.messaging', 'hr/static/src/models/messaging/messaging.js', { + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * @override + * @param {integer} [param0.employeeId] + */ + async getChat({ employeeId }) { + if (employeeId) { + const employee = this.env.models['hr.employee'].insert({ id: employeeId }); + return employee.getChat(); + } + return this._super(...arguments); + }, + /** + * @override + */ + async openProfile({ id, model }) { + if (model === 'hr.employee' || model === 'hr.employee.public') { + const employee = this.env.models['hr.employee'].insert({ id }); + return employee.openProfile(); + } + return this._super(...arguments); + }, +}); + +}); diff --git a/addons/hr/static/src/models/partner/partner.js b/addons/hr/static/src/models/partner/partner.js new file mode 100644 index 00000000000..cbbcb987dd2 --- /dev/null +++ b/addons/hr/static/src/models/partner/partner.js @@ -0,0 +1,63 @@ +odoo.define('hr/static/src/models/partner/partner.js', function (require) { +'use strict'; + +const { + registerInstancePatchModel, + registerFieldPatchModel, +} = require('mail/static/src/model/model_core.js'); +const { attr, one2one } = require('mail/static/src/model/model_field.js'); + +registerInstancePatchModel('mail.partner', 'hr/static/src/models/partner/partner.js', { + //-------------------------------------------------------------------------- + // Public + //-------------------------------------------------------------------------- + + /** + * Checks whether this partner has a related employee and links them if + * applicable. + */ + async checkIsEmployee() { + await this.async(() => this.env.models['hr.employee'].performRpcSearchRead({ + context: { active_test: false }, + domain: [['user_partner_id', '=', this.id]], + fields: ['user_id', 'user_partner_id'], + })); + this.update({ hasCheckedEmployee: true }); + }, + /** + * When a partner is an employee, its employee profile contains more useful + * information to know who he is than its partner profile. + * + * @override + */ + async openProfile() { + // limitation of patch, `this._super` becomes unavailable after `await` + const _super = this._super.bind(this, ...arguments); + if (!this.employee && !this.hasCheckedEmployee) { + await this.async(() => this.checkIsEmployee()); + } + if (this.employee) { + return this.employee.openProfile(); + } + return _super(); + }, +}); + +registerFieldPatchModel('mail.partner', 'hr/static/src/models/partner/partner.js', { + /** + * Employee related to this partner. It is computed through + * the inverse relation and should be considered read-only. + */ + employee: one2one('hr.employee', { + inverse: 'partner', + }), + /** + * Whether an attempt was already made to fetch the employee corresponding + * to this partner. This prevents doing the same RPC multiple times. + */ + hasCheckedEmployee: attr({ + default: false, + }), +}); + +}); diff --git a/addons/hr/static/src/models/user/user.js b/addons/hr/static/src/models/user/user.js new file mode 100644 index 00000000000..a0482d50deb --- /dev/null +++ b/addons/hr/static/src/models/user/user.js @@ -0,0 +1,18 @@ +odoo.define('hr/static/src/models/user/user.js', function (require) { +'use strict'; + +const { + registerFieldPatchModel, +} = require('mail/static/src/model/model_core.js'); +const { one2one } = require('mail/static/src/model/model_field.js'); + +registerFieldPatchModel('mail.user', 'hr/static/src/models/user/user.js', { + /** + * Employee related to this user. + */ + employee: one2one('hr.employee', { + inverse: 'user', + }), +}); + +}); |
