mirror of https://github.com/OCA/web.git
[IMP] web_widget_one2many_product_picker: Add option to use 'name_search'
parent
b453c9784e
commit
8c128aa3bf
|
@ -23,7 +23,7 @@ Web Widget One2Many Product Picker
|
|||
:target: https://runbot.odoo-community.org/runbot/162/13.0
|
||||
:alt: Try me on Runbot
|
||||
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|badge1| |badge2| |badge3| |badge4| |badge5|
|
||||
|
||||
Adds the 'one2many_product_picker' friendly mobile widget to create one2many lines linked with product.product records.
|
||||
|
||||
|
@ -68,7 +68,7 @@ Widget options:
|
|||
* price_unit -> The field that represent a price_unit ('price_unit' 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
|
||||
* domain -> The domain to use
|
||||
|
@ -76,6 +76,9 @@ Widget options:
|
|||
* $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
|
||||
|
||||
* 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_price > Enable/Disable price edits (True 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)
|
||||
* 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
|
||||
* domain -> The domain to use
|
||||
|
@ -34,6 +34,9 @@ Widget options:
|
|||
* $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
|
||||
|
||||
* 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_price > Enable/Disable price edits (True 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>
|
||||
</blockquote>
|
||||
</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>
|
||||
<ul>
|
||||
<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>
|
||||
</blockquote>
|
||||
</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>
|
||||
</blockquote>
|
||||
</li>
|
||||
|
|
|
@ -59,15 +59,18 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
this.attrs.options
|
||||
);
|
||||
if (!this.options.search) {
|
||||
// Default search domain
|
||||
this.options.search = [
|
||||
[this.options.field_map.name, "ilike", "$search"],
|
||||
{
|
||||
name: _t("By Name"),
|
||||
domain: [],
|
||||
name_search_value: "$search",
|
||||
},
|
||||
];
|
||||
}
|
||||
this._searchMode = 0;
|
||||
this._searchCategoryNames = [];
|
||||
if (!(this.options.search[0] instanceof Array)) {
|
||||
this._searchCategoryNames = _.map(this.options.search, "name");
|
||||
}
|
||||
this._searchCategoryNames = _.map(this.options.search, "name");
|
||||
this._searchContext = {};
|
||||
|
||||
// FIXME: Choose a better way to get the active controller or model objects
|
||||
this.parent_controller = parent.getParent();
|
||||
|
@ -84,18 +87,10 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
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") {
|
||||
this._activeSearchGroup = {
|
||||
name: "main_lines",
|
||||
};
|
||||
this._searchContext.activeTest = false;
|
||||
this._updateSearchContext(-1);
|
||||
} else {
|
||||
this._updateSearchContext(0);
|
||||
}
|
||||
return Promise.all([
|
||||
this._super.apply(this, arguments),
|
||||
|
@ -310,6 +305,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
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.
|
||||
* 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) {
|
||||
const arch = this.view.arch;
|
||||
const search_mode = this.options.search[this._searchMode];
|
||||
const field_name = this.options.field_map.product;
|
||||
const field_info = this.view.fieldsInfo[arch.tag][field_name];
|
||||
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
|
||||
// to continue
|
||||
const domain = this._getFullSearchDomain();
|
||||
const domain = this._getFullSearchDomain(search_mode);
|
||||
const soptions = options || {};
|
||||
const context = _.extend(
|
||||
{
|
||||
|
@ -337,20 +344,41 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
},
|
||||
this.value.getContext()
|
||||
);
|
||||
const limit = soptions.limit || this.options.records_per_page;
|
||||
const offset = soptions.offset || 0;
|
||||
|
||||
return new Promise(resolve => {
|
||||
const limit = soptions.limit || this.options.records_per_page;
|
||||
const offset = soptions.offset || 0;
|
||||
this._rpc({
|
||||
model: model,
|
||||
method: "search_read",
|
||||
fields: this.search_read_fields,
|
||||
domain: domain,
|
||||
limit: limit,
|
||||
offset: offset,
|
||||
orderBy: this._searchContext.order,
|
||||
kwargs: {context: context},
|
||||
}).then(results => {
|
||||
let task = false;
|
||||
if (search_mode.name_search_value) {
|
||||
const search_val = this._getSearchValue(
|
||||
this._searchContext.text,
|
||||
search_mode.name_search_value
|
||||
);
|
||||
const operator = search_mode.operator;
|
||||
task = this._doSearchRecordsNameSearch(
|
||||
model,
|
||||
search_val,
|
||||
domain,
|
||||
fields,
|
||||
orderby,
|
||||
operator,
|
||||
limit,
|
||||
offset,
|
||||
context
|
||||
);
|
||||
} else {
|
||||
task = this._doSearchRecords(
|
||||
model,
|
||||
domain,
|
||||
fields,
|
||||
orderby,
|
||||
limit,
|
||||
offset,
|
||||
context
|
||||
);
|
||||
}
|
||||
|
||||
task.then(results => {
|
||||
if (merge) {
|
||||
this._searchRecords = _.union(
|
||||
this._searchRecords || [],
|
||||
|
@ -368,11 +396,82 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
this._activeSearchGroup
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
* @param {MouseEvent} evt
|
||||
|
@ -380,12 +479,7 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
_onClickSearchGroup: function(evt) {
|
||||
const $btn = $(evt.target);
|
||||
const groupIndex = Number($btn.data("group")) || 0;
|
||||
this._activeSearchGroup = this.searchGroups[groupIndex];
|
||||
this._searchContext.domain = this._activeSearchGroup.domain;
|
||||
this._searchContext.order = this._activeSearchGroup.order;
|
||||
this._searchContext.activeTest = true;
|
||||
this.doRenderSearchRecords();
|
||||
this.$btnLines.removeClass("active");
|
||||
this.showGroup(groupIndex);
|
||||
$btn.parent()
|
||||
.find(".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.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} active_search
|
||||
* @returns {Array}
|
||||
*/
|
||||
_getFullSearchDomain: function() {
|
||||
_getFullSearchDomain: function(search_mode) {
|
||||
this._searchContext.involvedFields = [];
|
||||
const domain = _.clone(this._searchContext.domain) || [];
|
||||
if (this._searchContext.text) {
|
||||
let search_domain = this.options.search;
|
||||
if (!(search_domain[0] instanceof Array)) {
|
||||
search_domain = search_domain[this._searchMode].domain;
|
||||
}
|
||||
const search_domain = search_mode.domain;
|
||||
const involved_fields = [];
|
||||
|
||||
// Iterate domain triplets and logic operators
|
||||
|
@ -478,27 +570,15 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
// Is a triplet
|
||||
if (domain_cloned instanceof Array) {
|
||||
// Replace right leaf with the current value of the search input
|
||||
if (domain_cloned[2] === "$number_search") {
|
||||
domain_cloned[2] = Number(this._searchContext.text);
|
||||
involved_fields.push({
|
||||
type: "number",
|
||||
field: domain_cloned[0],
|
||||
oper: domain_cloned[1],
|
||||
});
|
||||
} else if (
|
||||
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_cloned[2] = this._getSearchValue(
|
||||
domain_cloned[2],
|
||||
this._searchContext.text
|
||||
);
|
||||
involved_fields.push({
|
||||
type: "number",
|
||||
field: domain_cloned[0],
|
||||
oper: domain_cloned[1],
|
||||
});
|
||||
}
|
||||
domain.push(domain_cloned);
|
||||
}
|
||||
|
@ -528,32 +608,57 @@ odoo.define("web_widget_one2many_product_picker.FieldOne2ManyProductPicker", fun
|
|||
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
|
||||
* that the search results. Use directy in-memory values.
|
||||
*/
|
||||
showLines: function() {
|
||||
this._updateSearchContext(-1);
|
||||
this._clearSearchInput();
|
||||
this.$btnLines
|
||||
.parent()
|
||||
.find(".active")
|
||||
.removeClass("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();
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Number} group_id
|
||||
*/
|
||||
showGroup: function(group_id) {
|
||||
this._updateSearchContext(group_id);
|
||||
this.doRenderSearchRecords();
|
||||
this.$btnLines.removeClass("active");
|
||||
},
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_clearSearchInput: function() {
|
||||
this.$searchInput.val("");
|
||||
this._searchContext.text = "";
|
||||
if (this.$searchInput) {
|
||||
this.$searchInput.val("");
|
||||
this._searchContext.text = "";
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue