mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
Merge remote-tracking branch 'origin/11.0' into 11.0
This commit is contained in:
@@ -40,6 +40,7 @@
|
|||||||
'views/inherited_res_partner_views.xml',
|
'views/inherited_res_partner_views.xml',
|
||||||
'views/hotel_room_type_views.xml',
|
'views/hotel_room_type_views.xml',
|
||||||
'views/hotel_room_views.xml',
|
'views/hotel_room_views.xml',
|
||||||
|
'views/hotel_shared_room_views.xml',
|
||||||
'views/hotel_room_type_class_views.xml',
|
'views/hotel_room_type_class_views.xml',
|
||||||
'views/general.xml',
|
'views/general.xml',
|
||||||
'views/inherited_product_template_views.xml',
|
'views/inherited_product_template_views.xml',
|
||||||
|
|||||||
3220
hotel/i18n/es.po
3220
hotel/i18n/es.po
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ from . import hotel_floor
|
|||||||
from . import hotel_folio
|
from . import hotel_folio
|
||||||
from . import hotel_reservation
|
from . import hotel_reservation
|
||||||
from . import hotel_room
|
from . import hotel_room
|
||||||
|
from . import hotel_shared_room
|
||||||
from . import hotel_amenity
|
from . import hotel_amenity
|
||||||
from . import hotel_amenity_type
|
from . import hotel_amenity_type
|
||||||
from . import hotel_room_type
|
from . import hotel_room_type
|
||||||
|
|||||||
@@ -99,7 +99,8 @@ class HotelFolio(models.Model):
|
|||||||
default=lambda self: _('New'))
|
default=lambda self: _('New'))
|
||||||
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
||||||
partner_id = fields.Many2one('res.partner',
|
partner_id = fields.Many2one('res.partner',
|
||||||
track_visibility='onchange')
|
track_visibility='onchange',
|
||||||
|
ondelete='restrict',)
|
||||||
|
|
||||||
room_lines = fields.One2many('hotel.reservation', 'folio_id',
|
room_lines = fields.One2many('hotel.reservation', 'folio_id',
|
||||||
readonly=False,
|
readonly=False,
|
||||||
@@ -107,19 +108,20 @@ class HotelFolio(models.Model):
|
|||||||
help="Hotel room reservation detail.",)
|
help="Hotel room reservation detail.",)
|
||||||
|
|
||||||
service_ids = fields.One2many('hotel.service', 'folio_id',
|
service_ids = fields.One2many('hotel.service', 'folio_id',
|
||||||
readonly=False,
|
readonly=False,
|
||||||
states={'done': [('readonly', True)]},
|
states={'done': [('readonly', True)]},
|
||||||
help="Hotel services detail provide to "
|
help="Hotel services detail provide to "
|
||||||
"customer and it will include in "
|
"customer and it will include in "
|
||||||
"main Invoice.")
|
"main Invoice.")
|
||||||
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('hotel.folio'))
|
company_id = fields.Many2one('res.company', 'Company', default=lambda self: self.env['res.company']._company_default_get('hotel.folio'))
|
||||||
analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a folio.", copy=False)
|
analytic_account_id = fields.Many2one('account.analytic.account', 'Analytic Account', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="The analytic account related to a folio.", copy=False)
|
||||||
currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id',
|
currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id',
|
||||||
string='Currency', readonly=True, required=True)
|
string='Currency', readonly=True, required=True, ondelete='restrict',)
|
||||||
|
|
||||||
pricelist_id = fields.Many2one('product.pricelist',
|
pricelist_id = fields.Many2one('product.pricelist',
|
||||||
string='Pricelist',
|
string='Pricelist',
|
||||||
required=True,
|
required=True,
|
||||||
|
ondelete='restrict',
|
||||||
states={'draft': [('readonly', False)],
|
states={'draft': [('readonly', False)],
|
||||||
'sent': [('readonly', False)]},
|
'sent': [('readonly', False)]},
|
||||||
help="Pricelist for current folio.")
|
help="Pricelist for current folio.")
|
||||||
@@ -136,10 +138,11 @@ class HotelFolio(models.Model):
|
|||||||
('agency', 'Agencia'),
|
('agency', 'Agencia'),
|
||||||
('operator', 'Tour operador'),
|
('operator', 'Tour operador'),
|
||||||
('virtualdoor', 'Virtual Door'),], 'Sales Channel', default='door')
|
('virtualdoor', 'Virtual Door'),], 'Sales Channel', default='door')
|
||||||
user_id = fields.Many2one('res.users', string='Salesperson', index=True,
|
user_id = fields.Many2one('res.users', string='Salesperson', index=True, ondelete='restrict',
|
||||||
track_visibility='onchange', default=lambda self: self.env.user)
|
track_visibility='onchange', default=lambda self: self.env.user)
|
||||||
tour_operator_id = fields.Many2one('res.partner',
|
tour_operator_id = fields.Many2one('res.partner',
|
||||||
'Tour Operator',
|
'Tour Operator',
|
||||||
|
ondelete='restrict',
|
||||||
domain=[('is_tour_operator', '=', True)])
|
domain=[('is_tour_operator', '=', True)])
|
||||||
date_order = fields.Datetime(
|
date_order = fields.Datetime(
|
||||||
string='Order Date',
|
string='Order Date',
|
||||||
@@ -245,12 +248,14 @@ class HotelFolio(models.Model):
|
|||||||
help='Margin in days to create a notice if a payment \
|
help='Margin in days to create a notice if a payment \
|
||||||
advance has not been recorded')
|
advance has not been recorded')
|
||||||
segmentation_ids = fields.Many2many('res.partner.category',
|
segmentation_ids = fields.Many2many('res.partner.category',
|
||||||
string='Segmentation')
|
string='Segmentation',
|
||||||
|
ondelete='restrict')
|
||||||
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
client_order_ref = fields.Char(string='Customer Reference', copy=False)
|
||||||
note = fields.Text('Terms and conditions')
|
note = fields.Text('Terms and conditions')
|
||||||
sequence = fields.Integer(string='Sequence', default=10)
|
sequence = fields.Integer(string='Sequence', default=10)
|
||||||
team_id = fields.Many2one('crm.team',
|
team_id = fields.Many2one('crm.team',
|
||||||
'Sales Channel',
|
'Sales Channel',
|
||||||
|
ondelete='restrict',
|
||||||
change_default=True,
|
change_default=True,
|
||||||
default=_get_default_team,
|
default=_get_default_team,
|
||||||
oldname='section_id')
|
oldname='section_id')
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class HotelReservation(models.Model):
|
|||||||
name = fields.Text('Reservation Description', required=True)
|
name = fields.Text('Reservation Description', required=True)
|
||||||
sequence = fields.Integer(string='Sequence', default=10)
|
sequence = fields.Integer(string='Sequence', default=10)
|
||||||
|
|
||||||
room_id = fields.Many2one('hotel.room', string='Room')
|
room_id = fields.Many2one('hotel.room', string='Room', ondelete='restrict')
|
||||||
|
|
||||||
reservation_no = fields.Char('Reservation No', size=64, readonly=True)
|
reservation_no = fields.Char('Reservation No', size=64, readonly=True)
|
||||||
adults = fields.Integer('Adults', size=64, readonly=False,
|
adults = fields.Integer('Adults', size=64, readonly=False,
|
||||||
@@ -316,8 +316,9 @@ class HotelReservation(models.Model):
|
|||||||
], string='Invoice Status', compute='_compute_invoice_status',
|
], string='Invoice Status', compute='_compute_invoice_status',
|
||||||
store=True, readonly=True, default='no')
|
store=True, readonly=True, default='no')
|
||||||
tax_ids = fields.Many2many('account.tax',
|
tax_ids = fields.Many2many('account.tax',
|
||||||
string='Taxes',
|
string='Taxes',
|
||||||
domain=['|', ('active', '=', False), ('active', '=', True)])
|
ondelete='restrict',
|
||||||
|
domain=['|', ('active', '=', False), ('active', '=', True)])
|
||||||
qty_to_invoice = fields.Float(
|
qty_to_invoice = fields.Float(
|
||||||
compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True,
|
compute='_get_to_invoice_qty', string='To Invoice', store=True, readonly=True,
|
||||||
digits=dp.get_precision('Product Unit of Measure'))
|
digits=dp.get_precision('Product Unit of Measure'))
|
||||||
|
|||||||
@@ -17,16 +17,16 @@ class HotelRoom(models.Model):
|
|||||||
active = fields.Boolean('Active', default=True)
|
active = fields.Boolean('Active', default=True)
|
||||||
sequence = fields.Integer('Sequence', default=0)
|
sequence = fields.Integer('Sequence', default=0)
|
||||||
room_type_id = fields.Many2one('hotel.room.type', 'Hotel Room Type',
|
room_type_id = fields.Many2one('hotel.room.type', 'Hotel Room Type',
|
||||||
required=True,
|
required=True,
|
||||||
ondelete='restrict')
|
ondelete='restrict')
|
||||||
floor_id = fields.Many2one('hotel.floor', 'Ubication',
|
floor_id = fields.Many2one('hotel.floor', 'Ubication',
|
||||||
help='At which floor the room is located.')
|
help='At which floor the room is located.')
|
||||||
max_adult = fields.Integer('Max Adult')
|
max_adult = fields.Integer('Max Adult')
|
||||||
max_child = fields.Integer('Max Child')
|
max_child = fields.Integer('Max Child')
|
||||||
capacity = fields.Integer('Capacity')
|
capacity = fields.Integer('Capacity')
|
||||||
# FIXME not used
|
|
||||||
to_be_cleaned = fields.Boolean('To be Cleaned', default=False)
|
to_be_cleaned = fields.Boolean('To be Cleaned', default=False)
|
||||||
shared_room = fields.Boolean('Shared Room', default=False)
|
shared_room_id = fields.Many2one('hotel.shared.room', 'Shared Room',
|
||||||
|
default=False)
|
||||||
description_sale = fields.Text(
|
description_sale = fields.Text(
|
||||||
'Sale Description', translate=True,
|
'Sale Description', translate=True,
|
||||||
help="A description of the Product that you want to communicate to "
|
help="A description of the Product that you want to communicate to "
|
||||||
@@ -36,11 +36,36 @@ class HotelRoom(models.Model):
|
|||||||
default='0',
|
default='0',
|
||||||
required=True)
|
required=True)
|
||||||
|
|
||||||
|
@api.constrains('room_type_id')
|
||||||
|
def _constrain_shared_room_type(self):
|
||||||
|
for record in self:
|
||||||
|
if record.shared_room_id:
|
||||||
|
if not record.room_type_id.shared_room:
|
||||||
|
raise ValidationError(_('We cant save normal rooms \
|
||||||
|
in a shared room type'))
|
||||||
|
else:
|
||||||
|
if record.room_type_id.shared_room:
|
||||||
|
raise ValidationError(_('We cant save shared rooms \
|
||||||
|
in a normal room type'))
|
||||||
|
|
||||||
|
@api.constrains('shared_room_id')
|
||||||
|
def _constrain_shared_room(self):
|
||||||
|
for record in self:
|
||||||
|
if record.shared_room_id:
|
||||||
|
if not record.capacity > 1:
|
||||||
|
raise ValidationError(_('We cant save normal rooms \
|
||||||
|
in a shared room type'))
|
||||||
|
|
||||||
@api.constrains('capacity')
|
@api.constrains('capacity')
|
||||||
def _check_capacity(self):
|
def _check_capacity(self):
|
||||||
if self.capacity < 1:
|
for record in self:
|
||||||
raise ValidationError(_("Room capacity can't be less than one"))
|
if record.shared_room_id and record.capacity != 1:
|
||||||
|
raise ValidationError(_("A Bed only can has capacity one"))
|
||||||
|
if record.capacity < 1:
|
||||||
|
raise ValidationError(_("Room capacity can't be less than one"))
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def get_capacity(self, extra_bed=0):
|
def get_capacity(self, extra_bed=0):
|
||||||
return self.capacity + extra_bed
|
if not self.shared_room_id:
|
||||||
|
return self.capacity + extra_bed
|
||||||
|
return self.capacity
|
||||||
|
|||||||
@@ -34,6 +34,8 @@ class HotelRoomType(models.Model):
|
|||||||
active = fields.Boolean('Active', default=True,
|
active = fields.Boolean('Active', default=True,
|
||||||
help="The active field allows you to hide the \
|
help="The active field allows you to hide the \
|
||||||
category without removing it.")
|
category without removing it.")
|
||||||
|
shared_room = fields.Boolean('Shared Room', default=False,
|
||||||
|
help="This room type is reservation by beds")
|
||||||
# Used for ordering
|
# Used for ordering
|
||||||
sequence = fields.Integer('Sequence', default=0)
|
sequence = fields.Integer('Sequence', default=0)
|
||||||
|
|
||||||
@@ -47,7 +49,7 @@ class HotelRoomType(models.Model):
|
|||||||
_sql_constraints = [('code_unique', 'unique(code_type)',
|
_sql_constraints = [('code_unique', 'unique(code_type)',
|
||||||
'Room Type Code must be unique!')]
|
'Room Type Code must be unique!')]
|
||||||
|
|
||||||
@api.depends('room_ids')
|
@api.depends('room_ids', 'room_ids.active')
|
||||||
def _compute_total_rooms(self):
|
def _compute_total_rooms(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.total_rooms_count = len(record.room_ids)
|
record.total_rooms_count = len(record.room_ids)
|
||||||
@@ -107,6 +109,18 @@ class HotelRoomType(models.Model):
|
|||||||
})
|
})
|
||||||
return super().create(vals)
|
return super().create(vals)
|
||||||
|
|
||||||
|
@api.constrains('shared_room', 'room_ids')
|
||||||
|
def _constrain_shared_room(self):
|
||||||
|
for record in self:
|
||||||
|
if record.shared_room:
|
||||||
|
if any(not room.shared_room_id for room in record.room_ids):
|
||||||
|
raise ValidationError(_('We cant save normal rooms \
|
||||||
|
in a shared room type'))
|
||||||
|
else:
|
||||||
|
if any(room.shared_room_id for room in record.room_ids):
|
||||||
|
raise ValidationError(_('We cant save shared rooms \
|
||||||
|
in a normal room type'))
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def unlink(self):
|
def unlink(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ class HotelService(models.Model):
|
|||||||
|
|
||||||
name = fields.Char('Service description', required=True)
|
name = fields.Char('Service description', required=True)
|
||||||
sequence = fields.Integer(string='Sequence', default=10)
|
sequence = fields.Integer(string='Sequence', default=10)
|
||||||
product_id = fields.Many2one('product.product', 'Service', required=True)
|
product_id = fields.Many2one('product.product', 'Service',
|
||||||
|
ondelete='restrict', required=True)
|
||||||
folio_id = fields.Many2one('hotel.folio', 'Folio',
|
folio_id = fields.Many2one('hotel.folio', 'Folio',
|
||||||
ondelete='cascade',
|
ondelete='cascade',
|
||||||
default=_default_folio_id)
|
default=_default_folio_id)
|
||||||
@@ -132,10 +133,11 @@ class HotelService(models.Model):
|
|||||||
product_image = fields.Binary('Product Image', related="product_id.image", store=False, related_sudo=True)
|
product_image = fields.Binary('Product Image', related="product_id.image", store=False, related_sudo=True)
|
||||||
company_id = fields.Many2one(related='folio_id.company_id', string='Company', store=True, readonly=True)
|
company_id = fields.Many2one(related='folio_id.company_id', string='Company', store=True, readonly=True)
|
||||||
invoice_status = fields.Selection([
|
invoice_status = fields.Selection([
|
||||||
('invoiced', 'Fully Invoiced'),
|
('invoiced', 'Fully Invoiced'),
|
||||||
('to invoice', 'To Invoice'),
|
('to invoice', 'To Invoice'),
|
||||||
('no', 'Nothing to Invoice')
|
('no', 'Nothing to Invoice')
|
||||||
], string='Invoice Status', compute='_compute_invoice_status', store=True, readonly=True, default='no')
|
], string='Invoice Status', compute='_compute_invoice_status',
|
||||||
|
store=True, readonly=True, default='no')
|
||||||
channel_type = fields.Selection([
|
channel_type = fields.Selection([
|
||||||
('door', 'Door'),
|
('door', 'Door'),
|
||||||
('mail', 'Mail'),
|
('mail', 'Mail'),
|
||||||
|
|||||||
101
hotel/models/hotel_shared_room.py
Normal file
101
hotel/models/hotel_shared_room.py
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Copyright 2017 Alexandre Díaz
|
||||||
|
# Copyright 2017 Dario Lodeiros
|
||||||
|
# Copyright 2018 Pablo Quesada
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
class HotelSharedRoom(models.Model):
|
||||||
|
_name = 'hotel.shared.room'
|
||||||
|
_description = 'Hotel Shared Room'
|
||||||
|
_order = "room_type_id, name"
|
||||||
|
|
||||||
|
name = fields.Char('Room Name', required=True)
|
||||||
|
active = fields.Boolean('Active', default=True)
|
||||||
|
room_type_id = fields.Many2one(
|
||||||
|
'hotel.room.type', 'Hotel Room Type',
|
||||||
|
required=True, ondelete='restrict',
|
||||||
|
domain=[('shared_room', '=', True)]
|
||||||
|
)
|
||||||
|
floor_id = fields.Many2one('hotel.floor', 'Ubication',
|
||||||
|
help='At which floor the room is located.',
|
||||||
|
ondelete='restrict',)
|
||||||
|
sequence = fields.Integer('Sequence', required=True)
|
||||||
|
beds = fields.Integer('Beds')
|
||||||
|
bed_ids = fields.One2many('hotel.room',
|
||||||
|
'shared_room_id',
|
||||||
|
readonly=True,
|
||||||
|
ondelete='restrict',)
|
||||||
|
description_sale = fields.Text(
|
||||||
|
'Sale Description', translate=True,
|
||||||
|
help="A description of the Product that you want to communicate to "
|
||||||
|
" your customers. This description will be copied to every Sales "
|
||||||
|
" Order, Delivery Order and Customer Invoice/Credit Note")
|
||||||
|
|
||||||
|
@api.constrains('beds')
|
||||||
|
def _constrain_beds(self):
|
||||||
|
self.ensure_one()
|
||||||
|
if self.beds < 1:
|
||||||
|
raise ValidationError(_("Room beds can't be less than one"))
|
||||||
|
if len(self.bed_ids) > self.beds:
|
||||||
|
raise ValidationError(_(
|
||||||
|
"If you want to eliminate beds in the \
|
||||||
|
room you must deactivate the beds from your form"))
|
||||||
|
beds = []
|
||||||
|
inactive_beds = self.env['hotel.room'].search([
|
||||||
|
('active', '=', False),
|
||||||
|
('shared_room_id', '=', self.id)
|
||||||
|
])
|
||||||
|
for i in range(len(self.bed_ids), self.beds):
|
||||||
|
if inactive_beds:
|
||||||
|
bed = inactive_beds[0]
|
||||||
|
bed.update({'active': True})
|
||||||
|
inactive_beds -= bed
|
||||||
|
continue
|
||||||
|
name = u'%s (%s)' % (self.name, i)
|
||||||
|
bed_vals = {
|
||||||
|
'name': name,
|
||||||
|
'max_adult': 1,
|
||||||
|
'max_child': 0,
|
||||||
|
'capacity': 1,
|
||||||
|
'room_type_id': self.room_type_id.id,
|
||||||
|
'sequence': self.sequence,
|
||||||
|
'floor_id': self.floor_id.id if self.floor_id else False,
|
||||||
|
'shared_room_id': self.id,
|
||||||
|
}
|
||||||
|
beds.append((0, False, bed_vals))
|
||||||
|
if beds:
|
||||||
|
self.update({
|
||||||
|
'bed_ids': beds
|
||||||
|
})
|
||||||
|
|
||||||
|
@api.constrains('active')
|
||||||
|
def _constrain_active(self):
|
||||||
|
self.bed_ids.write({
|
||||||
|
'active': self.active,
|
||||||
|
})
|
||||||
|
|
||||||
|
@api.constrains('room_type_id')
|
||||||
|
def _constrain_room_type_id(self):
|
||||||
|
self.bed_ids.write({
|
||||||
|
'room_type_id': self.room_type_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
@api.constrains('floor_id')
|
||||||
|
def _constrain_floor_id(self):
|
||||||
|
self.bed_ids.write({
|
||||||
|
'floor_id': self.floor_id.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
@api.constrains('sequence')
|
||||||
|
def _constrain_sequence(self):
|
||||||
|
self.bed_ids.write({
|
||||||
|
'sequence': self.sequence,
|
||||||
|
})
|
||||||
|
|
||||||
|
@api.constrains('descrition_sale')
|
||||||
|
def _constrain_descrition_sale(self):
|
||||||
|
self.bed_ids.write({
|
||||||
|
'description_sale': self.descrition_sale,
|
||||||
|
})
|
||||||
@@ -11,6 +11,7 @@ user_access_hotel_board_service,user_access_hotel_board_service,model_hotel_boar
|
|||||||
user_access_hotel_checkin_partner,user_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_user,1,1,1,1
|
user_access_hotel_checkin_partner,user_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_user,1,1,1,1
|
||||||
user_access_hotel_room_type_class,user_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_user,1,0,0,0
|
user_access_hotel_room_type_class,user_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_user,1,0,0,0
|
||||||
user_access_hotel_room,user_access_hotel_room,model_hotel_room,hotel.group_hotel_user,1,0,0,0
|
user_access_hotel_room,user_access_hotel_room,model_hotel_room,hotel.group_hotel_user,1,0,0,0
|
||||||
|
user_access_shared_hotel_room,user_access_hotel_shared_room,model_hotel_shared_room,hotel.group_hotel_user,1,0,0,0
|
||||||
user_access_hotel_room_type_restriction_item,user_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_user,1,0,0,0
|
user_access_hotel_room_type_restriction_item,user_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_user,1,0,0,0
|
||||||
user_access_hotel_reservation,user_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_user,1,1,1,1
|
user_access_hotel_reservation,user_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_user,1,1,1,1
|
||||||
user_access_hotel_folio,user_access_hotel_folio,model_hotel_folio,hotel.group_hotel_user,1,1,1,1
|
user_access_hotel_folio,user_access_hotel_folio,model_hotel_folio,hotel.group_hotel_user,1,1,1,1
|
||||||
@@ -32,6 +33,7 @@ manager_access_hotel_board_service,manager_access_hotel_board_service,model_hote
|
|||||||
manager_access_hotel_checkin_partner,manager_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_checkin_partner,manager_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_manager,1,1,1,1
|
||||||
manager_access_hotel_room_type_class,manager_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_room_type_class,manager_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_manager,1,1,1,1
|
||||||
manager_access_hotel_room,manager_access_hotel_room,model_hotel_room,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_room,manager_access_hotel_room,model_hotel_room,hotel.group_hotel_manager,1,1,1,1
|
||||||
|
manager_access_hotel_shared_room,manager_access_hotel_shared_room,model_hotel_shared_room,hotel.group_hotel_manager,1,1,1,1
|
||||||
manager_access_hotel_room_type_restriction_item,manager_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_room_type_restriction_item,manager_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_manager,1,1,1,1
|
||||||
manager_access_hotel_reservation,manager_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_reservation,manager_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_manager,1,1,1,1
|
||||||
manager_access_hotel_folio,manager_access_hotel_folio,model_hotel_folio,hotel.group_hotel_manager,1,1,1,1
|
manager_access_hotel_folio,manager_access_hotel_folio,model_hotel_folio,hotel.group_hotel_manager,1,1,1,1
|
||||||
@@ -52,6 +54,7 @@ call_access_hotel_board_service,call_access_hotel_board_service,model_hotel_boar
|
|||||||
call_access_hotel_checkin_partner,call_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_call,1,1,1,1
|
call_access_hotel_checkin_partner,call_access_hotel_checkin_partner,model_hotel_checkin_partner,hotel.group_hotel_call,1,1,1,1
|
||||||
call_access_hotel_room_type_class,call_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_call,1,0,0,0
|
call_access_hotel_room_type_class,call_access_hotel_room_type_class,model_hotel_room_type_class,hotel.group_hotel_call,1,0,0,0
|
||||||
call_access_hotel_room,call_access_hotel_room,model_hotel_room,hotel.group_hotel_call,1,0,0,0
|
call_access_hotel_room,call_access_hotel_room,model_hotel_room,hotel.group_hotel_call,1,0,0,0
|
||||||
|
call_access_hotel_shared_room,call_access_hotel_shared_room,model_hotel_shared_room,hotel.group_hotel_call,1,0,0,0
|
||||||
call_access_hotel_room_type_restriction_item,call_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_call,1,0,0,0
|
call_access_hotel_room_type_restriction_item,call_access_hotel_room_type_restriction_item,model_hotel_room_type_restriction_item,hotel.group_hotel_call,1,0,0,0
|
||||||
call_access_hotel_reservation,call_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_call,1,1,1,1
|
call_access_hotel_reservation,call_access_hotel_reservation,model_hotel_reservation,hotel.group_hotel_call,1,1,1,1
|
||||||
call_access_hotel_folio,call_access_hotel_folio,model_hotel_folio,hotel.group_hotel_call,1,1,1,1
|
call_access_hotel_folio,call_access_hotel_folio,model_hotel_folio,hotel.group_hotel_call,1,1,1,1
|
||||||
|
|||||||
|
@@ -395,7 +395,7 @@
|
|||||||
<span class="label label-warning" attrs="{'invisible': [('overbooking', '=', False)]}">OverBooking!</span>
|
<span class="label label-warning" attrs="{'invisible': [('overbooking', '=', False)]}">OverBooking!</span>
|
||||||
<h2>
|
<h2>
|
||||||
<field name="room_id" readonly="1" nolabel="1"
|
<field name="room_id" readonly="1" nolabel="1"
|
||||||
options="{'no_open': True}" style="margin-right: 30px;" />
|
options="{'no_create': True,'no_open': True}" style="margin-right: 30px;" />
|
||||||
<field name="partner_id" readonly="1" options="{'no_open': True}"/>
|
<field name="partner_id" readonly="1" options="{'no_open': True}"/>
|
||||||
<span class="fa fa-user" style="margin-left:20px;"
|
<span class="fa fa-user" style="margin-left:20px;"
|
||||||
attrs="{'invisible': [('reservation_type','not in',('normal'))]}"/>
|
attrs="{'invisible': [('reservation_type','not in',('normal'))]}"/>
|
||||||
@@ -463,7 +463,7 @@
|
|||||||
'exit_date': checkout,'reservation_id': id, 'hidden_checkin_partner': True, 'edit_checkin_partner': True }"
|
'exit_date': checkout,'reservation_id': id, 'hidden_checkin_partner': True, 'edit_checkin_partner': True }"
|
||||||
attrs="{'invisible':['|','|', ('state','not in',('confirm','booking')),('checkin_partner_pending_count','=', 0),('parent_reservation','!=',False)]}"
|
attrs="{'invisible':['|','|', ('state','not in',('confirm','booking')),('checkin_partner_pending_count','=', 0),('parent_reservation','!=',False)]}"
|
||||||
/>
|
/>
|
||||||
<field name="room_id" />
|
<field name="room_id" options="{'no_create': True,'no_open': True}" />
|
||||||
<button type="action" class="oe_stat_button"
|
<button type="action" class="oe_stat_button"
|
||||||
icon="fa fa-2x fa-list-ul"
|
icon="fa fa-2x fa-list-ul"
|
||||||
name="%(open_hotel_reservation_form_tree_all)d"
|
name="%(open_hotel_reservation_form_tree_all)d"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
</group>
|
</group>
|
||||||
<group colspan="2">
|
<group colspan="2">
|
||||||
<group name="room_ids_group">
|
<group name="room_ids_group">
|
||||||
|
<field name="shared_room" />
|
||||||
<field name="room_ids" widget="many2many_tags"/>
|
<field name="room_ids" widget="many2many_tags"/>
|
||||||
<field name="total_rooms_count"/>
|
<field name="total_rooms_count"/>
|
||||||
</group>
|
</group>
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
<div class="oe_title">
|
<div class="oe_title">
|
||||||
<label for="name" string="Name" />
|
<label for="name" string="Name" />
|
||||||
<h1>
|
<h1>
|
||||||
<field name="name" />
|
<field name="name"
|
||||||
|
attrs="{'readonly':[('shared_room_id','!=', False)]}" />
|
||||||
|
<field name="shared_room_id" string="Ubication"
|
||||||
|
invisible='True' />
|
||||||
</h1>
|
</h1>
|
||||||
<!-- <label for="to_be_cleaned" string="To be Cleaned" />
|
<!-- <label for="to_be_cleaned" string="To be Cleaned" />
|
||||||
<h2>
|
<h2>
|
||||||
@@ -29,12 +32,15 @@
|
|||||||
<notebook>
|
<notebook>
|
||||||
<page name="information_hotel_room" string="Information">
|
<page name="information_hotel_room" string="Information">
|
||||||
<group colspan="4" col="4">
|
<group colspan="4" col="4">
|
||||||
<field name="floor_id" string="Ubication" />
|
<field name="floor_id" string="Ubication"
|
||||||
|
attrs="{'readonly':[('shared_room_id','!=', False)]}" />
|
||||||
<!-- <field name="categ_id" select="1" domain="[('isroomtype','=',True)]" string="Room Type" /> -->
|
<!-- <field name="categ_id" select="1" domain="[('isroomtype','=',True)]" string="Room Type" /> -->
|
||||||
<field name="room_type_id" string="Room Type" />
|
<field name="room_type_id" string="Room Type"
|
||||||
<field name="capacity" />
|
attrs="{'readonly':[('shared_room_id','!=', False)]}"/>
|
||||||
<field name="shared_room" help="It allows several reservations on the same room simultaneously based on the capacity of people"/>
|
<field name="capacity"
|
||||||
<field name="extra_beds_allowed" />
|
attrs="{'readonly':[('shared_room_id','!=', False)]}" />
|
||||||
|
<field name="extra_beds_allowed"
|
||||||
|
attrs="{'invisible':[('shared_room_id','!=', False)]}"/>
|
||||||
<!-- <field name="uom_id" invisible="1" /> -->
|
<!-- <field name="uom_id" invisible="1" /> -->
|
||||||
</group>
|
</group>
|
||||||
<group>
|
<group>
|
||||||
@@ -56,7 +62,7 @@
|
|||||||
</page>
|
</page>
|
||||||
</notebook>
|
</notebook>
|
||||||
<group>
|
<group>
|
||||||
<field name="sequence" />
|
<field name="sequence" attrs="{'readonly':[('shared_room_id','!=', False)]}"/>
|
||||||
</group>
|
</group>
|
||||||
</sheet>
|
</sheet>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
121
hotel/views/hotel_shared_room_views.xml
Normal file
121
hotel/views/hotel_shared_room_views.xml
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- =================================================== Rooms =================================================== -->
|
||||||
|
<!-- Form view of hotel room -->
|
||||||
|
<record model="ir.ui.view" id="hotel_shared_room_view_form">
|
||||||
|
<field name="name">hotel.shared.room.form</field>
|
||||||
|
<field name="model">hotel.shared.room</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form string="Hotel Shared Room">
|
||||||
|
<sheet>
|
||||||
|
<div class="oe_button_box" name="button_box">
|
||||||
|
<button name="toggle_active" type="object"
|
||||||
|
class="oe_stat_button" icon="fa-archive">
|
||||||
|
<field name="active" widget="boolean_button"
|
||||||
|
options='{"terminology": "archive"}'/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="oe_title">
|
||||||
|
<label for="name" string="Name" />
|
||||||
|
<h1>
|
||||||
|
<field name="name" />
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
<notebook>
|
||||||
|
<page name="information_hotel_shared_shared_room" string="Information">
|
||||||
|
<group colspan="4" col="4">
|
||||||
|
<field name="floor_id" string="Ubication" />
|
||||||
|
<field name="room_type_id" string="Room Type" />
|
||||||
|
<field name="beds" />
|
||||||
|
<field name="sequence" />
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="bed_ids" />
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
<page string="Descriptions">
|
||||||
|
<group>
|
||||||
|
<field name="description_sale" colspan="2" string="Name in reports"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</notebook>
|
||||||
|
</sheet>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Kanban view of hotel room -->
|
||||||
|
<record model="ir.ui.view" id="hotel_shared_shared_room_view_kanban">
|
||||||
|
<field name="name">hotel.shared.room.kanban</field>
|
||||||
|
<field name="model">hotel.shared.room</field>
|
||||||
|
<field name="type">kanban</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<kanban class="o_kanban_mobile" >
|
||||||
|
<attribute name="group_create">false</attribute>
|
||||||
|
<field name="id"/>
|
||||||
|
<field name="name"/>
|
||||||
|
<templates>
|
||||||
|
<t t-name="kanban-box">
|
||||||
|
<div t-attf-class="oe_kanban_global_click">
|
||||||
|
<div class="oe_kanban_details">
|
||||||
|
<ul>
|
||||||
|
<li class="mb4">
|
||||||
|
<strong><field name="name"/></strong>
|
||||||
|
</li>
|
||||||
|
<li class="mb4">Room Type: <field name="room_type_id"/></li>
|
||||||
|
<li class="badge mb4">
|
||||||
|
<strong>Beds <field name="beds"/></strong>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</t>
|
||||||
|
</templates>
|
||||||
|
</kanban>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Search view of hotel room -->
|
||||||
|
<record model="ir.ui.view" id="hotel_shared_shared_room_view_search">
|
||||||
|
<field name="name">hotel.shared.room.search</field>
|
||||||
|
<field name="model">hotel.shared.room</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<search string="Hotel Shared Room">
|
||||||
|
<field name="name" />
|
||||||
|
<field name="room_type_id" />
|
||||||
|
<field name="beds" />
|
||||||
|
</search>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- Tree view of hotel room -->
|
||||||
|
<record model="ir.ui.view" id="hotel_shared_room_view_tree">
|
||||||
|
<field name="name">hotel.shared.room.tree</field>
|
||||||
|
<field name="model">hotel.shared.room</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="Hotel Shared Room">
|
||||||
|
<field name="name" />
|
||||||
|
<field name="room_type_id" />
|
||||||
|
<field name="beds" />
|
||||||
|
<field name="sequence" />
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Action for hotel room -->
|
||||||
|
<record model="ir.actions.act_window" id="action_hotel_shared_room_form">
|
||||||
|
<field name="name">Hotel Shared Room</field>
|
||||||
|
<field name="res_model">hotel.shared.room</field>
|
||||||
|
<field name="view_type">form</field>
|
||||||
|
<!-- <field name="context">{'default_isroom':1,'default_rental':1}
|
||||||
|
</field> -->
|
||||||
|
<field name="view_id" ref="hotel_shared_room_view_tree" />
|
||||||
|
<field name="view_mode">kanban,tree,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem name="Shared Rooms" id="menu_open_hotel_shared_room_form" action="action_hotel_shared_room_form"
|
||||||
|
sequence="5" parent="hotel.menu_hotel_room" />
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -141,7 +141,7 @@ class HotelReservation(models.Model):
|
|||||||
'capacity': room.capacity,
|
'capacity': room.capacity,
|
||||||
'class_name': room.room_type_id.class_id.name,
|
'class_name': room.room_type_id.class_id.name,
|
||||||
'class_id': room.room_type_id.class_id.id,
|
'class_id': room.room_type_id.class_id.id,
|
||||||
'shared': room.shared_room,
|
'shared_id': room.shared_room_id,
|
||||||
'price': room.room_type_id
|
'price': room.room_type_id
|
||||||
and ['pricelist', room.room_type_id.id, pricelist_id,
|
and ['pricelist', room.room_type_id.id, pricelist_id,
|
||||||
room.room_type_id.name] or 0,
|
room.room_type_id.name] or 0,
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ class HotelReservation(models.Model):
|
|||||||
@api.model
|
@api.model
|
||||||
def create(self, vals):
|
def create(self, vals):
|
||||||
from_channel = False
|
from_channel = False
|
||||||
if 'channel_bind_ids' in vals and vals.get('channel_bind_ids')[0][2] and \
|
if vals.get('channel_bind_ids') and vals.get('channel_bind_ids')[0][2] and \
|
||||||
vals.get('channel_bind_ids')[0][2].get('external_id') is not None:
|
vals.get('channel_bind_ids')[0][2].get('external_id') is not None:
|
||||||
vals.update({'preconfirm': False})
|
vals.update({'preconfirm': False})
|
||||||
from_channel = True
|
from_channel = True
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class ChannelHotelRoomType(models.Model):
|
|||||||
string='Room Type',
|
string='Room Type',
|
||||||
required=True,
|
required=True,
|
||||||
ondelete='cascade')
|
ondelete='cascade')
|
||||||
channel_short_code = fields.Char("Channel Short Code", old_name='wscode')
|
channel_short_code = fields.Char("Channel Short Code")
|
||||||
ota_capacity = fields.Integer("OTA's Capacity", default=1, old_name='wcapacity',
|
ota_capacity = fields.Integer("OTA's Capacity", default=1,
|
||||||
help="The capacity of the room for OTAs.")
|
help="The capacity of the room for OTAs.")
|
||||||
|
|
||||||
default_quota = fields.Integer("Default Quota",
|
default_quota = fields.Integer("Default Quota",
|
||||||
@@ -52,8 +52,8 @@ class ChannelHotelRoomType(models.Model):
|
|||||||
max_price = fields.Float('Max. Price', default=200.0, digits=dp.get_precision('Product Price'),
|
max_price = fields.Float('Max. Price', default=200.0, digits=dp.get_precision('Product Price'),
|
||||||
help="Setup the max price to prevent incidents while editing your prices.")
|
help="Setup the max price to prevent incidents while editing your prices.")
|
||||||
|
|
||||||
@api.onchange('default_quota', 'default_max_avail')
|
@api.constrains('default_quota', 'default_max_avail', 'total_rooms_count')
|
||||||
def _onchange_availability(self):
|
def _constrains_availability(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
to_eval = []
|
to_eval = []
|
||||||
to_eval.append(rec.total_rooms_count)
|
to_eval.append(rec.total_rooms_count)
|
||||||
@@ -64,8 +64,8 @@ class ChannelHotelRoomType(models.Model):
|
|||||||
|
|
||||||
rec.default_availability = min(to_eval)
|
rec.default_availability = min(to_eval)
|
||||||
|
|
||||||
@api.onchange('room_ids')
|
@api.constrains('room_ids')
|
||||||
def _get_capacity(self):
|
def _constrain_capacity(self):
|
||||||
for rec in self:
|
for rec in self:
|
||||||
rec.ota_capacity = rec.odoo_id.get_capacity()
|
rec.ota_capacity = rec.odoo_id.get_capacity()
|
||||||
|
|
||||||
|
|||||||
@@ -22,10 +22,16 @@ class ChannelBackend(models.Model):
|
|||||||
def _get_default_server(self):
|
def _get_default_server(self):
|
||||||
return 'https://wired.wubook.net/xrws/'
|
return 'https://wired.wubook.net/xrws/'
|
||||||
|
|
||||||
|
def _get_default_wubook_parity(self):
|
||||||
|
return self.env['ir.default'].sudo().get('res.config.settings', 'default_pricelist_id')
|
||||||
|
|
||||||
lcode = fields.Char('Channel Service lcode')
|
lcode = fields.Char('Channel Service lcode')
|
||||||
pkey = fields.Char('Channel Service PKey')
|
pkey = fields.Char('Channel Service PKey')
|
||||||
server = fields.Char('Channel Service Server',
|
server = fields.Char('Channel Service Server',
|
||||||
default=_get_default_server)
|
default=_get_default_server)
|
||||||
|
wubook_parity_pricelist_id = fields.Many2one('product.pricelist', 'WuBook Parity Pricelist',
|
||||||
|
required=True,
|
||||||
|
default=_get_default_wubook_parity)
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
@api.multi
|
@api.multi
|
||||||
|
|||||||
@@ -121,6 +121,8 @@ class HotelReservationImporter(Component):
|
|||||||
if 'tax_inclusive' in broom['ancillary'] and not broom['ancillary']['tax_inclusive']:
|
if 'tax_inclusive' in broom['ancillary'] and not broom['ancillary']['tax_inclusive']:
|
||||||
_logger.info("--- Incoming Reservation without taxes included!")
|
_logger.info("--- Incoming Reservation without taxes included!")
|
||||||
tax_inclusive = False
|
tax_inclusive = False
|
||||||
|
# rate_id ( 0: WuBook Parity (aka standard rate); > 0: the id of the booked pricing plan)
|
||||||
|
rate_id = 0
|
||||||
# Generate Reservation Day Lines
|
# Generate Reservation Day Lines
|
||||||
reservation_lines = []
|
reservation_lines = []
|
||||||
tprice = 0.0
|
tprice = 0.0
|
||||||
@@ -141,6 +143,16 @@ class HotelReservationImporter(Component):
|
|||||||
'price': room_day_price,
|
'price': room_day_price,
|
||||||
}))
|
}))
|
||||||
tprice += room_day_price
|
tprice += room_day_price
|
||||||
|
rate_id = brday['rate_id']
|
||||||
|
# TODO: Review different pricelist in the different booked rooms (folio in Odoo)
|
||||||
|
if rate_id > 0:
|
||||||
|
rate_id = self.env['channel.product.pricelist'].search(
|
||||||
|
'external_id', '=', rate_id).odoo_id.id
|
||||||
|
if rate_id <= 0:
|
||||||
|
rate_id = self.env['channel.backend'].sudo().search([
|
||||||
|
('id', '=', self.backend_record.id)
|
||||||
|
]).wubook_parity_pricelist_id.id
|
||||||
|
|
||||||
# Get OTA
|
# Get OTA
|
||||||
ota_id = self.env['channel.ota.info'].search([
|
ota_id = self.env['channel.ota.info'].search([
|
||||||
('backend_id', '=', self.backend_record.id),
|
('backend_id', '=', self.backend_record.id),
|
||||||
@@ -163,6 +175,7 @@ class HotelReservationImporter(Component):
|
|||||||
'checkout': checkout_str,
|
'checkout': checkout_str,
|
||||||
'adults': persons,
|
'adults': persons,
|
||||||
'children': book['children'],
|
'children': book['children'],
|
||||||
|
'pricelist_id': rate_id,
|
||||||
'reservation_line_ids': reservation_lines,
|
'reservation_line_ids': reservation_lines,
|
||||||
'to_assign': True,
|
'to_assign': True,
|
||||||
'state': is_cancellation and 'cancelled' or 'confirm',
|
'state': is_cancellation and 'cancelled' or 'confirm',
|
||||||
|
|||||||
@@ -9,6 +9,15 @@
|
|||||||
<field name="lcode" colspan="2"/>
|
<field name="lcode" colspan="2"/>
|
||||||
<field name="pkey" colspan="2"/>
|
<field name="pkey" colspan="2"/>
|
||||||
</xpath>
|
</xpath>
|
||||||
|
|
||||||
|
<xpath expr="//page[@name='security']" position='after'>
|
||||||
|
<page string='Booking Engine' name="engine">
|
||||||
|
<group colspan="4" col="4">
|
||||||
|
<field name="wubook_parity_pricelist_id" options="{'no_create': True}" colspan="4"/>
|
||||||
|
</group>
|
||||||
|
</page>
|
||||||
|
</xpath>
|
||||||
|
|
||||||
</field>
|
</field>
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
|
|||||||
74
hotel_data_bi/README.rst
Normal file
74
hotel_data_bi/README.rst
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
REVENUE EXPORTER
|
||||||
|
=============
|
||||||
|
|
||||||
|
Export Odoo data for Revenue MyDataBI
|
||||||
|
|
||||||
|
Usage
|
||||||
|
=======
|
||||||
|
To use this module, you need to:
|
||||||
|
|
||||||
|
Create a user and give the "Hotel Management / Export data BI" permission.
|
||||||
|
|
||||||
|
To connect to Odoo via xmlrpc there are examples in https://www.odoo.com/documentation/10.0/api_integration.html in the "Calling methods" section with examples in several languages.
|
||||||
|
|
||||||
|
A python example:
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
url = 'https://www.example.org'
|
||||||
|
|
||||||
|
username = 'username@example.org'
|
||||||
|
|
||||||
|
password = '123passwordexample'
|
||||||
|
|
||||||
|
db = 'example_db_name'
|
||||||
|
|
||||||
|
common = xmlrpclib.ServerProxy('{}/xmlrpc/2/common'.format(url))
|
||||||
|
|
||||||
|
uid = common.authenticate(db, username, password, {})
|
||||||
|
|
||||||
|
models = xmlrpclib.ServerProxy('{}/xmlrpc/2/object'.format(url))
|
||||||
|
|
||||||
|
models.execute_kw(db, uid, password,'data_bi','export_data_bi', [ 8, '2018-01-01'])
|
||||||
|
|
||||||
|
In the parameters of export_data_bi:
|
||||||
|
|
||||||
|
archivo == 1 'Tarifa'
|
||||||
|
|
||||||
|
archivo == 2 'Canal'
|
||||||
|
|
||||||
|
archivo == 3 'Hotel'
|
||||||
|
|
||||||
|
archivo == 4 'Pais'
|
||||||
|
|
||||||
|
archivo == 5 'Regimen'
|
||||||
|
|
||||||
|
archivo == 6 'Reservas'
|
||||||
|
|
||||||
|
archivo == 7 'Capacidad'
|
||||||
|
|
||||||
|
archivo == 8 'Tipo Habitación'
|
||||||
|
|
||||||
|
archivo == 9 'Budget'
|
||||||
|
|
||||||
|
archivo == 10 'Bloqueos'
|
||||||
|
|
||||||
|
archivo == 11 'Motivo Bloqueo'
|
||||||
|
|
||||||
|
archivo == 12 'Segmentos'
|
||||||
|
|
||||||
|
archivo == 13 'Clientes'
|
||||||
|
|
||||||
|
archivo == 14 'Estado Reservas'
|
||||||
|
|
||||||
|
fechafoto = start date to take data
|
||||||
|
|
||||||
|
in the example recive 8 'Tipo Habitación' from '2018-01-01'
|
||||||
|
|
||||||
|
|
||||||
|
Credits
|
||||||
|
=======
|
||||||
|
|
||||||
|
Creator
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||||
1
hotel_data_bi/__init__.py
Normal file
1
hotel_data_bi/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
from . import models
|
||||||
32
hotel_data_bi/__manifest__.py
Normal file
32
hotel_data_bi/__manifest__.py
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright 2018-2019 Jose Luis Algara Toledo
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
{
|
||||||
|
'name': 'Hotel Data Bi',
|
||||||
|
'description': """
|
||||||
|
Export hotel data for business intelligence
|
||||||
|
|
||||||
|
To use this module you need to:
|
||||||
|
|
||||||
|
Create a user and give the 'Hotel Management/Export data BI' permission.
|
||||||
|
""",
|
||||||
|
'summary': "Export hotel data for business intelligence",
|
||||||
|
'version': '2.0',
|
||||||
|
'license': 'AGPL-3',
|
||||||
|
'author': "Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>",
|
||||||
|
'website': 'www.aldahotels.com',
|
||||||
|
'depends': ['hotel', 'hotel_l10n_es', 'hotel_channel_connector'],
|
||||||
|
'category': 'hotel/revenue',
|
||||||
|
'data': [
|
||||||
|
'views/budget.xml',
|
||||||
|
'views/inherit_res_company.xml',
|
||||||
|
'security/data_bi.xml',
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
],
|
||||||
|
'demo': [
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'auto_install': False,
|
||||||
|
'application': False,
|
||||||
|
}
|
||||||
3
hotel_data_bi/models/__init__.py
Normal file
3
hotel_data_bi/models/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
from . import inherit_res_company
|
||||||
|
from . import budget
|
||||||
|
from . import data_bi
|
||||||
48
hotel_data_bi/models/budget.py
Normal file
48
hotel_data_bi/models/budget.py
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import api, fields, models
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
|
|
||||||
|
def get_years():
|
||||||
|
"""Return a year list, to select in year field."""
|
||||||
|
year_list = []
|
||||||
|
for i in range(2018, 2036):
|
||||||
|
year_list.append((i, str(i)))
|
||||||
|
return year_list
|
||||||
|
|
||||||
|
|
||||||
|
class Budget(models.Model):
|
||||||
|
"""Establish and save the budget for DataBI control by revenue"""
|
||||||
|
|
||||||
|
_name = 'budget'
|
||||||
|
|
||||||
|
# fecha Primer día del mes
|
||||||
|
month = fields.Selection([(1, 'January'), (2, 'February'), (3, 'March'),
|
||||||
|
(4, 'April'), (5, 'May'), (6, 'June'),
|
||||||
|
(7, 'July'), (8, 'August'), (9, 'September'),
|
||||||
|
(10, 'October'), (11, 'November'),
|
||||||
|
(12, 'December'), ],
|
||||||
|
string='Month', required=True)
|
||||||
|
year = fields.Selection(get_years(), string='Year', required=True)
|
||||||
|
room_nights = fields.Float("Room Nights", required=True, digits=(6, 2))
|
||||||
|
# Número de Room Nights
|
||||||
|
room_revenue = fields.Float("Room Revenue", required=True, digits=(6, 2))
|
||||||
|
# Ingresos por Reservas
|
||||||
|
estancias = fields.Integer("Number of Stays") # Número de Estancias
|
||||||
|
# ID_Tarifa numérico Código de la Tarifa
|
||||||
|
# ID_Canal numérico Código del Canal
|
||||||
|
# ID_Pais numérico Código del País
|
||||||
|
# ID_Regimen numérico Cóigo del Régimen
|
||||||
|
# ID_Tipo_Habitacion numérico Código del Tipo de Habitación
|
||||||
|
# iD_Segmento numérico Código del Segmento
|
||||||
|
# ID_Cliente numérico Código del Cliente
|
||||||
|
# Pension_Revenue numérico con dos decimales Ingresos por Pensión
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def export_data_bi(self,
|
||||||
|
archivo=False,
|
||||||
|
fechafoto=date.today().strftime('%Y-%m-%d')):
|
||||||
|
apidata = self.env['data_bi']
|
||||||
|
return apidata.export_data_bi(self)
|
||||||
548
hotel_data_bi/models/data_bi.py
Normal file
548
hotel_data_bi/models/data_bi.py
Normal file
@@ -0,0 +1,548 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
##############################################################################
|
||||||
|
#
|
||||||
|
# OpenERP, Open Source Management Solution
|
||||||
|
# Copyright (C) 2018 -2019 Alda Hotels <informatica@aldahotels.com>
|
||||||
|
# Jose Luis Algara <osotranquilo@gmail.com>
|
||||||
|
#
|
||||||
|
# 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/>.
|
||||||
|
#
|
||||||
|
##############################################################################
|
||||||
|
from openerp import models, api, _
|
||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def inv_percent(amount, percent):
|
||||||
|
"""Return the amount to which a percentage was applied."""
|
||||||
|
return round(amount*(100/float(100-percent)) - amount, 2)
|
||||||
|
|
||||||
|
|
||||||
|
class Data_Bi(models.Model):
|
||||||
|
"""Management and export data for MopSolution MyDataBI."""
|
||||||
|
|
||||||
|
_name = 'data_bi'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def export_data_bi(self,
|
||||||
|
archivo=False,
|
||||||
|
fechafoto=date.today().strftime('%Y-%m-%d')):
|
||||||
|
u"""Prepare a Json Objet to export data for MyDataBI.
|
||||||
|
|
||||||
|
Generate a dicctionary to by send in JSON
|
||||||
|
archivo = response file type
|
||||||
|
archivo == 0 'ALL'
|
||||||
|
archivo == 1 'Tarifa'
|
||||||
|
archivo == 2 'Canal'
|
||||||
|
archivo == 3 'Hotel'
|
||||||
|
archivo == 4 'Pais'
|
||||||
|
archivo == 5 'Regimen'
|
||||||
|
archivo == 6 'Reservas'
|
||||||
|
archivo == 7 'Capacidad'
|
||||||
|
archivo == 8 'Tipo Habitación'
|
||||||
|
archivo == 9 'Budget'
|
||||||
|
archivo == 10 'Bloqueos'
|
||||||
|
archivo == 11 'Motivo Bloqueo'
|
||||||
|
archivo == 12 'Segmentos'
|
||||||
|
archivo == 13 'Clientes'
|
||||||
|
archivo == 14 'Estado Reservas'
|
||||||
|
fechafoto = start date to take data
|
||||||
|
"""
|
||||||
|
|
||||||
|
if type(fechafoto) is dict:
|
||||||
|
fechafoto = date.today()
|
||||||
|
else:
|
||||||
|
fechafoto = datetime.strptime(fechafoto, '%Y-%m-%d').date()
|
||||||
|
|
||||||
|
_logger.warning("Init Export Data_Bi Module")
|
||||||
|
if not isinstance(archivo, int):
|
||||||
|
archivo = 0
|
||||||
|
dic_param = []
|
||||||
|
dic_param.append({'Archivo': archivo,
|
||||||
|
'Fechafoto': fechafoto.strftime('%Y-%m-%d')})
|
||||||
|
compan = self.env.user.company_id
|
||||||
|
limit_ago = (fechafoto - timedelta(
|
||||||
|
days=self.env.user.company_id.data_bi_days)).strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
dic_export = [] # Diccionario con todo lo necesario para exportar.
|
||||||
|
if (archivo == 0) or (archivo == 7) or (archivo == 8):
|
||||||
|
room_types = self.env['hotel.room.type'].search([])
|
||||||
|
if (archivo == 0) or (archivo == 10) or (archivo == 6):
|
||||||
|
line_res = self.env['hotel.reservation.line'].search(
|
||||||
|
[('date', '>=', limit_ago)], order="id")
|
||||||
|
estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled']
|
||||||
|
|
||||||
|
if (archivo == 0) or (archivo == 1):
|
||||||
|
dic_tarifa = self.data_bi_tarifa(compan.id_hotel)
|
||||||
|
dic_export.append({'Tarifa': dic_tarifa})
|
||||||
|
if (archivo == 0) or (archivo == 2):
|
||||||
|
dic_canal = self.data_bi_canal(compan.id_hotel)
|
||||||
|
dic_export.append({'Canal': dic_canal})
|
||||||
|
if (archivo == 0) or (archivo == 3):
|
||||||
|
dic_hotel = self.data_bi_hotel(compan)
|
||||||
|
dic_export.append({'Hotel': dic_hotel})
|
||||||
|
if (archivo == 0) or (archivo == 4):
|
||||||
|
dic_pais = self.data_bi_pais(compan.id_hotel)
|
||||||
|
dic_export.append({'Pais': dic_pais})
|
||||||
|
if (archivo == 0) or (archivo == 5):
|
||||||
|
dic_regimen = self.data_bi_regimen(compan.id_hotel)
|
||||||
|
dic_export.append({'Regimen': dic_regimen})
|
||||||
|
if (archivo == 0) or (archivo == 7):
|
||||||
|
dic_capacidad = self.data_bi_capacidad(compan.id_hotel, room_types)
|
||||||
|
dic_export.append({'Capacidad': dic_capacidad})
|
||||||
|
if (archivo == 0) or (archivo == 8):
|
||||||
|
dic_tipo_habitacion = self.data_bi_habitacione(compan.id_hotel,
|
||||||
|
room_types)
|
||||||
|
dic_export.append({'Tipo Habitación': dic_tipo_habitacion})
|
||||||
|
if (archivo == 0) or (archivo == 9):
|
||||||
|
dic_budget = self.data_bi_budget(compan.id_hotel)
|
||||||
|
dic_export.append({'Budget': dic_budget})
|
||||||
|
if (archivo == 0) or (archivo == 10):
|
||||||
|
dic_bloqueos = self.data_bi_bloqueos(compan.id_hotel, line_res)
|
||||||
|
dic_export.append({'Bloqueos': dic_bloqueos})
|
||||||
|
if (archivo == 0) or (archivo == 11):
|
||||||
|
dic_moti_bloq = self.data_bi_moti_bloq(compan.id_hotel)
|
||||||
|
dic_export.append({'Motivo Bloqueo': dic_moti_bloq})
|
||||||
|
if (archivo == 0) or (archivo == 12):
|
||||||
|
dic_segmentos = self.data_bi_segment(compan.id_hotel)
|
||||||
|
dic_export.append({'Segmentos': dic_segmentos})
|
||||||
|
if (archivo == 0) or (archivo == 13) or (archivo == 6):
|
||||||
|
dic_clientes = self.data_bi_client(compan.id_hotel)
|
||||||
|
dic_export.append({'Clientes': dic_clientes})
|
||||||
|
if (archivo == 0) or (archivo == 14):
|
||||||
|
dic_estados = self.data_bi_estados(compan.id_hotel, estado_array)
|
||||||
|
dic_export.append({'Estado Reservas': dic_estados})
|
||||||
|
if (archivo == 0) or (archivo == 6):
|
||||||
|
dic_reservas = self.data_bi_reservas(compan.id_hotel,
|
||||||
|
line_res,
|
||||||
|
estado_array,
|
||||||
|
dic_clientes)
|
||||||
|
dic_export.append({'Reservas': dic_reservas})
|
||||||
|
|
||||||
|
dictionaryToJson = json.dumps(dic_export)
|
||||||
|
_logger.warning("End Export Data_Bi Module to Json")
|
||||||
|
# Debug Stop -------------------
|
||||||
|
# import wdb; wdb.set_trace()
|
||||||
|
# Debug Stop -------------------
|
||||||
|
|
||||||
|
return dictionaryToJson
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_tarifa(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all fees")
|
||||||
|
dic_tarifa = [] # Diccionario con las tarifas
|
||||||
|
tarifas = self.env['product.pricelist'].search_read([], ['name'])
|
||||||
|
for tarifa in tarifas:
|
||||||
|
dic_tarifa.append({'ID_Hotel': compan,
|
||||||
|
'ID_Tarifa': tarifa['id'],
|
||||||
|
'Descripcion': tarifa['name']})
|
||||||
|
return dic_tarifa
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_canal(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all channels")
|
||||||
|
dic_canal = [] # Diccionario con los Canales
|
||||||
|
canal_array = ['Puerta', 'Mail', 'Telefono', 'Call Center', 'Web',
|
||||||
|
'Agencia', 'Touroperador', 'Virtual Door']
|
||||||
|
for i in range(0, len(canal_array)):
|
||||||
|
dic_canal.append({'ID_Hotel': compan,
|
||||||
|
'ID_Canal': i,
|
||||||
|
'Descripcion': canal_array[i]})
|
||||||
|
return dic_canal
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_hotel(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating hotel names")
|
||||||
|
dic_hotel = [] # Diccionario con el/los nombre de los hoteles
|
||||||
|
dic_hotel.append({'ID_Hotel': compan.id_hotel,
|
||||||
|
'Descripcion': compan.property_name})
|
||||||
|
return dic_hotel
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_pais(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all countries")
|
||||||
|
dic_pais = []
|
||||||
|
# Diccionario con los nombre de los Paises usando los del INE
|
||||||
|
paises = self.env['code.ine'].search_read([], ['code', 'name'])
|
||||||
|
for pais in paises:
|
||||||
|
dic_pais.append({'ID_Hotel': compan,
|
||||||
|
'ID_Pais': pais['code'],
|
||||||
|
'Descripcion': pais['name']})
|
||||||
|
return dic_pais
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_regimen(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all board services")
|
||||||
|
dic_regimen = [] # Diccionario con los Board Services
|
||||||
|
board_services = self.env['hotel.board.service'].search_read([])
|
||||||
|
dic_regimen.append({'ID_Hotel': compan,
|
||||||
|
'ID_Regimen': 0,
|
||||||
|
'Descripcion': 'Sin régimen'})
|
||||||
|
for board_service in board_services:
|
||||||
|
dic_regimen.append({'ID_Hotel': compan,
|
||||||
|
'ID_Regimen': board_service['id'],
|
||||||
|
'Descripcion': board_service['name']})
|
||||||
|
return dic_regimen
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_estados(self, compan, estado_array):
|
||||||
|
_logger.info("DataBi: Calculating all the states of the reserves")
|
||||||
|
dic_estados = [] # Diccionario con los Estados Reserva
|
||||||
|
estado_array_txt = ['Borrador', 'Confirmada', 'Hospedandose',
|
||||||
|
'Checkout', 'Cancelada']
|
||||||
|
# estado_array = ['draft', 'confirm', 'booking', 'done', 'cancelled']
|
||||||
|
for i in range(0, len(estado_array)):
|
||||||
|
dic_estados.append({'ID_Hotel': compan,
|
||||||
|
'ID_EstadoReserva': i,
|
||||||
|
'Descripcion': estado_array_txt[i]})
|
||||||
|
return dic_estados
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_habitacione(self, compan, rooms):
|
||||||
|
_logger.info("DataBi: Calculating all room types")
|
||||||
|
dic_tipo_habitacion = [] # Diccionario con Rooms types
|
||||||
|
for room in rooms:
|
||||||
|
dic_tipo_habitacion.append({
|
||||||
|
'ID_Hotel': compan,
|
||||||
|
'ID_Tipo_Habitacion': room['id'],
|
||||||
|
'Descripcion': room['name'],
|
||||||
|
'Estancias': room['capacity']})
|
||||||
|
return dic_tipo_habitacion
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_capacidad(self, compan, rooms):
|
||||||
|
_logger.info("DataBi: Calculating all the capacitys")
|
||||||
|
dic_capacidad = [] # Diccionario con las capacidades
|
||||||
|
for room in rooms:
|
||||||
|
dic_capacidad.append({
|
||||||
|
'ID_Hotel': compan,
|
||||||
|
'Hasta_Fecha':
|
||||||
|
(date.today() + timedelta(days=365 * 3)).strftime("%Y-%m-%d"),
|
||||||
|
'ID_Tipo_Habitacion': room['id'],
|
||||||
|
'Nro_Habitaciones': room['total_rooms_count']})
|
||||||
|
return dic_capacidad
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_budget(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating budget")
|
||||||
|
budgets = self.env['budget'].search([])
|
||||||
|
dic_budget = [] # Diccionario con las previsiones Budget
|
||||||
|
for budget in budgets:
|
||||||
|
dic_budget.append({'ID_Hotel': compan,
|
||||||
|
'Fecha': str(budget.year) + '-' +
|
||||||
|
str(budget.month).zfill(2) + '-01',
|
||||||
|
# 'ID_Tarifa': 0,
|
||||||
|
# 'ID_Canal': 0,
|
||||||
|
# 'ID_Pais': 0,
|
||||||
|
# 'ID_Regimen': 0,
|
||||||
|
# 'ID_Tipo_Habitacion': 0,
|
||||||
|
# 'ID_Cliente': 0,
|
||||||
|
'Room_Nights': budget.room_nights,
|
||||||
|
'Room_Revenue': budget.room_revenue,
|
||||||
|
# 'Pension_Revenue': 0,
|
||||||
|
'Estancias': budget.estancias})
|
||||||
|
# Fecha fecha Primer día del mes
|
||||||
|
# ID_Tarifa numérico Código de la Tarifa
|
||||||
|
# ID_Canal numérico Código del Canal
|
||||||
|
# ID_Pais numérico Código del País
|
||||||
|
# ID_Regimen numérico Cóigo del Régimen
|
||||||
|
# ID_Tipo_Habitacion numérico Código del Tipo de Habitación
|
||||||
|
# iD_Segmento numérico Código del Segmento
|
||||||
|
# ID_Cliente numérico Código del Cliente
|
||||||
|
# Pension_Revenue numérico con dos decimales Ingresos por Pensión
|
||||||
|
return dic_budget
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_moti_bloq(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all blocking reasons")
|
||||||
|
dic_moti_bloq = [] # Diccionario con Motivo de Bloqueos
|
||||||
|
bloqeo_array = ['Staff', _('Out of Service')]
|
||||||
|
for i in range(0, len(bloqeo_array)):
|
||||||
|
dic_moti_bloq.append({'ID_Hotel': compan,
|
||||||
|
'ID_Motivo_Bloqueo': i,
|
||||||
|
'Descripcion': bloqeo_array[i]})
|
||||||
|
return dic_moti_bloq
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_segment(self, compan):
|
||||||
|
_logger.info("DataBi: Calculating all the segmentations")
|
||||||
|
dic_segmentos = [] # Diccionario con Segmentación
|
||||||
|
lineas = self.env['res.partner.category'].search([])
|
||||||
|
for linea in lineas:
|
||||||
|
if linea.parent_id.name:
|
||||||
|
seg_desc = linea.parent_id.name + " / " + linea.name
|
||||||
|
dic_segmentos.append({'ID_Hotel': compan,
|
||||||
|
'ID_Segmento': linea.id,
|
||||||
|
'Descripcion': seg_desc})
|
||||||
|
return dic_segmentos
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_client(self, compan):
|
||||||
|
dic_clientes = [] # Diccionario con Clientes (OTAs y agencias)
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 0,
|
||||||
|
'Descripcion': u'Ninguno'})
|
||||||
|
lineas = self.env['channel.ota.info'].search([])
|
||||||
|
|
||||||
|
for linea in lineas:
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': linea.id,
|
||||||
|
'Descripcion': linea.name})
|
||||||
|
|
||||||
|
lineas = self.env['res.partner'].search([('is_tour_operator',
|
||||||
|
'=', True)])
|
||||||
|
id_cli_count = 700
|
||||||
|
for linea in lineas:
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': id_cli_count,
|
||||||
|
'Descripcion': linea.name})
|
||||||
|
id_cli_count += 1
|
||||||
|
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 999,
|
||||||
|
'Descripcion': u'Web Propia'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 901,
|
||||||
|
'Descripcion': u'Expedia Empaquedata'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 902,
|
||||||
|
'Descripcion': u'Expedia Sin Comisión'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 903,
|
||||||
|
'Descripcion': u'Puerta'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 904,
|
||||||
|
'Descripcion': u'E-Mail'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 905,
|
||||||
|
'Descripcion': u'Teléfono'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 906,
|
||||||
|
'Descripcion': u'Call-Center'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 907,
|
||||||
|
'Descripcion': u'Agencia'})
|
||||||
|
dic_clientes.append({'ID_Hotel': compan,
|
||||||
|
'ID_Cliente': 908,
|
||||||
|
'Descripcion': u'Touroperador'})
|
||||||
|
return dic_clientes
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_bloqueos(self, compan, lines):
|
||||||
|
_logger.info("DataBi: Calculating all reservations blocked")
|
||||||
|
dic_bloqueos = [] # Diccionario con Bloqueos
|
||||||
|
lines = lines.filtered(
|
||||||
|
lambda n: (n.reservation_id.reservation_type != 'normal') and (
|
||||||
|
n.reservation_id.state != 'cancelled'))
|
||||||
|
for line in lines:
|
||||||
|
# if linea.reservation_id.state != 'cancelled':
|
||||||
|
if line.reservation_id.reservation_type == 'out':
|
||||||
|
id_m_b = 1
|
||||||
|
else:
|
||||||
|
id_m_b = 0
|
||||||
|
dic_bloqueos.append({
|
||||||
|
'ID_Hotel': compan,
|
||||||
|
'Fecha_desde': line.date,
|
||||||
|
'Fecha_hasta': (datetime.strptime(line.date, "%Y-%m-%d") +
|
||||||
|
timedelta(days=1)).strftime("%Y-%m-%d"),
|
||||||
|
'ID_Tipo_Habitacion': line.reservation_id.room_type_id.id,
|
||||||
|
'ID_Motivo_Bloqueo': id_m_b,
|
||||||
|
'Nro_Habitaciones': 1})
|
||||||
|
return dic_bloqueos
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_reservas(self, compan, lineas, estado_array, dic_clientes):
|
||||||
|
dic_reservas = []
|
||||||
|
lineas = lineas.filtered(
|
||||||
|
lambda n: (n.reservation_id.reservation_type == 'normal') and (
|
||||||
|
n.price > 0))
|
||||||
|
channels = {'door': 0,
|
||||||
|
'mail': 1,
|
||||||
|
'phone': 2,
|
||||||
|
'call': 3,
|
||||||
|
'web': 4,
|
||||||
|
'agency': 5,
|
||||||
|
'operator': 6,
|
||||||
|
'virtualdoor': 7}
|
||||||
|
|
||||||
|
for linea in lineas:
|
||||||
|
id_segmen = 0
|
||||||
|
if len(linea.reservation_id.segmentation_ids) > 0:
|
||||||
|
id_segmen = linea.reservation_id.segmentation_ids[0].id
|
||||||
|
elif len(linea.reservation_id.partner_id.category_id) > 0:
|
||||||
|
id_segmen = (
|
||||||
|
linea.reservation_id.partner_id.category_id[0].id)
|
||||||
|
precio_neto = linea.price
|
||||||
|
precio_dto = 0
|
||||||
|
precio_iva = 0
|
||||||
|
precio_comision = 0
|
||||||
|
|
||||||
|
if linea.reservation_id.ota_id.id:
|
||||||
|
ota_prices = self.data_bi_comisiones_ota(linea)
|
||||||
|
precio_neto = ota_prices[0]['precio_neto']
|
||||||
|
precio_dto = ota_prices[0]['precio_dto']
|
||||||
|
precio_iva = ota_prices[0]['precio_iva']
|
||||||
|
precio_comision = ota_prices[0]['precio_comision']
|
||||||
|
|
||||||
|
# if linea.reservation_id.id == 6742:
|
||||||
|
# # # Debug Stop -------------------
|
||||||
|
# import wdb; wdb.set_trace()
|
||||||
|
# # # Debug Stop -------------------
|
||||||
|
if linea.reservation_id.discount != 0:
|
||||||
|
precio_dto = linea.price * (
|
||||||
|
linea.reservation_id.discount/100)
|
||||||
|
|
||||||
|
dic_reservas.append({
|
||||||
|
'ID_Reserva': linea.reservation_id.folio_id.name,
|
||||||
|
'ID_Hotel': compan,
|
||||||
|
'ID_EstadoReserva': estado_array.index(
|
||||||
|
linea.reservation_id.state),
|
||||||
|
'FechaVenta': linea.reservation_id.create_date[0:10],
|
||||||
|
'ID_Segmento': id_segmen,
|
||||||
|
# 'ID_Cliente': channel_c,
|
||||||
|
'ID_Canal': channels[linea.reservation_id.channel_type],
|
||||||
|
'FechaExtraccion': date.today().strftime('%Y-%m-%d'),
|
||||||
|
'Entrada': linea.date,
|
||||||
|
'Salida': (datetime.strptime(linea.date, "%Y-%m-%d") +
|
||||||
|
timedelta(days=1)).strftime("%Y-%m-%d"),
|
||||||
|
'Noches': 1,
|
||||||
|
'ID_TipoHabitacion': linea.reservation_id.room_type_id.id,
|
||||||
|
'ID_HabitacionDuerme':
|
||||||
|
linea.reservation_id.room_id.room_type_id.id,
|
||||||
|
'ID_Regimen': 0,
|
||||||
|
'Adultos': linea.reservation_id.adults,
|
||||||
|
'Menores': linea.reservation_id.children,
|
||||||
|
'Cunas': 0,
|
||||||
|
'PrecioDiario': precio_neto,
|
||||||
|
'PrecioComision': precio_comision,
|
||||||
|
'PrecioIva': precio_iva,
|
||||||
|
'PrecioDto': precio_dto,
|
||||||
|
'ID_Tarifa': linea.reservation_id.pricelist_id.id,
|
||||||
|
# 'ID_Pais': id_codeine
|
||||||
|
})
|
||||||
|
# ID_Reserva numérico Código único de la reserva
|
||||||
|
# ID_Hotel numérico Código del Hotel
|
||||||
|
# ID_EstadoReserva numérico Código del estado de la reserva
|
||||||
|
# FechaVenta fecha Fecha de la venta de la reserva
|
||||||
|
# ID_Segmento numérico Código del Segmento de la reserva
|
||||||
|
# ID_Cliente Numérico Código del Cliente de la reserva
|
||||||
|
# ID_Canal numérico Código del Canal
|
||||||
|
# FechaExtraccion fecha Fecha de la extracción de los datos (Foto)
|
||||||
|
# Entrada fecha Fecha de entrada
|
||||||
|
# Salida fecha Fecha de salida
|
||||||
|
# Noches numérico Nro. de noches de la reserva
|
||||||
|
# ID_TipoHabitacion numérico Código del Tipo de Habitación
|
||||||
|
# ID_Regimen numérico Código del Tipo de Régimen
|
||||||
|
# Adultos numérico Nro. de adultos
|
||||||
|
# Menores numérico Nro. de menores
|
||||||
|
# Cunas numérico Nro. de cunas
|
||||||
|
# PrecioDiario numérico con 2 decimales Precio por noche de la reserva
|
||||||
|
# ID_Tarifa numérico Código de la tarifa aplicada a la reserva
|
||||||
|
# ID_Pais numérico Código del país
|
||||||
|
return dic_reservas
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def data_bi_comisiones_ota(self, reserva):
|
||||||
|
response_dic = []
|
||||||
|
precio_neto = reserva.price
|
||||||
|
precio_comision = 0
|
||||||
|
precio_iva = 0
|
||||||
|
precio_dto = 0
|
||||||
|
if reserva.reservation_id.ota_id.ota_id == "2":
|
||||||
|
# Booking. 15% comision
|
||||||
|
precio_comision = (precio_neto*15/100)
|
||||||
|
precio_neto -= precio_comision
|
||||||
|
precio_iva = (precio_neto*10/100)
|
||||||
|
precio_neto -= precio_iva
|
||||||
|
|
||||||
|
if reserva.reservation_id.ota_id.ota_id == "9":
|
||||||
|
# Hotelbeds 20% comision
|
||||||
|
precio_comision = (precio_neto*20/100)
|
||||||
|
precio_neto -= precio_comision
|
||||||
|
precio_iva = (precio_neto*10/100)
|
||||||
|
precio_neto -= precio_iva
|
||||||
|
|
||||||
|
if reserva.reservation_id.ota_id.ota_id == "11":
|
||||||
|
# HRS 20% comision
|
||||||
|
precio_comision = (precio_neto*20/100)
|
||||||
|
precio_neto -= precio_comision
|
||||||
|
precio_iva = (precio_neto*10/100)
|
||||||
|
precio_neto -= precio_iva
|
||||||
|
|
||||||
|
if reserva.reservation_id.ota_id.ota_id == "1":
|
||||||
|
# Expedia.
|
||||||
|
precio_comision = (precio_neto*15/100)
|
||||||
|
precio_neto -= precio_comision
|
||||||
|
precio_iva = (precio_neto*10/100)
|
||||||
|
precio_neto -= precio_iva
|
||||||
|
data = json.loads(
|
||||||
|
reserva.reservation_id.channel_bind_ids.channel_raw_data)
|
||||||
|
|
||||||
|
jsonBooked = data['booked_rooms'][0]
|
||||||
|
if jsonBooked.get('ancillary').get(
|
||||||
|
'channel_rate_name') is not None:
|
||||||
|
jsonRate = jsonBooked.get('ancillary').get(
|
||||||
|
'channel_rate_name')
|
||||||
|
# _logger.warning("EXPEDIA ancillary : %s - %s",
|
||||||
|
# jsonRate, reserva.id)
|
||||||
|
|
||||||
|
elif jsonBooked.get('roomdays')[0].get(
|
||||||
|
'ancillary').get(
|
||||||
|
'channel_rate_name') is not None:
|
||||||
|
jsonRate = jsonBooked.get(
|
||||||
|
'roomdays')[0].get(
|
||||||
|
'ancillary').get('channel_rate_name')
|
||||||
|
# _logger.warning("EXPEDIA roomdays : %s - %s",
|
||||||
|
# jsonRate, reserva.id)
|
||||||
|
|
||||||
|
else:
|
||||||
|
_logger.critical(
|
||||||
|
"EXPEDIA Tarifa No Contemplada : "
|
||||||
|
+ jsonBooked)
|
||||||
|
|
||||||
|
jsonRefundable = jsonRate.upper().find('REFUNDABLE')
|
||||||
|
# _logger.warning("EXPEDIA Tarifa : %s", jsonRate)
|
||||||
|
# _logger.warning("EXPEDIA Tarifa : %s y %s",
|
||||||
|
# jsonRate, str(jsonRefundable))
|
||||||
|
|
||||||
|
# 10 % Iva
|
||||||
|
precio_iva = round((precio_neto-(precio_neto/1.1)), 2)
|
||||||
|
# 18 % comision ?
|
||||||
|
precio_comision = inv_percent(
|
||||||
|
precio_neto, self.env.user.company_id.expedia_rate)
|
||||||
|
precio_neto += precio_comision
|
||||||
|
# 3% Refundable ?
|
||||||
|
if jsonRefundable >= 0:
|
||||||
|
precio_dto = inv_percent(precio_neto, 3)
|
||||||
|
precio_neto += precio_dto
|
||||||
|
# _logger.warning("ODOO: %s , precio_neto: %s , precio_comision: \
|
||||||
|
# %s , precio_iva: %s , precio_dto: %s", reserva.price,
|
||||||
|
# precio_neto, precio_comision, precio_iva,
|
||||||
|
# precio_dto)
|
||||||
|
|
||||||
|
response_dic.append({'ota': reserva.reservation_id.ota_id.id,
|
||||||
|
'ota_id': reserva.reservation_id.ota_id.ota_id,
|
||||||
|
'precio_odoo': reserva.price,
|
||||||
|
'precio_neto': precio_neto,
|
||||||
|
'precio_comision': precio_comision,
|
||||||
|
'precio_iva': precio_iva,
|
||||||
|
'precio_dto': precio_dto,
|
||||||
|
})
|
||||||
|
return response_dic
|
||||||
|
|
||||||
|
# # Debug Stop -------------------
|
||||||
|
# import wdb; wdb.set_trace()
|
||||||
|
# # Debug Stop -------------------
|
||||||
24
hotel_data_bi/models/inherit_res_company.py
Normal file
24
hotel_data_bi/models/inherit_res_company.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
|
||||||
|
from odoo import fields, models
|
||||||
|
|
||||||
|
|
||||||
|
class Inherit_res_company(models.Model):
|
||||||
|
_inherit = 'res.company'
|
||||||
|
|
||||||
|
id_hotel = fields.Integer(
|
||||||
|
'Unique ID for DataBI', default=0,
|
||||||
|
help='It must be unique to be able to identify the hotel, \
|
||||||
|
within a hotel group.')
|
||||||
|
expedia_rate = fields.Integer(
|
||||||
|
'Expedia Rate DataBI',
|
||||||
|
default=18, required=True, digits=(2),
|
||||||
|
help='It is the commission percentage negotiated with the \
|
||||||
|
Expedia company, expressed with two digits. \
|
||||||
|
Example: 18 = 18% commission.')
|
||||||
|
data_bi_days = fields.Integer(
|
||||||
|
'Days to download',
|
||||||
|
default=60, required=True, digits=(3),
|
||||||
|
help='Number of days, which are downloaded data, \
|
||||||
|
backwards, by default are 60 days to download.')
|
||||||
10
hotel_data_bi/security/data_bi.xml
Normal file
10
hotel_data_bi/security/data_bi.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<data noupdate="1">
|
||||||
|
|
||||||
|
<!--Group for hotel export data bi -->
|
||||||
|
<record id="group_hotel_export_data" model="res.groups">
|
||||||
|
<field name="name">Hotel Management / Export data BI</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
</odoo>
|
||||||
14
hotel_data_bi/security/ir.model.access.csv
Normal file
14
hotel_data_bi/security/ir.model.access.csv
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
ir_model_hotel_budget,hoteldatabi.budget.manager,hotel_data_bi.model_budget,hotel.group_hotel_manager,1,1,1,1
|
||||||
|
ir_model_hotel_budget_user,hoteldatabi.budget.user,hotel_data_bi.model_budget,hotel.group_hotel_user,0,0,0,0
|
||||||
|
ir_model_hotel_data_bi_export_data,hoteldatabi.data_bi.export_data,hotel_data_bi.model_data_bi,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_room_type,hoteldatabi.hotel_room_type,hotel.model_hotel_room_type,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_hotel_reservation_line,hoteldatabi.hotel_reservation_line,hotel.model_hotel_reservation_line,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_hotel_reservation,hoteldatabi.hotel_reservation,hotel.model_hotel_reservation,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_hotel_board_service,hoteldatabi.hotel_board_service,hotel.model_hotel_board_service,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_code_ine,hoteldatabi.code_ine,hotel_l10n_es.model_code_ine,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_budget ,hoteldatabi.budget,hotel_data_bi.model_budget,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_channel_ota_info,hoteldatabi.channel_ota_info,hotel_channel_connector.model_channel_ota_info,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_hotel_folio,hoteldatabi.hotel_folio,hotel.model_hotel_folio,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_hotel_room,hoteldatabi.hotel_room,hotel.model_hotel_room,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
access_channel_hotel_reservation,hoteldatabi.channel_hotel_reservation,hotel_channel_connector.model_channel_hotel_reservation,hotel_data_bi.group_hotel_export_data,1,0,0,0
|
||||||
|
BIN
hotel_data_bi/static/description/icon.png
Normal file
BIN
hotel_data_bi/static/description/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 65 KiB |
71
hotel_data_bi/views/budget.xml
Normal file
71
hotel_data_bi/views/budget.xml
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Copyright 2019 Jose Luis Algara (Alda hotels) <osotranquilo@gmail.com>
|
||||||
|
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
|
||||||
|
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<act_window
|
||||||
|
id="budget_act_window"
|
||||||
|
name="Budget for DataBI"
|
||||||
|
res_model="budget"
|
||||||
|
view_mode="tree,form"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<menuitem
|
||||||
|
id="budget_menu"
|
||||||
|
name="Budget for DataBI"
|
||||||
|
parent="hotel.configuration_others"
|
||||||
|
sequence="36"
|
||||||
|
action="budget_act_window"
|
||||||
|
groups="hotel.group_hotel_manager"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="budget_form_view">
|
||||||
|
<field name="name">budget.form (in hotel_data_bi)</field>
|
||||||
|
<field name="model">budget</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<form>
|
||||||
|
<sheet>
|
||||||
|
<group string="Hotel Budget:" colspan="1">
|
||||||
|
<group name="group_top">
|
||||||
|
<group>
|
||||||
|
<div>
|
||||||
|
<label for="month" string="Period:"
|
||||||
|
style="font-weight: bold !important;margin-right:107px;margin-left: 0px;"/>
|
||||||
|
<field name="month" style="width:110px"/>
|
||||||
|
<field name="year" style="width:110px;margin-left: 34px;"/>
|
||||||
|
</div>
|
||||||
|
</group>
|
||||||
|
<group>
|
||||||
|
<field name="room_nights"/>
|
||||||
|
<field name="room_revenue"/>
|
||||||
|
<field name="estancias"/>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
</group>
|
||||||
|
<div>
|
||||||
|
<button name="export_data_bi" class="oe_form_button_save btn btn-primary btn-sm" type="object" string="Generate Conexion"
|
||||||
|
groups="base.group_system"/>
|
||||||
|
</div>
|
||||||
|
</sheet>
|
||||||
|
<div class="oe_chatter"></div>
|
||||||
|
</form>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
|
||||||
|
<record model="ir.ui.view" id="budget_tree_view">
|
||||||
|
<field name="name">budget.tree (in hotel_data_bi)</field>
|
||||||
|
<field name="model">budget</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree>
|
||||||
|
<field name="month"/>
|
||||||
|
<field name="year"/>
|
||||||
|
<field name="room_nights"/>
|
||||||
|
<field name="room_revenue"/>
|
||||||
|
<field name="estancias"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
23
hotel_data_bi/views/inherit_res_company.xml
Normal file
23
hotel_data_bi/views/inherit_res_company.xml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
|
||||||
|
<!-- Hotel Settings -->
|
||||||
|
<data>
|
||||||
|
<!-- Inherit Company view to add 'Hotel dataBI' -->
|
||||||
|
<record id="data_bi_view_company_form" model="ir.ui.view">
|
||||||
|
<field name="name">databi.config.view_company_form</field>
|
||||||
|
<field name="model">res.company</field>
|
||||||
|
<field name="inherit_id" ref="base.view_company_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='cardex_warning']" position="after">
|
||||||
|
<group string="DataBi Hotel Services" name="hotel_databi">
|
||||||
|
<field name="id_hotel" />
|
||||||
|
<field name="data_bi_days" />
|
||||||
|
<field name="expedia_rate" />
|
||||||
|
</group>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
@@ -69,42 +69,19 @@
|
|||||||
<xpath expr="//field[@name='partner_id']" position="after">
|
<xpath expr="//field[@name='partner_id']" position="after">
|
||||||
<field name="firstname" attrs="{'required': [
|
<field name="firstname" attrs="{'required': [
|
||||||
('lastname','==', False)
|
('lastname','==', False)
|
||||||
],'readonly': [('partner_id', '!=', False),
|
]}" />
|
||||||
('firstname','!=', False)]
|
|
||||||
}"
|
|
||||||
force_save="1" />
|
|
||||||
<field name="lastname" attrs="{'required': [
|
<field name="lastname" attrs="{'required': [
|
||||||
('firstname','==', False)
|
('firstname','==', False)
|
||||||
], 'readonly': [('partner_id', '!=', False),
|
]}" />
|
||||||
('lastname','!=', False)]}"
|
|
||||||
force_save="1" />
|
|
||||||
<field name="document_type"
|
<field name="document_type"
|
||||||
attrs="{'required': [
|
attrs="{'required': [
|
||||||
('document_number','!=', False)
|
('document_number','!=', False)
|
||||||
],'readonly': [('document_number', '!=', False),
|
]}" />
|
||||||
('document_type','!=', False),
|
<field name="document_number" string="Doc. Number" />
|
||||||
('partner_id','!=', False)]}"
|
<field name="document_expedition_date" string="Exp. Date" />
|
||||||
force_save="1"/>
|
<field name="birthdate_date" string="Birthdate" />
|
||||||
<field name="document_number" string="Doc. Number"
|
<field name="code_ine_id" />
|
||||||
attrs="{'readonly': [('partner_id', '!=', False),
|
<field name="gender" />
|
||||||
('document_number','!=', False)]}"
|
|
||||||
force_save="1"/>
|
|
||||||
<field name="document_expedition_date" string="Exp. Date"
|
|
||||||
attrs="{'readonly': [('partner_id', '!=', False),
|
|
||||||
('document_expedition_date','!=', False)]}"
|
|
||||||
force_save="1"/>
|
|
||||||
<field name="birthdate_date" string="Birthdate"
|
|
||||||
attrs="{'readonly': [('partner_id', '!=', False),
|
|
||||||
('birthdate_date','!=', False)]}"
|
|
||||||
force_save="1" />
|
|
||||||
<field name="code_ine_id"
|
|
||||||
attrs="{'readonly': [('partner_id', '!=', False),
|
|
||||||
('code_ine_id','!=', False)]}"
|
|
||||||
force_save="1" />
|
|
||||||
<field name="gender"
|
|
||||||
attrs="{'readonly': [('partner_id', '!=', False),
|
|
||||||
('gender','!=', False)]}"
|
|
||||||
force_save="1" />
|
|
||||||
</xpath>
|
</xpath>
|
||||||
<xpath expr="//field[@name='partner_id']" position="attributes">
|
<xpath expr="//field[@name='partner_id']" position="attributes">
|
||||||
<attribute name="options">{"no_create": True}</attribute>
|
<attribute name="options">{"no_create": True}</attribute>
|
||||||
|
|||||||
Reference in New Issue
Block a user