From 7e91154e8ecc3b42f841ed34152d9ff9ca67bfbc Mon Sep 17 00:00:00 2001 From: qsm-odoo Date: Fri, 24 Feb 2017 13:31:42 +0100 Subject: [PATCH] [IMP] web: some more linting to follow guidelines --- .../src/js/chrome/abstract_web_client.js | 52 +++++------ addons/web/static/src/js/core/mixins.js | 92 +++++++++---------- addons/web/static/src/js/core/session.js | 64 ++++++------- .../static/src/js/fields/abstract_field.js | 4 +- .../static/src/js/services/ajax_service.js | 4 +- .../src/js/views/kanban/kanban_controller.js | 2 +- .../test/fields/relational_fields_tests.js | 5 - 7 files changed, 109 insertions(+), 114 deletions(-) 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 498d1b41ea4..1caa03f2955 100644 --- a/addons/web/static/src/js/chrome/abstract_web_client.js +++ b/addons/web/static/src/js/chrome/abstract_web_client.js @@ -51,7 +51,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { this.notification_manager.warn(e.data.title, e.data.message, e.data.sticky); } }, - load_views: function(event) { + load_views: function (event) { var params = { model: event.data.modelName, context: event.data.context, @@ -61,13 +61,13 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { .load_views(params, event.data.options || {}) .then(event.data.on_success); }, - load_filters: function(event) { + load_filters: function (event) { return data_manager .load_filters(event.data.dataset, event.data.action_id) .then(event.data.on_success); }, // session - get_session: function(event) { + get_session: function (event) { if (event.data.callback) { event.data.callback(session); } @@ -80,7 +80,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { }); }, }, - init: function(parent) { + init: function (parent) { this.client_options = {}; mixins.ServiceProvider.init.call(this); this._super(parent); @@ -90,7 +90,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { this.action_mutex = new concurrency.Mutex(); this.set('title_part', {"zopenerp": "Odoo"}); }, - start: function() { + start: function () { var self = this; this.on("change:title_part", this, this._title_changed); @@ -118,20 +118,20 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { core.bus.trigger('web_client_ready'); }); }, - bind_events: function() { + bind_events: function () { var self = this; $('.oe_systray').show(); - this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function() { + this.$el.on('mouseenter', '.oe_systray > div:not([data-toggle=tooltip])', function () { $(this).attr('data-toggle', 'tooltip').tooltip().trigger('mouseenter'); }); - this.$el.on('click', '.oe_dropdown_toggle', function(ev) { + this.$el.on('click', '.oe_dropdown_toggle', function (ev) { ev.preventDefault(); var $toggle = $(this); var doc_width = $(document).width(); var $menu = $toggle.siblings('.oe_dropdown_menu'); $menu = $menu.size() >= 1 ? $menu : $toggle.find('.oe_dropdown_menu'); var state = $menu.is('.oe_opened'); - setTimeout(function() { + setTimeout(function () { // Do not alter propagation $toggle.add($menu).toggleClass('oe_opened', !state); if (!state) { @@ -145,7 +145,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { } }, 0); }); - core.bus.on('click', this, function(ev) { + core.bus.on('click', this, function (ev) { $('.tooltip').remove(); if (!$(ev.target).is('input[type=file]')) { self.$('.oe_dropdown_menu.oe_opened, .oe_dropdown_toggle.oe_opened').removeClass('oe_opened'); @@ -165,29 +165,29 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { }); }; }, - set_action_manager: function() { + set_action_manager: function () { this.action_manager = new ActionManager(this, {webclient: this}); return this.action_manager.appendTo(this.$('.o_main_content')); }, - set_notification_manager: function() { + set_notification_manager: function () { this.notification_manager = new NotificationManager(this); return this.notification_manager.appendTo(this.$el); }, - set_loading: function() { + set_loading: function () { this.loading = new Loading(this); return this.loading.appendTo(this.$el); }, - show_application: function() { + show_application: function () { }, - clear_uncommitted_changes: function() { + clear_uncommitted_changes: function () { var def = $.Deferred().resolve(); core.bus.trigger('clear_uncommitted_changes', function chain_callbacks(callback) { def = def.then(callback); }); return def; }, - destroy_content: function() { - _.each(_.clone(this.getChildren()), function(el) { + destroy_content: function () { + _.each(_.clone(this.getChildren()), function (el) { el.destroy(); }); this.$el.children().remove(); @@ -198,7 +198,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { /** Sets the first part of the title of the window, dedicated to the current action. */ - set_title: function(title) { + set_title: function (title) { this.set_title_part("action", title); }, /** @@ -206,15 +206,15 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { a title part is changed, all parts are gathered, ordered by alphabetical order and displayed in the title of the window separated by '-'. */ - set_title_part: function(part, title) { + set_title_part: function (part, title) { var tmp = _.clone(this.get("title_part")); tmp[part] = title; this.set("title_part", tmp); }, - _title_changed: function() { - var parts = _.sortBy(_.keys(this.get("title_part")), function(x) { return x; }); + _title_changed: function () { + var parts = _.sortBy(_.keys(this.get("title_part")), function (x) { return x; }); var tmp = ""; - _.each(parts, function(part) { + _.each(parts, function (part) { var str = this.get("title_part")[part]; if (str) { tmp = tmp ? tmp + " - " + str : str; @@ -229,17 +229,17 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { * When do_action is performed on the WebClient, forward it to the main ActionManager * This allows to widgets that are not inside the ActionManager to perform do_action */ - do_action: function() { + do_action: function () { return this.action_manager.do_action.apply(this, arguments); }, - do_reload: function() { + do_reload: function () { var self = this; return this.session.session_reload().then(function () { session.load_modules(true).then( self.menu.proxy('do_reload')); }); }, - do_push_state: function(state) { + do_push_state: function (state) { if ('title' in state) { this.set_title(state.title); delete state.title; @@ -281,7 +281,7 @@ var AbstractWebClient = Widget.extend(mixins.ServiceProvider, { //-------------------------------------------------------------- // Misc. //-------------------------------------------------------------- - toggle_fullscreen: function(fullscreen) { + toggle_fullscreen: function (fullscreen) { this.$el.toggleClass('o_fullscreen', fullscreen); }, }); diff --git a/addons/web/static/src/js/core/mixins.js b/addons/web/static/src/js/core/mixins.js index 5caac9a2c8c..266888a3948 100644 --- a/addons/web/static/src/js/core/mixins.js +++ b/addons/web/static/src/js/core/mixins.js @@ -13,7 +13,7 @@ var AbstractService = require('web.AbstractService'); */ var ParentedMixin = { __parentedMixin : true, - init: function() { + init: function () { this.__parentedDestroyed = false; this.__parentedChildren = []; this.__parentedParent = null; @@ -26,7 +26,7 @@ var ParentedMixin = { * previous parent will not return the current object anymore when its * getChildren() method is called. */ - setParent : function(parent) { + setParent : function (parent) { if (this.getParent()) { if (this.getParent().__parentedMixin) { this.getParent().__parentedChildren = _.without(this @@ -41,19 +41,19 @@ var ParentedMixin = { /** * Return the current parent of the object (or null). */ - getParent : function() { + getParent : function () { return this.__parentedParent; }, /** * Return a list of the children of the current object. */ - getChildren : function() { + getChildren : function () { return _.clone(this.__parentedChildren); }, /** * Returns true if destroy() was called on the current object. */ - isDestroyed : function() { + isDestroyed : function () { return this.__parentedDestroyed; }, /** @@ -72,7 +72,7 @@ var ParentedMixin = { with no arguments or never resolved if the current object is destroyed. */ - alive: function(promise, reject) { + alive: function (promise, reject) { var self = this; return $.Deferred(function (def) { promise.then(function () { @@ -96,8 +96,8 @@ var ParentedMixin = { * Inform the object it should destroy itself, releasing any * resource it could have reserved. */ - destroy : function() { - _.each(this.getChildren(), function(el) { + destroy : function () { + _.each(this.getChildren(), function (el) { el.destroy(); }); this.setParent(undefined); @@ -115,7 +115,7 @@ var ParentedMixin = { }, }; -function OdooEvent (target, name, data) { +function OdooEvent(target, name, data) { this.target = target; this.name = name; this.data = Object.create(null); @@ -147,7 +147,7 @@ OdooEvent.prototype.is_stopped = function () { * */ var Events = Class.extend({ - on : function(events, callback, context) { + on : function (events, callback, context) { var ev; events = events.split(/\s+/); var calls = this._callbacks || (this._callbacks = {}); @@ -161,7 +161,7 @@ var Events = Class.extend({ return this; }, - off : function(events, callback, context) { + off : function (events, callback, context) { var ev, calls, node; if (!events) { delete this._callbacks; @@ -183,9 +183,9 @@ var Events = Class.extend({ return this; }, - callbackList: function() { + callbackList: function () { var lst = []; - _.each(this._callbacks || {}, function(el, eventName) { + _.each(this._callbacks || {}, function (el, eventName) { var node = el; while ((node = node.next) && node.next) { lst.push([eventName, node.callback, node.context]); @@ -194,7 +194,7 @@ var Events = Class.extend({ return lst; }, - trigger : function(events) { + trigger : function (events) { var event, node, calls, tail, args, all, rest; if (!(calls = this._callbacks)) return this; @@ -237,18 +237,18 @@ var Events = Class.extend({ */ var EventDispatcherMixin = _.extend({}, ParentedMixin, { __eventDispatcherMixin: true, - init: function() { + init: function () { ParentedMixin.init.call(this); this.__edispatcherEvents = new Events(); this.__edispatcherRegisteredEvents = []; }, - on: function(events, dest, func) { + on: function (events, dest, func) { var self = this; if (typeof func !== "function") { throw new Error("Event handler must be a function."); } events = events.split(/\s+/); - _.each(events, function(eventName) { + _.each(events, function (eventName) { self.__edispatcherEvents.on(eventName, func, dest); if (dest && dest.__eventDispatcherMixin) { dest.__edispatcherRegisteredEvents.push({name: eventName, func: func, source: self}); @@ -256,20 +256,20 @@ var EventDispatcherMixin = _.extend({}, ParentedMixin, { }); return this; }, - off: function(events, dest, func) { + off: function (events, dest, func) { var self = this; events = events.split(/\s+/); - _.each(events, function(eventName) { + _.each(events, function (eventName) { self.__edispatcherEvents.off(eventName, func, dest); if (dest && dest.__eventDispatcherMixin) { - dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function(el) { + dest.__edispatcherRegisteredEvents = _.filter(dest.__edispatcherRegisteredEvents, function (el) { return !(el.name === eventName && el.func === func && el.source === self); }); } }); return this; }, - once: function(events, dest, func) { + once: function (events, dest, func) { // similar to this.on(), but func is executed only once var self = this; if (typeof func !== "function") { @@ -280,28 +280,28 @@ var EventDispatcherMixin = _.extend({}, ParentedMixin, { self.off(events, dest, what); }); }, - trigger: function() { + trigger: function () { this.__edispatcherEvents.trigger.apply(this.__edispatcherEvents, arguments); return this; }, - trigger_up: function(name, info) { + trigger_up: function (name, info) { var event = new OdooEvent(this, name, info); this._trigger_up(event); }, - _trigger_up: function(event) { + _trigger_up: function (event) { var parent; this.__edispatcherEvents.trigger(event.name, event); if (!event.is_stopped() && (parent = this.getParent())) { parent._trigger_up(event); } }, - destroy: function() { + destroy: function () { var self = this; - _.each(this.__edispatcherRegisteredEvents, function(event) { + _.each(this.__edispatcherRegisteredEvents, function (event) { event.source.__edispatcherEvents.off(event.name, event.func, self); }); this.__edispatcherRegisteredEvents = []; - _.each(this.__edispatcherEvents.callbackList(), function(cal) { + _.each(this.__edispatcherEvents.callbackList(), function (cal) { this.off(cal[0], cal[2], cal[1]); }, this); this.__edispatcherEvents.off(); @@ -310,11 +310,11 @@ var EventDispatcherMixin = _.extend({}, ParentedMixin, { }); var PropertiesMixin = _.extend({}, EventDispatcherMixin, { - init: function() { + init: function () { EventDispatcherMixin.init.call(this); this.__getterSetterInternalMap = {}; }, - set: function(arg1, arg2, arg3) { + set: function (arg1, arg2, arg3) { var map; var options; if (typeof arg1 === "string") { @@ -327,7 +327,7 @@ var PropertiesMixin = _.extend({}, EventDispatcherMixin, { } var self = this; var changed = false; - _.each(map, function(val, key) { + _.each(map, function (val, key) { var tmp = self.__getterSetterInternalMap[key]; if (tmp === val) return; @@ -354,30 +354,30 @@ var PropertiesMixin = _.extend({}, EventDispatcherMixin, { if (changed) self.trigger("change", self); }, - get: function(key) { + get: function (key) { return this.__getterSetterInternalMap[key]; } }); var ServicesMixin = { - call: function(service, method) { + call: function (service, method) { var args = Array.prototype.slice.call(arguments, 2); var result; this.trigger_up('call_service', { service: service, method: method, args: args, - callback: function(r) { + callback: function (r) { result = r; }, }); return result; }, // AJAX calls - performRPC: function(route, args) { + performRPC: function (route, args) { return this.call('ajax', 'rpc', route, args); }, - performModelRPC: function(model, method, args, kwargs) { + performModelRPC: function (model, method, args, kwargs) { return this.performRPC('/web/dataset/call_kw/' + model + '/' + method, { model: model, method: method, @@ -385,12 +385,12 @@ var ServicesMixin = { kwargs: kwargs || {}, }); }, - loadFieldView: function(dataset, view_id, view_type, options) { + loadFieldView: function (dataset, view_id, view_type, options) { return this.loadViews(dataset.model, dataset.get_context(), [[view_id, view_type]], options).then(function (result) { return result[view_type]; }); }, - loadViews: function(modelName, context, views, options) { + loadViews: function (modelName, context, views, options) { var def = $.Deferred(); this.trigger_up('load_views', { modelName: modelName, @@ -401,7 +401,7 @@ var ServicesMixin = { }); return def; }, - loadFilters: function(dataset, action_id) { + loadFilters: function (dataset, action_id) { var def = $.Deferred(); this.trigger_up('load_filters', { dataset: dataset, @@ -411,10 +411,10 @@ var ServicesMixin = { return def; }, // Session stuff - getSession: function() { + getSession: function () { var session; this.trigger_up('get_session', { - callback: function(result) { + callback: function (result) { session = result; } }); @@ -429,32 +429,32 @@ var ServicesMixin = { var def = $.Deferred(); options = options || {}; var oldOnSuccess = options.on_success; - options.on_success = function(result) { + options.on_success = function (result) { oldOnSuccess(result); def.resolve(result); }; this.trigger_up('do_action', {'action': action, options: options}); return def; }, - do_notify: function(title, message, sticky) { + do_notify: function (title, message, sticky) { this.trigger_up('notification', {title: title, message: message, sticky: sticky}); }, - do_warn: function(title, message, sticky) { + do_warn: function (title, message, sticky) { this.trigger_up('warning', {title: title, message: message, sticky: sticky}); }, }; var ServiceProvider = { services: {}, - init: function() { + init: function () { var self = this; - _.each(AbstractService.prototype.Services, function(Service) { + _.each(AbstractService.prototype.Services, function (Service) { var service = new Service(); self.services[service.name] = service; }); this.custom_events.call_service = this._call_service.bind(this); }, - _call_service: function(event) { + _call_service: function (event) { var service = this.services[event.data.service]; var result = service[event.data.method].apply(service, event.data.args); event.data.callback(result); diff --git a/addons/web/static/src/js/core/session.js b/addons/web/static/src/js/core/session.js index ca56eb62439..9ee5726cfc8 100644 --- a/addons/web/static/src/js/core/session.js +++ b/addons/web/static/src/js/core/session.js @@ -27,7 +27,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { by this session object. Specifying this option automatically implies that the option "override_session" is set to true. */ - init: function(parent, origin, options) { + init: function (parent, origin, options) { mixins.EventDispatcherMixin.init.call(this, parent); options = options || {}; this.module_list = (options.modules && options.modules.slice()) || (window.odoo._modules && window.odoo._modules.slice()) || []; @@ -49,7 +49,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { this.currencies = {}; this._groups_def = {}; }, - setup: function(origin, options) { + setup: function (origin, options) { // must be able to customize server var window_origin = location.protocol + "//" + location.host; origin = origin ? origin.replace( /\/+$/, '') : window_origin; @@ -68,7 +68,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { /** * Setup a session */ - session_bind: function(origin) { + session_bind: function (origin) { var self = this; this.setup(origin); qweb.default_dict._s = this.origin; @@ -91,21 +91,21 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { if (this.is_frontend) return def; var self = this; - return def.then(function() { + return def.then(function () { var modules = self.module_list.join(','); var deferred = self.load_qweb(modules); if(self.session_is_valid()) { - return deferred.then(function() { return self.load_modules(); }); + return deferred.then(function () { return self.load_modules(); }); } return $.when( deferred, - self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list}).then(function(trans) { + self.rpc('/web/webclient/bootstrap_translations', {mods: self.module_list}).then(function (trans) { _t.database.set_bundle(trans); }) ); }); }, - session_is_valid: function() { + session_is_valid: function () { var db = $.deparam.querystring().db; if (db && this.db !== db) { return false; @@ -115,19 +115,19 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { /** * The session is validated by restoration of a previous session */ - session_authenticate: function() { + session_authenticate: function () { var self = this; - return $.when(this._session_authenticate.apply(this, arguments)).then(function() { + return $.when(this._session_authenticate.apply(this, arguments)).then(function () { return self.load_modules(); }); }, /** * The session is validated either by login or by restoration of a previous session */ - _session_authenticate: function(db, login, password) { + _session_authenticate: function (db, login, password) { var self = this; var params = {db: db, login: login, password: password}; - return this.rpc("/web/session/authenticate", params).then(function(result) { + return this.rpc("/web/session/authenticate", params).then(function (result) { if (!result.uid) { return $.Deferred().reject(); } @@ -135,11 +135,11 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { _.extend(self, result); }); }, - session_logout: function() { + session_logout: function () { $.bbq.removeState(); return this.rpc("/web/session/destroy", {}); }, - user_has_group: function(group) { + user_has_group: function (group) { if (!this.uid) { return $.when(false); } @@ -188,7 +188,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { * Load additional web addons of that instance and init them * */ - load_modules: function() { + load_modules: function () { var self = this; var modules = odoo._modules; var all_modules = _.uniq(self.module_list.concat(modules)); @@ -203,19 +203,19 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { loaded, self.rpc('/web/webclient/csslist', {mods: to_load}).done(self.load_css.bind(self)), self.load_qweb(to_load), - self.rpc('/web/webclient/jslist', {mods: to_load}).done(function(files) { + self.rpc('/web/webclient/jslist', {mods: to_load}).done(function (files) { file_list = file_list.concat(files); }) ); } return loaded.then(function () { return self.load_js(file_list); - }).done(function() { + }).done(function () { self.on_modules_loaded(); self.trigger('module_loaded'); }); }, - load_translations: function() { + load_translations: function () { return _t.database.load_translations(this, this.module_list, this.user_context.lang); }, load_css: function (files) { @@ -224,7 +224,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { ajax.loadCSS(self.url(file, null)); }); }, - load_js: function(files) { + load_js: function (files) { var self = this; var d = $.Deferred(); if (files.length !== 0) { @@ -236,7 +236,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { } return d; }, - load_qweb: function(mods) { + load_qweb: function (mods) { this.qweb_mutex.exec(function () { return $.get('/web/webclient/qweb?mods=' + mods).then(function (doc) { if (!doc) { return; } @@ -245,7 +245,7 @@ var Session = core.Class.extend(mixins.EventDispatcherMixin, { }); return this.qweb_mutex.def; }, - on_modules_loaded: function() { + on_modules_loaded: function () { var openerp = window.openerp; for(var j=0; j 0) qs = "?" + qs; - var prefix = _.any(['http://', 'https://', '//'], function(el) { + var prefix = _.any(['http://', 'https://', '//'], function (el) { return path.length >= el.length && path.slice(0, el.length) === el; }) ? '' : this.prefix; return prefix + path + qs; diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js index ca8150c6567..3d7a827ff13 100644 --- a/addons/web/static/src/js/fields/abstract_field.js +++ b/addons/web/static/src/js/fields/abstract_field.js @@ -150,9 +150,9 @@ var AbstractField = Widget.extend({ * * @returns {Deferred} */ - start: function() { + start: function () { var self = this; - return this._super.apply(this, arguments).then(function() { + return this._super.apply(this, arguments).then(function () { self._render(); if (self.required) { self.$el.addClass('o_form_required'); diff --git a/addons/web/static/src/js/services/ajax_service.js b/addons/web/static/src/js/services/ajax_service.js index b86585eb5d8..e44893eb63f 100644 --- a/addons/web/static/src/js/services/ajax_service.js +++ b/addons/web/static/src/js/services/ajax_service.js @@ -6,11 +6,11 @@ var session = require('web.session'); var AjaxService = AbstractService.extend({ name: 'ajax', - rpc: function(route, args) { + rpc: function (route, args) { return session.rpc(route, args); }, }); return AjaxService; -}); \ No newline at end of file +}); diff --git a/addons/web/static/src/js/views/kanban/kanban_controller.js b/addons/web/static/src/js/views/kanban/kanban_controller.js index 0c1c4bc3d59..7ea09f38eb5 100644 --- a/addons/web/static/src/js/views/kanban/kanban_controller.js +++ b/addons/web/static/src/js/views/kanban/kanban_controller.js @@ -291,7 +291,7 @@ var KanbanController = BasicController.extend({ var self = this; var column = event.target; var data = this.model.get(column.db_id); - function add_record (records) { + function add_record(records) { return self.model .addRecordToGroup(data.id, records[0]) .then(function (db_id) { diff --git a/addons/web/static/test/fields/relational_fields_tests.js b/addons/web/static/test/fields/relational_fields_tests.js index 730fd3f82d5..6ed77912896 100644 --- a/addons/web/static/test/fields/relational_fields_tests.js +++ b/addons/web/static/test/fields/relational_fields_tests.js @@ -477,7 +477,6 @@ QUnit.module('relational_fields', { "embedded one2many should have a cell with relational value"); }); - QUnit.test('embedded one2many with widget', function (assert) { assert.expect(1); @@ -506,7 +505,6 @@ QUnit.module('relational_fields', { assert.strictEqual(form.$('span.o_row_handle').length, 1, "should have 1 handles"); }); - QUnit.test('one2many field when using the pager', function (assert) { assert.expect(13); @@ -1878,7 +1876,6 @@ QUnit.module('relational_fields', { assert.strictEqual($td.contents().length, 1, "select tag should be only child of td"); }); - QUnit.test('widget selection on a many2one field', function (assert) { assert.expect(7); @@ -2144,8 +2141,6 @@ QUnit.module('relational_fields', { "should have added the second element to the many2many"); }); - - }); }); });