mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[WIP][MIG][11.0] channel connector
This commit is contained in:
@@ -40,7 +40,6 @@
|
||||
'views/channel_hotel_room_type_restriction_views.xml',
|
||||
'views/channel_product_pricelist_views.xml',
|
||||
'views/channel_connector_backend_views.xml',
|
||||
'views/channel_connector_menu.xml',
|
||||
'data/menus.xml',
|
||||
'data/sequences.xml',
|
||||
#'security/ir.model.access.csv',
|
||||
|
||||
@@ -6,3 +6,4 @@ from . import backend_adapter
|
||||
from . import binder
|
||||
from . import importer
|
||||
from . import exporter
|
||||
from . import mapper
|
||||
|
||||
@@ -685,62 +685,6 @@ class HotelChannelConnectorImporter(AbstractComponent):
|
||||
count = count + 1
|
||||
return count
|
||||
|
||||
@api.model
|
||||
def get_rooms(self):
|
||||
count = 0
|
||||
try:
|
||||
results = self.backend_adapter.fetch_rooms()
|
||||
|
||||
room_type_obj = self.env['hotel.room.type']
|
||||
count = len(results)
|
||||
for room in results:
|
||||
vals = {
|
||||
'name': room['name'],
|
||||
'wrid': room['id'],
|
||||
'wscode': room['shortname'],
|
||||
'list_price': room['price'],
|
||||
'wcapacity': room['occupancy'],
|
||||
# 'max_real_rooms': room['availability'],
|
||||
}
|
||||
room_type = room_type_obj.search([('wrid', '=', room['id'])], limit=1)
|
||||
if room_type:
|
||||
room_type.with_context({'wubook_action': False}).write(vals)
|
||||
else:
|
||||
room_type_obj.with_context({'wubook_action': False}).create(vals)
|
||||
except ChannelConnectorError as err:
|
||||
self.create_issue('room', _("Can't import rooms from WuBook"), err.data['message'])
|
||||
|
||||
return count
|
||||
|
||||
@api.model
|
||||
def fetch_rooms_values(self, dfrom, dto, rooms=False,
|
||||
set_max_avail=False):
|
||||
# Sanitize Dates
|
||||
now_dt = date_utils.now()
|
||||
dfrom_dt = date_utils.get_datetime(dfrom)
|
||||
dto_dt = date_utils.get_datetime(dto)
|
||||
if dto_dt < now_dt:
|
||||
return True
|
||||
if dfrom_dt < now_dt:
|
||||
dfrom_dt = now_dt
|
||||
if dfrom_dt > dto_dt:
|
||||
dtemp_dt = dfrom_dt
|
||||
dfrom_dt = dto_dt
|
||||
dto_dt = dtemp_dt
|
||||
|
||||
try:
|
||||
results = self.backend_adapter.fetch_rooms_values(
|
||||
dfrom_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
|
||||
dto_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
|
||||
rooms)
|
||||
self._generate_room_values(dfrom, dto, results,
|
||||
set_max_avail=set_max_avail)
|
||||
except ChannelConnectorError as err:
|
||||
self.create_issue('room', _("Can't fetch rooms values from WuBook"),
|
||||
err.data['message'], dfrom=dfrom, dto=dto)
|
||||
return False
|
||||
return True
|
||||
|
||||
@api.model
|
||||
def fetch_booking(self, channel_reservation_id):
|
||||
try:
|
||||
@@ -861,3 +805,30 @@ class HotelChannelConnectorImporter(AbstractComponent):
|
||||
err.data['message'])
|
||||
return 0
|
||||
return count
|
||||
|
||||
class BatchImporter(AbstractComponent):
|
||||
""" The role of a BatchImporter is to search for a list of
|
||||
items to import, then it can either import them directly or delay
|
||||
the import of each item separately.
|
||||
"""
|
||||
|
||||
_name = 'channel.batch.importer'
|
||||
_inherit = ['base.importer', 'base.hotel.channel.connector']
|
||||
_usage = 'batch.importer'
|
||||
|
||||
def _import_record(self, external_id):
|
||||
""" Import a record directly or delay the import of the record.
|
||||
|
||||
Method to implement in sub-classes.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
class DirectBatchImporter(AbstractComponent):
|
||||
""" Import the records directly, without delaying the jobs. """
|
||||
|
||||
_name = 'channel.direct.batch.importer'
|
||||
_inherit = 'channel.batch.importer'
|
||||
|
||||
def _import_record(self, external_id):
|
||||
""" Import the record directly """
|
||||
self.model.import_record(self.backend_record, external_id)
|
||||
|
||||
16
hotel_channel_connector/components/mapper.py
Normal file
16
hotel_channel_connector/components/mapper.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.addons.component.core import AbstractComponent
|
||||
|
||||
|
||||
class ChannelImportMapper(AbstractComponent):
|
||||
_name = 'channel.import.mapper'
|
||||
_inherit = ['base.hotel.channel.connector', 'base.import.mapper']
|
||||
_usage = 'import.mapper'
|
||||
|
||||
|
||||
class ChannelExportMapper(AbstractComponent):
|
||||
_name = 'channel.export.mapper'
|
||||
_inherit = ['base.hotel.channel.connector', 'base.export.mapper']
|
||||
_usage = 'export.mapper'
|
||||
@@ -1,28 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record model="ir.actions.act_window" id="open_hotel_channel_connector_ota_info_tree_all">
|
||||
<field name="name">Hotel Channel Connector OTA's Info</field>
|
||||
<field name="res_model">hotel.channel.connector.ota.info</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem id="menu_channel_connector_root"
|
||||
parent="connector.menu_connector_root"
|
||||
name="Channel"
|
||||
sequence="10"
|
||||
groups="connector.group_connector_manager"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="open_hotel_channel_connector_issue_tree_all">
|
||||
<field name="name">Hotel Channel Connector Issues</field>
|
||||
<field name="res_model">hotel.channel.connector.issue</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_hotel_channel_connector_issue_form_search"/>
|
||||
<field name="context">{"search_default_to_read":True}</field>
|
||||
</record>
|
||||
<menuitem id="menu_cannel_backend"
|
||||
name="Backends"
|
||||
parent="menu_channel_connector_root"
|
||||
action="action_channel_backend"/>
|
||||
|
||||
<menuitem id="hotel_channel_connector_menu" name="Hotel Channel Connector"
|
||||
sequence="25" parent="base.menu_administration" />
|
||||
<menuitem id="hotel_channel_connector_ota_info_menu"
|
||||
name="OTA's"
|
||||
action="open_hotel_channel_connector_ota_info_tree_all"
|
||||
parent="menu_channel_connector_root"/>
|
||||
|
||||
<menuitem id="hotel_channel_connector_ota_info_menu" name="Channels Info"
|
||||
sequence="1" action="open_hotel_channel_connector_ota_info_tree_all" parent="hotel_channel_connector_menu"/>
|
||||
<menuitem id="hotel_channel_connector_issue_menu" name="Issues"
|
||||
sequence="2" action="open_hotel_channel_connector_issue_tree_all" parent="hotel_channel_connector_menu"/>
|
||||
<menuitem id="hotel_channel_connector_issue_menu"
|
||||
name="Issues"
|
||||
action="open_hotel_channel_connector_issue_tree_all"
|
||||
parent="menu_channel_connector_root"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -35,6 +35,13 @@ class ChannelBackend(models.Model):
|
||||
channel_hotel_reservation.import_reservations(backend)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def import_rooms(self):
|
||||
channel_hotel_room_type = self.env['channel.hotel.room.type']
|
||||
for backend in self:
|
||||
channel_hotel_room_type.import_rooms(backend)
|
||||
return True
|
||||
|
||||
@contextmanager
|
||||
@api.multi
|
||||
def work_on(self, model_name, **kwargs):
|
||||
@@ -47,6 +54,5 @@ class ChannelBackend(models.Model):
|
||||
self.pkey)
|
||||
with WuBookServer(wubook_login) as channel_api:
|
||||
_super = super(ChannelBackend, self)
|
||||
# from the components we'll be able to do: self.work.channel_api
|
||||
with _super.work_on(model_name, channel_api=channel_api, **kwargs) as work:
|
||||
yield work
|
||||
|
||||
@@ -2,3 +2,5 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import common
|
||||
from . import importer
|
||||
from . import exporter
|
||||
|
||||
@@ -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 ChannelHotelRoomType(models.Model):
|
||||
_name = 'channel.hotel.room.type'
|
||||
@@ -21,6 +23,13 @@ class ChannelHotelRoomType(models.Model):
|
||||
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')
|
||||
|
||||
@job(default_channel='root.channel')
|
||||
@api.model
|
||||
def import_rooms(self, backend):
|
||||
with backend.work_on(self._name) as work:
|
||||
importer = work.component(usage='hotel.room.type.importer')
|
||||
return importer.get_rooms()
|
||||
|
||||
@api.constrains('ota_capacity')
|
||||
def _check_ota_capacity(self):
|
||||
for record in self:
|
||||
@@ -63,20 +72,16 @@ class ChannelHotelRoomType(models.Model):
|
||||
@related_action(action='related_action_unwrap_binding')
|
||||
@api.multi
|
||||
def modify_room(self):
|
||||
_logger.info("PASA A =======")
|
||||
self.ensure_one()
|
||||
if self._context.get('channel_action', True) and self.channel_room_id:
|
||||
_logger.info("PASA b =======")
|
||||
if self._context.get('wubook_action', True) and self.channel_room_id:
|
||||
_logger.info("PASA C =======")
|
||||
with self.backend_id.work_on(self._name) as work:
|
||||
adapter = work.component(usage='backend.adapter')
|
||||
try:
|
||||
adapter.modify_room(
|
||||
self.channel_room_id,
|
||||
self.name,
|
||||
self.ota_capacity,
|
||||
self.list_price,
|
||||
self.total_rooms_count,
|
||||
self.channel_short_code)
|
||||
except ValidationError as e:
|
||||
self.create_issue('room', "Can't modify room on channel", "sss")
|
||||
_logger.info("PASA D =======")
|
||||
exporter = work.component(usage='hotel.room.type.exporter')
|
||||
exporter.modify_room(self)
|
||||
_logger.info("PASA E =======")
|
||||
|
||||
@job(default_channel='root.channel')
|
||||
@related_action(action='related_action_unwrap_binding')
|
||||
@@ -91,14 +96,6 @@ class ChannelHotelRoomType(models.Model):
|
||||
except ValidationError as e:
|
||||
self.create_issue('room', "Can't delete room on channel", "sss")
|
||||
|
||||
@job(default_channel='root.channel')
|
||||
@api.multi
|
||||
def import_rooms(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_rooms()
|
||||
|
||||
class HotelRoomType(models.Model):
|
||||
_inherit = 'hotel.room.type'
|
||||
|
||||
@@ -125,13 +122,31 @@ class HotelRoomType(models.Model):
|
||||
], limit=1)
|
||||
return restriction
|
||||
|
||||
class ChannelBindingRoomTypeListener(Component):
|
||||
_name = 'channel.binding.room.type.listener'
|
||||
class HotelRoomTypeAdapter(Component):
|
||||
_name = 'channel.hotel.room.type.adapter'
|
||||
_inherit = 'wubook.adapter'
|
||||
_apply_on = 'channel.hotel.room.type'
|
||||
|
||||
def fetch_rooms(self):
|
||||
return super(HotelRoomTypeAdapter, self).fetch_rooms()
|
||||
|
||||
class BindingHotelRoomTypeListener(Component):
|
||||
_name = 'binding.hotel.room.type.listener'
|
||||
_inherit = 'base.connector.listener'
|
||||
_apply_on = ['channel.room.type']
|
||||
_apply_on = ['hotel.room.type']
|
||||
|
||||
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
|
||||
def on_record_write(self, record, fields=None):
|
||||
if 'name' in fields or 'list_price' in fields:
|
||||
record.channel_bind_ids[0].modify_room()
|
||||
|
||||
class ChannelBindingRoomTypeListener(Component):
|
||||
_name = 'channel.binding.room.type.listener'
|
||||
_inherit = 'base.connector.listener'
|
||||
_apply_on = ['channel.hotel.room.type']
|
||||
|
||||
@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_room()
|
||||
|
||||
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
|
||||
@@ -140,4 +155,4 @@ class ChannelBindingRoomTypeListener(Component):
|
||||
|
||||
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
|
||||
def on_record_write(self, record, fields=None):
|
||||
record.with_delay(priority=20).modidy_room()
|
||||
record.modify_room()
|
||||
|
||||
25
hotel_channel_connector/models/hotel_room_type/exporter.py
Normal file
25
hotel_channel_connector/models/hotel_room_type/exporter.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.addons.component.core import Component
|
||||
from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError
|
||||
from odoo import api, _
|
||||
|
||||
class HotelRoomTypeExporter(Component):
|
||||
_name = 'channel.hotel.room.type.exporter'
|
||||
_inherit = 'hotel.channel.exporter'
|
||||
_apply_on = ['channel.hotel.room.type']
|
||||
_usage = 'hotel.room.type.exporter'
|
||||
|
||||
@api.model
|
||||
def modify_room(self, binding):
|
||||
try:
|
||||
return self.backend_adapter.modify_room(
|
||||
binding.channel_room_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'])
|
||||
93
hotel_channel_connector/models/hotel_room_type/importer.py
Normal file
93
hotel_channel_connector/models/hotel_room_type/importer.py
Normal file
@@ -0,0 +1,93 @@
|
||||
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.addons.component.core import Component
|
||||
from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError
|
||||
from odoo.addons.connector.components.mapper import mapping
|
||||
from odoo import fields, api, _
|
||||
from odoo.tools import (
|
||||
DEFAULT_SERVER_DATE_FORMAT,
|
||||
DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
|
||||
|
||||
class HotelRoomTypeImporter(Component):
|
||||
_name = 'channel.hotel.room.type.importer'
|
||||
_inherit = 'hotel.channel.importer'
|
||||
_apply_on = ['channel.hotel.room.type']
|
||||
_usage = 'hotel.room.type.importer'
|
||||
|
||||
def _import_record(self, external_id, job_options=None, **kwargs):
|
||||
return super(HotelRoomTypeImporter, self)._import_record(external_id)
|
||||
|
||||
@api.model
|
||||
def get_rooms(self):
|
||||
count = 0
|
||||
try:
|
||||
results = self.backend_adapter.fetch_rooms()
|
||||
|
||||
channel_room_type_obj = self.env['channel.hotel.room.type']
|
||||
room_mapper = self.component(usage='import.mapper',
|
||||
model_name='channel.hotel.room.type')
|
||||
count = len(results)
|
||||
for room in results:
|
||||
map_record = room_mapper.map_record(room)
|
||||
room_bind = channel_room_type_obj.search([
|
||||
('channel_room_id', '=', room['id'])
|
||||
], limit=1)
|
||||
if room_bind:
|
||||
room_bind.with_context({'wubook_action': False}).write(map_record.values())
|
||||
else:
|
||||
room_bind = channel_room_type_obj.with_context({'wubook_action': False}).create(
|
||||
map_record.values(for_create=True))
|
||||
room_bind.odoo_id.write({
|
||||
'list_price': room['price'],
|
||||
'name': room['name'],
|
||||
})
|
||||
except ChannelConnectorError as err:
|
||||
self.create_issue('room', _("Can't import rooms from WuBook"), err.data['message'])
|
||||
|
||||
return count
|
||||
|
||||
@api.model
|
||||
def fetch_rooms_values(self, dfrom, dto, rooms=False,
|
||||
set_max_avail=False):
|
||||
# Sanitize Dates
|
||||
now_dt = date_utils.now()
|
||||
dfrom_dt = date_utils.get_datetime(dfrom)
|
||||
dto_dt = date_utils.get_datetime(dto)
|
||||
if dto_dt < now_dt:
|
||||
return True
|
||||
if dfrom_dt < now_dt:
|
||||
dfrom_dt = now_dt
|
||||
if dfrom_dt > dto_dt:
|
||||
dfrom_dt, dto_dt = dto_dt, dfrom_dt
|
||||
|
||||
try:
|
||||
results = self.backend_adapter.fetch_rooms_values(
|
||||
dfrom_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
|
||||
dto_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
|
||||
rooms)
|
||||
self._generate_room_values(dfrom, dto, results,
|
||||
set_max_avail=set_max_avail)
|
||||
except ChannelConnectorError as err:
|
||||
self.create_issue('room', _("Can't fetch rooms values from WuBook"),
|
||||
err.data['message'], dfrom=dfrom, dto=dto)
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class HotelRoomTypeImportMapper(Component):
|
||||
_name = 'channel.hotel.room.type.import.mapper'
|
||||
_inherit = 'channel.import.mapper'
|
||||
_apply_on = 'channel.hotel.room.type'
|
||||
|
||||
direct = [
|
||||
('id', 'channel_room_id'),
|
||||
('shortname', 'channel_short_code'),
|
||||
('occupancy', 'ota_capacity'),
|
||||
]
|
||||
|
||||
@mapping
|
||||
def backend_id(self, record):
|
||||
return {'backend_id': self.backend_record.id}
|
||||
@@ -5,7 +5,7 @@
|
||||
<field name="name">channel.backend.form</field>
|
||||
<field name="model">channel.backend</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel Backend">
|
||||
<form string="Hotel Channel Backend">
|
||||
<header>
|
||||
<button name="synchronize_metadata"
|
||||
type="object"
|
||||
@@ -60,6 +60,15 @@
|
||||
string="Import in background"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<label string="Import Rooms" class="oe_inline"/>
|
||||
<div>
|
||||
<button name="import_rooms"
|
||||
type="object"
|
||||
class="oe_highlight"
|
||||
string="Import in background"/>
|
||||
</div>
|
||||
</group>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
@@ -79,7 +88,7 @@
|
||||
</record>
|
||||
|
||||
<record id="action_channel_backend" model="ir.actions.act_window">
|
||||
<field name="name">Channel Backends</field>
|
||||
<field name="name">Hotel Channel Backends</field>
|
||||
<field name="res_model">channel.backend</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<menuitem id="menu_channel_connector_root"
|
||||
parent="connector.menu_connector_root"
|
||||
name="Channel"
|
||||
sequence="10"
|
||||
groups="connector.group_connector_manager"/>
|
||||
|
||||
<menuitem id="menu_cannel_backend"
|
||||
name="Backends"
|
||||
parent="menu_channel_connector_root"
|
||||
action="action_channel_backend"/>
|
||||
|
||||
</odoo>
|
||||
@@ -69,4 +69,13 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="open_hotel_channel_connector_issue_tree_all">
|
||||
<field name="name">Hotel Channel Connector Issues</field>
|
||||
<field name="res_model">hotel.channel.connector.issue</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="search_view_id" ref="view_hotel_channel_connector_issue_form_search"/>
|
||||
<field name="context">{"search_default_to_read":True}</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -1,33 +1,40 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Form view -->
|
||||
<record model="ir.ui.view" id="view_hotel_channel_connector_ota_info_form">
|
||||
<field name="name">hotel.channel.connector.ota.info.form</field>
|
||||
<field name="model">hotel.channel.connector.ota.info</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel OTA's Info" >
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="ota_id" />
|
||||
<field name="name" />
|
||||
<field name="ical" readonly="True" widget="checkbox" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Form view -->
|
||||
<record model="ir.ui.view" id="view_hotel_channel_connector_ota_info_form">
|
||||
<field name="name">hotel.channel.connector.ota.info.form</field>
|
||||
<field name="model">hotel.channel.connector.ota.info</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Channel OTA's Info" >
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="ota_id" />
|
||||
<field name="name" />
|
||||
<field name="ical" readonly="True" widget="checkbox" />
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Tree view -->
|
||||
<record model="ir.ui.view" id="view_hotel_channel_connector_ota_info_tree">
|
||||
<field name="name">hotel.channel.connector.ota.info.tree</field>
|
||||
<field name="model">hotel.channel.connector.ota.info</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Channel OTA's Info">
|
||||
<field name="ota_id" />
|
||||
<field name="name" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
<!-- Tree view -->
|
||||
<record model="ir.ui.view" id="view_hotel_channel_connector_ota_info_tree">
|
||||
<field name="name">hotel.channel.connector.ota.info.tree</field>
|
||||
<field name="model">hotel.channel.connector.ota.info</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Channel OTA's Info">
|
||||
<field name="ota_id" />
|
||||
<field name="name" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="open_hotel_channel_connector_ota_info_tree_all">
|
||||
<field name="name">Hotel Channel Connector OTA's Info</field>
|
||||
<field name="res_model">hotel.channel.connector.ota.info</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user