From b1223b6a5d018b9b8c67822499326737f24f703a Mon Sep 17 00:00:00 2001 From: Pablo Date: Wed, 20 Feb 2019 16:46:32 +0100 Subject: [PATCH 01/10] [ADD][WIP] kanban view for hotel room segmentation --- hotel/views/hotel_room_views.xml | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hotel/views/hotel_room_views.xml b/hotel/views/hotel_room_views.xml index e99531004..ac7982998 100644 --- a/hotel/views/hotel_room_views.xml +++ b/hotel/views/hotel_room_views.xml @@ -63,6 +63,38 @@ + + + hotel.room.kanban + hotel.room + kanban + + + false + + + + + +
+
+
    +
  • + +
  • +
  • Room Type:
  • +
  • + Capacity +
  • +
+
+
+
+
+
+
+
+ hotel.room.search From 1667a0a60ce2f8d62c9c970a04a6a3ff3f2ba227 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 21 Feb 2019 10:48:09 +0100 Subject: [PATCH 02/10] [FIX] reservation domain includes also `dto` --- hotel/models/hotel_reservation.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hotel/models/hotel_reservation.py b/hotel/models/hotel_reservation.py index 8f4590cb9..e9cd5c880 100644 --- a/hotel/models/hotel_reservation.py +++ b/hotel/models/hotel_reservation.py @@ -942,10 +942,9 @@ class HotelReservation(models.Model): @api.model def get_reservations(self, dfrom, dto): """ - @param self: The object pointer @param dfrom: range date from @param dto: range date to - @return: array with the reservations _confirmed_ between dfrom and dto + @return: array with the reservations _confirmed_ between both dates `dfrom` and `dto` """ domain = self._get_domain_reservations_occupation(dfrom, dto) return self.env['hotel.reservation'].search(domain) @@ -953,7 +952,7 @@ class HotelReservation(models.Model): @api.model def _get_domain_reservations_occupation(self, dfrom, dto): domain = [('reservation_line_ids.date', '>=', dfrom), - ('reservation_line_ids.date', '<', dto), + ('reservation_line_ids.date', '<=', dto), ('state', '!=', 'cancelled'), ('overbooking', '=', False), ('reselling', '=', False),] From 9ccb618abeeaa3662c1762d6c604d83d4f448870 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 21 Feb 2019 18:58:22 +0100 Subject: [PATCH 03/10] [WIP] clean up proposarl --- hotel/models/hotel_reservation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hotel/models/hotel_reservation.py b/hotel/models/hotel_reservation.py index e9cd5c880..28d4630c3 100644 --- a/hotel/models/hotel_reservation.py +++ b/hotel/models/hotel_reservation.py @@ -958,6 +958,7 @@ class HotelReservation(models.Model): ('reselling', '=', False),] return domain + # INFO: This function is not in use and should include `dto` in the search @api.model def get_reservations_dates(self, dfrom, dto, room_type=False): """ From 2b9159c09c4a9ebcad1f96b3c5517eaab5630b14 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 21 Feb 2019 18:59:49 +0100 Subject: [PATCH 04/10] [WIP] default quota for channel manager availability --- hotel_channel_connector/models/hotel_room_type/common.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hotel_channel_connector/models/hotel_room_type/common.py b/hotel_channel_connector/models/hotel_room_type/common.py index bfdf8a78f..21c996855 100644 --- a/hotel_channel_connector/models/hotel_room_type/common.py +++ b/hotel_channel_connector/models/hotel_room_type/common.py @@ -94,6 +94,7 @@ class HotelRoomType(models.Model): inverse_name='odoo_id', string='Hotel Channel Connector Bindings') + default_quota = fields.Integer("Default Quota", compute="_compute_capacity") capacity = fields.Integer("Capacity", compute="_compute_capacity") @api.multi From 1137b9fa649deb2091479c616d867c3d337c82a3 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 21 Feb 2019 19:02:09 +0100 Subject: [PATCH 05/10] [WIP] manage channel availability based on quota --- .../hotel_room_type_availability/common.py | 111 +++++++++++++----- .../hotel_room_type_availability/exporter.py | 2 +- 2 files changed, 82 insertions(+), 31 deletions(-) diff --git a/hotel_channel_connector/models/hotel_room_type_availability/common.py b/hotel_channel_connector/models/hotel_room_type_availability/common.py index c66ddcfa0..a9339ccac 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/common.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/common.py @@ -2,7 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging -from datetime import timedelta +from datetime import datetime, timedelta from odoo import api, models, fields, _ from odoo.tools import DEFAULT_SERVER_DATE_FORMAT from odoo.exceptions import ValidationError @@ -33,14 +33,22 @@ class HotelRoomTypeAvailability(models.Model): room_type_id = fields.Many2one('hotel.room.type', 'Room Type', required=True, track_visibility='always', ondelete='cascade') - date = fields.Date('Date', required=True, track_visibility='always') - max_avail = fields.Integer("Max. Avail", default=-1, readonly=True) - quota = fields.Integer("Quota", default=_default_quota) channel_bind_ids = fields.One2many( comodel_name='channel.hotel.room.type.availability', inverse_name='odoo_id', string='Hotel Room Type Availability Connector Bindings') - no_ota = fields.Boolean('No OTA', default=False) + + date = fields.Date('Date', required=True, track_visibility='always') + + quota = fields.Integer("Quota", default=_default_quota, + help="Quota assigned to the channel.") + max_avail = fields.Integer("Max. Availability", default=-1, readonly=True, + help="Maximum simultaneous availability given no quota.") + + no_ota = fields.Boolean('No OTA', default=False, + help="Set zero availability to the connected OTAs " + "even when the availability is positive," + "except to the Online Reception (booking engine)") booked = fields.Boolean('Booked', default=False, readonly=True) _sql_constraints = [ @@ -53,10 +61,15 @@ class HotelRoomTypeAvailability(models.Model): @api.constrains('max_avail', 'quota') def _check_max_avail_quota(self): for record in self: - if record.max_avail != -1 and record.max_avail > record.quota: - raise ValidationError(_("Invalid Max Avail!")) - if record.quota != -1 and record.quota > record.room_type_id.total_rooms_count: - raise ValidationError(_("Invalid Quota!")) + if record.quota > record.room_type_id.total_rooms_count: + raise ValidationError(_("The quota assigned to the channel manager can't be greater " + "than the total rooms count!")) + if (record.max_avail > record.quota) and (record.quota >= 0): + raise ValidationError(_("The maximum simultaneous availability can't be greater " + "than a given quota.")) + if record.max_avail > record.room_type_id.total_rooms_count: + raise ValidationError(_("The maximum simultaneous availability can't be greater " + "than the total rooms count!")) @api.onchange('room_type_id') def onchange_room_type_id(self): @@ -70,11 +83,14 @@ class ChannelHotelRoomTypeAvailability(models.Model): _inherits = {'hotel.room.type.availability': 'odoo_id'} _description = 'Channel Availability' - # avail = fields.Integer("Avail", default=0, readonly=True) odoo_id = fields.Many2one(comodel_name='hotel.room.type.availability', string='Pricelist', required=True, ondelete='cascade') + channel_avail = fields.Integer("Availability", readonly=True, + help="Availability of the room type for the channel manager." + "This availability is set based on the real availability, " + "the quota, and the max availability.") channel_pushed = fields.Boolean("Channel Pushed", readonly=True, default=False) @@ -82,6 +98,7 @@ class ChannelHotelRoomTypeAvailability(models.Model): def _check_avail(self): room_type_obj = self.env['hotel.room.type'] issue_obj = self.env['hotel.channel.connector.issue'] + import wdb; wdb.set_trace() for record in self: cavail = len(room_type_obj.check_availability_room_type( record.date, @@ -110,52 +127,71 @@ class ChannelHotelRoomTypeAvailability(models.Model): date_diff = (date_end - date_start).days channel_room_type_obj = self.env['channel.hotel.room.type'] - channel_room_type_avail_obj = self.env['hotel.room.type.availability'] + channel_room_type_avail_obj = self.env['channel.hotel.room.type.availability'] if room_type_id: - # room_type_bind = channel_room_type_obj.browse(room_type_id) room_type_bind = channel_room_type_obj.search([('odoo_id', '=', room_type_id)]) else: domain = [('backend_id', '=', backend_id)] if room_id: domain.append(('room_ids', 'in', [room_id])) + # WARNING: more than one binding is currently not expected room_type_bind = channel_room_type_obj.search(domain, limit=1) if room_type_bind and room_type_bind.external_id: + _logger.info("==[ODOO->CHANNEL]==== REFRESH AVAILABILITY ==") for i in range(0, date_diff): ndate_dt = date_start + timedelta(days=i) ndate_str = ndate_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) to_eval = [] + # real availability based on rooms cavail = len(channel_room_type_obj.odoo_id.check_availability_room_type( ndate_str, ndate_str, room_type_id=room_type_bind.odoo_id.id)) to_eval.append(cavail) - to_eval.append(room_type_bind.total_rooms_count) + room_type_avail_id = channel_room_type_avail_obj.search([ ('room_type_id', '=', room_type_bind.odoo_id.id), ('date', '=', ndate_str)], limit=1) + # quota and max availability set by revenue ? if room_type_avail_id: - # BUG: Crashes with more than one channel_bind_ids - if room_type_avail_id.channel_bind_ids.max_avail >= 0: - to_eval.append( - room_type_avail_id.channel_bind_ids.max_avail) if room_type_avail_id.quota >= 0: to_eval.append(room_type_avail_id.quota) if room_type_avail_id.max_avail >= 0: to_eval.append(room_type_avail_id.max_avail) - avail = max(min(to_eval), 0) - import wdb; - wdb.set_trace() - _logger.info("==[ODOO->CHANNEL]==== REFRESH AVAILABILITY ==") - # CAVEAT: update hotel.room.type.availability in any change - if room_type_avail_id and avail != room_type_avail_id.max_avail: - room_type_avail_id.write({'max_avail': avail}) + if room_type_avail_id.quota < 0 and room_type_avail_id.max_avail < 0: + # add default availability for OTAs because + # on creation triggered by `no_ota` no rules are given + to_eval.append(room_type_bind.default_availability) else: - # create a new hotel.room.type.availability otherwhise + # default availability for OTAs if not record given + # This should happens only when refreshing availability from hotel.reservation + import wdb; wdb.set_trace() + to_eval.append(room_type_bind.default_availability) + + avail = max(min(to_eval), 0) + _logger.info({ + 'real_avail': cavail, + 'default_avail': room_type_bind.default_availability, + 'quota': room_type_avail_id.quota, + 'max_avail': room_type_avail_id.max_avail, + }) + _logger.info({ + 'room_type_id': room_type_bind.odoo_id.id, + 'date': ndate_str, + 'channel_avail': avail, + }) + if room_type_avail_id: + # CAVEAT: update channel.hotel.room.type.availability if needed + if room_type_avail_id.channel_avail != avail: + room_type_avail_id.write({'channel_avail': avail}) + else: + # This should happens only when refreshing availability from hotel.reservation + import wdb; wdb.set_trace() channel_room_type_avail_obj.create({ 'room_type_id': room_type_bind.odoo_id.id, 'date': ndate_str, - 'avail': avail, + 'channel_avail': avail, }) @job(default_channel='root.channel') @@ -182,13 +218,18 @@ class BindingHotelRoomTypeAvailabilityListener(Component): @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): - fields_to_check = ('max_avail', 'quota', 'no_ota') + fields_to_check = ('quota', 'max_avail', 'no_ota') fields_checked = [elm for elm in fields_to_check if elm in fields] + + _logger.info("==[on_record_write] :: hotel.room.type.availability==") + _logger.info(fields) + if any(fields_checked) and any(record.channel_bind_ids): for binding in record.channel_bind_ids: binding.refresh_availability( record.date, - record.date, + (datetime.strptime(record.date, DEFAULT_SERVER_DATE_FORMAT).date() + + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), binding.backend_id.id, room_type_id=record.room_type_id.id) @@ -204,14 +245,18 @@ class BindingHotelRoomTypeAvailabilityListener(Component): ('backend_id', '=', backend.id), ]) if not avail_bind: + # REVIEW :: WARNING :: This create triggers on_record_write above avail_bind = channel_room_type_avail_obj.create({ 'odoo_id': record.id, 'channel_pushed': False, 'backend_id': backend.id, }) + _logger.info("==[on_record_create] :: hotel.room.type.availability==") + _logger.info(avail_bind) avail_bind.refresh_availability( record.date, - record.date, + (datetime.strptime(record.date, DEFAULT_SERVER_DATE_FORMAT).date() + + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT), backend.id, # room_type_id=record.room_type_id.channel_bind_ids.id, room_type_id=record.room_type_id.id) @@ -224,14 +269,20 @@ class ChannelBindingHotelRoomTypeAvailabilityListener(Component): @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): - fields_to_check = ('date', 'max_avail', 'quota', 'no_ota') + fields_to_check = ('date', 'channel_avail') # no_ota ¿? fields_checked = [elm for elm in fields_to_check if elm in fields] + + _logger.info("==[on_record_write] :: channel.hotel.room.type.availability==") + _logger.info(fields) + if any(fields_checked): record.channel_pushed = False @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_fix_channel_availability(self, record, fields=None): if any(record.channel_bind_ids): + import wdb; + wdb.set_trace() for binding in record.channel_bind_ids: record.refresh_availability( record.checkin, diff --git a/hotel_channel_connector_wubook/models/hotel_room_type_availability/exporter.py b/hotel_channel_connector_wubook/models/hotel_room_type_availability/exporter.py index 9207d1394..220a8e44f 100644 --- a/hotel_channel_connector_wubook/models/hotel_room_type_availability/exporter.py +++ b/hotel_channel_connector_wubook/models/hotel_room_type_availability/exporter.py @@ -31,7 +31,7 @@ class HotelRoomTypeAvailabilityExporter(Component): date_dt = fields.Date.from_string(channel_room_type_avail.date) days.append({ 'date': date_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), - 'avail': channel_room_type_avail.quota, # FIXME max_avail __or__ quota ¿? + 'avail': channel_room_type_avail.channel_avail, 'no_ota': channel_room_type_avail.no_ota and 1 or 0, # 'booked': room_type_avail.booked and 1 or 0, }) From 980c8be29fcfefe1d4d7f7347b9727be7ff656a1 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 21 Feb 2019 19:44:09 +0100 Subject: [PATCH 06/10] [WIP] show channel availability in calendar management --- .../lib/hcalendar/js/hcalendar_management.js | 20 ++++++++++++++++++- .../inherited_hotel_calendar_management.py | 1 + 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js index 04c5ae2c0..31f834549 100644 --- a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js +++ b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js @@ -217,6 +217,7 @@ HotelCalendarManagement.prototype = { row = table.insertRow(); row.setAttribute('name', 'avail'); row.style.display = 'none'; + cell = row.insertCell(); cell.setAttribute('colspan', '2'); telm = document.createElement("input"); @@ -229,8 +230,9 @@ HotelCalendarManagement.prototype = { telm.classList.add('hcal-management-input'); telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); - cell.setAttribute('colspan', '2'); + cell.setAttribute('colspan', '1'); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`MAX_AVAIL_${roomId}_${dateShortStr}`)); telm.setAttribute('name', 'max_avail'); @@ -242,6 +244,21 @@ HotelCalendarManagement.prototype = { telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); + cell.setAttribute('colspan', '1'); + telm = document.createElement("input"); + telm.setAttribute('id', this._sanitizeId(`CHANNEL_AVAIL_${roomId}_${dateShortStr}`)); + telm.setAttribute('name', 'channel_avail'); + telm.setAttribute('type', 'edit'); + telm.setAttribute('title', this._t('Channel Availability')); + telm.setAttribute('readonly', 'readonly'); + telm.setAttribute('disabled', 'disabled'); + telm.style.backgroundColor = 'lightgray'; + telm.value = telm.dataset.orgValue = 0; + telm.dataset.hcalParentCell = parentCell.getAttribute('id'); + telm.classList.add('hcal-management-input'); + cell.appendChild(telm); + row = table.insertRow(); row.setAttribute('name', 'rest_a'); cell = row.insertCell(); @@ -725,6 +742,7 @@ HotelCalendarManagement.prototype = { var inputIds = [ `QUOTA_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.quota, `MAX_AVAIL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.max_avail, + `CHANNEL_AVAIL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.channel_avail, `NO_OTA_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.no_ota ]; diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py b/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py index a811de376..d707c1116 100644 --- a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py +++ b/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py @@ -38,6 +38,7 @@ class HotelCalendarManagement(models.TransientModel): 'no_ota': avail and avail.no_ota or False, 'quota': avail and avail.quota or -1, 'max_avail': avail and avail.max_avail or -1, + 'channel_avail': avail and avail.channel_bind_ids.channel_avail or room_type.channel_bind_ids.default_availability } @api.model From c83220ffbcd4ab7f3d2f480ce18b32ebc554cd1a Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 22 Feb 2019 12:26:04 +0100 Subject: [PATCH 07/10] [ADD] channel availability bus notification --- .../static/src/lib/hcalendar/js/hcalendar_management.js | 5 ++++- .../models/inherited_bus_hotel_calendar.py | 1 + .../models/inherited_hotel_room_type_availability.py | 1 + .../src/js/views/hotel_calendar_management_controller.js | 3 ++- 4 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js index 31f834549..aa76910cf 100644 --- a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js +++ b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js @@ -783,6 +783,8 @@ HotelCalendarManagement.prototype = { var inputMaxAvail = this.etable.querySelector(`#${inputMaxAvailId}`); var inputNoOTAId = this._sanitizeId(`NO_OTA_${room.id}_${ndateStr}`); var inputNoOTA = this.etable.querySelector(`#${inputNoOTAId}`); + var inputChannelAvailId = this._sanitizeId(`CHANNEL_AVAIL_${room.id}_${ndateStr}`); + var inputChannelAvail = this.etable.querySelector(`#${inputChannelAvailId}`); if (!onlyNew || (onlyNew && (inputQuota.value !== inputQuota.dataset.orgValue || inputMaxAvail.value !== inputMaxAvail.dataset.orgValue @@ -792,7 +794,8 @@ HotelCalendarManagement.prototype = { 'date': ndate.format('YYYY-MM-DD'), 'quota': inputQuota.value, 'max_avail': inputMaxAvail.value, - 'no_ota': Boolean(inputNoOTA.dataset.state === 'true') || false + 'no_ota': Boolean(inputNoOTA.dataset.state === 'true') || false, + 'channel_avail': inputChannelAvail.value, }); } } diff --git a/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py b/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py index 3f17225ec..b1cafb2ac 100644 --- a/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py +++ b/hotel_calendar_channel_connector/models/inherited_bus_hotel_calendar.py @@ -42,6 +42,7 @@ class BusHotelCalendar(models.TransientModel): 'max_avail': vals['max_avail'], 'id': vals['id'], 'no_ota': vals['no_ota'], + 'channel_avail': vals['channel_avail'], }, }, }, diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py b/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py index 8be516f90..d9a5492ed 100644 --- a/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py +++ b/hotel_calendar_channel_connector/models/inherited_hotel_room_type_availability.py @@ -14,6 +14,7 @@ class HotelRoomTypeAvailability(models.Model): 'max_avail': record.max_avail, 'room_type_id': record.room_type_id.id, 'id': record.id, + 'channel_avail': record.channel_bind_ids.channel_avail, } @api.model diff --git a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js index f3e8f6880..89ed396e2 100644 --- a/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js +++ b/hotel_calendar_channel_connector/static/src/js/views/hotel_calendar_management_controller.js @@ -30,7 +30,8 @@ var MPMSCalendarController = MPMSCalendarController.include({ 'quota': avail[room_type][day]['quota'], 'max_avail': avail[room_type][day]['max_avail'], 'no_ota': avail[room_type][day]['no_ota'], - 'id': avail[room_type][day]['id'] + 'id': avail[room_type][day]['id'], + 'channel_avail': avail[room_type][day]['channel_avail'] }]; this.renderer._hcalendar.addAvailability(availability); break; From 6708fe1be152e70769c06e8d2ea8e6300241d4d7 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 22 Feb 2019 12:26:43 +0100 Subject: [PATCH 08/10] [FIX] `avail and avail.max_quota or -1` returns -1 if quota = 0 --- .../inherited_hotel_calendar_management.py | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py b/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py index d707c1116..0d870814e 100644 --- a/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py +++ b/hotel_calendar_channel_connector/models/inherited_hotel_calendar_management.py @@ -32,14 +32,24 @@ class HotelCalendarManagement(models.TransientModel): @api.model def _generate_avalaibility_data(self, room_type, date, avail): - return { - 'id': avail and avail.id or False, - 'date': avail and avail.date or date, - 'no_ota': avail and avail.no_ota or False, - 'quota': avail and avail.quota or -1, - 'max_avail': avail and avail.max_avail or -1, - 'channel_avail': avail and avail.channel_bind_ids.channel_avail or room_type.channel_bind_ids.default_availability + avalaibility_data = { + 'id': False, + 'date': date, + 'no_ota': False, + 'quota': -1, # FIXED: `avail and avail.max_quota or -1` returns -1 if quota = 0 + 'max_avail': -1, + 'channel_avail': room_type.channel_bind_ids.default_availability } + if avail: + avalaibility_data = { + 'id': avail.id, + 'date': avail.date, + 'no_ota': avail.no_ota, + 'quota': avail.quota, + 'max_avail': avail.max_avail, + 'channel_avail': avail.channel_bind_ids.channel_avail + } + return avalaibility_data @api.model def _get_availability_values(self, vals): From d6a31db59e573651e38953dfe2a487063a5f4d68 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 22 Feb 2019 17:17:26 +0100 Subject: [PATCH 09/10] [IMP] UI calendar management --- .../lib/hcalendar/js/hcalendar_management.js | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js index aa76910cf..35d28cca1 100644 --- a/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js +++ b/hotel_calendar/static/src/lib/hcalendar/js/hcalendar_management.js @@ -200,6 +200,7 @@ HotelCalendarManagement.prototype = { row = table.insertRow(); row.setAttribute('name', 'price'); + cell = row.insertCell(); cell.setAttribute('colspan', '4'); telm = document.createElement("input"); @@ -219,7 +220,7 @@ HotelCalendarManagement.prototype = { row.style.display = 'none'; cell = row.insertCell(); - cell.setAttribute('colspan', '2'); + cell.setAttribute('colspan', '1'); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`QUOTA_${roomId}_${dateShortStr}`)); telm.setAttribute('name', 'quota'); @@ -244,6 +245,19 @@ HotelCalendarManagement.prototype = { telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); + cell.setAttribute('colspan', '1'); + telm = document.createElement("input"); + telm.setAttribute('id', this._sanitizeId(`FREE_ROOMS_${roomId}_${dateShortStr}`)); + telm.setAttribute('name', 'free_rooms'); + telm.setAttribute('type', 'edit'); + telm.setAttribute('title', this._t('Free Rooms')); + telm.setAttribute('readonly', 'readonly'); + telm.setAttribute('disabled', 'disabled'); + telm.style.backgroundColor = 'lightgray'; + telm.dataset.hcalParentCell = parentCell.getAttribute('id'); + cell.appendChild(telm); + cell = row.insertCell(); cell.setAttribute('colspan', '1'); telm = document.createElement("input"); @@ -253,14 +267,14 @@ HotelCalendarManagement.prototype = { telm.setAttribute('title', this._t('Channel Availability')); telm.setAttribute('readonly', 'readonly'); telm.setAttribute('disabled', 'disabled'); - telm.style.backgroundColor = 'lightgray'; - telm.value = telm.dataset.orgValue = 0; + telm.value = telm.dataset.orgValue = room.channel_avail; telm.dataset.hcalParentCell = parentCell.getAttribute('id'); telm.classList.add('hcal-management-input'); cell.appendChild(telm); row = table.insertRow(); row.setAttribute('name', 'rest_a'); + cell = row.insertCell(); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`MIN_STAY_${roomId}_${dateShortStr}`)); @@ -273,6 +287,7 @@ HotelCalendarManagement.prototype = { telm.classList.add('hcal-border-radius-left'); telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`MAX_STAY_${roomId}_${dateShortStr}`)); @@ -285,6 +300,7 @@ HotelCalendarManagement.prototype = { telm.classList.add('hcal-border-radius-right'); telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`MIN_STAY_ARRIVAL_${roomId}_${dateShortStr}`)); @@ -297,6 +313,7 @@ HotelCalendarManagement.prototype = { telm.classList.add('hcal-border-radius-left'); telm.addEventListener('change', function(ev){ $this.onInputChange(ev, this); }, false); cell.appendChild(telm); + cell = row.insertCell(); telm = document.createElement("input"); telm.setAttribute('id', this._sanitizeId(`MAX_STAY_ARRIVAL_${roomId}_${dateShortStr}`)); @@ -339,21 +356,10 @@ HotelCalendarManagement.prototype = { selectOpt.textContent = this._t("C. Arrival"); telm.appendChild(selectOpt); cell.appendChild(telm); - cell = row.insertCell(); - cell.setAttribute('colspan', '1'); - telm = document.createElement("input"); - telm.setAttribute('id', this._sanitizeId(`FREE_ROOMS_${roomId}_${dateShortStr}`)); - telm.setAttribute('name', 'free_rooms'); - telm.setAttribute('type', 'edit'); - telm.setAttribute('title', this._t('Free Rooms')); - telm.setAttribute('readonly', 'readonly'); - telm.setAttribute('disabled', 'disabled'); - telm.style.backgroundColor = 'lightgray'; - telm.dataset.hcalParentCell = parentCell.getAttribute('id'); - cell.appendChild(telm); row = table.insertRow(); row.setAttribute('name', 'rest_c'); + cell = row.insertCell(); cell.style.textAlign = 'center'; cell.setAttribute('colspan', '4'); @@ -745,7 +751,6 @@ HotelCalendarManagement.prototype = { `CHANNEL_AVAIL_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.channel_avail, `NO_OTA_${room_typeId}_${dd.format(HotelCalendarManagement._DATE_FORMAT_SHORT)}`, avail.no_ota ]; - for (var i=0; i Date: Fri, 22 Feb 2019 18:46:28 +0100 Subject: [PATCH 10/10] [ADD] unconfirmed channel price --- .../models/hotel_reservation/common.py | 2 ++ .../models/inherited_hotel_folio.py | 1 + .../models/hotel_reservation/importer.py | 18 ++++++++++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/hotel_channel_connector/models/hotel_reservation/common.py b/hotel_channel_connector/models/hotel_reservation/common.py index b362714d0..ad8ca9e2d 100644 --- a/hotel_channel_connector/models/hotel_reservation/common.py +++ b/hotel_channel_connector/models/hotel_reservation/common.py @@ -133,6 +133,8 @@ class ChannelHotelReservation(models.Model): class HotelReservation(models.Model): _inherit = 'hotel.reservation' + unconfirmed_channel_price = fields.Boolean(related='folio_id.unconfirmed_channel_price') + @api.multi def _set_access_for_channel_fields(self): for record in self: diff --git a/hotel_channel_connector/models/inherited_hotel_folio.py b/hotel_channel_connector/models/inherited_hotel_folio.py index e3f5fb0e4..503b8783d 100644 --- a/hotel_channel_connector/models/inherited_hotel_folio.py +++ b/hotel_channel_connector/models/inherited_hotel_folio.py @@ -19,6 +19,7 @@ class HotelFolio(models.Model): has_channel_reservations = fields.Boolean(compute=_has_channel_reservations, store=False, old_name='whas_wubook_reservations') + unconfirmed_channel_price = fields.Boolean(default=False) @job(default_channel='root.channel') @api.model diff --git a/hotel_channel_connector_wubook/models/hotel_reservation/importer.py b/hotel_channel_connector_wubook/models/hotel_reservation/importer.py index 7868f9c19..0fd195369 100644 --- a/hotel_channel_connector_wubook/models/hotel_reservation/importer.py +++ b/hotel_channel_connector_wubook/models/hotel_reservation/importer.py @@ -341,11 +341,21 @@ class HotelReservationImporter(Component): checkout_utc_dt, book, ) + # if vals['price_unit'] != book['amount']: - self.create_issue( - section='reservation', - internal_message="Invalid reservation total price! %.2f (calculated) != %.2f (wubook)" % (vals['price_unit'], book['amount']), - channel_object_id=book['reservation_code']) + bs = self.env['hotel.board.service.room.type'].browse(vals['board_service_room_id']) + price_room_services_set = vals['price_unit'] + (bs.amount * len(broom['roomdays'])) + vals.update({'unconfirmed_channel_price': True}) + # check if difference is owing to misconfigured board services + if price_room_services_set != book['amount']: + internal_reason = 'Please, review the board services included in the reservation.' + self.create_issue( + section='reservation', + internal_message="Invalid reservation total price! %.2f (calculated) != %.2f (wubook) %s" % ( + vals['price_unit'], book['amount'], internal_reason), + channel_object_id=book['reservation_code']) + # TODO: Add other reasons in case of need + free_rooms = room_type_bind.odoo_id.check_availability_room_type( vals['checkin'],