mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
Merge branch 'pr_multicalendar' of https://github.com/hootel/hootel into 11.0
This commit is contained in:
@@ -97,7 +97,7 @@ class HotelFolio(models.Model):
|
||||
readonly=True, copy=False,
|
||||
index=True, track_visibility='onchange',
|
||||
default='draft')
|
||||
|
||||
|
||||
|
||||
# Partner fields for being used directly in the Folio views---------
|
||||
email = fields.Char('E-mail', related='partner_id.email')
|
||||
@@ -165,7 +165,7 @@ class HotelFolio(models.Model):
|
||||
has_checkout_to_send = fields.Boolean(
|
||||
compute='_compute_has_checkout_to_send')
|
||||
|
||||
#Generic Fields-----------------------------------------------------
|
||||
#Generic Fields-----------------------------------------------------
|
||||
internal_comment = fields.Text(string='Internal Folio Notes')
|
||||
cancelled_reason = fields.Text('Cause of cancelled')
|
||||
closure_reason_id = fields.Many2one('room.closure.reason')
|
||||
@@ -349,7 +349,7 @@ class HotelFolio(models.Model):
|
||||
).next_by_code('hotel.folio') or _('New')
|
||||
else:
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('hotel.folio') or _('New')
|
||||
|
||||
|
||||
|
||||
# Makes sure partner_invoice_id' and 'pricelist_id' are defined
|
||||
lfields = ('partner_invoice_id', 'partner_shipping_id', 'pricelist_id')
|
||||
@@ -402,7 +402,7 @@ class HotelFolio(models.Model):
|
||||
self.pricelist_id.is_staff,
|
||||
self.reservation_type)}
|
||||
self.update(values)
|
||||
|
||||
|
||||
|
||||
@api.model
|
||||
def calcule_reservation_type(self, is_staff, current_type):
|
||||
@@ -753,13 +753,13 @@ class HotelFolio(models.Model):
|
||||
info_grouped = []
|
||||
for rline in self.room_lines:
|
||||
if (import_all or rline.to_send) and \
|
||||
not rline.parent_reservation and rline.state == state:
|
||||
dates = rline.get_real_checkin_checkout()
|
||||
not rline.parent_reservation and rline.state == state:
|
||||
dates = (rline.real_checkin, rline.real_checkout)
|
||||
vals = {
|
||||
'num': len(
|
||||
self.room_lines.filtered(
|
||||
lambda r: r.get_real_checkin_checkout()[0] == dates[0] and \
|
||||
r.get_real_checkin_checkout()[1] == dates[1] and \
|
||||
lambda r: r.real_checkin == dates[0] and \
|
||||
r.real_checkout == dates[1] and \
|
||||
r.room_type_id.id == rline.room_type_id.id and \
|
||||
(r.to_send or import_all) and not r.parent_reservation and \
|
||||
r.state == rline.state)
|
||||
|
||||
@@ -151,6 +151,10 @@ class HotelReservation(models.Model):
|
||||
checkout = fields.Date('Check Out', required=True,
|
||||
default=_get_default_checkout,
|
||||
track_visibility='onchange')
|
||||
real_checkin = fields.Date('Real Check In', required=True,
|
||||
track_visibility='onchange')
|
||||
real_checkout = fields.Date('Real Check Out', required=True,
|
||||
track_visibility='onchange')
|
||||
arrival_hour = fields.Char('Arrival Hour',
|
||||
default=_get_default_arrival_hour,
|
||||
help="Default Arrival Hour (HH:MM)")
|
||||
@@ -309,7 +313,11 @@ class HotelReservation(models.Model):
|
||||
vals.update(self.prepare_reservation_lines(
|
||||
vals['checkin'],
|
||||
days_diff,
|
||||
vals=vals)) #REVISAR el unlink
|
||||
vals=vals)) # REVISAR el unlink
|
||||
if 'checkin' in vals and 'checkout' in vals \
|
||||
and 'real_checkin' not in vals and 'real_checkout' not in vals:
|
||||
vals['real_checkin'] = vals['checkin']
|
||||
vals['real_checkout'] = vals['checkout']
|
||||
record = super(HotelReservation, self).create(vals)
|
||||
#~ if (record.state == 'draft' and record.folio_id.state == 'sale') or \
|
||||
#~ record.preconfirm:
|
||||
@@ -325,6 +333,13 @@ class HotelReservation(models.Model):
|
||||
for record in self:
|
||||
checkin = vals['checkin'] if 'checkin' in vals else record.checkin
|
||||
checkout = vals['checkout'] if 'checkout' in vals else record.checkout
|
||||
|
||||
if not record.splitted and not vals.get('splitted', False):
|
||||
if 'checkin' in vals:
|
||||
vals['real_checkin'] = vals['checkin']
|
||||
if 'checkout' in vals:
|
||||
vals['real_checkout'] = vals['checkout']
|
||||
|
||||
days_diff = (
|
||||
fields.Date.from_string(checkout) - \
|
||||
fields.Date.from_string(checkin)
|
||||
@@ -485,6 +500,8 @@ class HotelReservation(models.Model):
|
||||
'splitted': self.splitted,
|
||||
'room_type_id': self.room_type_id.id,
|
||||
'room_id': self.room_id.id,
|
||||
'real_checkin': self.real_checkin,
|
||||
'real_checkout': self.real_checkout,
|
||||
}
|
||||
|
||||
@api.constrains('adults')
|
||||
@@ -952,39 +969,13 @@ class HotelReservation(models.Model):
|
||||
RESERVATION SPLITTED -----------------------------------------------
|
||||
"""
|
||||
|
||||
@api.multi
|
||||
def get_real_checkin_checkout(self):
|
||||
self.ensure_one()
|
||||
if not self.splitted:
|
||||
return (self.checkin, self.checkout)
|
||||
|
||||
master_reservation = self.parent_reservation or self
|
||||
splitted_reservs = self.env['hotel.reservation'].search([
|
||||
'|',
|
||||
('splitted', '=', True),
|
||||
('id', '=', master_reservation.id), # This here because can create a splitted reserv before set as splitted the parent reservation (master)
|
||||
('folio_id', '=', self.folio_id.id),
|
||||
'|',
|
||||
('parent_reservation', '=', master_reservation.id),
|
||||
('id', '=', master_reservation.id)
|
||||
])
|
||||
last_checkout = splitted_reservs[0].checkout
|
||||
first_checkin = splitted_reservs[0].checkin
|
||||
for reserv in splitted_reservs:
|
||||
if last_checkout < reserv.checkout:
|
||||
last_checkout = reserv.checkout
|
||||
if first_checkin > reserv.checkin:
|
||||
first_checkin = reserv.checkin
|
||||
return (first_checkin, last_checkout)
|
||||
|
||||
@api.multi
|
||||
def split(self, nights):
|
||||
for record in self:
|
||||
date_start_dt = fields.Date.from_string(record.checkin)
|
||||
date_end_dt = fields.Date.from_string(record.checkout)
|
||||
date_diff = abs((date_end_dt - date_start_dt).days)
|
||||
new_start_date_dt = date_start_dt + \
|
||||
timedelta(days=date_diff-nights)
|
||||
new_start_date_dt = date_start_dt + timedelta(days=date_diff-nights)
|
||||
if nights >= date_diff or nights < 1:
|
||||
raise ValidationError(_("Invalid Nights! Max is \
|
||||
'%d'") % (date_diff-1))
|
||||
@@ -1058,19 +1049,24 @@ class HotelReservation(models.Model):
|
||||
|
||||
@api.model
|
||||
def unify_books(self, splitted_reservs):
|
||||
master_reservation = splitted_reservs[0].parent_reservation or splitted_reservs[0]
|
||||
parent_reservation = splitted_reservs[0].parent_reservation or splitted_reservs[0]
|
||||
room_type_ids = splitted_reservs.mapped('room_type_id.id')
|
||||
if len(room_type_ids) > 1 or \
|
||||
(len(room_type_ids) == 1
|
||||
and master_reservation.room_type_id.id != room_type_ids[0]):
|
||||
and parent_reservation.room_type_id.id != room_type_ids[0]):
|
||||
raise ValidationError(_("This reservation can't be unified: They \
|
||||
all need to be in the same room"))
|
||||
|
||||
# Search checkout
|
||||
last_checkout = splitted_reservs[0].checkout
|
||||
first_checkin = splitted_reservs[0].checkin
|
||||
master_reservation = splitted_reservs[0]
|
||||
for reserv in splitted_reservs:
|
||||
if last_checkout < reserv.checkout:
|
||||
last_checkout = reserv.checkout
|
||||
if first_checkin > reserv.checkin:
|
||||
first_checkin = reserv.checkin
|
||||
master_reservation = reserv
|
||||
|
||||
# Agrupate reservation lines
|
||||
reservation_line_ids = splitted_reservs.mapped('reservation_line_ids')
|
||||
@@ -1088,9 +1084,15 @@ class HotelReservation(models.Model):
|
||||
osplitted_reservs = splitted_reservs - master_reservation
|
||||
osplitted_reservs.sudo().unlink()
|
||||
|
||||
_logger.info("========== UNIFY")
|
||||
_logger.info(master_reservation.real_checkin)
|
||||
_logger.info(first_checkin)
|
||||
_logger.info(master_reservation.real_checkout)
|
||||
_logger.info(last_checkout)
|
||||
|
||||
master_reservation.write({
|
||||
'checkout': last_checkout,
|
||||
'splitted': master_reservation.get_real_checkin_checkout()[1] != last_checkout,
|
||||
'splitted': master_reservation.real_checkin != first_checkin or master_reservation.real_checkout != last_checkout,
|
||||
'reservation_line_ids': rlines,
|
||||
'price_total': tprice,
|
||||
})
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
'views/inherited_res_users_views.xml',
|
||||
'views/inherited_hotel_room_type_views.xml',
|
||||
'views/inherited_hotel_room_views.xml',
|
||||
'views/room_pricelist_cached_views.xml',
|
||||
'views/hotel_reservation_views.xml',
|
||||
'views/hotel_calendar_management_views.xml',
|
||||
'views/hotel_calendar_views.xml',
|
||||
|
||||
@@ -22,9 +22,6 @@
|
||||
web_icon="hotel_calendar,static/description/icon_calendar_configurator.png"
|
||||
action="action_hotel_calendar_management" groups="hotel.group_hotel_manager" />
|
||||
|
||||
<menuitem id="hotel_room_pricelist_cached" name="Room Pricelist Cached"
|
||||
sequence="1" action="hotel_room_pricelist_cached_action_form_tree" parent="sale.menu_sale_config"/>
|
||||
|
||||
<menuitem id="hotel_calendar_record_menu" name="Calendars"
|
||||
parent="hotel.hotel_configuration_menu" sequence="10"
|
||||
groups="hotel.group_hotel_manager"
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from . import hotel_calendar
|
||||
from . import bus_hotel_calendar
|
||||
from . import room_pricelist_cached
|
||||
from . import hotel_calendar_management
|
||||
from . import inherited_hotel_reservation
|
||||
from . import inherited_product_pricelist_item
|
||||
from . import inherited_res_users
|
||||
from . import inherited_hotel_room
|
||||
from . import inherited_hotel_room_type
|
||||
from . import inherited_hotel_room_type_restriction_item
|
||||
from . import inherited_hotel_room_type_availability
|
||||
from . import inherited_product_pricelist
|
||||
from . import inherited_product_pricelist_item
|
||||
from . import inherited_hotel_folio
|
||||
from . import ir_default
|
||||
from . import ir_actions_act_window_view
|
||||
from . import ir_ui_view
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import (
|
||||
DEFAULT_SERVER_DATE_FORMAT,
|
||||
DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -18,10 +16,6 @@ class HotelReservation(models.Model):
|
||||
reserve_color_text = fields.Char(compute='_compute_color', string='Color',
|
||||
store=True)
|
||||
|
||||
"""
|
||||
COMPUTE RESERVE COLOR ----------------------------------------------
|
||||
"""
|
||||
|
||||
@api.multi
|
||||
def _generate_color(self):
|
||||
self.ensure_one()
|
||||
@@ -79,54 +73,48 @@ class HotelReservation(models.Model):
|
||||
json_reservation_tooltips = {}
|
||||
for reserv in reservations:
|
||||
json_reservations.append({
|
||||
'room_id': reserv.room_id.id,
|
||||
'id': reserv.id,
|
||||
'name': reserv.folio_id.closure_reason_id.name or _('Out of service') if reserv.folio_id.reservation_type == 'out'
|
||||
else reserv.folio_id.partner_id.name,
|
||||
'adults': reserv.adults,
|
||||
'childrens': reserv.children,
|
||||
'checkin': reserv.checkin,
|
||||
'checkout': reserv.checkout,
|
||||
'folio_id': reserv.folio_id.id,
|
||||
'bgcolor': reserv.reserve_color,
|
||||
'color': reserv.reserve_color_text,
|
||||
'splitted': reserv.splitted,
|
||||
'parent_reservation': reserv.parent_reservation and reserv.parent_reservation.id or False,
|
||||
'room_id': reserv['room_id'],
|
||||
'id': reserv['id'],
|
||||
'name': reserv['closure_reason'] or _('Out of service')
|
||||
if reserv['reservation_type'] == 'out'
|
||||
else reserv['partner_name'],
|
||||
'adults': reserv['adults'],
|
||||
'childrens': reserv['children'],
|
||||
'checkin': reserv['checkin'],
|
||||
'checkout': reserv['checkout'],
|
||||
'folio_id': reserv['folio_id'],
|
||||
'bgcolor': reserv['reserve_color'],
|
||||
'color': reserv['reserve_color_text'],
|
||||
'splitted': reserv['splitted'],
|
||||
'parent_reservation': reserv['parent_reservation'] or False,
|
||||
'read_only': False, # Read-Only
|
||||
'fix_days': reserv.splitted, # Fix Days
|
||||
'fix_days': reserv['splitted'], # Fix Days
|
||||
'fix_room': False, # Fix Rooms
|
||||
'overbooking': reserv.overbooking,
|
||||
'state': reserv.state,
|
||||
'real_dates': reserv.get_real_checkin_checkout()})
|
||||
num_split = 0
|
||||
if reserv.splitted:
|
||||
master_reserv = reserv.parent_reservation or reserv
|
||||
num_split = self.search_count([
|
||||
('folio_id', '=', reserv.folio_id.id),
|
||||
'|', ('parent_reservation', '=', master_reserv.id),
|
||||
('id', '=', master_reserv.id),
|
||||
('splitted', '=', True),
|
||||
])
|
||||
'overbooking': reserv['overbooking'],
|
||||
'state': reserv['state'],
|
||||
'real_dates': [reserv['real_checkin'], reserv['real_checkout']]})
|
||||
json_reservation_tooltips.update({
|
||||
reserv.id: {
|
||||
'folio_name': reserv.folio_id.name,
|
||||
'name': _('Out of service') if reserv.folio_id.reservation_type == 'out' else reserv.folio_id.partner_id.name,
|
||||
'phone': reserv.mobile or reserv.phone or _('Phone not provided'),
|
||||
'email': reserv.email or _('Email not provided'),
|
||||
'room_type_name': reserv.room_type_id.name,
|
||||
'adults': reserv.adults,
|
||||
'children': reserv.children,
|
||||
'checkin': reserv.checkin,
|
||||
'checkout': reserv.checkout,
|
||||
'arrival_hour': reserv.arrival_hour,
|
||||
'departure_hour': reserv.departure_hour,
|
||||
'num_split': num_split,
|
||||
'amount_total': reserv.folio_id.amount_total,
|
||||
'pending_amount': reserv.folio_id.pending_amount,
|
||||
'amount_paid': reserv.folio_id.amount_total - reserv.folio_id.pending_amount,
|
||||
'type': reserv.reservation_type or 'normal',
|
||||
'out_service_description': reserv.out_service_description or
|
||||
_('No reason given'),
|
||||
reserv['id']: {
|
||||
'folio_name': reserv['folio_id'],
|
||||
'name': _('Out of service')
|
||||
if reserv['reservation_type'] == 'out'
|
||||
else reserv['partner_name'],
|
||||
'phone': reserv['mobile'] or reserv['phone']
|
||||
or _('Phone not provided'),
|
||||
'email': reserv['email'] or _('Email not provided'),
|
||||
'room_type_name': reserv['room_type'],
|
||||
'adults': reserv['adults'],
|
||||
'children': reserv['children'],
|
||||
'checkin': reserv['checkin'],
|
||||
'checkout': reserv['checkout'],
|
||||
'arrival_hour': reserv['arrival_hour'],
|
||||
'departure_hour': reserv['departure_hour'],
|
||||
'amount_total': reserv['amount_total'],
|
||||
'pending_amount': reserv['pending_amount'],
|
||||
'amount_paid': reserv['amount_total'] - (reserv['pending_amount'] or 0.0),
|
||||
'type': reserv['reservation_type'] or 'normal',
|
||||
'out_service_description': reserv['out_service_description']
|
||||
or _('No reason given'),
|
||||
# TODO: Add Board Services and Extra Service as Cradle, Bed, ...
|
||||
}
|
||||
})
|
||||
@@ -138,9 +126,8 @@ class HotelReservation(models.Model):
|
||||
'res.config.settings', 'default_pricelist_id')
|
||||
if pricelist_id:
|
||||
pricelist_id = int(pricelist_id)
|
||||
json_rooms = []
|
||||
for room in rooms:
|
||||
json_rooms.append({
|
||||
json_rooms = [
|
||||
{
|
||||
'id': room.id,
|
||||
'name': room.name,
|
||||
'capacity': room.capacity,
|
||||
@@ -148,163 +135,190 @@ class HotelReservation(models.Model):
|
||||
'class_id': room.room_type_id.class_id.id,
|
||||
'shared': room.shared_room,
|
||||
'price': room.room_type_id
|
||||
and ['pricelist', room.room_type_id.id, pricelist_id,
|
||||
room.room_type_id.name] or 0,
|
||||
and ['pricelist', room.room_type_id.id, pricelist_id,
|
||||
room.room_type_id.name] or 0,
|
||||
'room_type_name': room.room_type_id.name,
|
||||
'room_type_id': room.room_type_id.id,
|
||||
'floor_id': room.floor_id.id,
|
||||
'amentity_ids': room.room_type_id.room_amenity_ids.ids,
|
||||
})
|
||||
} for room in rooms]
|
||||
return json_rooms
|
||||
|
||||
@api.model
|
||||
def _hcalendar_calendar_data(self, calendars):
|
||||
json_calendars = []
|
||||
for calendar in calendars:
|
||||
json_calendars.append({
|
||||
return [
|
||||
{
|
||||
'id': calendar.id,
|
||||
'name': calendar.name,
|
||||
'segmentation_ids': calendar.segmentation_ids.ids,
|
||||
'location_ids': calendar.location_ids.ids,
|
||||
'amenity_ids': calendar.amenity_ids.ids,
|
||||
'room_type_ids': calendar.room_type_ids.ids,
|
||||
})
|
||||
return json_calendars
|
||||
} for calendar in calendars]
|
||||
|
||||
@api.model
|
||||
def _hcalendar_event_data(self, events):
|
||||
json_events = []
|
||||
for event in events:
|
||||
json_events.append({
|
||||
json_events = [
|
||||
{
|
||||
'id': event.id,
|
||||
'name': event.name,
|
||||
'date': event.start,
|
||||
'location': event.location,
|
||||
})
|
||||
} for event in events]
|
||||
return json_events
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_reservations_data(self, dfrom, dto, rooms):
|
||||
date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
|
||||
date_start_str = date_start.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
reservations_raw = self.env['hotel.reservation'].search(
|
||||
[('room_id', 'in', rooms.ids)],
|
||||
order="checkin DESC, checkout ASC, adults DESC, children DESC")
|
||||
reservations_ll = self.env['hotel.reservation'].search([
|
||||
('checkin', '<=', dto),
|
||||
('checkout', '>=', date_start_str)
|
||||
])
|
||||
reservations_lr = self.env['hotel.reservation'].search([
|
||||
('checkin', '>=', date_start_str),
|
||||
('checkout', '<=', dto)
|
||||
])
|
||||
reservations = (reservations_ll | reservations_lr) & reservations_raw
|
||||
return self._hcalendar_reservation_data(reservations)
|
||||
def get_hcalendar_calendar_data(self):
|
||||
calendars = self.env['hotel.calendar'].search([])
|
||||
res = self._hcalendar_calendar_data(calendars)
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_pricelist_data(self, dfrom, dto):
|
||||
def get_hcalendar_reservations_data(self, dfrom_dt, dto_dt, rooms):
|
||||
rdfrom_dt = dfrom_dt + timedelta(days=1) # Ignore checkout
|
||||
rdfrom_str = rdfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
dto_str = dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
self.env.cr.execute('''
|
||||
SELECT
|
||||
hr.id, hr.room_id, hr.adults, hr.children, hr.checkin, hr.checkout, hr.reserve_color, hr.reserve_color_text,
|
||||
hr.splitted, hr.parent_reservation, hr.overbooking, hr.state, hr.real_checkin, hr.real_checkout,
|
||||
hr.out_service_description, hr.arrival_hour, hr.departure_hour,
|
||||
|
||||
hf.id as folio_id, hf.name as folio_name, hf.reservation_type, hf.amount_total, hf.pending_amount,
|
||||
|
||||
rp.mobile, rp.phone, rp.email, rp.name as partner_name,
|
||||
|
||||
pt.name as room_type,
|
||||
|
||||
rcr.name as closure_reason
|
||||
FROM hotel_reservation AS hr
|
||||
LEFT JOIN hotel_folio AS hf ON hr.folio_id = hf.id
|
||||
LEFT JOIN hotel_room_type AS hrt ON hr.room_type_id = hrt.id
|
||||
LEFT JOIN product_product AS pp ON hrt.product_id = pp.id
|
||||
LEFT JOIN product_template AS pt ON pp.product_tmpl_id = pt.id
|
||||
LEFT JOIN res_partner AS rp ON hf.partner_id = rp.id
|
||||
LEFT JOIN room_closure_reason as rcr
|
||||
ON hf.closure_reason_id = rcr.id
|
||||
WHERE room_id IN %s AND (
|
||||
(checkin <= %s AND checkout >= %s AND checkout <= %s)
|
||||
OR (checkin >= %s AND checkout <= %s)
|
||||
OR (checkin >= %s AND checkin <= %s AND checkout >= %s)
|
||||
OR (checkin <= %s AND checkout >= %s))
|
||||
ORDER BY checkin DESC, checkout ASC, adults DESC, children DESC
|
||||
''', (tuple(rooms.ids),
|
||||
rdfrom_str, rdfrom_str, dto_str,
|
||||
rdfrom_str, dto_str,
|
||||
rdfrom_str, dto_str, dto_str,
|
||||
rdfrom_str, dto_str))
|
||||
return self._hcalendar_reservation_data(self.env.cr.dictfetchall())
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_pricelist_data(self, dfrom_dt, dto_dt):
|
||||
pricelist_id = self.env['ir.default'].sudo().get(
|
||||
'res.config.settings', 'default_pricelist_id')
|
||||
if pricelist_id:
|
||||
pricelist_id = int(pricelist_id)
|
||||
date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
|
||||
date_end = fields.Date.from_string(dto)
|
||||
date_diff = abs((date_end - date_start).days) + 1
|
||||
# Get Prices
|
||||
json_rooms_prices = {pricelist_id: []}
|
||||
room_typed_ids = self.env['hotel.room.type'].search(
|
||||
[],
|
||||
order='hcal_sequence ASC')
|
||||
room_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
|
||||
for room_type_id in room_typed_ids:
|
||||
days = {}
|
||||
for i in range(0, date_diff):
|
||||
ndate = date_start + timedelta(days=i)
|
||||
ndate_str = ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
prod_price_id = room_pr_cached_obj.search([
|
||||
('room_id', '=', room_type_id.id),
|
||||
('date', '=', ndate_str)
|
||||
], limit=1)
|
||||
days.update({
|
||||
ndate.strftime("%d/%m/%Y"): prod_price_id and
|
||||
prod_price_id.price or
|
||||
room_type_id.product_id.with_context(
|
||||
quantity=1,
|
||||
date=ndate_str,
|
||||
pricelist=pricelist_id).price
|
||||
room_types_ids = self.env['hotel.room.type'].search([])
|
||||
|
||||
dfrom_str = dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
dto_str = dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
|
||||
self.env.cr.execute('''
|
||||
WITH RECURSIVE gen_table_days AS (
|
||||
SELECT hrt.id, %s::Date AS date
|
||||
FROM hotel_room_type AS hrt
|
||||
UNION ALL
|
||||
SELECT hrt.id, (td.date + INTERVAL '1 day')::Date
|
||||
FROM gen_table_days as td
|
||||
LEFT JOIN hotel_room_type AS hrt ON hrt.id=td.id
|
||||
WHERE td.date < %s
|
||||
)
|
||||
SELECT
|
||||
TO_CHAR(gtd.date, 'DD/MM/YYYY') as date, gtd.id as room_type_id,
|
||||
pt.name, ppi.fixed_price as price, pt.list_price
|
||||
FROM gen_table_days AS gtd
|
||||
LEFT JOIN hotel_room_type AS hrt ON hrt.id = gtd.id
|
||||
LEFT JOIN product_product AS pp ON pp.id = hrt.product_id
|
||||
LEFT JOIN product_template AS pt ON pt.id = pp.product_tmpl_id
|
||||
LEFT JOIN product_pricelist_item AS ppi ON ppi.date_start = gtd.date AND ppi.date_end = gtd.date AND ppi.product_tmpl_id = pt.id
|
||||
WHERE gtd.id IN %s
|
||||
ORDER BY gtd.id ASC, gtd.date ASC
|
||||
''', (dfrom_str, dto_str, tuple(room_types_ids.ids)))
|
||||
query_results = self.env.cr.dictfetchall()
|
||||
|
||||
json_data = {}
|
||||
for results in query_results:
|
||||
if results['room_type_id'] not in json_data:
|
||||
json_data.setdefault(results['room_type_id'], {}).update({
|
||||
'title': results['name'],
|
||||
'room': results['room_type_id'],
|
||||
})
|
||||
json_rooms_prices[pricelist_id].append({
|
||||
'room': room_type_id.id,
|
||||
'days': days,
|
||||
'title': room_type_id.name,
|
||||
json_data[results['room_type_id']].setdefault('days', {}).update({
|
||||
results['date']: results['price'] or results['list_price']
|
||||
})
|
||||
|
||||
json_rooms_prices = {}
|
||||
for prices in list(json_data.values()):
|
||||
json_rooms_prices.setdefault(pricelist_id, []).append(prices)
|
||||
return json_rooms_prices
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_restrictions_data(self, dfrom, dto):
|
||||
def get_hcalendar_restrictions_data(self, dfrom_dt, dto_dt):
|
||||
restriction_id = self.env['ir.default'].sudo().get(
|
||||
'res.config.settings', 'default_restriction_id')
|
||||
if restriction_id:
|
||||
restriction_id = int(restriction_id)
|
||||
date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
|
||||
date_end = fields.Date.from_string(dto)
|
||||
date_diff = abs((date_end - date_start).days) + 1
|
||||
# Get Prices
|
||||
|
||||
# Get Restrictions
|
||||
json_rooms_rests = {}
|
||||
room_types = self.env['hotel.room.type'].search(
|
||||
room_typed_ids = self.env['hotel.room.type'].search(
|
||||
[],
|
||||
order='hcal_sequence ASC')
|
||||
room_type_rest_obj = self.env['hotel.room.type.restriction.item']
|
||||
for room_type in room_types:
|
||||
rtype_rest_ids = room_type_rest_obj.search([
|
||||
('room_type_id', 'in', room_typed_ids.ids),
|
||||
('date', '>=', dfrom_dt),
|
||||
('date', '<=', dto_dt),
|
||||
('restriction_id', '=', restriction_id)
|
||||
])
|
||||
|
||||
for room_type in room_typed_ids:
|
||||
days = {}
|
||||
for i in range(0, date_diff):
|
||||
ndate = date_start + timedelta(days=i)
|
||||
ndate_str = ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)
|
||||
rest_id = room_type_rest_obj.search([
|
||||
('room_type_id', '=', room_type.id),
|
||||
('date', '=', ndate_str),
|
||||
('restriction_id', '=', restriction_id)
|
||||
], limit=1)
|
||||
if rest_id and (rest_id.min_stay or rest_id.min_stay_arrival or
|
||||
rest_id.max_stay or rest_id.max_stay_arrival or
|
||||
rest_id.closed or rest_id.closed_arrival or
|
||||
rest_id.closed_departure):
|
||||
days.update({
|
||||
ndate.strftime("%d/%m/%Y"): (
|
||||
rest_id.min_stay,
|
||||
rest_id.min_stay_arrival,
|
||||
rest_id.max_stay,
|
||||
rest_id.max_stay_arrival,
|
||||
rest_id.closed,
|
||||
rest_id.closed_arrival,
|
||||
rest_id.closed_departure)
|
||||
})
|
||||
rest_ids = rtype_rest_ids.filtered(
|
||||
lambda x: x.room_type_id == room_type)
|
||||
for rest_id in rest_ids:
|
||||
days.update({
|
||||
fields.Date.from_string(rest_id.date).strftime("%d/%m/%Y"): (
|
||||
rest_id.min_stay,
|
||||
rest_id.min_stay_arrival,
|
||||
rest_id.max_stay,
|
||||
rest_id.max_stay_arrival,
|
||||
rest_id.closed,
|
||||
rest_id.closed_arrival,
|
||||
rest_id.closed_departure)
|
||||
})
|
||||
json_rooms_rests.update({room_type.id: days})
|
||||
return json_rooms_rests
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_events_data(self, dfrom, dto):
|
||||
date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
|
||||
date_start_str = date_start.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
def get_hcalendar_events_data(self, dfrom_dt, dto_dt):
|
||||
user_id = self.env['res.users'].browse(self.env.uid)
|
||||
domain = []
|
||||
domain = [
|
||||
'|', '&',
|
||||
('start', '<=', dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('stop', '>=', dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)),
|
||||
'&',
|
||||
('start', '>=', dfrom_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)),
|
||||
('stop', '<=', dto_dt.strftime(DEFAULT_SERVER_DATE_FORMAT))
|
||||
]
|
||||
if user_id.pms_allowed_events_tags:
|
||||
domain.append(('categ_ids', 'in', user_id.pms_allowed_events_tags))
|
||||
if user_id.pms_denied_events_tags:
|
||||
domain.append(
|
||||
('categ_ids', 'not in', user_id.pms_denied_events_tags))
|
||||
events_raw = self.env['calendar.event'].search(domain)
|
||||
events_ll = self.env['calendar.event'].search([
|
||||
('start', '<=', dto),
|
||||
('stop', '>=', date_start_str)
|
||||
])
|
||||
events_lr = self.env['calendar.event'].search([
|
||||
('start', '>=', date_start_str),
|
||||
('stop', '<=', dto)
|
||||
])
|
||||
events = (events_ll | events_lr) & events_raw
|
||||
return self._hcalendar_event_data(events)
|
||||
return self._hcalendar_event_data(events_raw)
|
||||
|
||||
@api.model
|
||||
def get_hcalendar_settings(self):
|
||||
@@ -332,19 +346,23 @@ class HotelReservation(models.Model):
|
||||
if not dfrom or not dto:
|
||||
raise ValidationError(_('Input Error: No dates defined!'))
|
||||
|
||||
dfrom_dt = fields.Date.from_string(dfrom)
|
||||
dto_dt = fields.Date.from_string(dto)
|
||||
rooms = self.env['hotel.room'].search([], order='hcal_sequence ASC')
|
||||
calendars = self.env['hotel.calendar'].search([])
|
||||
|
||||
json_res, json_res_tooltips = self.get_hcalendar_reservations_data(
|
||||
dfrom, dto, rooms)
|
||||
dfrom_dt, dto_dt, rooms)
|
||||
|
||||
vals = {
|
||||
'rooms': withRooms and self._hcalendar_room_data(rooms) or [],
|
||||
'reservations': json_res,
|
||||
'tooltips': json_res_tooltips,
|
||||
'pricelist': self.get_hcalendar_pricelist_data(dfrom, dto),
|
||||
'restrictions': self.get_hcalendar_restrictions_data(dfrom, dto),
|
||||
'events': self.get_hcalendar_events_data(dfrom, dto),
|
||||
'calendars': self._hcalendar_calendar_data(calendars)
|
||||
'pricelist': self.get_hcalendar_pricelist_data(dfrom_dt, dto_dt),
|
||||
'restrictions': self.get_hcalendar_restrictions_data(dfrom_dt,
|
||||
dto_dt),
|
||||
'events': self.get_hcalendar_events_data(dfrom_dt, dto_dt),
|
||||
'calendars': withRooms and self.get_hcalendar_calendar_data()
|
||||
or []
|
||||
}
|
||||
|
||||
return vals
|
||||
@@ -359,7 +377,7 @@ class HotelReservation(models.Model):
|
||||
'room_id': self.room_id.id,
|
||||
'reserv_id': self.id,
|
||||
'partner_name': (self.closure_reason_id.name or _('Out of service'))
|
||||
if self.reservation_type == 'out' else self.partner_id.name,
|
||||
if self.reservation_type == 'out' else self.partner_id.name,
|
||||
'adults': self.adults,
|
||||
'children': self.children,
|
||||
'checkin': self.checkin,
|
||||
@@ -370,12 +388,12 @@ class HotelReservation(models.Model):
|
||||
'reserve_color': self.reserve_color,
|
||||
'reserve_color_text': self.reserve_color_text,
|
||||
'splitted': self.splitted,
|
||||
'parent_reservation': self.parent_reservation and
|
||||
self.parent_reservation.id or 0,
|
||||
'parent_reservation': self.parent_reservation
|
||||
and self.parent_reservation.id or 0,
|
||||
'room_name': self.room_id.name,
|
||||
'room_type_name': self.room_type_id.name,
|
||||
'partner_phone': self.partner_id.mobile
|
||||
or self.partner_id.phone or _('Undefined'),
|
||||
or self.partner_id.phone or _('Undefined'),
|
||||
'partner_email': self.partner_id.email or _('Undefined'),
|
||||
'state': self.state,
|
||||
'fix_days': self.splitted,
|
||||
@@ -385,8 +403,9 @@ class HotelReservation(models.Model):
|
||||
'amount_paid': self.folio_id.amount_total - self.folio_id.pending_amount,
|
||||
'reservation_type': self.reservation_type or 'normal',
|
||||
'closure_reason_id': self.closure_reason_id,
|
||||
'out_service_description': self.out_service_description or _('No reason given'),
|
||||
'real_dates': self.get_real_checkin_checkout(),
|
||||
'out_service_description': self.out_service_description
|
||||
or _('No reason given'),
|
||||
'real_dates': [self.real_checkin, self.real_checkout],
|
||||
}
|
||||
|
||||
@api.multi
|
||||
@@ -441,24 +460,24 @@ class HotelReservation(models.Model):
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
ret = super(HotelReservation, self).write(vals)
|
||||
_logger.info("RESERV WRITE")
|
||||
ret = super(HotelReservation, self).write(vals)
|
||||
|
||||
if 'partner_id' in vals or 'checkin' in vals or \
|
||||
'checkout' in vals or 'product_id' in vals or \
|
||||
'adults' in vals or 'children' in vals or \
|
||||
'state' in vals or 'splitted' in vals or \
|
||||
'closure_reason_id' in vals or 'out_service_description' in vals or \
|
||||
'reservation_type' in vals or \
|
||||
'reserve_color' in vals or \
|
||||
'reserve_color_text' in vals or 'price_total' in vals or \
|
||||
'price_total' in vals or \
|
||||
'parent_reservation' in vals or 'overbooking' in vals or \
|
||||
'room_type_id' in vals:
|
||||
for record in self:
|
||||
record.send_bus_notification(
|
||||
'write',
|
||||
(record.state == 'cancelled') and 'warn' or 'notify',
|
||||
(record.state == 'cancelled') and
|
||||
_("Reservation Cancelled") or _("Reservation Changed")
|
||||
(record.state == 'cancelled')
|
||||
and _("Reservation Cancelled") or _("Reservation Changed")
|
||||
)
|
||||
elif not any(vals) or 'to_read' in vals or 'to_assign' in vals:
|
||||
self.send_bus_notification('write', 'noshow')
|
||||
|
||||
@@ -1,21 +1,9 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, fields, api
|
||||
from odoo import models, fields
|
||||
|
||||
|
||||
class HotelRoomType(models.Model):
|
||||
_inherit = 'hotel.room.type'
|
||||
|
||||
hcal_sequence = fields.Integer('Calendar Sequence', default=0)
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
room_type_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
for record in self:
|
||||
pr_chached = room_type_pr_cached_obj.search([
|
||||
('room_id', '=', record.id)
|
||||
])
|
||||
# Because 'pricelist.cached' is an isolated model,
|
||||
# doesn't trigger 'ondelete'. Need call 'unlink' instead.
|
||||
pr_chached.unlink()
|
||||
return super(HotelRoomType, self).unlink()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, fields, api
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
class ProductPricelistItem(models.Model):
|
||||
@@ -33,20 +33,6 @@ class ProductPricelistItem(models.Model):
|
||||
'price': prod_price,
|
||||
'id': self.id,
|
||||
})
|
||||
|
||||
room_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
room_pr_cached_id = room_pr_cached_obj.search([
|
||||
('room_id', '=', room_type.id),
|
||||
('date', '=', date_start),
|
||||
], limit=1)
|
||||
if room_pr_cached_id:
|
||||
room_pr_cached_id.write({'price': prod_price})
|
||||
else:
|
||||
room_pr_cached_obj.create({
|
||||
'room_id': room_type.id,
|
||||
'date': date_start,
|
||||
'price': prod_price,
|
||||
})
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
@@ -57,7 +43,6 @@ class ProductPricelistItem(models.Model):
|
||||
pricelist_default_id = int(pricelist_default_id)
|
||||
ret_vals = super(ProductPricelistItem, self).write(vals)
|
||||
|
||||
room_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
bus_calendar_obj = self.env['bus.hotel.calendar']
|
||||
room_type_obj = self.env['hotel.room.type']
|
||||
if vals.get('fixed_price'):
|
||||
@@ -87,19 +72,6 @@ class ProductPricelistItem(models.Model):
|
||||
'price': prod_price,
|
||||
'id': record.id,
|
||||
})
|
||||
|
||||
room_pr_cached_id = room_pr_cached_obj.search([
|
||||
('room_id', '=', room_type.id),
|
||||
('date', '=', date_start),
|
||||
], limit=1)
|
||||
if room_pr_cached_id:
|
||||
room_pr_cached_id.write({'price': prod_price})
|
||||
else:
|
||||
room_pr_cached_obj.create({
|
||||
'room_id': room_type.id,
|
||||
'date': date_start,
|
||||
'price': prod_price,
|
||||
})
|
||||
return ret_vals
|
||||
|
||||
@api.multi
|
||||
@@ -125,7 +97,6 @@ class ProductPricelistItem(models.Model):
|
||||
# Do Normal Stuff
|
||||
res = super(ProductPricelistItem, self).unlink()
|
||||
# Do extra operations
|
||||
room_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
bus_calendar_obj = self.env['bus.hotel.calendar']
|
||||
for vals in unlink_vals:
|
||||
pricelist_id = vals['pricelist_id']
|
||||
@@ -144,12 +115,4 @@ class ProductPricelistItem(models.Model):
|
||||
'price': prod.price,
|
||||
'id': vals['id'],
|
||||
})
|
||||
|
||||
# Remove records from cache model
|
||||
room_pr_cached_id = room_pr_cached_obj.search([
|
||||
('room_id', '=', room_type.id),
|
||||
('date', '=', date_start),
|
||||
], limit=1)
|
||||
if room_pr_cached_id:
|
||||
room_pr_cached_id.unlink()
|
||||
return res
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, fields, api
|
||||
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
|
||||
|
||||
|
||||
class IrDefault(models.Model):
|
||||
_inherit = 'ir.default'
|
||||
|
||||
@api.model
|
||||
def set(self, model_name, field_name, value, user_id=False, company_id=False, condition=False):
|
||||
super(IrDefault, self).set(model_name, field_name, value, user_id, company_id, condition)
|
||||
if model_name == 'res.config.settings' and field_name == 'default_pricelist_id':
|
||||
pricelist_id = int(value)
|
||||
self.env['room.pricelist.cached'].search([]).unlink()
|
||||
|
||||
pricelist_items = self.env['product.pricelist.item'].search([
|
||||
('pricelist_id', '=', pricelist_id)
|
||||
])
|
||||
room_type_obj = self.env['hotel.room.type']
|
||||
room_pr_cached_obj = self.env['room.pricelist.cached']
|
||||
for pitem in pricelist_items:
|
||||
date_start = pitem.date_start
|
||||
product_tmpl_id = pitem.product_tmpl_id.id
|
||||
fixed_price = pitem.fixed_price
|
||||
room_type = room_type_obj.search([
|
||||
('product_id.product_tmpl_id', '=', product_tmpl_id),
|
||||
], limit=1)
|
||||
if room_type:
|
||||
room_pr_cached_obj.create({
|
||||
'room_id': room_type.id,
|
||||
'date': date_start,
|
||||
'price': fixed_price,
|
||||
})
|
||||
@@ -1,17 +0,0 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, fields, api
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class RoomPricelistCached(models.Model):
|
||||
'''
|
||||
Cached Pricelist. Used only for Calendar Values
|
||||
'''
|
||||
|
||||
_name = 'room.pricelist.cached'
|
||||
|
||||
room_id = fields.Many2one('hotel.room.type', 'Virtual Room',
|
||||
required=True, track_visibility='always')
|
||||
price = fields.Float('Price', default=0.0)
|
||||
date = fields.Date('Date', required=True, track_visibility='always')
|
||||
@@ -1,6 +1,4 @@
|
||||
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||
access_room_price_cache_user,hotel_calendar.model_room_pricelist_cached_user,hotel_calendar.model_room_pricelist_cached,hotel.group_hotel_user,1,1,1,1
|
||||
access_room_price_cache_call,hotel_calendar.model_room_pricelist_cached_call,hotel_calendar.model_room_pricelist_cached,hotel.group_hotel_call,1,1,1,1
|
||||
access_hotel_product_pricelist_item_call,hotel_calendar.pricelist_item_call,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_call,1,1,1,1
|
||||
access_hotel_product_pricelist_item_user,hotel_calendar.pricelist_item_use,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_user,1,1,1,1
|
||||
access_hotel_calendar,access_hotel_calendar,model_hotel_calendar,base.group_user,1,0,0,0
|
||||
|
||||
|
@@ -22,8 +22,10 @@
|
||||
}
|
||||
|
||||
#pms-menu {
|
||||
padding: 0 0.2em !important;
|
||||
overflow: auto;
|
||||
background-color: #f8f8f8;
|
||||
height: 100%;
|
||||
padding: 0 2.5em;
|
||||
}
|
||||
|
||||
#pms-menu .input-group span, #pms-menu input {
|
||||
@@ -41,6 +43,32 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
#pms-search {
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
#hcal_widget {
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
#multicalendar_panels {
|
||||
background-color: white;
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.nav-tabs > li > a {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.nav-tabs {
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.navbar-default {
|
||||
border-color: #f8f8f8;
|
||||
}
|
||||
|
||||
button .led {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
@@ -90,6 +118,13 @@ input#bookings_search {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
#pms-menu .menu-filter-box h4 {
|
||||
cursor: pointer;
|
||||
}
|
||||
#pms-menu .menu-filter-box h4 i {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
#pms-menu .button-box {
|
||||
text-align: left;
|
||||
min-height: 3.5em;
|
||||
@@ -173,7 +208,7 @@ input#bookings_search {
|
||||
max-width: 550px;
|
||||
}
|
||||
.popover .container {
|
||||
max-width: -webkit-fill-available;
|
||||
max-width: 100%;
|
||||
}
|
||||
.popover .container p {
|
||||
margin-top: 3px;
|
||||
@@ -230,4 +265,4 @@ input#bookings_search {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
/* TODO: Use Odoo Colours based on http://www.odoo.com/openerp_website/static/src/less/variables.less */
|
||||
/* TODO: Use Odoo Colours based on http://www.odoo.com/openerp_website/static/src/less/variables.less */
|
||||
|
||||
@@ -104,9 +104,10 @@ var PMSCalendarController = AbstractController.extend({
|
||||
/** DO MAGIC **/
|
||||
var hcal_dates = this.renderer.get_view_filter_dates();
|
||||
var oparams = [
|
||||
hcal_dates[0].format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
|
||||
hcal_dates[0].subtract(1, 'd').format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
|
||||
hcal_dates[1].format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT)
|
||||
];
|
||||
|
||||
this.model.get_calendar_data(oparams).then(function(results){
|
||||
self._multi_calendar._days_tooltips = results['events'];
|
||||
self._multi_calendar._reserv_tooltips = results['tooltips'];
|
||||
@@ -191,7 +192,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
self._view_options['days'] = 7;
|
||||
}
|
||||
|
||||
var date_begin = moment().startOf('day');
|
||||
var date_begin = moment().local().startOf('day');
|
||||
var days = self._view_options['days'];
|
||||
if (self._view_options['days'] === 'month') {
|
||||
days = date_begin.daysInMonth();
|
||||
@@ -205,7 +206,6 @@ var PMSCalendarController = AbstractController.extend({
|
||||
$dateTimePickerBegin.data("DateTimePicker").date(date_begin);
|
||||
$dateEndDays.val(self._view_options['days']);
|
||||
|
||||
//self.renderer.init_calendar_view();
|
||||
self._load_calendars();
|
||||
self._assign_view_events();
|
||||
});
|
||||
@@ -213,23 +213,24 @@ var PMSCalendarController = AbstractController.extend({
|
||||
|
||||
_reload_active_calendar: function() {
|
||||
var self = this;
|
||||
var filterDates = this.renderer.get_view_filter_dates();
|
||||
var active_calendar = this._multi_calendar.get_active_calendar();
|
||||
var filterDates = active_calendar.getDates();
|
||||
// Clip dates
|
||||
var dfrom = filterDates[0].clone(),
|
||||
dto = filterDates[1].clone();
|
||||
|
||||
if (filterDates[0].isBetween(this._last_dates[0], this._last_dates[1], 'days') && filterDates[1].isAfter(this._last_dates[1], 'day')) {
|
||||
dfrom = this._last_dates[1].clone().local().startOf('day').utc();
|
||||
dfrom = this._last_dates[1].clone();
|
||||
} else if (this._last_dates[0].isBetween(filterDates[0], filterDates[1], 'days') && this._last_dates[1].isAfter(filterDates[0], 'day')) {
|
||||
dto = this._last_dates[0].clone().local().endOf('day').utc();
|
||||
dto = this._last_dates[0].clone();
|
||||
}
|
||||
|
||||
var oparams = [
|
||||
dfrom.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
|
||||
dfrom.subtract(1, 'd').format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
|
||||
dto.format(HotelConstants.ODOO_DATETIME_MOMENT_FORMAT),
|
||||
false
|
||||
];
|
||||
|
||||
this.model.get_calendar_data(oparams).then(function(results){
|
||||
var reservs = [];
|
||||
for (var r of results['reservations']) {
|
||||
@@ -238,12 +239,14 @@ var PMSCalendarController = AbstractController.extend({
|
||||
}
|
||||
|
||||
self._multi_calendar._reserv_tooltips = _.extend(this._multi_calendar._reserv_tooltips, results['tooltips']);
|
||||
self._multi_calendar.merge_days_tooltips(results['events']);
|
||||
self._multi_calendar.merge_pricelist(results['pricelist'], active_calendar);
|
||||
self._multi_calendar.merge_restrictions(results['restrictions'], active_calendar);
|
||||
self._multi_calendar.merge_reservations(reservs, active_calendar);
|
||||
_.defer(function(){
|
||||
self._multi_calendar.merge_days_tooltips(results['events']);
|
||||
self._multi_calendar.merge_pricelist(results['pricelist'], active_calendar);
|
||||
self._multi_calendar.merge_restrictions(results['restrictions'], active_calendar);
|
||||
self._multi_calendar.merge_reservations(reservs, active_calendar);
|
||||
|
||||
self._multi_calendar._assign_extra_info(active_calendar);
|
||||
self._multi_calendar._assign_extra_info(active_calendar);
|
||||
});
|
||||
}.bind(this)).then(function(){
|
||||
self._last_dates = filterDates;
|
||||
});
|
||||
@@ -255,10 +258,10 @@ var PMSCalendarController = AbstractController.extend({
|
||||
var $dateEndDays = this.renderer.$el.find('#pms-menu #date_end_days');
|
||||
$dateTimePickerBegin.on("dp.change", function (e) {
|
||||
$dateTimePickerBegin.data("DateTimePicker").hide();
|
||||
self._on_change_filter_date(true);
|
||||
self._on_change_filter_date();
|
||||
});
|
||||
$dateEndDays.on("change", function (e) {
|
||||
self._on_change_filter_date(false);
|
||||
self._on_change_filter_date();
|
||||
});
|
||||
|
||||
this.renderer.$el.find("#btn_swap button").on('click', function(ev){
|
||||
@@ -278,6 +281,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
} else {
|
||||
active_calendar.setSwapMode(HotelCalendar.MODE.NONE);
|
||||
$("#btn_swap span.ntext").html(_t("Start Swap"));
|
||||
$led.removeClass('led-green');
|
||||
$led.removeClass('led-blue');
|
||||
$led.addClass('led-disabled');
|
||||
}
|
||||
@@ -286,7 +290,6 @@ var PMSCalendarController = AbstractController.extend({
|
||||
this.renderer.$el.find('#pms-menu #btn_action_overbooking button').on('click', function(ev){
|
||||
var active_calendar = self._multi_calendar.get_active_calendar();
|
||||
active_calendar.toggleOverbookingsVisibility();
|
||||
active_calendar.addReservations(self._multi_calendar._dataset['reservations']);
|
||||
if (active_calendar.options.showOverbookings) {
|
||||
$(this).find('.led').removeClass('led-disabled');
|
||||
$(this).find('.led').addClass('led-enabled');
|
||||
@@ -294,12 +297,12 @@ var PMSCalendarController = AbstractController.extend({
|
||||
$(this).find('.led').addClass('led-disabled');
|
||||
$(this).find('.led').removeClass('led-enabled');
|
||||
}
|
||||
active_calendar.addReservations(_.reject(self._multi_calendar._dataset['reservations'], {overbooking:false}));
|
||||
});
|
||||
|
||||
this.renderer.$el.find('#pms-menu #btn_action_cancelled button').on('click', function(ev){
|
||||
var active_calendar = self._multi_calendar.get_active_calendar();
|
||||
active_calendar.toggleCancelledVisibility();
|
||||
active_calendar.addReservations(self._multi_calendar._dataset['reservations']);
|
||||
if (active_calendar.options.showCancelled) {
|
||||
$(this).find('.led').removeClass('led-disabled');
|
||||
$(this).find('.led').addClass('led-enabled');
|
||||
@@ -307,6 +310,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
$(this).find('.led').addClass('led-disabled');
|
||||
$(this).find('.led').removeClass('led-enabled');
|
||||
}
|
||||
active_calendar.addReservations(_.reject(self._multi_calendar._dataset['reservations'], {cancelled:false}));
|
||||
});
|
||||
|
||||
this.renderer.$el.find('#pms-menu #btn_action_divide button').on('click', function(ev){
|
||||
@@ -362,6 +366,12 @@ var PMSCalendarController = AbstractController.extend({
|
||||
});
|
||||
});
|
||||
|
||||
this.renderer.$el.find('#pms-menu .menu-filter-box #filters').on('show.bs.collapse', function(ev){
|
||||
self.renderer.$el.find('#pms-menu .menu-filter-box h4 i.fa').css({transform: 'rotate(90deg)'});
|
||||
}).on('hide.bs.collapse', function(ev){
|
||||
self.renderer.$el.find('#pms-menu .menu-filter-box h4 i.fa').css({transform: 'rotate(0deg)'});
|
||||
});
|
||||
|
||||
this._multi_calendar.on('tab_changed', function(ev, active_index){
|
||||
if (active_index) {
|
||||
self._refresh_view_options(active_index);
|
||||
@@ -374,26 +384,43 @@ var PMSCalendarController = AbstractController.extend({
|
||||
this._multi_calendar.on_calendar('hcalOnSavePricelist', function(ev){
|
||||
self.savePricelist(ev.detail.calendar_obj, ev.detail.pricelist_id, ev.detail.pricelist);
|
||||
});
|
||||
|
||||
$('.hcal-reservation noselect').popover();
|
||||
var _destroy_and_clear_popover_mark = function(ev){
|
||||
$(".marked-as-having-a-popover").popover('destroy');
|
||||
$('.hcal-reservation').removeClass("marked-as-having-a-popover");
|
||||
};
|
||||
|
||||
this._multi_calendar.on_calendar('hcalOnClickReservation', function(ev){
|
||||
var active_calendar = self._multi_calendar.get_active_calendar();
|
||||
if ( active_calendar.getSelectionMode() !== HotelCalendar.MODE.NONE
|
||||
|| active_calendar.getSwapMode() !== HotelCalendar.MODE.NONE )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (ev.detail.reservationObj) {
|
||||
var tp = self._multi_calendar._reserv_tooltips[ev.detail.reservationObj.id];
|
||||
var qdict = self._generate_reservation_tooltip_dict(tp);
|
||||
$(".marked-as-having-a-popover").popover('destroy');
|
||||
$(ev.detail.reservationDiv).addClass('marked-as-having-a-popover');
|
||||
var $reservationPopover = $(ev.detail.reservationDiv).popover({
|
||||
trigger: 'click focus',
|
||||
trigger: 'manual',
|
||||
container: 'body',
|
||||
animation: false,
|
||||
html: true,
|
||||
placement: 'bottom',
|
||||
/* title: "Come'n popovers!", */
|
||||
content: QWeb.render('HotelCalendar.TooltipReservation', qdict)
|
||||
}).popover('show');
|
||||
/* destroy popover if mouse click is done out the popover */
|
||||
$(document).click(function(e){
|
||||
if( $(e.target).closest(".popover-content").length == 0 && $(e.target).hasClass("marked-as-having-a-popover") == false ) {
|
||||
_destroy_and_clear_popover_mark();
|
||||
}
|
||||
});
|
||||
/* add actions */
|
||||
$reservationPopover.data('bs.popover').tip().find(".btn_popover_open_folio").on('click',
|
||||
{folio_id: ev.detail.reservationObj._userData.folio_id}, function(ev){
|
||||
$(".marked-as-having-a-popover").popover('destroy');
|
||||
_destroy_and_clear_popover_mark();
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hotel.folio',
|
||||
@@ -403,7 +430,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
});
|
||||
$reservationPopover.data('bs.popover').tip().find(".btn_popover_open_reservation").on('click',
|
||||
{reservation_id: ev.detail.reservationObj.id}, function(ev){
|
||||
$(".marked-as-having-a-popover").popover('destroy');
|
||||
_destroy_and_clear_popover_mark();
|
||||
self.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'hotel.reservation',
|
||||
@@ -412,7 +439,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
});
|
||||
});
|
||||
$reservationPopover.data('bs.popover').tip().find(".btn_popover_close").on('click', function(ev){
|
||||
$(".marked-as-having-a-popover").popover('destroy');
|
||||
_destroy_and_clear_popover_mark();
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -725,7 +752,6 @@ var PMSCalendarController = AbstractController.extend({
|
||||
'checkout_day_of_week': HotelCalendar.toMomentUTC(tp['checkout'], '').format("dddd"),
|
||||
'arrival_hour': tp['arrival_hour'],
|
||||
'departure_hour': tp['departure_hour'],
|
||||
'num_split': tp['num_split'],
|
||||
'amount_total': Number(tp['amount_total']).toLocaleString(),
|
||||
'pending_amount': Number(tp['pending_amount']).toLocaleString(),
|
||||
'amount_paid': Number(tp['amount_paid']).toLocaleString(),
|
||||
@@ -848,19 +874,18 @@ var PMSCalendarController = AbstractController.extend({
|
||||
this._multi_calendar.get_active_calendar().setDomain(HotelCalendar.DOMAIN.ROOMS, domain);
|
||||
},
|
||||
|
||||
_on_change_filter_date: function(isStartDate) {
|
||||
isStartDate = isStartDate || false;
|
||||
_on_change_filter_date: function() {
|
||||
var $dateTimePickerBegin = this.renderer.$el.find('#pms-menu #date_begin');
|
||||
var $dateEndDays = this.renderer.$el.find('#pms-menu #date_end_days');
|
||||
|
||||
// FIXME: Hackish onchange ignore (Used when change dates from code)
|
||||
if ($dateTimePickerBegin.data("ignore_onchange") || $dateEndDays.data("ignore_onchange")) {
|
||||
$dateTimePickerBegin.data("ignore_onchange", false);
|
||||
$dateEndDays.data("ignore_onchange", false)
|
||||
$dateEndDays.data("ignore_onchange", false);
|
||||
return true;
|
||||
}
|
||||
|
||||
var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone().utc();
|
||||
var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone();
|
||||
|
||||
var active_calendar = this._multi_calendar.get_active_calendar();
|
||||
if (active_calendar && date_begin) {
|
||||
@@ -869,7 +894,7 @@ var PMSCalendarController = AbstractController.extend({
|
||||
days = date_begin.daysInMonth();
|
||||
}
|
||||
var date_end = date_begin.clone().add(days, 'd');
|
||||
if (!date_begin.isSame(this._last_dates[0].clone().utc(), 'd') || !date_end.isSame(this._last_dates[1].clone().utc(), 'd')) {
|
||||
if (!date_begin.isSame(this._last_dates[0].clone(), 'd') || !date_end.isSame(this._last_dates[1].clone(), 'd')) {
|
||||
active_calendar.setStartDate(date_begin, $dateEndDays.val(), false, function(){
|
||||
this._reload_active_calendar();
|
||||
}.bind(this));
|
||||
|
||||
@@ -20,15 +20,6 @@ return AbstractModel.extend({
|
||||
this.modelManagementName = 'hotel.calendar.management'
|
||||
},
|
||||
|
||||
save_pricelist: function(params) {
|
||||
return this._rpc({
|
||||
model: this.modelManagementName,
|
||||
method: 'save_changes',
|
||||
args: params,
|
||||
context: Session.user_context,
|
||||
});
|
||||
},
|
||||
|
||||
swap_reservations: function(fromIds, toIds) {
|
||||
return this._rpc({
|
||||
model: this.modelName,
|
||||
@@ -39,11 +30,33 @@ return AbstractModel.extend({
|
||||
},
|
||||
|
||||
get_calendar_data: function(oparams) {
|
||||
var dialog = bootbox.dialog({
|
||||
message: '<div class="text-center"><i class="fa fa-spin fa-spinner"></i> Getting Calendar Data From Server...</div>',
|
||||
onEscape: false,
|
||||
closeButton: false,
|
||||
size: 'small',
|
||||
backdrop: false,
|
||||
});
|
||||
return this._rpc({
|
||||
model: this.modelName,
|
||||
method: 'get_hcalendar_all_data',
|
||||
args: oparams,
|
||||
context: Session.user_context,
|
||||
}, {
|
||||
xhr: function () {
|
||||
var xhr = new window.XMLHttpRequest();
|
||||
//Download progress
|
||||
xhr.addEventListener("readystatechange", function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
console.log(`[HotelCalendar] Downloaded ${(parseInt(xhr.getResponseHeader("Content-Length"), 10)/1024).toFixed(3)}KiB of data`);
|
||||
}
|
||||
}, false);
|
||||
return xhr;
|
||||
},
|
||||
success: function() {
|
||||
dialog.modal('hide');
|
||||
},
|
||||
shadow: true,
|
||||
});
|
||||
},
|
||||
|
||||
@@ -151,13 +164,15 @@ return AbstractModel.extend({
|
||||
},
|
||||
|
||||
save_changes: function(params) {
|
||||
params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer
|
||||
//params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer
|
||||
//console.log(params);
|
||||
return this._rpc({
|
||||
model: 'hotel.calendar.management',
|
||||
method: 'save_changes',
|
||||
args: params,
|
||||
context: Session.user_context,
|
||||
//context: Session.user_context,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -48,12 +48,12 @@ var HotelCalendarView = AbstractRenderer.extend({
|
||||
get_view_filter_dates: function () {
|
||||
var $dateTimePickerBegin = this.$el.find('#pms-menu #date_begin');
|
||||
var $dateEndDays = this.$el.find('#pms-menu #date_end_days');
|
||||
var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().set({'hour': 0, 'minute': 0, 'second': 0}).clone().utc();
|
||||
var date_begin = $dateTimePickerBegin.data("DateTimePicker").date().clone();
|
||||
var days = $dateEndDays.val();
|
||||
if (days === 'month') {
|
||||
days = date_begin.daysInMonth();
|
||||
}
|
||||
var date_end = date_begin.clone().add(days, 'd').set({'hour': 23, 'minute': 59, 'second': 59}).clone().utc();
|
||||
var date_end = date_begin.clone().add(days, 'd');
|
||||
return [date_begin, date_end];
|
||||
},
|
||||
|
||||
@@ -112,6 +112,10 @@ var HotelCalendarView = AbstractRenderer.extend({
|
||||
//language : moment.locale(),
|
||||
locale : moment.locale(),
|
||||
format : HotelConstants.L10N_DATE_MOMENT_FORMAT,
|
||||
widgetPositioning:{
|
||||
horizontal: 'auto',
|
||||
vertical: 'bottom'
|
||||
}
|
||||
};
|
||||
var $dateTimePickerBegin = this.$el.find('#pms-menu #date_begin');
|
||||
var $dateEndDays = this.$el.find('#pms-menu #date_end_days');
|
||||
|
||||
@@ -21,12 +21,12 @@ return AbstractModel.extend({
|
||||
},
|
||||
|
||||
save_changes: function (params) {
|
||||
params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer
|
||||
//params.splice(0, 0, false); // FIXME: ID=False because first parameter its an integer
|
||||
return this._rpc({
|
||||
model: this.modelName,
|
||||
method: 'save_changes',
|
||||
args: params,
|
||||
context: Session.user_context,
|
||||
//context: Session.user_context,
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -97,12 +97,8 @@ odoo.define('hotel_calendar.MultiCalendar', function(require) {
|
||||
var active_calendar = this.get_active_calendar();
|
||||
if (active_calendar) {
|
||||
setTimeout(function(calendar){
|
||||
for (var reserv of 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);
|
||||
}
|
||||
}
|
||||
calendar._updateOffsets();
|
||||
calendar._updateReservations(false);
|
||||
}.bind(this, active_calendar), 200);
|
||||
}
|
||||
},
|
||||
@@ -333,28 +329,27 @@ odoo.define('hotel_calendar.MultiCalendar', function(require) {
|
||||
|
||||
_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;
|
||||
$(calendar.etable).find('.hcal-cell-room-type-group-item.btn-hcal-left').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');
|
||||
} 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');
|
||||
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');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -41,25 +41,15 @@
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#pms-search {
|
||||
position: fixed;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
#pms-menu {
|
||||
background-color: white;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#hcal_widget {
|
||||
max-height: 100%;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#cal-pag-prev-plus, #cal-pag-prev, #cal-pag-selector, #cal-pag-next, #cal-pag-next-plus {
|
||||
min-height: 0px;
|
||||
}
|
||||
|
||||
.hcalendar-container {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
}
|
||||
|
||||
.table-reservations-header {
|
||||
order: 1;
|
||||
flex-grow: 1;
|
||||
@@ -85,6 +75,7 @@
|
||||
flex-grow: 2;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
font-size: 12px;
|
||||
max-height: 20vh;
|
||||
}
|
||||
.table-calcs input, .table-reservations-header input {
|
||||
@@ -96,17 +87,13 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hcal-event-day {
|
||||
background-color: #6ebcff !important;
|
||||
}
|
||||
|
||||
.btn-hcal { }
|
||||
.btn-hcal.hcal-cell-current-day {
|
||||
background-color: #7c7bad66;
|
||||
color: #654a37;
|
||||
}
|
||||
.btn-hcal.hcal-cell-end-week {
|
||||
background-color: #CBE5F8;
|
||||
background-color: #EDEDED83;
|
||||
}
|
||||
.btn-hcal-3d {
|
||||
border: 1px solid #eaeaea;
|
||||
@@ -175,7 +162,7 @@
|
||||
height: 100%;
|
||||
border-collapse: collapse !important;
|
||||
border: 0 dotted #eaeaea;
|
||||
border-width: 2px 0;
|
||||
border-width: 1px 0;
|
||||
}
|
||||
/*.hcal-table-day tr:first-child td{
|
||||
border: 1px solid #727272 !important;
|
||||
@@ -191,21 +178,21 @@
|
||||
}*/
|
||||
.hcal-table-day td {
|
||||
padding: 2px;
|
||||
height: 2.3em;
|
||||
height: 3em;
|
||||
font-size: 7px;
|
||||
vertical-align: middle;
|
||||
font-weight: bold;
|
||||
border: 1px solid #eaeaea !important;
|
||||
border: 0.5px solid #eaeaea !important;
|
||||
}
|
||||
.hcal-table-day td:hover:not(.hcal-cell-highlight):not(.hcal-cell-invalid) {
|
||||
background-color: #FCFEE1 !important;
|
||||
}
|
||||
|
||||
.hcal-cell-current-day {
|
||||
background-color: #FAFFD9;
|
||||
background-color: #7C7BADA5;
|
||||
}
|
||||
.hcal-cell-end-week {
|
||||
background-color: #ECF3FF;
|
||||
background-color: #EDEDED83;
|
||||
}
|
||||
|
||||
.hcal-cell-day-selector {
|
||||
@@ -233,6 +220,7 @@
|
||||
text-align: center !important;
|
||||
vertical-align: middle;
|
||||
white-space: nowrap;
|
||||
font-size: 12px;
|
||||
}
|
||||
.hcal-cell-month:nth-child(n+3) {
|
||||
border-left-width: 2px !important;
|
||||
@@ -303,7 +291,7 @@
|
||||
color: white;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
z-index:8;
|
||||
z-index: 8;
|
||||
}
|
||||
.hcal-reservation:hover {
|
||||
background-color: #4e97bf;
|
||||
@@ -407,21 +395,25 @@
|
||||
}
|
||||
|
||||
.hcal-reservation-divide-l {
|
||||
background-color: transparent !important;
|
||||
background-color: transparent;
|
||||
border: 2px dashed black;
|
||||
cursor: copy;
|
||||
pointer-events: none;
|
||||
border-color: black !important;
|
||||
border-right-style: solid !important;
|
||||
border-color: black;
|
||||
border-right-style: solid;
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.hcal-reservation-divide-r {
|
||||
background-color: transparent !important;
|
||||
background-color: transparent;
|
||||
border: 2px dashed black;
|
||||
cursor: copy;
|
||||
pointer-events: none;
|
||||
border-color: black !important;
|
||||
border-left-style: solid !important;
|
||||
border-color: black;
|
||||
border-left-style: solid;
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.hcal-row-room-type-group-item {
|
||||
@@ -474,6 +466,7 @@ td.hcal-cell-room-type-group-item {
|
||||
vertical-align: middle;
|
||||
font-size: x-small;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
td.hcal-cell-room-type-group-item:last-child {
|
||||
border-right-width: 2px;
|
||||
@@ -491,6 +484,7 @@ td.hcal-cell-room-type-group-item:last-child {
|
||||
td.hcal-cell-header-day {
|
||||
padding: 0;
|
||||
vertical-align: middle;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
td.hcal-cell-month-day-occupied {
|
||||
@@ -522,8 +516,7 @@ td.hcal-cell-month-day-occupied {
|
||||
}
|
||||
|
||||
.hcal-unused-zone {
|
||||
border: 1px solid #444;
|
||||
border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.input-price {
|
||||
|
||||
@@ -88,6 +88,31 @@ function HotelCalendar(/*String*/querySelector, /*Dictionary*/options, /*List*/p
|
||||
this._lazyModeReservationsSelection = false; // Store Info About Timer for Selection Action
|
||||
this._domains = {}; // Store domains for filter rooms & reservations
|
||||
this._divideDivs = false;
|
||||
this._extraRowIndicators = ['EX-', '/#'];
|
||||
|
||||
// Support
|
||||
var self = this;
|
||||
this._supportsPassive = false;
|
||||
try {
|
||||
var opts = Object.defineProperty({}, 'passive', {
|
||||
get: function() {
|
||||
self._supportsPassive = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener("testPassive", null, opts);
|
||||
window.removeEventListener("testPassive", null, opts);
|
||||
} catch (e) {}
|
||||
|
||||
// Calculate Capacities
|
||||
this._roomCapacityTotal = 0;
|
||||
this._roomCapacities = {};
|
||||
this._roomsMap = _.groupBy(this.options.rooms, 'type');
|
||||
var room_types = this.getRoomTypes();
|
||||
for (var rt of room_types) {
|
||||
this._roomsMap[rt] = _.filter(this._roomsMap[rt], {overbooking: false, cancelled: false});
|
||||
this._roomCapacities[rt] = _.reduce(this._roomsMap[rt], function(memo, tr){ return memo + (tr.shared?tr.capacity:1); }, 0);
|
||||
this._roomCapacityTotal += this._roomCapacities[rt];
|
||||
}
|
||||
|
||||
/***/
|
||||
this._reset_action_reservation();
|
||||
@@ -222,6 +247,12 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_updateOffsets: function() {
|
||||
this._etableOffset = this.loopedOffsetOptimized(this.etable);
|
||||
this._eOffset = this.loopedOffsetOptimized(this.e);
|
||||
this._edivrOffset = this.loopedOffsetOptimized(this.edivr);
|
||||
},
|
||||
|
||||
//==== DOMAINS
|
||||
setDomain: function(/*Int*/section, /*Array*/domain) {
|
||||
if (this._domains[section] !== domain) {
|
||||
@@ -276,6 +307,7 @@ HotelCalendar.prototype = {
|
||||
if (reservations.length > 0 && !(reservations[0] instanceof HReservation)) {
|
||||
console.warn("[HotelCalendar][addReservations] Invalid Reservation definition!");
|
||||
} else {
|
||||
var isCalendarEmpty = (this._reservations.length>0);
|
||||
// Merge
|
||||
var addedReservations = [];
|
||||
for (var r of reservations) {
|
||||
@@ -287,6 +319,7 @@ HotelCalendar.prototype = {
|
||||
continue;
|
||||
}
|
||||
|
||||
var hasCreatedExtraRows = false;
|
||||
r = r.clone(); // HOT-FIX: Multi-Calendar Support
|
||||
r.room = this.getRoom(r.room_id, r.overbooking || r.cancelled, r.id);
|
||||
// need create a overbooking row?
|
||||
@@ -297,6 +330,7 @@ HotelCalendar.prototype = {
|
||||
cancelled: r.cancelled,
|
||||
});
|
||||
this.createExtraRoomRow(r.room);
|
||||
hasCreatedExtraRows = true;
|
||||
} else {
|
||||
console.warn(`Can't found the room '${r.room_id}' for the reservation '${r.id}' (${r.title})!`);
|
||||
continue;
|
||||
@@ -324,6 +358,9 @@ HotelCalendar.prototype = {
|
||||
|
||||
// Create & Render New Reservations
|
||||
_.defer(function(reservs){
|
||||
// Update offsets (New Rooms change positions?)
|
||||
this._updateOffsets();
|
||||
|
||||
var unusedZones = this._createUnusedZones(reservs);
|
||||
// Add Unused Zones
|
||||
this._reservations = this._reservations.concat(unusedZones);
|
||||
@@ -396,14 +433,14 @@ HotelCalendar.prototype = {
|
||||
return day.isBetween(item.startDate, item.endDate, 'day', inclusivity) &&
|
||||
(typeof nbed === 'undefined' || item._beds.includes(nbed)) &&
|
||||
((includeUnusedZones && item.unusedZone) || !item.unusedZone) &&
|
||||
item !== ignoreThis;
|
||||
item !== ignoreThis && !item.overbooking && !item.cancelled;
|
||||
});
|
||||
} else {
|
||||
return _.filter(this._reservations, function(item){
|
||||
return day.isBetween(item.startDate, item.endDate, 'day', inclusivity) &&
|
||||
(typeof nbed === 'undefined' || item._beds.includes(nbed)) &&
|
||||
((includeUnusedZones && item.unusedZone) || !item.unusedZone) &&
|
||||
item !== ignoreThis;
|
||||
item !== ignoreThis && !item.overbooking && !item.cancelled;
|
||||
});
|
||||
}
|
||||
},
|
||||
@@ -496,21 +533,25 @@ HotelCalendar.prototype = {
|
||||
var numBeds = +limits.right.dataset.hcalBedNum - +limits.left.dataset.hcalBedNum;
|
||||
var ndate = reservation.startDate.clone().local();
|
||||
for (var i=0; i<diff_date; ++i) {
|
||||
for (var b=0; b<=numBeds; ++b) {
|
||||
var reservs = this.getReservationsByDay(ndate, true, true, reservation.room.id, +limits.left.dataset.hcalBedNum+b, reservation);
|
||||
if (reservs.length) {
|
||||
notFound = true;
|
||||
nbed = nbed?nbed+1:+limits.left.dataset.hcalBedNum+b+1;
|
||||
break;
|
||||
}
|
||||
var reservs = this.getReservationsByDay(ndate, true, true, reservation.room.id, +limits.left.dataset.hcalBedNum, reservation);
|
||||
if (reservs.length) {
|
||||
notFound = true;
|
||||
nbed = nbed?nbed+1:+limits.left.dataset.hcalBedNum+1;
|
||||
break;
|
||||
}
|
||||
if (notFound) { break; }
|
||||
ndate.add(1, 'd');
|
||||
}
|
||||
}
|
||||
} while (notFound && nbed <= reservation.room.capacity);
|
||||
} while (notFound && nbed < reservation.room.capacity);
|
||||
|
||||
reservation._limits = limits;
|
||||
|
||||
// Update Beds
|
||||
if (limits.isValid()) {
|
||||
var numBeds = (+limits.right.dataset.hcalBedNum)-(+limits.left.dataset.hcalBedNum);
|
||||
reservation._beds = [];
|
||||
for (var i=0; i<=numBeds; reservation._beds.push(+limits.left.dataset.hcalBedNum+i++));
|
||||
}
|
||||
},
|
||||
|
||||
//==== CELLS
|
||||
@@ -576,34 +617,11 @@ HotelCalendar.prototype = {
|
||||
},
|
||||
|
||||
getDayRoomTypeReservations: function(/*String,MomentObject*/day, /*String*/room_type) {
|
||||
var reservs = this.getReservationsByDay(day, true);
|
||||
return _.filter(reservs, function(item){ return item.room && item.room.type === room_type && !item.unusedZone; });
|
||||
},
|
||||
|
||||
getRoomsByType: function(/*String*/type) {
|
||||
return _.filter(this.options.rooms, function(item){ return item.type === type && !item.overbooking && !item.cancelled; });
|
||||
},
|
||||
|
||||
getRoomsCapacityByType: function(/*String*/type) {
|
||||
var trooms = this.getRoomsByType(type);
|
||||
var num_rooms = 0;
|
||||
for (var tr of trooms) {
|
||||
num_rooms += tr.shared?tr.capacity:1;
|
||||
}
|
||||
return num_rooms;
|
||||
},
|
||||
|
||||
getRoomsCapacityTotal: function() {
|
||||
var num_rooms = 0;
|
||||
var rooms = _.filter(this.options.rooms, function(item){ return !item.overbooking && !item.cancelled; });
|
||||
for (var tr of rooms) {
|
||||
num_rooms += tr.shared?tr.capacity:1;
|
||||
}
|
||||
return num_rooms;
|
||||
return _.filter(this.getReservationsByDay(day, true), function(item){ return item.room && item.room.type === room_type && !item.unusedZone; });
|
||||
},
|
||||
|
||||
getRoomTypes: function() {
|
||||
return _.uniq(_.pluck(this.options.rooms, 'type'));
|
||||
return _.keys(this._roomsMap);
|
||||
},
|
||||
|
||||
getRoom: function(/*String,Int*/id, /*Boolean?*/isExtra, /*Int?*/reservId) {
|
||||
@@ -656,32 +674,26 @@ HotelCalendar.prototype = {
|
||||
var exRoomRow = this.getExtraRoomRow(ex_reserv);
|
||||
if (exRoomRow) {
|
||||
// Update Reservations Position
|
||||
var bounds = exRoomRow.getBoundingClientRect();
|
||||
var cheight = bounds.bottom-bounds.top;
|
||||
var bounds = this.loopedOffsetOptimized(exRoomRow);
|
||||
var start_index = _.indexOf(this.options.rooms, ex_reserv.room) + 1;
|
||||
for (var i=start_index; i<this.options.rooms.length; i++) {
|
||||
var reservs = this.getReservationsByRoom(this.options.rooms[i], true);
|
||||
for (var reserv of reservs) {
|
||||
if (reserv && reserv._html) {
|
||||
var top = parseInt(reserv._html.style.top, 10);
|
||||
reserv._html.style.top = `${top - cheight}px`;
|
||||
reserv._html.style.top = `${top - bounds.height}px`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exRoomRow.parentNode.removeChild(exRoomRow);
|
||||
this.options.rooms = _.reject(this.options.rooms, function(item){ return item.id === ex_reserv.room.id; });
|
||||
this.options.rooms = _.reject(this.options.rooms, {id: ex_reserv.room.id});
|
||||
}
|
||||
},
|
||||
|
||||
getRealExtraRoomInfo: function(/*HRoomObject*/room) {
|
||||
// Obtain real id
|
||||
var isf = room.number.search('EX-');
|
||||
var isfb = room.number.search('/#');
|
||||
var cnumber = room.number;
|
||||
if (isf != -1 && isfb != -1) { cnumber = cnumber.substr(isf+3, isfb-(isf+3)); }
|
||||
|
||||
// Obtain the original room row
|
||||
var cnumber = this.getExtraRoomRealNumber(room);
|
||||
var mainRoomRowId = this._sanitizeId(`ROW_${cnumber}_${room.type}`);
|
||||
var mainRoomRow = this.e.querySelector('#'+mainRoomRowId);
|
||||
if (!mainRoomRow) {
|
||||
@@ -691,12 +703,16 @@ HotelCalendar.prototype = {
|
||||
return [this.getRoom(mainRoomRow.dataset.hcalRoomObjId), mainRoomRow];
|
||||
},
|
||||
|
||||
getExtraRoomRow: function(/*HReservationObject*/ex_reserv) {
|
||||
// Obtain real id
|
||||
var isf = ex_reserv.room.number.search('EX-');
|
||||
var isfb = ex_reserv.room.number.search('/#');
|
||||
var cnumber = ex_reserv.room.number;
|
||||
getExtraRoomRealNumber: function(/*HRoomObject*/room) {
|
||||
var isf = room.number.search(this._extraRowIndicators[0]);
|
||||
var isfb = room.number.search(this._extraRowIndicators[1]);
|
||||
var cnumber = room.number;
|
||||
if (isf != -1 && isfb != -1) { cnumber = cnumber.substr(isf+3, isfb-(isf+3)); }
|
||||
return cnumber;
|
||||
},
|
||||
|
||||
getExtraRoomRow: function(/*HReservationObject*/ex_reserv) {
|
||||
var cnumber = this.getExtraRoomRealNumber(ex_reserv.room);
|
||||
return this.e.querySelector(`#${this._sanitizeId(`ROW_${cnumber}_${ex_reserv.room.type}_EXTRA${ex_reserv.id}`)}`);
|
||||
},
|
||||
|
||||
@@ -711,7 +727,7 @@ HotelCalendar.prototype = {
|
||||
var exr = this.getExtraRooms(mainRoom.id);
|
||||
var ex_room = mainRoom.clone();
|
||||
ex_room.id = `${reservId}@${mainRoom.id}`;
|
||||
ex_room.number = `EX-${mainRoom.number}/#${exr.length}`;
|
||||
ex_room.number = `${this._extraRowIndicators[0]}${mainRoom.number}${this._extraRowIndicators[1]}${exr.length}`;
|
||||
for (var key in extraData) {
|
||||
ex_room[key] = extraData[key];
|
||||
}
|
||||
@@ -777,8 +793,8 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
|
||||
// Update Reservations Position
|
||||
var bounds = row.getBoundingClientRect();
|
||||
var cheight = bounds.bottom-bounds.top;
|
||||
var bounds = this.loopedOffsetOptimized(row);
|
||||
var cheight = bounds.height;
|
||||
var start_index = _.indexOf(this.options.rooms, ex_room) + 1;
|
||||
for (var i=start_index; i<this.options.rooms.length; ++i) {
|
||||
var reservs = this.getReservationsByRoom(this.options.rooms[i], true);
|
||||
@@ -810,39 +826,37 @@ HotelCalendar.prototype = {
|
||||
if (this._restrictions) {
|
||||
// Rooms Restrictions
|
||||
for (var room of this.options.rooms) {
|
||||
if (room.price[0] == 'pricelist') {
|
||||
var date = this.options.startDate.clone().startOf('day');
|
||||
for (var i=0; i<=this.options.days; ++i) {
|
||||
var dd = date.add(1, 'd');
|
||||
var date_str = dd.format(HotelCalendar.DATE_FORMAT_SHORT_);
|
||||
if (date_str in this._restrictions[room.price[1]]) {
|
||||
var restr = this._restrictions[room.price[1]][date_str];
|
||||
if (restr) {
|
||||
var cell = this.getMainCell(dd, room.type, room.number);
|
||||
if (cell) {
|
||||
if (restr[0] || restr[1] || restr[2] || restr[3] || restr[4] || restr[5] || restr[6]) {
|
||||
cell.classList.add('hcal-restriction-room-day');
|
||||
var humantext = "Restrictions:\n";
|
||||
if (restr[0] > 0)
|
||||
humantext += `Min. Stay: ${restr[0]}\n`;
|
||||
if (restr[1] > 0)
|
||||
humantext += `Min. Stay Arrival: ${restr[1]}\n`;
|
||||
if (restr[2] > 0)
|
||||
humantext += `Max. Stay: ${restr[2]}\n`;
|
||||
if (restr[3] > 0)
|
||||
humantext += `Max. Stay Arrival: ${restr[3]}\n`;
|
||||
if (restr[4])
|
||||
humantext += `Closed: ${restr[4]}\n`;
|
||||
if (restr[5])
|
||||
humantext += `Closed Arrival: ${restr[5]}\n`;
|
||||
if (restr[6])
|
||||
humantext += `Closed Departure: ${restr[6]}`;
|
||||
cell.title = humantext;
|
||||
}
|
||||
else {
|
||||
cell.classList.remove('hcal-restriction-room-day');
|
||||
cell.title = '';
|
||||
}
|
||||
var date = this.options.startDate.clone().startOf('day');
|
||||
for (var i=0; i<=this.options.days; ++i) {
|
||||
var dd = date.add(1, 'd');
|
||||
var date_str = dd.format(HotelCalendar.DATE_FORMAT_SHORT_);
|
||||
if (date_str in this._restrictions[room.price[1]]) {
|
||||
var restr = this._restrictions[room.price[1]][date_str];
|
||||
if (restr) {
|
||||
var cell = this.getMainCell(dd, room.type, room.number);
|
||||
if (cell) {
|
||||
if (restr[0] || restr[1] || restr[2] || restr[3] || restr[4] || restr[5] || restr[6]) {
|
||||
cell.classList.add('hcal-restriction-room-day');
|
||||
var humantext = "Restrictions:\n";
|
||||
if (restr[0] > 0)
|
||||
humantext += `Min. Stay: ${restr[0]}\n`;
|
||||
if (restr[1] > 0)
|
||||
humantext += `Min. Stay Arrival: ${restr[1]}\n`;
|
||||
if (restr[2] > 0)
|
||||
humantext += `Max. Stay: ${restr[2]}\n`;
|
||||
if (restr[3] > 0)
|
||||
humantext += `Max. Stay Arrival: ${restr[3]}\n`;
|
||||
if (restr[4])
|
||||
humantext += `Closed: ${restr[4]}\n`;
|
||||
if (restr[5])
|
||||
humantext += `Closed Arrival: ${restr[5]}\n`;
|
||||
if (restr[6])
|
||||
humantext += `Closed Departure: ${restr[6]}`;
|
||||
cell.title = humantext;
|
||||
}
|
||||
else {
|
||||
cell.classList.remove('hcal-restriction-room-day');
|
||||
cell.title = '';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -855,51 +869,22 @@ HotelCalendar.prototype = {
|
||||
//==== DETAIL CALCS
|
||||
calcDayRoomTypeReservations: function(/*String,MomentObject*/day, /*String*/room_type) {
|
||||
var day = HotelCalendar.toMoment(day);
|
||||
if (!day) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var reservs = this.getDayRoomTypeReservations(day, room_type);
|
||||
var num_rooms = this.getRoomsCapacityByType(room_type);
|
||||
for (var r of reservs) {
|
||||
if (r.unusedZone || r.overbooking || r.cancelled) {
|
||||
continue;
|
||||
}
|
||||
num_rooms -= (r.room && r.room.shared)?r.getTotalPersons(false):1;
|
||||
}
|
||||
if (!day) { return false; }
|
||||
|
||||
var num_rooms = this._roomCapacities[room_type];
|
||||
num_rooms -= _.reduce(this.getDayRoomTypeReservations(day, room_type), function(memo, r){ return memo + ((r.room && r.room.shared)?r.getTotalPersons(false):1); }, 0);
|
||||
return num_rooms;
|
||||
},
|
||||
|
||||
calcDayRoomTotalReservations: function(/*String,MomentObject*/day) {
|
||||
var day = HotelCalendar.toMoment(day);
|
||||
if (!day) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var reservs = this.getReservationsByDay(day, true);
|
||||
var num_rooms = this.getRoomsCapacityTotal();
|
||||
for (var r of reservs) {
|
||||
if (r.unusedZone || r.overbooking || r.cancelled) {
|
||||
continue;
|
||||
}
|
||||
num_rooms -= (r.room && r.room.shared)?r.getTotalPersons(false):1;
|
||||
}
|
||||
if (!day) { return false; }
|
||||
|
||||
var num_rooms = this._roomCapacityTotal;
|
||||
num_rooms -= _.reduce(this.getReservationsByDay(day, true), function(memo, r){ return memo + ((r.room && r.room.shared)?r.getTotalPersons(false):1); }, 0);
|
||||
return num_rooms;
|
||||
},
|
||||
|
||||
calcReservationOccupation: function(/*String,MomentObject*/day, /*String*/room_type) {
|
||||
var day = HotelCalendar.toMoment(day);
|
||||
if (!day) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var reservs = this.getReservationsByDay(day, true);
|
||||
return Math.round(reservs.length/_.filter(this.options.rooms, function(item){ return !item.overbooking && !item.cancelled; }).length*100.0);
|
||||
},
|
||||
|
||||
|
||||
|
||||
/** PRIVATE MEMBERS **/
|
||||
//==== MAIN FUNCTIONS
|
||||
@@ -946,17 +931,21 @@ HotelCalendar.prototype = {
|
||||
|
||||
var scrollThrottle = _.throttle(this._updateOBIndicators.bind(this), 100);
|
||||
|
||||
|
||||
this.edivcontainer = document.createElement("div");
|
||||
this.edivcontainer.classList.add('hcalendar-container');
|
||||
|
||||
// Reservations Table
|
||||
this.edivrh = document.createElement("div");
|
||||
this.edivrh.classList.add('table-reservations-header');
|
||||
this.e.appendChild(this.edivrh);
|
||||
this.edivcontainer.appendChild(this.edivrh);
|
||||
this.etableHeader = document.createElement("table");
|
||||
this.etableHeader.classList.add('hcal-table');
|
||||
this.etableHeader.classList.add('noselect');
|
||||
this.edivrh.appendChild(this.etableHeader);
|
||||
this.edivr = document.createElement("div");
|
||||
this.edivr.classList.add('table-reservations');
|
||||
this.e.appendChild(this.edivr);
|
||||
this.edivcontainer.appendChild(this.edivr);
|
||||
this.etable = document.createElement("table");
|
||||
this.etable.classList.add('hcal-table');
|
||||
this.etable.classList.add('noselect');
|
||||
@@ -965,14 +954,14 @@ HotelCalendar.prototype = {
|
||||
// Detail Calcs Table
|
||||
this.edivch = document.createElement("div");
|
||||
this.edivch.classList.add('table-calcs-header');
|
||||
this.e.appendChild(this.edivch);
|
||||
this.edivcontainer.appendChild(this.edivch);
|
||||
this.edtableHeader = document.createElement("table");
|
||||
this.edtableHeader.classList.add('hcal-table');
|
||||
this.edtableHeader.classList.add('noselect');
|
||||
this.edivch.appendChild(this.edtableHeader);
|
||||
this.edivc = document.createElement("div");
|
||||
this.edivc.classList.add('table-calcs');
|
||||
this.e.appendChild(this.edivc);
|
||||
this.edivcontainer.appendChild(this.edivc);
|
||||
this.edtable = document.createElement("table");
|
||||
this.edtable.classList.add('hcal-table');
|
||||
this.edtable.classList.add('noselect');
|
||||
@@ -983,7 +972,10 @@ HotelCalendar.prototype = {
|
||||
});
|
||||
observer.observe(this.edivr, { childList: true });
|
||||
|
||||
this.e.appendChild(this.edivcontainer);
|
||||
|
||||
this._updateView();
|
||||
//_.defer(function(self){ self._updateView(); }, this);
|
||||
this._tableCreated = true;
|
||||
|
||||
return true;
|
||||
@@ -1151,10 +1143,7 @@ HotelCalendar.prototype = {
|
||||
row.classList.add('hcal-row-room-type-group-item');
|
||||
if ((this.options.showOverbookings && itemRoom.overbooking) || (this.options.showCancelled && itemRoom.cancelled)) {
|
||||
var reservId = this.parseExtraRoomId(itemRoom.id)[0];
|
||||
var cnumber = itemRoom.number;
|
||||
var isf = cnumber.search('EX-');
|
||||
var isfb = cnumber.search('/#');
|
||||
if (isf != -1 && isfb != -1) { cnumber = cnumber.substr(isf+3, isfb-(isf+3)); }
|
||||
var cnumber = this.getExtraRoomRealNumber(itemRoom);
|
||||
row.setAttribute('id', this._sanitizeId(`ROW_${cnumber}_${itemRoom.type}_EXTRA${reservId}`));
|
||||
row.classList.add('hcal-row-room-type-group-overbooking-item');
|
||||
} else {
|
||||
@@ -1207,8 +1196,7 @@ HotelCalendar.prototype = {
|
||||
var cheight = 0.0;
|
||||
for (var i=0; i<this.options.showNumRooms && i<rows.length; ++i)
|
||||
{
|
||||
var bounds = rows[i].getBoundingClientRect();
|
||||
cheight += bounds.bottom-bounds.top;
|
||||
cheight += rows[i].offsetHeight;
|
||||
}
|
||||
this.edivr.style.height = `${cheight}px`;
|
||||
this.edivr.style.maxHeight = 'initial';
|
||||
@@ -1418,35 +1406,6 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
//==== PRICELISTS
|
||||
getPricelist: function(onlyNew) {
|
||||
var data = {};
|
||||
|
||||
var key = this._pricelist_id;
|
||||
var pricelist = this._pricelist[key];
|
||||
for (var listitem of pricelist) {
|
||||
for (var i=0; i<=this.options.days; ++i) {
|
||||
var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc();
|
||||
var dd_local = dd.clone().local();
|
||||
|
||||
var input = this.edtable.querySelector(`#${this._sanitizeId(`INPUT_PRICE_${key}_${listitem.room}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)}`);
|
||||
if (input.value !== input.dataset.orgValue) {
|
||||
var value = input.value;
|
||||
var orgValue = input.dataset.orgValue;
|
||||
var parentCell = this.edtable.querySelector(`#${input.dataset.hcalParentCell}`);
|
||||
var parentRow = this.edtable.querySelector(`#${parentCell.dataset.hcalParentRow}`);
|
||||
if (!(parentRow.dataset.hcalRoomTypeId in data)) { data[parentRow.dataset.hcalRoomTypeId] = []; }
|
||||
data[parentRow.dataset.hcalRoomTypeId].push({
|
||||
'date': HotelCalendar.toMoment(parentCell.dataset.hcalDate).format('YYYY-MM-DD'),
|
||||
'price': value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
//==== UPDATE FUNCTIONS
|
||||
_updateView: function(/*Bool*/notData, /*function*/callback) {
|
||||
this._createTableReservationDays();
|
||||
@@ -1456,25 +1415,34 @@ HotelCalendar.prototype = {
|
||||
this._updateCellSelection();
|
||||
this._createTableDetailDays();
|
||||
|
||||
_.defer(function(){
|
||||
this._updateReservations(true);
|
||||
}.bind(this));
|
||||
if (!notData) {
|
||||
_.defer(function(){
|
||||
this._updateRestrictions();
|
||||
this._updatePriceList();
|
||||
this._updateReservationOccupation();
|
||||
}.bind(this));
|
||||
}
|
||||
_.defer(function(self){
|
||||
self._updateOffsets();
|
||||
self._updateReservations(true);
|
||||
if (!notData) {
|
||||
_.defer(function(self){
|
||||
self._updateRestrictions();
|
||||
self._updatePriceList();
|
||||
self._updateReservationOccupation();
|
||||
}, self);
|
||||
}
|
||||
}, this);
|
||||
// if (!notData) {
|
||||
// _.defer(function(self){
|
||||
// self._createTableDetailDays();
|
||||
// self._updateRestrictions();
|
||||
// self._updatePriceList();
|
||||
// self._updateReservationOccupation();
|
||||
// }, this);
|
||||
// }
|
||||
},
|
||||
|
||||
_updateOBIndicators: function() {
|
||||
var mainBounds = this.edivr.getBoundingClientRect();
|
||||
var mainBounds = this._edivrOffset;
|
||||
for (var reserv of this._reservations) {
|
||||
if (reserv.overbooking && reserv._html) {
|
||||
var eOffset = this.e.getBoundingClientRect();
|
||||
var bounds = reserv._html.getBoundingClientRect();
|
||||
if (bounds.top > mainBounds.bottom) {
|
||||
var eOffset = this._eOffset;
|
||||
var bounds = this.loopedOffsetOptimized(reserv._html);
|
||||
if (bounds.top > mainBounds.height) {
|
||||
var warnDiv = this.e.querySelector(`div.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`);
|
||||
if (!warnDiv) {
|
||||
var warnDiv = document.createElement("DIV");
|
||||
@@ -1482,12 +1450,12 @@ HotelCalendar.prototype = {
|
||||
warnDiv.classList.add('hcal-warn-ob-indicator');
|
||||
warnDiv.style.borderTopLeftRadius = warnDiv.style.borderTopRightRadius = "50px";
|
||||
warnDiv.dataset.hcalReservationObjId = reserv.id;
|
||||
this.e.appendChild(warnDiv);
|
||||
this.edivcontainer.appendChild(warnDiv);
|
||||
var warnComputedStyle = window.getComputedStyle(warnDiv, null);
|
||||
warnDiv.style.top = `${mainBounds.bottom - eOffset.top - parseInt(warnComputedStyle.getPropertyValue("height"), 10)}px`;
|
||||
warnDiv.style.top = `${mainBounds.height - eOffset.top - parseInt(warnComputedStyle.getPropertyValue("height"), 10)}px`;
|
||||
warnDiv.style.left = `${(bounds.left + (bounds.right - bounds.left)/2.0 - parseInt(warnComputedStyle.getPropertyValue("width"), 10)/2.0) - mainBounds.left}px`;
|
||||
}
|
||||
} else if (bounds.bottom < mainBounds.top) {
|
||||
} else if (bounds.height < mainBounds.top) {
|
||||
var warnDiv = this.e.querySelector(`div.hcal-warn-ob-indicator[data-hcal-reservation-obj-id='${reserv.id}']`);
|
||||
if (!warnDiv) {
|
||||
var warnDiv = document.createElement("DIV");
|
||||
@@ -1496,7 +1464,7 @@ HotelCalendar.prototype = {
|
||||
warnDiv.style.borderBottomLeftRadius = warnDiv.style.borderBottomRightRadius = "50px";
|
||||
warnDiv.style.top = `${mainBounds.top - eOffset.top}px`;
|
||||
warnDiv.dataset.hcalReservationObjId = reserv.id;
|
||||
this.e.appendChild(warnDiv);
|
||||
this.edivcontainer.appendChild(warnDiv);
|
||||
var warnComputedStyle = window.getComputedStyle(warnDiv, null);
|
||||
warnDiv.style.left = `${(bounds.left + (bounds.right - bounds.left)/2.0 - parseInt(warnComputedStyle.getPropertyValue("width"), 10)/2.0) - mainBounds.left}px`;
|
||||
}
|
||||
@@ -1652,14 +1620,14 @@ HotelCalendar.prototype = {
|
||||
},
|
||||
|
||||
_updateScroll: function(/*HTMLObject*/reservationDiv) {
|
||||
var reservBounds = reservationDiv.getBoundingClientRect();
|
||||
var mainBounds = this.edivr.getBoundingClientRect();
|
||||
var eOffset = this.e.getBoundingClientRect();
|
||||
var bottom = mainBounds.bottom - eOffset.top;
|
||||
var reservBounds = this.loopedOffsetOptimized(reservationDiv);
|
||||
var mainBounds = this._edivrOffset;
|
||||
var eOffset = this._eOffset;
|
||||
var bottom = mainBounds.height - eOffset.top;
|
||||
var top = mainBounds.top + eOffset.top;
|
||||
var offset = 10.0;
|
||||
var scrollDisp = 10.0;
|
||||
if (reservBounds.bottom >= bottom-offset) {
|
||||
if (reservBounds.height >= bottom-offset) {
|
||||
this.edivr.scrollBy(0, scrollDisp);
|
||||
}
|
||||
else if (reservBounds.top <= top+offset) {
|
||||
@@ -1756,55 +1724,71 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
|
||||
if (!noRefresh) {
|
||||
var numBeds = (+reserv._limits.right.dataset.hcalBedNum)-(+reserv._limits.left.dataset.hcalBedNum);
|
||||
reserv._beds = [];
|
||||
for (var i=0; i<=numBeds; reserv._beds.push(+reserv._limits.left.dataset.hcalBedNum+i++));
|
||||
var boundsInit = this.loopedOffsetOptimized(reserv._limits.left);
|
||||
var boundsEnd = this.loopedOffsetOptimized(reserv._limits.right);
|
||||
var divHeight = (boundsEnd.top+boundsEnd.height)-boundsInit.top-4;
|
||||
var has_changed = false;
|
||||
|
||||
var boundsInit = reserv._limits.left.getBoundingClientRect();
|
||||
var boundsEnd = reserv._limits.right.getBoundingClientRect();
|
||||
var reservStyles = {
|
||||
backgroundColor: reserv.color,
|
||||
color: reserv.colorText,
|
||||
lineHeight: `${divHeight}px`,
|
||||
fontSize: '12px',
|
||||
top: `${boundsInit.top-this._etableOffset.top+2}px`,
|
||||
left: `${boundsInit.left-this._etableOffset.left+2}px`,
|
||||
width: `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-4}px`,
|
||||
height: `${divHeight}px`,
|
||||
borderLeftWidth: '',
|
||||
borderLeftStyle: '',
|
||||
borderRightWidth: '',
|
||||
borderRightStyle: '',
|
||||
};
|
||||
|
||||
reserv._html.removeAttribute('style');
|
||||
if (reserv._drawModes[0] === 'soft-start') {
|
||||
has_changed = true;
|
||||
reservStyles.borderLeftWidth = '3px';
|
||||
reservStyles.borderLeftStyle = 'double';
|
||||
reservStyles.left = `${boundsInit.left-this._etableOffset.left}px`;
|
||||
reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`;
|
||||
} else if (reserv.splitted && reserv.startDate.isSame(reserv.getUserData('realDates')[0], 'day')) {
|
||||
has_changed = true;
|
||||
reservStyles.borderLeftWidth = '0';
|
||||
reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`;
|
||||
}
|
||||
|
||||
if (reserv._drawModes[1] === 'soft-end') {
|
||||
has_changed = true;
|
||||
reservStyles.borderRightWidth = '3px';
|
||||
reservStyles.borderRightStyle = 'double';
|
||||
reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-2}px`;
|
||||
} else if (reserv.splitted && reserv.endDate.isSame(reserv.getUserData('realDates')[1], 'day')) {
|
||||
has_changed = true;
|
||||
reservStyles.borderRightWidth = '0';
|
||||
reservStyles.left = `${boundsInit.left-this._etableOffset.left-1}px`;
|
||||
reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width-1}px`;
|
||||
}
|
||||
|
||||
if (reserv.splitted) {
|
||||
reserv._html.classList.add('hcal-reservation-splitted');
|
||||
// 1. Use reservation ID as seed
|
||||
// 2. Use sinusiudal function
|
||||
// 3. Only use positive values (This decrease longitude, increase frequency)
|
||||
// 3. Only use positive values (This decrease longitude)
|
||||
// 4. Use the first 5 decimals to make the integer value
|
||||
// 5. Get integer value (Bitwise tilde method)
|
||||
// TODO: Improve pseudo-random number generator
|
||||
var magicNumber = ~~(Math.abs(Math.sin((reserv.getUserData('parent_reservation') || reserv.id))) * 100000);
|
||||
var bbColor = this._intToRgb(magicNumber);
|
||||
reserv._html.style.borderColor = `rgb(${bbColor[0]},${bbColor[1]},${bbColor[2]})`;
|
||||
reservStyles.borderColor = `rgb(${bbColor[0]},${bbColor[1]},${bbColor[2]})`;
|
||||
|
||||
if (!has_changed) {
|
||||
reservStyles.left = `${boundsInit.left-this._etableOffset.left-1}px`;
|
||||
reservStyles.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width+2}px`;
|
||||
}
|
||||
} else {
|
||||
reserv._html.classList.remove('hcal-reservation-splitted');
|
||||
}
|
||||
reserv._html.style.backgroundColor = reserv.color;
|
||||
reserv._html.style.color = reserv.colorText;
|
||||
|
||||
var etableOffset = this.etable.getBoundingClientRect();
|
||||
|
||||
reserv._html.style.top = `${boundsInit.top-etableOffset.top}px`;
|
||||
var divHeight = (boundsEnd.bottom-etableOffset.top)-(boundsInit.top-etableOffset.top);
|
||||
var fontHeight = divHeight/1.2;
|
||||
if (fontHeight > 16) { fontHeight = 16; }
|
||||
reserv._html.style.height = `${divHeight}px`;
|
||||
reserv._html.style.lineHeight = `${divHeight+fontHeight/2.0}px`;
|
||||
reserv._html.style.fontSize = `${fontHeight}px`;
|
||||
reserv._html.style.left = `${boundsInit.left-etableOffset.left}px`;
|
||||
reserv._html.style.width = `${(boundsEnd.left-boundsInit.left)+boundsEnd.width}px`;
|
||||
if (reserv._drawModes[0] === 'soft-start') {
|
||||
reserv._html.style.borderLeftWidth = '3px';
|
||||
reserv._html.style.borderLeftStyle = 'double';
|
||||
} else if (reserv.splitted && reserv.startDate.isSame(reserv.getUserData('realDates')[0], 'day')) {
|
||||
reserv._html.style.borderLeftWidth = '0';
|
||||
}
|
||||
if (reserv._drawModes[1] === 'soft-end') {
|
||||
reserv._html.style.borderRightWidth = '3px';
|
||||
reserv._html.style.borderRightStyle = 'double';
|
||||
} else if (reserv.splitted && reserv.endDate.isSame(reserv.getUserData('realDates')[1], 'day')) {
|
||||
reserv._html.style.borderRightWidth = '0';
|
||||
}
|
||||
Object.assign(reserv._html.style, reservStyles);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1834,12 +1818,7 @@ HotelCalendar.prototype = {
|
||||
refToRoomNewId = `${refFromReservs.id}@${refToRoomNewId}`;
|
||||
|
||||
if (refFromRoom.overbooking || refFromRoom.cancelled) {
|
||||
// Obtain real id
|
||||
var isf = refFromReservs.room.number.search('EX-');
|
||||
var isfb = refFromReservs.room.number.search('/#');
|
||||
var cnumber = refFromReservs.room.number;
|
||||
if (isf != -1 && isfb != -1) { cnumber = cnumber.substr(isf+3, isfb-(isf+3)); }
|
||||
|
||||
var cnumber = this.getExtraRoomRealNumber(refFromRoom);
|
||||
refFromRoom.id = refFromRoomNewId;
|
||||
var newRowId = `${this._sanitizeId(`ROW_${cnumber}_${refToRoom.type}_EXTRA${refToReservs.id}`)}`;
|
||||
var elms = fromRoomRow.querySelectorAll(`td[data-hcal-parent-row='${fromRoomRow.id}']`);
|
||||
@@ -1848,12 +1827,7 @@ HotelCalendar.prototype = {
|
||||
fromRoomRow.dataset.hcalRoomObjId = refFromRoom.id;
|
||||
}
|
||||
if (refToRoom.overbooking || refToRoom.cancelled) {
|
||||
// Obtain real id
|
||||
var isf = refToReservs.room.number.search('EX-');
|
||||
var isfb = refToReservs.room.number.search('/#');
|
||||
var cnumber = refToReservs.room.number;
|
||||
if (isf != -1 && isfb != -1) { cnumber = cnumber.substr(isf+3, isfb-(isf+3)); }
|
||||
|
||||
var cnumber = this.getExtraRoomRealNumber(refToRoom);
|
||||
refToRoom.id = refToRoomNewId;
|
||||
var newRowId = `${this._sanitizeId(`ROW_${cnumber}_${refFromRoom.type}_EXTRA${refFromReservs.id}`)}`;
|
||||
var elms = toRoomRow.querySelectorAll(`td[data-hcal-parent-row='${toRoomRow.id}']`);
|
||||
@@ -1959,7 +1933,7 @@ HotelCalendar.prototype = {
|
||||
var $this = this;
|
||||
reservDivs = reservDivs || this.e.querySelectorAll('div.hcal-reservation');
|
||||
for (var rdiv of reservDivs) {
|
||||
var bounds = rdiv.getBoundingClientRect();
|
||||
var bounds = this.loopedOffsetOptimized(rdiv);
|
||||
rdiv.addEventListener('mousemove', function(ev){
|
||||
var posAction = $this._getRerservationPositionAction(this, ev.layerX, ev.layerY);
|
||||
this.style.cursor = (posAction == HotelCalendar.ACTION.MOVE_LEFT || posAction == HotelCalendar.ACTION.MOVE_RIGHT)?'col-resize':'pointer';
|
||||
@@ -2060,13 +2034,13 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
|
||||
$this._lazyModeReservationsSelection = false;
|
||||
}.bind(this, $this), 100);
|
||||
}.bind(this, $this), 175);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
rdiv.addEventListener('mousedown', _funcEvent, false);
|
||||
rdiv.addEventListener('touchstart', _funcEvent, false);
|
||||
rdiv.addEventListener('mousedown', _funcEvent, this._supportsPassive ? { passive: true } : false);
|
||||
rdiv.addEventListener('touchstart', _funcEvent, this._supportsPassive ? { passive: true } : false);
|
||||
rdiv.addEventListener('click', function(ev){
|
||||
$this._dispatchEvent(
|
||||
'hcalOnClickReservation',
|
||||
@@ -2109,7 +2083,7 @@ HotelCalendar.prototype = {
|
||||
},
|
||||
|
||||
_getRerservationPositionAction: function(/*HTMLObject*/elm, /*Int*/posX, /*Int*/posY) {
|
||||
var bounds = elm.getBoundingClientRect();
|
||||
var bounds = this.loopedOffsetOptimized(elm);
|
||||
if (posX <= 5) { return HotelCalendar.ACTION.MOVE_LEFT; }
|
||||
else if (posX >= bounds.width-10) { return HotelCalendar.ACTION.MOVE_RIGHT; }
|
||||
return HotelCalendar.ACTION.MOVE_ALL;
|
||||
@@ -2161,26 +2135,24 @@ HotelCalendar.prototype = {
|
||||
return;
|
||||
}
|
||||
var cells = [
|
||||
this.edtable.querySelectorAll('td.hcal-cell-detail-room-free-type-group-item-day'),
|
||||
this.edtable.querySelectorAll('td.hcal-cell-detail-room-free-total-group-item-day'),
|
||||
this.edtable.querySelectorAll('td.hcal-cell-detail-room-perc-occup-group-item-day')
|
||||
this.edtable.querySelectorAll('.hcal-cell-detail-room-free-type-group-item-day'),
|
||||
this.edtable.querySelectorAll('.hcal-cell-detail-room-free-total-group-item-day'),
|
||||
this.edtable.querySelectorAll('.hcal-cell-detail-room-perc-occup-group-item-day')
|
||||
];
|
||||
|
||||
var cell;
|
||||
for (var i=0; i<=cells[0].length; ++i) {
|
||||
for (var cell of cells[0]) {
|
||||
// Occupation by Type
|
||||
cell = cells[0][i];
|
||||
if (cell) {
|
||||
var parentRow = this.$base.querySelector(`#${cell.dataset.hcalParentRow}`);
|
||||
var cell_date = cell.dataset.hcalDate;
|
||||
var num_rooms = this.getRoomsCapacityByType(parentRow.dataset.hcalRoomType);
|
||||
var num_free = this.calcDayRoomTypeReservations(cell_date, parentRow.dataset.hcalRoomType);
|
||||
var num_rooms_type = this._roomCapacities[cell.parentNode.dataset.hcalRoomType];
|
||||
var num_free = this.calcDayRoomTypeReservations(cell_date, cell.parentNode.dataset.hcalRoomType);
|
||||
cell.innerText = num_free;
|
||||
cell.style.backgroundColor = this._generateColor(num_free, num_rooms, 0.35, true, true);
|
||||
cell.style.backgroundColor = this._generateColor(num_free, num_rooms_type, 0.35, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
var num_rooms = this.getRoomsCapacityTotal();
|
||||
var cell;
|
||||
var num_rooms = this._roomCapacityTotal;
|
||||
for (var i=0; i<=this.options.days; ++i) {
|
||||
// Occupation Total
|
||||
cell = cells[1][i];
|
||||
@@ -2202,6 +2174,34 @@ HotelCalendar.prototype = {
|
||||
},
|
||||
|
||||
//==== PRICELIST
|
||||
getPricelist: function(onlyNew) {
|
||||
var data = {};
|
||||
|
||||
var key = this._pricelist_id;
|
||||
var pricelist = this._pricelist[key];
|
||||
for (var listitem of pricelist) {
|
||||
for (var i=0; i<=this.options.days; ++i) {
|
||||
var dd = this.options.startDate.clone().local().startOf('day').add(i,'d').utc();
|
||||
var dd_local = dd.clone().local();
|
||||
|
||||
var input = this.edtable.querySelector(`#${this._sanitizeId(`INPUT_PRICE_${key}_${listitem.room}_${dd_local.format(HotelCalendar.DATE_FORMAT_SHORT_)}`)}`);
|
||||
if (input.value !== input.dataset.orgValue) {
|
||||
var value = input.value;
|
||||
var orgValue = input.dataset.orgValue;
|
||||
var parentCell = this.edtable.querySelector(`#${input.dataset.hcalParentCell}`);
|
||||
var parentRow = this.edtable.querySelector(`#${parentCell.dataset.hcalParentRow}`);
|
||||
if (!(parentRow.dataset.hcalRoomTypeId in data)) { data[parentRow.dataset.hcalRoomTypeId] = []; }
|
||||
data[parentRow.dataset.hcalRoomTypeId].push({
|
||||
'date': HotelCalendar.toMoment(parentCell.dataset.hcalDate).format('YYYY-MM-DD'),
|
||||
'price': value
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
setPricelist: function(/*List*/pricelist) {
|
||||
this._pricelist = pricelist;
|
||||
this._updatePriceList();
|
||||
@@ -2293,9 +2293,7 @@ HotelCalendar.prototype = {
|
||||
},
|
||||
|
||||
getDateDiffDays: function(/*MomentObject*/start, /*MomentObject*/end) {
|
||||
var end_date = end.clone().startOf('day');
|
||||
var start_date = start.clone().startOf('day');
|
||||
return end_date.diff(start_date, 'days');
|
||||
return end.diff(start, 'days');
|
||||
},
|
||||
|
||||
getDateLimits: function(/*List HReservationObject*/reservs, /*Bool?*/noCheckouts) {
|
||||
@@ -2393,6 +2391,10 @@ HotelCalendar.prototype = {
|
||||
return true;
|
||||
},
|
||||
|
||||
getDates: function() {
|
||||
return [this.options.startDate.clone(), this._endDate.clone()];
|
||||
},
|
||||
|
||||
//==== EVENT FUNCTIONS
|
||||
_onInputChange: function(/*EventObject*/ev, /*HTMLObject*/elm) {
|
||||
//var parentCell = this.edtable.querySelector(`#${elm.dataset.hcalParentCell}`);
|
||||
@@ -2602,25 +2604,38 @@ HotelCalendar.prototype = {
|
||||
}
|
||||
if (reservs.length) {
|
||||
this._splitReservation = reservs[0];
|
||||
this._divideDivs = [$(this._splitReservation._html).clone().text('').appendTo(this.edivr), $(this._splitReservation._html).clone().text('').appendTo(this.edivr)];
|
||||
var defStyle = {
|
||||
top: this._splitReservation._html.style.top,
|
||||
left: this._splitReservation._html.style.left,
|
||||
height: this._splitReservation._html.style.height,
|
||||
};
|
||||
this._divideDivs = [
|
||||
$('<div/>', {class: 'hcal-reservation-divide-l', css: defStyle}).appendTo(this.edivr),
|
||||
$('<div/>', {class: 'hcal-reservation-divide-r', css: defStyle}).appendTo(this.edivr)
|
||||
];
|
||||
var diff = this.getDateDiffDays(this._splitReservation.startDate, date_cell);
|
||||
this._divideDivs[0].addClass('hcal-reservation-divide-l');
|
||||
this._divideDivs[1].addClass('hcal-reservation-divide-r');
|
||||
|
||||
var etableOffset = this.etable.getBoundingClientRect();
|
||||
var boundsCell = ev.target.getBoundingClientRect();
|
||||
var beginCell = this._splitReservation._limits.left.getBoundingClientRect();
|
||||
var endCell = this._splitReservation._limits.right.getBoundingClientRect();
|
||||
var splitCell = boundsCell;
|
||||
var splitDate = date_cell.clone();
|
||||
var boundsCell = false;
|
||||
var beginCell = this.loopedOffsetOptimized(this._splitReservation._limits.left);
|
||||
var endCell = this.loopedOffsetOptimized(this._splitReservation._limits.right);
|
||||
this._splitDate = date_cell.clone();
|
||||
if (date_cell.isSame(this._splitReservation.endDate.clone().subtract(1, 'd'), 'day')) {
|
||||
splitDate.subtract(1, 'd');
|
||||
splitCell = this.getCell(this._splitDate, this._splitReservation.room, 0);
|
||||
this._splitDate.subtract(1, 'd');
|
||||
var tcell = this.getCell(this._splitDate, this._splitReservation.room, 0);
|
||||
if (tcell) {
|
||||
boundsCell = this.loopedOffsetOptimized(tcell);
|
||||
} else {
|
||||
boundsCell = false;
|
||||
this._splitReservation = false;
|
||||
this._splitDate = false;
|
||||
}
|
||||
} else {
|
||||
boundsCell = this.loopedOffsetOptimized(ev.target);
|
||||
}
|
||||
if (boundsCell) {
|
||||
this._divideDivs[0][0].style.width = `${(boundsCell.left-beginCell.left)+boundsCell.width}px`;
|
||||
this._divideDivs[1][0].style.left = `${(boundsCell.left-this._etableOffset.left)+boundsCell.width}px`;
|
||||
this._divideDivs[1][0].style.width = `${(endCell.left-boundsCell.left)}px`;
|
||||
}
|
||||
this._divideDivs[0][0].style.width = `${(splitCell.left-beginCell.left)+splitCell.width}px`;
|
||||
this._divideDivs[1][0].style.left = `${(splitCell.left-etableOffset.left)+splitCell.width}px`;
|
||||
this._divideDivs[1][0].style.width = `${(endCell.left-splitCell.left)}px`;
|
||||
} else {
|
||||
this._splitReservation = false;
|
||||
this._splitDate = false;
|
||||
@@ -2688,6 +2703,7 @@ HotelCalendar.prototype = {
|
||||
hasInvalidLink = !hasInvalidLink && r._html.classList.contains('hcal-reservation-invalid');
|
||||
r._html.classList.remove('hcal-reservation-action');
|
||||
r._html.classList.remove('hcal-reservation-invalid');
|
||||
r._html.style.visibility = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2744,10 +2760,41 @@ HotelCalendar.prototype = {
|
||||
|
||||
onMainResize: function(/*EventObject*/ev) {
|
||||
_.defer(function(){
|
||||
this._updateOffsets();
|
||||
this._updateReservations();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
//=== OPTIMIZED OFFSET
|
||||
// Method from https://jsperf.com/offset-vs-getboundingclientrect/7
|
||||
loopedOffsetOptimized: function (elem) {
|
||||
var offsetLeft = elem.offsetLeft
|
||||
, offsetTop = elem.offsetTop
|
||||
, offsetWidth = elem.offsetWidth
|
||||
, offsetHeight = elem.offsetHeight
|
||||
, lastElem = elem;
|
||||
|
||||
while (elem = elem.offsetParent) {
|
||||
if (elem === document.body) { //from my observation, document.body always has scrollLeft/scrollTop == 0
|
||||
break;
|
||||
}
|
||||
offsetLeft += elem.offsetLeft;
|
||||
offsetTop += elem.offsetTop;
|
||||
lastElem = elem;
|
||||
}
|
||||
// if (lastElem && lastElem.style.position === 'fixed') { //slow - http://jsperf.com/offset-vs-getboundingclientrect/6
|
||||
// //if(lastElem !== document.body) { //faster but does gives false positive in Firefox
|
||||
// offsetLeft += window.pageXOffset || document.documentElement.scrollLeft;
|
||||
// offsetTop += window.pageYOffset || document.documentElement.scrollTop;
|
||||
// }
|
||||
return {
|
||||
left: offsetLeft,
|
||||
top: offsetTop,
|
||||
width: offsetWidth,
|
||||
height: offsetHeight,
|
||||
};
|
||||
},
|
||||
|
||||
//==== COLOR FUNCTIONS (RANGE: 0.0|1.0)
|
||||
_intToRgb: function(/*Int*/RGBint) {
|
||||
return [(RGBint >> 16) & 255, (RGBint >> 8) & 255, RGBint & 255];
|
||||
|
||||
@@ -113,24 +113,26 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-12 nopadding menu-filter-box">
|
||||
<h4>Filters</h4>
|
||||
<select class="form-control" id="type_list" placeholder="Select Segmentation..." multiple="multiple"/>
|
||||
<select class="list form-control" id="floor_list" placeholder="Select Location..." multiple="multiple"/>
|
||||
<select class="list form-control" id="amenities_list" placeholder="Select Amenities..." multiple="multiple"/>
|
||||
<select class="list form-control" id="virtual_list" placeholder="Select Type..." multiple="multiple"/>
|
||||
<div class="filter-record col-xs-12 col-md-12" style="padding:4px">
|
||||
<div class="col-xs-8 col-md-8 nopadding">
|
||||
<input type="edit" id="calendar_name" class="form-control" />
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2 nopadding">
|
||||
<button class="btn btn-primary col-xs-12 col-md-12" id="btn_save_calendar_record">
|
||||
<i class="fa fa-fw fa-save"> </i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2 nopadding">
|
||||
<button class="btn btn-primary col-xs-12 col-md-12" id="btn_reload_calendar_filters">
|
||||
<i class="fa fa-fw fa-refresh"> </i>
|
||||
</button>
|
||||
<h4 data-toggle="collapse" data-target="#filters"><i class="fa fa-chevron-circle-right"></i> Filters</h4>
|
||||
<div id="filters" class="collapse">
|
||||
<select class="form-control" id="type_list" placeholder="Select Segmentation..." multiple="multiple"/>
|
||||
<select class="list form-control" id="floor_list" placeholder="Select Location..." multiple="multiple"/>
|
||||
<select class="list form-control" id="amenities_list" placeholder="Select Amenities..." multiple="multiple"/>
|
||||
<select class="list form-control" id="virtual_list" placeholder="Select Type..." multiple="multiple"/>
|
||||
<div class="filter-record col-xs-12 col-md-12" style="padding:4px">
|
||||
<div class="col-xs-8 col-md-8 nopadding">
|
||||
<input type="edit" id="calendar_name" class="form-control" />
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2 nopadding">
|
||||
<button class="btn btn-primary col-xs-12 col-md-12" id="btn_save_calendar_record">
|
||||
<i class="fa fa-fw fa-save"> </i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-xs-2 col-md-2 nopadding">
|
||||
<button class="btn btn-primary col-xs-12 col-md-12" id="btn_reload_calendar_filters">
|
||||
<i class="fa fa-fw fa-refresh"> </i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -17,13 +17,6 @@
|
||||
<field name="domain">[('checkout','=', datetime.datetime.now().strftime('%Y-%m-%d'))]</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="hotel_room_pricelist_cached_action_form_tree">
|
||||
<field name="name">Room Pricelist Cached</field>
|
||||
<field name="res_model">room.pricelist.cached</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="hotel_calendar_action_form_tree">
|
||||
<field name="name">Hotel Calendar</field>
|
||||
<field name="res_model">hotel.calendar</field>
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Form view of hotel room -->
|
||||
<record model="ir.ui.view" id="hotel_room_pricelist_cached_view_form">
|
||||
<field name="name">hotel.room.pricelist.cached.form</field>
|
||||
<field name="model">room.pricelist.cached</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Room Pricelist Cached">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="room_id" />
|
||||
<field name="date" />
|
||||
<field name="price" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Tree view of hotel room -->
|
||||
<record model="ir.ui.view" id="hotel_room_pricelist_cached_view_tree">
|
||||
<field name="name">hotel.room.pricelist.cached.tree</field>
|
||||
<field name="model">room.pricelist.cached</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Room Pricelist Cached">
|
||||
<field name="room_id" />
|
||||
<field name="date" />
|
||||
<field name="price" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -43,10 +43,10 @@ class HotelFolio(models.Model):
|
||||
info_grouped = []
|
||||
for rline in self.room_lines:
|
||||
if (import_all or rline.to_send) and not rline.parent_reservation and rline.state == state and ((rline.state == 'cancelled' and not rline.channel_modified) or rline.state != 'cancelled'):
|
||||
dates = rline.get_real_checkin_checkout()
|
||||
dates = (rline.real_checkin, rline.real_checkout)
|
||||
vals = {
|
||||
'num': len(
|
||||
self.room_lines.filtered(lambda r: r.get_real_checkin_checkout()[0] == dates[0] and r.get_real_checkin_checkout()[1] == dates[1] and r.room_type_id.id == rline.room_type_id.id and (r.to_send or import_all) and not r.parent_reservation and r.state == rline.state and ((r.state == 'cancelled' and not r.channel_modified) or r.state != 'cancelled'))
|
||||
self.room_lines.filtered(lambda r: r.real_checkin == dates[0] and r.real_checkout == dates[1] and r.room_type_id.id == rline.room_type_id.id and (r.to_send or import_all) and not r.parent_reservation and r.state == rline.state and ((r.state == 'cancelled' and not r.channel_modified) or r.state != 'cancelled'))
|
||||
),
|
||||
'room_type': {
|
||||
'id': rline.room_type_id.id,
|
||||
|
||||
Reference in New Issue
Block a user