forked from Techsystech/web
[IMP] web_widget_one2many_product_picker: Add option to use 'name_search'
parent
b453c9784e
commit
8c128aa3bf
|
@ -68,7 +68,7 @@ Widget options:
|
||||||
* price_unit -> The field that represent a price_unit ('price_unit' by default)
|
* price_unit -> The field that represent a price_unit ('price_unit' by default)
|
||||||
* discount -> The field that represent a discount ('discount' by default)
|
* discount -> The field that represent a discount ('discount' by default)
|
||||||
|
|
||||||
* search > Array of dictionaries or Array of 'triplets' ([[field_map.name, 'ilike', '$search']] by default)
|
* search > Array of dictionaries (defines to use name_search by default)
|
||||||
|
|
||||||
* name -> The name to display
|
* name -> The name to display
|
||||||
* domain -> The domain to use
|
* domain -> The domain to use
|
||||||
|
@ -76,6 +76,9 @@ Widget options:
|
||||||
* $search -> Replaces it with the current value of the searchbox
|
* $search -> Replaces it with the current value of the searchbox
|
||||||
* $number_search -> Replaces all the leaf with the current value of the searchbox as a number
|
* $number_search -> Replaces all the leaf with the current value of the searchbox as a number
|
||||||
|
|
||||||
|
* name_search_value -> Enables the use of 'name_search' instead of 'search_read' and defines the value to search ('$search' by default)
|
||||||
|
* operator -> Operator used in 'name_search' ('ilike' by default)
|
||||||
|
|
||||||
* edit_discount > Enable/Disable discount edits (False by default)
|
* edit_discount > Enable/Disable discount edits (False by default)
|
||||||
* edit_price > Enable/Disable price edits (True by default)
|
* edit_price > Enable/Disable price edits (True by default)
|
||||||
* show_discount > Enable/Disable display discount (False by default)
|
* show_discount > Enable/Disable display discount (False by default)
|
||||||
|
|
|
@ -26,7 +26,7 @@ Widget options:
|
||||||
* price_unit -> The field that represent a price_unit ('price_unit' by default)
|
* price_unit -> The field that represent a price_unit ('price_unit' by default)
|
||||||
* discount -> The field that represent a discount ('discount' by default)
|
* discount -> The field that represent a discount ('discount' by default)
|
||||||
|
|
||||||
* search > Array of dictionaries or Array of 'triplets' ([[field_map.name, 'ilike', '$search']] by default)
|
* search > Array of dictionaries (defines to use name_search by default)
|
||||||
|
|
||||||
* name -> The name to display
|
* name -> The name to display
|
||||||
* domain -> The domain to use
|
* domain -> The domain to use
|
||||||
|
@ -34,6 +34,9 @@ Widget options:
|
||||||
* $search -> Replaces it with the current value of the searchbox
|
* $search -> Replaces it with the current value of the searchbox
|
||||||
* $number_search -> Replaces all the leaf with the current value of the searchbox as a number
|
* $number_search -> Replaces all the leaf with the current value of the searchbox as a number
|
||||||
|
|
||||||
|
* name_search_value -> Enables the use of 'name_search' instead of 'search_read' and defines the value to search ('$search' by default)
|
||||||
|
* operator -> Operator used in 'name_search' ('ilike' by default)
|
||||||
|
|
||||||
* edit_discount > Enable/Disable discount edits (False by default)
|
* edit_discount > Enable/Disable discount edits (False by default)
|
||||||
* edit_price > Enable/Disable price edits (True by default)
|
* edit_price > Enable/Disable price edits (True by default)
|
||||||
* show_discount > Enable/Disable display discount (False by default)
|
* show_discount > Enable/Disable display discount (False by default)
|
||||||
|
|
|
@ -442,7 +442,7 @@ You need to define the view fields. The view must be of <tt class="docutils lite
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</li>
|
</li>
|
||||||
<li><p class="first">search > Array of dictionaries or Array of ‘triplets’ ([[field_map.name, ‘ilike’, ‘$search’]] by default)</p>
|
<li><p class="first">search > Array of dictionaries (defines to use name_search by default)</p>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<ul>
|
<ul>
|
||||||
<li><p class="first">name -> The name to display</p>
|
<li><p class="first">name -> The name to display</p>
|
||||||
|
@ -455,6 +455,10 @@ You need to define the view fields. The view must be of <tt class="docutils lite
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</li>
|
</li>
|
||||||
|
<li><p class="first">name_search_value -> Enables the use of ‘name_search’ instead of ‘search_read’ and defines the value to search (‘$search’ by default)</p>
|
||||||
|
</li>
|
||||||
|
<li><p class="first">operator -> Operator used in ‘name_search’ (‘ilike’ by default)</p>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -59,15 +59,18 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
this.attrs.options
|
this.attrs.options
|
||||||
);
|
);
|
||||||
if (!this.options.search) {
|
if (!this.options.search) {
|
||||||
|
// Default search domain
|
||||||
this.options.search = [
|
this.options.search = [
|
||||||
[this.options.field_map.name, "ilike", "$search"],
|
{
|
||||||
|
name: _t("By Name"),
|
||||||
|
domain: [],
|
||||||
|
name_search_value: "$search",
|
||||||
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
this._searchMode = 0;
|
this._searchMode = 0;
|
||||||
this._searchCategoryNames = [];
|
this._searchCategoryNames = _.map(this.options.search, "name");
|
||||||
if (!(this.options.search[0] instanceof Array)) {
|
this._searchContext = {};
|
||||||
this._searchCategoryNames = _.map(this.options.search, "name");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Choose a better way to get the active controller or model objects
|
// FIXME: Choose a better way to get the active controller or model objects
|
||||||
this.parent_controller = parent.getParent();
|
this.parent_controller = parent.getParent();
|
||||||
|
@ -84,18 +87,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Uses to work with searchs, so we can mix properties with the user values.
|
|
||||||
this._searchContext = {
|
|
||||||
domain: this.mode === "readonly" ? this._getLinesDomain() : false,
|
|
||||||
text: false,
|
|
||||||
order: false,
|
|
||||||
activeTest: true,
|
|
||||||
};
|
|
||||||
if (this.mode === "readonly") {
|
if (this.mode === "readonly") {
|
||||||
this._activeSearchGroup = {
|
this._updateSearchContext(-1);
|
||||||
name: "main_lines",
|
} else {
|
||||||
};
|
this._updateSearchContext(0);
|
||||||
this._searchContext.activeTest = false;
|
|
||||||
}
|
}
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
this._super.apply(this, arguments),
|
this._super.apply(this, arguments),
|
||||||
|
@ -310,6 +305,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
this.updateSubtotalPrice();
|
this.updateSubtotalPrice();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getSearchValue: function(value, format) {
|
||||||
|
if (format === "$number_search") {
|
||||||
|
return Number(value);
|
||||||
|
} else if (typeof value === "string") {
|
||||||
|
return format.replace(/\$search/, value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the linked records defined in the options.
|
* Obtain the linked records defined in the options.
|
||||||
* If merge is true the current records aren't removed.
|
* If merge is true the current records aren't removed.
|
||||||
|
@ -321,13 +325,16 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
*/
|
*/
|
||||||
_getSearchRecords: function(options, merge) {
|
_getSearchRecords: function(options, merge) {
|
||||||
const arch = this.view.arch;
|
const arch = this.view.arch;
|
||||||
|
const search_mode = this.options.search[this._searchMode];
|
||||||
const field_name = this.options.field_map.product;
|
const field_name = this.options.field_map.product;
|
||||||
const field_info = this.view.fieldsInfo[arch.tag][field_name];
|
const field_info = this.view.fieldsInfo[arch.tag][field_name];
|
||||||
const model = this.view.viewFields[field_info.name].relation;
|
const model = this.view.viewFields[field_info.name].relation;
|
||||||
|
const orderby = this._searchContext.order;
|
||||||
|
const fields = this.search_read_fields;
|
||||||
|
|
||||||
// Launch the rpc request and ensures that we wait for the reply
|
// Launch the rpc request and ensures that we wait for the reply
|
||||||
// to continue
|
// to continue
|
||||||
const domain = this._getFullSearchDomain();
|
const domain = this._getFullSearchDomain(search_mode);
|
||||||
const soptions = options || {};
|
const soptions = options || {};
|
||||||
const context = _.extend(
|
const context = _.extend(
|
||||||
{
|
{
|
||||||
|
@ -337,20 +344,41 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
},
|
},
|
||||||
this.value.getContext()
|
this.value.getContext()
|
||||||
);
|
);
|
||||||
|
const limit = soptions.limit || this.options.records_per_page;
|
||||||
|
const offset = soptions.offset || 0;
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const limit = soptions.limit || this.options.records_per_page;
|
let task = false;
|
||||||
const offset = soptions.offset || 0;
|
if (search_mode.name_search_value) {
|
||||||
this._rpc({
|
const search_val = this._getSearchValue(
|
||||||
model: model,
|
this._searchContext.text,
|
||||||
method: "search_read",
|
search_mode.name_search_value
|
||||||
fields: this.search_read_fields,
|
);
|
||||||
domain: domain,
|
const operator = search_mode.operator;
|
||||||
limit: limit,
|
task = this._doSearchRecordsNameSearch(
|
||||||
offset: offset,
|
model,
|
||||||
orderBy: this._searchContext.order,
|
search_val,
|
||||||
kwargs: {context: context},
|
domain,
|
||||||
}).then(results => {
|
fields,
|
||||||
|
orderby,
|
||||||
|
operator,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
task = this._doSearchRecords(
|
||||||
|
model,
|
||||||
|
domain,
|
||||||
|
fields,
|
||||||
|
orderby,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
task.then(results => {
|
||||||
if (merge) {
|
if (merge) {
|
||||||
this._searchRecords = _.union(
|
this._searchRecords = _.union(
|
||||||
this._searchRecords || [],
|
this._searchRecords || [],
|
||||||
|
@ -368,11 +396,82 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
this._activeSearchGroup
|
this._activeSearchGroup
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
resolve(results);
|
resolve(results);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_doSearchRecordsNameSearch: function(
|
||||||
|
model,
|
||||||
|
search_val,
|
||||||
|
domain,
|
||||||
|
fields,
|
||||||
|
orderby,
|
||||||
|
operator,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
context
|
||||||
|
) {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
this._rpc({
|
||||||
|
model: model,
|
||||||
|
method: "name_search",
|
||||||
|
kwargs: {
|
||||||
|
name: search_val,
|
||||||
|
args: domain || [],
|
||||||
|
operator: operator || "ilike",
|
||||||
|
limit: this.limit,
|
||||||
|
context: context || {},
|
||||||
|
},
|
||||||
|
}).then(results => {
|
||||||
|
const record_ids = results.map(item => item[0]);
|
||||||
|
this._doSearchRecords(
|
||||||
|
model,
|
||||||
|
[["id", "in", record_ids]],
|
||||||
|
fields,
|
||||||
|
orderby,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
context
|
||||||
|
).then(records => {
|
||||||
|
resolve(records);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {String} model
|
||||||
|
* @param {Array} domain
|
||||||
|
* @param {Array} fields
|
||||||
|
* @param {Array} orderby
|
||||||
|
* @param {Number} limit
|
||||||
|
* @param {Number} offset
|
||||||
|
* @param {Object} context
|
||||||
|
* @returns {Promise}
|
||||||
|
*/
|
||||||
|
_doSearchRecords: function(
|
||||||
|
model,
|
||||||
|
domain,
|
||||||
|
fields,
|
||||||
|
orderby,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
context
|
||||||
|
) {
|
||||||
|
return this._rpc({
|
||||||
|
model: model,
|
||||||
|
method: "search_read",
|
||||||
|
fields: fields,
|
||||||
|
domain: domain,
|
||||||
|
limit: limit,
|
||||||
|
offset: offset,
|
||||||
|
orderBy: orderby,
|
||||||
|
kwargs: {context: context},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
* @param {MouseEvent} evt
|
* @param {MouseEvent} evt
|
||||||
|
@ -380,12 +479,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
_onClickSearchGroup: function(evt) {
|
_onClickSearchGroup: function(evt) {
|
||||||
const $btn = $(evt.target);
|
const $btn = $(evt.target);
|
||||||
const groupIndex = Number($btn.data("group")) || 0;
|
const groupIndex = Number($btn.data("group")) || 0;
|
||||||
this._activeSearchGroup = this.searchGroups[groupIndex];
|
this.showGroup(groupIndex);
|
||||||
this._searchContext.domain = this._activeSearchGroup.domain;
|
|
||||||
this._searchContext.order = this._activeSearchGroup.order;
|
|
||||||
this._searchContext.activeTest = true;
|
|
||||||
this.doRenderSearchRecords();
|
|
||||||
this.$btnLines.removeClass("active");
|
|
||||||
$btn.parent()
|
$btn.parent()
|
||||||
.find(".active")
|
.find(".active")
|
||||||
.removeClass("active");
|
.removeClass("active");
|
||||||
|
@ -459,16 +553,14 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
* This domain is used to get the records to display.
|
* This domain is used to get the records to display.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
|
* @param {Object} active_search
|
||||||
* @returns {Array}
|
* @returns {Array}
|
||||||
*/
|
*/
|
||||||
_getFullSearchDomain: function() {
|
_getFullSearchDomain: function(search_mode) {
|
||||||
this._searchContext.involvedFields = [];
|
this._searchContext.involvedFields = [];
|
||||||
const domain = _.clone(this._searchContext.domain) || [];
|
const domain = _.clone(this._searchContext.domain) || [];
|
||||||
if (this._searchContext.text) {
|
if (this._searchContext.text) {
|
||||||
let search_domain = this.options.search;
|
const search_domain = search_mode.domain;
|
||||||
if (!(search_domain[0] instanceof Array)) {
|
|
||||||
search_domain = search_domain[this._searchMode].domain;
|
|
||||||
}
|
|
||||||
const involved_fields = [];
|
const involved_fields = [];
|
||||||
|
|
||||||
// Iterate domain triplets and logic operators
|
// Iterate domain triplets and logic operators
|
||||||
|
@ -478,27 +570,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
// Is a triplet
|
// Is a triplet
|
||||||
if (domain_cloned instanceof Array) {
|
if (domain_cloned instanceof Array) {
|
||||||
// Replace right leaf with the current value of the search input
|
// Replace right leaf with the current value of the search input
|
||||||
if (domain_cloned[2] === "$number_search") {
|
domain_cloned[2] = this._getSearchValue(
|
||||||
domain_cloned[2] = Number(this._searchContext.text);
|
domain_cloned[2],
|
||||||
involved_fields.push({
|
this._searchContext.text
|
||||||
type: "number",
|
);
|
||||||
field: domain_cloned[0],
|
involved_fields.push({
|
||||||
oper: domain_cloned[1],
|
type: "number",
|
||||||
});
|
field: domain_cloned[0],
|
||||||
} else if (
|
oper: domain_cloned[1],
|
||||||
typeof domain_cloned[2] === "string" &&
|
});
|
||||||
domain_cloned[2].includes("$search")
|
|
||||||
) {
|
|
||||||
domain_cloned[2] = domain_cloned[2].replace(
|
|
||||||
/\$search/,
|
|
||||||
this._searchContext.text
|
|
||||||
);
|
|
||||||
involved_fields.push({
|
|
||||||
type: "text",
|
|
||||||
field: domain_cloned[0],
|
|
||||||
oper: domain_cloned[1],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
domain.push(domain_cloned);
|
domain.push(domain_cloned);
|
||||||
}
|
}
|
||||||
|
@ -528,32 +608,57 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
||||||
return [["id", "in", ids]];
|
return [["id", "in", ids]];
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} group_id
|
||||||
|
*/
|
||||||
|
_updateSearchContext: function(group_id) {
|
||||||
|
if (group_id >= 0) {
|
||||||
|
this._activeSearchGroup = this.searchGroups[group_id];
|
||||||
|
this._searchContext.domain = this._activeSearchGroup.domain;
|
||||||
|
this._searchContext.order = this._activeSearchGroup.order;
|
||||||
|
this._searchContext.activeTest = this._activeSearchGroup.active_test;
|
||||||
|
} else {
|
||||||
|
this._activeSearchGroup = {
|
||||||
|
name: "main_lines",
|
||||||
|
};
|
||||||
|
this._searchContext.domain = this._getLinesDomain();
|
||||||
|
this._searchContext.order = [{name: "sequence"}, {name: "id"}];
|
||||||
|
this._searchContext.activeTest = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The lines are special data, so we need display it in a other way
|
* The lines are special data, so we need display it in a other way
|
||||||
* that the search results. Use directy in-memory values.
|
* that the search results. Use directy in-memory values.
|
||||||
*/
|
*/
|
||||||
showLines: function() {
|
showLines: function() {
|
||||||
|
this._updateSearchContext(-1);
|
||||||
this._clearSearchInput();
|
this._clearSearchInput();
|
||||||
this.$btnLines
|
this.$btnLines
|
||||||
.parent()
|
.parent()
|
||||||
.find(".active")
|
.find(".active")
|
||||||
.removeClass("active");
|
.removeClass("active");
|
||||||
this.$btnLines.addClass("active");
|
this.$btnLines.addClass("active");
|
||||||
this._activeSearchGroup = {
|
|
||||||
name: "main_lines",
|
|
||||||
};
|
|
||||||
this._searchContext.domain = this._getLinesDomain();
|
|
||||||
this._searchContext.order = [{name: "sequence"}, {name: "id"}];
|
|
||||||
this._searchContext.activeTest = false;
|
|
||||||
this.doRenderSearchRecords();
|
this.doRenderSearchRecords();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Number} group_id
|
||||||
|
*/
|
||||||
|
showGroup: function(group_id) {
|
||||||
|
this._updateSearchContext(group_id);
|
||||||
|
this.doRenderSearchRecords();
|
||||||
|
this.$btnLines.removeClass("active");
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
_clearSearchInput: function() {
|
_clearSearchInput: function() {
|
||||||
this.$searchInput.val("");
|
if (this.$searchInput) {
|
||||||
this._searchContext.text = "";
|
this.$searchInput.val("");
|
||||||
|
this._searchContext.text = "";
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue