From 044640990d6fc9e79a94e36562fe266cb8073867 Mon Sep 17 00:00:00 2001 From: QS5ELkMu Date: Tue, 6 Nov 2018 13:37:22 +0100 Subject: [PATCH] [WIP] Channel Connector: Pricelist Items --- hotel_channel_connector/components/core.py | 12 -- hotel_channel_connector/data/menus.xml | 6 - hotel_channel_connector/models/__init__.py | 1 - .../models/channel_backend/common.py | 13 +- .../models/channel_binding/common.py | 12 ++ .../models/channel_ota_info/common.py | 10 +- .../models/channel_ota_info/importer.py | 47 +++---- .../models/hotel_room_type/common.py | 37 +++++- .../models/hotel_room_type/exporter.py | 67 ++++------ .../models/hotel_room_type/importer.py | 61 +++------ .../hotel_room_type_availability/common.py | 32 ++++- .../hotel_room_type_availability/exporter.py | 38 ++---- .../hotel_room_type_availability/importer.py | 80 ++++++------ .../hotel_room_type_restriction/common.py | 37 +++++- .../hotel_room_type_restriction/exporter.py | 37 +----- .../hotel_room_type_restriction/importer.py | 45 +++---- .../common.py | 31 +++-- .../exporter.py | 50 +++----- .../importer.py | 27 ++-- .../models/product_pricelist/common.py | 37 +++++- .../models/product_pricelist/exporter.py | 36 +----- .../models/product_pricelist/importer.py | 38 +++--- .../models/product_pricelist_item/__init__.py | 2 + .../models/product_pricelist_item/common.py | 72 ++++++++++- .../models/product_pricelist_item/exporter.py | 106 ++++++++++++++++ .../models/product_pricelist_item/importer.py | 120 ++++++++++++++++++ hotel_channel_connector/models/res_config.py | 32 ----- .../views/channel_connector_backend_views.xml | 22 +++- .../views/channel_ota_info_views.xml | 7 - ...inherited_product_pricelist_item_views.xml | 1 + 30 files changed, 686 insertions(+), 430 deletions(-) create mode 100644 hotel_channel_connector/models/product_pricelist_item/exporter.py create mode 100644 hotel_channel_connector/models/product_pricelist_item/importer.py delete mode 100644 hotel_channel_connector/models/res_config.py diff --git a/hotel_channel_connector/components/core.py b/hotel_channel_connector/components/core.py index 629108ea3..0400e7fba 100644 --- a/hotel_channel_connector/components/core.py +++ b/hotel_channel_connector/components/core.py @@ -9,18 +9,6 @@ class BaseHotelChannelConnectorComponent(AbstractComponent): _inherit = 'base.connector' _collection = 'channel.backend' - @api.model - def create_issue(self, **kwargs): - self.env['hotel.channel.connector.issue'].sudo().create({ - 'backend_id': kwargs.get('backend', False), - 'section': kwargs.get('section', False), - 'internal_message': kwargs.get('internal_message', False), - 'channel_object_id': kwargs.get('channel_object_id', False), - 'channel_message': kwargs.get('channel_message', False), - 'date_start': kwargs.get('dfrom', False), - 'date_end': kwargs.get('dto', False), - }) - class ChannelConnectorError(Exception): def __init__(self, message, data): super().__init__(message) diff --git a/hotel_channel_connector/data/menus.xml b/hotel_channel_connector/data/menus.xml index 06b13e837..3b6264619 100644 --- a/hotel_channel_connector/data/menus.xml +++ b/hotel_channel_connector/data/menus.xml @@ -13,12 +13,6 @@ parent="menu_channel_connector_root" action="action_channel_backend"/> - - dto_dt: dfrom_dt, dto_dt = dto_dt, dfrom_dt - try: - results = self.backend_adapter.fetch_rooms_values( - dfrom_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), - dto_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), - rooms) - self._generate_room_values(dfrom, dto, results, - set_max_avail=set_max_avail) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='room', - internal_message=_("Can't fetch rooms values from WuBook"), - channel_message=err.data['message'], dfrom=dfrom, dto=dto) - return False - return True + results = self.backend_adapter.fetch_rooms_values( + dfrom_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), + dto_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), + rooms) + self._generate_room_values(dfrom, dto, results, + set_max_avail=set_max_avail) @api.model def _map_room_values_availability(self, day_vals, set_max_avail): 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 9a4f78e87..4fb1ea017 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/common.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/common.py @@ -8,6 +8,7 @@ from odoo.exceptions import ValidationError from odoo.addons.queue_job.job import job, related_action from odoo.addons.component.core import Component from odoo.addons.component_event import skip_if +from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo.addons.hotel_channel_connector.components.backend_adapter import ( DEFAULT_WUBOOK_DATE_FORMAT) @@ -47,21 +48,42 @@ class ChannelHotelRoomTypeAvailability(models.Model): def update_availability(self, backend): with backend.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.availability.exporter') - return exporter.update_availability(self) + try: + return exporter.update_availability(self) + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='avail', + internal_message=_("Can't update availability in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @api.model def import_availability(self, backend): with backend.work_on(self._name) as work: importer = work.component(usage='hotel.room.type.availability.importer') - return importer.get_availability(backend.avail_from, backend.avail_to) + try: + return importer.get_availability(backend.avail_from, backend.avail_to) + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='avail', + internal_message=_("Can't import availability from WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @api.model def push_availability(self, backend): with backend.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.availability.exporter') - return exporter.push_availability() + try: + return exporter.push_availability() + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='avail', + internal_message=_("Can't update availability in WuBook"), + channel_message=err.data['message']) class HotelRoomTypeAvailability(models.Model): _inherit = 'hotel.room.type.availability' @@ -171,10 +193,6 @@ class ChannelBindingHotelRoomTypeAvailabilityListener(Component): _inherit = 'base.connector.listener' _apply_on = ['channel.hotel.room.type.availability'] - @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) - def on_record_create(self, record, fields=None): - record.channel_pushed = False - @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): if 'avail' in fields: diff --git a/hotel_channel_connector/models/hotel_room_type_availability/exporter.py b/hotel_channel_connector/models/hotel_room_type_availability/exporter.py index 589d7d27a..3c5cfae81 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/exporter.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/exporter.py @@ -18,24 +18,17 @@ class HotelRoomTypeAvailabilityExporter(Component): @api.model def update_availability(self, binding): if any(binding.room_type_id.channel_bind_ids): - try: - sday_dt = fields.Date.from_string(binding.date) - # FIXME: Supossed that only exists one channel connector per record - binding.channel_pushed = True - return self.backend_adapter.update_availability({ - 'id': binding.room_type_id.channel_bind_ids[0].channel_room_id, - 'days': [{ - 'date': sday_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), - 'avail': binding.avail, - 'no_ota': binding.no_ota, - }], - }) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='avail', - internal_message=_("Can't update availability in WuBook"), - channel_message=err.data['message']) + sday_dt = fields.Date.from_string(binding.date) + # FIXME: Supossed that only exists one channel connector per record + binding.channel_pushed = True + return self.backend_adapter.update_availability({ + 'id': binding.room_type_id.channel_bind_ids[0].channel_room_id, + 'days': [{ + 'date': sday_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT), + 'avail': binding.avail, + 'no_ota': binding.no_ota, + }], + }) def push_availability(self): channel_room_type_avail_ids = self.env['channel.hotel.room.type.availability'].search([ @@ -66,11 +59,4 @@ class HotelRoomTypeAvailabilityExporter(Component): _logger.info("UPDATING AVAILABILITY IN WUBOOK...") _logger.info(avails) if any(avails): - try: - self.backend_adapter.update_availability(avails) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='avail', - internal_message=_("Can't update availability in WuBook"), - channel_message=err.data['message']) + self.backend_adapter.update_availability(avails) diff --git a/hotel_channel_connector/models/hotel_room_type_availability/importer.py b/hotel_channel_connector/models/hotel_room_type_availability/importer.py index 843fdd678..a10301fd5 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/importer.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/importer.py @@ -6,7 +6,7 @@ from datetime import date, timedelta from odoo.exceptions import ValidationError from odoo.addons.component.core import Component from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError -from odoo.addons.connector.components.mapper import mapping, external_to_m2o +from odoo.addons.connector.components.mapper import mapping, only_create from odoo.addons.hotel import date_utils from odoo import fields, api, _ _logger = logging.getLogger(__name__) @@ -29,47 +29,40 @@ class HotelRoomTypeAvailabilityImporter(Component): dfrom_dt, dto_dt = dto_dt, dfrom_dt if dto_dt < now_dt: return True - count = 0 - try: - results = self.backend_adapter.fetch_rooms_values(date_from, date_to) - channel_room_type_avail_obj = self.env['channel.hotel.room.type.availability'] - channel_room_type_obj = self.env['channel.hotel.room.type'] - room_avail_mapper = self.component( - usage='import.mapper', - model_name='channel.hotel.room.type.availability') - count = len(results) - for room_k, room_v in results.items(): - iter_day = dfrom_dt - channel_room_type = channel_room_type_obj.search([ - ('channel_room_id', '=', room_k) - ], limit=1) - if channel_room_type: - for room in room_v: - room.update({ - 'room_type_id': channel_room_type.odoo_id.id, - 'date': fields.Date.to_string(iter_day), - }) - map_record = room_avail_mapper.map_record(room) - room_type_avail_bind = channel_room_type_avail_obj.search([ - ('room_type_id', '=', room['room_type_id']), - ('date', '=', room['date']) - ], limit=1) - if room_type_avail_bind: - room_type_avail_bind.with_context({ - 'wubook_action': False - }).write(map_record.values()) - else: - room_type_avail_bind = channel_room_type_avail_obj.with_context({ - 'wubook_action': False - }).create(map_record.values(for_create=True)) - iter_day += timedelta(days=1) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='avail', - internal_message=_("Can't import availability from WuBook"), - channel_message=err.data['message']) + results = self.backend_adapter.fetch_rooms_values(date_from, date_to) + + channel_room_type_avail_obj = self.env['channel.hotel.room.type.availability'] + channel_room_type_obj = self.env['channel.hotel.room.type'] + room_avail_mapper = self.component( + usage='import.mapper', + model_name='channel.hotel.room.type.availability') + count = len(results) + for room_k, room_v in results.items(): + iter_day = dfrom_dt + channel_room_type = channel_room_type_obj.search([ + ('channel_room_id', '=', room_k) + ], limit=1) + if channel_room_type: + for room in room_v: + room.update({ + 'room_type_id': channel_room_type.odoo_id.id, + 'date': fields.Date.to_string(iter_day), + }) + map_record = room_avail_mapper.map_record(room) + room_type_avail_bind = channel_room_type_avail_obj.search([ + ('room_type_id', '=', room['room_type_id']), + ('date', '=', room['date']) + ], limit=1) + if room_type_avail_bind: + room_type_avail_bind.with_context({ + 'wubook_action': False + }).write(map_record.values()) + else: + room_type_avail_bind = channel_room_type_avail_obj.with_context({ + 'wubook_action': False + }).create(map_record.values(for_create=True)) + iter_day += timedelta(days=1) return count @@ -85,6 +78,11 @@ class HotelRoomTypeAvailabilityImportMapper(Component): ('date', 'date'), ] + @only_create + @mapping + def channel_pushed(self, record): + return {'channel_pushed': True} + @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} diff --git a/hotel_channel_connector/models/hotel_room_type_restriction/common.py b/hotel_channel_connector/models/hotel_room_type_restriction/common.py index d0b5908d6..fef19def4 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/common.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/common.py @@ -7,6 +7,7 @@ from odoo.exceptions import ValidationError from odoo.addons.queue_job.job import job, related_action from odoo.addons.component.core import Component from odoo.addons.component_event import skip_if +from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError _logger = logging.getLogger(__name__) class ChannelHotelRoomTypeRestriction(models.Model): @@ -28,7 +29,14 @@ class ChannelHotelRoomTypeRestriction(models.Model): if not self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.restriction.exporter') - exporter.create_rplan(self) + try: + exporter.create_rplan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't create restriction plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -38,7 +46,14 @@ class ChannelHotelRoomTypeRestriction(models.Model): if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.restriction.exporter') - exporter.rename_rplan(self) + try: + exporter.rename_rplan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't modify restriction plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -48,14 +63,28 @@ class ChannelHotelRoomTypeRestriction(models.Model): if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.restriction.exporter') - exporter.delete_rplan(self) + try: + exporter.delete_rplan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't delete restriction plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @api.model def import_restriction_plans(self, backend): with backend.work_on(self._name) as work: importer = work.component(usage='hotel.room.type.restriction.importer') - return importer.import_restriction_plans() + try: + return importer.import_restriction_plans() + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='restriction', + internal_message=_("Can't fetch restriction plans from wubook"), + channel_message=err.data['message']) class HotelRoomTypeRestriction(models.Model): _inherit = 'hotel.room.type.restriction' diff --git a/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py b/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py index b60878830..9348347d3 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py @@ -3,7 +3,6 @@ import logging from odoo.addons.component.core import Component -from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo import api, _ _logger = logging.getLogger(__name__) @@ -15,38 +14,16 @@ class HotelRoomTypeRestrictionExporter(Component): @api.model def rename_rplan(self, binding): - try: - return self.backend_adapter.rename_rplan( - binding.external_id, - binding.name) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't modify restriction plan in WuBook"), - channel_message=err.data['message']) + return self.backend_adapter.rename_rplan( + binding.external_id, + binding.name) @api.model def delete_rplan(self, binding): - try: - return self.backend_adapter.delete_rplan(binding.external_id) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't delete restriction plan in WuBook"), - channel_message=err.data['message']) + return self.backend_adapter.delete_rplan(binding.external_id) @api.model def create_rplan(self, binding): - try: - external_id = self.backend_adapter.create_rplan(binding.name) - binding.external_id = external_id - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't create restriction plan in WuBook"), - channel_message=err.data['message']) - else: - self.binder.bind(external_id, binding) + external_id = self.backend_adapter.create_rplan(binding.name) + binding.external_id = external_id + self.binder.bind(external_id, binding) diff --git a/hotel_channel_connector/models/hotel_room_type_restriction/importer.py b/hotel_channel_connector/models/hotel_room_type_restriction/importer.py index ac687dbca..c20db4ce0 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/importer.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/importer.py @@ -5,7 +5,6 @@ import logging from datetime import timedelta from odoo.exceptions import ValidationError from odoo.addons.component.core import Component -from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo.addons.connector.components.mapper import mapping from odoo.addons.hotel import date_utils from odoo import fields, api, _ @@ -20,32 +19,24 @@ class HotelRoomTypeRestrictionImporter(Component): @api.model def import_restriction_plans(self): - count = 0 - try: - results = self.backend_adapter.rplan_rplans() - channel_restriction_obj = self.env['channel.hotel.room.type.restriction'] - restriction_mapper = self.component(usage='import.mapper', - model_name='channel.hotel.room.type.restriction') - for plan in results: - plan_record = restriction_mapper.map_record(plan) - plan_bind = channel_restriction_obj.search([ - ('external_id', '=', str(plan['id'])) - ], limit=1) - if not plan_bind: - channel_restriction_obj.with_context({ - 'wubook_action': False, - 'rules': plan.get('rules'), - }).create(plan_record.values(for_create=True)) - else: - plan_bind.with_context({'wubook_action': False}).write( - plan_record.values()) - count = count + 1 - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't fetch restriction plans from wubook"), - channel_message=err.data['message']) + results = self.backend_adapter.rplan_rplans() + channel_restriction_obj = self.env['channel.hotel.room.type.restriction'] + restriction_mapper = self.component(usage='import.mapper', + model_name='channel.hotel.room.type.restriction') + for plan in results: + plan_record = restriction_mapper.map_record(plan) + plan_bind = channel_restriction_obj.search([ + ('external_id', '=', str(plan['id'])) + ], limit=1) + if not plan_bind: + channel_restriction_obj.with_context({ + 'wubook_action': False, + 'rules': plan.get('rules'), + }).create(plan_record.values(for_create=True)) + else: + plan_bind.with_context({'wubook_action': False}).write( + plan_record.values()) + count = count + 1 return count diff --git a/hotel_channel_connector/models/hotel_room_type_restriction_item/common.py b/hotel_channel_connector/models/hotel_room_type_restriction_item/common.py index 5377b8cef..c57c8db29 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction_item/common.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction_item/common.py @@ -6,6 +6,7 @@ from odoo.exceptions import ValidationError from odoo.addons.queue_job.job import job, related_action from odoo.addons.component.core import Component from odoo.addons.component_event import skip_if +from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError class ChannelHotelRoomTypeRestrictionItem(models.Model): _name = 'channel.hotel.room.type.restriction.item' @@ -25,17 +26,33 @@ class ChannelHotelRoomTypeRestrictionItem(models.Model): def import_restriction_values(self, backend): with backend.work_on(self._name) as work: importer = work.component(usage='hotel.room.type.restriction.item.importer') - return importer.import_restriction_values( - backend.restriction_from, - backend.restriction_to, - channel_restr_id=backend.restriction_id) + try: + return importer.import_restriction_values( + backend.restriction_from, + backend.restriction_to, + channel_restr_id=backend.restriction_id) + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='restriction', + internal_message=_("Can't fetch plan restrictions from wubook"), + channel_message=err.data['message'], + channel_object_id=backend.restriction_id, + dfrom=backend.restriction_from, dto=backend.restriction_to) @job(default_channel='root.channel') @api.model def push_restriction(self, backend): with backend.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.restriction.item.exporter') - return exporter.push_restriction() + try: + return exporter.push_restriction() + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='restriction', + internal_message=_("Can't update restrictions in WuBook"), + channel_message=err.data['message']) class HotelRoomTypeRestrictionItem(models.Model): _inherit = 'hotel.room.type.restriction.item' @@ -75,10 +92,6 @@ class ChannelBindingHotelRoomTypeRestrictionItemListener(Component): _inherit = 'base.connector.listener' _apply_on = ['channel.hotel.room.type.restriction.item'] - @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) - def on_record_create(self, record, fields=None): - record.channel_pushed = False - @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): fields_to_check = ('min_stay', 'min_stay_arrival', 'max_stay', 'max_stay_arrival', diff --git a/hotel_channel_connector/models/hotel_room_type_restriction_item/exporter.py b/hotel_channel_connector/models/hotel_room_type_restriction_item/exporter.py index b8788aaf1..ac44d4c58 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction_item/exporter.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction_item/exporter.py @@ -20,27 +20,20 @@ class HotelRoomTypeRestrictionItemExporter(Component): @api.model def update_restriction(self, binding): if any(binding.restriction_id.channel_bind_ids): - try: - # FIXME: Supossed that only exists one channel connector per record - binding.channel_pushed = True - return self.backend_adapter.update_rplan_values( - binding.restriction_id.channel_bind_ids[0].external_id, - binding.date, - { - 'min_stay': binding.min_stay or 0, - 'min_stay_arrival': binding.min_stay_arrival or 0, - 'max_stay': binding.max_stay or 0, - 'max_stay_arrival': binding.max_stay_arrival or 0, - 'closed': binding.closed and 1 or 0, - 'closed_arrival': binding.closed_arrival and 1 or 0, - 'closed_departure': binding.closed_departure and 1 or 0, - }) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't update restriction in WuBook"), - channel_message=err.data['message']) + # FIXME: Supossed that only exists one channel connector per record + binding.channel_pushed = True + return self.backend_adapter.update_rplan_values( + binding.restriction_id.channel_bind_ids[0].external_id, + binding.date, + { + 'min_stay': binding.min_stay or 0, + 'min_stay_arrival': binding.min_stay_arrival or 0, + 'max_stay': binding.max_stay or 0, + 'max_stay_arrival': binding.max_stay_arrival or 0, + 'closed': binding.closed and 1 or 0, + 'closed_arrival': binding.closed_arrival and 1 or 0, + 'closed_departure': binding.closed_departure and 1 or 0, + }) @api.model def push_restriction(self): @@ -91,17 +84,10 @@ class HotelRoomTypeRestrictionItemExporter(Component): _logger.info(restrictions) for k_res, v_res in restrictions.items(): if any(v_res): - try: - self.backend_adapter.update_rplan_values( - int(k_res), - date_start.strftime(DEFAULT_SERVER_DATE_FORMAT), - v_res) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't update restrictions in WuBook"), - channel_message=err.data['message']) + self.backend_adapter.update_rplan_values( + int(k_res), + date_start.strftime(DEFAULT_SERVER_DATE_FORMAT), + v_res) unpushed.with_context({ 'wubook_action': False}).write({'channel_pushed': True}) return True diff --git a/hotel_channel_connector/models/hotel_room_type_restriction_item/importer.py b/hotel_channel_connector/models/hotel_room_type_restriction_item/importer.py index 426e11028..4c6a925b8 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction_item/importer.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction_item/importer.py @@ -66,21 +66,12 @@ class HotelRoomTypeRestrictionImporter(Component): @api.model def import_restriction_values(self, date_from, date_to, channel_restr_id=False): channel_restr_plan_id = channel_restr_id.external_id if channel_restr_id else False - try: - results = self.backend_adapter.wired_rplan_get_rplan_values( - date_from, - date_to, - int(channel_restr_plan_id)) - if any(results): - self._generate_restriction_items(results) - except ChannelConnectorError as err: - self.create_issue( - section='restriction', - internal_message=_("Can't fetch plan restrictions from wubook"), - channel_message=err.data['message'], - channel_object_id=channel_restr_plan_id, - dfrom=date_from, dto=date_to) - return False + results = self.backend_adapter.wired_rplan_get_rplan_values( + date_from, + date_to, + int(channel_restr_plan_id)) + if any(results): + self._generate_restriction_items(results) class HotelRoomTypeRestrictionItemImportMapper(Component): _name = 'channel.hotel.room.type.restriction.item.import.mapper' @@ -103,6 +94,12 @@ class HotelRoomTypeRestrictionItemImportMapper(Component): def applied_on(self, record): return {'applied_on': '0_room_type'} + @only_create + @mapping + def channel_pushed(self, record): + return {'channel_pushed': True} + + @mapping def room_type_id(self, record): return {'room_type_id': record['room_type_id']} diff --git a/hotel_channel_connector/models/product_pricelist/common.py b/hotel_channel_connector/models/product_pricelist/common.py index 775bd698a..9f45630da 100644 --- a/hotel_channel_connector/models/product_pricelist/common.py +++ b/hotel_channel_connector/models/product_pricelist/common.py @@ -6,6 +6,7 @@ from odoo.exceptions import ValidationError from odoo.addons.queue_job.job import job, related_action from odoo.addons.component.core import Component from odoo.addons.component_event import skip_if +from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError class ChannelProductPricelist(models.Model): _name = 'channel.product.pricelist' @@ -27,7 +28,14 @@ class ChannelProductPricelist(models.Model): if not self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='product.pricelist.exporter') - exporter.create_plan(self) + try: + exporter.create_plan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't create pricelist plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -37,7 +45,14 @@ class ChannelProductPricelist(models.Model): if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='product.pricelist.exporter') - exporter.rename_plan(self) + try: + exporter.rename_plan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't modify pricelist plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -47,14 +62,28 @@ class ChannelProductPricelist(models.Model): if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='product.pricelist.exporter') - exporter.delete_plan(self) + try: + exporter.delete_plan(self) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_id.id, + section='restriction', + internal_message=_("Can't delete pricelist plan in WuBook"), + channel_message=err.data['message']) @job(default_channel='root.channel') @api.model def import_price_plans(self, backend): with backend.work_on(self._name) as work: importer = work.component(usage='product.pricelist.importer') - return importer.import_pricing_plans() + try: + return importer.import_pricing_plans() + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='pricelist', + internal_message=_("Can't get pricing plans from wubook"), + channel_message=err.data['message']) class ProductPricelist(models.Model): _inherit = 'product.pricelist' diff --git a/hotel_channel_connector/models/product_pricelist/exporter.py b/hotel_channel_connector/models/product_pricelist/exporter.py index 45b028d84..fb990cfb2 100644 --- a/hotel_channel_connector/models/product_pricelist/exporter.py +++ b/hotel_channel_connector/models/product_pricelist/exporter.py @@ -3,7 +3,6 @@ import logging from odoo.addons.component.core import Component -from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo import api, _ _logger = logging.getLogger(__name__) @@ -15,38 +14,15 @@ class ProductPricelistExporter(Component): @api.model def rename_plan(self, binding): - try: - return self.backend_adapter.rename_plan( - binding.external_id, - binding.name) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't modify pricelist plan in WuBook"), - channel_message=err.data['message']) + return self.backend_adapter.rename_plan( + binding.external_id, + binding.name) @api.model def delete_plan(self, binding): - try: - return self.backend_adapter.delete_plan(binding.external_id) - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't delete pricelist plan in WuBook"), - channel_message=err.data['message']) + return self.backend_adapter.delete_plan(binding.external_id) @api.model def create_plan(self, binding): - try: - external_id = self.backend_adapter.create_plan(binding.name) - binding.external_id = external_id - except ChannelConnectorError as err: - self.create_issue( - backend=self.backend_adapter.id, - section='restriction', - internal_message=_("Can't create pricelist plan in WuBook"), - channel_message=err.data['message']) - else: - self.binder.bind(external_id, binding) + external_id = self.backend_adapter.create_plan(binding.name) + binding.external_id = external_id diff --git a/hotel_channel_connector/models/product_pricelist/importer.py b/hotel_channel_connector/models/product_pricelist/importer.py index 74dabe6e8..c99add698 100644 --- a/hotel_channel_connector/models/product_pricelist/importer.py +++ b/hotel_channel_connector/models/product_pricelist/importer.py @@ -4,7 +4,6 @@ import logging from datetime import datetime, timedelta from odoo.addons.component.core import Component -from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo.addons.connector.components.mapper import mapping, only_create from odoo.addons.hotel_channel_connector.components.backend_adapter import ( DEFAULT_WUBOOK_DATE_FORMAT) @@ -24,32 +23,25 @@ class ProductPricelistImporter(Component): channel_product_listprice_obj = self.env['channel.product.pricelist'] pricelist_mapper = self.component(usage='import.mapper', model_name='channel.product.pricelist') + results = self.backend_adapter.get_pricing_plans() count = 0 - try: - results = self.backend_adapter.get_pricing_plans() - for plan in results: - if 'vpid' in plan: - continue # FIXME: Ignore Virtual Plans - plan_record = pricelist_mapper.map_record(plan) - plan_bind = channel_product_listprice_obj.search([ - ('external_id', '=', str(plan['id'])) - ], limit=1) - if not plan_bind: - channel_product_listprice_obj.with_context({ - 'wubook_action': False}).create(plan_record.values(for_create=True)) - else: - channel_product_listprice_obj.write(plan_record.values()) - count = count + 1 - except ChannelConnectorError as err: - self.create_issue( - section='pricelist', - internal_message=_("Can't get pricing plans from wubook"), - channel_message=err.data['message']) - return 0 + for plan in results: + if 'vpid' in plan: + continue # FIXME: Ignore Virtual Plans + plan_record = pricelist_mapper.map_record(plan) + plan_bind = channel_product_listprice_obj.search([ + ('external_id', '=', str(plan['id'])) + ], limit=1) + if not plan_bind: + channel_product_listprice_obj.with_context({ + 'wubook_action': False}).create(plan_record.values(for_create=True)) + else: + channel_product_listprice_obj.write(plan_record.values()) + count = count + 1 return count -class ProductPricelistMapper(Component): +class ProductPricelistImportMapper(Component): _name = 'channel.product.pricelist.import.mapper' _inherit = 'channel.import.mapper' _apply_on = 'channel.product.pricelist' diff --git a/hotel_channel_connector/models/product_pricelist_item/__init__.py b/hotel_channel_connector/models/product_pricelist_item/__init__.py index 257ab04fc..fe02f8e98 100644 --- a/hotel_channel_connector/models/product_pricelist_item/__init__.py +++ b/hotel_channel_connector/models/product_pricelist_item/__init__.py @@ -2,3 +2,5 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import common +from . import importer +from . import exporter diff --git a/hotel_channel_connector/models/product_pricelist_item/common.py b/hotel_channel_connector/models/product_pricelist_item/common.py index f80d542c5..03457d231 100644 --- a/hotel_channel_connector/models/product_pricelist_item/common.py +++ b/hotel_channel_connector/models/product_pricelist_item/common.py @@ -6,6 +6,7 @@ from odoo.exceptions import ValidationError from odoo.addons.queue_job.job import job, related_action from odoo.addons.component.core import Component from odoo.addons.component_event import skip_if +from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError class ChannelProductPricelistItem(models.Model): _name = 'channel.product.pricelist.item' @@ -14,13 +15,44 @@ class ChannelProductPricelistItem(models.Model): _description = 'Channel Product Pricelist Item' odoo_id = fields.Many2one(comodel_name='product.pricelist.item', - string='Pricelist Item', + string='Hotel Product Pricelist Item', required=True, ondelete='cascade') - channel_pushed = fields.Boolean("Channel Pushed", readonly=True, default=False, old_name='wpushed') + @job(default_channel='root.channel') + @api.model + def import_pricelist_values(self, backend): + with backend.work_on(self._name) as work: + importer = work.component(usage='product.pricelist.item.importer') + try: + if not backend.pricelist_id: + return importer.import_all_pricelist_values( + backend.pricelist_from, + backend.pricelist_to) + return importer.import_pricelist_values( + backend.pricelist_id.external_id, + backend.pricelist_from, + backend.pricelist_to) + except ChannelConnectorError as err: + self.create_issue( + backend=backend.id, + section='pricelist', + internal_message="Can't fetch plan prices from wubook!", + channel_message=err.data['message'], + channel_object_id=backend.pricelist_id.external_id, + dfrom=backend.pricelist_from, + dto=backend.pricelist_to) + return False + + @job(default_channel='root.channel') + @api.model + def push_pricelist(self, backend): + with backend.work_on(self._name) as work: + exporter = work.component(usage='product.pricelist.item.exporter') + return exporter.push_pricelist() + class ProductPricelistItem(models.Model): _inherit = 'product.pricelist.item' @@ -28,3 +60,39 @@ class ProductPricelistItem(models.Model): comodel_name='channel.product.pricelist.item', inverse_name='odoo_id', string='Hotel Channel Connector Bindings') + +class ProducrPricelistItemAdapter(Component): + _name = 'channel.product.pricelist.item.adapter' + _inherit = 'wubook.adapter' + _apply_on = 'channel.product.pricelist.item' + + def fetch_plan_prices(self, external_id, date_from, date_to, rooms): + return super(ProducrPricelistItemAdapter, self).fetch_plan_prices( + external_id, + date_from, + date_to, + rooms) + +class BindingProductPricelistItemListener(Component): + _name = 'binding.product.pricelist.item.listener' + _inherit = 'base.connector.listener' + _apply_on = ['product.pricelist.item'] + + @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) + def on_record_write(self, record, fields=None): + fields_to_check = ('date_start', 'date_end', 'fixed_price', 'product_tmpl_id') + fields_checked = [elm for elm in fields_to_check if elm in fields] + if any(fields_checked): + record.channel_bind_ids.write({'channel_pushed': False}) + +class ChannelBindingProductPricelistItemListener(Component): + _name = 'channel.binding.product.pricelist.item.listener' + _inherit = 'base.connector.listener' + _apply_on = ['channel.product.pricelist.item'] + + @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) + def on_record_write(self, record, fields=None): + fields_to_check = ('date_start', 'date_end', 'fixed_price', 'product_tmpl_id') + fields_checked = [elm for elm in fields_to_check if elm in fields] + if any(fields_checked): + record.channel_pushed = False diff --git a/hotel_channel_connector/models/product_pricelist_item/exporter.py b/hotel_channel_connector/models/product_pricelist_item/exporter.py new file mode 100644 index 000000000..1498a354b --- /dev/null +++ b/hotel_channel_connector/models/product_pricelist_item/exporter.py @@ -0,0 +1,106 @@ +# Copyright 2018 Alexandre Díaz +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import timedelta +from odoo.addons.component.core import Component +from odoo.addons.hotel_channel_connector.components.backend_adapter import ( + DEFAULT_WUBOOK_DATE_FORMAT) +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT +from odoo import fields, api, _ +_logger = logging.getLogger(__name__) + +class ProductPricelistItemExporter(Component): + _name = 'channel.product.pricelist.item.exporter' + _inherit = 'hotel.channel.exporter' + _apply_on = ['channel.product.pricelist.item'] + _usage = 'product.pricelist.item.exporter' + + @api.model + def update_restriction(self, binding): + if any(binding.restriction_id.channel_bind_ids): + try: + # FIXME: Supossed that only exists one channel connector per record + binding.channel_pushed = True + return self.backend_adapter.update_rplan_values( + binding.restriction_id.channel_bind_ids[0].external_id, + binding.date, + { + 'min_stay': binding.min_stay or 0, + 'min_stay_arrival': binding.min_stay_arrival or 0, + 'max_stay': binding.max_stay or 0, + 'max_stay_arrival': binding.max_stay_arrival or 0, + 'closed': binding.closed and 1 or 0, + 'closed_arrival': binding.closed_arrival and 1 or 0, + 'closed_departure': binding.closed_departure and 1 or 0, + }) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_adapter.id, + section='restriction', + internal_message=_("Can't update restriction in WuBook"), + channel_message=err.data['message']) + + @api.model + def push_restriction(self): + channel_room_type_rest_obj = self.env['channel.hotel.room.type.restriction'] + channel_rest_item_obj = self.env['channel.hotel.room.type.restriction.item'] + unpushed = channel_rest_item_obj.search([ + ('channel_pushed', '=', False), + ('date', '>=', fields.Date.today()) + ], order="date ASC") + if any(unpushed): + date_start = fields.Date.from_string(unpushed[0].date) + date_end = fields.Date.from_string(unpushed[-1].date) + days_diff = (date_end-date_start).days + 1 + restrictions = {} + channel_restr_plan_ids = channel_room_type_rest_obj.search([]) + for rp in channel_restr_plan_ids: + restrictions.update({rp.external_id: {}}) + unpushed_rp = channel_rest_item_obj.search([ + ('channel_pushed', '=', False), + ('restriction_id', '=', rp.odoo_id.id) + ]) + room_type_ids = unpushed_rp.mapped('room_type_id') + for room_type in room_type_ids: + if any(room_type.channel_bind_ids): + # FIXME: Supossed that only exists one channel connector per record + room_type_external_id = room_type.channel_bind_ids[0].external_id + restrictions[rp.external_id].update({ + room_type_external_id: [], + }) + for i in range(0, days_diff): + ndate_dt = date_start + timedelta(days=i) + restr = room_type.get_restrictions( + ndate_dt.strftime(DEFAULT_SERVER_DATE_FORMAT), + rp.odoo_id.id) + if restr: + restrictions[rp.external_id][room_type_external_id].append({ + 'min_stay': restr.min_stay or 0, + 'min_stay_arrival': restr.min_stay_arrival or 0, + 'max_stay': restr.max_stay or 0, + 'max_stay_arrival': restr.max_stay_arrival or 0, + 'closed': restr.closed and 1 or 0, + 'closed_arrival': restr.closed_arrival and 1 or 0, + 'closed_departure': restr.closed_departure and 1 or 0, + }) + else: + restrictions[rp.external_id][room_type_external_id].append({}) + _logger.info("==[ODOO->CHANNEL]==== UPDATING RESTRICTIONS ==") + _logger.info(restrictions) + for k_res, v_res in restrictions.items(): + if any(v_res): + try: + self.backend_adapter.update_rplan_values( + int(k_res), + date_start.strftime(DEFAULT_SERVER_DATE_FORMAT), + v_res) + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_adapter.id, + section='restriction', + internal_message=_("Can't update restrictions in WuBook"), + channel_message=err.data['message']) + unpushed.with_context({ + 'wubook_action': False}).write({'channel_pushed': True}) + return True diff --git a/hotel_channel_connector/models/product_pricelist_item/importer.py b/hotel_channel_connector/models/product_pricelist_item/importer.py new file mode 100644 index 000000000..bb7bb30b7 --- /dev/null +++ b/hotel_channel_connector/models/product_pricelist_item/importer.py @@ -0,0 +1,120 @@ +# Copyright 2018 Alexandre Díaz +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import logging +from datetime import timedelta +from odoo.addons.component.core import Component +from odoo.addons.connector.components.mapper import mapping, only_create +from odoo.addons.hotel_channel_connector.components.backend_adapter import ( + DEFAULT_WUBOOK_DATE_FORMAT) +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT +from odoo import fields, api, _ +_logger = logging.getLogger(__name__) + + +class ProductPricelistItemImporter(Component): + _name = 'channel.product.pricelist.item.importer' + _inherit = 'hotel.channel.importer' + _apply_on = ['channel.product.pricelist.item'] + _usage = 'product.pricelist.item.importer' + + @api.model + def _generate_pricelist_items(self, channel_plan_id, date_from, date_to, plan_prices): + channel_hotel_room_type_obj = self.env['channel.hotel.room.type'] + pricelist_bind = self.env['channel.product.pricelist'].search([ + ('external_id', '=', channel_plan_id) + ], limit=1) + pricelist_item_mapper = self.component( + usage='import.mapper', + model_name='channel.product.pricelist.item') + if pricelist_bind: + channel_pricelist_item_obj = self.env['channel.product.pricelist.item'] + dfrom_dt = fields.Date.from_string(date_from) + dto_dt = fields.Date.from_string(date_to) + days_diff = (dto_dt-dfrom_dt).days + 1 + for i in range(0, days_diff): + ndate_dt = dfrom_dt + timedelta(days=i) + for k_rid, v_rid in plan_prices.items(): + channel_room_type = channel_hotel_room_type_obj.search([ + ('external_id', '=', k_rid) + ], limit=1) + if channel_room_type: + ndate_str = ndate_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) + item = { + 'price': plan_prices[k_rid][i], + 'channel_room_type': channel_room_type, + 'pricelist_id': pricelist_bind.odoo_id.id, + 'date': ndate_str, + } + map_record = pricelist_item_mapper.map_record(item) + pricelist_item = channel_pricelist_item_obj.search([ + ('pricelist_id', '=', pricelist_bind.odoo_id.id), + ('date_start', '=', ndate_str), + ('date_end', '=', ndate_str), + ('compute_price', '=', 'fixed'), + ('applied_on', '=', '1_product'), + ('product_tmpl_id', '=', + channel_room_type.product_id.product_tmpl_id.id) + ], limit=1) + if pricelist_item: + pricelist_item.with_context({ + 'wubook_action': False}).write(map_record.values()) + else: + channel_pricelist_item_obj.with_context({ + 'wubook_action': False}).create(map_record.values(for_create=True)) + return True + + @api.model + def import_all_pricelist_values(self, date_from, date_to, rooms=None): + external_ids = self.env['channel.product.pricelist'].search([]).mapped('external_id') + for external_id in external_ids: + if external_id: + self.import_pricelist_values(external_id, date_from, date_to, rooms=rooms) + return True + + @api.model + def import_pricelist_values(self, external_id, date_from, date_to, rooms=None): + results = self.backend_adapter.fetch_plan_prices( + external_id, + date_from, + date_to, + rooms) + self._generate_pricelist_items(external_id, date_from, date_to, results) + +class ProductPricelistItemImportMapper(Component): + _name = 'channel.product.pricelist.item.import.mapper' + _inherit = 'channel.import.mapper' + _apply_on = 'channel.product.pricelist.item' + + direct = [ + ('price', 'fixed_price'), + ('date', 'date_start'), + ('date', 'date_end'), + ] + + @only_create + @mapping + def compute_price(self, record): + return {'compute_price': 'fixed'} + + @only_create + @mapping + def channel_pushed(self, record): + return {'channel_pushed': True} + + @only_create + @mapping + def applied_on(self, record): + return {'applied_on': '1_product'} + + @mapping + def product_tmpl_id(self, record): + return {'product_tmpl_id': record['channel_room_type'].product_id.product_tmpl_id.id} + + @mapping + def pricelist_id(self, record): + return {'pricelist_id': record['pricelist_id']} + + @mapping + def backend_id(self, record): + return {'backend_id': self.backend_record.id} diff --git a/hotel_channel_connector/models/res_config.py b/hotel_channel_connector/models/res_config.py deleted file mode 100644 index 25de1266d..000000000 --- a/hotel_channel_connector/models/res_config.py +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2018 Alexandre Díaz -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from openerp import models, fields, api - - -class HotelConfiguration(models.TransientModel): - _inherit = 'res.config.settings' - - default_channel_connector = fields.Many2one( - 'channel.backend', - 'Default Channel Connector Backend') - - @api.multi - def set_values(self): - super(HotelConfiguration, self).set_values() - - self.env['ir.default'].sudo().set( - 'res.config.settings', 'default_channel_connector', - self.default_channel_connector.id) - - @api.model - def get_values(self): - res = super(HotelConfiguration, self).get_values() - - # ONLY FOR v11. DO NOT FORWARD-PORT - default_channel_connector = self.env['ir.default'].sudo().get( - 'res.config.settings', 'default_channel_connector') - - res.update( - default_channel_connector=default_channel_connector, - ) - return res diff --git a/hotel_channel_connector/views/channel_connector_backend_views.xml b/hotel_channel_connector/views/channel_connector_backend_views.xml index 2b947a5d0..76f7bd117 100644 --- a/hotel_channel_connector/views/channel_connector_backend_views.xml +++ b/hotel_channel_connector/views/channel_connector_backend_views.xml @@ -28,6 +28,8 @@ + +