1 openerp.web_kanban = function (openerp) {
3 var QWeb = openerp.web.qweb;
4 QWeb.add_template('/web_kanban/static/src/xml/web_kanban.xml');
5 openerp.web.views.add('kanban', 'openerp.web_kanban.KanbanView');
6 openerp.web_kanban.KanbanView = openerp.web.View.extend({
7 init: function (parent, dataset, view_id, options) {
9 this.set_default_options(options);
10 this.dataset = dataset;
11 this.model = dataset.model;
12 this.domain = dataset.domain;
13 this.context = dataset.context;
14 this.view_id = view_id;
15 this.fields_view = {};
17 this.source_index = {};
18 this.all_display_data = false;
20 this.qweb = new QWeb2.Engine();
22 this.NO_OF_COLUMNS = 3;
23 if (this.options.action_views_ids.form) {
24 this.form_dialog = new openerp.web.FormDialog(this, {}, this.options.action_views_ids.form, dataset).start();
25 this.form_dialog.on_form_dialog_saved.add_last(this.on_record_saved);
30 return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type": "kanban"}, this.on_loaded);
32 on_loaded: function(data) {
34 this.fields_view = data;
35 this.add_qweb_template();
36 if (this.qweb.has_template('kanban-box')) {
37 this.do_actual_search();
40 add_qweb_template: function() {
41 var group_operator = ["avg", "max", "min", "sum", "count"]
42 for (var i=0, ii=this.fields_view.arch.children.length; i < ii; i++) {
43 var child = this.fields_view.arch.children[i];
44 if (child.tag === "field") {
45 for(j=0, jj=group_operator.length; j < jj; j++) {
46 if (child.attrs[group_operator[j]]) {
47 this.aggregates[child.attrs.name] = child.attrs[group_operator[j]];
52 if (child.tag === "templates") {
53 this.transform_qweb_template(child);
54 this.qweb.add_template(openerp.web.json_node_to_xml(child));
59 kanban_color: function(variable) {
60 var number_of_color_schemes = 8,
62 switch (typeof(variable)) {
64 for (var i=0, ii=variable.length; i<ii; i++) {
65 index += variable.charCodeAt(i);
69 index = Math.round(variable);
74 return 'oe_kanban_color_' + ((index % number_of_color_schemes) || number_of_color_schemes);
76 kanban_gravatar: function(email, size) {
78 var email_md5 = '2eb60ad22dadcf4dc456b28390a80268';
79 return 'http://www.gravatar.com/avatar/' + email_md5 + '.png?s=' + size;
81 transform_qweb_template: function(node) {
82 var qweb_prefix = QWeb.prefix;
86 node.attrs['t-esc'] = 'record.' + node.attrs['name'] + '.value';
90 var type = node.attrs.type || '';
91 if (_.indexOf('action,object,edit,delete,color'.split(','), type) !== -1) {
92 _.each(node.attrs, function(v, k) {
93 if (_.indexOf('icon,type,name,string,context,states'.split(','), k) != -1) {
94 node.attrs['data-' + k] = v;
95 delete(node.attrs[k]);
98 if (node.attrs['data-states']) {
99 var states = _.map(node.attrs['data-states'].split(','), function(state) {
100 return "record.state.value == '" + _.trim(state) + "'";
102 node.attrs['t-if'] = states.join(' or ');
104 if (node.attrs['data-string']) {
105 node.attrs.title = node.attrs['data-string'];
107 if (node.attrs['data-icon']) {
111 src: '/web/static/src/img/icons/' + node.attrs['data-icon'] + '.png',
117 if (node.tag == 'a') {
118 node.attrs.href = '#';
120 node.attrs.type = 'button';
122 node.attrs['class'] = (node.attrs['class'] || '') + ' oe_kanban_action oe_kanban_action_' + node.tag;
127 for (var i = 0, ii = node.children.length; i < ii; i++) {
128 this.transform_qweb_template(node.children[i]);
132 sort_group: function (first, second) {
133 if (first.header && second.header)
135 first = first.header.toLowerCase();
136 second = second.header.toLowerCase();
137 if (first > second) return 1;
138 else if (first < second) return -1;
143 on_show_data: function() {
145 if (!this.group_by.length) {
146 this.do_record_group();
148 self.all_display_data.sort(this.sort_group);
149 this.$element.html(QWeb.render("KanbanView", {"data": self.all_display_data}));
150 this.on_reload_kanban();
151 this.$element.find(".oe_vertical_text").hide();
152 var drag_handel = false;
153 if (this.$element.find(".oe_kanban_draghandle").length > 0) {
154 drag_handel = ".oe_kanban_draghandle";
156 if (!this.group_by.length) {
159 this.$element.find(".oe_column").sortable({
160 connectWith: ".oe_column",
161 handle : drag_handel,
162 start: function(event, ui) {
163 self.source_index['index'] = ui.item.index();
164 self.source_index['column'] = ui.item.parent().attr('id');
166 stop: self.on_receive_record,
169 this.$element.find(".oe_column").disableSelection()
170 this.$element.find('button.oe_kanban_button_new').click(this.do_add_record);
171 this.$element.find(".fold-columns-icon").click(function(event) {
172 self.do_fold_unfold_columns(event, this.id);
175 do_fold_unfold_columns: function(event, element_id) {
176 var column_id = "column_" + element_id;
177 var column_element = this.$element.find("#" + column_id + " .oe_fold_column");
178 if (column_element.is(":hidden")) {
179 this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/minus-icon.png');
180 column_element.show();
181 this.$element.find("#" + column_id + ".oe_table_column").css("width",Math.round(99 / this.all_display_data.length) + "%");
182 this.$element.find("#" + column_id + ".oe_vertical_text").hide();
185 this.$element.find("#" + column_id).find("img.fold-columns-icon").attr('src', '/web_kanban/static/src/img/plus-icon.png');
186 column_element.hide();
187 this.$element.find("#" + column_id + ".oe_table_column").css("width","0.5%");
188 this.$element.find("#" + column_id + ".oe_vertical_text").show();
192 do_record_group: function() {
193 if (this.NO_OF_COLUMNS && this.all_display_data.length > 0) {
194 var records = this.all_display_data[0].records;
195 var record_per_group = Math.round((records).length / this.NO_OF_COLUMNS);
196 this.all_display_data = [];
197 for (var i=0, ii=this.NO_OF_COLUMNS; i < ii; i++) {
198 this.all_display_data.push({'records': records.slice(0,record_per_group), 'value':i, 'header' : false, 'ids':[]});
199 records.splice(0,record_per_group);
203 on_button_click: function (button_attrs, record_id) {
204 this.on_execute_button_click(this.dataset, button_attrs, record_id);
206 do_add_record: function() {
207 this.dataset.index = null;
208 this.do_switch_view('form');
210 do_edit_record: function(record_id) {
211 if (this.form_dialog) {
212 this.form_dialog.load_id(record_id);
213 this.form_dialog.open();
215 this.notification.warn("Kanban", "No form view defined for this object");
218 on_record_saved: function(r) {
219 var id = this.form_dialog.form.datarecord.id;
220 // TODO fme: reload record instead of all. need refactoring
221 this.do_actual_search();
223 do_change_color: function(record_id, $e) {
226 colors = '#FFC7C7,#FFF1C7,#E3FFC7,#C7FFD5,#C7FFFF,#C7D5FF,#E3C7FF,#FFC7F1'.split(','),
227 $cpicker = $(QWeb.render('KanbanColorPicker', { colors : colors, columns: 2 }));
229 $cpicker.mouseenter(function() {
230 clearTimeout($cpicker.data('timeoutId'));
231 }).mouseleave(function(evt) {
232 var timeoutId = setTimeout(function() { $cpicker.remove() }, 500);
233 $cpicker.data('timeoutId', timeoutId);
235 $cpicker.find('a').click(function() {
237 data[$e.data('name')] = $(this).data('color');
238 self.dataset.write(id, data, {}, function() {
239 // TODO fme: reload record instead of all. need refactoring
240 self.on_reload_record(id, data);
246 Reload one record in view.
247 record_id : reload record id.
248 data : change value in particular record.
250 on_reload_record: function (record_id, data){
252 for (var i=0, ii=this.all_display_data.length; i < ii; i++) {
253 for(j=0, jj=this.all_display_data[i].records.length; j < jj; j++) {
254 if (this.all_display_data[i].records[j].id == record_id) {
255 _.extend(this.all_display_data[i].records[j], data);
256 this.$element.find("#main_" + record_id).children().remove();
257 this.$element.find("#main_" + record_id).append(this.qweb.render('kanban-box', {
258 record: this.do_transform_record(this.all_display_data[i].records[j]),
259 kanban_color: this.kanban_color,
260 kanban_gravatar: this.kanban_gravatar
266 this.$element.find("#main_" + record_id + " .oe_kanban_action").click(this.on_action_clicked);
267 this.$element.find("#main_" + record_id + " .oe_kanban_box_show_onclick_trigger").click(function() {
268 $(this).parent("#main_" + record_id + " .oe_kanban_box").find(".oe_kanban_box_show_onclick").toggle();
271 do_delete: function (id) {
273 return $.when(this.dataset.unlink([id])).then(function () {
274 self.drop_records(id);
277 drop_records: function (id) {
279 _.each(self.all_display_data, function(data, index) {
280 _.each(data.records, function(record, index_row) {
281 if (parseInt(record.id) == id) {
282 self.all_display_data[index]['records'].splice(index_row, 1);
283 self.all_display_data[index]['ids'].splice(index_row, 1);
288 self.$element.find("#main_" + id).remove();
290 on_execute_button_click: function (dataset, button_attrs, record_id) {
292 this.do_execute_action(
293 button_attrs, dataset,
294 record_id, function () {
295 self.do_actual_search();
299 on_receive_record: function (event, ui) {
301 var from = ui.item.index();
302 var search_action = false;
303 var to = ui.item.prev().index() || 0;
304 if (!ui.item.attr("id")) {
307 // TODO fme: check what was this sequence
308 if (self.fields_view.fields.sequence != undefined && ((self.source_index.index >= 0 && self.source_index.index != from) ||
309 (self.source_index.column && self.source_index.column != ui.item.parent().attr('id')))) {
310 var child_record = ui.item.parent().children();
311 var data, sequence = 1, index = to;
312 child_record.splice(0, to);
314 if (to >= 0 && child_record) {
315 var record_id = parseInt($(child_record).attr("id").split("_")[1]);
317 _.each(self.all_display_data, function(data, index) {
318 _.each(data.records, function(record, index_row) {
319 if(record_id == record.id && record.sequence) {
320 sequence = record.sequence;
325 if(flag) {return false;}
329 _.each(child_record, function (child) {
330 var child_id = parseInt($(child).attr("id").split("_")[1]);
333 _.each(self.all_display_data, function(data, index) {
334 _.each(data.records, function(record, index_row) {
335 if(parseInt(record.id) == child_id) {
336 self.all_display_data[index]['records'][index_row]['sequence'] = sequence;
341 if (flag) {return false;}
343 self.dataset.write(child_id, {sequence: sequence});
345 search_action = true;
349 if (self.group_by.length > 0 && self.source_index.column && self.source_index.column != ui.item.parent().attr('id')) {
350 var value = ui.item.closest("td").attr("id");
353 var wirte_id = parseInt(ui.item.attr("id").split("_")[1]);
354 value = value.split("_")[1];
355 if (value == 'false') {
358 var update_record = false;
359 _.each(self.all_display_data, function(data, index) {
360 _.each(data.records, function(record, index_row) {
361 if(parseInt(record.id) == wirte_id) {
362 self.all_display_data[index]['records'][index_row][self.group_by[0]] = value;
363 update_record = self.all_display_data[index]['records'].splice(index_row,1)
367 if (update_record) {return false;}
369 _.each(self.all_display_data, function(data, index) {
370 if (data.value == value || (data.value == 'false' && value == false)) {
371 self.all_display_data[index]['records'].push(update_record[0]);
374 data_val[self.group_by[0]] = value;
375 self.dataset.write(wirte_id, data_val);
376 search_action = true;
380 self.on_reload_kanban();
382 this.source_index = {};
384 on_reload_kanban: function (){
386 _.each(self.all_display_data, function(data, index) {
387 if (data.records.length > 0){
388 _.each(data.records, function(record) {
389 self.$element.find("#main_" + record.id).children().remove();
390 self.$element.find("#main_" + record.id).append(self.qweb.render('kanban-box', {
391 record: self.do_transform_record(record),
392 kanban_color: self.kanban_color,
393 kanban_gravatar: self.kanban_gravatar
398 this.$element.find('.oe_kanban_action').click(this.on_action_clicked);
399 this.$element.find('.oe_kanban_box_show_onclick_trigger').click(function() {
400 $(this).parent('.oe_kanban_box').find('.oe_kanban_box_show_onclick').toggle();
403 on_action_clicked: function(evt) {
404 var $action = $(evt.currentTarget),
405 record_id = parseInt($action.closest(".oe_kanban_record").attr("id").split('_')[1]),
406 type = $action.data('type');
407 if (type == 'delete') {
408 this.do_delete(record_id);
409 } else if (type == 'edit') {
410 this.do_edit_record(record_id);
411 } else if (type == 'color') {
412 this.do_change_color(record_id, $action);
414 var button_attrs = $action.data();
415 this.on_button_click(button_attrs, record_id);
418 do_transform_record: function(record) {
421 _.each(record, function(value, name) {
422 var r = _.clone(self.fields_view.fields[name]);
424 r.value = openerp.web.format_value(value, r);
425 new_record[name] = r;
429 do_search: function (domains, contexts, group_by) {
431 this.rpc('/web/session/eval_domain_and_context', {
432 domains: [this.dataset.get_domain()].concat(domains),
433 contexts: [this.dataset.get_context()].concat(contexts),
434 group_by_seq: group_by
435 }, function (results) {
436 self.domain = results.domain;
437 self.context = results.context;
438 self.group_by = results.group_by;
439 self.do_actual_search();
442 do_actual_search : function () {
444 group_by = self.group_by;
445 if (!group_by.length && this.fields_view.arch.attrs.default_group_by) {
446 group_by = [this.fields_view.arch.attrs.default_group_by];
447 self.group_by = group_by;
449 self.datagroup = new openerp.web.DataGroup(self, self.model, self.domain, self.context, group_by);
450 self.datagroup.list([],
452 self.groups = groups;
454 self.do_render_group(groups);
457 self.all_display_data = [];
463 self.dataset.read_slice([], {'domain': self.domain, 'context': self.context}, function(records) {
464 if (records.length) self.all_display_data = [{'records': records, 'value':false, 'header' : false, 'ids': self.dataset.ids}];
465 else self.all_display_data = [];
466 self.$element.find(".oe_kanban_view").remove();
472 do_render_group : function (datagroups) {
473 this.all_display_data = [];
475 _.each(datagroups, function (group) {
476 var group_name = group.value;
477 var group_value = group.value;
479 group_name = "Undefined";
480 group_value = 'false';
481 } else if (group.value instanceof Array) {
482 group_name = group.value[1];
483 group_value = group.value[0];
485 var group_aggregates = '';
486 _.each(self.aggregates, function(value, key) {
487 group_aggregates += value + ": " + group.aggregates[key];
489 self.dataset.read_slice([], {'domain': group.domain, 'context': group.context}, function(records) {
490 self.all_display_data.push({"value" : group_value, "records": records, 'header':group_name, 'ids': self.dataset.ids, 'aggregates': group_aggregates});
491 if (datagroups.length == self.all_display_data.length) {
492 self.$element.find(".oe_kanban_view").remove();
498 do_show: function () {
499 this.$element.show();
501 do_hide: function () {
502 this.$element.hide();
507 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: