Browse Source

[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/1086/head
ernesto 7 years ago
committed by Pedro M. Baeza
parent
commit
60b2779437
  1. 98
      web_m2x_options/README.rst
  2. 2
      web_m2x_options/__init__.py
  3. 17
      web_m2x_options/__manifest__.py
  4. 1
      web_m2x_options/models/__init__.py
  5. 12
      web_m2x_options/models/ir_config_parameter.py
  6. 11
      web_m2x_options/readme/CONTRIBUTORS.rst
  7. 10
      web_m2x_options/readme/DESCRIPTION.rst
  8. 6
      web_m2x_options/readme/ROADMAP.rst
  9. 88
      web_m2x_options/readme/USAGE.rst
  10. 588
      web_m2x_options/static/src/js/form.js
  11. 2
      web_m2x_options/static/src/xml/base.xml
  12. 15
      web_m2x_options/views/view.xml

98
web_m2x_options/README.rst

@ -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
:alt: License: AGPL-3
==================================
Add new options for many2one field
==================================
Description
-----------
===============
web_m2x_options
===============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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
control options.
@ -20,19 +36,16 @@ 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.
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
**Table of contents**
For further information, please visit:
.. contents::
:local:
* https://www.odoo.com/forum/help-1
Usage
=====
in the field's options dict
---------------------------
~~~~~~~~~~~~~~~~~~~~~~~~~~~
``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)
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.
@ -112,7 +125,7 @@ To add these parameters go to Configuration -> Technical -> Parameters -> System
Example
-------
~~~~~~~
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'}}"/>
...
Known issues
============
Known issues / Roadmap
======================
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.
Roadmap
=======
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.
@ -138,14 +148,23 @@ Bug Tracker
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.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`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**>`_.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`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
=======
Authors
~~~~~~~
* ACSONE SA/NV
* 0k.io
* Tecnativa
Contributors
------------
~~~~~~~~~~~~
* David Coninckx <davconinckx@gmail.com>
* Emanuel Cino <ecino@compassion.ch>
@ -153,20 +172,25 @@ Contributors
* Nicolas JEUDY <nicolas@sudokeys.com>
* Yannick Vaucher <yannick.vaucher@camptocamp.com>
* Zakaria Makrelouf <z.makrelouf@gmail.com>
* Jairo Llopis <jairo.llopis@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com>
* `Tecnativa <https://www.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
:alt: Odoo Community Association
:target: https://odoo-community.org
This module is maintained by the OCA.
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
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.

2
web_m2x_options/__init__.py

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

17
web_m2x_options/__manifest__.py

