mirror of https://github.com/OCA/web.git
[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 readmepull/2434/head
parent
a2662c0b44
commit
1aaae8a352
|
@ -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
|
||||
===============
|
||||
web_m2x_options
|
||||
===============
|
||||
|
||||
==================================
|
||||
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
|
||||
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.
|
||||
|
||||
**Table of contents**
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
|
||||
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
|
||||
---------------------------
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
``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.
|
||||
|
|
|
@ -1 +1 @@
|
|||
# coding: utf-8
|
||||
from . import models
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
from . import ir_config_parameter
|
|
@ -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"])
|
|
@ -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>
|
|
@ -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.
|
|
@ -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.
|
|
@ -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'}}"/>
|
||||
...
|
|
@ -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;
|
||||
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 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');
|
||||
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;
|
||||
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();
|
||||
});
|
||||
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
|
||||
}
|
||||
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
|
||||
this.field_color = this.nodeOptions.field_color;
|
||||
this.colors = this.nodeOptions.colors;
|
||||
|
||||
var dataset = new data.DataSet(this, this.field.relation,
|
||||
self.build_context());
|
||||
var blacklist = this.get_search_blacklist();
|
||||
this.last_query = search_val;
|
||||
var context = this.record.getContext(this.recordParams);
|
||||
var domain = this.record.getDomain(this.recordParams);
|
||||
|
||||
function searcher (domain) {
|
||||
return self.orderer.add(dataset.name_search(
|
||||
search_val,
|
||||
domain,
|
||||
'ilike', self.limit + 1,
|
||||
self.build_context()));
|
||||
var blacklisted_ids = this._getSearchBlacklist();
|
||||
if (blacklisted_ids.length > 0) {
|
||||
domain.push(['id', 'not in', blacklisted_ids]);
|
||||
}
|
||||
|
||||
this._rpc({
|
||||
model: this.field.relation,
|
||||
method: "name_search",
|
||||
kwargs: {
|
||||
name: search_val,
|
||||
args: domain,
|
||||
operator: "ilike",
|
||||
limit: this.limit + 1,
|
||||
context: 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]]);
|
||||
}
|
||||
|
||||
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;
|
||||
}).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',
|
||||
}),
|
||||
|
||||
_onDeleteTag: function (event) {
|
||||
var result = this._super.apply(this, arguments);
|
||||
event.stopPropagation();
|
||||
return result;
|
||||
},
|
||||
|
||||
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 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;
|
||||
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
|
||||
// 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'
|
||||
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);
|
||||
}
|
||||
},
|
||||
|
||||
});
|
||||
})
|
||||
});
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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…
Reference in New Issue