diff --git a/addons/base/controllers/main.py b/addons/base/controllers/main.py index e21f4fb853f..923df69ca2d 100644 --- a/addons/base/controllers/main.py +++ b/addons/base/controllers/main.py @@ -357,6 +357,28 @@ class DataSet(openerpweb.Controller): reads = Model.read(ids, fields or False, request.context) reads.sort(key=lambda obj: ids.index(obj['id'])) return reads + + @openerpweb.jsonrequest + def read(self, request, model, ids, fields=False): + return self.do_search_read(request, model, ids, fields) + def search_read(self, request, model, ids, fields=False): + """ Performs a read() + + :param request: a JSON-RPC request object + :type request: openerpweb.JsonRequest + :param str model: the name of the model to search on + :param ids: the ids of the records + :type ids: [?] + :param fields: a list of the fields to return in the result records + :type fields: [str] + :returns: a list of result records + :rtype: list + """ + Model = request.session.model(model) + + reads = Model.read(ids, fields or False, request.context) + reads.sort(key=lambda obj: ids.index(obj['id'])) + return reads @openerpweb.jsonrequest def get(self, request, model, ids, fields=False): @@ -424,6 +446,12 @@ class DataSet(openerpweb.Controller): m = req.session.model(model) r = m.default_get(fields, context) return {'result': r} + + @openerpweb.jsonrequest + def name_search(self, req, model, search_str, domain=[], context={}): + m = req.session.model(model) + r = m.name_search(search_str+'%', domain, '=ilike', context) + return {'result': r} class DataGroup(openerpweb.Controller): _cp_path = "/base/group" diff --git a/addons/base/static/src/base.html b/addons/base/static/src/base.html index 74ce3c267b6..39909413397 100644 --- a/addons/base/static/src/base.html +++ b/addons/base/static/src/base.html @@ -28,6 +28,7 @@ + diff --git a/addons/base/static/src/css/base.css b/addons/base/static/src/css/base.css index f5a375708ce..9b3ac6ef47c 100644 --- a/addons/base/static/src/css/base.css +++ b/addons/base/static/src/css/base.css @@ -825,3 +825,12 @@ body.openerp { border-color: #828282; } +/* Many2one Autosearch */ +.openerp .ui_combo { + height: 20px; + margin-right: 1px; + top: 4px; + width: 22px; +} +/* ------------- End autocomplete ------- */ + diff --git a/addons/base/static/src/img/icons/index1.jpeg b/addons/base/static/src/img/icons/index1.jpeg new file mode 100644 index 00000000000..f43c68d79ec Binary files /dev/null and b/addons/base/static/src/img/icons/index1.jpeg differ diff --git a/addons/base/static/src/img/icons/index2.jpeg b/addons/base/static/src/img/icons/index2.jpeg new file mode 100644 index 00000000000..ec9d1eb6d3c Binary files /dev/null and b/addons/base/static/src/img/icons/index2.jpeg differ diff --git a/addons/base/static/src/js/base.js b/addons/base/static/src/js/base.js index 86ef80877cb..19d608e0260 100644 --- a/addons/base/static/src/js/base.js +++ b/addons/base/static/src/js/base.js @@ -126,6 +126,7 @@ openerp.base = function(instance) { openerp.base.views(instance); openerp.base.search(instance); openerp.base.list(instance); + openerp.base.m2o(instance); openerp.base.form(instance); }; diff --git a/addons/base/static/src/js/data.js b/addons/base/static/src/js/data.js index 0cef3c83e0f..95c09ef483f 100644 --- a/addons/base/static/src/js/data.js +++ b/addons/base/static/src/js/data.js @@ -213,12 +213,12 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. context: this.context }, callback); }, - create: function(data, callback) { + create: function(data, callback, error_callback) { return this.rpc('/base/dataset/create', { model: this.model, data: data, context: this.context - }, callback); + }, callback, error_callback); }, write: function (id, data, callback) { return this.rpc('/base/dataset/save', { @@ -243,6 +243,15 @@ openerp.base.DataSet = openerp.base.Controller.extend( /** @lends openerp.base. args: args }, callback); }, + name_search: function (search_str, callback) { + search_str = search_str || ''; + return this.rpc('/base/dataset/name_search', { + model: this.model, + search_str: search_str, + domain: this.domain || [], + context: this.context + }, callback); + }, exec_workflow: function (id, signal, callback) { return this.rpc('/base/dataset/exec_workflow', { model: this.model, diff --git a/addons/base/static/src/js/form.js b/addons/base/static/src/js/form.js index c5ea8918552..dc631c1256a 100644 --- a/addons/base/static/src/js/form.js +++ b/addons/base/static/src/js/form.js @@ -886,10 +886,33 @@ openerp.base.form.FieldSelection = openerp.base.form.Field.extend({ } }); +openerp.base.form.FieldMany2OneDatasSet = openerp.base.DataSetStatic.extend({ + start: function() { + }, + write: function (id, data, callback) { + this._super(id, data, callback); + }, +}); + +openerp.base.form.FieldMany2OneViewManager = openerp.base.ViewManager.extend({ + init: function(session, element_id, dataset, views) { + this._super(session, element_id, dataset, views); + } +}); + openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); this.template = "FieldMany2One"; + this.is_field_m2o = true; + }, + start: function() { + this.$element = $('#' + this.element_id); + this.dataset = new openerp.base.form.FieldMany2OneDatasSet(this.session, this.field.relation); + var views = [ [false,"list"], [false,"form"] ]; + this.viewmanager = new openerp.base.form.FieldMany2OneViewManager(this.view.session, this.element_id, this.dataset, views); + new openerp.base.m2o(this.viewmanager, this.$element, this.field.relation, this.dataset, this.session) + this.$element.find('input').change(this.on_ui_change); }, set_value: function(value) { this._super.apply(this, arguments); @@ -899,6 +922,16 @@ openerp.base.form.FieldMany2One = openerp.base.form.Field.extend({ this.value = value[0]; } this.$element.find('input').val(show_value); + this.$element.find('input').attr('m2o_id', this.value); + }, + + get_value: function() { + var val = this.$element.find('input').attr('m2o_id') || this.value + return val; + }, + + on_ui_change: function() { + this.touched = this.view.touched = true; } }); @@ -959,6 +992,8 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ this._super(view, node); this.template = "FieldMany2Many"; this.list_id = _.uniqueId("many2many"); + this.is_started = false; + this.is_setted = false; }, start: function() { this._super.apply(this, arguments); @@ -968,16 +1003,30 @@ openerp.base.form.FieldMany2Many = openerp.base.form.Field.extend({ var self = this; this.list_view.m2m_field = this; this.list_view.start(); + var hack = {loaded: false}; + this.list_view.on_loaded.add_last(function() { + if (! hack.loaded) { + self.is_started = true; + self.check_load(); + hack.loaded = true; + } + }); }, set_value: function(value) { if (value != false) { this.dataset.ids = value; this.dataset.count = value.length; - this.list_view.do_reload(); + this.is_setted = true; + this.check_load(); } }, get_value: function() { return [[6,false,this.dataset.ids]]; + }, + check_load: function() { + if(this.is_started && this.is_setted) { + this.list_view.do_reload(); + } } }); @@ -1002,8 +1051,19 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({ 'context': this.dataset.context }, this.do_fill_table); }, - do_add_record: function () { - // TODO: need to open a popup with search view + do_add_record: function (e) { + e.stopImmediatePropagation(); + var pop = new openerp.base.form.Many2XSelectPopup(null, this.m2m_field.view.session); + pop.select_element(this.model); + var self = this; + pop.on_select_element.add(function(element_id) { + if(! _.detect(self.dataset.ids, function(x) {return x == element_id;})) { + self.dataset.ids.push(element_id); + self.dataset.count = self.dataset.ids.length; + self.do_reload(); + } + pop.stop(); + }); }, select_record: function(index) { var id = this.rows[index].data.id.value; @@ -1023,6 +1083,97 @@ openerp.base.form.Many2ManyListView = openerp.base.ListView.extend({ } }); +openerp.base.form.Many2XSelectPopup = openerp.base.BaseWidget.extend({ + identifier_prefix: "many2xselectpopup", + template: "Many2XSelectPopup", + select_element: function(model) { + this.model = model; + var html = this.render(); + jQuery(html).dialog({title: '', + modal: true, + minWidth: 800}); + this.start(); + }, + start: function() { + this._super(); + this.dataset = new openerp.base.DataSetSearch(this.session, this.model); + this.setup_search_view(); + }, + setup_search_view: function() { + var self = this; + if (this.searchview) { + this.searchview.stop(); + } + this.searchview = new openerp.base.SearchView(this, this.session, this.element_id + "_search", + this.dataset, false, {}); + this.searchview.on_search.add(function(domains, contexts, groupbys) { + self.view_list.do_search.call( + self, domains, contexts, groupbys); + }); + this.searchview.on_loaded.add_last(function () { + var $buttons = self.searchview.$element.find(".oe_search-view-buttons"); + $buttons.append(QWeb.render("Many2XSelectPopup.search.buttons")); + var $nbutton = $buttons.find(".oe_many2xselectpopup-search-new"); + $nbutton.click(function() { + self.new_object(); + }); + var $cbutton = $buttons.find(".oe_many2xselectpopup-search-close"); + $cbutton.click(function() { + self.stop(); + }); + self.view_list = new openerp.base.form.Many2XPopupListView( null, self.session, + self.element_id + "_view_list", self.dataset, false); + self.view_list.popup = self; + self.view_list.do_show(); + self.view_list.start(); + var tmphack = {"loaded": false}; + self.view_list.on_loaded.add_last(function() { + if ( !tmphack.loaded ) { + self.view_list.do_reload(); + tmphack.loaded = true; + }; + }); + }); + this.searchview.start(); + }, + on_select_element: function(element_id) { + }, + new_object: function() { + var self = this; + this.searchview.hide(); + this.view_list.$element.hide(); + this.dataset.index = null; + this.view_form = new openerp.base.FormView({}, this.session, + this.element_id + "_view_form", this.dataset, false); + this.view_form.start(); + this.view_form.on_loaded.add_last(function() { + var $buttons = self.view_form.$element.find(".oe_form_buttons"); + $buttons.html(QWeb.render("Many2XSelectPopup.form.buttons")); + var $nbutton = $buttons.find(".oe_many2xselectpopup-form-save"); + $nbutton.click(function() { + self.view_form.do_save(); + }); + var $cbutton = $buttons.find(".oe_many2xselectpopup-form-close"); + $cbutton.click(function() { + self.stop(); + }); + }); + this.view_form.on_created.add_last(function(r, success) { + if (r.result) { + var id = arguments[0].result; + self.on_select_element(id); + } + }); + this.view_form.do_show(); + } +}); + +openerp.base.form.Many2XPopupListView = openerp.base.ListView.extend({ + switch_to_record: function(index) { + this.popup.on_select_element(this.dataset.ids[index]); + } +}); + openerp.base.form.FieldReference = openerp.base.form.Field.extend({ init: function(view, node) { this._super(view, node); diff --git a/addons/base/static/src/js/m2o.js b/addons/base/static/src/js/m2o.js new file mode 100644 index 00000000000..b20eab0689e --- /dev/null +++ b/addons/base/static/src/js/m2o.js @@ -0,0 +1,121 @@ +openerp.base.m2o = function(openerp){ + + openerp.base.m2o = openerp.base.Controller.extend({ + init: function(view_manager, element_id, model, dataset, session){ + this._super(element_id, model, dataset, session); + + this.view_manager = view_manager + this.session = session; + this.element = element_id.find('input'); + this.dataset = dataset; + var cache = {}; + var lastXhr; + this.relation = model; + this.result_ids = [] + var self = this + + var $input = this.element.autocomplete({ + + source: function(request, response){ + var search_val = request.term; + if (search_val in cache) { + response(cache[search_val]); + return; + } + //pass request to server + lastXhr = self.dataset.name_search(search_val, function(obj, status, xhr){ + var result = obj.result + var values = []; + if (!result.length) { + values.push({'value': 'Create...', id: 'create'}) + } + + $.each(result, function(i, val){ + values.push({ + value: val[1], + id: val[0], + orig_val: val[1] + }); + self.result_ids.push(result[i][0]) + }); + + if (values.length > 10) { + values = values.slice(0, 10); + values.push({'value': 'More...', id: 'more'}) + } + //process response + cache[search_val] = values; + response(values); + }); + return; + }, + + select: function(event, ui){ + ui.item.value = ui.item.orig_val? ui.item.orig_val : self.element.data( "autocomplete" ).term + if (ui.item.id == 'more') { + self.dataset.ids = self.result_ids; + self.dataset.count = self.dataset.ids.length; + self.dataset.domain = [] + self.element.val('') + var pop = new openerp.base.form.Many2XSelectPopup(null, self.session); + pop.select_element(self.relation, self.dataset); + return; + } + + if (ui.item.id == 'create') { + var val = self.element.val() + self.dataset.create({'name': ui.item.value}, + function(r){}, function(r){ + var element_id = _.uniqueId("act_window_dialog"); + var dialog = jQuery('
', + {'id': element_id + }).dialog({ + modal: true, + minWidth: 800 + }); + self.element.val('') + var event_form = new openerp.base.FormView(self.view_manager, self.session, element_id, self.dataset, false); + event_form.start(); + }); + $input.val(self.element.data( "autocomplete" ).term); + return true; + } + self.element.attr('m2o_id', ui.item.id); + }, + + minLength: 0, + + focus: function(event, ui) { + if (ui.item.id == ('create')) { + return true; + } + ui.item.value = self.element.data("autocomplete").term.length ? self.element.val() + '[' + ui.item.orig_val.substring(self.element.data("autocomplete").term.length) + ']' : this.lastSearch + }, + + }); + + $("
 
") + .attr("tabIndex", -1) + .attr("title", "Show All Items") + .insertAfter($input) + .button({ + icons: { + primary: "ui-icon-triangle-1-s" + }, + text: false + }) + .removeClass("ui-corner-all") + .addClass("ui-corner-right ui-button-icon") + .click(function() { + // close if already visible + if ($input.autocomplete("widget").is(":visible")) { + $input.autocomplete( "close" ); + return; + } + $(this).blur(); + $input.autocomplete("search", "" ); + $input.focus(); + }); + } + }); +} \ No newline at end of file diff --git a/addons/base/static/src/js/search.js b/addons/base/static/src/js/search.js index ae5e8551b93..efaca3c0239 100644 --- a/addons/base/static/src/js/search.js +++ b/addons/base/static/src/js/search.js @@ -121,7 +121,10 @@ openerp.base.SearchView = openerp.base.Controller.extend({ 'lines': lines, 'defaults': this.defaults }); - this.$element.html(render); + // We don't understand why the following commented line does not work in Chrome but + // the non-commented line does. As far as we investigated, only God knows. + //this.$element.html(render); + jQuery(render).appendTo(this.$element); var f = this.$element.find('form'); this.$element.find('form') diff --git a/addons/base/static/src/xml/base.xml b/addons/base/static/src/xml/base.xml index d6d5d8d5620..856db3cedbd 100644 --- a/addons/base/static/src/xml/base.xml +++ b/addons/base/static/src/xml/base.xml @@ -257,7 +257,7 @@ +
@@ -396,7 +398,7 @@

-
+
@@ -504,7 +506,8 @@ -
+
+
@@ -584,4 +587,19 @@

+ +
+
+
+
+
+
+ + + + + + + + diff --git a/addons/base/static/test/test.html b/addons/base/static/test/test.html index 5c7fb1282d5..d7638a4f4bd 100644 --- a/addons/base/static/test/test.html +++ b/addons/base/static/test/test.html @@ -22,6 +22,7 @@ +