[WIP] Channel connector

This commit is contained in:
QS5ELkMu
2018-11-03 16:54:29 +01:00
parent fee28b8706
commit a2c357729b
9 changed files with 237 additions and 71 deletions

View File

@@ -12,7 +12,7 @@ from odoo.tools import (
DEFAULT_SERVER_DATETIME_FORMAT)
from .backend_adapter import DEFAULT_WUBOOK_DATE_FORMAT
from odoo.addons.hotel import date_utils
from odoo import api
from odoo import api, fields
_logger = logging.getLogger(__name__)
class HotelChannelConnectorExporter(AbstractComponent):
@@ -25,42 +25,6 @@ class HotelChannelConnectorExporter(AbstractComponent):
return self.push_availability() and self.push_priceplans() and \
self.push_restrictions()
@api.model
def push_availability(self):
room_type_avail_ids = self.env['hotel.room.type.availability'].search([
('wpushed', '=', False),
('date', '>=', date_utils.now(hours=False).strftime(
DEFAULT_SERVER_DATE_FORMAT))
])
room_types = room_type_avail_ids.mapped('room_type_id')
avails = []
for room_type in room_types:
room_type_avails = room_type_avail_ids.filtered(
lambda x: x.room_type_id.id == room_type.id)
days = []
for room_type_avail in room_type_avails:
room_type_avail.with_context({
'wubook_action': False}).write({'wpushed': True})
wavail = room_type_avail.avail
if wavail > room_type_avail.wmax_avail:
wavail = room_type_avail.wmax_avail
date_dt = date_utils.get_datetime(
room_type_avail.date,
dtformat=DEFAULT_SERVER_DATE_FORMAT)
days.append({
'date': date_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
'avail': wavail,
'no_ota': room_type_avail.no_ota and 1 or 0,
# 'booked': room_type_avail.booked and 1 or 0,
})
avails.append({'id': room_type.wrid, 'days': days})
_logger.info("UPDATING AVAILABILITY IN WUBOOK...")
_logger.info(avails)
if any(avails):
self.backend_adapter.update_availability(avails)
return True
@api.model
def push_priceplans(self):
unpushed = self.env['product.pricelist.item'].search([

View File

@@ -31,6 +31,9 @@ class ChannelBackend(models.Model):
pkey = fields.Char('Channel Service PKey')
security_token = fields.Char('Channel Service Security Token')
avail_from = fields.Date('Availability From')
avail_to = fields.Date('Availability To')
@api.multi
def generate_key(self):
for record in self:
@@ -57,6 +60,23 @@ class ChannelBackend(models.Model):
channel_ota_info_obj.import_otas_info(backend)
return True
@api.multi
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)
return True
@api.multi
def push_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.push_availability(backend)
return True
@contextmanager
@api.multi
def work_on(self, model_name, **kwargs):

View File

@@ -142,7 +142,7 @@ class BindingHotelRoomTypeListener(Component):
@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:
if any(record.channel_bind_ids) and 'name' in fields or 'list_price' in fields:
record.channel_bind_ids[0].modify_room()
# @skip_if(lambda self, record, **kwargs: self.no_connector_export(record))

View File

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

View File

@@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from datetime import timedelta
from odoo import api, models, fields
from odoo import api, models, fields, _
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
from odoo.exceptions import ValidationError
from odoo.addons.queue_job.job import job, related_action
@@ -15,7 +15,7 @@ class ChannelHotelRoomTypeAvailability(models.Model):
_name = 'channel.hotel.room.type.availability'
_inherit = 'channel.binding'
_inherits = {'hotel.room.type.availability': 'odoo_id'}
_description = 'Channel Product Pricelist'
_description = 'Channel Availability'
@api.model
def _default_channel_max_avail(self):
@@ -44,19 +44,24 @@ class ChannelHotelRoomTypeAvailability(models.Model):
@job(default_channel='root.channel')
@related_action(action='related_action_unwrap_binding')
@api.multi
def update_availability(self):
self.ensure_one()
if self._context.get('channel_action', True):
with self.backend_id.work_on(self._name) as work:
adapter = work.component(usage='backend.adapter')
date_dt = fields.Date.from_string(self.date)
adapter.update_availability([{
'id': self.odoo_id.room_type_id.channel_room_id,
'days': [{
'date': date_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
'avail': self.odoo_id.avail,
}],
}])
def update_availability(self, backend):
with backend.work_on(self._name) as work:
exporter = work.component(usage='hotel.room.type.availability.exporter')
return exporter.update_availability(self)
@job(default_channel='root.channel')
@api.model
def import_availability(self, backend, date_from, date_to):
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)
@job(default_channel='root.channel')
@api.model
def push_availability(self, backend):
with backend.work_on(self._name) as work:
exporter = work.component(usage='hotel.room.type.availability.exporter')
return exporter.push_availability()
class HotelRoomTypeAvailability(models.Model):
_inherit = 'hotel.room.type.availability'
@@ -79,16 +84,15 @@ class HotelRoomTypeAvailability(models.Model):
if record.avail > max_avail:
issue_obj.sudo().create({
'section': 'avail',
'message': _(r"The new availability can't be greater than \
the actual availability \
\n[%s]\nInput: %d\Limit: %d") % (record.room_type_id.name,
record.avail,
record),
'channel_id': record.room_type_id.channel_bind_ids[0].channel_plan_id,
'internal_message': _(r"The new availability can't be greater than \
the max. availability \
(%s) [Input: %d\Max: %d]") % (record.room_type_id.name,
record.avail,
max_avail),
'date_start': record.date,
'date_end': record.date,
})
# Auto-Fix wubook availability
# Auto-Fix channel availability
self._event('on_fix_channel_availability').notify(record)
return super(HotelRoomTypeAvailability, self)._check_avail()
@@ -97,12 +101,6 @@ class HotelRoomTypeAvailability(models.Model):
if self.room_type_id:
self.channel_max_avail = self.room_type_id.total_rooms_count
@api.multi
def write(self, vals):
if self._context.get('channel_action', True):
vals.update({'channel_pushed': False})
return super(HotelRoomTypeAvailability, self).write(vals)
@api.model
def refresh_availability(self, checkin, checkout, product_id):
date_start = fields.Date.from_string(checkin)
@@ -143,6 +141,32 @@ class HotelRoomTypeAvailability(models.Model):
'avail': avail,
})
class HotelRoomTypeAvailabilityAdapter(Component):
_name = 'channel.hotel.room.type.availability.adapter'
_inherit = 'wubook.adapter'
_apply_on = 'channel.hotel.room.type.availability'
def fetch_rooms_values(self, date_from, date_to, rooms=False):
return super(HotelRoomTypeAvailabilityAdapter, self).fetch_rooms_values(
date_from,
date_to,
rooms)
def update_availability(self, rooms_avail):
return super(HotelRoomTypeAvailabilityAdapter, self).update_availability(
rooms_avail)
class BindingHotelRoomTypeAvailabilityListener(Component):
_name = 'binding.hotel.room.type.listener'
_inherit = 'base.connector.listener'
_apply_on = ['hotel.room.type.availability']
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):
if 'avail' in fields:
for binding in record.channel_bind_ids:
binding.channel_pushed = False
class ChannelBindingHotelRoomTypeAvailabilityListener(Component):
_name = 'channel.binding.hotel.room.type.availability.listener'
_inherit = 'base.connector.listener'

View File

@@ -0,0 +1,74 @@
# 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, fields, _
from odoo.addons.hotel_channel_connector.components.backend_adapter import (
DEFAULT_WUBOOK_DATE_FORMAT)
_logger = logging.getLogger(__name__)
class HotelRoomTypeAvailabilityExporter(Component):
_name = 'channel.hotel.room.type.availability.exporter'
_inherit = 'hotel.channel.exporter'
_apply_on = ['channel.hotel.room.type.availability']
_usage = 'hotel.room.type.availability.exporter'
@api.model
def update_availability(self, binding):
if any(binding.room_type_id.channel_bind_ids):
try:
sday_dt = fields.Date.from_string(binding.date)
# Supossed that only exists one channel connector per record
binding.channel_pushed = True
return self.backend_adapter.update_availability({
'id': binding.room_type_id.channel_bind_ids[0].channel_room_id,
'days': [{
'date': sday_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
'avail': binding.avail,
'no_ota': binding.no_ota,
}],
})
except ChannelConnectorError as err:
self.create_issue(
'room',
_("Can't update availability in WuBook"),
err.data['message'])
def push_availability(self):
channel_room_type_avail_ids = self.env['channel.hotel.room.type.availability'].search([
('channel_pushed', '=', False),
('date', '>=', fields.Date.today())
])
room_types = channel_room_type_avail_ids.mapped('room_type_id')
avails = []
for room_type in room_types:
if any(room_type.channel_bind_ids):
channel_room_type_avails = channel_room_type_avail_ids.filtered(
lambda x: x.room_type_id.id == room_type.id)
days = []
for channel_room_type_avail in channel_room_type_avails:
channel_room_type_avail.channel_pushed = True
cavail = channel_room_type_avail.avail
if channel_room_type_avail.channel_max_avail >= 0 and \
cavail > channel_room_type_avail.channel_max_avail:
cavail = channel_room_type_avail.channel_max_avail
date_dt = fields.Date.from_string(channel_room_type_avail.date)
days.append({
'date': date_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
'avail': cavail,
'no_ota': channel_room_type_avail.no_ota and 1 or 0,
# 'booked': room_type_avail.booked and 1 or 0,
})
avails.append({'id': room_type.channel_bind_ids[0].channel_room_id, 'days': days})
_logger.info("UPDATING AVAILABILITY IN WUBOOK...")
_logger.info(avails)
if any(avails):
try:
self.backend_adapter.update_availability(avails)
except ChannelConnectorError as err:
self.create_issue(
'room',
_("Can't update availability in WuBook"),
err.data['message'])

View File

@@ -2,11 +2,11 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import timedelta
from datetime import date, timedelta
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.addons.connector.components.mapper import mapping, external_to_m2o
from odoo.addons.hotel import date_utils
from odoo import fields, api, _
_logger = logging.getLogger(__name__)
@@ -18,6 +18,59 @@ class HotelRoomTypeAvailabilityImporter(Component):
_apply_on = ['channel.hotel.room.type.availability']
_usage = 'hotel.room.type.availability.importer'
@api.model
def get_availability(self, date_from, date_to):
now_dt = date.today()
dfrom_dt = fields.Date.from_string(date_from)
dto_dt = fields.Date.from_string(date_to)
if dfrom_dt < now_dt:
dfrom_dt = now_dt
if dfrom_dt > dto_dt:
dfrom_dt, dto_dt = dto_dt, dfrom_dt
if dto_dt < now_dt:
return True
count = 0
try:
results = self.backend_adapter.fetch_rooms_values(date_from, date_to)
channel_room_type_avail_obj = self.env['channel.hotel.room.type.availability']
channel_room_type_obj = self.env['channel.hotel.room.type']
room_avail_mapper = self.component(
usage='import.mapper',
model_name='channel.hotel.room.type.availability')
count = len(results)
for room_k, room_v in results.items():
iter_day = dfrom_dt
channel_room_type = channel_room_type_obj.search([
('channel_room_id', '=', room_k)
], limit=1)
if channel_room_type:
for room in room_v:
room.update({
'room_type_id': channel_room_type.odoo_id.id,
'date': fields.Date.to_string(iter_day),
})
map_record = room_avail_mapper.map_record(room)
room_type_avail_bind = channel_room_type_avail_obj.search([
('room_type_id', '=', room['room_type_id']),
('date', '=', room['date'])
], limit=1)
if room_type_avail_bind:
room_type_avail_bind.with_context({
'wubook_action': False
}).write(map_record.values())
else:
room_type_avail_bind = channel_room_type_avail_obj.with_context({
'wubook_action': False
}).create(map_record.values(for_create=True))
iter_day += timedelta(days=1)
except ChannelConnectorError as err:
self.create_issue(
'room',
_("Can't import availability from WuBook"),
err.data['message'])
return count
class HotelRoomTypeAvailabilityImportMapper(Component):
_name = 'channel.hotel.room.type.availability.import.mapper'
@@ -28,10 +81,13 @@ class HotelRoomTypeAvailabilityImportMapper(Component):
('no_ota', 'no_ota'),
('booked', 'booked'),
('avail', 'avail'),
('room_type_id', 'room_type_id'),
('date', 'date'),
]
@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}
@mapping
def room_type_id(self, record):
return {'room_type_id': record['room_type_id']}

View File

@@ -85,8 +85,13 @@ class HotelRoomTypeRestriction(models.Model):
names = []
for name in org_names:
restriction_id = room_type_restriction_obj.browse(name[0])
if restriction_id.channel_bind_ids.channel_plan_id:
names.append((name[0], '%s (WuBook)' % name[1]))
if any(restriction_id.channel_bind_ids) and \
restriction_id.channel_bind_ids[0].channel_plan_id:
names.append((
name[0],
'%s (%s Backend)' % (name[1],
restriction_id.channel_bind_ids[0].backend_id.name),
))
else:
names.append((name[0], name[1]))
return names

View File

@@ -84,6 +84,28 @@
string="Import in background"/>
</div>
</group>
<group>
<label string="Import Availability" class="oe_inline"/>
<div>
<field name="avail_from" class="oe_inline" nolabel="1"/>
<field name="avail_to" class="oe_inline" nolabel="1"/>
<button name="import_availability"
type="object"
class="oe_highlight"
string="Import in background"/>
</div>
</group>
</page>
<page name="export" string="Exports">
<group>
<label string="Push Availability" class="oe_inline"/>
<div>
<button name="push_availability"
type="object"
class="oe_highlight"
string="Export in background"/>
</div>
</group>
</page>
</notebook>
</sheet>