From 19127e3091c5d2ad5d04437cc479c9d417353117 Mon Sep 17 00:00:00 2001 From: QS5ELkMu Date: Mon, 5 Nov 2018 15:23:48 +0100 Subject: [PATCH] [WIP] Channel Connector --- hotel_channel_connector/__manifest__.py | 2 + .../components/backend_adapter.py | 23 +++--- hotel_channel_connector/components/binder.py | 2 + hotel_channel_connector/components/core.py | 16 ++-- .../components/importer.py | 68 ++++++++-------- hotel_channel_connector/models/__init__.py | 2 +- .../models/channel_backend/common.py | 29 ++++++- .../models/channel_binding/common.py | 2 + .../models/channel_ota_info/importer.py | 6 +- .../models/hotel_channel_connector_issue.py | 9 ++- .../models/hotel_reservation/common.py | 1 - .../models/hotel_reservation/importer.py | 13 +-- .../models/hotel_room_type/common.py | 7 +- .../models/hotel_room_type/exporter.py | 24 ++++-- .../models/hotel_room_type/importer.py | 17 ++-- .../hotel_room_type_availability/common.py | 4 +- .../hotel_room_type_availability/exporter.py | 14 ++-- .../hotel_room_type_availability/importer.py | 7 +- .../hotel_room_type_restriction/__init__.py | 1 + .../hotel_room_type_restriction/common.py | 79 +++++++++++-------- .../hotel_room_type_restriction/exporter.py | 52 ++++++++++++ .../hotel_room_type_restriction/importer.py | 37 +++++++-- .../common.py | 27 ++++++- .../importer.py | 78 +++++++++++++++++- .../inherited_product_pricelist_item.py | 56 ------------- .../models/product_pricelist/common.py | 15 +++- .../models/product_pricelist_item/__init__.py | 4 + .../models/product_pricelist_item/common.py | 30 +++++++ .../views/channel_connector_backend_views.xml | 32 ++++++++ .../views/channel_hotel_reservation_views.xml | 2 +- ...nel_hotel_room_type_availability_views.xml | 4 + ...nnel_hotel_room_type_restriction_views.xml | 7 +- .../views/channel_hotel_room_type_views.xml | 3 +- .../views/channel_ota_info_views.xml | 4 + .../channel_product_pricelist_item_views.xml | 27 +++++++ .../views/channel_product_pricelist_views.xml | 8 +- .../hotel_channel_connector_issue_views.xml | 5 ++ .../inherited_hotel_reservation_views.xml | 2 +- ...ted_hotel_room_type_availability_views.xml | 2 +- ...hotel_room_type_restriction_item_views.xml | 24 ++++++ ...ited_hotel_room_type_restriction_views.xml | 18 +++-- .../views/inherited_hotel_room_type_views.xml | 2 +- ...inherited_product_pricelist_item_views.xml | 18 +++-- .../inherited_product_pricelist_views.xml | 21 +++-- 44 files changed, 579 insertions(+), 225 deletions(-) create mode 100644 hotel_channel_connector/models/hotel_room_type_restriction/exporter.py delete mode 100644 hotel_channel_connector/models/inherited_product_pricelist_item.py create mode 100644 hotel_channel_connector/models/product_pricelist_item/__init__.py create mode 100644 hotel_channel_connector/models/product_pricelist_item/common.py create mode 100644 hotel_channel_connector/views/channel_product_pricelist_item_views.xml create mode 100644 hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml diff --git a/hotel_channel_connector/__manifest__.py b/hotel_channel_connector/__manifest__.py index 8bb150279..77b4d51f9 100644 --- a/hotel_channel_connector/__manifest__.py +++ b/hotel_channel_connector/__manifest__.py @@ -29,6 +29,7 @@ 'views/inherited_product_pricelist_views.xml', 'views/inherited_product_pricelist_item_views.xml', 'views/inherited_hotel_room_type_restriction_views.xml', + 'views/inherited_hotel_room_type_restriction_item_views.xml', 'views/inherited_res_partner_views.xml', 'views/channel_ota_info_views.xml', 'views/hotel_channel_connector_issue_views.xml', @@ -37,6 +38,7 @@ 'views/channel_hotel_room_type_availability_views.xml', 'views/channel_hotel_room_type_restriction_views.xml', 'views/channel_product_pricelist_views.xml', + 'views/channel_product_pricelist_item_views.xml', 'views/channel_connector_backend_views.xml', 'data/menus.xml', 'data/sequences.xml', diff --git a/hotel_channel_connector/components/backend_adapter.py b/hotel_channel_connector/components/backend_adapter.py index e5b64afd7..930b1dd92 100644 --- a/hotel_channel_connector/components/backend_adapter.py +++ b/hotel_channel_connector/components/backend_adapter.py @@ -9,7 +9,6 @@ from odoo.tools import ( DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT) from odoo.addons.payment.models.payment_acquirer import _partner_split_name -from odoo.addons.hotel import date_utils from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError from odoo import fields, _ _logger = logging.getLogger(__name__) @@ -276,8 +275,8 @@ class WuBookAdapter(AbstractComponent): rcode, results = self._server.fetch_rooms_values( self._session_info[0], self._session_info[1], - date_utils.get_datetime(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), - date_utils.get_datetime(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), rooms) if rcode != 0: raise ChannelConnectorError("Can't fetch rooms values from WuBook", { @@ -317,14 +316,14 @@ class WuBookAdapter(AbstractComponent): 'phone': phone, 'street': address, 'country': country_code, - 'arrival_hour': date_utils.get_datetime(checkin).strftime("%H:%M"), + 'arrival_hour': fields.Datetime.from_string(checkin).strftime("%H:%M"), 'notes': notes } rcode, results = self._server.new_reservation( self._session_info[0], self._session_info[1], - date_utils.get_datetime(checkin).strftime(DEFAULT_WUBOOK_DATE_FORMAT), - date_utils.get_datetime(checkout).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(checkin).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(checkout).strftime(DEFAULT_WUBOOK_DATE_FORMAT), {channel_room_id: [adults+children, 'nb']}, customer, adults+children) @@ -431,7 +430,7 @@ class WuBookAdapter(AbstractComponent): self._session_info[0], self._session_info[1], channel_plan_id, - date_utils.get_datetime(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), prices) if rcode != 0: raise ChannelConnectorError("Can't update pricing plan in wubook", { @@ -469,8 +468,8 @@ class WuBookAdapter(AbstractComponent): self._session_info[0], self._session_info[1], channel_plan_id, - date_utils(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), - date_utils(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), rooms or []) if rcode != 0: raise ChannelConnectorError("Can't get pricing plans from wubook", { @@ -496,8 +495,8 @@ class WuBookAdapter(AbstractComponent): rcode, results = self._server.wired_rplan_get_rplan_values( self._session_info[0], self._session_info[1], - date_utils(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), - date_utils(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT), channel_restriction_plan_id) if rcode != 0: raise ChannelConnectorError("Can't fetch restriction plans from wubook", { @@ -513,7 +512,7 @@ class WuBookAdapter(AbstractComponent): self._session_info[0], self._session_info[1], channel_restriction_plan_id, - date_utils(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), + fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT), values) if rcode != 0: raise ChannelConnectorError("Can't update plan restrictions on wubook", { diff --git a/hotel_channel_connector/components/binder.py b/hotel_channel_connector/components/binder.py index 3276998d5..3ed074208 100644 --- a/hotel_channel_connector/components/binder.py +++ b/hotel_channel_connector/components/binder.py @@ -11,6 +11,8 @@ class HotelConnectorModelBinder(Component): 'channel.hotel.room.type', 'channel.hotel.room.type.availability', 'channel.hotel.room.type.restriction', + 'channel.hotel.room.type.restriction.item', 'channel.product.pricelist', + 'channel.product.pricelist.item', 'channel.ota.info', ] diff --git a/hotel_channel_connector/components/core.py b/hotel_channel_connector/components/core.py index 951c40996..629108ea3 100644 --- a/hotel_channel_connector/components/core.py +++ b/hotel_channel_connector/components/core.py @@ -10,15 +10,15 @@ class BaseHotelChannelConnectorComponent(AbstractComponent): _collection = 'channel.backend' @api.model - def create_issue(self, section, message, channel_message, channel_object_id=False, - dfrom=False, dto=False): + def create_issue(self, **kwargs): self.env['hotel.channel.connector.issue'].sudo().create({ - 'section': section, - 'internal_message': message, - 'channel_object_id': channel_object_id, - 'channel_message': channel_message, - 'date_start': dfrom, - 'date_end': dto, + '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): diff --git a/hotel_channel_connector/components/importer.py b/hotel_channel_connector/components/importer.py index dc94680dd..093a7b7f2 100644 --- a/hotel_channel_connector/components/importer.py +++ b/hotel_channel_connector/components/importer.py @@ -238,9 +238,9 @@ class HotelChannelConnectorImporter(AbstractComponent): # the same transaction an cancellation) if crcode in failed_reservations: self.create_issue( - 'reservation', - "Can't process a reservation that previusly failed!", - '', channel_object_id=book['reservation_code']) + section='reservation', + internal_emssage="Can't process a reservation that previusly failed!", + channel_object_id=book['reservation_code']) continue # Get dates for the reservation (GMT->UTC) @@ -342,10 +342,10 @@ class HotelChannelConnectorImporter(AbstractComponent): ], limit=1) if not room_type: self.create_issue( - 'reservation', - "Can't found any room type associated to '%s' \ - in this hotel" % book['rooms'], - '', channel_object_id=book['reservation_code']) + section='reservation', + internal_message="Can't found any room type associated to '%s' \ + in this hotel" % book['rooms'], + channel_object_id=book['reservation_code']) failed_reservations.append(crcode) continue @@ -376,9 +376,9 @@ class HotelChannelConnectorImporter(AbstractComponent): ) if vals['price_unit'] != book['amount']: self.create_issue( - 'reservation', - "Invalid reservation total price! %.2f != %.2f" % (vals['price_unit'], book['amount']), - '', channel_object_id=book['reservation_code']) + section='reservation', + internal_message="Invalid reservation total price! %.2f != %.2f" % (vals['price_unit'], book['amount']), + channel_object_id=book['reservation_code']) free_rooms = room_type.odoo_id.check_availability_room( checkin_str, @@ -439,9 +439,9 @@ class HotelChannelConnectorImporter(AbstractComponent): }) reservations.append((0, False, vals)) self.create_issue( - 'reservation', - "Reservation imported with overbooking state", - '', channel_object_id=rcode) + section='reservation', + internal_message="Reservation imported with overbooking state", + channel_object_id=rcode) dates_checkin = [False, False] dates_checkout = [False, False] split_booking = False @@ -458,9 +458,9 @@ class HotelChannelConnectorImporter(AbstractComponent): if split_booking: self.create_issue( - 'reservation', - "Reservation Splitted", - '', channel_object_id=rcode) + section='reservation', + internal_message="Reservation Splitted", + channel_object_id=rcode) # Create Folio if not any(failed_reservations) and any(reservations): @@ -498,9 +498,9 @@ class HotelChannelConnectorImporter(AbstractComponent): processed_rids.append(rcode) except ChannelConnectorError as err: self.create_issue( - 'reservation', - err.data['message'], - '', channel_object_id=rcode) + section='reservation', + internal_message=err.data['message'], + channel_object_id=rcode) failed_reservations.append(crcode) return (processed_rids, any(failed_reservations), checkin_utc_dt, checkout_utc_dt) @@ -692,9 +692,9 @@ class HotelChannelConnectorImporter(AbstractComponent): count = self._generate_pricelists(results) except ChannelConnectorError as err: self.create_issue( - 'plan', - _("Can't get pricing plans from wubook"), - err.data['message']) + section='plan', + internal_message=_("Can't get pricing plans from wubook"), + channel_message=err.data['message']) return 0 return count @@ -709,9 +709,9 @@ class HotelChannelConnectorImporter(AbstractComponent): self._generate_pricelist_items(channel_plan_id, date_from, date_to, results) except ChannelConnectorError as err: self.create_issue( - 'plan', - _("Can't fetch plan prices from wubook"), - err.data['message']) + section='plan', + internal_message=_("Can't fetch plan prices from wubook"), + channel_message=err.data['message']) return False return True @@ -732,9 +732,9 @@ class HotelChannelConnectorImporter(AbstractComponent): self._generate_pricelist_items(channel_plan_id, date_from, date_to, results) except ChannelConnectorError as err: self.create_issue( - 'plan', - "Can't fetch all plan prices from wubook!", - err.data['message'], + section='plan', + internal_message="Can't fetch all plan prices from wubook!", + channel_message=err.data['message'], channel_object_id=channel_plan_id, dfrom=date_from, dto=date_to) return False return no_errors @@ -746,9 +746,9 @@ class HotelChannelConnectorImporter(AbstractComponent): count = self._generate_restrictions(results) except ChannelConnectorError as err: self.create_issue( - 'rplan', - _("Can't fetch restriction plans from wubook"), - err.data['message']) + section='rplan', + internal_message=_("Can't fetch restriction plans from wubook"), + channel_message=err.data['message']) return 0 return count @@ -763,9 +763,9 @@ class HotelChannelConnectorImporter(AbstractComponent): self._generate_restriction_items(results) except ChannelConnectorError as err: self.create_issue( - 'rplan', - _("Can't fetch plan restrictions from wubook"), - err.data['message'], + section='rplan', + internal_message=_("Can't fetch plan restrictions from wubook"), + channel_message=err.data['message'], channel_object_id=channel_restriction_plan_id, dfrom=date_from, dto=date_to) return False diff --git a/hotel_channel_connector/models/__init__.py b/hotel_channel_connector/models/__init__.py index 622a159e4..b9ecc917a 100644 --- a/hotel_channel_connector/models/__init__.py +++ b/hotel_channel_connector/models/__init__.py @@ -5,7 +5,7 @@ from . import channel_binding from . import channel_backend from . import hotel_room_type from . import product_pricelist -from . import inherited_product_pricelist_item +from . import product_pricelist_item from . import hotel_room_type_restriction from . import hotel_room_type_restriction_item from . import hotel_room_type_availability diff --git a/hotel_channel_connector/models/channel_backend/common.py b/hotel_channel_connector/models/channel_backend/common.py index 34b30c0e5..1901bfb36 100644 --- a/hotel_channel_connector/models/channel_backend/common.py +++ b/hotel_channel_connector/models/channel_backend/common.py @@ -34,6 +34,16 @@ class ChannelBackend(models.Model): avail_from = fields.Date('Availability From') avail_to = fields.Date('Availability To') + restriction_from = fields.Date('Restriction From') + restriction_to = fields.Date('Restriction To') + restriction_id = fields.Many2one('channel.hotel.room.type.restriction', + 'Channel Restriction') + + issue_ids = fields.One2many('hotel.channel.connector.issue', + 'backend_id', + string='Issues', + copy=True) + @api.multi def generate_key(self): for record in self: @@ -64,10 +74,7 @@ class ChannelBackend(models.Model): def import_availability(self): channel_hotel_room_type_avail_obj = self.env['channel.hotel.room.type.availability'] for backend in self: - channel_hotel_room_type_avail_obj.import_availability( - backend, - self.avail_from, - self.avail_to) + channel_hotel_room_type_avail_obj.import_availability(backend) return True @api.multi @@ -77,6 +84,20 @@ class ChannelBackend(models.Model): channel_hotel_room_type_avail_obj.push_availability(backend) return True + @api.multi + def import_restriction_plans(self): + channel_hotel_room_type_restr_obj = self.env['channel.hotel.room.type.restriction'] + for backend in self: + channel_hotel_room_type_restr_obj.import_restriction_plans(backend) + return True + + @api.multi + def import_restriction_values(self): + channel_hotel_restr_item_obj = self.env['channel.hotel.room.type.restriction.item'] + for backend in self: + channel_hotel_restr_item_obj.import_restriction_values(backend) + return True + @contextmanager @api.multi def work_on(self, model_name, **kwargs): diff --git a/hotel_channel_connector/models/channel_binding/common.py b/hotel_channel_connector/models/channel_binding/common.py index 0db43c593..558404509 100644 --- a/hotel_channel_connector/models/channel_binding/common.py +++ b/hotel_channel_connector/models/channel_binding/common.py @@ -16,6 +16,8 @@ class ChannelBinding(models.AbstractModel): required=True, ondelete='restrict') + external_id = fields.Char(string='ID on Channel') + _sql_constraints = [ ('channel_uniq', 'unique(backend_id, external_id)', 'A binding already exists with the same Channel ID.'), diff --git a/hotel_channel_connector/models/channel_ota_info/importer.py b/hotel_channel_connector/models/channel_ota_info/importer.py index 6fed02a7d..bef4ee915 100644 --- a/hotel_channel_connector/models/channel_ota_info/importer.py +++ b/hotel_channel_connector/models/channel_ota_info/importer.py @@ -42,7 +42,11 @@ class ChannelOtaInfoImporter(Component): ota_info_bind.create(map_record.values(for_create=True)) count = count + 1 except ChannelConnectorError as err: - self.create_issue('room', _("Can't import rooms from WuBook"), err.data['message']) + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message=_("Can't import rooms from WuBook"), + channel_message=err.data['message']) return count diff --git a/hotel_channel_connector/models/hotel_channel_connector_issue.py b/hotel_channel_connector/models/hotel_channel_connector_issue.py index 5e77127c7..fba387289 100644 --- a/hotel_channel_connector/models/hotel_channel_connector_issue.py +++ b/hotel_channel_connector/models/hotel_channel_connector_issue.py @@ -9,11 +9,16 @@ class HotelChannelConnectorIssue(models.Model): _name = 'hotel.channel.connector.issue' _old_name = 'wubook.issue' + backend_id = fields.Many2one('channel.backend', + 'Restriction Plan', + ondelete='cascade', + index=True) + section = fields.Selection([ ('channel', 'Channel'), ('reservation', 'Reservation'), - ('rplan', 'Restriction Plan'), - ('plan', 'Price Plan'), + ('restriction', 'Restriction Plan'), + ('pricelist', 'Price Plan'), ('room', 'Room'), ('avail', 'Availability')], required=True) to_read = fields.Boolean("To Read", default=True) diff --git a/hotel_channel_connector/models/hotel_reservation/common.py b/hotel_channel_connector/models/hotel_reservation/common.py index 87ed888d2..c0e059130 100644 --- a/hotel_channel_connector/models/hotel_reservation/common.py +++ b/hotel_channel_connector/models/hotel_reservation/common.py @@ -25,7 +25,6 @@ class ChannelHotelReservation(models.Model): string='Reservation', required=True, ondelete='cascade') - channel_reservation_id = fields.Char("Channel Reservation ID", readonly=True, old_name='wrid') ota_id = fields.Many2one('channel.ota.info', string='Channel OTA ID', readonly=True, diff --git a/hotel_channel_connector/models/hotel_reservation/importer.py b/hotel_channel_connector/models/hotel_reservation/importer.py index 565b24ac9..0b6f8b981 100644 --- a/hotel_channel_connector/models/hotel_reservation/importer.py +++ b/hotel_channel_connector/models/hotel_reservation/importer.py @@ -26,9 +26,9 @@ class HotelReservationImporter(Component): rcodeb, resultsb = self.backend_adapter.mark_bookings(uniq_rids) if rcodeb != 0: self.create_issue( - 'wubook', - _("Problem trying mark bookings (%s)") % str(processed_rids), - '') + backend=self.backend_adapter.id, + section='wubook', + internal_message=_("Problem trying mark bookings (%s)") % str(processed_rids)) # Update Odoo availability (don't wait for wubook) # This cause abuse service in first import!! if checkin_utc_dt and checkout_utc_dt: @@ -37,8 +37,9 @@ class HotelReservationImporter(Component): checkout_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)) except ChannelConnectorError as err: self.create_issue( - 'reservation', - _("Can't process reservations from wubook"), - err.data['message']) + backend=self.backend_adapter.id, + section='reservation', + internal_message=_("Can't process reservations from wubook"), + channel_message=err.data['message']) return False return True diff --git a/hotel_channel_connector/models/hotel_room_type/common.py b/hotel_channel_connector/models/hotel_room_type/common.py index 4c6e2abb8..ca490e8dd 100644 --- a/hotel_channel_connector/models/hotel_room_type/common.py +++ b/hotel_channel_connector/models/hotel_room_type/common.py @@ -19,7 +19,6 @@ class ChannelHotelRoomType(models.Model): string='Room Type', required=True, ondelete='cascade') - channel_room_id = fields.Char("Channel Room ID", readonly=True, old_name='wrid') channel_short_code = fields.Char("Channel Short Code", readonly=True, old_name='wscode') ota_capacity = fields.Integer("OTA's Capacity", default=1, old_name='wcapacity') @@ -57,7 +56,7 @@ class ChannelHotelRoomType(models.Model): @api.multi def create_room(self): self.ensure_one() - if not self.channel_room_id: + if not self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.exporter') exporter.create_room(self) @@ -67,7 +66,7 @@ class ChannelHotelRoomType(models.Model): @api.multi def modify_room(self): self.ensure_one() - if self.channel_room_id: + if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.exporter') exporter.modify_room(self) @@ -77,7 +76,7 @@ class ChannelHotelRoomType(models.Model): @api.multi def delete_room(self): self.ensure_one() - if self.channel_room_id: + if self.external_id: with self.backend_id.work_on(self._name) as work: exporter = work.component(usage='hotel.room.type.exporter') exporter.delete_room(self) diff --git a/hotel_channel_connector/models/hotel_room_type/exporter.py b/hotel_channel_connector/models/hotel_room_type/exporter.py index 5ee014734..e35e15ecf 100644 --- a/hotel_channel_connector/models/hotel_room_type/exporter.py +++ b/hotel_channel_connector/models/hotel_room_type/exporter.py @@ -17,21 +17,29 @@ class HotelRoomTypeExporter(Component): def modify_room(self, binding): try: return self.backend_adapter.modify_room( - binding.channel_room_id, + binding.external_id, binding.name, binding.ota_capacity, binding.list_price, binding.total_rooms_count, binding.channel_short_code) except ChannelConnectorError as err: - self.create_issue('room', _("Can't modify rooms in WuBook"), err.data['message']) + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message=_("Can't modify rooms in WuBook"), + channel_message=err.data['message']) @api.model def delete_room(self, binding): try: - return self.backend_adapter.delete_room(binding.channel_room_id) + return self.backend_adapter.delete_room(binding.external_id) except ChannelConnectorError as err: - self.create_issue('room', _("Can't delete room in WuBook"), err.data['message']) + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message=_("Can't delete room in WuBook"), + channel_message=err.data['message']) @api.model def create_room(self, binding): @@ -46,10 +54,14 @@ class HotelRoomTypeExporter(Component): binding.total_rooms_count ) binding.write({ - 'channel_room_id': external_id, + 'external_id': external_id, 'channel_short_code': short_code, }) except ChannelConnectorError as err: - self.create_issue('room', _("Can't delete room in WuBook"), err.data['message']) + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message=_("Can't create room in WuBook"), + channel_message=err.data['message']) else: self.binder.bind(external_id, binding) diff --git a/hotel_channel_connector/models/hotel_room_type/importer.py b/hotel_channel_connector/models/hotel_room_type/importer.py index d1728e3f6..9a471dcd3 100644 --- a/hotel_channel_connector/models/hotel_room_type/importer.py +++ b/hotel_channel_connector/models/hotel_room_type/importer.py @@ -35,7 +35,7 @@ class HotelRoomTypeImporter(Component): for room in results: map_record = room_mapper.map_record(room) room_bind = channel_room_type_obj.search([ - ('channel_room_id', '=', room['id']) + ('external_id', '=', room['id']) ], limit=1) if room_bind: room_bind.with_context({'wubook_action': False}).write(map_record.values()) @@ -43,7 +43,11 @@ class HotelRoomTypeImporter(Component): room_bind = channel_room_type_obj.with_context({'wubook_action': False}).create( map_record.values(for_create=True)) except ChannelConnectorError as err: - self.create_issue('room', _("Can't import rooms from WuBook"), err.data['message']) + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message=_("Can't import rooms from WuBook"), + channel_message=err.data['message']) return count @@ -69,8 +73,11 @@ class HotelRoomTypeImporter(Component): self._generate_room_values(dfrom, dto, results, set_max_avail=set_max_avail) except ChannelConnectorError as err: - self.create_issue('room', _("Can't fetch rooms values from WuBook"), - err.data['message'], dfrom=dfrom, dto=dto) + 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 @@ -158,7 +165,7 @@ class HotelRoomTypeImportMapper(Component): _apply_on = 'channel.hotel.room.type' direct = [ - ('id', 'channel_room_id'), + ('id', 'externa_id'), ('shortname', 'channel_short_code'), ('occupancy', 'ota_capacity'), ('price', 'list_price'), 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 17c533951..7210008e6 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/common.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/common.py @@ -51,10 +51,10 @@ class ChannelHotelRoomTypeAvailability(models.Model): @job(default_channel='root.channel') @api.model - def import_availability(self, backend, date_from, date_to): + 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(date_from, date_to) + return importer.get_availability(backend.avail_from, backend.avail_to) @job(default_channel='root.channel') @api.model 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 c98747a03..8eb17c2fc 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/exporter.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/exporter.py @@ -32,9 +32,10 @@ class HotelRoomTypeAvailabilityExporter(Component): }) except ChannelConnectorError as err: self.create_issue( - 'room', - _("Can't update availability in WuBook"), - err.data['message']) + backend=self.backend_adapter.id, + section='avail', + internal_message=_("Can't update availability in WuBook"), + channel_message=err.data['message']) def push_availability(self): channel_room_type_avail_ids = self.env['channel.hotel.room.type.availability'].search([ @@ -69,6 +70,7 @@ class HotelRoomTypeAvailabilityExporter(Component): self.backend_adapter.update_availability(avails) except ChannelConnectorError as err: self.create_issue( - 'room', - _("Can't update availability in WuBook"), - err.data['message']) + backend=self.backend_adapter.id, + section='avail', + internal_message=_("Can't update availability in WuBook"), + channel_message=err.data['message']) 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 0aba55d20..843fdd678 100644 --- a/hotel_channel_connector/models/hotel_room_type_availability/importer.py +++ b/hotel_channel_connector/models/hotel_room_type_availability/importer.py @@ -66,9 +66,10 @@ class HotelRoomTypeAvailabilityImporter(Component): iter_day += timedelta(days=1) except ChannelConnectorError as err: self.create_issue( - 'room', - _("Can't import availability from WuBook"), - err.data['message']) + backend=self.backend_adapter.id, + section='avail', + internal_message=_("Can't import availability from WuBook"), + channel_message=err.data['message']) return count diff --git a/hotel_channel_connector/models/hotel_room_type_restriction/__init__.py b/hotel_channel_connector/models/hotel_room_type_restriction/__init__.py index 06e54858b..fe02f8e98 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/__init__.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/__init__.py @@ -3,3 +3,4 @@ from . import common from . import importer +from . import exporter 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 ed2c2befe..d0b5908d6 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/common.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/common.py @@ -1,11 +1,13 @@ # Copyright 2018 Alexandre Díaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging from odoo import api, models, fields 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 +_logger = logging.getLogger(__name__) class ChannelHotelRoomTypeRestriction(models.Model): _name = 'channel.hotel.room.type.restriction' @@ -17,57 +19,43 @@ class ChannelHotelRoomTypeRestriction(models.Model): string='Hotel Virtual Room Restriction', required=True, ondelete='cascade') - channel_plan_id = fields.Char("Channel Plan ID", readonly=True, old_name='wpid') - is_daily_plan = fields.Boolean("Channel Daily Plan", default=True, old_name='wdaily_plan') @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @api.multi def create_plan(self): self.ensure_one() - if self._context.get('channel_action', True): + if not self.external_id: with self.backend_id.work_on(self._name) as work: - adapter = work.component(usage='backend.adapter') - try: - channel_plan_id = adapter.create_rplan(self.name) - if channel_plan_id: - self.channel_plan_id = channel_plan_id - except ValidationError as e: - self.create_issue('room', "Can't create restriction plan on channel", "sss") + exporter = work.component(usage='hotel.room.type.restriction.exporter') + exporter.create_rplan(self) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @api.multi def update_plan_name(self): self.ensure_one() - if self._context.get('channel_action', True): + if self.external_id: with self.backend_id.work_on(self._name) as work: - adapter = work.component(usage='backend.adapter') - try: - adapter.rename_rplan(self.channel_plan_id, self.name) - except ValidationError as e: - self.create_issue('room', "Can't update restriction plan name on channel", "sss") + exporter = work.component(usage='hotel.room.type.restriction.exporter') + exporter.rename_rplan(self) @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @api.multi def delete_plan(self): self.ensure_one() - if self._context.get('channel_action', True) and self.channel_room_id: + if self.external_id: with self.backend_id.work_on(self._name) as work: - adapter = work.component(usage='backend.adapter') - try: - adapter.delete_rplan(self.channel_plan_id) - except ValidationError as e: - self.create_issue('room', "Can't delete restriction plan on channel", "sss") + exporter = work.component(usage='hotel.room.type.restriction.exporter') + exporter.delete_rplan(self) @job(default_channel='root.channel') - @api.multi - def import_restriction_plans(self): - if self._context.get('channel_action', True): - with self.backend_id.work_on(self._name) as work: - importer = work.component(usage='channel.importer') - return importer.import_restriction_plans() + @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() class HotelRoomTypeRestriction(models.Model): _inherit = 'hotel.room.type.restriction' @@ -86,7 +74,7 @@ class HotelRoomTypeRestriction(models.Model): for name in org_names: restriction_id = room_type_restriction_obj.browse(name[0]) if any(restriction_id.channel_bind_ids) and \ - restriction_id.channel_bind_ids[0].channel_plan_id: + restriction_id.channel_bind_ids[0].external_id: names.append(( name[0], '%s (%s Backend)' % (name[1], @@ -96,6 +84,33 @@ class HotelRoomTypeRestriction(models.Model): names.append((name[0], name[1])) return names +class HotelRoomTypeRestrictionAdapter(Component): + _name = 'channel.hotel.room.type.restriction.adapter' + _inherit = 'wubook.adapter' + _apply_on = 'channel.hotel.room.type.restriction' + + def rplan_rplans(self): + return super(HotelRoomTypeRestrictionAdapter, self).rplan_rplans() + + def create_rplan(self, name): + return super(HotelRoomTypeRestrictionAdapter, self).create_rplan(name) + + def delete_rplan(self, external_id): + return super(HotelRoomTypeRestrictionAdapter, self).delete_rplan(external_id) + + def rename_rplan(self, external_id, new_name): + return super(HotelRoomTypeRestrictionAdapter, self).rename_rplan(external_id, new_name) + +class BindingHotelRoomTypeListener(Component): + _name = 'binding.hotel.room.type.restriction.listener' + _inherit = 'base.connector.listener' + _apply_on = ['hotel.room.type.restriction'] + + @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) + def on_record_write(self, record, fields=None): + if any(record.channel_bind_ids) and 'name' in fields: + record.channel_bind_ids[0].update_plan_name() + class ChannelBindingHotelRoomTypeRestrictionListener(Component): _name = 'channel.binding.hotel.room.type.restriction.listener' _inherit = 'base.connector.listener' @@ -103,13 +118,13 @@ class ChannelBindingHotelRoomTypeRestrictionListener(Component): @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_create(self, record, fields=None): - record.with_delay(priority=20).create_plan() + record.create_plan() @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_unlink(self, record, fields=None): - record.with_delay(priority=20).delete_plan() + record.delete_plan() @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): if 'name' in fields: - record.with_delay(priority=20).update_plan_name() + record.update_plan_name() diff --git a/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py b/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py new file mode 100644 index 000000000..b60878830 --- /dev/null +++ b/hotel_channel_connector/models/hotel_room_type_restriction/exporter.py @@ -0,0 +1,52 @@ +# Copyright 2018 Alexandre Díaz +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +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__) + +class HotelRoomTypeRestrictionExporter(Component): + _name = 'channel.hotel.room.type.restriction.exporter' + _inherit = 'hotel.channel.exporter' + _apply_on = ['channel.hotel.room.type.restriction'] + _usage = 'hotel.room.type.restriction.exporter' + + @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']) + + @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']) + + @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) 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 33b15b3a4..a328904bf 100644 --- a/hotel_channel_connector/models/hotel_room_type_restriction/importer.py +++ b/hotel_channel_connector/models/hotel_room_type_restriction/importer.py @@ -18,6 +18,36 @@ class HotelRoomTypeRestrictionImporter(Component): _apply_on = ['channel.hotel.room.type.restriction'] _usage = 'hotel.room.type.restriction.importer' + @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()) + else: + plan_bind.with_context({'wubook_action': False}).write( + plan_record.values(for_create=True)) + count = count + 1 + except ChannelConnectorError as err: + self.create_issue( + backend=self.backend_adapter.id, + section='rplan', + internal_message=_("Can't fetch restriction plans from wubook"), + channel_message=err.data['message']) + return count + class HotelRoomTypeRestrictionImportMapper(Component): _name = 'channel.hotel.room.type.restriction.import.mapper' @@ -25,11 +55,8 @@ class HotelRoomTypeRestrictionImportMapper(Component): _apply_on = 'channel.hotel.room.type.restriction' direct = [ - ('no_ota', 'no_ota'), - ('booked', 'booked'), - ('avail', 'avail'), - ('room_type_id', 'room_type_id'), - ('date', 'date') + ('name', 'name'), + ('id', 'external_id'), ] @mapping 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 2f79bfc38..c01c39197 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 @@ -26,6 +26,16 @@ class ChannelHotelRoomTypeRestrictionItem(models.Model): self.ensure_one() self.channel_pushed = status + @job(default_channel='root.channel') + @api.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) + class HotelRoomTypeRestrictionItem(models.Model): _inherit = 'hotel.room.type.restriction.item' @@ -34,15 +44,26 @@ class HotelRoomTypeRestrictionItem(models.Model): inverse_name='odoo_id', string='Hotel Channel Connector Bindings') +class HotelRoomTypeRestrictionItemAdapter(Component): + _name = 'channel.hotel.room.type.restriction.item.adapter' + _inherit = 'wubook.adapter' + _apply_on = 'channel.hotel.room.type.restriction.item' + + def wired_rplan_get_rplan_values(self, date_from, date_to, channel_restriction_plan_id): + return super(HotelRoomTypeRestrictionItemAdapter, self).wired_rplan_get_rplan_values( + date_from, + date_to, + channel_restriction_plan_id) + class ChannelBindingHotelRoomTypeRestrictionItemListener(Component): - _name = 'channel.binding.hotel.room.type.restriction.listener' + _name = 'channel.binding.hotel.room.type.restriction.item.listener' _inherit = 'base.connector.listener' _apply_on = ['channel.hotel.room.type.restriction'] @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_create(self, record, fields=None): - record.update_channel_pushed(False) + return True @skip_if(lambda self, record, **kwargs: self.no_connector_export(record)) def on_record_write(self, record, fields=None): - record.update_channel_pushed(False) + 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 a50a8868d..426e11028 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 @@ -2,11 +2,13 @@ # 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.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 import date_utils +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__) @@ -17,6 +19,69 @@ class HotelRoomTypeRestrictionImporter(Component): _apply_on = ['channel.hotel.room.type.restriction.item'] _usage = 'hotel.room.type.restriction.item.importer' + # FIXME: Reduce Nested Loops!! + @api.model + def _generate_restriction_items(self, plan_restrictions): + channel_hotel_room_type_obj = self.env['channel.hotel.room.type'] + channel_reserv_restriction_obj = self.env['channel.hotel.room.type.restriction'] + channel_restriction_item_obj = self.env['channel.hotel.room.type.restriction.item'] + restriction_item_mapper = self.component( + usage='import.mapper', + model_name='channel.hotel.room.type.restriction.item') + _logger.info("==[CHANNEL->ODOO]==== RESTRICTIONS ==") + _logger.info(plan_restrictions) + for k_rpid, v_rpid in plan_restrictions.items(): + channel_restriction_id = channel_reserv_restriction_obj.search([ + ('external_id', '=', k_rpid) + ], limit=1) + if channel_restriction_id: + for k_rid, v_rid in v_rpid.items(): + channel_room_type = channel_hotel_room_type_obj.search([ + ('external_id', '=', k_rid) + ], limit=1) + if channel_room_type: + for item in v_rid: + map_record = restriction_item_mapper.map_record(item) + date_dt = datetime.strptime(item['date'], DEFAULT_WUBOOK_DATE_FORMAT) + date_str = date_dt.strftime(DEFAULT_SERVER_DATE_FORMAT) + channel_restriction_item = channel_restriction_item_obj.search([ + ('restriction_id', '=', channel_restriction_id.odoo_id.id), + ('date', '=', date_str), + ('applied_on', '=', '0_room_type'), + ('room_type_id', '=', channel_room_type.odoo_id.id) + ], limit=1) + item.update({ + 'date': date_str, + 'room_type_id': channel_room_type.odoo_id.id, + 'restriction_id': channel_restriction_id.odoo_id.id, + }) + if channel_restriction_item: + channel_restriction_item.with_context({ + 'wubook_action': False}).write(map_record.values()) + else: + channel_restriction_item_obj.with_context({ + 'wubook_action': False + }).create(map_record.values(for_create=True)) + + @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 + class HotelRoomTypeRestrictionItemImportMapper(Component): _name = 'channel.hotel.room.type.restriction.item.import.mapper' _inherit = 'channel.import.mapper' @@ -30,7 +95,6 @@ class HotelRoomTypeRestrictionItemImportMapper(Component): ('closed', 'closed'), ('closed_departure', 'closed_departure'), ('closed_arrival', 'closed_arrival'), - ('room_type_id', 'room_type_id'), ('date', 'date'), ] @@ -39,6 +103,14 @@ class HotelRoomTypeRestrictionItemImportMapper(Component): def applied_on(self, record): return {'applied_on': '0_room_type'} + @mapping + def room_type_id(self, record): + return {'room_type_id': record['room_type_id']} + + @mapping + def restriction_id(self, record): + return {'restriction_id': record['restriction_id']} + @mapping def backend_id(self, record): return {'backend_id': self.backend_record.id} diff --git a/hotel_channel_connector/models/inherited_product_pricelist_item.py b/hotel_channel_connector/models/inherited_product_pricelist_item.py deleted file mode 100644 index 49934f850..000000000 --- a/hotel_channel_connector/models/inherited_product_pricelist_item.py +++ /dev/null @@ -1,56 +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, _ -from openerp.exceptions import ValidationError - - -class ProductPricelistItem(models.Model): - _inherit = 'product.pricelist.item' - - is_channel_pushed = fields.Boolean("WuBook Pushed", default=True, readonly=True, - old_name='wpushed') - is_daily_plan = fields.Boolean(related='pricelist_id.channel_bind_ids.is_daily_plan', readonly=True, - old_name='wdaily') - - @api.constrains('fixed_price') - def _check_fixed_price(self): - room_type_obj = self.env['hotel.room.type'] - for record in self: - room_type = room_type_obj.search([ - ('product_id.product_tmpl_id', '=', record.product_tmpl_id.id) - ], limit=1) - if room_type and room_type.channel_room_id and record.compute_price == 'fixed' \ - and record.fixed_price <= 0.0: - raise ValidationError(_("Price need be greater than zero")) - - @api.model - def create(self, vals): - if self._context.get('channel_action', True): - pricelist_id = self.env['product.pricelist'].browse( - vals.get('pricelist_id')) - room_type = self.env['hotel.room.type'].search([ - ('product_id.product_tmpl_id', '=', - vals.get('product_tmpl_id')), - ('channel_room_id', '!=', False) - ]) - if room_type and pricelist_id.channel_plan_id: - vals.update({'is_channel_pushed': False}) - return super(ProductPricelistItem, self).create(vals) - - @api.multi - def write(self, vals): - if self._context.get('channel_action', True): - prices_obj = self.env['product.pricelist'] - for record in self: - pricelist_id = prices_obj.browse(vals.get('pricelist_id')) if \ - vals.get('pricelist_id') else record.pricelist_id - product_tmpl_id = vals.get('product_tmpl_id') or \ - record.product_tmpl_id.id - room_type = self.env['hotel.room.type'].search([ - ('product_id.product_tmpl_id', '=', product_tmpl_id), - ('channel_room_id', '!=', False), - ]) - if room_type and pricelist_id.channel_plan_id: - vals.update({'is_channel_pushed': False}) - return super(ProductPricelistItem, self).write(vals) diff --git a/hotel_channel_connector/models/product_pricelist/common.py b/hotel_channel_connector/models/product_pricelist/common.py index 976e0b338..3430fcc87 100644 --- a/hotel_channel_connector/models/product_pricelist/common.py +++ b/hotel_channel_connector/models/product_pricelist/common.py @@ -34,7 +34,10 @@ class ChannelProductPricelist(models.Model): if channel_plan_id: self.channel_plan_id = channel_plan_id except ValidationError as e: - self.create_issue('room', "Can't create plan on channel", "sss") + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message="Can't create plan on channel") @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -49,7 +52,10 @@ class ChannelProductPricelist(models.Model): self.channel_plan_id, self.name) except ValidationError as e: - self.create_issue('room', "Can't update plan name on channel", "sss") + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message="Can't update plan name on channel") @job(default_channel='root.channel') @related_action(action='related_action_unwrap_binding') @@ -62,7 +68,10 @@ class ChannelProductPricelist(models.Model): try: adapter.delete_plan(self.channel_plan_id) except ValidationError as e: - self.create_issue('room', "Can't delete plan on channel", "sss") + self.create_issue( + backend=self.backend_adapter.id, + section='room', + internal_message="Can't delete plan on channel") @job(default_channel='root.channel') @api.multi diff --git a/hotel_channel_connector/models/product_pricelist_item/__init__.py b/hotel_channel_connector/models/product_pricelist_item/__init__.py new file mode 100644 index 000000000..257ab04fc --- /dev/null +++ b/hotel_channel_connector/models/product_pricelist_item/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2018 Alexandre Díaz +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import common diff --git a/hotel_channel_connector/models/product_pricelist_item/common.py b/hotel_channel_connector/models/product_pricelist_item/common.py new file mode 100644 index 000000000..f80d542c5 --- /dev/null +++ b/hotel_channel_connector/models/product_pricelist_item/common.py @@ -0,0 +1,30 @@ +# Copyright 2018 Alexandre Díaz +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, fields +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 + +class ChannelProductPricelistItem(models.Model): + _name = 'channel.product.pricelist.item' + _inherit = 'channel.binding' + _inherits = {'product.pricelist.item': 'odoo_id'} + _description = 'Channel Product Pricelist Item' + + odoo_id = fields.Many2one(comodel_name='product.pricelist.item', + string='Pricelist Item', + required=True, + ondelete='cascade') + + channel_pushed = fields.Boolean("Channel Pushed", readonly=True, default=False, + old_name='wpushed') + +class ProductPricelistItem(models.Model): + _inherit = 'product.pricelist.item' + + channel_bind_ids = fields.One2many( + comodel_name='channel.product.pricelist.item', + inverse_name='odoo_id', + string='Hotel Channel Connector Bindings') diff --git a/hotel_channel_connector/views/channel_connector_backend_views.xml b/hotel_channel_connector/views/channel_connector_backend_views.xml index 1f06b3c7b..3e2dcb8c5 100644 --- a/hotel_channel_connector/views/channel_connector_backend_views.xml +++ b/hotel_channel_connector/views/channel_connector_backend_views.xml @@ -95,6 +95,27 @@ string="Import in background"/> + + + + @@ -107,6 +128,17 @@ + + + + + + + + + + + diff --git a/hotel_channel_connector/views/channel_hotel_reservation_views.xml b/hotel_channel_connector/views/channel_hotel_reservation_views.xml index f75d53daa..bf6057427 100644 --- a/hotel_channel_connector/views/channel_hotel_reservation_views.xml +++ b/hotel_channel_connector/views/channel_hotel_reservation_views.xml @@ -7,7 +7,7 @@
- + diff --git a/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml index d55fa121f..c8e34c01c 100644 --- a/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml +++ b/hotel_channel_connector/views/channel_hotel_room_type_availability_views.xml @@ -6,6 +6,10 @@ channel.hotel.room.type.availability + + + + diff --git a/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml index 662cc317c..d8956faad 100644 --- a/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml +++ b/hotel_channel_connector/views/channel_hotel_room_type_restriction_views.xml @@ -7,8 +7,11 @@ - - + + + + + diff --git a/hotel_channel_connector/views/channel_hotel_room_type_views.xml b/hotel_channel_connector/views/channel_hotel_room_type_views.xml index 846964b59..5cc063650 100644 --- a/hotel_channel_connector/views/channel_hotel_room_type_views.xml +++ b/hotel_channel_connector/views/channel_hotel_room_type_views.xml @@ -7,10 +7,11 @@
+ - + diff --git a/hotel_channel_connector/views/channel_ota_info_views.xml b/hotel_channel_connector/views/channel_ota_info_views.xml index 8dcc4dff8..168957bd8 100644 --- a/hotel_channel_connector/views/channel_ota_info_views.xml +++ b/hotel_channel_connector/views/channel_ota_info_views.xml @@ -8,6 +8,10 @@ + + + + diff --git a/hotel_channel_connector/views/channel_product_pricelist_item_views.xml b/hotel_channel_connector/views/channel_product_pricelist_item_views.xml new file mode 100644 index 000000000..b8cb4554d --- /dev/null +++ b/hotel_channel_connector/views/channel_product_pricelist_item_views.xml @@ -0,0 +1,27 @@ + + + + + channel.product.pricelist.item.form + channel.product.pricelist.item + + + + + + + + + + + + channel.hotel.product.pricelist.item.tree + channel.product.pricelist.item + + + + + + + + diff --git a/hotel_channel_connector/views/channel_product_pricelist_views.xml b/hotel_channel_connector/views/channel_product_pricelist_views.xml index 1e64681d2..5f23c0c21 100644 --- a/hotel_channel_connector/views/channel_product_pricelist_views.xml +++ b/hotel_channel_connector/views/channel_product_pricelist_views.xml @@ -7,14 +7,18 @@
- + + + + +
- + channel.hotel.product.pricelist.tree channel.product.pricelist diff --git a/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml b/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml index 5732014df..10e854da5 100644 --- a/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml +++ b/hotel_channel_connector/views/hotel_channel_connector_issue_views.xml @@ -18,6 +18,9 @@ class="oe_stat_button" icon="fa-warning" attrs="{'invisible':['|', ['section', '!=', 'reservation'], ['channel_object_id', '=', False]]}"/> + + + @@ -42,6 +45,7 @@ tree + @@ -58,6 +62,7 @@ hotel.channel.connector.issue + diff --git a/hotel_channel_connector/views/inherited_hotel_reservation_views.xml b/hotel_channel_connector/views/inherited_hotel_reservation_views.xml index 143f9247c..ac6207ab6 100644 --- a/hotel_channel_connector/views/inherited_hotel_reservation_views.xml +++ b/hotel_channel_connector/views/inherited_hotel_reservation_views.xml @@ -23,7 +23,7 @@ - + diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_availability_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_availability_views.xml index c93b12a18..d7f17c58e 100644 --- a/hotel_channel_connector/views/inherited_hotel_room_type_availability_views.xml +++ b/hotel_channel_connector/views/inherited_hotel_room_type_availability_views.xml @@ -8,7 +8,7 @@ - + diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml new file mode 100644 index 000000000..f28fe7e7f --- /dev/null +++ b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_item_views.xml @@ -0,0 +1,24 @@ + + + + + hotel.room.type.restriction.item + + + + + + + + + + + + + + + + + + + diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml index 86ef97d11..d640d760f 100644 --- a/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml +++ b/hotel_channel_connector/views/inherited_hotel_room_type_restriction_views.xml @@ -5,11 +5,19 @@ hotel.room.type.restriction - -
-
-
+ + + + + + + + + + + + +
diff --git a/hotel_channel_connector/views/inherited_hotel_room_type_views.xml b/hotel_channel_connector/views/inherited_hotel_room_type_views.xml index e75a7a7c0..70251b4b6 100644 --- a/hotel_channel_connector/views/inherited_hotel_room_type_views.xml +++ b/hotel_channel_connector/views/inherited_hotel_room_type_views.xml @@ -7,7 +7,7 @@ - + diff --git a/hotel_channel_connector/views/inherited_product_pricelist_item_views.xml b/hotel_channel_connector/views/inherited_product_pricelist_item_views.xml index 696e56773..caef730f9 100644 --- a/hotel_channel_connector/views/inherited_product_pricelist_item_views.xml +++ b/hotel_channel_connector/views/inherited_product_pricelist_item_views.xml @@ -5,12 +5,18 @@ product.pricelist.item - - - - - - {'readonly': [('is_daily_plan', '=', True)]} + + + + + + + + + + + +
diff --git a/hotel_channel_connector/views/inherited_product_pricelist_views.xml b/hotel_channel_connector/views/inherited_product_pricelist_views.xml index ab4882128..0f1018f0b 100644 --- a/hotel_channel_connector/views/inherited_product_pricelist_views.xml +++ b/hotel_channel_connector/views/inherited_product_pricelist_views.xml @@ -5,14 +5,19 @@ product.pricelist - -
-
-
- - - + + + + + + + + + + + + +