[IMP] Channel Connector

This commit is contained in:
QS5ELkMu
2018-12-05 19:02:53 +01:00
committed by GitHub
parent b09cc18eec
commit 9bcc9bde47
37 changed files with 378 additions and 157 deletions

View File

@@ -28,6 +28,7 @@
'views/room_pricelist_cached_views.xml',
'views/hotel_reservation_views.xml',
'views/hotel_calendar_management_views.xml',
'views/hotel_calendar_views.xml',
'data/menus.xml',
'security/ir.model.access.csv',
],

View File

@@ -25,4 +25,9 @@
<menuitem id="hotel_room_pricelist_cached" name="Room Pricelist Cached"
sequence="1" action="hotel_room_pricelist_cached_action_form_tree" parent="sale.menu_sale_config"/>
<menuitem id="hotel_calendar_record_menu" name="Calendars"
parent="hotel.hotel_configuration_menu" sequence="10"
groups="hotel.group_hotel_manager"
action="hotel_calendar_action_form_tree" />
</odoo>

View File

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

View File

@@ -0,0 +1,15 @@
# Copyright 2018 Alexandre Díaz <dev@redneboa.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import models, api, _, fields
from odoo.exceptions import ValidationError
class HotelCalendar(models.Model):
_name = 'hotel.calendar'
name = fields.Char('Name', required=True)
segmentation_ids = fields.Many2many('hotel.room.type.class', string='Segmentation')
location_ids = fields.Many2many('hotel.floor', string='Location')
amenity_ids = fields.Many2many('hotel.amenity', string='Amenity')
room_type_ids = fields.Many2many('hotel.room.type', string='Room Type')

View File

@@ -122,13 +122,13 @@ class HotelCalendarManagement(models.TransientModel):
def _hcalendar_room_json_data(self, rooms):
json_data = []
for room in rooms:
json_data.append((
room.id,
room.name,
room.get_capacity(),
room.list_price,
room.total_rooms_count,
))
json_data.append({
'id': room.id,
'name': room.name,
'capacity': room.get_capacity(),
'price': room.list_price,
'total_rooms': room.total_rooms_count,
})
return json_data
@api.model

View File

@@ -64,19 +64,20 @@ class HotelReservation(models.Model):
pricelist_id = int(pricelist_id)
json_rooms = []
for room in rooms:
json_rooms.append((
room.id,
room.name,
room.capacity,
'', # Reserved for type code
room.shared_room,
room.room_type_id
and ['pricelist', room.room_type_id.id, pricelist_id,
room.room_type_id.name]
or 0,
room.room_type_id.name,
room.room_type_id.id,
room.floor_id.id))
json_rooms.append({
'id': room.id,
'name': room.name,
'capacity': room.capacity,
'class_id': room.room_type_id.class_id.id,
'shared': room.shared_room,
'price': room.room_type_id
and ['pricelist', room.room_type_id.id, pricelist_id,
room.room_type_id.name] or 0,
'room_type_name': room.room_type_id.name,
'room_type_id': room.room_type_id.id,
'floor_id': room.floor_id.id,
'amentity_ids': room.room_type_id.room_amenity_ids.ids,
})
return json_rooms
@api.model

View File

