[FIX] web_m2x_options: Scroll dance

pull/2208/head
Alexandre D. Díaz 2022-05-05 11:07:26 +02:00
parent 09cec9e675
commit d83b0baad7
3 changed files with 175 additions and 147 deletions

View File

@ -177,6 +177,7 @@ Contributors
* Jairo Llopis <jairo.llopis@tecnativa.com> * Jairo Llopis <jairo.llopis@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com> * David Vidal <david.vidal@tecnativa.com>
* Ernesto Tejeda <ernesto.tejeda87@gmail.com> * Ernesto Tejeda <ernesto.tejeda87@gmail.com>
* Alexandre D. Díaz <alexandre.diaz@tecnativa.com>
* Bhavesh Odedra <bodedra@opensourceintegrators.com> * Bhavesh Odedra <bodedra@opensourceintegrators.com>
Maintainers Maintainers

View File

@ -9,4 +9,5 @@
* Jairo Llopis <jairo.llopis@tecnativa.com> * Jairo Llopis <jairo.llopis@tecnativa.com>
* David Vidal <david.vidal@tecnativa.com> * David Vidal <david.vidal@tecnativa.com>
* Ernesto Tejeda <ernesto.tejeda87@gmail.com> * Ernesto Tejeda <ernesto.tejeda87@gmail.com>
* Alexandre D. Díaz <alexandre.diaz@tecnativa.com>
* Bhavesh Odedra <bodedra@opensourceintegrators.com> * Bhavesh Odedra <bodedra@opensourceintegrators.com>

View File

