diff --git a/web_m2x_options/README.rst b/web_m2x_options/README.rst new file mode 100644 index 00000000..be6e7603 --- /dev/null +++ b/web_m2x_options/README.rst @@ -0,0 +1,115 @@ +================================== +Add new options for many2one field +================================== + + +Description +----------- + +This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display +control options. + +**New: support many2manytags widget !** + +**New: support global option management with ir.config_parameter !** + +Options provided includes possibility to remove "Create..." and/or "Create and +Edit..." entries from many2one drop down. You can also change default number of +proposition appearing in the drop-down. Or prevent the dialog box poping in +case of validation error. + +If not specified, the module will avoid proposing any of the create options +if the current user have no permission rights to create the related object. + + +Requirements +------------ + +Was tested on openerp 8.0, trunk, saas-5 branch. New way to import js file. (thanks to tfossoul) + + +New options +----------- + +``create`` *boolean* (Default: depends if user have create rights) + + Whether to display the "Create..." entry in dropdown panel. + +``create_edit`` *boolean* (Default: depends if user have create rights) + + Whether to display "Create and Edit..." entry in dropdown panel + +``m2o_dialog`` *boolean* (Default: depends if user have create rights) + + Whether to display the many2one dialog in case of validation error. + +``limit`` *int* (Default: openerp default value is ``7``) + + Number of displayed record in drop-down panel + +``search_more`` *boolean* + + Used to force disable/enable search more button. + +``field_color`` *string* + + A string to define the field used to define color. + This option has to be used with colors. + +``colors`` *dictionary* + + A dictionary to link field value with a HTML color. + This option has to be used with field_color. + + + +ir.config_parameter options +--------------------------- + +Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. +If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition. + +``web_m2x_options.create`` *boolean* (Default: depends if user have create rights) + + Whether to display the "Create..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.create_edit`` *boolean* (Default: depends if user have create rights) + + Whether to display "Create and Edit..." entry in dropdown panel for all fields in the odoo instance. + +``web_m2x_options.m2o_dialog`` *boolean* (Default: depends if user have create rights) + + Whether to display the many2one dialog in case of validation error for all fields in the odoo instance. + +``web_m2x_options.limit`` *int* (Default: openerp default value is ``7``) + + Number of displayed record in drop-down panel for all fields in the odoo instance + +``web_m2x_options.search_more`` *boolean* (Default: default value is ``False``) + + Whether the field should always show "Search more..." entry or not. + +To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like: + +- web_m2x_options.create: False +- web_m2x_options.create_edit: False +- web_m2x_options.m2o_dialog: False +- web_m2x_options.limit: 10 +- web_m2x_options.search_more: True + + +Example +------- + +Your XML form view definition could contain:: + + ... + + ... + +Note +---- + +Double check that you have no inherited view that remote ``options`` you set on a field ! +If nothing work, add a debugger in the first ligne of ``get_search_result method`` and enable debug mode in OpenERP. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. + diff --git a/web_m2x_options/__init__.py b/web_m2x_options/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/web_m2x_options/__openerp__.py b/web_m2x_options/__openerp__.py new file mode 100644 index 00000000..37b7dbf4 --- /dev/null +++ b/web_m2x_options/__openerp__.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +{ + "name": 'web_m2x_options', + "version": "8.0.0.2", + "depends": [ + 'base', + 'web', + ], + 'data': ['views/view.xml'], + "author": "0k.io,Odoo Community Association (OCA)", + "installable": True, + "active": False, +} diff --git a/web_m2x_options/static/description/icon.png b/web_m2x_options/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/web_m2x_options/static/description/icon.png differ diff --git a/web_m2x_options/static/description/index.html b/web_m2x_options/static/description/index.html new file mode 100644 index 00000000..bf406ab1 --- /dev/null +++ b/web_m2x_options/static/description/index.html @@ -0,0 +1,412 @@ + + + + + + +Add new options for many2one field + + + +
+

Add new options for many2one field

+ +
+

Description

+

This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display +control options.

+

New: support many2manytags widget !

+

New: support global option management with ir.config_parameter !

+

Options provided includes possibility to remove "Create..." and/or "Create and +Edit..." entries from many2one drop down. You can also change default number of +proposition appearing in the drop-down. Or prevent the dialog box poping in +case of validation error.

+

If not specified, the module will avoid proposing any of the create options +if the current user have no permission rights to create the related object.

+
+
+

Requirements

+

Was tested on openerp 8.0, trunk, saas-5 branch. New way to import js file. (thanks to tfossoul)

+
+
+

New options

+

create boolean (Default: depends if user have create rights)

+
+Whether to display the "Create..." entry in dropdown panel.
+

create_edit boolean (Default: depends if user have create rights)

+
+Whether to display "Create and Edit..." entry in dropdown panel
+

m2o_dialog boolean (Default: depends if user have create rights)

+
+Whether to display the many2one dialog in case of validation error.
+

limit int (Default: openerp default value is 7)

+
+Number of displayed record in drop-down panel
+
+
+

ir.config_parameter options

+

Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. +If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition.

+

web_m2x_options.create boolean (Default: depends if user have create rights)

+
+Whether to display the "Create..." entry in dropdown panel for all fields in the odoo instance.
+

web_m2x_options.create_edit boolean (Default: depends if user have create rights)

+
+Whether to display "Create and Edit..." entry in dropdown panel for all fields in the odoo instance.
+

web_m2x_options.limit int (Default: openerp default value is 7)

+
+Number of displayed record in drop-down panel for all fields in the odoo instance
+

To add these parameters go to Configuration -> Technical -> Parameters -> System Parameters and add new parameters like:

+
    +
  • web_m2x_options.create: False
  • +
  • web_m2x_options.create_edit: False
  • +
  • web_m2x_options.limit: 10
  • +
+
+
+

Example

+

Your XML form view definition could contain:

+
+...
+<field name="partner_id" options="{'limit': 10, 'create': false, 'create_edit': false}"/>
+...
+
+
+
+

Note

+

Double check that you have no inherited view that remote options you set on a field ! +If nothing work, add a debugger in the first ligne of get_search_result method and enable debug mode in OpenERP. When you write something in a many2one field, javascript debugger should pause. If not verify your installation.

+
+
+ + diff --git a/web_m2x_options/static/src/js/form.js b/web_m2x_options/static/src/js/form.js new file mode 100644 index 00000000..bc4d3beb --- /dev/null +++ b/web_m2x_options/static/src/js/form.js @@ -0,0 +1,341 @@ +/*global openerp, _, $ */ + +openerp.web_m2x_options = function (instance) { + + "use strict"; + + var QWeb = instance.web.qweb, + _t = instance.web._t, + _lt = instance.web._lt; + + var OPTIONS = ['web_m2x_options.create', + 'web_m2x_options.create_edit', + 'web_m2x_options.limit', + 'web_m2x_options.search_more', + 'web_m2x_options.m2o_dialog',]; + + instance.web.form.FieldMany2One = instance.web.form.FieldMany2One.extend({ + + start: function() { + this._super.apply(this, arguments); + return this.get_options(); + }, + + get_options: function() { + var self = this; + if (!_.isUndefined(this.view) && _.isUndefined(this.view.ir_options_loaded)) { + this.view.ir_options_loaded = $.Deferred(); + this.view.ir_options = {}; + (new instance.web.Model("ir.config_parameter")) + .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) + .all().then(function(records) { + _(records).each(function(record) { + self.view.ir_options[record.key] = record.value; + }); + self.view.ir_options_loaded.resolve(); + }); + return this.view.ir_options_loaded; + } + return $.when(); + }, + + is_option_set: function(option) { + if (_.isUndefined(option)) { + return false + } + var is_string = typeof option === 'string' + var is_bool = typeof option === 'boolean' + if (is_string) { + return option === 'true' || option === 'True' + } else if (is_bool) { + return option + } + return false + }, + + show_error_displayer: function () { + if(this.is_option_set(this.options.m2o_dialog) || + _.isUndefined(this.options.m2o_dialog) && this.is_option_set(this.view.ir_options['web_m2x_options.m2o_dialog']) || + this.can_create && _.isUndefined(this.options.m2o_dialog) && _.isUndefined(this.view.ir_options['web_m2x_options.m2o_dialog'])) { + new instance.web.form.M2ODialog(this).open(); + } + }, + + get_search_result: function (search_val) { + var Objects = new instance.web.Model(this.field.relation); + var def = $.Deferred(); + var self = this; + // add options limit used to change number of selections record + // returned. + if (_.isUndefined(this.view)) + return this._super.apply(this, arguments); + if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { + this.limit = parseInt(this.view.ir_options['web_m2x_options.limit']); + } + + if (typeof this.options.limit === 'number') { + this.limit = this.options.limit; + } + + // add options search_more to force enable or disable search_more button + if (this.is_option_set(this.options.search_more) || _.isUndefined(this.options.search_more) && this.is_option_set(self.view.ir_options['web_m2x_options.search_more'])) { + this.search_more = true + } + + // add options field_color and colors to color item(s) depending on field_color value + this.field_color = this.options.field_color + this.colors = this.options.colors + + var dataset = new instance.web.DataSet(this, this.field.relation, + self.build_context()); + var blacklist = this.get_search_blacklist(); + this.last_query = search_val; + + var search_result = this.orderer.add(dataset.name_search( + search_val, + new instance.web.CompoundDomain( + self.build_domain(), [["id", "not in", blacklist]]), + 'ilike', this.limit + 1, + self.build_context())); + + var create_rights; + if (typeof this.options.create === "undefined" || + typeof this.options.create_edit === "undefined") { + create_rights = new instance.web.Model(this.field.relation).call( + "check_access_rights", ["create", false]); + } + + $.when(search_result, create_rights).then(function (data, can_create) { + + self.can_create = can_create; // for ``.show_error_displayer()`` + self.last_search = data; + // possible selections for the m2o + var values = _.map(data, function (x) { + x[1] = x[1].split("\n")[0]; + return { + label: _.str.escapeHTML(x[1]), + value: x[1], + name: x[1], + id: x[0], + }; + }); + + // Search result value colors + + if (self.colors && self.field_color) { + var value_ids = []; + for (var index in values) { + value_ids.push(values[index].id); + } + + // RPC request to get field_color from Objects + Objects.query([self.field_color]) + .filter([['id', 'in', value_ids]]) + .all().done(function (objects) { + for (var index in objects) { + for (var index_value in values) { + if (values[index_value].id == objects[index].id) { + // Find value in values by comparing ids + var value = values[index_value]; + + // Find color with field value as key + var color = self.colors[objects[index][self.field_color]] || 'black'; + value.label = ''+value.label+''; + break; + } + } + } + def.resolve(values); + }); + } + + // search more... if more results that max + + if (values.length > self.limit || self.search_more) { + values = values.slice(0, self.limit); + values.push({ + label: _t("Search More..."), + action: function () { + dataset.name_search( + search_val, self.build_domain(), + 'ilike', false).done(function (data) { + self._search_create_popup("search", data); + }); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + + // quick create + + var raw_result = _(data.result).map(function (x) { + return x[1]; + }); + + if ((_.isUndefined(self.options.create) && _.isUndefined(self.view.ir_options['web_m2x_options.create']) && can_create) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create'] == "True") || + self.options.create) { + + if (search_val.length > 0 && + !_.include(raw_result, search_val)) { + + values.push({ + label: _.str.sprintf( + _t('Create "%s"'), + $('').text(search_val).html()), + action: function () { + self._quick_create(search_val); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + } + + // create... + + if ((_.isUndefined(self.options.create_edit) && _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']) && can_create) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create_edit'] == "True") || + self.options.create_edit) { + + values.push({ + label: _t("Create and Edit..."), + action: function () { + self._search_create_popup( + "form", undefined, + self._create_context(search_val)); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + + // Check if colors specified to wait for RPC + if (!(self.field_color && self.colors)){ + def.resolve(values); + } + }); + + return def; + } + }); + + instance.web.form.FieldMany2ManyTags.include({ + + show_error_displayer: function () { + if ((typeof this.options.m2o_dialog === 'undefined' && this.can_create) || + this.options.m2o_dialog) { + new instance.web.form.M2ODialog(this).open(); + } + }, + + start: function() { + this._super.apply(this, arguments); + return this.get_options(); + }, + + get_options: function() { + var self = this; + if (_.isUndefined(this.view.ir_options_loaded)) { + this.view.ir_options_loaded = $.Deferred(); + this.view.ir_options = {}; + (new instance.web.Model("ir.config_parameter")) + .query(["key", "value"]).filter([['key', 'in', OPTIONS]]) + .all().then(function(records) { + _(records).each(function(record) { + self.view.ir_options[record.key] = record.value; + }); + self.view.ir_options_loaded.resolve(); + }); + } + return this.view.ir_options_loaded; + }, + + /** + * Call this method to search using a string. + */ + + get_search_result: function(search_val) { + var self = this; + + // add options limit used to change number of selections record + // returned. + + if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { + this.limit = parseInt(this.view.ir_options['web_m2x_options.limit']); + } + + if (typeof this.options.limit === 'number') { + this.limit = this.options.limit; + } + + var dataset = new instance.web.DataSet(this, this.field.relation, self.build_context()); + var blacklist = this.get_search_blacklist(); + this.last_query = search_val; + + return this.orderer.add(dataset.name_search( + search_val, new instance.web.CompoundDomain(self.build_domain(), [["id", "not in", blacklist]]), + 'ilike', this.limit + 1, self.build_context())).then(function(data) { + self.last_search = data; + // possible selections for the m2o + var values = _.map(data, function(x) { + x[1] = x[1].split("\n")[0]; + return { + label: _.str.escapeHTML(x[1]), + value: x[1], + name: x[1], + id: x[0], + }; + }); + + // search more... if more results that max + if (values.length > self.limit) { + values = values.slice(0, self.limit); + values.push({ + label: _t("Search More..."), + action: function() { + dataset.name_search(search_val, self.build_domain(), 'ilike', false).done(function(data) { + self._search_create_popup("search", data); + }); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + // quick create + + if ((_.isUndefined(self.options.create) && _.isUndefined(self.view.ir_options['web_m2x_options.create'])) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create'] == 'True') || + self.options.create) { + + var raw_result = _(data.result).map(function(x) {return x[1];}); + if (search_val.length > 0 && !_.include(raw_result, search_val)) { + values.push({ + label: _.str.sprintf(_t('Create "%s"'), + $('').text(search_val).html()), + action: function() { + self._quick_create(search_val); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + } + + // create... + + if ((_.isUndefined(self.options.create_edit === 'undefined') && _.isUndefined(self.view.ir_options['web_m2x_options.create_edit'])) || + (_.isUndefined(self.options.create) && self.view.ir_options['web_m2x_options.create_edit'] == 'True') || + self.options.create_edit) { + + values.push({ + label: _t("Create and Edit..."), + action: function() { + self._search_create_popup("form", undefined, self._create_context(search_val)); + }, + classname: 'oe_m2o_dropdown_option' + }); + } + + return values; + }) + }, + }); +}; + diff --git a/web_m2x_options/views/view.xml b/web_m2x_options/views/view.xml new file mode 100644 index 00000000..9628625b --- /dev/null +++ b/web_m2x_options/views/view.xml @@ -0,0 +1,12 @@ + + + + + + +