@@ -33,17 +33,9 @@ class HotelRoomTypeResrtrictionItem(models.Model):
@api.multi
def write(self, vals):
restrictions_default_id = self.env['ir.default'].sudo().get(
'res.config.settings', 'default_restriction_id')
if restrictions_default_id:
restrictions_default_id = int(restrictions_default_id)
ret_vals = super(HotelRoomTypeResrtrictionItem, self).write(vals)
bus_hotel_calendar_obj = self.env['bus.hotel.calendar']
for record in self:
if record.restriction_id.id != restrictions_default_id or \
record.applied_on != '0_room_type':
continue
bus_hotel_calendar_obj.send_restriction_notification({
'restriction_id': record.restriction_id.id,
'date': record.date,

View File

@@ -3,3 +3,4 @@ access_room_price_cache_user,hotel_calendar.model_room_pricelist_cached_user,hot
access_room_price_cache_call,hotel_calendar.model_room_pricelist_cached_call,hotel_calendar.model_room_pricelist_cached,hotel.group_hotel_call,1,1,1,1
access_hotel_product_pricelist_item_call,hotel_calendar.pricelist_item_call,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_call,1,1,1,1
access_hotel_product_pricelist_item_user,hotel_calendar.pricelist_item_use,hotel_calendar.model_product_pricelist_item,hotel.group_hotel_user,1,1,1,1
access_hotel_calendar,access_hotel_calendar,model_hotel_calendar,base.group_user,1,0,0,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_room_price_cache_call hotel_calendar.model_room_pricelist_cached_call hotel_calendar.model_room_pricelist_cached hotel.group_hotel_call 1 1 1 1
4 access_hotel_product_pricelist_item_call hotel_calendar.pricelist_item_call hotel_calendar.model_product_pricelist_item hotel.group_hotel_call 1 1 1 1
5 access_hotel_product_pricelist_item_user hotel_calendar.pricelist_item_use hotel_calendar.model_product_pricelist_item hotel.group_hotel_user 1 1 1 1
6 access_hotel_calendar access_hotel_calendar model_hotel_calendar base.group_user 1 0 0 0

View File

@@ -110,18 +110,19 @@ var PMSCalendarController = AbstractController.extend({
var rooms = [];
for (var r of results['rooms']) {
var nroom = new HRoom(
r[0], // Id
r[1], // Name
r[2], // Capacity
r[3], // Category
r[4], // Shared Room
r[5] // Price
r['id'],
r['name'],
r['capacity'],
r['class_id'],
r['shared'],
r['price']
);
nroom.addUserData({
'room_type_name': r[6],
'room_type_id': r[7],
'floor_id': r[8],
'amenities': r[9]
'room_type_name': r['room_type_name'],
'room_type_id': r['room_type_id'],
'floor_id': r['floor_id'],
'amenities': r['amenity_ids'],
'class_id': r['class_id']
});
rooms.push(nroom);
}
@@ -236,10 +237,10 @@ var PMSCalendarController = AbstractController.extend({
_onLoadViewFilters: function (ev) {
var self = this;
$.when(
this.model.get_room_types(),
this.model.get_room_type_class(),
this.model.get_floors(),
this.model.get_amenities(),
this.model.get_rooms()
this.model.get_room_types()
).then(function(a1, a2, a3, a4){
self.renderer.loadViewFilters(a1, a2, a3, a4);
});

View File

@@ -79,9 +79,9 @@ return AbstractModel.extend({
context: Session.user_context,
});
},
get_rooms: function() {
get_room_type_class: function() {
return this._rpc({
model: 'hotel.room.type',
model: 'hotel.room.type.class',
method: 'search_read',
args: [false, ['id','name']],
context: Session.user_context,

View File

@@ -30,6 +30,7 @@ var HotelCalendarView = AbstractRenderer.extend({
_reserv_tooltips: {},
_days_tooltips: [],
_last_dates: [false, false],
_hcalendars: [],
/** VIEW METHODS **/
@@ -771,7 +772,7 @@ var HotelCalendarView = AbstractRenderer.extend({
var virtual = _.map(this.$el.find('#pms-search #virtual_list').val(), function(item){ return +item; });
var domain = [];
if (category && category.length > 0) {
domain.push(['categ_id', 'in', category]);
domain.push(['class_id', 'in', category]);
}
if (floor && floor.length > 0) {
domain.push(['floor_id', 'in', floor]);

View File

@@ -89,10 +89,10 @@ var MPMSCalendarController = AbstractController.extend({
var rooms = [];
for (var r of results['rooms']) {
var nroom = new HRoomType(
r[0], // Id
r[1], // Name
r[2], // Capacity
r[3], // Price
r['id'],
r['name'],
r['capacity'],
r['price'],
);
rooms.push(nroom);
}

View File

@@ -41,23 +41,23 @@ function HotelCalendar(/*String*/querySelector, /*Dictionary*/options, /*List*/p
};
/** Options **/
options = options || {};
this.options = {
startDate: ((options.startDate && HotelCalendar.toMomentUTC(options.startDate)) || moment(new Date()).utc()).subtract('1', 'd'),
days: (options.days || ((options.startDate && HotelCalendar.toMomentUTC(options.startDate)) || moment(new Date())).clone().local().daysInMonth()),
rooms: options.rooms || [],
allowInvalidActions: options.allowInvalidActions || false,
assistedMovement: options.assistedMovement || false,
endOfWeek: options.endOfWeek || 6,
endOfWeekOffset: options.endOfWeekOffset || 0,
divideRoomsByCapacity: options.divideRoomsByCapacity || false,
currencySymbol: options.currencySymbol || '€',
showPricelist: options.showPricelist || false,
showAvailability: options.showAvailability || false,
showNumRooms: options.showNumRooms || 0,
paginatorStepsMin: options.paginatorAdv || 1,
paginatorStepsMax: options.paginatorAdv || 15,
};
this.options = _.extend({
startDate: moment(new Date()).utc(),
days: moment(new Date()).local().daysInMonth(),
rooms: [],
allowInvalidActions: false,
assistedMovement: false,
endOfWeek: 6,
endOfWeekOffset: 0,
divideRoomsByCapacity: false,
currencySymbol: '€',
showPricelist: false,
showAvailability: false,
showNumRooms: 0,
paginatorStepsMin: 1,
paginatorStepsMax: 15,
}, options);
this.options.startDate.subtract('1', 'd');
// Check correct values
if (this.options.rooms.length > 0 && !(this.options.rooms[0] instanceof HRoom)) {
@@ -104,14 +104,15 @@ HotelCalendar.prototype = {
//==== CALENDAR
setStartDate: function(/*String,MomentObject*/date, /*Int?*/days, /*Bool*/fullUpdate, /*Functions*/callback) {
if (moment.isMoment(date)) {
this.options.startDate = date.subtract('1','d');
this.options.startDate = date;
} else if (typeof date === 'string'){
this.options.startDate = HotelCalendar.toMomentUTC(date).subtract('1','d');
this.options.startDate = HotelCalendar.toMomentUTC(date);
} else {
console.warn("[Hotel Calendar][setStartDate] Invalid date format!");
return;
}
this.options.startDate.subtract('1','d');
if (typeof days !== 'undefined') {
this.options.days = days;
}

View File

@@ -24,4 +24,11 @@
<field name="view_mode">tree,form</field>
</record>
<record model="ir.actions.act_window" id="hotel_calendar_action_form_tree">
<field name="name">Hotel Calendar</field>
<field name="res_model">hotel.calendar</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Form view of hotel room -->
<record model="ir.ui.view" id="hotel_calendar_view_form">
<field name="name">hotel.calendar.form</field>
<field name="model">hotel.calendar</field>
<field name="arch" type="xml">
<form string="Hotel Calendar">
<sheet>
<group>
<field name="name" />
</group>
<group>
<field name="segmentation_ids" />
<field name="location_ids" />
<field name="amenity_ids" />
<field name="room_type_ids" />
</group>
</sheet>
</form>
</field>
</record>
<!-- Tree view of hotel room -->
<record model="ir.ui.view" id="hotel_calendar_view_tree">
<field name="name">hotel.calendar.tree</field>
<field name="model">hotel.calendar</field>
<field name="arch" type="xml">
<tree string="Hotel Calendar">
<field name="name" />
</tree>
</field>
</record>
</odoo>

View File

@@ -73,8 +73,7 @@
<button name="mark_as_readed" string="Mark as Read"
type="object" class="oe_highlight"
icon="fa-1x fa-thumb-tack"
attrs="{'invisible':[('to_read','=', False)]}"
/>
attrs="{'invisible':[('to_read','=', False)]}" />
</xpath>
</field>
</record>

View File

@@ -113,8 +113,7 @@ class ChannelHotelReservation(models.Model):
def unlink(self):
vals = []
for record in self:
if any(record.channel_bind_ids) and record.channel_bind_ids[0].external_id \
and not record.parent_reservation:
if record.is_from_ota and self._context.get('ota_limits', True):
raise UserError(_("You can't delete OTA's reservations"))
vals.append({
'checkin': record.checkin,
@@ -140,26 +139,28 @@ class HotelReservation(models.Model):
user = self.env['res.users'].browse(self.env.uid)
record.able_to_modify_channel = user.has_group('base.group_system')
@api.depends('channel_type', 'channel_bind_ids.ota_id')
def _get_origin_sale(self):
for record in self:
if not record.channel_type:
record.channel_type = 'door'
if record.channel_type == 'web' and any(record.channel_bind_ids) and \
record.channel_bind_ids[0].ota_id:
record.origin_sale = record.channel_bind_ids[0].ota_id.name
else:
record.origin_sale = dict(
self.fields_get(allfields=['channel_type'])['channel_type']['selection']
)[record.channel_type]
# TODO: Dario v2
# @api.depends('channel_type', 'channel_bind_ids.ota_id')
# def _get_origin_sale(self):
# for record in self:
# if not record.channel_type:
# record.channel_type = 'door'
#
# if record.channel_type == 'web' and any(record.channel_bind_ids) and \
# record.channel_bind_ids[0].ota_id:
# record.origin_sale = record.channel_bind_ids[0].ota_id.name
# else:
# record.origin_sale = dict(
# self.fields_get(allfields=['channel_type'])['channel_type']['selection']
# )[record.channel_type]
channel_bind_ids = fields.One2many(
comodel_name='channel.hotel.reservation',
inverse_name='odoo_id',
string='Hotel Channel Connector Bindings')
origin_sale = fields.Char('Origin', compute=_get_origin_sale,
store=True)
# TODO: Dario v2
# origin_sale = fields.Char('Origin', compute=_get_origin_sale,
# store=True)
is_from_ota = fields.Boolean('Is From OTA',
readonly=True,
old_name='wis_from_channel')

View File

@@ -113,10 +113,9 @@ class BindingHotelRoomTypeListener(Component):
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):
if any(record.channel_bind_ids) and 'name' in fields or 'list_price' in fields or \
'room_ids' in fields:
# FIXME: Supossed that only exists one channel connector per record
record.channel_bind_ids[0].modify_room()
if 'name' in fields or 'list_price' in fields or 'room_ids' in fields:
for binding in record.channel_bind_ids:
binding.modify_room()
# @skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
# def on_record_create(self, record, fields=None):

View File

@@ -68,13 +68,12 @@ class HotelRoomTypeRestriction(models.Model):
names = []
for name in org_names:
restriction_id = room_type_restriction_obj.browse(name[0])
if any(restriction_id.channel_bind_ids) and \
restriction_id.channel_bind_ids[0].external_id:
names.append((
name[0],
'%s (%s Backend)' % (name[1],
restriction_id.channel_bind_ids[0].backend_id.name),
))
new_name = name[1]
if any(restriction_id.channel_bind_ids):
for restriction_bind in restriction_id.channel_bind_ids:
if restriction_bind.external_id:
new_name += ' (%s Backend)' % restriction_bind.backend_id.name
names.append((name[0], new_name))
else:
names.append((name[0], name[1]))
return names
@@ -86,8 +85,9 @@ class BindingHotelRoomTypeListener(Component):
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):
if any(record.channel_bind_ids) and 'name' in fields:
record.channel_bind_ids[0].update_plan_name()
if 'name' in fields:
for binding in record.channel_bind_ids:
binding.update_plan_name()
class ChannelBindingHotelRoomTypeRestrictionListener(Component):
_name = 'channel.binding.hotel.room.type.restriction.listener'

View File

@@ -37,6 +37,7 @@ class ChannelHotelRoomTypeRestrictionItem(models.Model):
exporter = work.component(usage='hotel.room.type.restriction.item.exporter')
return exporter.push_restriction()
class HotelRoomTypeRestrictionItem(models.Model):
_inherit = 'hotel.room.type.restriction.item'
@@ -45,6 +46,7 @@ class HotelRoomTypeRestrictionItem(models.Model):
inverse_name='odoo_id',
string='Hotel Channel Connector Bindings')
class BindingHotelRoomTypeRestrictionItemListener(Component):
_name = 'binding.hotel.room.type.restriction.item.listener'
_inherit = 'base.connector.listener'
@@ -59,6 +61,24 @@ class BindingHotelRoomTypeRestrictionItemListener(Component):
if any(fields_checked):
record.channel_bind_ids.write({'channel_pushed': False})
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_create(self, record, fields=None):
if not any(record.channel_bind_ids):
channel_hotel_room_type_rest_item_obj = self.env[
'channel.hotel.room.type.restriction.item']
for restriction_bind in record.restriction_id.channel_bind_ids:
restriction_item_bind = channel_hotel_room_type_rest_item_obj.search([
('odoo_id', '=', record.id),
('backend_id', '=', restriction_bind.backend_id.id),
])
if not restriction_item_bind:
channel_hotel_room_type_rest_item_obj.create({
'odoo_id': record.id,
'channel_pushed': False,
'backend_id': restriction_bind.backend_id.id,
})
class ChannelBindingHotelRoomTypeRestrictionItemListener(Component):
_name = 'channel.binding.hotel.room.type.restriction.item.listener'
_inherit = 'base.connector.listener'

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 openerp import models, fields, api, _
from openerp.exceptions import ValidationError
class HotelRoomTypeClass(models.Model):
_inherit = 'hotel.room.type.class'
_locked_codes = []
@api.multi
def write(self, vals):
for record in self:
if record.code_class in self._locked_codes:
raise ValidationError(_("Can't modify channel room type class"))
return super(HotelRoomTypeClass, self).write(vals)
@api.multi
def unlink(self):
for record in self:
if record.code_class in self._locked_codes:
raise ValidationError(_("Can't delete channel room type class"))
return super(HotelRoomTypeClass, self).unlink()

View File

@@ -68,14 +68,13 @@ class ProductPricelist(models.Model):
org_names = super(ProductPricelist, self).name_get()
names = []
for name in org_names:
priclist_id = pricelist_obj.browse(name[0])
if any(priclist_id.channel_bind_ids) and \
priclist_id.channel_bind_ids[0].external_id:
names.append((name[0], '%s (%s Backend)' % (
name[1],
priclist_id.channel_bind_ids[0].backend_id.name)))
else:
names.append((name[0], name[1]))
pricelist_id = pricelist_obj.browse(name[0])
new_name = name[1]
if any(pricelist_id.channel_bind_ids):
for pricelist_bind in pricelist_id.channel_bind_ids:
if pricelist_bind.external_id:
new_name += ' (%s Backend)' % pricelist_bind.backend_id.name
names.append((name[0], new_name))
return names
class BindingProductPricelistListener(Component):
@@ -85,8 +84,9 @@ class BindingProductPricelistListener(Component):
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_write(self, record, fields=None):
if any(record.channel_bind_ids) and 'name' in fields:
record.channel_bind_ids[0].update_plan_name()
if 'name' in fields:
for binding in record.channel_bind_ids:
binding.update_plan_name()
class ChannelBindingProductPricelistListener(Component):
_name = 'channel.binding.product.pricelist.listener'

View File

@@ -61,6 +61,23 @@ class BindingProductPricelistItemListener(Component):
if any(fields_checked):
record.channel_bind_ids.write({'channel_pushed': False})
@skip_if(lambda self, record, **kwargs: self.no_connector_export(record))
def on_record_create(self, record, fields=None):
if not any(record.channel_bind_ids):
channel_product_pricelist_item_obj = self.env[
'channel.product.pricelist.item']
for pricelist_bind in record.restriction_id.channel_bind_ids:
pricelist_item_bind = channel_product_pricelist_item_obj.search([
('odoo_id', '=', record.id),
('backend_id', '=', pricelist_bind.backend_id.id),
])
if not pricelist_item_bind:
channel_product_pricelist_item_obj.create({
'odoo_id': record.id,
'channel_pushed': False,
'backend_id': pricelist_bind.backend_id.id,
})
class ChannelBindingProductPricelistItemListener(Component):
_name = 'channel.binding.product.pricelist.item.listener'
_inherit = 'base.connector.listener'

View File

@@ -10,7 +10,7 @@
<field name="customer_notes" readonly="True" attrs="{'invisible':[('has_channel_reservations', '=', False)]}"/>
</xpath>
<xpath expr="//notebook/page/field[@name='room_lines']/tree/field[@name='checkout']" position="after">
<field name="origin_sale"/>
<!-- field name="origin_sale"/-->
<field name="is_from_ota" invisible="1"/>
</xpath>
<xpath expr="//notebook/page/field[@name='room_lines']/tree/field[@name='checkout']" position="attributes">
@@ -20,7 +20,7 @@
<attribute name="attrs">{'readonly': [('is_from_ota', '!=', False)]}</attribute>
</xpath>
<xpath expr="//notebook/page/field[@name='room_lines']/form/sheet/header/field[@name='folio_id']" position="after">
<field name="origin_sale" invisible="1"/>
<!-- field name="origin_sale" invisible="1"/-->
<field name="is_from_ota" invisible="1"/>
</xpath>
<xpath expr="//notebook/page/field[@name='room_lines']/form/sheet/h3/field[@name='checkout']" position="attributes">

View File

@@ -64,7 +64,7 @@
<field name="inherit_id" ref="hotel.hotel_reservation_view_tree" />
<field name="arch" type="xml">
<xpath expr="//field[@name='checkout']" position="after">
<field name="origin_sale"/>
<!-- field name="origin_sale"/-->
</xpath>
</field>
</record>
@@ -78,8 +78,8 @@
<!-- field name="wchannel_reservation_code"/-->
</xpath>
<xpath expr="//group" position="inside">
<filter name="origin" string="Origin" domain="[]"
context="{'group_by':'origin_sale'}"/>
<!-- filter name="origin" string="Origin" domain="[]"
context="{'group_by':'origin_sale'}"/-->
</xpath>
</field>
</record>
@@ -90,7 +90,7 @@
<field name="model">hotel.reservation</field>
<field name="arch" type="xml">
<graph type="bar">
<field name="origin_sale" string="Origin"/>
<!-- field name="origin_sale" string="Origin"/-->
<field name="price_total" type="measure" />
</graph>
</field>
@@ -103,7 +103,7 @@
<field name="arch" type="xml">
<pivot string="Reservations">
<field name="checkin" type="row" />
<field name="origin_sale" string="Origin" type="col" />
<!-- field name="origin_sale" string="Origin" type="col" /-->
<field name="price_total" string="Price" type="measure" />
</pivot>
</field>

View File

@@ -18,6 +18,7 @@
'data': [
'data/cron_jobs.xml',
'data/sequences.xml',
'data/records.xml',
'views/inherited_channel_connector_backend_views.xml',
'views/inherited_channel_ota_info_views.xml',
#'security/wubook_security.xml',

View File

@@ -102,8 +102,7 @@ class WuBookAdapter(AbstractComponent):
rcode_a, results_a = self._server.push_activation(
self._session_info[0],
self._session_info[1],
urljoin(base_url,
"/wubook/push/reservations/%s" % security_token),
urljoin(base_url, "wubook/push/reservations/%s" % security_token),
1)
if rcode_a != 0:
raise ChannelConnectorError(_("Can't activate push reservations"), {
@@ -113,7 +112,7 @@ class WuBookAdapter(AbstractComponent):
rcode_b, results_b = self._server.push_update_activation(
self._session_info[0],
self._session_info[1],
urljoin(base_url, "/wubook/push/rooms/%s" % security_token))
urljoin(base_url, "wubook/push/rooms/%s" % security_token))
if rcode_b != 0:
raise ChannelConnectorError(_("Can't activate push rooms"), {
'message': results_b,
@@ -122,7 +121,7 @@ class WuBookAdapter(AbstractComponent):
return rcode_a == 0 and results_b == 0
# === ROOMS
def create_room(self, shortcode, name, capacity, price, availability):
def create_room(self, shortcode, name, capacity, price, availability, defboard, rtype):
rcode, results = self._server.new_room(
self._session_info[0],
self._session_info[1],
@@ -132,8 +131,8 @@ class WuBookAdapter(AbstractComponent):
price,
availability,
shortcode[:4],
'nb' # TODO: Complete this part
# rtype=('name' in vals and vals['name'] and 3) or 1
defboard,
rtype=rtype
)
if rcode != 0:
raise ChannelConnectorError(_("Can't create room in WuBook"), {
@@ -141,7 +140,7 @@ class WuBookAdapter(AbstractComponent):
})
return results
def modify_room(self, channel_room_id, name, capacity, price, availability, scode):
def modify_room(self, channel_room_id, name, capacity, price, availability, scode, defboard, rtype):
rcode, results = self._server.mod_room(
self._session_info[0],
self._session_info[1],
@@ -151,8 +150,8 @@ class WuBookAdapter(AbstractComponent):
price,
availability,
scode,
'nb'
# rtype=('name' in vals and vals['name'] and 3) or 1
defboard,
rtype=rtype
)
if rcode != 0:
raise ChannelConnectorError(_("Can't modify room in WuBook"), {
@@ -405,6 +404,7 @@ class WuBookAdapter(AbstractComponent):
rcode, results = self._server.wired_rplan_get_rplan_values(
self._session_info[0],
self._session_info[1],
'1.1',
fields.Date.from_string(date_from).strftime(DEFAULT_WUBOOK_DATE_FORMAT),
fields.Date.from_string(date_to).strftime(DEFAULT_WUBOOK_DATE_FORMAT),
int(channel_restriction_plan_id))

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record model="hotel.room.type.class">
<field name="name">Room</field>
<field name="code_class">1</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Apartment</field>
<field name="code_class">2</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Beds Number</field>
<field name="code_class">3</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Beds</field>
<field name="code_class">4</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Bungalow</field>
<field name="code_class">5</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Tent</field>
<field name="code_class">6</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Villa</field>
<field name="code_class">7</field>
</record>
<record model="hotel.room.type.class">
<field name="name">Chalet</field>
<field name="code_class">8</field>
</record>
</odoo>

View File

@@ -10,4 +10,5 @@ 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_hotel_room_type_class
from . import channel_ota_info

View File

@@ -17,7 +17,7 @@ class ChannelBackend(models.Model):
to redefine the ``version`` field in the ``_inherit`` model.
"""
super(ChannelBackend, self).select_versions()
return [('1.2', '1.2+')]
return [('1.1', '1.1')]
def _get_default_server(self):
return 'https://wired.wubook.net/xrws/'

View File

@@ -11,7 +11,8 @@ from odoo.addons.hotel_channel_connector_wubook.components.backend_adapter impor
WUBOOK_STATUS_ACCEPTED,
WUBOOK_STATUS_CANCELLED,
WUBOOK_STATUS_CANCELLED_PENALTY,
WUBOOK_STATUS_BAD)
WUBOOK_STATUS_BAD,
WUBOOK_STATUS_GOOD)
class ChannelHotelReservation(models.Model):
@@ -32,37 +33,31 @@ class HotelReservation(models.Model):
@api.multi
def action_cancel(self):
no_export = self._context.get('connector_no_export', True)
if no_export:
for record in self:
# Can't cancel in Odoo
if record.is_from_ota:
raise ValidationError(_("Can't cancel reservations from OTA's"))
for record in self:
# Can't cancel in Odoo
if record.is_from_ota and self._context.get('ota_limits', True):
raise ValidationError(_("Can't cancel reservations from OTA's"))
user = self.env['res.users'].browse(self.env.uid)
if user.has_group('hotel.group_hotel_call'):
self.write({'to_read': True, 'to_assign': True})
res = super(HotelReservation, self).action_cancel()
if no_export:
for record in self:
# Only can cancel reservations created directly in wubook
if any(record.channel_bind_ids) and \
record.channel_bind_ids[0].external_id and \
not record.channel_bind_ids[0].ota_id and \
record.channel_bind_ids[0].channel_status in ['1', '2', '4']:
self._event('on_record_cancel').notify(record)
for record in self:
# Only can cancel reservations created directly in wubook
for binding in record.channel_bind_ids:
if binding.external_id and not binding.ota_id and \
binding.channel_status in WUBOOK_STATUS_GOOD:
self._event('on_record_cancel').notify(binding)
return res
@api.multi
def confirm(self):
can_confirm = True
for record in self:
if record.is_from_ota and any(record.channel_bind_ids) and \
int(record.channel_bind_ids[0].channel_status) in WUBOOK_STATUS_BAD:
can_confirm = False
break
if not can_confirm:
raise ValidationError(_("Can't confirm OTA's cancelled reservations"))
if record.is_from_ota:
for binding in record.channel_bind_ids:
if int(binding.channel_status) in WUBOOK_STATUS_BAD \
and self._context.get('ota_limits', True):
raise ValidationError(_("Can't confirm OTA's cancelled reservations"))
return super(HotelReservation, self).confirm()

View File

@@ -3,7 +3,7 @@
from odoo.addons.component.core import Component
from odoo.addons.hotel_channel_connector.components.core import ChannelConnectorError
from odoo import api
from odoo import api, fields
class HotelRoomTypeExporter(Component):
@@ -12,13 +12,16 @@ class HotelRoomTypeExporter(Component):
@api.model
def modify_room(self, binding):
try:
binding.sync_date = fields.Datetime.now()
return self.backend_adapter.modify_room(
binding.external_id,
binding.name,
binding.ota_capacity,
binding.list_price,
binding.total_rooms_count,
binding.channel_short_code)
binding.channel_short_code,
'nb',
binding.class_id and binding.class_id.class_code or False)
except ChannelConnectorError as err:
self.create_issue(
section='room',
@@ -35,8 +38,9 @@ class HotelRoomTypeExporter(Component):
binding.name,
binding.ota_capacity,
binding.list_price,
binding.total_rooms_count
)
binding.total_rooms_count,
'nb',
binding.class_id and binding.class_id.class_code or False)
except ChannelConnectorError as err:
self.create_issue(
section='room',

View File

@@ -22,9 +22,16 @@ class HotelRoomTypeImporter(Component):
channel_message=err.data['message'])
else:
channel_room_type_obj = self.env['channel.hotel.room.type']
hotel_room_type_class_obj = self.env['hotel.room.type.class']
room_mapper = self.component(usage='import.mapper',
model_name='channel.hotel.room.type')
for room in results:
room_type_class = hotel_room_type_class_obj.search([
('code_class', '=', str(room['rtype'])),
])
room.update({
'class_id': room_type_class and room_type_class.id or False,
})
map_record = room_mapper.map_record(room)
room_bind = channel_room_type_obj.search([
('backend_id', '=', self.backend_record.id),
@@ -57,3 +64,7 @@ class HotelRoomTypeImportMapper(Component):
@mapping
def backend_id(self, record):
return {'backend_id': self.backend_record.id}
@mapping
def class_id(self, record):
return {'class_id': record['class_id']}

View File

@@ -14,6 +14,7 @@ class HotelRoomTypeAvailabilityExporter(Component):
_inherit = 'channel.hotel.room.type.availability.exporter'
def push_availability(self):
channel_hotel_room_type_obj = self.env['channel.hotel.room.type']
channel_room_type_avail_ids = self.env['channel.hotel.room.type.availability'].search([
('backend_id', '=', self.backend_record.id),
('channel_pushed', '=', False),
@@ -38,7 +39,11 @@ class HotelRoomTypeAvailabilityExporter(Component):
'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})
room_type_bind = channel_hotel_room_type_obj.search([
('odoo_id', '=', room_type.id),
('backend_id', '=', self.backend_record.id),
], limit=1)
avails.append({'id': room_type_bind.external_id, 'days': days})
_logger.info("==[ODOO->CHANNEL]==== AVAILABILITY ==")
_logger.info(avails)
if any(avails):

View File

@@ -13,6 +13,7 @@ class HotelRoomTypeRestrictionItemExporter(Component):
@api.model
def push_restriction(self):
channel_hotel_room_type_obj = self.env['channel.hotel.room.type']
channel_room_type_rest_obj = self.env['channel.hotel.room.type.restriction']
channel_rest_item_obj = self.env['channel.hotel.room.type.restriction.item']
unpushed = channel_rest_item_obj.search([
@@ -38,8 +39,11 @@ class HotelRoomTypeRestrictionItemExporter(Component):
room_type_ids = unpushed_rp.mapped('room_type_id')
for room_type in room_type_ids:
if any(room_type.channel_bind_ids):
# FIXME: Supossed that only exists one channel connector per record
room_type_external_id = room_type.channel_bind_ids[0].external_id
room_type_bind = channel_hotel_room_type_obj.search([
('odoo_id', '=', room_type.id),
('backend_id', '=', self.backend_record.id),
], limit=1)
room_type_external_id = room_type_bind.external_id
restrictions[rp.external_id].update({
room_type_external_id: [],
})

View File

@@ -27,6 +27,7 @@ class HotelRoomTypeRestrictionImporter(Component):
model_name='channel.hotel.room.type.restriction.item')
_logger.info("==[CHANNEL->ODOO]==== RESTRICTIONS ==")
_logger.info(plan_restrictions)
count = 0
for k_rpid, v_rpid in plan_restrictions.items():
channel_restriction_id = channel_reserv_restriction_obj.search([
('backend_id', '=', self.backend_record.id),
@@ -40,6 +41,7 @@ class HotelRoomTypeRestrictionImporter(Component):
], limit=1)
if channel_room_type:
for item in v_rid:
_logger.info(item)
map_record = restriction_item_mapper.map_record(item)
date_dt = datetime.strptime(item['date'], DEFAULT_WUBOOK_DATE_FORMAT)
date_str = date_dt.strftime(DEFAULT_SERVER_DATE_FORMAT)
@@ -47,7 +49,6 @@ class HotelRoomTypeRestrictionImporter(Component):
('backend_id', '=', self.backend_record.id),
('restriction_id', '=', channel_restriction_id.odoo_id.id),
('date', '=', date_str),
('applied_on', '=', '0_room_type'),
('room_type_id', '=', channel_room_type.odoo_id.id)
], limit=1)
item.update({
@@ -64,6 +65,8 @@ class HotelRoomTypeRestrictionImporter(Component):
'connector_no_export': True
}).create(map_record.values(for_create=True))
channel_restriction_item.channel_pushed = True
count += 1
return count
@api.model
def import_restriction_values(self, date_from, date_to, channel_restr_id=False):
@@ -82,7 +85,8 @@ class HotelRoomTypeRestrictionImporter(Component):
dfrom=date_from, dto=date_to)
else:
if any(results):
self._generate_restriction_items(results)
return self._generate_restriction_items(results)
return 0
class HotelRoomTypeRestrictionItemImportMapper(Component):
@@ -96,7 +100,7 @@ class HotelRoomTypeRestrictionItemImportMapper(Component):
('max_stay', 'max_stay'),
('max_stay_arrival', 'max_stay_arrival'),
('closed', 'closed'),
('closed_departure', 'closed_departure'),
#('closed_departure', 'closed_departure'),
('closed_arrival', 'closed_arrival'),
('date', 'date'),
]
@@ -121,3 +125,7 @@ class HotelRoomTypeRestrictionItemImportMapper(Component):
@mapping
def sync_date(self, record):
return {'sync_date': fields.Datetime.now()}
@mapping
def closed_departure(self, record):
return {'closed_departure': int(record['closed_departure'])}

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 openerp import models, fields, api, _
from openerp.exceptions import ValidationError
class HotelRoomTypeClass(models.Model):
_inherit = 'hotel.room.type.class'
_locked_codes = ('1', '2', '3', '4', '5', '6', '7', '8')
@api.multi
def write(self, vals):
for record in self:
if record.code_class in self._locked_codes:
raise ValidationError(_("Can't modify channel room type class"))
return super(HotelRoomTypeClass, self).write(vals)
@api.multi
def unlink(self):
for record in self:
if record.code_class in self._locked_codes:
raise ValidationError(_("Can't delete channel room type class"))
return super(HotelRoomTypeClass, self).unlink()