@ -1,18 +1,23 @@
# -*- coding: utf-8 -*-
{
"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": [
'base',
'web',
],
'data': [
'views/view.xml'
],
'qweb': [
'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,
}

1
web_m2x_options/models/__init__.py

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

12
web_m2x_options/models/ir_config_parameter.py

@ -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"])

11
web_m2x_options/readme/CONTRIBUTORS.rst

@ -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>

10
web_m2x_options/readme/DESCRIPTION.rst

@ -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.

6
web_m2x_options/readme/ROADMAP.rst

@ -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.

88
web_m2x_options/readme/USAGE.rst

@ -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'}}"/>
...

588
web_m2x_options/static/src/js/form.js

@ -7,157 +7,158 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
var core = require('web.core'),
data = require('web.data'),
Dialog = require('web.Dialog'),
Model = require('web.Model'),
form_relational = require('web.form_relational'),
_t = core._t;
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',];
// 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');
view_dialogs = require('web.view_dialogs'),
relational_fields = require('web.relational_fields'),
FieldMany2One = relational_fields.FieldMany2One,
FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags,
rpc = require('web.rpc'),
_t = core._t;
var web_m2x_options = rpc.query({
model: "ir.config_parameter",
method: 'get_web_m2x_options',
});
var M2ODialog = Dialog.extend({
template: "M2ODialog",
init: function(parent) {
this.name = parent.string;
init: function (parent, name, value) {
this.name = name;
this.value = value;
this._super(parent, {
title: _.str.sprintf(_t("Create a %s"), parent.string),
title: _.str.sprintf(_t("Create a %s"), this.name),
size: 'medium',
buttons: [
{text: _t('Create'), classes: 'btn-primary', click: function() {
if (this.$("input").val() !== ''){
this.getParent()._quick_create(this.$("input").val());
this.close();
buttons: [{
text: _t('Create'),
classes: 'btn-primary',
click: function () {
if (this.$("input").val() !== '') {
this.trigger_up('quick_create', {value: this.$('input').val()});
this.close(true);
} else {
e.preventDefault();
this.$("input").focus();
}
}},
{text: _t('Create and edit'), classes: 'btn-primary', close: true, click: function() {
this.getParent()._search_create_popup("form", undefined, this.getParent()._create_context(this.$("input").val()));
}},
{text: _t('Cancel'), close: true}
]
},
}, {
text: _t('Create and edit'),
classes: 'btn-primary',
close: true,
click: function () {
this.trigger_up('search_create_popup', {
view_type: 'form',
value: this.$('input').val(),
});
},
}, {
text: _t('Cancel'),
close: true,
}],
});
},
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(text);
this.$("input").val(this.getParent().$input.val());
start: function () {
this.$("p").text(_.str.sprintf(_t("You are creating a new %s, are you sure it does not exist yet?"), this.name));
this.$("input").val(this.value);
},
/**
* @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({
start: function() {
start: function () {
this._super.apply(this, arguments);
return this.get_options();
},
get_options: function() {
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 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();
if (_.isUndefined(this.ir_options_loaded)) {
this.ir_options_loaded = $.Deferred();
this.ir_options = {};
web_m2x_options.done(function (records) {
_(records).each(function(record) {
self.ir_options[record.key] = record.value;
});
self.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
}
is_option_set: function (option) {
if (_.isUndefined(option))
return false;
if (typeof option === 'string')
return option === 'true' || option === 'True';
if (typeof option === 'boolean')
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 M2ODialog(this).open();
_onInputFocusout: function () {
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']);
if (this.can_create && this.floating && m2o_dialog_opt) {
new M2ODialog(this, this.string, this.$input.val()).open();
}
},
get_search_result: function (search_val) {
var Objects = new Model(this.field.relation);
var def = $.Deferred();
_search: function (search_val) {
var self = this;
var def = $.Deferred();
this.orderer.add(def);
// 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'], 10);
if (!_.isUndefined(this.ir_options['web_m2x_options.limit'])) {
this.limit = parseInt(this.ir_options['web_m2x_options.limit'], 10);
}
if (typeof this.options.limit === 'number') {
this.limit = this.options.limit;
if (typeof this.nodeOptions.limit === 'number') {
this.limit = this.nodeOptions.limit;
}
// 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 data.DataSet(this, this.field.relation,
self.build_context());
var blacklist = this.get_search_blacklist();
this.last_query = search_val;
this.field_color = this.nodeOptions.field_color;
this.colors = this.nodeOptions.colors;
function searcher (domain) {
return self.orderer.add(dataset.name_search(
search_val,
domain,
'ilike', self.limit + 1,
self.build_context()));
}
try {
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]]);
}
var context = this.record.getContext(this.recordParams);
var domain = this.record.getDomain(this.recordParams);
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]);
})();
var blacklisted_ids = this._getSearchBlacklist();
if (blacklisted_ids.length > 0) {
domain.push(['id', 'not in', blacklisted_ids]);
}
$.when(search_result, this.create_rights).then(function (data, can_create) {
self.can_create = can_create; // for ``.show_error_displayer()``
self.last_search = data;
this._rpc({
model: this.field.relation,
method: "name_search",
kwargs: {
name: search_val,
args: domain,
operator: "ilike",
limit: this.limit + 1,
context: context,
}
}).then(function (result) {
// possible selections for the m2o
var values = _.map(data, function (x) {
x[1] = x[1].split("\n")[0];
var values = _.map(result, function (x) {
x[1] = self._getDisplayName(x[1]);
return {
label: _.str.escapeHTML(x[1]),
label: _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
value: x[1],
name: x[1],
id: x[0],
@ -170,94 +171,104 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
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 = '<span style="color:'+color+'">'+value.label+'</span>';
break;
}
}
}
def.resolve(values);
});
self._rpc({
model: self.field.relation,
method: 'search_read',
fields: [self.field_color],
domain: [['id', 'in', value_ids]]
}).then(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 = '<span style="color:' + color + '">' + value.label + '</span>';
break;
}
}
}
def.resolve(values);
})
}
// search more... if more results that max
var can_search_more = (self.options && self.is_option_set(self.options.search_more)),
search_more_undef = _.isUndefined(self.options.search_more) && _.isUndefined(self.view.ir_options['web_m2x_options.search_more']),
search_more = self.is_option_set(self.view.ir_options['web_m2x_options.search_more']);
var can_search_more = (self.nodeOptions && self.is_option_set(self.nodeOptions.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.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.push({
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)) {
if (can_search_more || search_more_undef || search_more) {
values.push({
label: _.str.sprintf(
_t('Create "<strong>%s</strong>"'),
$('<span />').text(search_val).html()),
label: _t("Search More..."),
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_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit),
create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit),
m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']),
m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']);
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))));
if (self.can_create && show_create_edit){
var create_enabled = self.can_create && !self.nodeOptions.no_create;
// quick create
var raw_result = _.map(result, function (x) { return x[1]; });
var quick_create = self.is_option_set(self.nodeOptions.create),
quick_create_undef = _.isUndefined(self.nodeOptions.create),
m2x_create_undef = _.isUndefined(self.ir_options['web_m2x_options.create']),
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({
label: _t("Create and Edit..."),
action: function () {
self._search_create_popup(
"form", undefined,
self._create_context(search_val));
},
classname: 'oe_m2o_dropdown_option o_m2o_dropdown_option'
action: createAndEditAction,
classname: 'o_m2o_dropdown_option',
});
} else if (values.length === 0) {
values.push({
label: _t("No results to show..."),
});
}
// Check if colors specified to wait for RPC
if (!(self.field_color && self.colors)){
if (!(self.field_color && self.colors)) {
def.resolve(values);
}
});
@ -266,173 +277,82 @@ odoo.define('web_m2x_options.web_m2x_options', function (require) {
}
});
form_relational.FieldMany2ManyTags.include({
init: function () {
this.events["click .badge"] = "open_badge";
return this._super.apply(this, arguments);
},
show_error_displayer: function () {
if ((typeof this.options.m2o_dialog === 'undefined' && this.can_create) ||
this.options.m2o_dialog) {
new M2ODialog(this).open();
}
},
FormFieldMany2ManyTags.include({
events: _.extend({}, FormFieldMany2ManyTags.prototype.events, {
'click .badge': '_onOpenBadge',
}),
start: function() {
this._super.apply(this, arguments);
return this.get_options();
_onDeleteTag: function (event) {
var result = this._super.apply(this, arguments);
event.stopPropagation();
return result;
},
get_options: function() {
var self = this;
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
}
is_option_set: function (option) {
if (_.isUndefined(option))
return false;
if (typeof option === 'string')
return option === 'true' || option === 'True';
if (typeof option === 'boolean')
return option;
return false
},
/**
* Call this method to search using a string.
*/
get_search_result: function(search_val) {
_onOpenBadge: function (event) {
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'], 10);
}
if (typeof this.options.limit === 'number') {
this.limit = this.options.limit;
}
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'
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);
if (self.mode === 'readonly') {
event.preventDefault();
event.stopPropagation();
self._rpc({
model: self.field.relation,
method: 'get_formview_action',
args: [[id]],
context: context,
})
.then(function (action) {
self.trigger_up('do_action', {action: action});
});
}
}
// create...
var create_edit = self.is_option_set(self.options.create) || self.is_option_set(self.options.create_edit),
create_edit_undef = _.isUndefined(self.options.create) && _.isUndefined(self.options.create_edit),
m2x_create_edit_undef = _.isUndefined(self.view.ir_options['web_m2x_options.create_edit']),
m2x_create_edit = self.is_option_set(self.view.ir_options['web_m2x_options.create_edit']);
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))));
if (show_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 o_m2o_dropdown_option'
});
else {
$.when(
self._rpc({
model: self.field.relation,
method: 'get_formview_id',
args: [[id]],
context: context,
}),
self._rpc({
model: self.field.relation,
method: 'check_access_rights',
kwargs: {operation: 'write', raise_exception: false}
})
).then(function (view_id, write_access) {
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();
})
}
return values;
})
},
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);
} else if (!no_color_picker) {
self._onOpenColorPicker(event);
}
},
});
})
});

2
web_m2x_options/static/src/xml/base.xml

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

15
web_m2x_options/views/view.xml

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