[WIP] Channel Connector

This commit is contained in:
QS5ELkMu
2018-11-05 15:23:48 +01:00
parent f2d6d42353
commit 19127e3091
44 changed files with 579 additions and 225 deletions

View File

@@ -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',

View File

@@ -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", {

View File

@@ -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',
]

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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):

View File

@@ -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.'),

View File

@@ -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

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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'),

View File

@@ -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

View File

@@ -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'])

View File

@@ -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

View File

@@ -3,3 +3,4 @@
from . import common
from . import importer
from . import exporter

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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}

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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')

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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">

View File

@@ -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"/>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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">

View File

@@ -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>

View File

@@ -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>