[WIP][MIG][11.0] connector

This commit is contained in:
QS5ELkMu
2018-09-24 01:26:57 +02:00
parent 5b1abbfa0f
commit c36c32890c
47 changed files with 690 additions and 646 deletions

View File

@@ -47,12 +47,12 @@
'views/inherit_product_product.xml',
'views/hotel_room_amenities_type.xml',
'views/hotel_room_amenities.xml',
'views/reservation_restriction_views.xml',
'views/reservation_restriction_item_views.xml',
'views/hotel_room_type_restriction_views.xml',
'views/hotel_room_type_restriction_item_views.xml',
'views/hotel_reservation.xml',
# 'views/room_type_views.xml',
'views/cardex.xml',
'views/room_type_availability.xml',
'views/hotel_room_type_availability.xml',
# 'views/hotel_dashboard.xml',
'data/cron_jobs.xml',
'data/records.xml',

View File

@@ -16,8 +16,7 @@ class HotelRoomTypeRestrictionItem(models.Model):
# required=True, ondelete='cascade')
room_type_id = fields.Many2one('hotel.room.type', 'Room Type',
required=True, ondelete='cascade')
date_start = fields.Date('From')
date_end = fields.Date("To")
date = fields.Date('Date')
applied_on = fields.Selection([
('1_global', 'Global'),
# ('0_room_type', 'Virtual Room')], string="Apply On", required=True,
@@ -35,7 +34,7 @@ class HotelRoomTypeRestrictionItem(models.Model):
closed_arrival = fields.Boolean('Closed Arrival')
_sql_constraints = [('room_type_registry_unique',
'unique(restriction_id, room_type_id, date_start, date_end)',
'unique(restriction_id, room_type_id, date)',
'Only can exists one restriction in the same day for the same room type!')]
@api.constrains('min_stay', 'min_stay_arrival', 'max_stay',
@@ -52,17 +51,6 @@ class HotelRoomTypeRestrictionItem(models.Model):
raise ValidationError(
("Max. Stay Arrival can't be less than zero"))
@api.constrains('date_start', 'date_end')
def _check_date_start_date_end(self):
if self.applied_on == '1_global':
self.date_start = False
self.date_end = False
elif self.date_start and self.date_end:
date_start_dt = fields.Date.from_string(self.date_start)
date_end_dt = fields.Date.from_string(self.date_end)
if date_end_dt < date_start_dt:
raise ValidationError(_("Invalid Dates"))
@api.constrains('applied_on')
def _check_applied_on(self):
count = self.search_count([('applied_on', '=', '1_global')])

View File

@@ -2,7 +2,7 @@
<odoo>
<!-- FORM restriction -->
<record id="reservation_restriction_item_view_form" model="ir.ui.view">
<record id="room_type_restriction_item_view_form" model="ir.ui.view">
<field name="name">hotel.room.type.restriction.item.form</field>
<field name="model">hotel.room.type.restriction.item</field>
<field name="arch" type="xml">
@@ -13,12 +13,7 @@
<field name="room_type_id" attrs="{'invisible':[['applied_on', '=', '1_global']]}" required="True"/>
</group>
<group>
<group>
<field name="date_start"/>
</group>
<group>
<field name="date_end"/>
</group>
<field name="date"/>
</group>
<group>
<group>
@@ -37,7 +32,7 @@
</record>
<!-- TREE restriction -->
<record id="reservation_restriction_item_view_tree" model="ir.ui.view">
<record id="room_type_restriction_item_view_tree" model="ir.ui.view">
<field name="name">hotel.room.type.restriction.item.tree</field>
<field name="model">hotel.room.type.restriction.item</field>
<field name="arch" type="xml">
@@ -45,8 +40,7 @@
<field name="applied_on"/>
<!-- <field name="room_type_id" attrs="{'invisible':[['applied_on', '=', '1_room_type']]}"/> -->
<field name="room_type_id" attrs="{'invisible':[['applied_on', '=', '0_room_type']]}"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="date"/>
<field name="min_stay"/>
<field name="closed"/>
</tree>

View File

@@ -2,7 +2,7 @@
<odoo>
<!-- FORM restriction -->
<record id="reservation_restriction_view_form" model="ir.ui.view">
<record id="room_type_restriction_view_form" model="ir.ui.view">
<field name="name">hotel.room.type.restriction.form</field>
<field name="model">hotel.room.type.restriction</field>
<field name="arch" type="xml">
@@ -21,8 +21,7 @@
<field name="applied_on"/>
<!-- <field name="room_type_id" attr="{'invisible':[['applied_on', '=', '1_room_type']]}"/> -->
<field name="room_type_id" attr="{'invisible':[['applied_on', '=', '0_room_type']]}"/>
<field name="date_start"/>
<field name="date_end"/>
<field name="date"/>
<field name="min_stay"/>
<field name="closed"/>
</tree>
@@ -34,7 +33,7 @@
</record>
<!-- TREE restriction -->
<record id="reservation_restriction_view_tree" model="ir.ui.view">
<record id="room_type_restriction_view_tree" model="ir.ui.view">
<field name="name">hotel.room.type.restriction.tree</field>
<field name="model">hotel.room.type.restriction</field>
<field name="arch" type="xml">
@@ -46,7 +45,7 @@
</record>
<!-- Action of reservation restriction -->
<record model="ir.actions.act_window" id="reservation_restriction_action">
<record model="ir.actions.act_window" id="room_type_restriction_action">
<field name="name">Reservation restrictions</field>
<field name="res_model">hotel.room.type.restriction</field>
<field name="view_type">form</field>
@@ -55,7 +54,7 @@
<!-- MENUS -->
<menuitem name="Restrictions" id="reservation_restriction_menu"
action="reservation_restriction_action" sequence="22"
action="room_type_restriction_action" sequence="22"
parent="hotel.configuration_others"/>
</odoo>

View File

@@ -84,8 +84,7 @@ class HotelCalendarManagement(models.TransientModel):
for k_res in restrictions.keys():
for restriction in restrictions[k_res]:
res_id = room_type_rest_item_obj.search([
('date_start', '>=', restriction['date']),
('date_end', '<=', restriction['date']),
('date', '=', restriction['date']),
('restriction_id', '=', int(restriction_id)),
('applied_on', '=', '0_room_type'),
('room_type_id', '=', int(k_res)),
@@ -93,8 +92,7 @@ class HotelCalendarManagement(models.TransientModel):
vals = self._get_restrictions_values(restriction)
if not res_id:
vals.update({
'date_start': restriction['date'],
'date_end': restriction['date'],
'date': restriction['date'],
'restriction_id': int(restriction_id),
'applied_on': '0_room_type',
'room_type_id': int(k_res),
@@ -159,10 +157,9 @@ class HotelCalendarManagement(models.TransientModel):
def _hcalendar_restriction_json_data(self, restrictions):
json_data = {}
for rec in restrictions:
# TODO: date_end - date_start loop
json_data.setdefault(rec.room_type_id.id, []).append({
'id': rec.id,
'date': rec.date_start,
'date': rec.date,
'min_stay': rec.min_stay,
'min_stay_arrival': rec.min_stay_arrival,
'max_stay': rec.max_stay,
@@ -282,7 +279,7 @@ class HotelCalendarManagement(models.TransientModel):
room_type_rest_it_obj = self.env['hotel.room.type.restriction.item']
restriction_item_ids = room_type_rest_it_obj.search([
('date_start', '>=', dfrom), ('date_end', '<=', dto),
('date', '>=', dfrom), ('date', '<=', dto),
('restriction_id', '=', restriction_id),
('applied_on', '=', '0_room_type'),
])

View File

@@ -162,7 +162,7 @@ class HotelReservation(models.Model):
restriction_id = int(restriction_id)
date_start = fields.Date.from_string(dfrom) - timedelta(days=1)
date_end = fields.Date.from_string(dto)
date_diff = abs((date_end - date_sart).days) + 1
date_diff = abs((date_end - date_start).days) + 1
# Get Prices
json_rooms_rests = {}
room_types = self.env['hotel.room.type'].search(
@@ -176,8 +176,7 @@ class HotelReservation(models.Model):
ndate_str = ndate.strftime(DEFAULT_SERVER_DATE_FORMAT)
rest_id = room_type_rest_obj.search([
('room_type_id', '=', room_type.id),
('date_start', '>=', ndate_str),
('date_end', '<=', ndate_str),
('date', '>=', ndate_str),
('applied_on', '=', '0_room_type'),
('restriction_id', '=', restriction_id)
], limit=1)

View File

@@ -10,7 +10,7 @@ class HotelRoomType(models.Model):
@api.multi
def unlink(self):
room_type_pr_cached_obj = self.env['hotel.room.pricelist.cached']
room_type_pr_cached_obj = self.env['room.pricelist.cached']
for record in self:
pr_chached = room_type_pr_cached_obj.search([
('room_id', '=', record.id)

View File

@@ -10,7 +10,7 @@ class HotelRoomTypeResrtrictionItem(models.Model):
@api.model
def create(self, vals):
res = super(HotelVirtualRoomResrtrictionItem, self).create(vals)
res = super(HotelRoomTypeResrtrictionItem, self).create(vals)
restrictions_parity_id = self.env['ir.default'].sudo().get(
'res.config.settings', 'parity_restrictions_id')
if restrictions_parity_id:
@@ -20,7 +20,7 @@ class HotelRoomTypeResrtrictionItem(models.Model):
self.applied_on == '0_room_type':
self.env['bus.hotel.calendar'].send_restriction_notification({
'restriction_id': self.restriction_id.id,
'date': self.date_start,
'date': self.date,
'min_stay': self.min_stay,
'min_stay_arrival': self.min_stay_arrival,
'max_stay': self.max_stay,
@@ -39,7 +39,7 @@ class HotelRoomTypeResrtrictionItem(models.Model):
'res.config.settings', 'parity_restrictions_id')
if restrictions_parity_id:
restrictions_parity_id = int(restrictions_parity_id)
ret_vals = super(HotelVirtualRoomResrtrictionItem, self).write(vals)
ret_vals = super(HotelRoomTypeResrtrictionItem, self).write(vals)
bus_hotel_calendar_obj = self.env['bus.hotel.calendar']
for record in self:
@@ -48,7 +48,7 @@ class HotelRoomTypeResrtrictionItem(models.Model):
continue
bus_hotel_calendar_obj.send_restriction_notification({
'restriction_id': record.restriction_id.id,
'date': record.date_start,
'date': record.date,
'min_stay': record.min_stay,
'min_stay_arrival': record.min_stay_arrival,
'max_stay': record.max_stay,
@@ -86,7 +86,7 @@ class HotelRoomTypeResrtrictionItem(models.Model):
'room_type_id': record.room_type_id.id,
'id': record.id,
})
res = super(HotelVirtualRoomResrtrictionItem, self).unlink()
res = super(HotelRoomTypeResrtrictionItem, self).unlink()
bus_hotel_calendar_obj = self.env['bus.hotel.calendar']
for uval in unlink_vals:
bus_hotel_calendar_obj.send_restriction_notification(uval)

View File

@@ -18,7 +18,6 @@
},
'data': [
'data/cron_jobs.xml',
'wizard/wubook_installer.xml',
'wizard/wubook_import_plan_prices.xml',
'wizard/wubook_import_plan_restrictions.xml',
'wizard/wubook_import_availability.xml',
@@ -29,10 +28,9 @@
'views/inherited_hotel_folio_views.xml',
'views/inherited_product_pricelist_views.xml',
'views/inherited_product_pricelist_item_views.xml',
'views/inherited_reservation_restriction_views.xml',
'views/inherited_reservation_restriction_item_views.xml',
'views/inherited_hotel_room_type_restriction_views.xml',
'views/inherited_res_partner_views.xml',
'views/hotel_channel_connector_ota_info_views.xml',
'views/channel_ota_info_views.xml',
'views/hotel_channel_connector_issue_views.xml',
'views/channel_hotel_reservation_views.xml',
'views/channel_hotel_room_type_views.xml',

View File

@@ -12,4 +12,5 @@ class HotelConnectorModelBinder(Component):
'channel.hotel.room.type.availability',
'channel.hotel.room.type.restriction',
'channel.product.pricelist',
'channel.ota.info',
]

View File

@@ -14,7 +14,7 @@ class BaseHotelChannelConnectorComponent(AbstractComponent):
dfrom=False, dto=False):
self.env['hotel.channel.connector.issue'].sudo().create({
'section': section,
'message': message,
'internal_message': message,
'channel_object_id': channel_object_id,
'channel_message': channel_message,
'date_start': dfrom,

View File

@@ -2,6 +2,10 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
import psycopg2
from contextlib import contextmanager
from odoo.addons.connector.exception import (IDMissingInBackend,
RetryableJobError)
from odoo.addons.component.core import AbstractComponent
from odoo.tools import (
DEFAULT_SERVER_DATE_FORMAT,

View File

@@ -663,28 +663,6 @@ class HotelChannelConnectorImporter(AbstractComponent):
return True
@api.model
def _generate_wubook_channel_info(self, channels):
channel_info_obj = self.env['wubook.channel.info']
count = 0
for k_cid, v_cid in channels.iteritems():
vals = {
'name': v_cid['name'],
'ical': v_cid['ical'] == 1,
}
channel_info = channel_info_obj.search([
('wid', '=', k_cid)
], limit=1)
if channel_info:
channel_info.write(vals)
else:
vals.update({
'wid': k_cid
})
channel_info_obj.create(vals)
count = count + 1
return count
@api.model
def fetch_booking(self, channel_reservation_id):
try:
@@ -793,19 +771,6 @@ class HotelChannelConnectorImporter(AbstractComponent):
return False
return True
@api.model
def import_channels_info(self):
try:
results = self.backend_adapter.get_channels_info()
count = self._generate_wubook_channel_info(results)
except ChannelConnectorError as err:
self.create_issue(
'channel',
_("Can't import channels info from wubook"),
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

View File

@@ -9,16 +9,19 @@
<menuitem id="menu_cannel_backend"
name="Backends"
sequence="1"
parent="menu_channel_connector_root"
action="action_channel_backend"/>
<menuitem id="hotel_channel_connector_ota_info_menu"
<menuitem id="channel_ota_info_menu"
name="OTA's"
action="open_hotel_channel_connector_ota_info_tree_all"
sequence="2"
action="open_channel_ota_info_tree_all"
parent="menu_channel_connector_root"/>
<menuitem id="hotel_channel_connector_issue_menu"
name="Issues"
sequence="3"
action="open_hotel_channel_connector_issue_tree_all"
parent="menu_channel_connector_root"/>

View File

@@ -2,17 +2,16 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import channel_binding
from . import channel_backend
from . import res_config
from . import hotel_room_type
from . import product_pricelist
from . import inherited_product_pricelist_item
from . import hotel_room_type_restriction
from . import inherited_reservation_restriction_item
from . import hotel_room_type_restriction_item
from . import hotel_room_type_availability
from . import hotel_reservation
from . import inherited_hotel_folio
from . import inherited_res_partner
from . import hotel_channel_connector_ota_info
from . import channel_ota_info
from . import hotel_channel_connector_issue
from . import res_config

View File

@@ -27,19 +27,27 @@ class ChannelBackend(models.Model):
server = fields.Char('Channel Service Server',
default='https://wired.wubook.net/xrws/')
pkey = fields.Char('Channel Service PKey')
security_token = fields.Char('Channel Service Security Token')
@api.multi
def import_reservations(self):
channel_hotel_reservation = self.env['channel.hotel.reservation']
channel_hotel_reservation_obj = self.env['channel.hotel.reservation']
for backend in self:
channel_hotel_reservation.import_reservations(backend)
channel_hotel_reservation_obj.import_reservations(backend)
return True
@api.multi
def import_rooms(self):
channel_hotel_room_type = self.env['channel.hotel.room.type']
channel_hotel_room_type_obj = self.env['channel.hotel.room.type']
for backend in self:
channel_hotel_room_type.import_rooms(backend)
channel_hotel_room_type_obj.import_rooms(backend)
return True
@api.multi
def import_otas_info(self):
channel_ota_info_obj = self.env['channel.ota.info']
for backend in self:
channel_ota_info_obj.import_otas_info(backend)
return True
@contextmanager
@@ -56,3 +64,163 @@ class ChannelBackend(models.Model):
_super = super(ChannelBackend, self)
with _super.work_on(model_name, channel_api=channel_api, **kwargs) as work:
yield work
# Dangerus method: Usefull for cloned instances with new wubook account
@api.multi
def resync(self):
self.ensure_one()
now_utc_dt = fields.Date.now()
now_utc_str = now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
# Reset Issues
issue_ids = self.env['wubook.issue'].search([])
issue_ids.write({
'to_read': False
})
# Push Virtual Rooms
wubook_obj = self.env['wubook'].with_context({
'init_connection': False
})
if wubook_obj.init_connection():
ir_seq_obj = self.env['ir.sequence']
room_types = self.env['hotel.room.type'].search([])
for room_type in room_types:
shortcode = ir_seq_obj.next_by_code('hotel.room.type')[:4]
channel_room_id = wubook_obj.create_room(
shortcode,
room_type.name,
room_type.wcapacity,
room_type.list_price,
room_type.total_rooms_count
)
if channel_room_id:
room_type.with_context(wubook_action=False).write({
'channel_room_id': channel_room_id,
'wscode': shortcode,
})
else:
room_type.with_context(wubook_action=False).write({
'channel_room_id': '',
'wscode': '',
})
# Create Restrictions
room_type_rest_obj = self.env['hotel.room.type.restriction']
restriction_ids = room_type_rest_obj.search([])
for restriction in restriction_ids:
if restriction.wpid != '0':
channel_plan_id = wubook_obj.create_rplan(restriction.name)
restriction.write({
'channel_plan_id': channel_plan_id or ''
})
# Create Pricelist
pricelist_ids = self.env['product.pricelist'].search([])
for pricelist in pricelist_ids:
channel_plan_id = wubook_obj.create_plan(pricelist.name, pricelist.is_daily_plan)
pricelist.write({
'channel_plan_id': channel_plan_id or ''
})
wubook_obj.close_connection()
# Reset Folios
folio_ids = self.env['hotel.folio'].search([])
folio_ids.with_context(wubook_action=False).write({
'wseed': '',
})
# Reset Reservations
reservation_ids = self.env['hotel.reservation'].search([
('channel_reservation_id', '!=', ''),
('channel_reservation_id', '!=', False)
])
reservation_ids.with_context(wubook_action=False).write({
'channel_reservation_id': '',
'ota_id': False,
'ota_reservation_id': '',
'is_from_ota': False,
'wstatus': 0
})
# Get Parity Models
pricelist_id = int(self.env['ir.default'].sudo().get(
'res.config.settings', 'parity_pricelist_id'))
restriction_id = int(self.env['ir.default'].sudo().get(
'res.config.settings', 'parity_restrictions_id'))
room_type_restr_it_obj = self.env['hotel.room.type.restriction.item']
# Secure Wubook Input
restriction_item_ids = room_type_restr_it_obj.search([
('applied_on', '=', '0_room_type'),
('date_start', '<', now_utc_str),
])
if any(restriction_item_ids):
restriction_item_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push restrictions
restriction_item_ids = room_type_restr_it_obj.search([
('restriction_id', '=', restriction_id),
('applied_on', '=', '0_room_type'),
('wpushed', '=', True),
('date_start', '>=', now_utc_str),
])
if any(restriction_item_ids):
restriction_item_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Secure Wubook Input
pricelist_item_ids = self.env['product.pricelist.item'].search([
('applied_on', '=', '1_product'),
('compute_price', '=', 'fixed'),
('date_start', '<', now_utc_str),
])
if any(pricelist_item_ids):
pricelist_item_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push pricelists
pricelist_item_ids = self.env['product.pricelist.item'].search([
('pricelist_id', '=', pricelist_id),
('applied_on', '=', '1_product'),
('compute_price', '=', 'fixed'),
('wpushed', '=', True),
('date_start', '>=', now_utc_str),
])
if any(pricelist_item_ids):
pricelist_item_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Secure Wubook Input
availabity_ids = self.env['hotel.room.type.availability'].search([
('date', '<', now_utc_str),
])
if any(availabity_ids):
availabity_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push availability
availabity_ids = self.env['hotel.room.type.availability'].search([
('wpushed', '=', True),
('date', '>=', now_utc_str),
])
if any(availabity_ids):
availabity_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Generate Security Token
self.env['ir.default'].sudo().set(
'wubook.config.settings',
'wubook_push_security_token',
binascii.hexlify(os.urandom(16)).decode())
self.env.cr.commit() # FIXME: Need do this
# Push Changes
if wubook_obj.init_connection():
wubook_obj.push_activation()
wubook_obj.import_channels_info()
wubook_obj.push_changes()
wubook_obj.close_connection()

View File

@@ -15,3 +15,8 @@ class ChannelBinding(models.AbstractModel):
string='Hotel Channel Connector Backend',
required=True,
ondelete='restrict')
_sql_constraints = [
('channel_uniq', 'unique(backend_id, external_id)',
'A binding already exists with the same Channel ID.'),
]

View File

@@ -0,0 +1,5 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import common
from . import importer

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.addons.queue_job.job import job
from odoo.addons.component.core import Component
class ChannelOtaInfo(models.Model):
_name = 'channel.ota.info'
_inherit = 'channel.binding'
_description = 'Channel OTA Info'
ota_id = fields.Char("Channel OTA ID", required=True)
name = fields.Char("OTA Name", required=True)
ical = fields.Boolean("ical", default=False)
@job(default_channel='root.channel')
@api.model
def import_otas_info(self, backend):
with backend.work_on(self._name) as work:
importer = work.component(usage='ota.info.importer')
return importer.import_otas_info()
class HotelRoomTypeAdapter(Component):
_name = 'channel.ota.info.adapter'
_inherit = 'wubook.adapter'
_apply_on = 'channel.ota.info'
def fetch_rooms(self):
return super(HotelRoomTypeAdapter, self).fetch_rooms()

View File

@@ -0,0 +1,62 @@
# 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 ChannelOtaInfoImporter(Component):
_name = 'channel.ota.info.importer'
_inherit = 'hotel.channel.importer'
_apply_on = ['channel.ota.info']
_usage = 'ota.info.importer'
@api.model
def import_otas_info(self):
count = 0
try:
results = self.backend_adapter.get_channels_info()
channel_ota_info_obj = self.env['channel.ota.info']
ota_info_mapper = self.component(usage='import.mapper',
model_name='channel.ota.info')
for ota_id in results.keys():
vals = {
'id': ota_id,
'name': results[ota_id]['name'],
'ical': results[ota_id]['ical'] == 1,
}
map_record = ota_info_mapper.map_record(vals)
ota_info_bind = channel_ota_info_obj.search([
('ota_id', '=', ota_id)
], limit=1)
if ota_info_bind:
ota_info_bind.write(map_record.values())
else:
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'])
return count
class ChannelOtaInfoImportMapper(Component):
_name = 'channel.ota.info.import.mapper'
_inherit = 'channel.import.mapper'
_apply_on = 'channel.ota.info'
direct = [
('id', 'ota_id'),
('name', 'name'),
('ical', 'ical'),
]
@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}

View File

@@ -1,20 +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 odoo.addons.queue_job.job import job
class HotelChannelConnectorOTAInfo(models.Model):
_name = 'hotel.channel.connector.ota.info'
ota_id = fields.Char("Channel OTA ID", required=True)
name = fields.Char("OTA Name", required=True)
ical = fields.Boolean("ical", default=False)
@job(default_channel='root.channel')
@api.multi
def import_channels_info(self):
self.ensure_one()
with self.backend_id.work_on(self._name) as work:
importer = work.component(usage='channel.importer')
return importer.import_channels_info()

View File

@@ -26,7 +26,7 @@ class ChannelHotelReservation(models.Model):
required=True,
ondelete='cascade')
channel_reservation_id = fields.Char("Channel Reservation ID", readonly=True, old_name='wrid')
ota_id = fields.Many2one('hotel.channel.connector.ota.info',
ota_id = fields.Many2one('channel.ota.info',
string='Channel OTA ID',
readonly=True,
old_name='wchannel_id')

View File

@@ -23,6 +23,15 @@ 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')
@api.onchange('room_ids')
def _get_capacity(self):
for rec in self:
rec.ota_capacity = rec.odoo_id.get_capacity()
def _check_self_unlink(self):
if not self.odoo_id:
self.sudo().unlink()
@job(default_channel='root.channel')
@api.model
def import_rooms(self, backend):
@@ -48,53 +57,30 @@ class ChannelHotelRoomType(models.Model):
@api.multi
def create_room(self):
self.ensure_one()
if self._context.get('channel_action', True):
seq_obj = self.env['ir.sequence']
shortcode = seq_obj.next_by_code('hotel.room.type')[:4]
if not self.channel_room_id:
with self.backend_id.work_on(self._name) as work:
adapter = work.component(usage='backend.adapter')
try:
channel_room_id = adapter.create_room(
shortcode,
self.name,
self.ota_capacity,
self.list_price,
self.total_rooms_count)
if channel_room_id:
self.write({
'channel_room_id': channel_room_id,
'channel_short_code': shortcode,
})
except ValidationError as e:
self.create_issue('room', "Can't create room on channel", "sss")
exporter = work.component(usage='hotel.room.type.exporter')
exporter.create_room(self)
@job(default_channel='root.channel')
@related_action(action='related_action_unwrap_binding')
@api.multi
def modify_room(self):
_logger.info("PASA A =======")
self.ensure_one()
_logger.info("PASA b =======")
if self._context.get('wubook_action', True) and self.channel_room_id:
_logger.info("PASA C =======")
if self.channel_room_id:
with self.backend_id.work_on(self._name) as work:
_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')
@api.multi
def delete_room(self):
self.ensure_one()
if self._context.get('channel_action', True) and self.channel_room_id:
if self.channel_room_id:
with self.backend_id.work_on(self._name) as work:
adapter = work.component(usage='backend.adapter')
try:
adapter.delete_room(self.channel_room_id)
except ValidationError as e:
self.create_issue('room', "Can't delete room on channel", "sss")
exporter = work.component(usage='hotel.room.type.exporter')
exporter.delete_room(self)
class HotelRoomType(models.Model):
_inherit = 'hotel.room.type'
@@ -104,10 +90,16 @@ class HotelRoomType(models.Model):
inverse_name='odoo_id',
string='Hotel Channel Connector Bindings')
capacity = fields.Integer("Capacity", compute="_compute_capacity")
@api.multi
def _compute_capacity(self):
for record in self:
record.capacity = record.get_capacity()
@api.onchange('room_ids')
def _get_capacity(self):
for rec in self:
rec.channel_bind_ids.ota_capacity = rec.get_capacity()
def _onchange_room_ids(self):
self._compute_capacity()
@api.multi
def get_restrictions(self, date):
@@ -115,13 +107,26 @@ class HotelRoomType(models.Model):
'res.config.settings', 'parity_restrictions_id'))
self.ensure_one()
restriction = self.env['hotel.room.type.restriction.item'].search([
('date_start', '=', date),
('date_end', '=', date),
('date', '=', date),
('room_type_id', '=', self.id),
('restriction_id', '=', restriction_plan_id)
], limit=1)
return restriction
@api.multi
def create_bindings(self):
backends = self.env['channel.backend'].search([])
binding_obj = self.env['channel.hotel.room.type']
for backend in backends:
binding = binding_obj.search([
('odoo_id', '=', self.id),
('backend_id', '=', backend.id)], limit=1)
if not binding:
binding_obj.sudo().create({
'odoo_id': self.id,
'backend_id': backend.id,
})
class HotelRoomTypeAdapter(Component):
_name = 'channel.hotel.room.type.adapter'
_inherit = 'wubook.adapter'
@@ -140,6 +145,10 @@ class BindingHotelRoomTypeListener(Component):
if '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))
# def on_record_create(self, record, fields=None):
# record.create_bindings()
class ChannelBindingRoomTypeListener(Component):
_name = 'channel.binding.room.type.listener'
_inherit = 'base.connector.listener'
@@ -147,11 +156,11 @@ class ChannelBindingRoomTypeListener(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_room()
record.create_room()
@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_room()
record.delete_room()
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):

View File

@@ -23,3 +23,31 @@ class HotelRoomTypeExporter(Component):
binding.channel_short_code)
except ChannelConnectorError as err:
self.create_issue('room', _("Can't modify rooms in WuBook"), err.data['message'])
@api.model
def delete_room(self, binding):
try:
return self.backend_adapter.delete_room(binding.channel_room_id)
except ChannelConnectorError as err:
self.create_issue('room', _("Can't delete room in WuBook"), err.data['message'])
@api.model
def create_room(self, binding):
try:
seq_obj = self.env['ir.sequence']
short_code = seq_obj.next_by_code('hotel.room.type')[:4]
external_id = self.backend_adapter.create_room(
short_code,
binding.name,
binding.ota_capacity,
binding.list_price,
binding.total_rooms_count
)
binding.write({
'channel_room_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'])
else:
self.binder.bind(external_id, binding)

View File

@@ -1,14 +1,19 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import 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.hotel import date_utils
from odoo import fields, api, _
from odoo.tools import (
DEFAULT_SERVER_DATE_FORMAT,
DEFAULT_SERVER_DATETIME_FORMAT)
from odoo.addons.hotel_channel_connector.components.backend_adapter import DEFAULT_WUBOOK_DATE_FORMAT
_logger = logging.getLogger(__name__)
class HotelRoomTypeImporter(Component):
@@ -17,9 +22,6 @@ class HotelRoomTypeImporter(Component):
_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
@@ -40,10 +42,6 @@ class HotelRoomTypeImporter(Component):
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'])
@@ -76,6 +74,83 @@ class HotelRoomTypeImporter(Component):
return False
return True
@api.model
def _map_room_values_availability(self, day_vals, set_max_avail):
channel_room_type_avail_obj = self.env['channel.hotel.room.type.availability']
room_avail_mapper = self.component(usage='import.mapper',
model_name='channel.hotel.room.type.availability')
map_record = room_avail_mapper.map_record(day_vals)
map_record.update(channel_pushed=True)
if set_max_avail:
map_record.update(max_avail=day_vals.get('avail', 0))
channel_room_type_avail = channel_room_type_avail_obj.search([
('room_type_id', '=', day_vals['room_type_id']),
('date', '=', day_vals['date'])
], limit=1)
if channel_room_type_avail:
channel_room_type_avail.with_context({
'wubook_action': False,
}).write(map_record.values())
else:
channel_room_type_avail_obj.with_context({
'wubook_action': False,
'mail_create_nosubscribe': True,
}).create(map_record.values(for_create=True))
@api.model
def _map_room_values_restrictions(self, day_vals):
channel_room_type_restr_item_obj = self.env['channel.hotel.room.type.restriction.item']
room_restriction_mapper = self.component(
usage='import.mapper',
model_name='channel.hotel.room.type.restriction.item')
map_record = room_restriction_mapper.map_record(day_vals)
map_record.update(channel_pushed=True)
room_type_restr = channel_room_type_restr_item_obj.search([
('room_type_id', '=', day_vals['room_type_id']),
('applied_on', '=', '0_room_type'),
('date', '=', day_vals['date']),
('restriction_id', '=', day_vals['restriction_plan_id']),
])
if room_type_restr:
room_type_restr.with_context({
'wubook_action': False,
}).write(map_record.values())
else:
channel_room_type_restr_item_obj.with_context({
'wubook_action': False,
}).create(map_record.values(for_create=True))
@api.model
def _generate_room_values(self, dfrom, dto, values, set_max_avail=False):
channel_room_type_restr_obj = self.env['channel.hotel.room.type.restriction']
channel_hotel_room_type_obj = self.env['channel.hotel.room.type']
def_restr_plan = channel_room_type_restr_obj.search([('channel_plan_id', '=', '0')])
_logger.info("==== ROOM VALUES (%s -- %s)", dfrom, dto)
_logger.info(values)
for k_rid, v_rid in values.iteritems():
room_type = channel_hotel_room_type_obj.search([
('channel_plan_id', '=', k_rid)
], limit=1)
if room_type:
date_dt = date_utils.get_datetime(
dfrom,
dtformat=DEFAULT_WUBOOK_DATE_FORMAT)
for day_vals in v_rid:
date_str = date_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
day_vals.update({
'room_type_id': room_type.odoo_id.id,
'date': date_str,
})
self._map_room_values_availability(day_vals, set_max_avail)
if def_restr_plan:
day_vals.update({
'restriction_plan_id': def_restr_plan.odoo_id.id
})
self._map_room_values_restrictions(day_vals)
date_dt = date_dt + timedelta(days=1)
return True
class HotelRoomTypeImportMapper(Component):
_name = 'channel.hotel.room.type.import.mapper'
@@ -86,6 +161,8 @@ class HotelRoomTypeImportMapper(Component):
('id', 'channel_room_id'),
('shortname', 'channel_short_code'),
('occupancy', 'ota_capacity'),
('price', 'list_price'),
('name', 'name'),
]
@mapping

View File

@@ -2,3 +2,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import common
from . import importer

View File

@@ -38,7 +38,7 @@ class ChannelHotelRoomTypeAvailability(models.Model):
for record in self:
if record.channel_max_avail > record.odoo_id.room_type_id.total_rooms_count:
raise ValidationError(_("max avail for channel can't be high \
than toal rooms \
than total rooms \
count: %d") % record.odoo_id.room_type_id.total_rooms_count)
@job(default_channel='root.channel')

View File

@@ -0,0 +1,37 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import 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.hotel import date_utils
from odoo import fields, api, _
_logger = logging.getLogger(__name__)
class HotelRoomTypeAvailabilityImporter(Component):
_name = 'channel.hotel.room.type.availability.importer'
_inherit = 'hotel.channel.importer'
_apply_on = ['channel.hotel.room.type.availability']
_usage = 'hotel.room.type.availability.importer'
class HotelRoomTypeAvailabilityImportMapper(Component):
_name = 'channel.hotel.room.type.availability.import.mapper'
_inherit = 'channel.import.mapper'
_apply_on = 'channel.hotel.room.type.availability'
direct = [
('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}

View File

@@ -2,3 +2,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import common
from . import importer

View File

@@ -0,0 +1,37 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import 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.hotel import date_utils
from odoo import fields, api, _
_logger = logging.getLogger(__name__)
class HotelRoomTypeRestrictionImporter(Component):
_name = 'channel.hotel.room.type.restriction.importer'
_inherit = 'hotel.channel.importer'
_apply_on = ['channel.hotel.room.type.restriction']
_usage = 'hotel.room.type.restriction.importer'
class HotelRoomTypeRestrictionImportMapper(Component):
_name = 'channel.hotel.room.type.restriction.import.mapper'
_inherit = 'channel.import.mapper'
_apply_on = 'channel.hotel.room.type.restriction'
direct = [
('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}

View File

@@ -0,0 +1,5 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from . import common
from . import importer

View File

@@ -0,0 +1,48 @@
# 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 ChannelHotelRoomTypeRestrictionItem(models.Model):
_name = 'channel.hotel.room.type.restriction.item'
_inherit = 'channel.binding'
_inherits = {'hotel.room.type.restriction.item': 'odoo_id'}
_description = 'Channel Hotel Room Type Restriction Item'
odoo_id = fields.Many2one(comodel_name='hotel.room.type.restriction.item',
string='Hotel Virtual Room Restriction',
required=True,
ondelete='cascade')
channel_pushed = fields.Boolean("Channel Pushed", readonly=True, default=False,
old_name='wpushed')
@job(default_channel='root.channel')
@api.multi
def update_channel_pushed(self, status):
self.ensure_one()
self.channel_pushed = status
class HotelRoomTypeRestrictionItem(models.Model):
_inherit = 'hotel.room.type.restriction.item'
channel_bind_ids = fields.One2many(
comodel_name='channel.hotel.room.type.restriction.item',
inverse_name='odoo_id',
string='Hotel Channel Connector Bindings')
class ChannelBindingHotelRoomTypeRestrictionItemListener(Component):
_name = 'channel.binding.hotel.room.type.restriction.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)
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):
record.update_channel_pushed(False)

View File

@@ -0,0 +1,44 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import logging
from datetime import 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 import fields, api, _
_logger = logging.getLogger(__name__)
class HotelRoomTypeRestrictionImporter(Component):
_name = 'channel.hotel.room.type.restriction.item.importer'
_inherit = 'hotel.channel.importer'
_apply_on = ['channel.hotel.room.type.restriction.item']
_usage = 'hotel.room.type.restriction.item.importer'
class HotelRoomTypeRestrictionItemImportMapper(Component):
_name = 'channel.hotel.room.type.restriction.item.import.mapper'
_inherit = 'channel.import.mapper'
_apply_on = 'channel.hotel.room.type.restriction.item'
direct = [
('min_stay', 'min_stay'),
('min_stay_arrival', 'min_stay_arrival'),
('max_stay', 'max_stay'),
('max_stay_arrival', 'max_stay_arrival'),
('closed', 'closed'),
('closed_departure', 'closed_departure'),
('closed_arrival', 'closed_arrival'),
('room_type_id', 'room_type_id'),
('date', 'date'),
]
@only_create
@mapping
def applied_on(self, record):
return {'applied_on': '0_room_type'}
@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}

View File

@@ -1,7 +1,7 @@
# 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 odoo import models, fields, api
class ResPartner(models.Model):

View File

@@ -1,29 +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
class ReservationRestrictionItem(models.Model):
_inherit = 'hotel.room.type.restriction.item'
channel_pushed = fields.Boolean("Channel Pushed", default=False, readonly=True,
old_name='wpushed')
@api.onchange('date_start')
def _onchange_date_start(self):
self.date_end = self.date_start
@api.model
def create(self, vals):
if vals.get('date_start'):
vals['date_end'] = vals.get('date_start')
return super(ReservationRestrictionItem, self).create(vals)
@api.multi
def write(self, vals):
if vals.get('date_start'):
vals['date_end'] = vals.get('date_start')
if self._context.get('channel_action', True):
vals.update({'channel_pushed': False})
return super(ReservationRestrictionItem, self).write(vals)

View File

@@ -1,196 +1,32 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# Copyright 2018 Alexandre Díaz
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
import os
import binascii
import logging
from datetime import datetime, timedelta
from odoo import models, fields, api, _
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT
_logger = logging.getLogger(__name__)
from openerp import models, fields, api
class HotelChannelConnectorConfiguration(models.TransientModel):
class HotelConfiguration(models.TransientModel):
_inherit = 'res.config.settings'
channel_push_security_token = fields.Char('WuBook Push Notification Security Token')
default_channel_connector = fields.Many2one(
'channel.backend',
'Default Channel Connector Backend')
@api.multi
def set_values(self):
super(HotelChannelConnectorConfiguration, self).set_values()
super(HotelConfiguration, self).set_values()
self.env['ir.default'].sudo().set(
'res.config.settings', 'channel_push_security_token',
self.channel_push_security_token)
'res.config.settings', 'default_channel_connector',
self.default_channel_connector.id)
@api.model
def get_values(self):
res = super(HotelChannelConnectorConfiguration, self).get_values()
res = super(HotelConfiguration, self).get_values()
# ONLY FOR v11. DO NOT FORWARD-PORT
channel_push_security_token = self.env['ir.default'].sudo().get(
'res.config.settings', 'channel_push_security_token')
default_channel_connector = self.env['ir.default'].sudo().get(
'res.config.settings', 'default_channel_connector')
res.update(
channel_push_security_token=channel_push_security_token,
default_channel_connector=default_channel_connector,
)
return res
# Dangerus method: Usefull for cloned instances with new wubook account
@api.multi
def resync(self):
self.ensure_one()
now_utc_dt = fields.Date.now()
now_utc_str = now_utc_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
# Reset Issues
issue_ids = self.env['wubook.issue'].search([])
issue_ids.write({
'to_read': False
})
# Push Virtual Rooms
wubook_obj = self.env['wubook'].with_context({
'init_connection': False
})
if wubook_obj.init_connection():
ir_seq_obj = self.env['ir.sequence']
room_types = self.env['hotel.room.type'].search([])
for room_type in room_types:
shortcode = ir_seq_obj.next_by_code('hotel.room.type')[:4]
channel_room_id = wubook_obj.create_room(
shortcode,
room_type.name,
room_type.wcapacity,
room_type.list_price,
room_type.total_rooms_count
)
if channel_room_id:
room_type.with_context(wubook_action=False).write({
'channel_room_id': channel_room_id,
'wscode': shortcode,
})
else:
room_type.with_context(wubook_action=False).write({
'channel_room_id': '',
'wscode': '',
})
# Create Restrictions
room_type_rest_obj = self.env['hotel.room.type.restriction']
restriction_ids = room_type_rest_obj.search([])
for restriction in restriction_ids:
if restriction.wpid != '0':
channel_plan_id = wubook_obj.create_rplan(restriction.name)
restriction.write({
'channel_plan_id': channel_plan_id or ''
})
# Create Pricelist
pricelist_ids = self.env['product.pricelist'].search([])
for pricelist in pricelist_ids:
channel_plan_id = wubook_obj.create_plan(pricelist.name, pricelist.is_daily_plan)
pricelist.write({
'channel_plan_id': channel_plan_id or ''
})
wubook_obj.close_connection()
# Reset Folios
folio_ids = self.env['hotel.folio'].search([])
folio_ids.with_context(wubook_action=False).write({
'wseed': '',
})
# Reset Reservations
reservation_ids = self.env['hotel.reservation'].search([
('channel_reservation_id', '!=', ''),
('channel_reservation_id', '!=', False)
])
reservation_ids.with_context(wubook_action=False).write({
'channel_reservation_id': '',
'ota_id': False,
'ota_reservation_id': '',
'is_from_ota': False,
'wstatus': 0
})
# Get Parity Models
pricelist_id = int(self.env['ir.default'].sudo().get(
'res.config.settings', 'parity_pricelist_id'))
restriction_id = int(self.env['ir.default'].sudo().get(
'res.config.settings', 'parity_restrictions_id'))
room_type_restr_it_obj = self.env['hotel.room.type.restriction.item']
# Secure Wubook Input
restriction_item_ids = room_type_restr_it_obj.search([
('applied_on', '=', '0_room_type'),
('date_start', '<', now_utc_str),
])
if any(restriction_item_ids):
restriction_item_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push restrictions
restriction_item_ids = room_type_restr_it_obj.search([
('restriction_id', '=', restriction_id),
('applied_on', '=', '0_room_type'),
('wpushed', '=', True),
('date_start', '>=', now_utc_str),
])
if any(restriction_item_ids):
restriction_item_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Secure Wubook Input
pricelist_item_ids = self.env['product.pricelist.item'].search([
('applied_on', '=', '1_product'),
('compute_price', '=', 'fixed'),
('date_start', '<', now_utc_str),
])
if any(pricelist_item_ids):
pricelist_item_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push pricelists
pricelist_item_ids = self.env['product.pricelist.item'].search([
('pricelist_id', '=', pricelist_id),
('applied_on', '=', '1_product'),
('compute_price', '=', 'fixed'),
('wpushed', '=', True),
('date_start', '>=', now_utc_str),
])
if any(pricelist_item_ids):
pricelist_item_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Secure Wubook Input
availabity_ids = self.env['hotel.room.type.availability'].search([
('date', '<', now_utc_str),
])
if any(availabity_ids):
availabity_ids.with_context(wubook_action=False).write({
'wpushed': True
})
# Put to push availability
availabity_ids = self.env['hotel.room.type.availability'].search([
('wpushed', '=', True),
('date', '>=', now_utc_str),
])
if any(availabity_ids):
availabity_ids.with_context(wubook_action=False).write({
'wpushed': False
})
# Generate Security Token
self.env['ir.default'].sudo().set(
'wubook.config.settings',
'wubook_push_security_token',
binascii.hexlify(os.urandom(16)).decode())
self.env.cr.commit() # FIXME: Need do this
# Push Changes
if wubook_obj.init_connection():
wubook_obj.push_activation()
wubook_obj.import_channels_info()
wubook_obj.push_changes()
wubook_obj.close_connection()

View File

@@ -7,10 +7,10 @@
<field name="arch" type="xml">
<form string="Hotel Channel Backend">
<header>
<button name="synchronize_metadata"
<button name="synchronize_push_urls"
type="object"
class="oe_highlight"
string="Synchronize Metadata"/>
string="Synchronize Push URL's"/>
</header>
<sheet>
<label for="name" class="oe_edit_only"/>
@@ -27,6 +27,7 @@
<field name="pkey" colspan="2"/>
<field name="username" colspan="2"/>
<field name="passwd" password="1" colspan="2"/>
<field name="security_token" colspan="4"/>
</group>
</page>
</notebook>
@@ -69,6 +70,15 @@
string="Import in background"/>
</div>
</group>
<group>
<label string="Import OTA's Info" class="oe_inline"/>
<div>
<button name="import_otas_info"
type="object"
class="oe_highlight"
string="Import in background"/>
</div>
</group>
</page>
</notebook>
</sheet>

View File

@@ -6,6 +6,9 @@
<field name="model">channel.hotel.room.type</field>
<field name="arch" type="xml">
<form string="Hotel Channel Virtual Room">
<group>
<field name="backend_id" attrs="{'visible': [('id','=', False)]}" />
</group>
<group>
<field name="channel_room_id" />
<field name="channel_short_code" />

View File

@@ -3,8 +3,8 @@
<!-- 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="name">channel.ota.info.form</field>
<field name="model">channel.ota.info</field>
<field name="arch" type="xml">
<form string="Channel OTA's Info" >
<sheet>
@@ -20,8 +20,8 @@
<!-- 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="name">channel.ota.info.tree</field>
<field name="model">channel.ota.info</field>
<field name="arch" type="xml">
<tree string="Channel OTA's Info">
<field name="ota_id" />
@@ -30,9 +30,9 @@
</field>
</record>
<record model="ir.actions.act_window" id="open_hotel_channel_connector_ota_info_tree_all">
<record model="ir.actions.act_window" id="open_channel_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="res_model">channel.ota.info</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>

View File

@@ -1,9 +1,9 @@
<?xml version="1.0"?>
<odoo>
<record id="reservation_restriction_view" model="ir.ui.view">
<record id="room_type_restriction_view" model="ir.ui.view">
<field name="model">hotel.room.type.restriction</field>
<field name="inherit_id" ref="hotel.reservation_restriction_view_form" />
<field name="inherit_id" ref="hotel.room_type_restriction_view_form" />
<field name="arch" type="xml">
<xpath expr="//form[1]//sheet" position="before">
<header>

View File

@@ -9,9 +9,10 @@
<notebook>
<page name="connector">
<group string="Hotel Channel Bindings">
<field name="channel_bind_ids" nolabel="1">
<field name="capacity" invisible="1" />
<field name="channel_bind_ids" context="{'default_ota_capacity': capacity}" nolabel="1">
<tree>
<field name="backend_id"/>
<field name="backend_id" />
</tree>
</field>
</group>

View File

@@ -1,14 +0,0 @@
<?xml version="1.0"?>
<odoo>
<record id="reservation_restriction_item_form_view" model="ir.ui.view">
<field name="model">hotel.room.type.restriction.item</field>
<field name="inherit_id" ref="hotel.reservation_restriction_item_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='date_end']" position="attributes">
<attribute name="readonly">True</attribute>
</xpath>
</field>
</record>
</odoo>

View File

@@ -19,7 +19,6 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from . import wubook_installer
from . import wubook_import_plan_prices
from . import wubook_import_plan_restrictions
from . import wubook_import_availability

View File

@@ -1,156 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2017 Solucións Aloxa S.L. <info@aloxa.eu>
# Alexandre Díaz <dev@redneboa.es>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import os
import binascii
from openerp import models, fields, api, _
from openerp.exceptions import ValidationError
from ..components.backend_adapter import DEFAULT_WUBOOK_DATE_FORMAT
from odoo.addons.hotel import date_utils
class WuBookInstaller(models.TransientModel):
_name = 'wubook.installer'
_inherit = 'res.config.installer'
wubook_user = fields.Char('User', required=True)
wubook_passwd = fields.Char('Password', required=True)
wubook_lcode = fields.Char('LCode', required=True)
wubook_server = fields.Char(string='Server',
default='https://wired.wubook.net/xrws/',
required=True)
wubook_pkey = fields.Char('PKey', required=True)
activate_push = fields.Boolean('Active Push Notifications', default=True)
@api.multi
def execute(self):
super(WuBookInstaller, self).execute()
return self.execute_simple()
@api.multi
def execute_simple(self):
activate_push = True
for rec in self:
self.env['ir.default'].sudo().set('wubook.config.settings',
'wubook_user',
rec.wubook_user)
self.env['ir.default'].sudo().set('wubook.config.settings',
'wubook_passwd',
rec.wubook_passwd)
self.env['ir.default'].sudo().set('wubook.config.settings',
'wubook_lcode',
rec.wubook_lcode)
self.env['ir.default'].sudo().set('wubook.config.settings',
'wubook_server',
rec.wubook_server)
self.env['ir.default'].sudo().set('wubook.config.settings',
'wubook_pkey',
rec.wubook_pkey)
activate_push = rec.activate_push
self.env['ir.default'].sudo().set(
'wubook.config.settings',
'wubook_push_security_token',
binascii.hexlify(os.urandom(16)).decode())
self.env.cr.commit() # FIXME: Need do this
# Create Wubook Base Restrictions
restr_obj = self.env['hotel.room.type.restriction'].with_context({
'wubook_action': False
})
base_rest = restr_obj.search([('wpid', '=', '0')], limit=1)
if not base_rest:
nrest = restr_obj.create({
'name': 'Base WuBook Restrictions',
'wpid': '0',
})
if not nrest:
raise ValidationError(_("Can't create base wubook restrictions"))
# Initialize WuBook
wres = self.env['wubook'].initialize(activate_push)
if not wres:
raise ValidationError("Can't finish installation!")
# Open Next Step
v_id = 'hotel_wubook_proto.view_wubook_configuration_installer_parity'
return {
'name': _("Configure Hotel Parity"),
'type': 'ir.actions.act_window',
'res_model': 'wubook.installer.parity',
'view_id': self.env.ref(v_id).id,
'view_type': 'form',
'view_mode': 'form',
'target': 'new'
}
class WuBookInstallerParity(models.TransientModel):
_name = 'wubook.installer.parity'
_inherit = 'res.config.installer'
parity_pricelist_id = fields.Many2one('product.pricelist',
'Product Pricelist')
parity_restrictions_id = fields.Many2one('hotel.room.type.restriction',
'Restrictions')
import_data = fields.Boolean('Import Data From WuBook', default=False)
date_start = fields.Date('Date Start')
date_end = fields.Date('Date End')
@api.multi
def execute(self):
self.execute_simple()
return super(WuBookInstallerParity, self).execute()
@api.multi
def execute_simple(self):
wubookObj = self.env['wubook']
irValuesObj = self.env['ir.values']
for rec in self:
irValuesObj.sudo().set_default('res.config.settings',
'parity_pricelist_id',
rec.parity_pricelist_id.id)
irValuesObj.sudo().set_default('res.config.settings',
'parity_restrictions_id',
rec.parity_restrictions_id.id)
import_data = rec.import_data
if rec.import_data:
date_start_dt = date_utils.get_datetime(rec.date_start)
date_end_dt = date_utils.get_datetime(rec.date_end)
# Availability
wresAvail = wubookObj.fetch_rooms_values(
date_start_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
date_end_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT))
# Pricelist
wresPrices = wubookObj.fetch_plan_prices(
rec.parity_pricelist_id.wpid,
date_start_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
date_end_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT))
# Restrictions
wresRestr = wubookObj.fetch_rplan_restrictions(
date_start_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
date_end_dt.strftime(DEFAULT_WUBOOK_DATE_FORMAT),
rec.parity_restrictions_id.wpid)
if not wresAvail or not wresPrices or not wresRestr:
raise ValidationError(_("Errors importing data from WuBook"))
# Reservations
wubookObj.fetch_new_bookings()

View File

@@ -1,90 +0,0 @@
<odoo>
<record id="view_wubook_configuration_installer" model="ir.ui.view">
<field name="name">wubook.installer.form</field>
<field name="model">wubook.installer</field>
<field name="inherit_id" ref="base.res_config_installer"/>
<field name="arch" type="xml">
<xpath expr="//form[1]" position="attributes">
<attribute name="string">WuBook Configuration</attribute>
</xpath>
<xpath expr="//footer[1]" position="replace">
<footer>
<button name="action_next" type="object" string="Continue" class="oe_highlight"/>
</footer>
</xpath>
<xpath expr="//form/separator[1]" position="replace">
<p class="oe_grey">
WuBook API Configuration. This wizard will activate push request and synchronize rooms &amp; reservations with Odoo.
</p>
<group>
<field name="wubook_server" />
</group>
<group>
<field name="wubook_user" />
<field name="wubook_passwd" password="True" />
</group>
<group>
<field name="wubook_lcode" />
<field name="wubook_pkey" />
</group>
<group>
<field name="activate_push" />
</group>
</xpath>
</field>
</record>
<record id="view_wubook_configuration_installer_parity" model="ir.ui.view">
<field name="name">wubook.installer.parity.form</field>
<field name="model">wubook.installer.parity</field>
<field name="inherit_id" ref="base.res_config_installer"/>
<field name="arch" type="xml">
<xpath expr="//form[1]" position="attributes">
<attribute name="string">WuBook Configuration Parity</attribute>
</xpath>
<xpath expr="//footer[1]" position="replace">
<footer>
<button name="action_next" type="object" string="Finish Installation" class="oe_highlight"/>
</footer>
</xpath>
<xpath expr="//form/separator[1]" position="replace">
<p class="oe_grey">
These models are used as masters
</p>
<group>
<field name="parity_pricelist_id" domain="[('wpid', '!=', False),('wpid', '!=', '')]" required="True" />
<field name="parity_restrictions_id" domain="[('wpid', '!=', False),('wpid', '!=', '')]" required="True" />
</group>
<group>
<field name="import_data" />
</group>
<group attrs="{'invisible':[('import_data', '=', False)]}">
<group>
<field name="date_start" attrs="{'required':[('import_data', '=', True)]}" />
</group>
<group>
<field name="date_end" attrs="{'required':[('import_data', '=', True)]}" />
</group>
</group>
</xpath>
</field>
</record>
<record id="action_wubook_configuration_installer" model="ir.actions.act_window">
<field name="name">Configure WuBook Data</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">wubook.installer</field>
<field name="view_id" ref="view_wubook_configuration_installer"/>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<!--record id="wubook_configuration_installer_todo" model="ir.actions.todo">
<field name="action_id" ref="action_wubook_configuration_installer"/>
<field name="sequence">3</field>
<field name="type">automatic</field>
</record-->
</odoo>