diff --git a/CHANGELOG b/CHANGELOG index e7c66543f3..eb50a48bbf 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Validate dictionary items * Add (i)like support to domain inversion * Improve loading strategy by using views * Add support of width attribute on tree view diff --git a/src/model.js b/src/model.js index 15c6e777cc..3106d877d0 100644 --- a/src/model.js +++ b/src/model.js @@ -2461,6 +2461,11 @@ Sao.field.Dict = Sao.class_(Sao.field.Field, { _default: {}, + init: function(description) { + Sao.field.Dict._super.init.call(this, description); + this.schema_model = new Sao.Model(description.schema_model); + this.keys = {}; + }, set: function(record, value) { if (value) { // Order keys to allow comparison with stringify @@ -2504,6 +2509,75 @@ }, time_format: function(record) { return '%X'; + }, + add_keys: function(keys, record) { + var schema_model = this.description.schema_model; + var context = this.get_context(record); + var domain = this.get_domain(record); + var batchlen = Math.min(10, Sao.config.limit); + + keys = jQuery.extend([], keys); + var get_keys = function(key_ids) { + return this.schema_model.execute('get_keys', + [key_ids], context).then(update_keys); + }.bind(this); + var update_keys = function(values) { + for (var i = 0, len = values.length; i < len; i++) { + var k = values[i]; + this.keys[k.name] = k; + } + }.bind(this); + + var prms = []; + while (keys.length > 0) { + var sub_keys = keys.splice(0, batchlen); + prms.push(this.schema_model.execute('search', + [[['name', 'in', sub_keys], domain], + 0, Sao.config.limit, null], context) + .then(get_keys)); + } + return jQuery.when.apply(jQuery, prms); + }, + add_new_keys: function(ids, record) { + var context = this.get_context(record); + return this.schema_model.execute('get_keys', [ids], context) + .then(function(new_fields) { + var names = []; + new_fields.forEach(function(new_field) { + this.keys[new_field.name] = new_field; + names.push(new_field.name); + }.bind(this)); + return names; + }.bind(this)); + }, + validate: function(record, softvalidation, pre_validate) { + var valid = Sao.field.Dict._super.validate.call( + this, record, softvalidation, pre_validate); + + if (this.description.readonly) { + return valid; + } + + var decoder = new Sao.PYSON.Decoder(); + var field_value = this.get_eval(record); + var domain = []; + for (var key in field_value) { + if (!(key in this.keys)) { + continue; + } + var key_domain = this.keys[key].domain; + if (key_domain) { + domain.push(decoder.decode(key_domain)); + } + } + + var inversion = new Sao.common.DomainInversion(); + var valid_value = inversion.eval_domain(domain, field_value); + if (!valid_value) { + this.get_state_attrs(record).invalid = 'domain'; + } + + return valid && valid_value; } }); }()); diff --git a/src/view/form.js b/src/view/form.js index 45e0eaeb99..f6b493a0fd 100644 --- a/src/view/form.js +++ b/src/view/form.js @@ -1052,17 +1052,19 @@ function eval_pyson(value){ } else { this.el.removeClass('readonly'); } + var required_el = this._required_el(); this.set_required(required); if (!readonly && required) { - this.el.addClass('required'); + required_el.addClass('required'); } else { - this.el.removeClass('required'); + required_el.removeClass('required'); } var invalid = state_attrs.invalid; + var invalid_el = this._invalid_el(); if (!readonly && invalid) { - this.el.addClass('has-error'); + invalid_el.addClass('has-error'); } else { - this.el.removeClass('has-error'); + invalid_el.removeClass('has-error'); } if (invisible === undefined) { invisible = field.get_state_attrs(record).invisible; @@ -1072,6 +1074,12 @@ function eval_pyson(value){ } this.set_invisible(invisible); }, + _required_el: function () { + return this.el; + }, + _invalid_el: function() { + return this.el; + }, record: function() { if (this.view && this.view.screen) { return this.view.screen.current_record; @@ -3980,8 +3988,7 @@ function eval_pyson(value){ Sao.View.Form.Dict._super.init.call( this, field_name, model, attributes); - this.schema_model = new Sao.Model(attributes.schema_model); - this.keys = {}; + this.schema_model = attributes.schema_model; this.fields = {}; this.rows = {}; @@ -4038,6 +4045,12 @@ function eval_pyson(value){ this._readonly = false; this._record_id = null; }, + _required_el: function() { + return this.wid_text; + }, + _invalid_el: function() { + return this.wid_text; + }, add: function() { var context = this.field().get_context(this.record()); var value = this.wid_text.val(); @@ -4054,7 +4067,7 @@ function eval_pyson(value){ }.bind(this); var parser = new Sao.common.DomainParser(); - var win = new Sao.Window.Search(this.schema_model.name, + var win = new Sao.Window.Search(this.schema_model, callback, { sel_multi: true, context: context, @@ -4065,19 +4078,17 @@ function eval_pyson(value){ }); }, add_new_keys: function(ids) { - var context = this.field().get_context(this.record()); - this.schema_model.execute('get_keys', [ids], context) - .then(function(new_fields) { + var field = this.field(); + field.add_new_keys(ids, this.record()) + .then(function(new_names) { var focus = false; - new_fields.forEach(function(new_field) { - if (this.fields[new_field.name]) { - return; - } - this.keys[new_field.name] = new_field; - this.add_line(new_field.name); - if (!focus) { - this.fields[new_field.name].input.focus(); - focus = true; + new_names.forEach(function(name) { + if (!(name in this.fields)) { + this.add_line(name); + if (!focus) { + this.fields[name].input.focus(); + focus = true; + } } }.bind(this)); }.bind(this)); @@ -4130,13 +4141,14 @@ function eval_pyson(value){ }, add_line: function(key) { var field, row; - this.fields[key] = field = new (this.get_entries( - this.keys[key].type_))(key, this); + var key_schema = this.field().keys[key]; + this.fields[key] = field = new ( + this.get_entries(key_schema.type_))(key, this); this.rows[key] = row = jQuery('
', { 'class': 'row' }); // TODO RTL - var text = this.keys[key].string + Sao.i18n.gettext(':'); + var text = key_schema.string + Sao.i18n.gettext(':'); var label = jQuery('', { 'text': text }).appendTo(jQuery('', { @@ -4156,33 +4168,6 @@ function eval_pyson(value){ row.appendTo(this.container); }, - add_keys: function(keys) { - var context = this.field().get_context(this.record()); - var domain = this.field().get_domain(this.record()); - var batchlen = Math.min(10, Sao.config.limit); - keys = jQuery.extend([], keys); - - var get_keys = function(key_ids) { - return this.schema_model.execute('get_keys', - [key_ids], context).then(update_keys); - }.bind(this); - var update_keys = function(values) { - for (var i = 0, len = values.length; i < len; i++) { - var k = values[i]; - this.keys[k.name] = k; - } - }.bind(this); - - var prms = []; - while (keys.length > 0) { - var sub_keys = keys.splice(0, batchlen); - prms.push(this.schema_model.execute('search', - [[['name', 'in', sub_keys], domain], - 0, Sao.config.limit, null], context) - .then(get_keys)); - } - return jQuery.when.apply(jQuery, prms); - }, display: function(record, field) { Sao.View.Form.Dict._super.display.call(this, record, field); @@ -4202,22 +4187,24 @@ function eval_pyson(value){ var value = field.get_client(record); var new_key_names = Object.keys(value).filter(function(e) { - return !this.keys[e]; + return !this.fields[e]; }.bind(this)); var prm; if (!jQuery.isEmptyObject(new_key_names)) { - prm = this.add_keys(new_key_names); + prm = field.add_keys(new_key_names, record); } else { prm = jQuery.when(); } prm.then(function() { var i, len, key; var keys = Object.keys(value).sort(); + var decoder = new Sao.PYSON.Decoder(); + var inversion = new Sao.common.DomainInversion(); for (i = 0, len = keys.length; i < len; i++) { key = keys[i]; var val = value[key]; - if (!this.keys[key]) { + if (!field.keys[key]) { continue; } if (!this.fields[key]) { @@ -4226,6 +4213,15 @@ function eval_pyson(value){ var widget = this.fields[key]; widget.set_value(val); widget.set_readonly(this._readonly); + var key_domain = (decoder.decode(field.keys[key].domain || + 'null')); + if (key_domain !== null) { + if (!inversion.eval_domain(key_domain, value)) { + widget.el.addClass('has-error'); + } else { + widget.el.removeClass('has-error'); + } + } } var removed_key_names = Object.keys(this.fields).filter( function(e) { @@ -4264,7 +4260,7 @@ function eval_pyson(value){ class_: 'dict-char', init: function(name, parent_widget) { this.name = name; - this.definition = parent_widget.keys[name]; + this.definition = parent_widget.field().keys[name]; this.parent_widget = parent_widget; this.create_widget(); },