[FIX] Generate Groups.
[odoo/odoo.git] / addons / web_gantt / static / src / js / gantt.js
1 /*---------------------------------------------------------
2  * OpenERP web_gantt
3  *---------------------------------------------------------*/
4 openerp.web_gantt = function (openerp) {
5 var _t = openerp.web._t;
6 var QWeb = openerp.web.qweb;
7
8 QWeb.add_template('/web_gantt/static/src/xml/web_gantt.xml');
9 openerp.web.views.add('gantt', 'openerp.web_gantt.GanttView');
10 openerp.web_gantt.GanttView = openerp.web.View.extend({
11
12 init: function(parent, dataset, view_id) {
13         this._super(parent);
14         this.view_manager = parent || new openerp.web.NullViewManager();
15         this.dataset = dataset;
16         this.model = dataset.model;
17         this.view_id = view_id;
18         this.domain = this.dataset.domain || [];
19         this.context = this.dataset.context || {};
20         this.has_been_loaded = $.Deferred();
21     },
22
23     start: function() {
24         this._super();
25         return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type": "gantt"}, this.on_loaded);
26     },
27
28     on_loaded: function(data) {
29         this.fields_view = data,
30         this.name =  this.fields_view.arch.attrs.string,
31         this.view_id = this.fields_view.view_id,
32         this.fields = this.fields_view.fields;
33         
34         this.date_start = this.fields_view.arch.attrs.date_start,
35         this.date_delay = this.fields_view.arch.attrs.date_delay,
36         this.date_stop = this.fields_view.arch.attrs.date_stop,
37         this.day_length = this.fields_view.arch.attrs.day_length || 8;
38
39         this.color_field = this.fields_view.arch.attrs.color,
40         this.colors = this.fields_view.arch.attrs.colors;
41         
42         var level = this.fields_view.arch.children[0];
43         this.parent = level.attrs.link,
44         this.text = level.children.length ? level.children[0].attrs.name : level.attrs.name;
45         
46         if (!this.date_start) {
47             return self.do_warn(_t("date_start is not defined "))
48         }
49         
50         this.$element.html(QWeb.render("GanttView", {'height': $('.oe-application-container').height(), 'width': $('.oe-application-container').width()}));
51         this.has_been_loaded.resolve();
52     },
53     
54     on_project_loaded: function(projects) {
55         
56         if(!projects.length) return;
57         var self = this,
58             started_projects = _.filter(projects, function(res) {
59             return res[self.date_start];
60         });
61         
62         this.database_projects = started_projects;
63         
64         if(!self.name) {
65             var name = started_projects[0][self.parent];
66             self.name = name instanceof Array? name[name.length - 1] : name;
67         }
68         
69         $.when(this.project_starting_date(), this.get_project_duration(), this.calculate_difference())
70             .then(function() {
71                 if(self.ganttChartControl) {
72                     self.ganttChartControl.clearAll();
73                     self.$element.find('#GanttView').empty();
74                 }
75             })
76             .then(this.group_projects())
77             .then(this.generate_projects())
78             .then(this.add_tasks())
79             .done(this.init_gantt_view());
80     },
81     
82     generate_projects : function() {
83         var projects = this.database_projects,
84             self = this;
85         
86         this.GanttProjects = [],
87         this.GanttTasks = [];
88         if(this.GroupProject) {
89             _.each(this.GroupProject, function(grp, index) {
90                 self.GanttProjects.push(new GanttProjectInfo(index, grp, self.project_start_date));
91                 self.GanttTasks.push(new GanttTaskInfo(index, grp, self.project_start_date, self.total_duration, 100, ""));
92             });
93         } else {
94             this.GanttProjects.push(new GanttProjectInfo(0, self.name, self.project_start_date));
95             this.GanttTasks.push(new GanttTaskInfo(0, self.name, self.project_start_date, self.total_duration, 100, ""));
96         }
97         
98         return $.Deferred().resolve().promise();
99     },
100     
101     group_projects: function() {
102         var def = $.Deferred(),
103             self = this,
104             projects = this.database_projects;
105             
106         if (!this.group_by.length) return def.resolve().promise();
107         
108         var groups = _.pluck(projects, this.group_by[0]);
109         this.GroupProject = [];
110         _.each(groups, function(grp) {
111             if(grp instanceof Array) {
112                 grp = grp[grp.length - 1];
113             }
114             if(!_.include(self.GroupProject,grp))
115                 self.GroupProject.push(grp);
116         });
117         
118         return def.resolve().promise();
119     },
120     
121     get_project_duration: function() {
122         
123         var self = this,
124             projects = this.database_projects;
125             
126         this.project_duration = [];
127         
128         _.each(projects, function(project, index) {
129             if (self.date_stop && project[self.date_stop]) {
130                 //ToDO
131                 console.log('TODO for date_stop');
132                 self.project_duration.push(0);
133             } else if(self.date_delay && project[self.date_delay]) {
134                 self.project_duration.push(project[self.date_delay]);
135             } else {
136                 self.project_duration.push(0);
137             }
138         });
139         
140         this.max_project_duration = _.max(this.project_duration);
141         return $.Deferred().resolve().promise();
142     },
143     
144     calculate_difference: function() {
145         var extend_end_date_day = Math.floor(this.max_project_duration / this.day_length),
146             extend_end_date_hours = this.max_project_duration % this.day_length;
147         
148         this.project_end_date = this.project_end_date.add({days: extend_end_date_day, hours: extend_end_date_hours})
149         
150         var DAY = 1000 * 60 * 60 * 24,
151             difference = Math.abs(this.project_start_date.getTime() - this.project_end_date.getTime()),
152             day = Math.ceil(difference / DAY),
153             hour = (difference % DAY)/(1000 * 60 * 60),
154             DiffHour = (day * this.day_length) + hour;
155             
156         this.total_duration = parseFloat(DiffHour.toFixed(2));
157         return $.Deferred().resolve().promise();
158     },
159     
160     add_tasks: function() {
161         var self = this,
162             tasks = this.database_projects;
163         
164         _.each(tasks, function(task, index) {
165             var name = task[self.text];
166             if(task[self.text] instanceof Array) {
167                 name = task[self.text][1];
168             }
169             self.GanttTasks[0].addChildTask(
170                 new GanttTaskInfo(task.id, name, self.format_date(task[self.date_start]), self.project_duration[index], 100, "")
171             );
172         });
173         
174         return $.Deferred().resolve().promise();
175     },
176     
177     project_starting_date : function() {
178         var self = this,
179             projects = this.database_projects,
180             min_date = _.min(projects, function(prj) {
181                 return new Date(prj[self.date_start]);
182             }),
183             max_date = _.max(projects, function(prj) {
184                 return self.format_date(prj[self.date_start]);
185             });
186             
187         this.project_end_date =  this.format_date(max_date[self.date_start]);
188         if (min_date) this.project_start_date = this.format_date(min_date[self.date_start]);
189         else 
190             this.project_start_date = Date.today();
191         return $.Deferred().resolve().promise();
192     },
193
194     init_gantt_view: function() {
195         
196         
197         this.GanttProjects[0].addTask(this.GanttTasks[0]);
198         var self = this;
199         
200         var ganttChartControl = this.ganttChartControl = new GanttChart();
201
202         // Setup paths and behavior
203         ganttChartControl.setImagePath("/web_gantt/static/lib/dhtmlxGantt/codebase/imgs/");
204         ganttChartControl.setEditable(true);
205         ganttChartControl.showTreePanel(true);
206         ganttChartControl.showContextMenu(false);
207         ganttChartControl.showDescTask(true,'d,s-f');
208         ganttChartControl.showDescProject(true,'n,d');
209         
210         // Load data structure      
211         ganttChartControl.addProject(this.GanttProjects[0]);
212         // Create Gantt control
213         ganttChartControl.create('GanttView');
214         
215         // Setup Events
216         ganttChartControl.attachEvent("onTaskStartDrag", function(task) {
217             var task_date = task.getEST();
218             if(task_date.getHours()) {
219                 task_date.set({hour: task_date.getHours(), minute : task_date.getMinutes(), second:0});
220             }
221         });
222         ganttChartControl.attachEvent("onTaskEndResize", function(task) {return self.ResizeTask(task);});
223         ganttChartControl.attachEvent("onTaskEndDrag", function(task) {return self.ResizeTask(task);});
224         ganttChartControl.attachEvent("onTaskDblClick", function(task) { return self.editTask(task);});
225     },
226     
227     format_date : function(date) {
228         var datetime_regex = /^(\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d)(?:\.\d+)?$/,
229             date_regex = /^\d\d\d\d-\d\d-\d\d$/,
230             time_regex = /^(\d\d:\d\d:\d\d)(?:\.\d+)?$/,
231             def = $.Deferred();
232         if(date_regex.exec(date)) {
233             this.date_format = "yyyy-MM-dd";
234         } else if(time_regex.exec(date)) {
235             this.date_format = "HH:mm:ss";
236         } else {
237             this.date_format = "yyyy-MM-dd HH:mm:ss";
238         }
239         return openerp.web.auto_str_to_date(date);
240     },
241     
242     ResizeTask: function(task) {
243         
244         var event_id = task.getId();
245         
246         
247         if(!event_id)
248             return this.do_warn(_t("Project can not be resized"));
249             
250         var data = {};
251         data[this.date_start] = task.getEST().toString(this.date_format);
252         
253         if(this.date_stop) {
254             var diff = task.getDuration() % this.day_length,
255                 finished_date = task.getFinishDate().add({hours: diff});
256             data[this.date_stop] = finished_date.toString(this.date_format);
257         } else {
258             data[this.date_delay] = task.getDuration();
259         }
260         this.dataset
261             .write(event_id, data, {})
262             .done(function() {
263                 self.reloadView();
264             });
265     },
266     
267     editTask: function(task) {
268         var self = this;
269         var event_id = task.getId();
270         if(!event_id)
271             return false;
272             
273         if(event_id) event_id = parseInt(event_id, 10);
274         
275         var action_manager = new openerp.web.ActionManager(this);
276         
277         var dialog = new openerp.web.Dialog(this, {
278             width: 800,
279             height: 600,
280             buttons : {
281                 Cancel : function() {
282                     $(this).dialog('destroy');
283                 },
284                 Save : function() {
285                     var form_view = action_manager.inner_viewmanager.views.form.controller;
286
287                     form_view.do_save(function() {
288                         self.reloadView();
289                     });
290                     $(this).dialog('destroy');
291                 }
292             }
293         }).start().open();
294         action_manager.appendTo(dialog.$element);
295         action_manager.do_action({
296             res_model : this.dataset.model,
297             res_id: event_id,
298             views : [[false, 'form']],
299             type : 'ir.actions.act_window',
300             auto_search : false,
301             flags : {
302                 search_view: false,
303                 sidebar : false,
304                 views_switcher : false,
305                 action_buttons : false,
306                 pager: false
307             }
308         });
309     },
310     
311     reloadView: function() {
312        self.on_project_loaded(self.database_projects);
313     },
314
315     do_show: function () {
316         this.$element.show();
317     },
318
319     do_hide: function () {
320         this.$element.hide();
321     },
322
323     do_search: function (domains, contexts, groupbys) {
324         var self = this;
325         this.group_by = groupbys;
326         $.when(this.has_been_loaded).then(function() {
327             self.dataset
328                 .read_slice([], {
329                     domain: domains,
330                     context: contexts,
331                     group_by: groupbys
332                 })
333                 .done(function(projects) {
334                     self.on_project_loaded(projects);
335                 });
336         })
337         
338     }
339
340 });
341
342 // here you may tweak globals object, if any, and play with on_* or do_* callbacks on them
343
344 };
345 // vim:et fdc=0 fdl=0: