[MIG] web_m2x_options: Migration to 11.0

web_m2x_options: Fix usage for non-admins

web_m2x_options: Reduce rpc calls

web_m2x_options: Update manifest and readme
pull/1392/head
ernesto 2018-05-28 00:19:27 -04:00 committed by Jeroen Evens
parent d6b64d1550
commit ec487f57db
12 changed files with 461 additions and 385 deletions

View File

@ -1,13 +1,29 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg ===============
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html web_m2x_options
:alt: License: AGPL-3 ===============
================================== .. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Add new options for many2one field !! This file is generated by oca-gen-addon-readme !!
================================== !! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Description .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
----------- :target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/11.0/web_m2x_options
:alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-11-0/web-11-0-web_m2x_options
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/162/11.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display
control options. control options.
@ -20,19 +36,16 @@ case of validation error.
If not specified, the module will avoid proposing any of the create options If not specified, the module will avoid proposing any of the create options
if the current user has no permission rights to create the related object. if the current user has no permission rights to create the related object.
**Table of contents**
.. contents::
:local:
Usage Usage
===== =====
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/162/8.0
For further information, please visit:
* https://www.odoo.com/forum/help-1
in the field's options dict in the field's options dict
--------------------------- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
``create`` *boolean* (Default: depends if user have create rights) ``create`` *boolean* (Default: depends if user have create rights)
@ -77,7 +90,7 @@ in the field's options dict
Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set) Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)
ir.config_parameter options ir.config_parameter options
--------------------------- ~~~~~~~~~~~~~~~~~~~~~~~~~~~
Now you can disable "Create..." and "Create and Edit..." entry for all widgets in the odoo instance. 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. If you disable one option, you can enable it for particular field by setting "create: True" option directly on the field definition.
@ -112,7 +125,7 @@ To add these parameters go to Configuration -> Technical -> Parameters -> System
Example Example
------- ~~~~~~~
Your XML form view definition could contain:: Your XML form view definition could contain::
@ -120,14 +133,11 @@ Your XML form view definition could contain::
<field name="partner_id" options="{'limit': 10, 'create': false, 'create_edit': false, 'search_more':true 'field_color':'state', 'colors':{'active':'green'}}"/> <field name="partner_id" options="{'limit': 10, 'create': false, 'create_edit': false, 'search_more':true 'field_color':'state', 'colors':{'active':'green'}}"/>
... ...
Known issues Known issues / Roadmap
============ ======================
Double check that you have no inherited view that remove ``options`` you set on a field ! Double check that you have no inherited view that remove ``options`` you set on a field !
If nothing works, add a debugger in the first line of ``get_search_result method`` and enable debug mode in Odoo. When you write something in a many2one field, javascript debugger should pause. If not verify your installation. If nothing works, add a debugger in the first line of ``_search method`` and enable debug mode in Odoo. When you write something in a many2one field, javascript debugger should pause. If not verify your installation.
Roadmap
=======
- Instead of making the tags rectangle clickable, I think it's better to put the text as a clickable link, so we will get a consistent behaviour/aspect with other clickable elements (many2one...). - Instead of making the tags rectangle clickable, I think it's better to put the text as a clickable link, so we will get a consistent behaviour/aspect with other clickable elements (many2one...).
- In edit mode, it would be great to add an icon like the one on many2one fields to allow to open the many2many in a popup window. - In edit mode, it would be great to add an icon like the one on many2one fields to allow to open the many2many in a popup window.
@ -138,14 +148,23 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_. Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/issues>`_.
In case of trouble, please check there if your issue has already been reported. In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback If you spotted it first, help us smashing it by providing a detailed and welcomed
`here <https://github.com/OCA/web/issues/new?body=module:%20web_m2x_options%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_. `feedback <https://github.com/OCA/web/issues/new?body=module:%20web_m2x_options%0Aversion:%2011.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits Credits
======= =======
Authors
~~~~~~~
* ACSONE SA/NV
* 0k.io
* Tecnativa
Contributors Contributors
------------ ~~~~~~~~~~~~
* David Coninckx <davconinckx@gmail.com> * David Coninckx <davconinckx@gmail.com>
* Emanuel Cino <ecino@compassion.ch> * Emanuel Cino <ecino@compassion.ch>
@ -153,20 +172,25 @@ Contributors
* Nicolas JEUDY <nicolas@sudokeys.com> * Nicolas JEUDY <nicolas@sudokeys.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com> * Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Zakaria Makrelouf <z.makrelouf@gmail.com> * Zakaria Makrelouf <z.makrelouf@gmail.com>
* Jairo Llopis <jairo.llopis@tecnativa.com> * `Tecnativa <https://www.tecnativa.com>`_:
* David Vidal <david.vidal@tecnativa.com>
Maintainer * Jairo Llopis <jairo.llopis@tecnativa.com>
---------- * David Vidal <david.vidal@tecnativa.com>
* Ernesto Tejeda <ernesto.tejeda87@gmail.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png .. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association :alt: Odoo Community Association
:target: https://odoo-community.org :target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and mission is to support the collaborative development of Odoo features and
promote its widespread use. promote its widespread use.
To contribute to this module, please visit http://odoo-community.org. This module is part of the `OCA/web <https://github.com/OCA/web/tree/11.0/web_m2x_options>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@ -1 +1 @@
# coding: utf-8 from . import models

View File

@ -1,18 +1,23 @@
# -*- coding: utf-8 -*-
{ {
"name": 'web_m2x_options', "name": 'web_m2x_options',
"version": "10.0.1.1.0", "version": "11.0.1.0.0",
'category': 'Web',
"author": "ACSONE SA/NV, "
"0k.io, "
"Tecnativa, "
"Odoo Community Association (OCA)",
'website': 'https://github.com/OCA/web',
'license': 'AGPL-3',
"depends": [ "depends": [
'base', 'base',
'web', 'web',
], ],
'data': [
'views/view.xml'
],
'qweb': [ 'qweb': [
'static/src/xml/base.xml', 'static/src/xml/base.xml',
], ],
'license': 'AGPL-3',
'data': ['views/view.xml'],
"author": "ACSONE SA/NV, 0k.io, Tecnativa, "
"Odoo Community Association (OCA)",
'installable': True, 'installable': True,
} }

View File

@ -0,0 +1 @@
from . import ir_config_parameter

View File

@ -0,0 +1,12 @@
from odoo import api, models
class IrConfigParameter(models.Model):
_inherit = 'ir.config_parameter'
@api.model
def get_web_m2x_options(self):
opts = ['web_m2x_options.create', 'web_m2x_options.create_edit',
'web_m2x_options.limit', 'web_m2x_options.search_more',
'web_m2x_options.m2o_dialog']
return self.sudo().search_read([['key', 'in', opts]], ["key", "value"])

View File

@ -0,0 +1,11 @@
* David Coninckx <davconinckx@gmail.com>
* Emanuel Cino <ecino@compassion.ch>
* Holger Brunn <hbrunn@therp.nl>
* Nicolas JEUDY <nicolas@sudokeys.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Zakaria Makrelouf <z.makrelouf@gmail.com>
* `Tecnativa <https://www.tecnativa.com>`_:
* Jairo Llopis <jairo.llopis@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com>
* Ernesto Tejeda <ernesto.tejeda87@gmail.com>

View File

@ -0,0 +1,10 @@
This modules modifies "many2one" and "many2manytags" form widgets so as to add some new display
control options.
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 has no permission rights to create the related object.

View File

@ -0,0 +1,6 @@
Double check that you have no inherited view that remove ``options`` you set on a field !
If nothing works, add a debugger in the first line of ``_search method`` and enable debug mode in Odoo. When you write something in a many2one field, javascript debugger should pause. If not verify your installation.
- Instead of making the tags rectangle clickable, I think it's better to put the text as a clickable link, so we will get a consistent behaviour/aspect with other clickable elements (many2one...).
- In edit mode, it would be great to add an icon like the one on many2one fields to allow to open the many2many in a popup window.
- Include this feature as a configurable option via parameter to have this behaviour by default in all many2many tags.

View File

