[IMP] factorize view/load part2 addons
[odoo/odoo.git] / addons / web_calendar / static / src / js / calendar.js
1 /*---------------------------------------------------------
2  * OpenERP web_calendar
3  *---------------------------------------------------------*/
4
5 openerp.web_calendar = function(openerp) {
6 QWeb.add_template('/web_calendar/static/src/xml/web_calendar.xml');
7 openerp.web.views.add('calendar', 'openerp.web_calendar.CalendarView');
8 openerp.web_calendar.CalendarView = openerp.web.View.extend({
9 // Dhtmlx scheduler ?
10     init: function(parent, element_id, dataset, view_id, options) {
11         this._super(parent, element_id);
12         this.set_default_options(options);
13         this.dataset = dataset;
14         this.model = dataset.model;
15         this.view_id = view_id;
16         this.domain = this.dataset.domain || [];
17         this.context = this.dataset.context || {};
18         this.has_been_loaded = $.Deferred();
19         this.creating_event_id = null;
20         this.dataset_events = [];
21         if (this.options.action_views_ids.form) {
22             this.form_dialog = new openerp.web_calendar.CalendarFormDialog(this, {}, this.options.action_views_ids.form, dataset);
23             this.form_dialog.start();
24         }
25         this.COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
26              '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
27              '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
28              '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900' ];
29         this.color_map = {};
30     },
31     start: function() {
32         this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': true}, this.on_loaded);
33     },
34     stop: function() {
35         scheduler.clearAll();
36     },
37     on_loaded: function(data) {
38         this.fields_view = data;
39         this.calendar_fields = {};
40         this.ids = this.dataset.ids;
41         this.color_values = [];
42         this.info_fields = [];
43
44         this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
45         this.view_id = this.fields_view.view_id;
46
47         this.date_start = this.fields_view.arch.attrs.date_start;
48         this.date_delay = this.fields_view.arch.attrs.date_delay;
49         this.date_stop = this.fields_view.arch.attrs.date_stop;
50
51         this.colors = this.fields_view.arch.attrs.colors;
52         this.day_length = this.fields_view.arch.attrs.day_length || 8;
53         this.color_field = this.fields_view.arch.attrs.color;
54         this.fields =  this.fields_view.fields;
55
56         //* Calendar Fields *
57         this.calendar_fields.date_start = {'name': this.date_start, 'kind': this.fields[this.date_start].type};
58
59         if (this.date_delay) {
60             if (this.fields[this.date_delay].type != 'float') {
61                 throw new Error("Calendar view has a 'date_delay' type != float");
62             }
63             this.calendar_fields.date_delay = {'name': this.date_delay, 'kind': this.fields[this.date_delay].type};
64         }
65         if (this.date_stop) {
66             this.calendar_fields.date_stop = {'name': this.date_stop, 'kind': this.fields[this.date_stop].type};
67         }
68         if (!this.date_delay && !this.date_stop) {
69             throw new Error("Calendar view has none of the following attributes : 'date_stop', 'date_delay'");
70         }
71
72         for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
73             this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
74         }
75         this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
76
77         if (this.options.sidebar && this.options.sidebar_id) {
78             this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
79             this.sidebar.start();
80             this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this.sidebar.add_section('navigator', "Navigator"), this);
81             this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this.sidebar.add_section('responsible', "Responsible"), this);
82             this.sidebar.add_toolbar(this.fields_view.toolbar);
83             this.set_common_sidebar_sections(this.sidebar);
84             this.sidebar.do_unfold();
85             this.sidebar.do_fold.add_last(this.refresh_scheduler);
86             this.sidebar.do_unfold.add_last(this.refresh_scheduler);
87             this.sidebar.do_toggle.add_last(this.refresh_scheduler);
88         }
89
90         this.init_scheduler();
91         this.has_been_loaded.resolve();
92         if (this.dataset.ids.length) {
93             this.dataset.read_ids(this.dataset.ids, _.keys(this.fields), this.on_events_loaded);
94         }
95     },
96     init_scheduler: function() {
97         var self = this;
98         scheduler.clearAll();
99         if (this.fields[this.date_start]['type'] == 'time') {
100             scheduler.config.xml_date = "%H:%M:%S";
101         } else {
102             scheduler.config.xml_date = "%Y-%m-%d %H:%i";
103         }
104         scheduler.config.api_date = "%Y-%m-%d %H:%i";
105         scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
106         scheduler.config.start_on_monday = true;
107         scheduler.config.scroll_hour = 8;
108         scheduler.config.drag_resize = true;
109         scheduler.config.drag_create = true;
110
111         // Initialize Sceduler
112         this.mode = this.mode || 'month';
113         scheduler.init('openerp_scheduler', null, this.mode);
114
115         scheduler.detachAllEvents();
116         scheduler.attachEvent('onEventAdded', this.do_create_event);
117         scheduler.attachEvent('onEventDeleted', this.do_delete_event);
118         scheduler.attachEvent('onEventChanged', this.do_save_event);
119         scheduler.attachEvent('onDblClick', this.do_edit_event);
120         scheduler.attachEvent('onBeforeLightbox', this.do_edit_event);
121
122         this.mini_calendar = scheduler.renderCalendar({
123             container: this.sidebar.navigator.element_id,
124             navigation: true,
125             date: scheduler._date,
126             handler: function(date, calendar) {
127                 scheduler.setCurrentView(date, 'day');
128             }
129         });
130     },
131     refresh_scheduler: function() {
132         scheduler.setCurrentView(scheduler._date);
133     },
134     refresh_minical: function() {
135         scheduler.updateCalendar(this.mini_calendar);
136     },
137     reload_event: function(id) {
138         this.dataset.read_ids([id], _.keys(this.fields), this.on_events_loaded);
139     },
140     get_color: function(key) {
141         if (this.color_map[key]) {
142             return this.color_map[key];
143         }
144         var index = _.keys(this.color_map).length % this.COLOR_PALETTE.length;
145         var color = this.COLOR_PALETTE[index];
146         this.color_map[key] = color;
147         return color;
148     },
149     on_events_loaded: function(events, fn_filter, no_filter_reload) {
150         var self = this;
151
152         //To parse Events we have to convert date Format
153         var res_events = [],
154             sidebar_items = {};
155         for (var e = 0; e < events.length; e++) {
156             var evt = events[e];
157             if (!evt[this.date_start]) {
158                 this.notification.warn("Start date is not defined for event :", evt['id']);
159                 break;
160             }
161
162             if (this.color_field) {
163                 var filter = evt[this.color_field];
164                 if (filter) {
165                     var filter_value = (typeof filter === 'object') ? filter[0] : filter;
166                     if (typeof(fn_filter) === 'function' && !fn_filter(filter_value)) {
167                         continue;
168                     }
169                     var filter_item = {
170                         value: filter_value,
171                         label: (typeof filter === 'object') ? filter[1] : filter,
172                         color: this.get_color(filter_value)
173                     };
174                     if (!sidebar_items[filter_value]) {
175                         sidebar_items[filter_value] = filter_item;
176                     }
177                     evt.color = filter_item.color;
178                     evt.textColor = '#ffffff';
179                 }
180             }
181
182             if (this.fields[this.date_start]['type'] == 'date') {
183                 evt[this.date_start] = openerp.web.str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss');
184             }
185             if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
186                 evt[this.date_stop] = openerp.web.str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss');
187             }
188             res_events.push(this.convert_event(evt));
189         }
190         scheduler.parse(res_events, 'json');
191         this.refresh_scheduler();
192         this.refresh_minical();
193         if (!no_filter_reload) {
194             this.sidebar.responsible.on_events_loaded(sidebar_items);
195         }
196     },
197     convert_event: function(evt) {
198         var date_start = openerp.web.str_to_datetime(evt[this.date_start]),
199             date_stop = this.date_stop ? openerp.web.str_to_datetime(evt[this.date_stop]) : null,
200             date_delay = evt[this.date_delay] || null,
201             res_text = '',
202             res_description = [];
203
204         if (this.info_fields) {
205             var fld = evt[this.info_fields[0]];
206             res_text = (typeof fld == 'object') ? fld[fld.length -1] : res_text = fld;
207
208             var sliced_info_fields = this.info_fields.slice(1);
209             for (var sl_fld in sliced_info_fields) {
210                 var slc_fld = evt[sliced_info_fields[sl_fld]];
211                 if (typeof slc_fld == 'object') {
212                     res_description.push(slc_fld[slc_fld.length - 1]);
213                 } else if (slc_fld) {
214                     res_description.push(slc_fld);
215                 }
216             }
217         }
218         if (!date_stop && date_delay) {
219             date_stop = date_start.clone().addHours(date_delay);
220         }
221         var r = {
222             'start_date': date_start.toString('yyyy-MM-dd HH:mm:ss'),
223             'end_date': date_stop.toString('yyyy-MM-dd HH:mm:ss'),
224             'text': res_text,
225             'id': evt.id,
226             'title': res_description.join()
227         };
228         if (evt.color) {
229             r.color = evt.color;
230         }
231         if (evt.textColor) {
232             r.textColor = evt.textColor;
233         }
234         return r;
235     },
236     do_create_event: function(event_id, event_obj) {
237         var self = this,
238             data = this.get_event_data(event_obj);
239         this.dataset.create(data, function(r) {
240             var id = parseInt(r.result, 10);
241             self.dataset.ids.push(id);
242             scheduler.changeEventId(event_id, id);
243             self.refresh_minical();
244         }, function(r, event) {
245             self.creating_event_id = event_id;
246             self.form_dialog.form.on_record_loaded(data);
247             self.form_dialog.open();
248             event.preventDefault();
249         });
250     },
251     do_save_event: function(event_id, event_obj) {
252         var self = this,
253             data = this.get_event_data(event_obj);
254         this.dataset.write(parseInt(event_id, 10), data, {}, function() {
255             self.refresh_minical();
256         });
257     },
258     do_delete_event: function(event_id, event_obj) {
259         var self = this;
260         // dhtmlx sends this event even when it does not exist in openerp.
261         // Eg: use cancel in dhtmlx new event dialog
262         if (_.indexOf(this.dataset.ids, parseInt(event_id, 10)) > -1) {
263             this.dataset.unlink(parseInt(event_id, 10), function() {
264                 self.refresh_minical();
265             });
266         }
267     },
268     do_edit_event: function(event_id) {
269         event_id = parseInt(event_id, 10);
270         var index = _.indexOf(this.dataset.ids, event_id);
271         if (index > -1) {
272             this.dataset.index = index;
273             this.form_dialog.form.do_show();
274             this.form_dialog.open();
275             return false;
276         }
277         return true;
278     },
279     get_event_data: function(event_obj) {
280         var data = {
281             name: event_obj.text
282         };
283         data[this.date_start] = openerp.web.datetime_to_str(event_obj.start_date);
284         if (this.date_stop) {
285             data[this.date_stop] = openerp.web.datetime_to_str(event_obj.end_date);
286         }
287         if (this.date_delay) {
288             var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000);
289             data[this.date_delay] = diff_seconds / 3600;
290         }
291         return data;
292     },
293     do_search: function(domains, contexts, groupbys) {
294         var self = this;
295         scheduler.clearAll();
296         $.when(this.has_been_loaded).then(function() {
297             self.rpc('/web/session/eval_domain_and_context', {
298                 domains: domains,
299                 contexts: contexts,
300                 group_by_seq: groupbys
301             }, function (results) {
302                 // TODO: handle non-empty results.group_by with read_group
303                 self.dataset.context = self.context = results.context;
304                 self.dataset.domain = self.domain = results.domain;
305                 self.dataset.read_slice(_.keys(self.fields), {
306                         offset:0,
307                         limit: self.limit
308                     }, function(events) {
309                         self.dataset_events = events;
310                         self.on_events_loaded(events);
311                     }
312                 );
313             });
314         });
315     },
316     do_show: function () {
317         var self = this;
318         $.when(this.has_been_loaded).then(function() {
319             self.$element.show();
320             if (self.sidebar) {
321                 self.sidebar.$element.show();
322             }
323         });
324     },
325     do_hide: function () {
326         this.$element.hide();
327         if (this.sidebar) {
328             this.sidebar.$element.hide();
329         }
330     },
331     get_selected_ids: function() {
332         // no way to select a record anyway
333         return [];
334     }
335 });
336
337 openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({
338     init: function(view, options, view_id, dataset) {
339         this._super(view, options);
340         this.dataset = dataset;
341         this.view_id = view_id;
342         this.view = view;
343     },
344     start: function() {
345         this._super();
346         this.form = new openerp.web.FormView(this, this.element_id, this.dataset, this.view_id, {
347             sidebar: false,
348             pager: false
349         });
350         this.form.start();
351         this.form.on_created.add_last(this.on_form_dialog_saved);
352         this.form.on_saved.add_last(this.on_form_dialog_saved);
353     },
354     on_form_dialog_saved: function() {
355         var id = this.dataset.ids[this.dataset.index];
356         if (this.view.creating_event_id) {
357             scheduler.changeEventId(this.view.creating_event_id, id);
358             this.view.creating_event_id = null;
359         }
360         this.view.reload_event(id);
361         this.close();
362     },
363     on_close: function() {
364         if (this.view.creating_event_id) {
365             scheduler.deleteEvent(this.view.creating_event_id);
366             this.view.creating_event_id = null;
367         }
368     }
369 });
370
371 openerp.web_calendar.SidebarResponsible = openerp.web.Widget.extend({
372     init: function(parent, element_id, view) {
373         this._super(parent, element_id);
374         this.view = view;
375         this.$element.delegate('input:checkbox', 'change', this.on_filter_click);
376     },
377     on_events_loaded: function(filters) {
378         this.$element.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
379     },
380     on_filter_click: function(e) {
381         var responsibles = [],
382             $e = $(e.target);
383         this.$element.find('div.oe_calendar_responsible input:checked').each(function() {
384             responsibles.push($(this).val());
385         });
386         scheduler.clearAll();
387         if (responsibles.length) {
388             this.view.on_events_loaded(this.view.dataset_events, function(filter_value) {
389                 return _.indexOf(responsibles, filter_value.toString()) > -1;
390             }, true);
391         } else {
392             this.view.on_events_loaded(this.view.dataset_events, false, true);
393         }
394     }
395 });
396
397 openerp.web_calendar.SidebarNavigator = openerp.web.Widget.extend({
398     init: function(parent, element_id, view) {
399         this._super(parent, element_id);
400         this.view = view;
401     },
402     on_events_loaded: function(events) {
403     }
404 });
405 };
406
407 // DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))
408 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: