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