@ -0,0 +1,88 @@
in the field's options dict
~~~~~~~~~~~~~~~~~~~~~~~~~~~
``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.
``no_open_edit`` *boolean* (Default: value of ``no_open`` which is ``False`` if not set)
Causes a many2one not to offer to click through in edit mode, but well in read mode
``open`` *boolean* (Default: ``False``)
Makes many2many_tags buttons that open the linked resource
``no_color_picker`` *boolean* (Default: ``False``)
Deactivates the color picker on many2many_tags buttons to do nothing (ignored if open is set)
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::
...
<field name="partner_id" options="{'limit': 10, 'create': false, 'create_edit': false, 'search_more':true 'field_color':'state', 'colors':{'active':'green'}}"/>
...

View File

@ -7,157 +7,158 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
var core = require('web.core'), var core = require('web.core'),
data = require('web.data'), data = require('web.data'),
Dialog = require('web.Dialog'), Dialog = require('web.Dialog'),
Model = require('web.Model'), view_dialogs = require('web.view_dialogs'),
form_relational = require('web.form_relational'), relational_fields = require('web.relational_fields'),
_t = core._t; FieldMany2One = relational_fields.FieldMany2One,
FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags,
rpc = require('web.rpc'),
_t = core._t;
var OPTIONS = ['web_m2x_options.create', var web_m2x_options = rpc.query({
'web_m2x_options.create_edit', model: "ir.config_parameter",
'web_m2x_options.limit', method: 'get_web_m2x_options',
'web_m2x_options.search_more', });
'web_m2x_options.m2o_dialog',];
// In odoo 9.c FielMany2One is not exposed by form_relational
// To bypass this limitation we use the widget registry to get the
// reference to the FielMany2One widget.
var FieldMany2One = core.form_widget_registry.get('many2one');
var M2ODialog = Dialog.extend({ var M2ODialog = Dialog.extend({
template: "M2ODialog", template: "M2ODialog",
init: function(parent) { init: function (parent, name, value) {
this.name = parent.string; this.name = name;
this.value = value;
this._super(parent, { this._super(parent, {
title: _.str.sprintf(_t("Create a %s"), parent.string), title: _.str.sprintf(_t("Create a %s"), this.name),
size: 'medium', size: 'medium',
buttons: [ buttons: [{
{text: _t('Create'), classes: 'btn-primary', click: function() { text: _t('Create'),
if (this.$("input").val() !== ''){ classes: 'btn-primary',
this.getParent()._quick_create(this.$("input").val()); click: function () {
this.close(); if (this.$("input").val() !== '') {
this.trigger_up('quick_create', {value: this.$('input').val()});
this.close(true);
} else { } else {
e.preventDefault();
this.$("input").focus(); this.$("input").focus();
} }
}}, },
}, {
{text: _t('Create and edit'), classes: 'btn-primary', close: true, click: function() { text: _t('Create and edit'),
this.getParent()._search_create_popup("form", undefined, this.getParent()._create_context(this.$("input").val())); classes: 'btn-primary',
}}, close: true,
click: function () {
{text: _t('Cancel'), close: true} this.trigger_up('search_create_popup', {
] view_type: 'form',
value: this.$('input').val(),
});
},
}, {
text: _t('Cancel'),
close: true,
}],
}); });
}, },
start: function() { start: function () {
var text = _.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name); this.$("p").text(_.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name));
this.$("p").text(text); this.$("input").val(this.value);
this.$("input").val(this.getParent().$input.val()); },
/**
* @override
* @param {boolean} isSet
*/
close: function (isSet) {
this.isSet = isSet;
this._super.apply(this, arguments);
},
/**
* @override
*/
destroy: function () {
if (!this.isSet) {
this.trigger_up('closed_unset');
}
this._super.apply(this, arguments);
}, },
}); });
FieldMany2One.include({ FieldMany2One.include({
start: function() { start: function () {
this._super.apply(this, arguments); this._super.apply(this, arguments);
return this.get_options(); return this.get_options();
}, },
get_options: function() { get_options: function () {
var self = this; var self = this;
if (!_.isUndefined(this.view) && _.isUndefined(this.view.ir_options_loaded)) { if (_.isUndefined(this.ir_options_loaded)) {
this.view.ir_options_loaded = $.Deferred(); this.ir_options_loaded = $.Deferred();
this.view.ir_options = {}; this.ir_options = {};
(new Model("ir.config_parameter")) web_m2x_options.done(function (records) {
.query(["key", "value"]).filter([['key', 'in', OPTIONS]]) _(records).each(function(record) {
.all().then(function(records) { self.ir_options[record.key] = record.value;
_(records).each(function(record) { });
self.view.ir_options[record.key] = record.value; self.ir_options_loaded.resolve();
}); });
self.view.ir_options_loaded.resolve();
});
return this.view.ir_options_loaded;
} }
return $.when(); return $.when();
}, },
is_option_set: function(option) { is_option_set: function (option) {
if (_.isUndefined(option)) { if (_.isUndefined(option))
return false return false;
} if (typeof option === 'string')
var is_string = typeof option === 'string' return option === 'true' || option === 'True';
var is_bool = typeof option === 'boolean' if (typeof option === 'boolean')
if (is_string) { return option;
return option === 'true' || option === 'True'
} else if (is_bool) {
return option
}
return false return false
}, },
show_error_displayer: function () { _onInputFocusout: function () {
if(this.is_option_set(this.options.m2o_dialog) || var m2o_dialog_opt = this.is_option_set(this.nodeOptions.m2o_dialog) || _.isUndefined(this.nodeOptions.m2o_dialog) && this.is_option_set(this.ir_options['web_m2x_options.m2o_dialog']) || _.isUndefined(this.nodeOptions.m2o_dialog) && _.isUndefined(this.ir_options['web_m2x_options.m2o_dialog']);
_.isUndefined(this.options.m2o_dialog) && this.is_option_set(this.view.ir_options['web_m2x_options.m2o_dialog']) || if (this.can_create && this.floating && m2o_dialog_opt) {
this.can_create && _.isUndefined(this.options.m2o_dialog) && _.isUndefined(this.view.ir_options['web_m2x_options.m2o_dialog'])) { new M2ODialog(this, this.string, this.$input.val()).open();
new M2ODialog(this).open();
} }
}, },
get_search_result: function (search_val) { _search: function (search_val) {
var Objects = new Model(this.field.relation);
var def = $.Deferred();
var self = this; var self = this;
var def = $.Deferred();
this.orderer.add(def);
// add options limit used to change number of selections record // add options limit used to change number of selections record
// returned. // returned.
if (_.isUndefined(this.view)) if (!_.isUndefined(this.ir_options['web_m2x_options.limit'])) {
return this._super.apply(this, arguments); this.limit = parseInt(this.ir_options['web_m2x_options.limit'], 10);
if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) {
this.limit = parseInt(this.view.ir_options['web_m2x_options.limit'], 10);
} }
if (typeof this.options.limit === 'number') { if (typeof this.nodeOptions.limit === 'number') {
this.limit = this.options.limit; this.limit = this.nodeOptions.limit;
} }
// add options field_color and colors to color item(s) depending on field_color value // add options field_color and colors to color item(s) depending on field_color value
this.field_color = this.options.field_color this.field_color = this.nodeOptions.field_color;
this.colors = this.options.colors this.colors = this.nodeOptions.colors;
var dataset = new data.DataSet(this, this.field.relation, var context = this.record.getContext(this.recordParams);
self.build_context()); var domain = this.record.getDomain(this.recordParams);
var blacklist = this.get_search_blacklist();
this.last_query = search_val;
function searcher (domain) { var blacklisted_ids = this._getSearchBlacklist();
return self.orderer.add(dataset.name_search( if (blacklisted_ids.length > 0) {
search_val, domain.push(['id', 'not in', blacklisted_ids]);
domain, }
'ilike', self.limit + 1,
self.build_context())); this._rpc({
model: this.field.relation,
method: "name_search",
kwargs: {
name: search_val,
args: domain,
operator: "ilike",
limit: this.limit + 1,
context: context,
} }
try { }).then(function (result) {
var search_result = searcher(new data.CompoundDomain(
self.build_domain(), [["id", "not in", blacklist]]));
// In search views sometimes the field domain cannot be evaluated
} catch (error) {
var search_result = searcher([["id", "not in", blacklist]]);
}
if (!(self.options && (self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit)))) {
this.create_rights = this.create_rights || (function(){
return new Model(self.field.relation).call(
"check_access_rights", ["create", false]);
})();
}
$.when(search_result, this.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 // possible selections for the m2o
var values = _.map(data, function (x) { var values = _.map(result, function (x) {
x[1] = x[1].split("\n")[0]; x[1] = self._getDisplayName(x[1]);
return { return {
label: _.str.escapeHTML(x[1]), label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
value: x[1], value: x[1],
name: x[1], name: x[1],
id: x[0], id: x[0],
@ -170,94 +171,104 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
for (var index in values) { for (var index in values) {
value_ids.push(values[index].id); value_ids.push(values[index].id);
} }
// RPC request to get field_color from Objects self._rpc({
Objects.query([self.field_color]) model: self.field.relation,
.filter([['id', 'in', value_ids]]) method: 'search_read',
.all().done(function (objects) { fields: [self.field_color],
for (var index in objects) { domain: [['id', 'in', value_ids]]
for (var index_value in values) { }).then(function (objects) {
if (values[index_value].id == objects[index].id) { for (var index in objects) {
// Find value in values by comparing ids for (var index_value in values) {
var value = values[index_value]; if (values[index_value].id == objects[index].id) {
// Find color with field value as key // Find value in values by comparing ids
var color = self.colors[objects[index][self.field_color]] || 'black'; var value = values[index_value];
value.label = '<span style="color:'+color+'">'+value.label+'</span>'; // Find color with field value as key
break; var color = self.colors[objects[index][self.field_color]] || 'black';
} value.label = '<span style="color:' + color + '">' + value.label + '</span>';
} break;
} }
def.resolve(values); }
}); }
def.resolve(values);
})
} }
// search more... if more results that max // search more... if more results that max
var can_search_more = (self.options && self.is_option_set(self.options.search_more)), var can_search_more = (self.nodeOptions && self.is_option_set(self.nodeOptions.search_more)),
search_more_undef = _.isUndefined(self.options.search_more) && _.isUndefined(self.view.ir_options['web_m2x_options.search_more']), search_more_undef = _.isUndefined(self.nodeOptions.search_more) && _.isUndefined(self.ir_options['web_m2x_options.search_more']),
search_more = self.is_option_set(self.view.ir_options['web_m2x_options.search_more']); search_more = self.is_option_set(self.ir_options['web_m2x_options.search_more']);
if (values.length > self.limit && (can_search_more || search_more_undef || search_more)) { if (values.length > self.limit) {
values = values.slice(0, self.limit); values = values.slice(0, self.limit);
values.push({ if (can_search_more || search_more_undef || search_more) {
label: _t("Search More..."),
action: function () {
// limit = 160 for improving performance, similar
// to Odoo implementation here:
// https://github.com/odoo/odoo/blob/feeac2a4f1cd777770dd2b42534904ac71f23e46/addons/web/static/src/js/views/form_common.js#L213
dataset.name_search(
search_val, self.build_domain(),
'ilike', 160).done(function (data) {
self._search_create_popup("search", data);
});
},
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option'
});
}
// quick create
var raw_result = _(data.result).map(function (x) {
return x[1];
});
var quick_create = self.is_option_set(self.options.create) || self.is_option_set(self.options.quick_create),
quick_create_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.quick_create),
m2x_create_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create']),
m2x_create = self.is_option_set(self.view.ir_options['web_m2x_options.create']);
var show_create = (!self.options && (m2x_create_undef || m2x_create)) || (self.options && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create))));
if (self.can_create && show_create){
if (search_val.length > 0 &&
!_.include(raw_result, search_val)) {
values.push({ values.push({
label: _.str.sprintf( label: _t("Search More..."),
_t('Create "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
action: function () { action: function () {
self._quick_create(search_val); // limit = 80 for improving performance, similar
// to Odoo implementation here:
// https://github.com/odoo/odoo/commit/8c3cdce539d87775b59b3f2d5ceb433f995821bf
self._rpc({
model: self.field.relation,
method: 'name_search',
kwargs: {
name: search_val,
args: domain,
operator: "ilike",
limit: 80,
context: context,
},
})
.then(self._searchCreatePopup.bind(self, "search"));
}, },
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' classname: 'o_m2o_dropdown_option',
}); });
} }
} }
// create... var create_enabled = self.can_create && !self.nodeOptions.no_create;
var create_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit), // quick create
create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit), var raw_result = _.map(result, function (x) { return x[1]; });
m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']), var quick_create = self.is_option_set(self.nodeOptions.create),
m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']); quick_create_undef = _.isUndefined(self.nodeOptions.create),
var show_create_edit = (!self.options && (m2x_create_edit_undef || m2x_create_edit)) || (self.options && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); m2x_create_undef = _.isUndefined(self.ir_options['web_m2x_options.create']),
if (self.can_create && show_create_edit){ m2x_create = self.is_option_set(self.ir_options['web_m2x_options.create']);
var show_create = (!self.nodeOptions && (m2x_create_undef || m2x_create)) || (self.nodeOptions && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create))));
if (create_enabled && !self.nodeOptions.no_quick_create &&
search_val.length > 0 && !_.contains(raw_result, search_val) &&
show_create) {
values.push({
label: _.str.sprintf(_t('Create "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
action: self._quickCreate.bind(self, search_val),
classname: 'o_m2o_dropdown_option'
});
}
// create and edit ...
var create_edit = self.is_option_set(self.nodeOptions.create) || self.is_option_set(self.nodeOptions.create_edit),
create_edit_undef = _.isUndefined(self.nodeOptions.create) && _.isUndefined(self.nodeOptions.create_edit),
m2x_create_edit_undef = _.isUndefined(self.ir_options['web_m2x_options.create_edit']),
m2x_create_edit = self.is_option_set(self.ir_options['web_m2x_options.create_edit']);
var show_create_edit = (!self.nodeOptions && (m2x_create_edit_undef || m2x_create_edit)) || (self.nodeOptions && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit))));
if (create_enabled && !self.nodeOptions.no_create_edit && show_create_edit) {
var createAndEditAction = function () {
// Clear the value in case the user clicks on discard
self.$('input').val('');
return self._searchCreatePopup("form", false, self._createContext(search_val));
};
values.push({ values.push({
label: _t("Create and Edit..."), label: _t("Create and Edit..."),
action: function () { action: createAndEditAction,
self._search_create_popup( classname: 'o_m2o_dropdown_option',
"form", undefined, });
self._create_context(search_val)); } else if (values.length === 0) {
}, values.push({
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' label: _t("No results to show..."),
}); });
} }
// Check if colors specified to wait for RPC // Check if colors specified to wait for RPC
if (!(self.field_color && self.colors)){ if (!(self.field_color && self.colors)) {
def.resolve(values); def.resolve(values);
} }
}); });
@ -266,173 +277,82 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
} }
}); });
form_relational.FieldMany2ManyTags.include({ FormFieldMany2ManyTags.include({
init: function () { events: _.extend({}, FormFieldMany2ManyTags.prototype.events, {
this.events["click .badge"] = "open_badge"; 'click .badge': '_onOpenBadge',
return this._super.apply(this, arguments); }),
},
show_error_displayer: function () { _onDeleteTag: function (event) {
if ((typeof this.options.m2o_dialog === 'undefined' && this.can_create) || var result = this._super.apply(this, arguments);
this.options.m2o_dialog) { event.stopPropagation();
new M2ODialog(this).open(); return result;
}
}, },
start: function() { is_option_set: function (option) {
this._super.apply(this, arguments); if (_.isUndefined(option))
return this.get_options(); return false;
}, if (typeof option === 'string')
return option === 'true' || option === 'True';
get_options: function() { if (typeof option === 'boolean')
var self = this; return option;
if (_.isUndefined(this.view.ir_options_loaded)) {
this.view.ir_options_loaded = $.Deferred();
this.view.ir_options = {};
(new 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;
},
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 return false
}, },
/** _onOpenBadge: function (event) {
* Call this method to search using a string.
*/
get_search_result: function(search_val) {
var self = this; var self = this;
var open = (self.nodeOptions && self.is_option_set(self.nodeOptions.open));
var no_color_picker = (self.nodeOptions && self.is_option_set(self.nodeOptions.no_color_picker));
if (open) {
var context = self.record.getContext(self.recordParams);
var id = parseInt($(event.currentTarget).data('id'), 10);
// add options limit used to change number of selections record if (self.mode === 'readonly') {
// returned. event.preventDefault();
event.stopPropagation();
if (!_.isUndefined(this.view.ir_options['web_m2x_options.limit'])) { self._rpc({
this.limit = parseInt(this.view.ir_options['web_m2x_options.limit'], 10); model: self.field.relation,
} method: 'get_formview_action',
args: [[id]],
if (typeof this.options.limit === 'number') { context: context,
this.limit = this.options.limit; })
} .then(function (action) {
self.trigger_up('do_action', {action: action});
var dataset = new data.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 data.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() {
// limit = 80 for improving performance, similar
// to Odoo implementation here:
// https://github.com/odoo/odoo/commit/8c3cdce539d87775b59b3f2d5ceb433f995821bf
dataset.name_search(search_val, self.build_domain(), 'ilike', 80).done(function(data) {
self._search_create_popup("search", data);
});
},
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option'
});
}
// quick create
var quick_create = self.is_option_set(self.options.create) || self.is_option_set(self.options.quick_create),
quick_create_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.quick_create),
m2x_create_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create']),
m2x_create = self.is_option_set(self.view.ir_options['web_m2x_options.create']);
var show_create = (!self.options && (m2x_create_undef || m2x_create)) || (self.options && (quick_create || (quick_create_undef && (m2x_create_undef || m2x_create))));
if (show_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 "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
action: function() {
self._quick_create(search_val);
},
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option'
}); });
}
} }
else {
// create... $.when(
var create_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit), self._rpc({
create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit), model: self.field.relation,
m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']), method: 'get_formview_id',
m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']); args: [[id]],
var show_create_edit = (!self.options && (m2x_create_edit_undef || m2x_create_edit)) || (self.options && (create_edit || (create_edit_undef && (m2x_create_edit_undef || m2x_create_edit)))); context: context,
if (show_create_edit){ }),
self._rpc({
values.push({ model: self.field.relation,
label: _t("Create and Edit..."), method: 'check_access_rights',
action: function() { kwargs: {operation: 'write', raise_exception: false}
self._search_create_popup("form", undefined, self._create_context(search_val)); })
}, ).then(function (view_id, write_access) {
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option' var can_write = 'can_write' in self.attrs ? JSON.parse(self.attrs.can_write) : true;
}); new view_dialogs.FormViewDialog(self, {
res_model: self.field.relation,
res_id: id,
context: context,
title: _t("Open: ") + self.string,
view_id: view_id,
readonly: !can_write || !write_access,
on_saved: function (record, changed) {
if (changed) {
self._setValue(self.value.data, {forceChange: true});
self.trigger_up('reload', {db_id: self.value.id});
}
},
}).open();
})
} }
} else if (!no_color_picker) {
return values; self._onOpenColorPicker(event);
})
},
open_badge: function(ev){
var self = this;
var open = (self.options && self.is_option_set(self.options.open));
var no_color_picker = (self.options && self.is_option_set(self.options.no_color_picker));
if(open){
self.mutex.exec(function(){
var id = parseInt($(ev.currentTarget).data('id'), 10);
self.do_action({
type: 'ir.actions.act_window',
res_model: self.field.relation,
views: [[false, 'form']],
res_id: id,
target: "new"
});
}.bind(this));
}else if(no_color_picker){
self.mutex.exec(function(){
return
}.bind(this));
}else{
self.open_color_picker(ev);
} }
}, },
})
});
}); });

View File

@ -6,7 +6,7 @@
<t t-extend="FieldMany2One"> <t t-extend="FieldMany2One">
<t t-jquery=".o_external_button" t-operation="attributes"> <t t-jquery=".o_external_button" t-operation="attributes">
<attribute name="t-if"> <attribute name="t-if">
!(widget.options.no_open || widget.options.no_open_edit) !(widget.nodeOptions.no_open || widget.nodeOptions.no_open_edit)
</attribute> </attribute>
</t> </t>
</t> </t>

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<template id="assets_backend" name="m2x options assets" <template id="assets_backend" name="m2x options assets"
inherit_id="web.assets_backend"> inherit_id="web.assets_backend">
<xpath expr="." position="inside"> <xpath expr="." position="inside">
<script type="text/javascript" <script type="text/javascript"
src="/web_m2x_options/static/src/js/form.js"></script> src="/web_m2x_options/static/src/js/form.js"></script>
</xpath> </xpath>
</template> </template>
</odoo> </odoo>