[MERGE] project_issue: days since creation date added
[odoo/odoo.git] / addons / base_gantt / static / src / js / gantt.js
1 /*---------------------------------------------------------
2  * OpenERP base_gantt
3  *---------------------------------------------------------*/
4 openerp.base_gantt = function (openerp) {
5 QWeb.add_template('/base_gantt/static/src/xml/base_gantt.xml');
6 openerp.base.views.add('gantt', 'openerp.base_gantt.GanttView');
7 openerp.base_gantt.GanttView = openerp.base.View.extend({
8
9 init: function(parent, element_id, dataset, view_id) {
10         this._super(parent, element_id);
11         this.view_manager = parent || new openerp.base.NullViewManager();
12         this.dataset = dataset;
13         this.model = dataset.model;
14         this.view_id = view_id;
15         this.fields_views = {};
16         this.widgets = {};
17         this.widgets_counter = 0;
18         this.fields = this.dataset.fields ? this.dataset.fields: {};
19         this.ids = this.dataset.ids;
20         this.name = "";
21         this.date_start = "";
22         this.date_delay = "";
23         this.date_stop = "";
24         this.color_field = "";
25         this.colors = [];
26         this.color_values = [];
27         this.calendar_fields = {};
28         this.info_fields = [];
29         this.domain = this.dataset._domain ? this.dataset._domain: [];
30         this.context = this.dataset.context || {};
31     },
32
33     start: function() {
34         this.rpc("/base_gantt/ganttview/load",
35         {"model": this.model, "view_id": this.view_id}, this.on_loaded);
36     },
37
38     on_loaded: function(data) {
39
40         var self = this;
41         this.fields_view = data.fields_view;
42
43         this.name =  this.fields_view.arch.attrs.string;
44         this.view_id = this.fields_view.view_id;
45
46         this.date_start = this.fields_view.arch.attrs.date_start;
47         this.date_delay = this.fields_view.arch.attrs.date_delay;
48         this.date_stop = this.fields_view.arch.attrs.date_stop;
49
50         this.color_field = this.fields_view.arch.attrs.color;
51         this.day_length = this.fields_view.arch.attrs.day_length || 8;
52         this.colors = this.fields_view.arch.attrs.colors;
53         var arch_children = this.fields_view.arch.children[0];
54         this.text = arch_children.children[0] ? arch_children.children[0].attrs.name : arch_children.attrs.name;
55         this.parent = this.fields_view.arch.children[0].attrs.link;
56
57         this.format = "yyyy-MM-dd";
58         this.grp = [];
59
60         self.create_gantt();
61         self.get_events();
62
63         this.$element.html(QWeb.render("GanttView", {"view": this, "fields_view": this.fields_view}));
64
65     },
66
67     create_gantt: function() {
68
69         ganttChartControl = new GanttChart(this.day_length);
70         ganttChartControl.setImagePath("/base_gantt/static/lib/dhtmlxGantt/codebase/imgs/");
71         ganttChartControl.setEditable(true);
72         ganttChartControl.showTreePanel(true);
73         ganttChartControl.showContextMenu(true);
74         ganttChartControl.showDescTask(true,'d,s-f');
75         ganttChartControl.showDescProject(true,'n,d');
76
77     },
78
79     get_events: function() {
80
81         var self = this;
82         this.dataset.read_slice({}, function(result) {
83             self.load_event(result);
84         });
85
86     },
87
88     load_event: function(events) {
89
90         var self = this;
91         var result = events;
92         var smalldate = "";
93
94         COLOR_PALETTE = ['#ccccff', '#cc99ff', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
95                  '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
96                  '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
97                  '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900']
98
99         if (result.length != 0){
100             var show_event = [];
101             for (var i in result){
102                 var res = result[i];
103                 if (res[this.date_start] != false){
104                     
105                     var start_date = this.convert_str_date(res[this.date_start]);
106                     res[this.date_start] = start_date;
107                     show_event.push(res);
108                     if (smalldate == ""){
109                         smalldate = start_date;
110                     }
111                     else{
112                         if (start_date < smalldate){
113                             smalldate = start_date;
114                         }
115                     }
116                 }
117             }
118             if (smalldate == ""){
119                 smalldate = Date.today();
120             }
121             project = new GanttProjectInfo("_1", "", smalldate);
122             ganttChartControl.addProject(project);
123         }
124
125         //create child
126         var k = 0;
127         var color_box = {};
128         var parents = {};
129         var all_events = {};
130         var child_event = {};
131         var temp_id = "";
132         var final_events = [];
133         for (var i in show_event) {
134
135             var res = show_event[i];
136
137             var id = res['id'];
138             var text = res[this.text];
139             var start_date = res[this.date_start];
140
141             var color = res[this.color_field][0] || res[this.color_field];
142             if (color_box[color] == undefined){
143                 color_box[color] = COLOR_PALETTE[k];
144                 k = k + 1;
145             }
146
147             if (this.date_stop != undefined){
148                 if (res[this.date_stop] != false){
149                     var stop_date = this.convert_str_date(res[this.date_stop]);
150                     var duration= self.hours_between(start_date, stop_date);
151                 }
152                 else{
153                     var duration = 0;
154                 }
155             }
156             else{
157                 var duration = res[this.date_delay];
158             }
159             if (duration == false)
160                 duration = 0
161
162             if (self.grp.length){
163                 for (var j in self.grp){
164                     var grp_key = res[self.grp[j]['group_by']];
165                     if (typeof(grp_key) == "object"){
166                         grp_key = res[self.grp[j]['group_by']][1];
167                     }
168                     else{
169                         grp_key = res[self.grp[j]['group_by']];
170                     }
171
172                     if (grp_key == false){
173                         grp_key = "Undefined";
174                     }
175
176                     if (j == 0){
177                         if (parents[grp_key] == undefined){
178                             var mod_id = i+ "_" +j;
179                             parents[grp_key] = mod_id;
180                             child_event[mod_id] = {};
181                             all_events[mod_id] = {'parent': "", 'evt':[mod_id , grp_key, start_date, start_date, 100, "", "white"]};
182                         }
183                         else{
184                             mod_id = parents[grp_key];
185                         }
186                         temp_id = mod_id;
187                     }else{
188                         if (child_event[mod_id][grp_key] == undefined){
189                             var ch_mod_id = i+ "_" +j;
190                             child_event[mod_id][grp_key] = ch_mod_id;
191                             child_event[ch_mod_id] = {};
192                             temp_id = ch_mod_id;
193                             all_events[ch_mod_id] = {'parent': mod_id, 'evt':[ch_mod_id , grp_key, start_date, start_date, 100, "","white"]};
194                             mod_id = ch_mod_id;
195                         }
196                         else{
197                             mod_id = child_event[mod_id][grp_key];
198                             temp_id = mod_id;
199                         }
200                     }
201                 }
202                 all_events[id] = {'parent': temp_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
203                 final_events.push(id);
204             }
205             else {
206                 if (i == 0) {
207                     var mod_id = "_" + i;
208                     all_events[mod_id] = {'parent': "", 'evt': [mod_id, this.name, start_date, start_date, 100, "", "white"]};
209                 }
210                 all_events[id] = {'parent': mod_id, 'evt':[id , text, start_date, duration, 100, "", color_box[color]]};
211                 final_events.push(id);
212             }
213         }
214
215         for (var i in final_events){
216             var evt_id = final_events[i];
217             var evt_date = all_events[evt_id]['evt'][2];
218             while (all_events[evt_id]['parent'] != "") {
219                var parent_id =all_events[evt_id]['parent'];
220                if (all_events[parent_id]['evt'][2] > evt_date){
221                     all_events[parent_id]['evt'][2] = evt_date;
222                }
223                evt_id = parent_id;
224             }
225         }
226         var evt_id = [];
227         var evt_date = "";
228         var evt_duration = "";
229         var evt_end_date = "";
230
231         for (var i in final_events){
232             evt_id = final_events[i];
233             evt_date = all_events[evt_id]['evt'][2];
234             evt_duration = all_events[evt_id]['evt'][3];
235
236             evt_str_date = this.convert_date_str(evt_date);
237             evt_end_date = this.end_date(evt_str_date, evt_duration);
238
239             while (all_events[evt_id]['parent'] != "") {
240                var parent_id =all_events[evt_id]['parent'];
241                if (all_events[parent_id]['evt'][3] < evt_end_date){
242                     all_events[parent_id]['evt'][3] = evt_end_date;
243                }
244                evt_id = parent_id;
245             }
246         }
247
248         for (var j in self.grp) {
249             self.render_events(all_events, j);
250         }
251
252         if (!self.grp.length) {
253             self.render_events(all_events, 0);
254         }
255
256         for (var i in final_events){
257             evt_id = final_events[i];
258             res = all_events[evt_id];
259             task=new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
260             prt = project.getTaskById(res['parent']);
261             prt.addChildTask(task);
262         }
263
264         var oth_hgt = 264;
265         var min_hgt = 150;
266         var name_min_wdt = 150;
267         var gantt_hgt = jQuery(window).height() - oth_hgt;
268         var search_wdt = jQuery("#oe_app_search").width();
269
270         if (gantt_hgt > min_hgt){
271             jQuery('#GanttDiv').height(gantt_hgt).width(search_wdt);
272         } else{
273             jQuery('#GanttDiv').height(min_hgt).width(search_wdt);
274         }
275
276         ganttChartControl.create("GanttDiv");
277         ganttChartControl.attachEvent("onTaskStartDrag", function(task) {self.on_drag_start(task);});
278         ganttChartControl.attachEvent("onTaskEndResize", function(task) {self.on_resize_drag_end(task, "resize");});
279         ganttChartControl.attachEvent("onTaskEndDrag", function(task) {self.on_resize_drag_end(task, "drag");});
280         ganttChartControl.attachEvent("onTaskDblClick", function(task) {self.open_popup(task);});
281
282         var taskdiv = jQuery("div.taskPanel").parent();
283         taskdiv.addClass('ganttTaskPanel');
284         taskdiv.prev().addClass('ganttDayPanel');
285         var $gantt_panel = jQuery(".ganttTaskPanel , .ganttDayPanel");
286
287         var ganttrow = jQuery('.taskPanel').closest('tr');
288         var gtd =  ganttrow.children(':first-child');
289         gtd.children().addClass('task-name');
290
291         jQuery(".toggle-sidebar").click(function(e) {
292             self.set_width();
293         });
294
295         jQuery(window).bind('resize',function(){
296             window.clearTimeout(ganttChartControl._resize_timer);
297             ganttChartControl._resize_timer = window.setTimeout(function(){
298                 self.reload_gantt();
299             }, 200);
300         });
301
302         jQuery("div #_1, div #_1 + div").hide();
303     },
304
305     set_width: function() {
306
307         $gantt_panel.width(1);
308         jQuery(".ganttTaskPanel").parent().width(1);
309
310         var search_wdt = jQuery("#oe_app_search").width();
311         var day_wdt = jQuery(".ganttDayPanel").children().children().width();
312         jQuery('#GanttDiv').css('width','100%');
313
314         if (search_wdt - day_wdt <= name_min_wdt){
315             jQuery(".ganttTaskPanel").parent().width(search_wdt - name_min_wdt);
316             jQuery(".ganttTaskPanel").width(search_wdt - name_min_wdt);
317             jQuery(".ganttDayPanel").width(search_wdt - name_min_wdt - 14);
318             jQuery('.task-name').width(name_min_wdt);
319             jQuery('.task-name').children().width(name_min_wdt);
320         }else{
321             jQuery(".ganttTaskPanel").parent().width(day_wdt);
322             jQuery(".ganttTaskPanel").width(day_wdt);
323             jQuery(".taskPanel").width(day_wdt - 16);
324             jQuery(".ganttDayPanel").width(day_wdt -16);
325             jQuery('.task-name').width(search_wdt - day_wdt);
326             jQuery('.task-name').children().width(search_wdt - day_wdt);
327         }
328
329     },
330
331     end_date: function(dat, duration) {
332
333          var self = this;
334
335          var dat = this.convert_str_date(dat);
336
337          var day = Math.floor(duration/self.day_length);
338          var hrs = duration % self.day_length;
339
340          dat.add(day).days();
341          dat.add(hrs).hour();
342
343          return dat;
344     },
345
346     hours_between: function(date1, date2, parent_task) {
347
348         var ONE_DAY = 1000 * 60 * 60 * 24;
349         var date1_ms = date1.getTime();
350         var date2_ms = date2.getTime();
351         var difference_ms = Math.abs(date1_ms - date2_ms);
352
353         var d = parent_task? Math.ceil(difference_ms / ONE_DAY) : Math.floor(difference_ms / ONE_DAY);
354         var h = (difference_ms % ONE_DAY)/(1000 * 60 * 60);
355         var num = (d * this.day_length) + h;
356         return parseFloat(num.toFixed(2));
357
358     },
359
360     render_events : function(all_events, j) {
361
362         var self = this;
363         for (var i in all_events){
364             var res = all_events[i];
365             if ((typeof(res['evt'][3])) == "object"){
366                 res['evt'][3] = self.hours_between(res['evt'][2],res['evt'][3], true);
367             }
368
369             k = res['evt'][0].toString().indexOf('_');
370
371             if (k != -1) {
372                 if (res['evt'][0].substring(k) == "_"+j){
373                     if (j == 0){
374                         task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
375                         project.addTask(task);
376                     } else {
377                         task = new GanttTaskInfo(res['evt'][0], res['evt'][1], res['evt'][2], res['evt'][3], res['evt'][4], "",res['evt'][6]);
378                         prt = project.getTaskById(res['parent']);
379                         prt.addChildTask(task);
380                     }
381                 }
382             }
383         }
384     },
385
386     open_popup : function(task) {
387         var event_id = task.getId();
388         if(event_id.toString().search("_") != -1)
389             return;
390         if(event_id) event_id = parseInt(event_id, 10);
391
392         var action = {
393             "res_model": this.dataset.model,
394             "res_id": event_id,
395             "views":[[false,"form"]],
396             "type":"ir.actions.act_window",
397             "view_type":"form",
398             "view_mode":"form"
399         }
400
401         action.flags = {
402             search_view: false,
403             sidebar : false,
404             views_switcher : false,
405             pager: false
406             }
407         var element_id = _.uniqueId("act_window_dialog");
408         var dialog = jQuery('<div>', {
409             'id': element_id
410             }).dialog({
411                 modal: true,
412                 width: 'auto',
413                 height: 'auto',
414                 buttons: {
415                     Cancel: function() {
416                         $(this).dialog("destroy");
417                     },
418                     Save: function() {
419                         var view_manager = action_manager.viewmanager;
420                         var _dialog = this;
421                         view_manager.views[view_manager.active_view].controller.do_save(function(r) {
422                             $(_dialog).dialog("destroy");
423                             self.reload_gantt();
424                         })
425                     }
426                 }
427         });
428         var action_manager = new openerp.base.ActionManager(this, element_id);
429         action_manager.start();
430         action_manager.do_action(action);
431
432         //Default_get
433         if(!event_id) action_manager.viewmanager.dataset.index = null;
434     },
435
436     on_drag_start : function(task){
437         var st_date = task.getEST();
438         if(st_date.getHours()){
439             self.hh = st_date.getHours();
440             self.mm = st_date.getMinutes();
441         }
442     },
443
444     on_resize_drag_end : function(task, evt){
445
446         var event_id = task.getId();
447         var data = {};
448
449         if(event_id.toString().search("_") != -1)
450             return;
451         if (evt == "drag"){
452             full_date = task.getEST().set({hour: self.hh, minute : self.mm, second:0});
453             data[this.date_start] = this.convert_date_str(full_date);
454         }
455         if (this.date_stop != undefined){
456             tm = (task.getDuration() % this.day_length);
457             stp = task.getFinishDate().add(tm).hour();
458             data[this.date_stop] = this.convert_date_str(stp);
459         }else{
460             data[this.date_delay] = task.getDuration();
461         }
462         this.dataset.write(event_id, data, function(result) {});
463
464     },
465
466     do_show: function () {
467         this.$element.show();
468     },
469
470     do_hide: function () {
471         this.$element.hide();
472     },
473
474     convert_str_date: function (str){
475         if (str.length == 19){
476             this.format = "yyyy-MM-dd HH:mm:ss";
477             return openerp.base.parse_datetime(str);
478         } else if (str.length == 10){
479             this.format = "yyyy-MM-dd";
480             return openerp.base.parse_date(str);
481         } else if (str.length == 8){
482             this.format = "HH:mm:ss";
483             return openerp.base.parse_time(str);
484         }
485         throw "Unrecognized date/time format";
486     },
487
488     convert_date_str: function(full_date) {
489         if (this.format == "yyyy-MM-dd HH:mm:ss"){
490             return openerp.base.format_datetime(full_date);
491         } else if (this.format == "yyyy-MM-dd"){
492             return openerp.base.format_date(full_date);
493         } else if (this.format == "HH:mm:ss"){
494             return openerp.base.format_time(full_date);
495         }
496         throw "Unrecognized date/time format";
497     },
498
499     reload_gantt: function() {
500         var self = this;
501         this.dataset.read_slice({}, function(response) {
502             ganttChartControl.clearAll();
503             jQuery("#GanttDiv").children().remove();
504             self.load_event(response);
505         });
506     },
507
508     do_search: function (domains, contexts, groupbys) {
509         var self = this;
510         this.grp = groupbys;
511         return this.rpc('/base/session/eval_domain_and_context', {
512             domains: domains,
513             contexts: contexts,
514             group_by_seq: groupbys
515         }, function (results) {
516             self.dataset.context = results.context;
517             self.dataset.domain = results.domain;
518             self.reload_gantt();
519         });
520     }
521
522 });
523
524 // here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
525
526 };
527 // vim:et fdc=0 fdl=0: