[WIP] Multi-Calendar

This commit is contained in:
QS5ELkMu
2018-12-07 14:16:50 +01:00
parent 5212f3801a
commit 902ff6e758
4 changed files with 995 additions and 683 deletions

View File

@@ -10,7 +10,6 @@ var Core = require('web.core'),
Session = require('web.session'),
AbstractRenderer = require('web.AbstractRenderer'),
HotelConstants = require('hotel_calendar.Constants'),
MultiCalendar = require('hotel_calendar.MultiCalendar'),
//Formats = require('web.formats'),
_t = Core._t,
@@ -27,23 +26,17 @@ var HotelCalendarView = AbstractRenderer.extend({
// Custom Options
_view_options: {},
_hcalendar: null,
_reserv_tooltips: {},
_days_tooltips: [],
_last_dates: [false, false],
_multi_calendar: null,
/** VIEW METHODS **/
init: function(parent, state, params) {
this._super.apply(this, arguments);
this._multi_calendar = new MultiCalendar(this);
},
start: function () {
this._multi_calendar.setElement(this.$el.find('#hcal_widget'));
this._multi_calendar.start();
this.init_calendar_view();
return this._super();
},
@@ -53,7 +46,7 @@ var HotelCalendarView = AbstractRenderer.extend({
if (!this._is_visible) {
// FIXME: Workaround for restore "lost" reservations (Drawn when the view is hidden)
this._multi_calendar.recalculate_reservation_positions();
this.trigger_up('onViewAttached');
}
},
@@ -70,11 +63,6 @@ var HotelCalendarView = AbstractRenderer.extend({
};
},
load_reservations: function(reservs) {
this._hcalendar.setReservations(reservs);
this._assign_extra_info();
},
get_view_filter_dates: function () {
var $dateTimePickerBegin = this.$el.find('#pms-search #date_begin');
var $dateTimePickerEnd = this.$el.find('#pms-search #date_end');
@@ -98,327 +86,6 @@ var HotelCalendarView = AbstractRenderer.extend({
this._last_dates = this.get_view_filter_dates();
},
destroy_calendar: function() {
if (this._hcalendar) {
this._hcalendar.$base.empty();
delete this._hcalendar;
}
},
_assign_hcalendar_events: function() {
var self = this;
this._hcalendar.addEventListener('hcalOnSavePricelist', function(ev){
var pricelist = self._hcalendar.getPricelist();
var oparams = [self._hcalendar._pricelist_id, false, pricelist, {}, {}];
self.trigger_up('onSaveChanges', oparams);
});
this._hcalendar.addEventListener('hcalOnMouseEnterReservation', function(ev){
if (ev.detail.reservationObj) {
var tp = self._reserv_tooltips[ev.detail.reservationObj.id];
var qdict = self._generate_reservation_tooltip_dict(tp);
$(ev.detail.reservationDiv).tooltip('destroy').tooltip({
animation: false,
html: true,
placement: 'bottom',
title: QWeb.render('HotelCalendar.TooltipReservation', qdict)
}).tooltip('show');
}
});
this._hcalendar.addEventListener('hcalOnClickReservation', function(ev){
//var res_id = ev.detail.reservationObj.getUserData('folio_id');
$(ev.detail.reservationDiv).tooltip('hide');
self.do_action({
type: 'ir.actions.act_window',
res_model: 'hotel.reservation',
res_id: ev.detail.reservationObj.id,
views: [[false, 'form']]
});
// self._model.call('get_formview_id', [res_id, Session.user_context]).then(function(view_id){
// var pop = new ViewDialogs.FormViewDialog(self, {
// res_model: 'hotel.folio',
// res_id: res_id,
// title: _t("Open: ") + ev.detail.reservationObj.title,
// view_id: view_id
// //readonly: false
// }).open();
// pop.on('write_completed', self, function(){
// self.trigger('changed_value');
// });
// });
});
this._hcalendar.addEventListener('hcalOnSwapReservations', function(ev){
var qdict = {};
var dialog = new Dialog(self, {
title: _t("Confirm Reservation Swap"),
buttons: [
{
text: _t("Yes, swap it"),
classes: 'btn-primary',
close: true,
click: function () {
if (self._hcalendar.swapReservations(ev.detail.inReservs, ev.detail.outReservs)) {
var fromIds = _.pluck(ev.detail.inReservs, 'id');
var toIds = _.pluck(ev.detail.outReservs, 'id');
var refFromReservDiv = ev.detail.inReservs[0]._html;
var refToReservDiv = ev.detail.outReservs[0]._html;
// Animate Movement
for (var nreserv of ev.detail.inReservs) {
$(nreserv._html).animate({'top': refToReservDiv.style.top});
}
for (var nreserv of ev.detail.outReservs) {
$(nreserv._html).animate({'top': refFromReservDiv.style.top});
}
self.trigger_up('onSwapReservations', {
'fromIds': fromIds,
'toIds': toIds,
'detail': ev.detail,
'refFromReservDiv': refFromReservDiv,
'refToReservDiv': refToReservDiv
});
} else {
var qdict = {};
var dialog = new Dialog(self, {
title: _t("Invalid Reservation Swap"),
buttons: [
{
text: _t("Oops, Ok!"),
classes: 'btn-primary',
close: true
}
],
$content: QWeb.render('HotelCalendar.InvalidSwapOperation', qdict)
}).open();
}
}
},
{
text: _t("No"),
close: true
}
],
$content: QWeb.render('HotelCalendar.ConfirmSwapOperation', qdict)
}).open();
});
this._hcalendar.addEventListener('hcalOnCancelSwapReservations', function(ev){
$("#btn_swap span.ntext").html(_t("START SWAP"));
$("#btn_swap").css({
'backgroundColor': '',
'fontWeight': 'normal'
});
});
this._hcalendar.addEventListener('hcalOnChangeReservation', function(ev){
var newReservation = ev.detail.newReserv;
var oldReservation = ev.detail.oldReserv;
var oldPrice = ev.detail.oldPrice;
var newPrice = ev.detail.newPrice;
var folio_id = newReservation.getUserData('folio_id');
var linkedReservs = _.find(self._hcalendar._reservations, function(item){
return item.id !== newReservation.id && !item.unusedZone && item.getUserData('folio_id') === folio_id;
});
var hasChanged = false;
var qdict = {
ncheckin: newReservation.startDate.clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT),
ncheckout: newReservation.endDate.clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT),
nroom: newReservation.room.number,
nprice: newPrice,
ocheckin: oldReservation.startDate.clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT),
ocheckout: oldReservation.endDate.clone().local().format(HotelConstants.L10N_DATETIME_MOMENT_FORMAT),
oroom: oldReservation.room.number,
oprice: oldPrice,
hasReservsLinked: (linkedReservs && linkedReservs.length !== 0)?true:false
};
var dialog = new Dialog(self, {
title: _t("Confirm Reservation Changes"),
buttons: [
{
text: _t("Yes, change it"),
classes: 'btn-primary',
close: true,
disabled: !newReservation.id,
click: function () {
var roomId = newReservation.room.id;
if (newReservation.room.overbooking) {
roomId = +newReservation.room.id.substr(newReservation.room.id.indexOf('@')+1);
}
var write_values = {
'checkin': newReservation.startDate.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
'checkout': newReservation.endDate.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
'room_id': roomId,
'overbooking': newReservation.room.overbooking
};
self.trigger_up('onUpdateReservations', {
'ids': [newReservation.id],
'values': write_values,
'oldReservation': oldReservation,
'newReservation': newReservation
});
// Workarround for dispatch room lines regeneration
// new Model('hotel.reservation').call('on_change_checkin_checkout_product_id', [[newReservation.id], false]);
hasChanged = true;
}
},
{
text: _t("No"),
close: true,
}
],
$content: QWeb.render('HotelCalendar.ConfirmReservationChanges', qdict)
}).open();
dialog.opened(function(e){
if (!hasChanged) {
self._hcalendar.replaceReservation(newReservation, oldReservation);
}
});
});
this._hcalendar.addEventListener('hcalOnUpdateSelection', function(ev){
for (var td of ev.detail.old_cells) {
$(td).tooltip('destroy');
}
if (ev.detail.cells.length > 1) {
var last_cell = ev.detail.cells[ev.detail.cells.length-1];
var date_cell_start = HotelCalendar.toMoment(self._hcalendar.etable.querySelector(`#${ev.detail.cells[0].dataset.hcalParentCell}`).dataset.hcalDate);
var date_cell_end = HotelCalendar.toMoment(self._hcalendar.etable.querySelector(`#${last_cell.dataset.hcalParentCell}`).dataset.hcalDate);
var parentRow = document.querySelector(`#${ev.detail.cells[0].dataset.hcalParentRow}`);
var room = self._hcalendar.getRoom(parentRow.dataset.hcalRoomObjId);
if (room.overbooking) {
return;
}
var nights = date_cell_end.diff(date_cell_start, 'days');
var qdict = {
'total_price': Number(ev.detail.totalPrice).toLocaleString(),
'nights': nights
};
$(last_cell).tooltip({
animation: false,
html: true,
placement: 'top',
title: QWeb.render('HotelCalendar.TooltipSelection', qdict)
}).tooltip('show');
}
});
this._hcalendar.addEventListener('hcalOnChangeSelection', function(ev){
var parentRow = document.querySelector(`#${ev.detail.cellStart.dataset.hcalParentRow}`);
var parentCellStart = document.querySelector(`#${ev.detail.cellStart.dataset.hcalParentCell}`);
var parentCellEnd = document.querySelector(`#${ev.detail.cellEnd.dataset.hcalParentCell}`);
var startDate = HotelCalendar.toMoment(parentCellStart.dataset.hcalDate);
var endDate = HotelCalendar.toMoment(parentCellEnd.dataset.hcalDate);
var room = self._hcalendar.getRoom(parentRow.dataset.hcalRoomObjId);
if (room.overbooking) {
return;
}
var numBeds = (room.shared || self._hcalendar.getOptions('divideRoomsByCapacity'))?(ev.detail.cellEnd.dataset.hcalBedNum - ev.detail.cellStart.dataset.hcalBedNum)+1:room.capacity;
if (numBeds <= 0) {
return;
}
// Normalize Dates
if (startDate.isAfter(endDate)) {
var tt = endDate;
endDate = startDate;
startDate = tt;
}
var def_arrival_hour = self._view_options['default_arrival_hour'].split(':');
var def_departure_hour = self._view_options['default_departure_hour'].split(':');
startDate.set({'hour': def_arrival_hour[0], 'minute': def_arrival_hour[1], 'second': 0});
endDate.set({'hour': def_departure_hour[0], 'minute': def_departure_hour[1], 'second': 0});
var popCreate = new ViewDialogs.FormViewDialog(self, {
res_model: 'hotel.reservation',
context: {
'default_checkin': startDate.utc().format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
'default_checkout': endDate.utc().format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
'default_adults': numBeds,
'default_children': 0,
'default_room_id': room.id,
'default_room_type_id': room.getUserData('room_type_id'),
},
title: _t("Create: ") + _t("Reservation"),
initial_view: "form",
disable_multiple_selection: true,
}).open();
});
this._hcalendar.addEventListener('hcalOnDateChanged', function(ev){
var $dateTimePickerBegin = this.$el.find('#pms-search #date_begin');
var $dateTimePickerEnd = this.$el.find('#pms-search #date_end');
$dateTimePickerBegin.data("ignore_onchange", true);
$dateTimePickerEnd.data("DateTimePicker").minDate(false);
$dateTimePickerEnd.data("DateTimePicker").maxDate(false);
$dateTimePickerBegin.data("DateTimePicker").date(ev.detail.date_begin.local().add(1, 'd'));
$dateTimePickerEnd.data("ignore_onchange", true);
$dateTimePickerEnd.data("DateTimePicker").date(ev.detail.date_end.local());
this.reload_hcalendar_reservations(false);
}.bind(this));
},
_assign_extra_info: function() {
var self = this;
$(this._hcalendar.etable).find('.hcal-cell-room-type-group-item.btn-hcal-3d').on("mouseenter", function(){
var $this = $(this);
var room = self._hcalendar.getRoom($this.parent().data("hcalRoomObjId"));
if (room.overbooking) {
$this.tooltip({
animation: true,
html: true,
placement: 'right',
title: QWeb.render('HotelCalendar.TooltipRoomOverbooking', {'name': room.number})
}).tooltip('show');
return;
} else {
var qdict = {
'room_type_name': room.getUserData('room_type_name'),
'name': room.number
};
$this.tooltip({
animation: true,
html: true,
placement: 'right',
title: QWeb.render('HotelCalendar.TooltipRoom', qdict)
}).tooltip('show');
}
});
$(this._hcalendar.etableHeader).find('.hcal-cell-header-day').each(function(index, elm){
var $elm = $(elm);
var cdate = HotelCalendar.toMoment($elm.data('hcalDate'), HotelConstants.L10N_DATE_MOMENT_FORMAT);
var data = _.filter(self._days_tooltips, function(item) {
var ndate = HotelCalendar.toMoment(item[2], HotelConstants.ODOO_DATE_MOMENT_FORMAT);
return ndate.isSame(cdate, 'd');
});
if (data.length > 0) {
$elm.addClass('hcal-event-day');
$elm.prepend("<i class='fa fa-bell' style='margin-right: 0.1em'></i>");
$elm.on("mouseenter", function(data){
var $this = $(this);
if (data.length > 0) {
var qdict = {
'date': $this.data('hcalDate'),
'events': _.map(data, function(item){
return {
'name': item[1],
'date': item[2],
'location': item[3]
};
})
};
$this.attr('title', '');
$this.tooltip({
animation: true,
html: true,
placement: 'bottom',
title: QWeb.render('HotelCalendar.TooltipEvent', qdict)
}).tooltip('show');
}
}.bind(elm, data));
}
});
},
update_buttons_counter: function(ncheckouts, ncheckins, noverbookings) {
var self = this;
// Checkouts Button
@@ -581,7 +248,6 @@ var HotelCalendarView = AbstractRenderer.extend({
return $.when(
this.trigger_up('onLoadCalendarSettings'),
this.trigger_up('onUpdateButtonsCounter'),
this.trigger_up('onLoadCalendar'),
this.trigger_up('onLoadViewFilters'),
);
},

View File

@@ -9,8 +9,11 @@ odoo.define('hotel_calendar.MultiCalendar', function(require) {
var MultiCalendar = Widget.extend({
_calendars: [],
_active_calendar: -1,
_active_index: -1,
_events: {},
_tabs: [],
_dataset: {},
_base: null,
init: function(parent) {
this._super.apply(this, arguments);
@@ -22,23 +25,108 @@ odoo.define('hotel_calendar.MultiCalendar', function(require) {
this._create_tabs_panel();
},
get_calendar: function(index) {
return this._calendars[index];
},
get_tab: function(index) {
return this._tabs[index];
},
get_active_index: function() {
return this._active_index;
},
get_active_calendar: function() {
return this._calendars[this._active_index];
},
get_active_tab: function() {
return this._tabs[this._active_index];
},
recalculate_reservation_positions: function() {
setTimeout(function(){
for (var reserv of this.get_active_calendar()._reservations) {
var style = window.getComputedStyle(reserv._html, null);
if (parseInt(style.width, 10) < 15 || parseInt(style.height, 10) < 15 || parseInt(style.top, 10) === 0) {
this.get_active_calendar()._updateReservation(reserv);
}
}
}.bind(this), 200);
},
update_all_reservations: function() {
for (var calendar of this._calendars) {
this._update_reservations(calendar);
}
},
update_reservations: function(calendar) {
calendar.setReservations(this._dataset['reservations']);
this._assign_extra_info(calendar);
},
set_active_calendar: function(index) {
this._tabs[index][0].tab('show');
},
set_datasets: function(pricelist, restrictions, reservations) {
this._dataset = {
pricelist: pricelist,
restrictions: restrictions,
reservations: reservations,
};
},
set_options: function(options) {
this._options = options;
},
set_base_element: function(element) {
this._base = element;
},
create_calendar: function(name) {
var [$tab, $panel] = this._create_tab(name, `calendar-pane-${name}`);
var calendar = new HotelCalendar(
$panel[0],
this._options,
this._dataset['pricelist'],
this._dataset['restrictions'],
this._base);
this._assign_calendar_events(calendar);
this._calendars.push(calendar);
return this._calendars.length-1;
},
on: function(event_name, callback) {
this._events[event_name] = callback;
},
_create_tab: function(name, id) {
var sanitized_id = this._sanitizeId(id);
// '+' Tab
var $tab = $('<a/>', {
id: name,
href: `#${id}`,
id: this._sanitizeId(name),
href: `#${sanitized_id}`,
text: name,
}).appendTo($('<li/>').prependTo(this.$tabs));
role: 'tab',
}).data('tabindex', this._tabs.length).appendTo($('<li/>').prependTo(this.$tabs));
$tab[0].dataset.toggle = 'tab';
var $pane = $('<div/>', {
id: id,
var $panel = $('<div/>', {
id: sanitized_id,
class: 'tab-pane',
role: 'tabpanel'
}).appendTo(this.$tabs_content);
return $pane;
this._tabs.push([$tab, $panel]);
return this._tabs[this._tabs.length-1];
},
_create_tabs_panel: function() {
var self = this;
this.$el.empty();
this.$tabs = $('<ul/>', {
class: 'nav nav-tabs',
@@ -48,44 +136,89 @@ odoo.define('hotel_calendar.MultiCalendar', function(require) {
}).appendTo(this.$el);
// '+' Tab
var $pane = this._create_tab('+', 'default');
var [$tab, $panel] = this._create_tab('+', 'default');
$tab.on('shown.bs.tab', function(ev){
ev.preventDefault();
var new_calendar_id = self.create_calendar(`Calendar #${self._calendars.length}`);
self.set_active_calendar(new_calendar_id);
});
$('<p/>', {
class: 'warn-message',
text: "NO CALENDAR DEFINED!",
}).appendTo($pane);
},
get_active_calendar: function() {
return this._calendars[this._active_calendar];
},
recalculate_reservation_positions: function() {
setTimeout(function(){
for (var reserv of this.get_active_calendar._reservations) {
var style = window.getComputedStyle(reserv._html, null);
if (parseInt(style.width, 10) < 15 || parseInt(style.height, 10) < 15 || parseInt(style.top, 10) === 0) {
this.get_active_calendar()._updateReservation(reserv);
}
}
}.bind(this), 200);
},
create_calendar: function(name, options, pricelist, restrictions, base) {
var $pane = this._create_tab(name, `calendar-pane-${name}`);
var calendar = new HotelCalendar($pane[0], options, pricelist, restrictions, base);
this._assign_calendar_events(calendar);
this._calendars.push(calendar);
this._active_calendar = this._calendars.length - 1;
},
on: function(event_name, callback) {
for (var calendar of this._calendars) {
calendar.addEventListener(event_name, callback);
}
}).appendTo($panel);
},
_assign_calendar_events: function(calendar) {
for (var event_name in this._events) {
calendar.addEventListener(event_name, this._events[event_name]);
}
},
_assign_extra_info: function(calendar) {
var self = this;
$(calendar.etable).find('.hcal-cell-room-type-group-item.btn-hcal-3d').on("mouseenter", function(){
var $this = $(this);
var room = calendar.getRoom($this.parent().data("hcalRoomObjId"));
if (room.overbooking) {
$this.tooltip({
animation: true,
html: true,
placement: 'right',
title: QWeb.render('HotelCalendar.TooltipRoomOverbooking', {'name': room.number})
}).tooltip('show');
return;
} else {
var qdict = {
'room_type_name': room.getUserData('room_type_name'),
'name': room.number
};
$this.tooltip({
animation: true,
html: true,
placement: 'right',
title: QWeb.render('HotelCalendar.TooltipRoom', qdict)
}).tooltip('show');
}
});
$(calendar.etableHeader).find('.hcal-cell-header-day').each(function(index, elm){
var $elm = $(elm);
var cdate = HotelCalendar.toMoment($elm.data('hcalDate'), HotelConstants.L10N_DATE_MOMENT_FORMAT);
var data = _.filter(self._days_tooltips, function(item) {
var ndate = HotelCalendar.toMoment(item[2], HotelConstants.ODOO_DATE_MOMENT_FORMAT);
return ndate.isSame(cdate, 'd');
});
if (data.length > 0) {
$elm.addClass('hcal-event-day');
$elm.prepend("<i class='fa fa-bell' style='margin-right: 0.1em'></i>");
$elm.on("mouseenter", function(data){
var $this = $(this);
if (data.length > 0) {
var qdict = {
'date': $this.data('hcalDate'),
'events': _.map(data, function(item){
return {
'name': item[1],
'date': item[2],
'location': item[3]
};
})
};
$this.attr('title', '');
$this.tooltip({
animation: true,
html: true,
placement: 'bottom',
title: QWeb.render('HotelCalendar.TooltipEvent', qdict)
}).tooltip('show');
}
}.bind(elm, data));
}
});
},
_sanitizeId: function(/*String*/str) {
return str.replace(/[^a-zA-Z0-9\-_]/g, '_');
},
});

View File

@@ -218,7 +218,17 @@ HotelCalendar.prototype = {
this._reservations[rindex] = r;
this._cleanUnusedZones(r);
} else {
this._reservations.push(r);
var room = this.getRoom(r.room_id, r.overbooking, r.id);
// need create a overbooking row?
if (!room && r.overbooking) {
room = this.createOBRoom(this.getRoom(r.room_id), r.id);
this.createOBRoomRow(room);
}
if (room) {
this._reservations.push(r);
} else {
console.warn(`Can't found a room for the reservation '${r[0]}'!`);
}
}
}
@@ -1138,7 +1148,10 @@ HotelCalendar.prototype = {
this.btnSaveChanges.innerHTML = "<i class='fa fa-save fa-2x'> </i>";
this.btnSaveChanges.addEventListener('click', function(ev){
if (this.classList.contains('need-save')) {
$this._dispatchEvent('hcalOnSavePricelist');
$this._dispatchEvent('hcalOnSavePricelist', {
pricelist: $this.getPricelist(),
pricelist_id: $this._pricelist_id,
});
}
});
cell.appendChild(this.btnSaveChanges);
@@ -2234,7 +2247,10 @@ HotelCalendar.prototype = {
},
_dispatchEvent: function(/*String*/eventName, /*Dictionary*/data) {
this.e.dispatchEvent(new CustomEvent(eventName, { 'detail': data }));
this.e.dispatchEvent(new CustomEvent(eventName, {
'detail': data,
'calendar': this,
}));
},
_sanitizeId: function(/*String*/str) {
@@ -2744,7 +2760,7 @@ function HReservation(/*Dictionary*/rValues) {
}
this.id = rValues.id;
this.room = rValues.room;
this.room_id = rValues.room_id;
this.adults = rValues.adults || 1;
this.childrens = rValues.childrens || 0;
this.title = rValues.title || '';