[IMP] web: adapt field domain to new API
Add the implementation of the "FieldDomain" (widget='domain') in basic fields file.
This commit is contained in:
@@ -14,10 +14,14 @@ var core = require('web.core');
|
||||
var crash_manager = require('web.crash_manager');
|
||||
var datepicker = require('web.datepicker');
|
||||
var dom = require('web.dom');
|
||||
var Domain = require('web.Domain');
|
||||
var DomainSelector = require('web.DomainSelector');
|
||||
var DomainSelectorDialog = require('web.DomainSelectorDialog');
|
||||
var field_utils = require('web.field_utils');
|
||||
var framework = require('web.framework');
|
||||
var session = require('web.session');
|
||||
var utils = require('web.utils');
|
||||
var view_dialogs = require('web.view_dialogs');
|
||||
|
||||
var qweb = core.qweb;
|
||||
var _t = core._t;
|
||||
@@ -1188,6 +1192,225 @@ var JournalDashboardGraph = AbstractField.extend({
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* The "Domain" field allows the user to construct a technical-prefix domain
|
||||
* thanks to a tree-like interface and see the selected records in real time.
|
||||
* In debug mode, an input is also there to be able to enter the prefix char
|
||||
* domain directly (or to build advanced domains the tree-like interface does
|
||||
* not allow to).
|
||||
*/
|
||||
var FieldDomain = AbstractField.extend({
|
||||
/**
|
||||
* The model this field widget works with could be dependant on another
|
||||
* field value. This field widget must then be reset when a field is changed
|
||||
* in the view.
|
||||
*/
|
||||
resetOnAnyFieldChange: true,
|
||||
|
||||
events: _.extend({}, AbstractField.prototype.events, {
|
||||
"click .o_domain_show_selection_button": "_onShowSelectionButtonClick",
|
||||
"click .o_form_field_domain_dialog_button": "_onDialogEditButtonClick",
|
||||
}),
|
||||
custom_events: {
|
||||
"domain_changed": "_onDomainSelectorValueChange",
|
||||
"domain_selected": "_onDomainSelectorDialogValueChange",
|
||||
},
|
||||
/**
|
||||
* @constructor
|
||||
* @override init from AbstractField
|
||||
*/
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.inDialog = !!this.nodeOptions.in_dialog;
|
||||
this.modelOption = this.nodeOptions.model;
|
||||
this.fsFilters = this.nodeOptions.fs_filters || {};
|
||||
|
||||
this.className = "o_form_field_domain";
|
||||
if (this.mode === "edit") {
|
||||
this.className += " o_edit_mode";
|
||||
}
|
||||
if (!this.inDialog) {
|
||||
this.className += " o_inline_mode";
|
||||
}
|
||||
|
||||
this._initModel();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override isValid from AbstractField.isValid
|
||||
* Parsing the char value is not enough for this field. It is considered
|
||||
* valid if the internal domain selector was built correctly and that the
|
||||
* query to the model to test the domain did not fail.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isValid: function () {
|
||||
return (
|
||||
this._super.apply(this, arguments)
|
||||
&& this._isValidForModel
|
||||
);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @override _render from AbstractField
|
||||
* @returns {Deferred}
|
||||
*/
|
||||
_render: function () {
|
||||
// If there is no model, only change the non-domain-selector content
|
||||
if (!this.domainModel) {
|
||||
this._replaceContent();
|
||||
return $.when();
|
||||
}
|
||||
|
||||
// Convert char value to array value
|
||||
var value = this.value || "[]";
|
||||
var domain = Domain.prototype.stringToArray(value);
|
||||
|
||||
// Create the domain selector or change the value of the current one...
|
||||
var def;
|
||||
if (!this.domainSelector) {
|
||||
this.domainSelector = new DomainSelector(this, this.domainModel, domain, {
|
||||
readonly: this.mode === "readonly" || this.inDialog,
|
||||
filters: this.fsFilters,
|
||||
debugMode: session.debug,
|
||||
});
|
||||
def = this.domainSelector.prependTo(this.$el);
|
||||
} else {
|
||||
def = this.domainSelector.setDomain(domain);
|
||||
}
|
||||
// ... then query the model to get the number of matched record if valid
|
||||
var self = this;
|
||||
return def.then(function () {
|
||||
var context = {}; // FIXME ?
|
||||
return self.performModelRPC(self.domainModel, "search_count", [
|
||||
domain,
|
||||
context
|
||||
]).then(
|
||||
function (data) {
|
||||
self._isValidForModel = true;
|
||||
return data;
|
||||
},
|
||||
function (error, e) {
|
||||
e.preventDefault();
|
||||
self._isValidForModel = false;
|
||||
}
|
||||
).always(self._replaceContent.bind(self));
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Render the field DOM except for the domain selector part. The full field
|
||||
* DOM is composed of a DIV which contains the domain selector widget,
|
||||
* followed by other content. This other content is handled by this method.
|
||||
*
|
||||
* @private
|
||||
* @param {integer} [nbRecords=0] - The number of records the domain matches
|
||||
*/
|
||||
_replaceContent(nbRecords) {
|
||||
if (this._$content) {
|
||||
this._$content.remove();
|
||||
}
|
||||
this._$content = $(qweb.render("FieldDomain.content", {
|
||||
hasModel: !!this.domainModel,
|
||||
isValid: !!this._isValidForModel,
|
||||
nbRecords: nbRecords || 0,
|
||||
inDialogEdit: this.inDialog && this.mode === "edit",
|
||||
}));
|
||||
this._$content.appendTo(this.$el);
|
||||
},
|
||||
/**
|
||||
* @override _reset from AbstractField
|
||||
* Check if the model the field works with has (to be) changed.
|
||||
*/
|
||||
_reset: function () {
|
||||
this._super.apply(this, arguments);
|
||||
var oldDomainModel = this.domainModel;
|
||||
this._initModel();
|
||||
if (this.domainSelector && this.domainModel !== oldDomainModel) {
|
||||
// If the model has changed, destroy the current domain selector
|
||||
this.domainSelector.destroy();
|
||||
this.domainSelector = null;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks the model the field must work with. It is either directly given
|
||||
* in the field option or it is the value of another field in the view.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_initModel: function () {
|
||||
this.domainModel = this.modelOption;
|
||||
if (this.recordData.hasOwnProperty(this.domainModel)) {
|
||||
this.domainModel = this.recordData[this.domainModel];
|
||||
}
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Called when the "Show selection" button is clicked
|
||||
* -> Open a modal to see the matched records
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
_onShowSelectionButtonClick: function (e) {
|
||||
e.preventDefault();
|
||||
new view_dialogs.SelectCreateDialog(this, {
|
||||
title: _t("Selected records"),
|
||||
res_model: this.domainModel,
|
||||
domain: this.value || "[]",
|
||||
no_create: true,
|
||||
readonly: true,
|
||||
disable_multiple_selection: true,
|
||||
}).open();
|
||||
},
|
||||
/**
|
||||
* Called when the "Edit domain" button is clicked (when using the in_dialog
|
||||
* option) -> Open a DomainSelectorDialog to edit the value
|
||||
*
|
||||
* @param {Event} e
|
||||
*/
|
||||
_onDialogEditButtonClick: function (e) {
|
||||
e.preventDefault();
|
||||
new DomainSelectorDialog(this, this.domainModel, this.value || "[]", {
|
||||
readonly: this.mode === "readonly",
|
||||
filters: this.fsFilters,
|
||||
debugMode: session.debug,
|
||||
}).open();
|
||||
},
|
||||
/**
|
||||
* Called when the domain selector value is changed (do nothing if it is the
|
||||
* one which is in a dialog (@see _onDomainSelectorDialogValueChange))
|
||||
* -> Adapt the internal value state
|
||||
*
|
||||
* @param {OdooEvent} e
|
||||
*/
|
||||
_onDomainSelectorValueChange: function (e) {
|
||||
if (this.inDialog) return;
|
||||
this._setValue(Domain.prototype.arrayToString(this.domainSelector.getDomain()));
|
||||
},
|
||||
/**
|
||||
* Called when the in-dialog domain selector value is confirmed
|
||||
* -> Adapt the internal value state
|
||||
*
|
||||
* @param {OdooEvent} e
|
||||
*/
|
||||
_onDomainSelectorDialogValueChange: function (e) {
|
||||
this._setValue(Domain.prototype.arrayToString(e.data.domain));
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* This widget is intended to be used on Text fields. It will provide Ace Editor
|
||||
* for editing XML and Python.
|
||||
@@ -1275,6 +1498,7 @@ return {
|
||||
FieldChar: FieldChar,
|
||||
FieldDate: FieldDate,
|
||||
FieldDateTime: FieldDateTime,
|
||||
FieldDomain: FieldDomain,
|
||||
FieldFloat: FieldFloat,
|
||||
FieldFloatTime: FieldFloatTime,
|
||||
FieldHtml: FieldHtml,
|
||||
|
||||
@@ -24,6 +24,7 @@ registry
|
||||
.add('boolean', basic_fields.FieldBoolean)
|
||||
.add('date', basic_fields.FieldDate)
|
||||
.add('datetime', basic_fields.FieldDateTime)
|
||||
.add('domain', basic_fields.FieldDomain)
|
||||
.add('text', basic_fields.FieldText)
|
||||
.add('float', basic_fields.FieldFloat)
|
||||
.add('char', basic_fields.FieldChar)
|
||||
@@ -63,4 +64,4 @@ registry
|
||||
.add('statusbar', relational_fields.FieldStatus)
|
||||
.add('one2many_list', relational_fields.FieldOne2Many);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -301,6 +301,12 @@ var DomainSelector = DomainTree.extend({
|
||||
}
|
||||
return this._super(domain);
|
||||
},
|
||||
setDomain: function (domain) {
|
||||
if (Domain.prototype.arrayToString(domain) === Domain.prototype.arrayToString(this.getDomain())) {
|
||||
return $.when();
|
||||
}
|
||||
return this._redraw(domain);
|
||||
},
|
||||
_postRender: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
|
||||
@@ -2,11 +2,20 @@ odoo.define("web.ModelFieldSelector", function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require("web.core");
|
||||
var Model = require("web.DataModel");
|
||||
var Widget = require("web.Widget");
|
||||
|
||||
var _t = core._t;
|
||||
|
||||
/**
|
||||
* Field Selector Cache - TODO Should be improved to use external cache ?
|
||||
* - Stores fields per model used in field selector
|
||||
* @see ModelFieldSelector._getModelFieldsFromCache
|
||||
*/
|
||||
var modelFieldsCache = {
|
||||
cache: {},
|
||||
cacheDefs: {},
|
||||
};
|
||||
|
||||
/// The ModelFieldSelector widget can be used to select a particular field chain from a given model.
|
||||
var ModelFieldSelector = Widget.extend({
|
||||
template: "FieldSelector",
|
||||
@@ -32,7 +41,7 @@ var ModelFieldSelector = Widget.extend({
|
||||
},
|
||||
|
||||
// Handle a direct change in the debug input
|
||||
"change input": function() {
|
||||
"change input": function () {
|
||||
var userChain = this.$input.val();
|
||||
if (!this.options.followRelations) {
|
||||
var fields = userChain.split(".");
|
||||
@@ -208,7 +217,7 @@ var ModelFieldSelector = Widget.extend({
|
||||
/// @return a deferred which is resolved once the last page is shown
|
||||
_prefill: function () {
|
||||
this.pages = [];
|
||||
return this._pushPageData(this.model).then((function() {
|
||||
return this._pushPageData(this.model).then((function () {
|
||||
return (this.chain ? processChain.call(this, this.chain.split(".").reverse()) : $.when());
|
||||
}).bind(this));
|
||||
|
||||
@@ -234,7 +243,7 @@ var ModelFieldSelector = Widget.extend({
|
||||
if (this.model === model && this.options.fields) {
|
||||
def = $.when(sortFields(this.options.fields));
|
||||
} else {
|
||||
def = fieldsCache.getFields(model, this.options.filters);
|
||||
def = this._getModelFieldsFromCache(model, this.options.filters);
|
||||
}
|
||||
return def.then((function (fields) {
|
||||
this.pages.push(fields);
|
||||
@@ -294,36 +303,35 @@ var ModelFieldSelector = Widget.extend({
|
||||
name: name,
|
||||
});
|
||||
},
|
||||
/**
|
||||
* The _getFieldsFromCache method searches the cache for the given model
|
||||
* fields, according to the given filter. If the cache does not know about
|
||||
* the model, the cache is updated.
|
||||
*
|
||||
* @param {string} model
|
||||
* @param {Object} filters @see ModelFieldSelector.init.options.filters
|
||||
*
|
||||
* @returns {Object[]} a list of the model fields info, sorted by field
|
||||
* non-technical name
|
||||
*/
|
||||
_getModelFieldsFromCache: function (model, filters) {
|
||||
var def = modelFieldsCache.cacheDefs[model];
|
||||
if (!def) {
|
||||
def = modelFieldsCache.cacheDefs[model] = this.performModelRPC(model, "fields_get", [
|
||||
false,
|
||||
["store", "searchable", "type", "string", "relation", "selection", "related"],
|
||||
]).then((function (fields) {
|
||||
modelFieldsCache.cache[model] = sortFields(fields);
|
||||
}).bind(this));
|
||||
}
|
||||
return def.then((function () {
|
||||
return _.filter(modelFieldsCache.cache[model], function (f) {
|
||||
return !filters.searchable || f.searchable;
|
||||
});
|
||||
}).bind(this));
|
||||
},
|
||||
});
|
||||
|
||||
/// Field Selector Cache
|
||||
///
|
||||
/// * Stores fields per model used in field selector
|
||||
/// * Apply filters on the fly
|
||||
var fieldsCache = {
|
||||
cache: {},
|
||||
cacheDefs: {},
|
||||
getFields: function (model, filters) {
|
||||
return (this.cacheDefs[model] ? this.cacheDefs[model] : this.updateCache(model)).then((function () {
|
||||
return this.filter(model, filters);
|
||||
}).bind(this));
|
||||
},
|
||||
updateCache: function (model) {
|
||||
this.cacheDefs[model] = new Model(model).call("fields_get", [
|
||||
false,
|
||||
["store", "searchable", "type", "string", "relation", "selection", "related"],
|
||||
]).then((function (fields) {
|
||||
this.cache[model] = sortFields(fields);
|
||||
}).bind(this));
|
||||
return this.cacheDefs[model];
|
||||
},
|
||||
filter: function (model, filters) {
|
||||
return _.filter(this.cache[model], function (f) {
|
||||
return !filters.searchable || f.searchable;
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
function sortFields(fields) {
|
||||
return _.chain(fields)
|
||||
.pairs()
|
||||
|
||||
@@ -665,19 +665,19 @@
|
||||
t-att-maxlength="widget.field.size"/>
|
||||
</t>
|
||||
|
||||
<div t-name="FieldDomain" t-attf-class="o_form_field_domain#{widget.get('effective_readonly') ? '' : ' o_edit_mode'}#{widget.options.in_dialog ? '' : ' o_inline_mode'}">
|
||||
<!-- domain selector will be instantiated here -->
|
||||
<div class="o_form_field_domain_panel">
|
||||
<t t-name="FieldDomain.content">
|
||||
<div t-if="hasModel" class="o_form_field_domain_panel">
|
||||
<i class="fa fa-arrow-right"/>
|
||||
<button class="btn btn-xs btn-default o_domain_show_selection_button" type="button">
|
||||
<span class="o_domain_records_count"/> record(s)
|
||||
|
||||
<button t-if="isValid" class="btn btn-xs btn-default o_domain_show_selection_button" type="button">
|
||||
<t t-esc="nbRecords"/> record(s)
|
||||
</button>
|
||||
<span class="text-warning o_domain_error_message hidden"><i class="fa fa-exclamation-triangle"/> Invalid domain</span>
|
||||
<button t-if="widget.options.in_dialog && !widget.get('effective_readonly')"
|
||||
class="btn btn-xs btn-primary o_form_field_domain_dialog_button">Edit domain</button>
|
||||
<span t-else="" class="text-warning"><i class="fa fa-exclamation-triangle"/> Invalid domain</span>
|
||||
|
||||
<button t-if="inDialogEdit" class="btn btn-xs btn-primary o_form_field_domain_dialog_button">Edit domain</button>
|
||||
</div>
|
||||
<div class="o_domain_model_missing o_hidden">Select a model to add a filter.</div>
|
||||
</div>
|
||||
<div t-else="">Select a model to add a filter.</div>
|
||||
</t>
|
||||
|
||||
<t t-name="DomainNode.ControlPanel">
|
||||
<div t-if="!widget.readonly && !widget.noControlPanel" class="btn-group btn-group-sm pull-right o_domain_node_control_panel">
|
||||
|
||||
@@ -16,17 +16,17 @@ QUnit.module('basic_fields', {
|
||||
this.data = {
|
||||
partner: {
|
||||
fields: {
|
||||
date: {string: "A date", type: "date"},
|
||||
display_name: { string: "Displayed name", type: "char" },
|
||||
foo: {string: "Foo", type: "char", default: "My little Foo Value"},
|
||||
bar: {string: "Bar", type: "boolean", default: true},
|
||||
int_field: {string: "int_field", type: "integer", sortable: true},
|
||||
qux: {string: "Qux", type: "float", digits: [16,1] },
|
||||
p: {string: "one2many field", type: "one2many", relation: 'partner'},
|
||||
trululu: {string: "Trululu", type: "many2one", relation: 'partner'},
|
||||
timmy: { string: "pokemon", type: "many2many", relation: 'partner_type'},
|
||||
product_id: {string: "Product", type: "many2one", relation: 'product'},
|
||||
sequence: {type: "integer", string: "Sequence"},
|
||||
date: {string: "A date", type: "date", searchable: true},
|
||||
display_name: {string: "Displayed name", type: "char", searchable: true},
|
||||
foo: {string: "Foo", type: "char", default: "My little Foo Value", searchable: true},
|
||||
bar: {string: "Bar", type: "boolean", default: true, searchable: true},
|
||||
int_field: {string: "int_field", type: "integer", sortable: true, searchable: true},
|
||||
qux: {string: "Qux", type: "float", digits: [16,1], searchable: true},
|
||||
p: {string: "one2many field", type: "one2many", relation: 'partner', searchable: true},
|
||||
trululu: {string: "Trululu", type: "many2one", relation: 'partner', searchable: true},
|
||||
timmy: {string: "pokemon", type: "many2many", relation: 'partner_type', searchable: true},
|
||||
product_id: {string: "Product", type: "many2one", relation: 'product', searchable: true},
|
||||
sequence: {type: "integer", string: "Sequence", searchable: true},
|
||||
},
|
||||
records: [{
|
||||
id: 1,
|
||||
@@ -62,7 +62,7 @@ QUnit.module('basic_fields', {
|
||||
},
|
||||
product: {
|
||||
fields: {
|
||||
name: {string: "Product Name", type: "char"}
|
||||
name: {string: "Product Name", type: "char", searchable: true}
|
||||
},
|
||||
records: [{
|
||||
id: 37,
|
||||
@@ -74,8 +74,8 @@ QUnit.module('basic_fields', {
|
||||
},
|
||||
partner_type: {
|
||||
fields: {
|
||||
name: {string: "Partner Type", type: "char"},
|
||||
color: {string: "Color index", type: "int"},
|
||||
name: {string: "Partner Type", type: "char", searchable: true},
|
||||
color: {string: "Color index", type: "integer", searchable: true},
|
||||
},
|
||||
records: [
|
||||
{id: 12, display_name: "gold", color: 2},
|
||||
@@ -539,6 +539,154 @@ QUnit.module('basic_fields', {
|
||||
"date input should have the focus (make sure the test window had the focus)");
|
||||
});
|
||||
|
||||
QUnit.module('FieldDomain');
|
||||
|
||||
QUnit.test('basic domain field usage is ok', function (assert) {
|
||||
assert.expect(6);
|
||||
|
||||
this.data.partner.records[0].foo = "[]";
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<group>' +
|
||||
'<field name="foo" widget="domain" options="{\'model\': \'partner_type\'}"/>' +
|
||||
'</group>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
// As the domain is empty, there should be a button to add the first
|
||||
// domain part
|
||||
var $domain = form.$(".o_form_field_domain");
|
||||
var $domainAddFirstNodeButton = $domain.find(".o_domain_add_first_node_button");
|
||||
assert.equal($domainAddFirstNodeButton.length, 1,
|
||||
"there should be a button to create first domain element");
|
||||
|
||||
// Clicking on the button should add the [["id", "=", "1"]] domain, so
|
||||
// there should be a field selector in the DOM
|
||||
$domainAddFirstNodeButton.click();
|
||||
var $fieldSelector = $domain.find(".o_field_selector");
|
||||
assert.equal($fieldSelector.length, 1,
|
||||
"there should be a field selector");
|
||||
|
||||
// Focusing the field selector input should open the field selector
|
||||
// popover
|
||||
$fieldSelector.find("> input").focus();
|
||||
var $fieldSelectorPopover = $fieldSelector.find(".o_field_selector_popover");
|
||||
assert.ok($fieldSelectorPopover.is(":visible"),
|
||||
"field selector popover should be visible");
|
||||
|
||||
// The popover should contain the list of partner_type fields and so
|
||||
// there should be the "Color index" field
|
||||
var $lis = $fieldSelectorPopover.find("li");
|
||||
var $colorIndex = $();
|
||||
$lis.each(function () {
|
||||
var $li = $(this);
|
||||
if ($li.html().indexOf("Color index") >= 0) {
|
||||
$colorIndex = $li;
|
||||
}
|
||||
});
|
||||
assert.equal($colorIndex.length, 1,
|
||||
"field selector popover should contain 'Color index' field");
|
||||
|
||||
// Clicking on this field should close the popover, then changing the
|
||||
// associated value should reveal one matched record
|
||||
$colorIndex.click();
|
||||
$domain.find(".o_domain_leaf_value_input").val(2).change();
|
||||
assert.equal($domain.find(".o_domain_show_selection_button").text().trim().substr(0, 2), "1 ",
|
||||
"changing color value to 2 should reveal only one record");
|
||||
|
||||
// Saving the form view should show a readonly domain containing the
|
||||
// "color" field
|
||||
form.$buttons.find('.o_form_button_save').click();
|
||||
$domain = form.$(".o_form_field_domain");
|
||||
assert.ok($domain.html().indexOf("color") >= 0,
|
||||
"field selector readonly value should now contain 'color'");
|
||||
});
|
||||
|
||||
QUnit.test('domain field is correctly reset on every view change', function (assert) {
|
||||
assert.expect(7);
|
||||
|
||||
this.data.partner.records[0].foo = '[["id","=",1]]';
|
||||
this.data.partner.fields.bar.type = "char";
|
||||
this.data.partner.records[0].bar = "product";
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch:
|
||||
'<form>' +
|
||||
'<sheet>' +
|
||||
'<group>' +
|
||||
'<field name="bar"/>' +
|
||||
'<field name="foo" widget="domain" options="{\'model\': \'bar\'}"/>' +
|
||||
'</group>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
});
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
// As the domain is equal to [["id", "=", 1]] there should be a field
|
||||
// selector to change this
|
||||
var $domain = form.$(".o_form_field_domain");
|
||||
var $fieldSelector = $domain.find(".o_field_selector");
|
||||
assert.equal($fieldSelector.length, 1,
|
||||
"there should be a field selector");
|
||||
|
||||
// Focusing its input should open the field selector popover
|
||||
$fieldSelector.find("> input").focus();
|
||||
var $fieldSelectorPopover = $fieldSelector.find(".o_field_selector_popover");
|
||||
assert.ok($fieldSelectorPopover.is(":visible"),
|
||||
"field selector popover should be visible");
|
||||
|
||||
// As the value of the "bar" field is "product", the field selector
|
||||
// popover should contain the list of "product" fields
|
||||
var $lis = $fieldSelectorPopover.find("li");
|
||||
var $sampleLi = $();
|
||||
$lis.each(function () {
|
||||
var $li = $(this);
|
||||
if ($li.html().indexOf("Product Name") >= 0) {
|
||||
$sampleLi = $li;
|
||||
}
|
||||
});
|
||||
assert.strictEqual($lis.length, 1,
|
||||
"field selector popover should contain only one field");
|
||||
assert.strictEqual($sampleLi.length, 1,
|
||||
"field selector popover should contain 'Product Name' field");
|
||||
|
||||
// Now change the value of the "bar" field to "partner_type"
|
||||
form.$(".o_field_widget.o_form_input").click().val("partner_type").trigger("input");
|
||||
|
||||
// Refocusing the field selector input should open the popover again
|
||||
$fieldSelector = form.$(".o_field_selector");
|
||||
$fieldSelector.find("> input").focus();
|
||||
$fieldSelectorPopover = $fieldSelector.find(".o_field_selector_popover");
|
||||
assert.ok($fieldSelectorPopover.is(":visible"),
|
||||
"field selector popover should be visible");
|
||||
|
||||
// Now the list of fields should be the ones of the "partner_type" model
|
||||
$lis = $fieldSelectorPopover.find("li");
|
||||
$sampleLi = $();
|
||||
$lis.each(function () {
|
||||
var $li = $(this);
|
||||
if ($li.html().indexOf("Color index") >= 0) {
|
||||
$sampleLi = $li;
|
||||
}
|
||||
});
|
||||
assert.strictEqual($lis.length, 2,
|
||||
"field selector popover should contain two fields");
|
||||
assert.strictEqual($sampleLi.length, 1,
|
||||
"field selector popover should contain 'Color index' field");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user