@ -1,28 +1,34 @@
/* Copyright 2016 0k.io,ACSONE SA/NV /* Copyright 2016 0k.io,ACSONE SA/NV
* * License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */ * Copyright 2022 Tecnativa - Alexandre D. Díaz
* License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). */
odoo.define("web_m2x_options.web_m2x_options", function(require) { odoo.define("web_m2x_options.web_m2x_options", function(require) {
"use strict"; "use strict";
var core = require("web.core"), const core = require("web.core"),
data = require("web.data"), data = require("web.data"),
Dialog = require("web.Dialog"), Dialog = require("web.Dialog"),
view_dialogs = require("web.view_dialogs"), view_dialogs = require("web.view_dialogs"),
relational_fields = require("web.relational_fields"), relational_fields = require("web.relational_fields"),
rpc = require("web.rpc"); rpc = require("web.rpc");
var _t = core._t, const _t = core._t,
FieldMany2ManyTags = relational_fields.FieldMany2ManyTags, FieldMany2ManyTags = relational_fields.FieldMany2ManyTags,
FieldMany2One = relational_fields.FieldMany2One, FieldMany2One = relational_fields.FieldMany2One,
FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags; FormFieldMany2ManyTags = relational_fields.FormFieldMany2ManyTags;
var web_m2x_options = rpc.query({ // Launch the query ASAP
const web_m2x_options = rpc.query({
model: "ir.config_parameter", model: "ir.config_parameter",
method: "get_web_m2x_options", method: "get_web_m2x_options",
}); });
var M2ODialog = Dialog.extend({ /**
* Cloned from web/static/src/js/fields/relational_fields.js
*/
const M2ODialog = Dialog.extend({
template: "M2ODialog", template: "M2ODialog",
init: function(parent, name, value) { init: function(parent, name, value) {
this.name = name; this.name = name;
this.value = value; this.value = value;
@ -62,6 +68,7 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
], ],
}); });
}, },
start: function() { start: function() {
this.$("p").text( this.$("p").text(
_.str.sprintf( _.str.sprintf(
@ -93,36 +100,53 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
}); });
FieldMany2One.include({ FieldMany2One.include({
start: function() { /**
this._super.apply(this, arguments); * @override
return this.get_options(); */
init: function() {
this.ir_options = {};
return this._super.apply(this, arguments);
}, },
/**
* @override
*/
start: function() {
return this._super.apply(this, arguments).then(() => this.get_options());
},
/**
* @returns {Promise}
*/
get_options: function() { get_options: function() {
var self = this; if (_.isEmpty(this.ir_options)) {
if (_.isUndefined(this.ir_options_loaded)) { return web_m2x_options.then(records => {
this.ir_options_loaded = $.Deferred(); for (const record of records) {
this.ir_options = {}; this.ir_options[record.key] = record.value;
web_m2x_options.then(function(records) { }
_(records).each(function(record) {
self.ir_options[record.key] = record.value;
});
self.ir_options_loaded.resolve();
}); });
} }
return $.when(); return Promise.resolve();
}, },
/**
* @param {String/Boolean/Undefined} option
* @returns {Boolean}
*/
is_option_set: function(option) { is_option_set: function(option) {
if (_.isUndefined(option)) return false; if (typeof option === "string") {
if (typeof option === "string") return option.toLowerCase() === "true";
return option === "true" || option === "True"; } else if (typeof option === "boolean") {
if (typeof option === "boolean") return option; return option;
}
return false; return false;
}, },
/**
* @override
*/
_onInputFocusout: function() { _onInputFocusout: function() {
var m2o_dialog_opt = const m2o_dialog_opt =
this.is_option_set(this.nodeOptions.m2o_dialog) || this.is_option_set(this.nodeOptions.m2o_dialog) ||
(_.isUndefined(this.nodeOptions.m2o_dialog) && (_.isUndefined(this.nodeOptions.m2o_dialog) &&
this.is_option_set( this.is_option_set(
@ -135,48 +159,50 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
} }
}, },
/**
* @override
*/
_search: function(search_val) { _search: function(search_val) {
var self = this; if (!search_val) {
if (search_val === undefined) {
return this._super.apply(this, arguments); return this._super.apply(this, arguments);
} }
var def = new Promise(resolve => { const def = new Promise(resolve => {
// Add options limit used to change number of selections record // Add options limit used to change number of selections record
// returned. // returned.
if (!_.isUndefined(self.ir_options["web_m2x_options.limit"])) { if (!_.isUndefined(this.ir_options["web_m2x_options.limit"])) {
this.limit = parseInt(self.ir_options["web_m2x_options.limit"], 10); this.limit = parseInt(this.ir_options["web_m2x_options.limit"], 10);
} }
if (typeof self.nodeOptions.limit === "number") { if (typeof this.nodeOptions.limit === "number") {
self.limit = self.nodeOptions.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
self.field_color = self.nodeOptions.field_color; this.field_color = this.nodeOptions.field_color;
self.colors = self.nodeOptions.colors; this.colors = this.nodeOptions.colors;
var context = self.record.getContext(self.recordParams); const context = this.record.getContext(this.recordParams);
var domain = self.record.getDomain(self.recordParams); const domain = this.record.getDomain(this.recordParams);
var blacklisted_ids = self._getSearchBlacklist(); const blacklisted_ids = this._getSearchBlacklist();
if (blacklisted_ids.length > 0) { if (blacklisted_ids.length > 0) {
domain.push(["id", "not in", blacklisted_ids]); domain.push(["id", "not in", blacklisted_ids]);
} }
self._rpc({ this._rpc({
model: self.field.relation, model: this.field.relation,
method: "name_search", method: "name_search",
kwargs: { kwargs: {
name: search_val, name: search_val,
args: domain, args: domain,
operator: "ilike", operator: "ilike",
limit: self.limit + 1, limit: this.limit + 1,
context: context, context: context,
}, },
}).then(result => { }).then(result => {
// Possible selections for the m2o // Possible selections for the m2o
var values = _.map(result, x => { let values = _.map(result, x => {
x[1] = self._getDisplayName(x[1]); x[1] = this._getDisplayName(x[1]);
return { return {
label: label:
_.str.escapeHTML(x[1].trim()) || data.noDisplayContent, _.str.escapeHTML(x[1].trim()) || data.noDisplayContent,
@ -187,26 +213,23 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
}); });
// Search result value colors // Search result value colors
if (self.colors && self.field_color) { if (this.colors && this.field_color) {
var value_ids = []; const value_ids = _.map(values, value => value.id);
for (var val_index in values) { this._rpc({
value_ids.push(values[val_index].id); model: this.field.relation,
}
self._rpc({
model: self.field.relation,
method: "search_read", method: "search_read",
fields: [self.field_color], fields: [this.field_color],
domain: [["id", "in", value_ids]], domain: [["id", "in", value_ids]],
}).then(objects => { }).then(objects => {
for (var index in objects) { for (const index in objects) {
for (var index_value in values) { for (const index_value in values) {
if (values[index_value].id === objects[index].id) { if (values[index_value].id === objects[index].id) {
// Find value in values by comparing ids // Find value in values by comparing ids
var value = values[index_value]; const value = values[index_value];
// Find color with field value as key // Find color with field value as key
var color = const color =
self.colors[ this.colors[
objects[index][self.field_color] objects[index][this.field_color]
] || "black"; ] || "black";
value.label = value.label =
'<span style="color:' + '<span style="color:' +
@ -223,47 +246,45 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
} }
// Search more... if more results that max // Search more... if more results that max
var can_search_more = const can_search_more =
self.nodeOptions && this.nodeOptions &&
self.is_option_set(self.nodeOptions.search_more), this.is_option_set(this.nodeOptions.search_more),
search_more_undef = search_more_undef =
_.isUndefined(self.nodeOptions.search_more) && _.isUndefined(this.nodeOptions.search_more) &&
_.isUndefined( _.isUndefined(
self.ir_options["web_m2x_options.search_more"] this.ir_options["web_m2x_options.search_more"]
), ),
search_more = self.is_option_set( search_more = this.is_option_set(
self.ir_options["web_m2x_options.search_more"] this.ir_options["web_m2x_options.search_more"]
); );
if ( if (
(values.length > self.limit && (values.length > this.limit &&
(can_search_more || search_more_undef)) || (can_search_more || search_more_undef)) ||
search_more search_more
) { ) {
values = values.slice(0, self.limit); values = values.slice(0, this.limit);
values.push({ values.push({
label: _t("Search More..."), label: _t("Search More..."),
action: function() { action: () => {
var prom = []; let prom = Promise.resolve();
if (search_val !== "") { if (search_val) {
prom = self._rpc({ prom = this._rpc({
model: self.field.relation, model: this.field.relation,
method: "name_search", method: "name_search",
kwargs: { kwargs: {
name: search_val, name: search_val,
args: domain, args: domain,
operator: "ilike", operator: "ilike",
limit: self.SEARCH_MORE_LIMIT, limit: this.SEARCH_MORE_LIMIT,
context: context, context: context,
}, },
}); });
} }
Promise.resolve(prom).then(function(results) { prom.then(results => {
var dynamicFilters = []; let dynamicFilters = [];
if (results) { if (results) {
var ids = _.map(results, function(x) { const ids = _.map(results, x => x[0]);
return x[0];
});
if (search_val) { if (search_val) {
dynamicFilters = [ dynamicFilters = [
{ {
@ -274,11 +295,9 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
domain: [["id", "in", ids]], domain: [["id", "in", ids]],
}, },
]; ];
} else {
dynamicFilters = [];
} }
} }
self._searchCreatePopup( this._searchCreatePopup(
"search", "search",
false, false,
{}, {},
@ -290,28 +309,27 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
}); });
} }
var create_enabled = self.can_create && !self.nodeOptions.no_create; const create_enabled =
this.can_create && !this.nodeOptions.no_create;
// Quick create // Quick create
var raw_result = _.map(result, function(x) { const raw_result = _.map(result, x => x[1]);
return x[1]; const quick_create = this.is_option_set(this.nodeOptions.create),
}); quick_create_undef = _.isUndefined(this.nodeOptions.create),
var quick_create = self.is_option_set(self.nodeOptions.create),
quick_create_undef = _.isUndefined(self.nodeOptions.create),
m2x_create_undef = _.isUndefined( m2x_create_undef = _.isUndefined(
self.ir_options["web_m2x_options.create"] this.ir_options["web_m2x_options.create"]
), ),
m2x_create = self.is_option_set( m2x_create = this.is_option_set(
self.ir_options["web_m2x_options.create"] this.ir_options["web_m2x_options.create"]
); );
var show_create = const show_create =
(!self.nodeOptions && (m2x_create_undef || m2x_create)) || (!this.nodeOptions && (m2x_create_undef || m2x_create)) ||
(self.nodeOptions && (this.nodeOptions &&
(quick_create || (quick_create ||
(quick_create_undef && (quick_create_undef &&
(m2x_create_undef || m2x_create)))); (m2x_create_undef || m2x_create))));
if ( if (
create_enabled && create_enabled &&
!self.nodeOptions.no_quick_create && !this.nodeOptions.no_quick_create &&
search_val.length > 0 && search_val.length > 0 &&
!_.contains(raw_result, search_val) && !_.contains(raw_result, search_val) &&
show_create show_create
@ -323,48 +341,47 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
.text(search_val) .text(search_val)
.html() .html()
), ),
action: self._quickCreate.bind(self, search_val), action: this._quickCreate.bind(this, search_val),
classname: "o_m2o_dropdown_option", classname: "o_m2o_dropdown_option",
}); });
} }
// Create and edit ... // Create and edit ...
var create_edit = const create_edit =
self.is_option_set(self.nodeOptions.create) || this.is_option_set(this.nodeOptions.create) ||
self.is_option_set(self.nodeOptions.create_edit), this.is_option_set(this.nodeOptions.create_edit),
create_edit_undef = create_edit_undef =
_.isUndefined(self.nodeOptions.create) && _.isUndefined(this.nodeOptions.create) &&
_.isUndefined(self.nodeOptions.create_edit), _.isUndefined(this.nodeOptions.create_edit),
m2x_create_edit_undef = _.isUndefined( m2x_create_edit_undef = _.isUndefined(
self.ir_options["web_m2x_options.create_edit"] this.ir_options["web_m2x_options.create_edit"]
), ),
m2x_create_edit = self.is_option_set( m2x_create_edit = this.is_option_set(
self.ir_options["web_m2x_options.create_edit"] this.ir_options["web_m2x_options.create_edit"]
); );
var show_create_edit = const show_create_edit =
(!self.nodeOptions && (!this.nodeOptions &&
(m2x_create_edit_undef || m2x_create_edit)) || (m2x_create_edit_undef || m2x_create_edit)) ||
(self.nodeOptions && (this.nodeOptions &&
(create_edit || (create_edit ||
(create_edit_undef && (create_edit_undef &&
(m2x_create_edit_undef || m2x_create_edit)))); (m2x_create_edit_undef || m2x_create_edit))));
if ( if (
create_enabled && create_enabled &&
!self.nodeOptions.no_create_edit && !this.nodeOptions.no_create_edit &&
show_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: createAndEditAction, action: () => {
// Clear the value in case the user clicks on discard
this.$("input").val("");
return this._searchCreatePopup(
"form",
false,
this._createContext(search_val)
);
},
classname: "o_m2o_dropdown_option", classname: "o_m2o_dropdown_option",
}); });
} else if (values.length === 0) { } else if (values.length === 0) {
@ -373,7 +390,7 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
}); });
} }
// Check if colors specified to wait for RPC // Check if colors specified to wait for RPC
if (!(self.field_color && self.colors)) { if (!(this.field_color && this.colors)) {
resolve(values); resolve(values);
} }
}); });
@ -388,69 +405,77 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
"click .badge": "_onOpenBadge", "click .badge": "_onOpenBadge",
}), }),
/**
* @override
*/
_onDeleteTag: function(event) { _onDeleteTag: function(event) {
var result = this._super.apply(this, arguments); const result = this._super.apply(this, arguments);
event.stopPropagation(); event.stopPropagation();
return result; return result;
}, },
/**
* @param {String/Boolean/Undefined} option
* @returns {Boolean}
*/
is_option_set: function(option) { is_option_set: function(option) {
if (_.isUndefined(option)) return false; if (typeof option === "string") {
if (typeof option === "string") return option.toLowerCase() === "true";
return option === "true" || option === "True"; }
if (typeof option === "boolean") return option; if (typeof option === "boolean") {
return option;
}
return false; return false;
}, },
_onOpenBadge: function(event) { _onOpenBadge: function(event) {
var self = this; const open = this.nodeOptions && this.is_option_set(this.nodeOptions.open);
var open = self.nodeOptions && self.is_option_set(self.nodeOptions.open);
if (open) { if (open) {
var context = self.record.getContext(self.recordParams); const context = this.record.getContext(this.recordParams);
var id = parseInt($(event.currentTarget).data("id"), 10); const id = Number($(event.currentTarget).data("id"));
if (self.mode === "readonly") { if (this.mode === "readonly") {
event.preventDefault(); event.preventDefault();
event.stopPropagation(); event.stopPropagation();
self._rpc({ this._rpc({
model: self.field.relation, model: this.field.relation,
method: "get_formview_action", method: "get_formview_action",
args: [[id]], args: [[id]],
context: context, context: context,
}).then(function(action) { }).then(action => {
self.trigger_up("do_action", {action: action}); this.trigger_up("do_action", {action: action});
}); });
} else { } else {
$.when( Promise.all([
self._rpc({ this._rpc({
model: self.field.relation, model: this.field.relation,
method: "get_formview_id", method: "get_formview_id",
args: [[id]], args: [[id]],
context: context, context: context,
}), }),
self._rpc({ this._rpc({
model: self.field.relation, model: this.field.relation,
method: "check_access_rights", method: "check_access_rights",
kwargs: {operation: "write", raise_exception: false}, kwargs: {operation: "write", raise_exception: false},
}) }),
).then(function(view_id, write_access) { ]).then((view_id, write_access) => {
var can_write = const can_write =
"can_write" in self.attrs "can_write" in this.attrs
? JSON.parse(self.attrs.can_write) ? JSON.parse(this.attrs.can_write)
: true; : true;
new view_dialogs.FormViewDialog(self, { new view_dialogs.FormViewDialog(this, {
res_model: self.field.relation, res_model: this.field.relation,
res_id: id, res_id: id,
context: context, context: context,
title: _t("Open: ") + self.string, title: _t("Open: ") + this.string,
view_id: view_id, view_id: view_id,
readonly: !can_write || !write_access, readonly: !can_write || !write_access,
on_saved: function(record, changed) { on_saved: (record, changed) => {
if (changed) { if (changed) {
self._setValue(self.value.data, { this._setValue(this.value.data, {
forceChange: true, forceChange: true,
}); });
self.trigger_up("reload", {db_id: self.value.id}); this.trigger_up("reload", {db_id: this.value.id});
} }
}, },
}).open(); }).open();
@ -466,9 +491,10 @@ odoo.define("web_m2x_options.web_m2x_options", function(require) {
}), }),
_onOpenBadge: function(event) { _onOpenBadge: function(event) {
var open = this.is_option_set(this.nodeOptions.open); const open = this.is_option_set(this.nodeOptions.open);
var no_color_picker = this.is_option_set(this.nodeOptions.no_color_picker); const no_color_picker = this.is_option_set(
this._super.apply(this, arguments); this.nodeOptions.no_color_picker
);
if (!open && !no_color_picker) { if (!open && !no_color_picker) {
this._onOpenColorPicker(event); this._onOpenColorPicker(event);
} else { } else {