mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[WIP] Channel Connector
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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", {
|
||||
|
||||
@@ -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',
|
||||
]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.'),
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@
|
||||
|
||||
from . import common
|
||||
from . import importer
|
||||
from . import exporter
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# 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()
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# 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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# 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)
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import common
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# 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')
|
||||
@@ -95,6 +95,27 @@
|
||||
string="Import in background"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<label string="Import Restriction Plans" class="oe_inline"/>
|
||||
<div>
|
||||
<button name="import_restriction_plans"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
string="Import in background"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<label string="Import Restriction Values" class="oe_inline"/>
|
||||
<div>
|
||||
<field name="restriction_id" class="oe_inline" nolabel="1"/>
|
||||
<field name="restriction_from" class="oe_inline" nolabel="1"/>
|
||||
<field name="restriction_to" class="oe_inline" nolabel="1"/>
|
||||
<button name="import_restriction_values"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
string="Import in background"/>
|
||||
</div>
|
||||
</group>
|
||||
</page>
|
||||
<page name="export" string="Exports">
|
||||
<group>
|
||||
@@ -107,6 +128,17 @@
|
||||
</div>
|
||||
</group>
|
||||
</page>
|
||||
<page name="issues" string="Issues">
|
||||
<field name="issue_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="section"/>
|
||||
<field name="internal_message"/>
|
||||
<field name="channel_message"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Reservation">
|
||||
<group>
|
||||
<field name="channel_reservation_id" />
|
||||
<field name="external_id" />
|
||||
<field name="ota_reservation_id" />
|
||||
<field name="wstatus" />
|
||||
<field name="wstatus_reason" />
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
<field name="model">channel.hotel.room.type.availability</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Room Availability">
|
||||
<group>
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="channel_max_avail" />
|
||||
<field name="channel_pushed" />
|
||||
|
||||
@@ -7,8 +7,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Room Restriction">
|
||||
<group>
|
||||
<field name="channel_plan_id" />
|
||||
<field name="is_daily_plan" />
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="external_id" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Virtual Room">
|
||||
<group>
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="channel_room_id" />
|
||||
<field name="external_id" />
|
||||
<field name="channel_short_code" />
|
||||
<field name="ota_capacity" />
|
||||
</group>
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel OTA's Info" >
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="ota_id" />
|
||||
<field name="name" />
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="view_channel_product_pricelist_item_form" model="ir.ui.view">
|
||||
<field name="name">channel.product.pricelist.item.form</field>
|
||||
<field name="model">channel.product.pricelist.item</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Product Pricelist Item">
|
||||
<group>
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_channel_hotel_product_pricelist_item_tree" model="ir.ui.view">
|
||||
<field name="name">channel.hotel.product.pricelist.item.tree</field>
|
||||
<field name="model">channel.product.pricelist.item</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Hotel Channel Product Pricelist Item">
|
||||
<field name="backend_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -7,14 +7,18 @@
|
||||
<field name="arch" type="xml">
|
||||
<form string="Hotel Channel Product Pricelist">
|
||||
<group>
|
||||
<field name="channel_plan_id" />
|
||||
<field name="id" invisible="1" />
|
||||
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
|
||||
</group>
|
||||
<group>
|
||||
<field name="external_id" />
|
||||
<field name="is_daily_plan" />
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_channel_hotel_room_type_restriction_tree" model="ir.ui.view">
|
||||
<record id="view_channel_product_pricelist_tree" model="ir.ui.view">
|
||||
<field name="name">channel.hotel.product.pricelist.tree</field>
|
||||
<field name="model">channel.product.pricelist</field>
|
||||
<field name="arch" type="xml">
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
class="oe_stat_button" icon="fa-warning"
|
||||
attrs="{'invisible':['|', ['section', '!=', 'reservation'], ['channel_object_id', '=', False]]}"/>
|
||||
</div>
|
||||
<group>
|
||||
<field name="backend_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="section"/>
|
||||
<field name="internal_message"/>
|
||||
@@ -42,6 +45,7 @@
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Channel Connector Issues">
|
||||
<field name="backend_id"/>
|
||||
<field name="create_date"/>
|
||||
<field name="section"/>
|
||||
<field name="internal_message"/>
|
||||
@@ -58,6 +62,7 @@
|
||||
<field name="model">hotel.channel.connector.issue</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Issues">
|
||||
<field name="backend_id"/>
|
||||
<field name="create_date"/>
|
||||
<field name="section"/>
|
||||
<field name="internal_message"/>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
</xpath>
|
||||
|
||||
<xpath expr="//page[@name='days']" position="after">
|
||||
<page name="connector">
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<notebook>
|
||||
<page name="connector">
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
|
||||
<record id="room_type_restriction_item_view" model="ir.ui.view">
|
||||
<field name="model">hotel.room.type.restriction.item</field>
|
||||
<field name="inherit_id" ref="hotel.room_type_restriction_item_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<notebook>
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="backend_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -5,11 +5,19 @@
|
||||
<field name="model">hotel.room.type.restriction</field>
|
||||
<field name="inherit_id" ref="hotel.room_type_restriction_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form[1]//sheet" position="before">
|
||||
<header>
|
||||
<button name="%(action_wubook_import_plan_restrictions)d" string="Import Restrictions From Channel" type="action" class="oe_highlight" />
|
||||
</header>
|
||||
</xpath>
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<notebook>
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="backend_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<notebook>
|
||||
<page name="connector">
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="capacity" invisible="1" />
|
||||
<field name="channel_bind_ids" context="{'default_ota_capacity': capacity}" nolabel="1">
|
||||
|
||||
@@ -5,12 +5,18 @@
|
||||
<field name="model">product.pricelist.item</field>
|
||||
<field name="inherit_id" ref="product.product_pricelist_item_form_view" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//field[@name='name']" position="after">
|
||||
<!--field name="channel_pushed" readonly="True" /-->
|
||||
<field name="is_daily_plan" invisible="1" />
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='date_end']" position="attributes">
|
||||
<attribute name="attrs">{'readonly': [('is_daily_plan', '=', True)]}</attribute>
|
||||
<xpath expr="//form" position="inside">
|
||||
<notebook>
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="backend_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -5,14 +5,19 @@
|
||||
<field name="model">product.pricelist</field>
|
||||
<field name="inherit_id" ref="product.product_pricelist_view" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form[1]//sheet" position="before">
|
||||
<header>
|
||||
<button name="%(action_wubook_import_plan_prices)d" string="Import Prices From Channel" type="action" class="oe_highlight" />
|
||||
</header>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='company_id']" position="after">
|
||||
<field name="id" invisible="1"/>
|
||||
</xpath>
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<notebook>
|
||||
<page name="connector" string="Channel Connector">
|
||||
<group string="Hotel Channel Bindings">
|
||||
<field name="channel_bind_ids" nolabel="1">
|
||||
<tree>
|
||||
<field name="backend_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user