From 80a8b66246762598c901b380cb113a988d579ff9 Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Fri, 1 Feb 2019 04:46:48 +0000 Subject: [PATCH 1/3] [IMP] web: m2o/m2m SelectCreateDialog will now open kanban view - many2one widget in mobile will open kanban view instead of listview when records needs to be selected - Add support of "kanban_view_ref" on many2x fields: For many2one fields, this specific kanban view will be opened when you click on this field. Note that in desktop, the behavior stay unchanged: 'Search More' will open the tree view. For many2many fields, this specific kanban view will be opened when you add a record in mobile. - Add support "selection_mode" in kanban views. This boolean is set to true when you want to use a kaban view to select a record instead of open a record. "selection_mode" is now passed in qweb_context to allow developers to use t-if='selection_mode' in kanban views to show or hide elements; buttons, dropdowns, anchor tags are not necessary when selection_mode is enabled. So those elements can be hidden based on this condition. Useful when kanban view is opened from m2o in mobile environment. Task ID : 1924779 --- .../static/src/js/fields/relational_fields.js | 2 + .../src/js/views/kanban/kanban_record.js | 6 ++ .../static/src/js/views/kanban/kanban_view.js | 14 ++-- .../select_create_controllers_registry.js | 60 +++++++++++++++++ .../web/static/src/js/views/view_dialogs.js | 65 +++++++++---------- addons/web/views/webclient_templates.xml | 1 + doc/reference/views.rst | 8 +++ odoo/addons/base/rng/common.rng | 1 + 8 files changed, 119 insertions(+), 38 deletions(-) create mode 100644 addons/web/static/src/js/views/select_create_controllers_registry.js diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index d486838d3f5..58dada8703e 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -393,6 +393,7 @@ var FieldMany2One = AbstractField.extend({ initial_view: view, disable_multiple_selection: true, no_create: !self.can_create, + kanban_view_ref: this.attrs.kanban_view_ref, on_selected: function (records) { self.reinitialize(records[0]); self.activate(); @@ -1750,6 +1751,7 @@ var FieldMany2Many = FieldX2Many.extend({ title: _t("Add: ") + this.string, no_create: this.nodeOptions.no_create || !this.activeActions.create, fields_view: this.attrs.views.form, + kanban_view_ref: this.attrs.kanban_view_ref, on_selected: function (records) { var resIDs = _.pluck(records, 'id'); var newIDs = _.difference(resIDs, self.value.res_ids); diff --git a/addons/web/static/src/js/views/kanban/kanban_record.js b/addons/web/static/src/js/views/kanban/kanban_record.js index 0ff5667c2a6..701180a357f 100644 --- a/addons/web/static/src/js/views/kanban/kanban_record.js +++ b/addons/web/static/src/js/views/kanban/kanban_record.js @@ -52,6 +52,7 @@ var KanbanRecord = Widget.extend({ this.editable = options.editable; this.deletable = options.deletable; this.read_only_mode = options.read_only_mode; + this.selectionMode = options.selectionMode; this.qweb = options.qweb; this.subWidgets = {}; @@ -345,6 +346,10 @@ var KanbanRecord = Widget.extend({ this.$el.addClass('o_kanban_record').attr("tabindex", 0); this.$el.attr('role', 'article'); this.$el.data('record', this); + // forcefully add class oe_kanban_global_click to have clickable record always to select it + if (this.selectionMode) { + this.$el.addClass('oe_kanban_global_click'); + } if (this.$el.hasClass('oe_kanban_global_click') || this.$el.hasClass('oe_kanban_global_click_edit')) { this.$el.on('click', this._onGlobalClick.bind(this)); @@ -502,6 +507,7 @@ var KanbanRecord = Widget.extend({ kanban_getcolor: this._getColorID.bind(this), kanban_getcolorname: this._getColorname.bind(this), kanban_compute_domain: this._computeDomain.bind(this), + selection_mode: this.selectionMode, read_only_mode: this.read_only_mode, record: this.record, user_context: this.getSession().user_context, diff --git a/addons/web/static/src/js/views/kanban/kanban_view.js b/addons/web/static/src/js/views/kanban/kanban_view.js index e31bff931d1..62205a2fc46 100644 --- a/addons/web/static/src/js/views/kanban/kanban_view.js +++ b/addons/web/static/src/js/views/kanban/kanban_view.js @@ -36,6 +36,7 @@ var KanbanView = BasicView.extend({ // false so that they will won't be loaded by the initial load this.loadParams.openGroupByDefault = config.device.isMobile ? false : true; this.loadParams.type = 'list'; + this.noDefaultGroupby = params.noDefaultGroupby; var progressBar; utils.traverse(this.arch, function (n) { var isProgressBar = (n.tag === 'progressbar'); @@ -70,6 +71,7 @@ var KanbanView = BasicView.extend({ editable: activeActions.edit, deletable: activeActions.delete, read_only_mode: params.readOnlyMode, + selectionMode: params.selectionMode, }; this.rendererParams.quickCreateEnabled = this._isQuickCreateEnabled(); this.rendererParams.readOnlyMode = params.readOnlyMode; @@ -79,7 +81,7 @@ var KanbanView = BasicView.extend({ } this.controllerParams.on_create = archAttrs.on_create; - this.controllerParams.hasButtons = true; + this.controllerParams.hasButtons = !params.selectionMode ? true : false; this.controllerParams.quickCreateEnabled = this.rendererParams.quickCreateEnabled; @@ -114,10 +116,12 @@ var KanbanView = BasicView.extend({ */ _updateMVCParams: function () { this._super.apply(this, arguments); - var defaultGroupBy = this.arch.attrs.default_group_by; - this.loadParams.groupBy = defaultGroupBy ? - [defaultGroupBy] : - (this.loadParams.groupedBy || []); + if (!this.noDefaultGroupby) { + var defaultGroupBy = this.arch.attrs.default_group_by; + this.loadParams.groupBy = defaultGroupBy ? + [defaultGroupBy] : + (this.loadParams.groupedBy || []); + } }, }); diff --git a/addons/web/static/src/js/views/select_create_controllers_registry.js b/addons/web/static/src/js/views/select_create_controllers_registry.js new file mode 100644 index 00000000000..2cbc0fd0ce3 --- /dev/null +++ b/addons/web/static/src/js/views/select_create_controllers_registry.js @@ -0,0 +1,60 @@ +odoo.define('web.select_create_controllers_registry', function (require) { +"use strict"; + +return {}; + +}); + +odoo.define('web._select_create_controllers_registry', function (require) { +"use strict"; + +var KanbanController = require('web.KanbanController'); +var ListController = require('web.ListController'); +var select_create_controllers_registry = require('web.select_create_controllers_registry'); + +var SelectCreateKanbanController = KanbanController.extend({ + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Override to select the clicked record instead of opening it + * + * @override + * @private + */ + _onOpenRecord: function (ev) { + var selectedRecord = this.model.get(ev.data.id); + this.trigger_up('select_record', { + id: selectedRecord.res_id, + display_name: selectedRecord.data.display_name, + }); + }, +}); + +var SelectCreateListController = ListController.extend({ + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- + + /** + * Override to select the clicked record instead of opening it + * + * @override + * @private + */ + _onOpenRecord: function (ev) { + var selectedRecord = this.model.get(ev.data.id); + this.trigger_up('select_record', { + id: selectedRecord.res_id, + display_name: selectedRecord.data.display_name, + }); + }, +}); + +_.extend(select_create_controllers_registry, { + SelectCreateListController: SelectCreateListController, + SelectCreateKanbanController: SelectCreateKanbanController, +}); + +}); diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 2eb3b7787e8..4634bf1b1fa 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -5,9 +5,8 @@ var config = require('web.config'); var core = require('web.core'); var Dialog = require('web.Dialog'); var dom = require('web.dom'); -var ListController = require('web.ListController'); -var ListView = require('web.ListView'); var view_registry = require('web.view_registry'); +var select_create_controllers_registry = require('web.select_create_controllers_registry'); var _t = core._t; @@ -279,26 +278,6 @@ var FormViewDialog = ViewDialog.extend({ }, }); -var SelectCreateListController = ListController.extend({ - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Override to select the clicked record instead of opening it - * - * @override - * @private - */ - _onOpenRecord: function (ev) { - var selectedRecord = this.model.get(ev.data.id); - this.trigger_up('select_record', { - id: selectedRecord.res_id, - display_name: selectedRecord.data.display_name, - }); - }, -}); - /** * Search dialog (displays a list of records and permits to create a new one by switching to a form view) */ @@ -330,6 +309,7 @@ var SelectCreateDialog = ViewDialog.extend({ _.defaults(this.options, { initial_view: 'search' }); this.on_selected = this.options.on_selected || (function () {}); this.initialIDs = this.options.initial_ids; + this.viewType = 'list'; }, open: function () { @@ -338,12 +318,14 @@ var SelectCreateDialog = ViewDialog.extend({ } var self = this; var _super = this._super.bind(this); - return this.loadViews(this.res_model, this.context, [[false, 'list'], [false, 'search']], {}) + var viewRefID = this.viewType === 'kanban' ? + (this.options.kanban_view_ref && JSON.parse(this.options.kanban_view_ref) || false) : false; + return this.loadViews(this.res_model, this.context, [[viewRefID, this.viewType], [false, 'search']], {}) .then(this.setup.bind(this)) .then(function (fragment) { self.opened().then(function () { dom.append(self.$el, fragment, { - callbacks: [{widget: self.listController}], + callbacks: [{widget: self.viewController}], in_DOM: true, }); self.set_buttons(self.__buttons); @@ -360,23 +342,40 @@ var SelectCreateDialog = ViewDialog.extend({ if (this.initialIDs) { domain = domain.concat([['id', 'in', this.initialIDs]]); } - var listView = new ListView(fieldsViews.list, _.extend({ + var ViewClass = view_registry.get(this.viewType); + var viewOptions = {}; + var selectCreateController; + if (this.viewType === 'list') { // add listview specific options + _.extend(viewOptions, { + hasSelectors: !this.options.disable_multiple_selection, + readonly: true, + + }, this.options.list_view_options); + selectCreateController = select_create_controllers_registry.SelectCreateListController; + } + if (this.viewType === 'kanban') { + _.extend(viewOptions, { + noDefaultGroupby: true, + selectionMode: this.options.selectionMode || false, + }); + selectCreateController = select_create_controllers_registry.SelectCreateKanbanController; + } + var view = new ViewClass(fieldsViews[this.viewType], _.extend(viewOptions, { action: { controlPanelFieldsView: fieldsViews.search, + help: _.str.sprintf("

%s

", _t("No records found!")), }, action_buttons: false, dynamicFilters: this.options.dynamicFilters, context: this.context, domain: domain, - hasSelectors: !this.options.disable_multiple_selection, modelName: this.res_model, - readonly: true, withBreadcrumbs: false, withSearchPanel: false, - }, this.options.list_view_options)); - listView.setController(SelectCreateListController); - return listView.getController(this).then(function (controller) { - self.listController = controller; + })); + view.setController(selectCreateController); + return view.getController(this).then(function (controller) { + self.viewController = controller; // render the footer buttons self.__buttons = [{ text: _t("Cancel"), @@ -397,7 +396,7 @@ var SelectCreateDialog = ViewDialog.extend({ disabled: true, close: true, click: function () { - var records = self.listController.getSelectedRecords(); + var records = self.viewController.getSelectedRecords(); var values = _.map(records, function (record) { return { id: record.res_id, @@ -408,7 +407,7 @@ var SelectCreateDialog = ViewDialog.extend({ }, }); } - return self.listController.appendTo(fragment); + return self.viewController.appendTo(fragment); }).then(function () { return fragment; }); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 1ed2df632f2..623ce4e2f86 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -320,6 +320,7 @@ + diff --git a/doc/reference/views.rst b/doc/reference/views.rst index f3bc35fa286..854d85c4b98 100644 --- a/doc/reference/views.rst +++ b/doc/reference/views.rst @@ -567,6 +567,9 @@ system. Available semantic components are: ``password`` indicates that a :class:`~odoo.fields.Char` field stores a password and that its data shouldn't be displayed + ``kanban_view_ref`` + for opening specific kanban view when selecting records from m2o/m2m in mobile + environment .. todo:: classes for forms @@ -1106,6 +1109,11 @@ Possible children of the view element are: self-explanatory ``read_only_mode`` self-explanatory + ``selection_mode`` + set to true when kanban view is opened in mobile environment from m2o/m2m field + for selecting records. + + .. note:: clicking on m2o/m2m field in mobile environment opens kanban view .. rubric:: buttons and fields diff --git a/odoo/addons/base/rng/common.rng b/odoo/addons/base/rng/common.rng index 43a46d8a0e5..7cb12f02fa8 100644 --- a/odoo/addons/base/rng/common.rng +++ b/odoo/addons/base/rng/common.rng @@ -264,6 +264,7 @@ + From 597142d12c35e2cfe40323f76b3d6c7645d7638f Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Wed, 7 Aug 2019 05:59:27 +0000 Subject: [PATCH 2/3] [REF] web: move some logic in _prepareButtons method This function was too long and it's now easier to override it in enterprise Task ID : 1924779 --- .../web/static/src/js/views/view_dialogs.js | 68 +++++++++++-------- 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 4634bf1b1fa..beead650622 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -377,36 +377,7 @@ var SelectCreateDialog = ViewDialog.extend({ return view.getController(this).then(function (controller) { self.viewController = controller; // render the footer buttons - self.__buttons = [{ - text: _t("Cancel"), - classes: 'btn-secondary o_form_button_cancel', - close: true, - }]; - if (!self.options.no_create) { - self.__buttons.unshift({ - text: _t("Create"), - classes: 'btn-primary', - click: self.create_edit_record.bind(self) - }); - } - if (!self.options.disable_multiple_selection) { - self.__buttons.unshift({ - text: _t("Select"), - classes: 'btn-primary o_select_button', - disabled: true, - close: true, - click: function () { - var records = self.viewController.getSelectedRecords(); - var values = _.map(records, function (record) { - return { - id: record.res_id, - display_name: record.data.display_name, - }; - }); - self.on_selected(values); - }, - }); - } + self._prepareButtons(); return self.viewController.appendTo(fragment); }).then(function () { return fragment; @@ -438,6 +409,43 @@ var SelectCreateDialog = ViewDialog.extend({ }); return isFocusSet; }, + /** + * prepare buttons for dialog footer based on options + * + * @private + */ + _prepareButtons: function () { + this.__buttons = [{ + text: _t("Cancel"), + classes: 'btn-secondary o_form_button_cancel', + close: true, + }]; + if (!this.options.no_create) { + this.__buttons.unshift({ + text: _t("Create"), + classes: 'btn-primary', + click: this.create_edit_record.bind(this) + }); + } + if (!this.options.disable_multiple_selection) { + this.__buttons.unshift({ + text: _t("Select"), + classes: 'btn-primary o_select_button', + disabled: true, + close: true, + click: function () { + var records = this.viewController.getSelectedRecords(); + var values = _.map(records, function (record) { + return { + id: record.res_id, + display_name: record.data.display_name, + }; + }); + this.on_selected(values); + }, + }); + } + }, }); return { From 25ebd80b40d52d8e6c59f264179dba35753f69da Mon Sep 17 00:00:00 2001 From: Mohammed Shekha Date: Thu, 27 Jun 2019 13:16:26 +0000 Subject: [PATCH 3/3] [IMP] *: add selection_mode condition to show/hide elements in kanban If we use a kanban view to select a record we want to hide those elements: dropdown boxes, buttons, anchors, some widgets, kanban color. When it was too much change and used the new attribute "kanban_view_ref" to explicitly define which view to open in selection_mode. When also had to create previously unexisting kanban for fleet.vehicle.model to show name and brand. Task ID : 1924779 --- .../views/account_journal_dashboard_view.xml | 2 +- .../analytic/views/analytic_account_views.xml | 6 +++--- addons/crm/views/crm_lead_views.xml | 2 +- addons/crm/views/crm_stage_views.xml | 2 +- .../wizard/crm_lead_to_opportunity_views.xml | 4 ++-- .../wizard/crm_merge_opportunities_views.xml | 2 +- .../crm_iap_lead/views/crm_iap_lead_views.xml | 2 +- .../views/crm_reveal_views.xml | 2 +- addons/event/views/event_views.xml | 2 +- .../fleet/views/fleet_vehicle_model_views.xml | 17 +++++++++++++++++ addons/fleet/views/fleet_vehicle_views.xml | 4 ++-- addons/gamification/views/badge.xml | 2 +- addons/hr/views/hr_department_views.xml | 6 +++--- addons/hr/views/hr_job_views.xml | 2 +- addons/hr_contract/views/hr_contract_views.xml | 2 +- addons/hr_recruitment/views/hr_job_views.xml | 2 +- .../views/hr_recruitment_views.xml | 2 +- .../hr_work_entry/views/hr_work_entry_views.xml | 4 ++-- addons/maintenance/views/maintenance_views.xml | 14 +++++++------- .../views/mailing_mailing_views.xml | 6 +++--- addons/mrp/views/mrp_workcenter_views.xml | 2 +- addons/mrp/views/stock_picking_views.xml | 9 +++++---- addons/note/views/note_views.xml | 2 +- addons/payment/views/payment_views.xml | 6 +++--- addons/pos_sale/views/pos_config_views.xml | 2 +- addons/pos_sale/views/sales_team_views.xml | 4 ++-- addons/project/views/project_views.xml | 8 ++++---- addons/sale/views/sale_views.xml | 4 ++-- .../sales_team/views/sales_team_dashboard.xml | 2 +- addons/stock/views/stock_picking_views.xml | 11 +++++++---- addons/survey/views/survey_survey_views.xml | 8 ++++---- .../views/event_track_views.xml | 2 +- .../views/res_config_settings_views.xml | 2 +- odoo/addons/base/views/res_company_views.xml | 16 +++++++++++++--- 34 files changed, 97 insertions(+), 66 deletions(-) diff --git a/addons/account/views/account_journal_dashboard_view.xml b/addons/account/views/account_journal_dashboard_view.xml index 691354faa0f..c91c8e1f902 100644 --- a/addons/account/views/account_journal_dashboard_view.xml +++ b/addons/account/views/account_journal_dashboard_view.xml @@ -14,7 +14,7 @@ -
+
diff --git a/addons/analytic/views/analytic_account_views.xml b/addons/analytic/views/analytic_account_views.xml index 8505e72cefa..e4893f51bd1 100644 --- a/addons/analytic/views/analytic_account_views.xml +++ b/addons/analytic/views/analytic_account_views.xml @@ -205,14 +205,14 @@
-
+

-
+
- Balance: + Balance:
diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index de5abd619b3..fb2c8958962 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -393,7 +393,7 @@ -
+