1 /*---------------------------------------------------------
3 *---------------------------------------------------------*/
5 openerp.web_calendar = function(openerp) {
6 var _t = openerp.web._t;
7 var QWeb = openerp.web.qweb;
8 openerp.web.views.add('calendar', 'openerp.web_calendar.CalendarView');
9 openerp.web_calendar.CalendarView = openerp.web.View.extend({
11 init: function(parent, dataset, view_id, options) {
13 this.ready = $.Deferred();
14 this.set_default_options(options);
15 this.dataset = dataset;
16 this.model = dataset.model;
17 this.fields_view = {};
18 this.view_id = view_id;
19 this.has_been_loaded = $.Deferred();
20 this.creating_event_id = null;
21 this.dataset_events = [];
22 this.form_dialog = new openerp.web_calendar.CalendarFormDialog(this, {}, this.options.action_views_ids.form, dataset);
23 this.form_dialog.start();
24 this.COLOR_PALETTE = ['#f57900', '#cc0000', '#d400a8', '#75507b', '#3465a4', '#73d216', '#c17d11', '#edd400',
25 '#fcaf3e', '#ef2929', '#ff00c9', '#ad7fa8', '#729fcf', '#8ae234', '#e9b96e', '#fce94f',
26 '#ff8e00', '#ff0000', '#b0008c', '#9000ff', '#0078ff', '#00ff00', '#e6ff00', '#ffff00',
27 '#905000', '#9b0000', '#840067', '#510090', '#0000c9', '#009b00', '#9abe00', '#ffc900' ];
29 this.last_search = [];
30 this.range_start = null;
31 this.range_stop = null;
32 this.update_range_dates(Date.today());
36 return this.rpc("/web/view/load", {"model": this.model, "view_id": this.view_id, "view_type":"calendar", 'toolbar': true}, this.on_loaded);
42 on_loaded: function(data) {
43 this.fields_view = data;
44 this.calendar_fields = {};
45 this.ids = this.dataset.ids;
46 this.color_values = [];
47 this.info_fields = [];
49 this.name = this.fields_view.name || this.fields_view.arch.attrs.string;
50 this.view_id = this.fields_view.view_id;
52 // mode, one of month, week or day
53 this.mode = this.fields_view.arch.attrs.mode;
55 // date_start is mandatory, date_delay and date_stop are optional
56 this.date_start = this.fields_view.arch.attrs.date_start;
57 this.date_delay = this.fields_view.arch.attrs.date_delay;
58 this.date_stop = this.fields_view.arch.attrs.date_stop;
60 this.colors = this.fields_view.arch.attrs.colors;
61 this.day_length = this.fields_view.arch.attrs.day_length || 8;
62 this.color_field = this.fields_view.arch.attrs.color;
63 this.fields = this.fields_view.fields;
65 if (!this.date_start) {
66 throw new Error("Calendar view has not defined 'date_start' attribute.");
70 this.calendar_fields.date_start = {'name': this.date_start, 'kind': this.fields[this.date_start].type};
72 if (this.date_delay) {
73 if (this.fields[this.date_delay].type != 'float') {
74 throw new Error("Calendar view has a 'date_delay' type != float");
76 this.calendar_fields.date_delay = {'name': this.date_delay, 'kind': this.fields[this.date_delay].type};
79 this.calendar_fields.date_stop = {'name': this.date_stop, 'kind': this.fields[this.date_stop].type};
82 for (var fld = 0; fld < this.fields_view.arch.children.length; fld++) {
83 this.info_fields.push(this.fields_view.arch.children[fld].attrs.name);
85 this.$element.html(QWeb.render("CalendarView", {"fields_view": this.fields_view}));
87 if (this.options.sidebar && this.options.sidebar_id) {
88 this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
90 this.sidebar.navigator = new openerp.web_calendar.SidebarNavigator(this.sidebar, this);
91 this.sidebar.responsible = new openerp.web_calendar.SidebarResponsible(this.sidebar, this);
92 this.sidebar.add_toolbar(this.fields_view.toolbar);
93 this.set_common_sidebar_sections(this.sidebar);
94 this.sidebar.do_unfold();
95 this.sidebar.do_fold.add_last(this.refresh_scheduler);
96 this.sidebar.do_unfold.add_last(this.refresh_scheduler);
97 this.sidebar.do_toggle.add_last(this.refresh_scheduler);
100 this.init_scheduler();
101 this.has_been_loaded.resolve();
103 init_scheduler: function() {
105 scheduler.clearAll();
106 if (this.fields[this.date_start]['type'] == 'time') {
107 scheduler.config.xml_date = "%H:%M:%S";
109 scheduler.config.xml_date = "%Y-%m-%d %H:%i";
111 scheduler.config.api_date = "%Y-%m-%d %H:%i";
112 scheduler.config.multi_day = true; //Multi day events are not rendered in daily and weekly views
113 scheduler.config.start_on_monday = true;
114 scheduler.config.time_step = 30;
115 scheduler.config.scroll_hour = 8;
116 scheduler.config.drag_resize = true;
117 scheduler.config.drag_create = true;
118 scheduler.config.mark_now = true;
119 scheduler.config.day_date = '%l %j';
121 scheduler.init('openerp_scheduler', null, this.mode || 'month');
123 // Remove hard coded style attributes from dhtmlx scheduler
124 this.$element.find(".dhx_cal_navline div").removeAttr('style');
126 scheduler.detachAllEvents();
127 scheduler.attachEvent('onEventAdded', this.do_create_event);
128 scheduler.attachEvent('onEventDeleted', this.do_delete_event);
129 scheduler.attachEvent('onEventChanged', this.do_save_event);
130 scheduler.attachEvent('onClick', this.do_edit_event);
131 scheduler.attachEvent('onLightbox', this.do_edit_event);
133 scheduler.attachEvent('onViewChange', this.on_view_changed);
134 this.refresh_scheduler();
136 if (this.options.sidebar) {
137 this.mini_calendar = scheduler.renderCalendar({
138 container: this.sidebar.navigator.element_id,
140 date: scheduler._date,
141 handler: function(date, calendar) {
142 scheduler.setCurrentView(date, 'day');
147 on_view_changed: function(mode, date) {
148 this.$element.removeClass('oe_cal_day oe_cal_week oe_cal_month').addClass('oe_cal_' + mode);
149 if (!date.between(this.range_start, this.range_stop)) {
150 this.update_range_dates(date);
151 this.do_ranged_search();
153 this.ready.resolve();
155 update_range_dates: function(date) {
156 this.range_start = date.clone().moveToFirstDayOfMonth();
157 this.range_stop = this.range_start.clone().addMonths(1).addSeconds(-1);
159 refresh_scheduler: function() {
160 scheduler.setCurrentView(scheduler._date);
162 refresh_minical: function() {
163 if (this.options.sidebar) {
164 scheduler.updateCalendar(this.mini_calendar);
167 reload_event: function(id) {
168 this.dataset.read_ids([id], _.keys(this.fields), this.on_events_loaded);
170 get_color: function(key) {
171 if (this.color_map[key]) {
172 return this.color_map[key];
174 var index = _.keys(this.color_map).length % this.COLOR_PALETTE.length;
175 var color = this.COLOR_PALETTE[index];
176 this.color_map[key] = color;
179 on_events_loaded: function(events, fn_filter, no_filter_reload) {
182 //To parse Events we have to convert date Format
185 for (var e = 0; e < events.length; e++) {
187 if (!evt[this.date_start]) {
191 if (this.color_field) {
192 var filter = evt[this.color_field];
194 var filter_value = (typeof filter === 'object') ? filter[0] : filter;
195 if (typeof(fn_filter) === 'function' && !fn_filter(filter_value)) {
200 label: (typeof filter === 'object') ? filter[1] : filter,
201 color: this.get_color(filter_value)
203 if (!sidebar_items[filter_value]) {
204 sidebar_items[filter_value] = filter_item;
206 evt.color = filter_item.color;
207 evt.textColor = '#ffffff';
211 if (this.fields[this.date_start]['type'] == 'date') {
212 evt[this.date_start] = openerp.web.auto_str_to_date(evt[this.date_start]).set({hour: 9}).toString('yyyy-MM-dd HH:mm:ss');
214 if (this.date_stop && evt[this.date_stop] && this.fields[this.date_stop]['type'] == 'date') {
215 evt[this.date_stop] = openerp.web.auto_str_to_date(evt[this.date_stop]).set({hour: 17}).toString('yyyy-MM-dd HH:mm:ss');
217 res_events.push(this.convert_event(evt));
219 scheduler.parse(res_events, 'json');
220 this.refresh_scheduler();
221 this.refresh_minical();
222 if (!no_filter_reload && this.options.sidebar) {
223 this.sidebar.responsible.on_events_loaded(sidebar_items);
226 convert_event: function(evt) {
227 var date_start = openerp.web.str_to_datetime(evt[this.date_start]),
228 date_stop = this.date_stop ? openerp.web.str_to_datetime(evt[this.date_stop]) : null,
229 date_delay = evt[this.date_delay] || 1.0,
231 res_description = [];
233 if (this.info_fields) {
234 var fld = evt[this.info_fields[0]];
235 res_text = (typeof fld == 'object') ? fld[fld.length -1] : res_text = fld;
237 var sliced_info_fields = this.info_fields.slice(1);
238 for (var sl_fld in sliced_info_fields) {
239 var slc_fld = evt[sliced_info_fields[sl_fld]];
240 if (typeof slc_fld == 'object') {
241 res_description.push(slc_fld[slc_fld.length - 1]);
242 } else if (slc_fld) {
243 res_description.push(slc_fld);
247 if (!date_stop && date_delay) {
248 date_stop = date_start.clone().addHours(date_delay);
251 'start_date': date_start.toString('yyyy-MM-dd HH:mm:ss'),
252 'end_date': date_stop.toString('yyyy-MM-dd HH:mm:ss'),
255 'title': res_description.join()
261 r.textColor = evt.textColor;
265 do_create_event: function(event_id, event_obj) {
267 data = this.get_event_data(event_obj);
268 this.dataset.create(data, function(r) {
270 self.dataset.ids.push(id);
271 scheduler.changeEventId(event_id, id);
272 self.refresh_minical();
273 }, function(r, event) {
274 event.preventDefault();
275 self.do_create_event_with_formdialog(event_id, event_obj);
278 do_create_event_with_formdialog: function(event_id, event_obj) {
280 event_obj = scheduler.getEvent(event_id);
283 data = this.get_event_data(event_obj),
284 form = self.form_dialog.form,
285 fields_to_fetch = _(form.fields_view.fields).keys();
286 this.dataset.index = null;
287 self.creating_event_id = event_id;
288 this.form_dialog.form.do_show().then(function() {
289 form.show_invalid = false;
290 _.each(['date_start', 'date_stop', 'date_delay'], function(field) {
291 var field_name = self[field];
293 field = form.fields[field_name];
294 field.set_value(data[field_name]);
296 form.do_onchange(field);
299 form.show_invalid = true;
300 self.form_dialog.open();
303 do_save_event: function(event_id, event_obj) {
305 data = this.get_event_data(event_obj),
306 index = this.dataset.get_id_index(event_id);
308 event_id = this.dataset.ids[index];
309 this.dataset.write(event_id, data, {}, function() {
310 self.refresh_minical();
314 do_delete_event: function(event_id, event_obj) {
315 // dhtmlx sends this event even when it does not exist in openerp.
316 // Eg: use cancel in dhtmlx new event dialog
318 index = this.dataset.get_id_index(event_id);
319 if (index !== null) {
320 this.dataset.unlink(event_id, function() {
321 self.refresh_minical();
325 do_edit_event: function(event_id) {
327 var index = this.dataset.get_id_index(event_id);
328 if (index !== null) {
329 this.dataset.index = index;
330 this.do_switch_view('page');
331 } else if (scheduler.getState().mode === 'month') {
332 var event_obj = scheduler.getEvent(event_id);
333 if (event_obj._length === 1) {
334 event_obj['start_date'].addHours(8);
335 event_obj['end_date'] = new Date(event_obj['start_date']);
336 event_obj['end_date'].addHours(1);
338 this.do_create_event_with_formdialog(event_id, event_obj);
340 // Theorically, returning false should prevent the lightbox to open.
341 // It works, but then the scheduler is in a buggy state where drag'n drop
342 // related internal Event won't be fired anymore.
343 // I tried scheduler.editStop(event_id); but doesn't work either
344 // After losing one hour on this, here's a quick and very dirty fix :
345 $(".dhx_cancel_btn").click();
348 get_event_data: function(event_obj) {
352 data[this.date_start] = openerp.web.datetime_to_str(event_obj.start_date);
353 if (this.date_stop) {
354 data[this.date_stop] = openerp.web.datetime_to_str(event_obj.end_date);
356 if (this.date_delay) {
357 var diff_seconds = Math.round((event_obj.end_date.getTime() - event_obj.start_date.getTime()) / 1000);
358 data[this.date_delay] = diff_seconds / 3600;
362 do_search: function(domain, context, group_by) {
363 this.last_search = arguments;
364 this.do_ranged_search();
366 do_ranged_search: function() {
368 scheduler.clearAll();
369 $.when(this.has_been_loaded, this.ready).then(function() {
370 self.dataset.read_slice(_.keys(self.fields), {
372 domain: self.get_range_domain(),
373 context: self.last_search[1]
374 }, function(events) {
375 self.dataset_events = events;
376 self.on_events_loaded(events);
380 get_range_domain: function() {
381 var format = openerp.web.date_to_str,
382 domain = this.last_search[0].slice(0);
383 domain.unshift([this.date_start, '>=', format(this.range_start.clone().addDays(-6))]);
384 domain.unshift([this.date_start, '<=', format(this.range_stop.clone().addDays(6))]);
387 do_show: function () {
389 $.when(this.has_been_loaded).then(function() {
390 self.$element.show();
392 self.sidebar.$element.show();
396 do_hide: function () {
399 this.sidebar.$element.hide();
402 get_selected_ids: function() {
403 // no way to select a record anyway
408 openerp.web_calendar.CalendarFormDialog = openerp.web.Dialog.extend({
409 init: function(view, options, view_id, dataset) {
410 this._super(view, options);
411 this.dataset = dataset;
412 this.view_id = view_id;
417 this.form = new openerp.web.FormView(this, this.dataset, this.view_id, {
421 this.form.appendTo(this.$element);
422 this.form.on_created.add_last(this.on_form_dialog_saved);
423 this.form.on_saved.add_last(this.on_form_dialog_saved);
425 on_form_dialog_saved: function() {
426 var id = this.dataset.ids[this.dataset.index];
427 if (this.view.creating_event_id) {
428 scheduler.changeEventId(this.view.creating_event_id, id);
429 this.view.creating_event_id = null;
431 this.view.reload_event(id);
434 on_close: function() {
435 if (this.view.creating_event_id) {
436 scheduler.deleteEvent(this.view.creating_event_id);
437 this.view.creating_event_id = null;
442 openerp.web_calendar.SidebarResponsible = openerp.web.Widget.extend({
443 init: function(parent, view) {
444 var $section = parent.add_section(_t('Responsible'), 'responsible');
445 this.$div = $('<div></div>');
446 $section.append(this.$div);
447 this._super(parent, $section.attr('id'));
449 this.$element.delegate('input:checkbox', 'change', this.on_filter_click);
451 on_events_loaded: function(filters) {
452 this.$div.html(QWeb.render('CalendarView.sidebar.responsible', { filters: filters }));
454 on_filter_click: function(e) {
455 var responsibles = [],
457 this.$element.find('div.oe_calendar_responsible input:checked').each(function() {
458 responsibles.push($(this).val());
460 scheduler.clearAll();
461 if (responsibles.length) {
462 this.view.on_events_loaded(this.view.dataset_events, function(filter_value) {
463 return _.indexOf(responsibles, filter_value.toString()) > -1;
466 this.view.on_events_loaded(this.view.dataset_events, false, true);
471 openerp.web_calendar.SidebarNavigator = openerp.web.Widget.extend({
472 init: function(parent, view) {
473 var $section = parent.add_section(_t('Navigator'), 'navigator');
474 this._super(parent, $section.attr('id'));
477 on_events_loaded: function(events) {
482 // DEBUG_RPC:rpc.request:('execute', 'addons-dsh-l10n_us', 1, '*', ('ir.filters', 'get_filters', u'res.partner'))
483 // vim:et fdc=0 fdl=0 foldnestmax=3 fdm=syntax: