Support create record from autocomplete with default values
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Support create record from autocomplete
|
||||
* Use grid layout for form container
|
||||
* Support scanning code
|
||||
* Reset search offset when domain has changed
|
||||
|
||||
+19
-5
@@ -3740,15 +3740,15 @@
|
||||
Sao.common.set_overflow(this.input, 'show');
|
||||
});
|
||||
},
|
||||
set_actions: function(action_activated, search, create) {
|
||||
set_actions: function(action_activated, search=true, create=true) {
|
||||
if (action_activated !== undefined) {
|
||||
this.action_activated = action_activated;
|
||||
}
|
||||
var actions = [];
|
||||
if (search || search == undefined) {
|
||||
if (search) {
|
||||
actions.push(['search', Sao.i18n.gettext('Search...')]);
|
||||
}
|
||||
if (create || create == undefined) {
|
||||
if (create) {
|
||||
actions.push(['create', Sao.i18n.gettext('Create...')]);
|
||||
}
|
||||
this.menu.find('li.action').remove();
|
||||
@@ -3840,7 +3840,7 @@
|
||||
});
|
||||
|
||||
Sao.common.get_completion = function(el, source,
|
||||
match_selected, action_activated, search, create) {
|
||||
match_selected, action_activated, search=true, create=true) {
|
||||
var format = function(content) {
|
||||
return content.name;
|
||||
};
|
||||
@@ -3849,6 +3849,7 @@
|
||||
if (action_activated) {
|
||||
completion.set_actions(action_activated, search, create);
|
||||
}
|
||||
completion._allow_create = create;
|
||||
return completion;
|
||||
};
|
||||
|
||||
@@ -3867,7 +3868,20 @@
|
||||
var sao_model = new Sao.Model(model);
|
||||
return sao_model.execute(
|
||||
'autocomplete',
|
||||
[search_text, domain, Sao.config.limit, order], context).fail(function() {
|
||||
[search_text, domain, Sao.config.limit, order], context)
|
||||
.then(
|
||||
function(result) {
|
||||
return result.filter((value) => {
|
||||
return value.id || entry.completion._allow_create;
|
||||
}).map((value) => {
|
||||
if (value.id === null) {
|
||||
value.name = Sao.i18n.gettext(
|
||||
'Create "%1"...', value.name);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
},
|
||||
function() {
|
||||
Sao.Logger.warning(
|
||||
"Unable to search for completion of %s", model);
|
||||
});
|
||||
|
||||
+21
-5
@@ -2515,6 +2515,7 @@ function eval_pyson(value){
|
||||
group,
|
||||
this._update_completion.bind(this),
|
||||
this._completion_match_selected.bind(this));
|
||||
this.entry.completion = this.wid_completion;
|
||||
}
|
||||
this.el.change(this.focus_out.bind(this));
|
||||
this._readonly = false;
|
||||
@@ -2908,9 +2909,13 @@ function eval_pyson(value){
|
||||
this.entry, record, field, model);
|
||||
},
|
||||
_completion_match_selected: function(value) {
|
||||
this.record.field_set_client(this.field_name,
|
||||
if (value.id !== null) {
|
||||
this.record.field_set_client(this.field_name,
|
||||
this.value_from_id(
|
||||
value.id, value.rec_name), true);
|
||||
} else {
|
||||
this.new_(value.defaults);
|
||||
}
|
||||
},
|
||||
_completion_action_activated: function(action) {
|
||||
if (action == 'search') {
|
||||
@@ -3188,6 +3193,7 @@ function eval_pyson(value){
|
||||
this._completion_match_selected.bind(this),
|
||||
this._completion_action_activated.bind(this),
|
||||
this.read_access, this.create_access);
|
||||
this.wid_text.completion = this.wid_completion;
|
||||
}
|
||||
|
||||
buttons = jQuery('<div/>', {
|
||||
@@ -3749,8 +3755,12 @@ function eval_pyson(value){
|
||||
this.wid_text, this.record, this.field, model, domain);
|
||||
},
|
||||
_completion_match_selected: function(value) {
|
||||
this.screen.group.load([value.id], true);
|
||||
this.wid_text.val('');
|
||||
if (value.id !== null) {
|
||||
this.screen.group.load([value.id], true);
|
||||
this.wid_text.val('');
|
||||
} else {
|
||||
this.new_(value.defaults);
|
||||
}
|
||||
},
|
||||
_completion_action_activated: function(action) {
|
||||
if (action == 'search') {
|
||||
@@ -3813,6 +3823,7 @@ function eval_pyson(value){
|
||||
this._completion_match_selected.bind(this),
|
||||
this._completion_action_activated.bind(this),
|
||||
this.read_access, this.create_access);
|
||||
this.entry.completion = this.wid_completion;
|
||||
}
|
||||
|
||||
var buttons = jQuery('<div/>', {
|
||||
@@ -4106,8 +4117,12 @@ function eval_pyson(value){
|
||||
this.entry, this.record, this.field, model, domain);
|
||||
},
|
||||
_completion_match_selected: function(value) {
|
||||
this.screen.group.load([value.id], true);
|
||||
this.entry.val('');
|
||||
if (value.id !== null) {
|
||||
this.screen.group.load([value.id], true);
|
||||
this.entry.val('');
|
||||
} else {
|
||||
this.new_(value.defaults);
|
||||
}
|
||||
},
|
||||
_completion_action_activated: function(action) {
|
||||
if (action == 'search') {
|
||||
@@ -4754,6 +4769,7 @@ function eval_pyson(value){
|
||||
group,
|
||||
this._update_completion.bind(this),
|
||||
this._completion_match_selected.bind(this));
|
||||
this.wid_text.completion = this.wid_completion;
|
||||
}
|
||||
|
||||
this.but_add = jQuery('<button/>', {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* Support create record from autocomplete
|
||||
* Support scanning code
|
||||
* Reset search offset when domain has changed
|
||||
* Add a canonicalize function for domains
|
||||
|
||||
@@ -17,13 +17,14 @@ def get_completion(search=True, create=True):
|
||||
"Return a EntryCompletion"
|
||||
completion = Gtk.EntryCompletion()
|
||||
completion.set_match_func(lambda *a: True)
|
||||
completion.set_model(Gtk.ListStore(str, int))
|
||||
completion.set_model(Gtk.ListStore(str, object, object))
|
||||
completion.set_text_column(0)
|
||||
completion.props.popup_set_width = False
|
||||
if search:
|
||||
completion.insert_action_markup(0, _('<i>Search...</i>'))
|
||||
if create:
|
||||
completion.insert_action_markup(1, _('<i>Create...</i>'))
|
||||
completion._allow_create = create
|
||||
return completion
|
||||
|
||||
|
||||
@@ -34,7 +35,8 @@ def update_completion(entry, record, field, model, domain=None):
|
||||
return False
|
||||
if search_text != entry.get_text():
|
||||
return False
|
||||
completion_model = entry.get_completion().get_model()
|
||||
completion = entry.get_completion()
|
||||
completion_model = completion.get_model()
|
||||
if not search_text or not model:
|
||||
completion_model.clear()
|
||||
completion_model.search_text = search_text
|
||||
@@ -55,7 +57,13 @@ def update_completion(entry, record, field, model, domain=None):
|
||||
return False
|
||||
completion_model.clear()
|
||||
for result in results:
|
||||
completion_model.append([result['name'], result['id']])
|
||||
if result['id'] is not None or completion._allow_create:
|
||||
if result['id'] is None:
|
||||
name = _('Create "%s"...') % result['name']
|
||||
else:
|
||||
name = result['name']
|
||||
completion_model.append((
|
||||
name, result['id'], result['defaults']))
|
||||
completion_model.search_text = search_text
|
||||
# Force display of popup
|
||||
entry.emit('changed')
|
||||
|
||||
@@ -307,14 +307,17 @@ class Many2Many(Widget):
|
||||
return True
|
||||
|
||||
def _completion_match_selected(self, completion, model, iter_):
|
||||
record_id, = model.get(iter_, 1)
|
||||
self.screen.load([record_id], modified=True)
|
||||
self.wid_text.set_text('')
|
||||
self.wid_text.grab_focus()
|
||||
record_id, defaults = model.get(iter_, 1, 2)
|
||||
if record_id is not None:
|
||||
self.screen.load([record_id], modified=True)
|
||||
self.wid_text.set_text('')
|
||||
self.wid_text.grab_focus()
|
||||
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = self.wid_text.get_text()
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = self.wid_text.get_text()
|
||||
else:
|
||||
self._sig_new(defaults)
|
||||
return True
|
||||
|
||||
def _update_completion(self, widget):
|
||||
|
||||
@@ -347,15 +347,18 @@ class Many2One(Widget):
|
||||
self.wid_text.set_completion(self.wid_completion)
|
||||
|
||||
def _completion_match_selected(self, completion, model, iter_):
|
||||
rec_name, record_id = model.get(iter_, 0, 1)
|
||||
# GTK on win32 doesn't like synchronous call to set_client
|
||||
# because it triggers a display which reset the completion
|
||||
GLib.idle_add(self.field.set_client, self.record,
|
||||
self.value_from_id(record_id, rec_name), True)
|
||||
rec_name, record_id, defaults = model.get(iter_, 0, 1, 2)
|
||||
if record_id is not None:
|
||||
# GTK on win32 doesn't like synchronous call to set_client
|
||||
# because it triggers a display which reset the completion
|
||||
GLib.idle_add(self.field.set_client, self.record,
|
||||
self.value_from_id(record_id, rec_name), True)
|
||||
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = rec_name
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = rec_name
|
||||
else:
|
||||
self.sig_new(defaults)
|
||||
return True
|
||||
|
||||
def _update_completion(self, widget):
|
||||
|
||||
@@ -564,14 +564,17 @@ class One2Many(Widget):
|
||||
return True
|
||||
|
||||
def _completion_match_selected(self, completion, model, iter_):
|
||||
record_id, = model.get(iter_, 1)
|
||||
self.screen.load([record_id], modified=True)
|
||||
self.wid_text.set_text('')
|
||||
self.wid_text.grab_focus()
|
||||
record_id, defaults = model.get(iter_, 1, 2)
|
||||
if record_id is not None:
|
||||
self.screen.load([record_id], modified=True)
|
||||
self.wid_text.set_text('')
|
||||
self.wid_text.grab_focus()
|
||||
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = self.wid_text.get_text()
|
||||
completion_model = self.wid_completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = self.wid_text.get_text()
|
||||
else:
|
||||
self._sig_new(defaults)
|
||||
return True
|
||||
|
||||
def _update_completion(self, widget):
|
||||
|
||||
@@ -979,14 +979,25 @@ class M2O(GenericText):
|
||||
|
||||
def _completion_match_selected(
|
||||
self, completion, model, iter_, record, field, model_name):
|
||||
rec_name, record_id = model.get(iter_, 0, 1)
|
||||
field.set_client(
|
||||
record, self.value_from_id(model_name, record_id, rec_name))
|
||||
rec_name, record_id, defaults = model.get(iter_, 0, 1, 2)
|
||||
if record_id is not None:
|
||||
field.set_client(
|
||||
record, self.value_from_id(model_name, record_id, rec_name))
|
||||
|
||||
completion.get_entry().set_text(rec_name)
|
||||
completion_model = completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = rec_name
|
||||
completion.get_entry().set_text(rec_name)
|
||||
completion_model = completion.get_model()
|
||||
completion_model.clear()
|
||||
completion_model.search_text = rec_name
|
||||
else:
|
||||
entry = completion.get_entry()
|
||||
entry.handler_block(entry.editing_done_id)
|
||||
|
||||
def callback():
|
||||
entry.handler_unblock(entry.editing_done_id)
|
||||
entry.set_text(field.get_client(record))
|
||||
self.open_remote(
|
||||
record, create=True, text=entry.get_text(), defaults=defaults,
|
||||
callback=callback)
|
||||
return True
|
||||
|
||||
def _update_completion(self, entry, record, field):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
* Support default values for autocomplete
|
||||
* Support default value from context
|
||||
* Add autocomplete method on ModelView
|
||||
* Add likify tools
|
||||
|
||||
@@ -245,10 +245,14 @@ Instance methods:
|
||||
.. method:: ModelView.autocomplete(text[, domain[, limit[, order]]])
|
||||
|
||||
Return a list of dictionary for records completing ``text``.
|
||||
The dictionary is composed of ``id`` and ``name``.
|
||||
The dictionary is composed of ``id``, ``name`` and ``defaults``.
|
||||
|
||||
The client call this method to provide completion.
|
||||
|
||||
.. note::
|
||||
The ``defaults`` is used when ``id`` is empty to get the default values
|
||||
for the record to create.
|
||||
|
||||
ModelStorage
|
||||
============
|
||||
|
||||
|
||||
@@ -813,7 +813,9 @@ class ModelView(Model):
|
||||
if issubclass(cls, ModelStorage):
|
||||
result = cls.search_read(
|
||||
domain, limit=limit, order=order, fields_names=['rec_name'])
|
||||
result = [{'id': r['id'], 'name': r['rec_name']} for r in result]
|
||||
result = [
|
||||
{'id': r['id'], 'name': r['rec_name'], 'defaults': None}
|
||||
for r in result]
|
||||
return result
|
||||
|
||||
@property
|
||||
|
||||
@@ -734,15 +734,16 @@ class ModelView(unittest.TestCase):
|
||||
self.assertEqual(Model.autocomplete('test'), [])
|
||||
self.assertEqual(
|
||||
Model.autocomplete('foo'),
|
||||
[{'name': 'foo', 'id': foo.id}])
|
||||
[{'name': 'foo', 'id': foo.id, 'defaults': None}])
|
||||
self.assertEqual(
|
||||
Model.autocomplete(''), [
|
||||
{'name': 'foo', 'id': foo.id},
|
||||
{'name': 'bar', 'id': bar.id}])
|
||||
{'name': 'foo', 'id': foo.id, 'defaults': None},
|
||||
{'name': 'bar', 'id': bar.id, 'defaults': None}])
|
||||
self.assertEqual(Model.autocomplete('foo', [('id', '!=', foo.id)]), [])
|
||||
self.assertEqual(
|
||||
Model.autocomplete('', limit=1), [{'name': 'foo', 'id': foo.id}])
|
||||
Model.autocomplete('', limit=1),
|
||||
[{'name': 'foo', 'id': foo.id, 'defaults': None}])
|
||||
self.assertEqual(
|
||||
Model.autocomplete('', order=[('name', 'ASC')]), [
|
||||
{'name': 'bar', 'id': bar.id},
|
||||
{'name': 'foo', 'id': foo.id}])
|
||||
{'name': 'bar', 'id': bar.id, 'defaults': None},
|
||||
{'name': 'foo', 'id': foo.id, 'defaults': None}])
|
||||
|
||||
Reference in New Issue
Block a user