summaryrefslogtreecommitdiff
path: root/addons/hr/static/src
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-08-28 12:53:38 +0000
committerSébastien Theys <seb@odoo.com>2020-08-28 16:15:15 +0000
commit17611f4adc5e950b0a75a2a6615b1da3c956387f (patch)
treeede6cbc0bc67c1dceed3b5ca6c1bb3e08389675c /addons/hr/static/src
parent5ba64925fd846e9a9a4884913e3d1004d05ecdf6 (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.js10
-rw-r--r--addons/hr/static/src/bugfix/bugfix.scss6
-rw-r--r--addons/hr/static/src/bugfix/bugfix.xml11
-rw-r--r--addons/hr/static/src/bugfix/bugfix_tests.js18
-rw-r--r--addons/hr/static/src/js/chat.js43
-rw-r--r--addons/hr/static/src/js/many2one_avatar_employee.js36
-rw-r--r--addons/hr/static/src/models/employee/employee.js204
-rw-r--r--addons/hr/static/src/models/messaging/messaging.js36
-rw-r--r--addons/hr/static/src/models/partner/partner.js63
-rw-r--r--addons/hr/static/src/models/user/user.js18
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',
+ }),
+});
+
+});