[WIP][MIG][11.0] channel connector

This commit is contained in:
QS5ELkMu
2018-09-22 01:45:50 +02:00
parent 1c6afa7f3a
commit 5b1abbfa0f
14 changed files with 281 additions and 146 deletions

View File

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

View File

@@ -6,3 +6,4 @@ from . import backend_adapter
from . import binder
from . import importer
from . import exporter
from . import mapper

View File

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

View 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'

View File

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

View File

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

View File

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

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

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

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

View File

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

View File

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

View File

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

View File

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