Merge branch 'hotel_node_master' into 11.0

This commit is contained in:
Pablo
2018-11-02 17:16:42 +01:00
9 changed files with 504 additions and 195 deletions

View File

@@ -32,10 +32,34 @@ class HotelRoomType(models.Model):
('date', '>=', dfrom), ('date', '>=', dfrom),
('date', '<', dto), ('date', '<', dto),
('room_type_id', '=', room_type_id), ('room_type_id', '=', room_type_id),
], ['avail']) or [{'avail': availability_real}]
], ['avail']) or float('inf') availability_plan = min([r['avail'] for r in availability_plan])
if isinstance(availability_plan, list):
availability_plan = min([r['avail'] for r in availability_plan])
return min(availability_real, availability_plan) return min(availability_real, availability_plan)
@api.model
def get_room_type_price_unit(self, dfrom, dto, room_type_id):
# TODO review how to get the prices
reservation_line_ids = self.env['hotel.reservation'].prepare_reservation_lines(
dfrom,
(fields.Date.from_string(dto) - fields.Date.from_string(dfrom)).days,
{'room_type_id': room_type_id}
)
reservation_line_ids = reservation_line_ids['reservation_line_ids']
# QUESTION Why add [[5, 0, 0], ¿?
del reservation_line_ids[0]
return reservation_line_ids
@api.model
def get_room_type_restrictions(self, dfrom, dto, room_type_id):
restrictions_plan = self.env['hotel.room.type.restriction.item'].search_read([
('date', '>=', dfrom),
('date', '<', dto),
('room_type_id', '=', room_type_id),
], ['min_stay']) or [{'min_stay': 0}]
min_stay = max([r['min_stay'] for r in restrictions_plan])
return min_stay

View File

@@ -14,12 +14,12 @@
{'python' : ['odoorpc']}, {'python' : ['odoorpc']},
'license': "AGPL-3", 'license': "AGPL-3",
'data': [ 'data': [
'wizards/wizard_hotel_node_reservation.xml',
'views/hotel_node.xml', 'views/hotel_node.xml',
'views/hotel_node_user.xml', 'views/hotel_node_user.xml',
'views/hotel_node_group.xml', 'views/hotel_node_group.xml',
'views/hotel_node_room_type.xml', 'views/hotel_node_room_type.xml',
'views/inherited_res_partner_views.xml', 'views/inherited_res_partner_views.xml',
'wizards/wizard_hotel_node_reservation.xml',
'security/hotel_node_security.xml', 'security/hotel_node_security.xml',
'security/ir.model.access.csv' 'security/ir.model.access.csv'
], ],

View File

@@ -145,6 +145,13 @@ class HotelNode(models.Model):
master_ids = [r['id'] for r in master_users] master_ids = [r['id'] for r in master_users]
remote_ids = [r['remote_user_id'] for r in master_users] remote_ids = [r['remote_user_id'] for r in master_users]
# For the first hotel, gui_ids and xml_ids is empty. You must recover the previously written groups
master_groups = self.env["hotel.node.group"].search_read(
[('odoo_version', '=', self.odoo_version)], ['xml_id'])
gui_ids = [r['id'] for r in master_groups]
xml_ids = [r['xml_id'] for r in master_groups]
user_ids = [] user_ids = []
for user in remote_users: for user in remote_users:
group_ids = [] group_ids = []

View File

@@ -8,11 +8,16 @@
<form string="Hotel Node"> <form string="Hotel Node">
<sheet string="Hotel Node"> <sheet string="Hotel Node">
<div class="oe_button_box" name="button_box" groups="base.group_user"> <div class="oe_button_box" name="button_box" groups="base.group_user">
<button name="%(hotel_node_reservation_action)d" type="action" <button name="%(hotel_node_reservation_wizard_action)d" type="action"
string="Reserve" help="Make a reservation in this hotel" string="Reserve" help="Make a reservation in this hotel"
class="oe_stat_button" icon="fa-suitcase" class="oe_stat_button" icon="fa-suitcase"
context="{'node_id': id}"> context="{'node_id': id}">
</button> </button>
<button name="%(hotel_node_reservation_wizard_action_search)d" type="action"
string="Search" help="Search a reservation in this hotel"
class="oe_stat_button" icon="fa-search"
context="{'node_id': id}">
</button>
<button class="oe_stat_button" type="action" <button class="oe_stat_button" type="action"
name="" icon="fa-tasks"> name="" icon="fa-tasks">
<field string="Tasks" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/> <field string="Tasks" name="task_count" widget="statinfo" options="{'label_field': 'label_tasks'}"/>
@@ -105,7 +110,7 @@
</field> </field>
</record> </record>
<record id="hotel_node_action_open_dashboard" model="ir.actions.act_window"> <record id="hotel_node_action_kanban" model="ir.actions.act_window">
<field name="name">Hotels</field> <field name="name">Hotels</field>
<field name="res_model">project.project</field> <field name="res_model">project.project</field>
<field name="view_type">form</field> <field name="view_type">form</field>
@@ -114,81 +119,19 @@
<field name="target">main</field> <field name="target">main</field>
</record> </record>
<!-- Action to open Hotel Nodes List --> <menuitem id="hotel_node_menu"
<act_window id="hotel_node_action" name="Hotel Central Dashboard"
name="List of Hotels" action="hotel_node_action_kanban"
res_model="project.project"
view_mode="tree,form"
/>
<!-- Action to open Hotel Room Types List -->
<act_window id="hotel_node_room_type_action"
name="List of Room Types in Hotels"
res_model="hotel.node.room.type"
view_mode="tree,form"
/>
<!-- Action to open Hotel Users List -->
<act_window id="hotel_node_user_action"
name="List of Users in Hotels"
res_model="hotel.node.user"
view_mode="tree,form"
/>
<!-- Action to open Hotel Groups List -->
<act_window id="hotel_node_group_action"
name="List of Access Groups in Hotels"
res_model="hotel.node.group"
view_mode="tree,form"
/>
<!-- Action to open Hotel Node Reservation List -->
<act_window id="hotel_node_reservation_action"
name="Hotel Reservation Wizard"
res_model="hotel.node.reservation.wizard"
view_mode="form"
target="new"
/> />
<!-- Menu default to open Hotel Nodes Dashboard --> <menuitem id="hotel_node_menu_dashboard"
<menuitem id="hotel_node_dashboard_menu"
name="Hotel Central Dashboard"
action="hotel_node_action_open_dashboard"
/>
<!-- Menu Management Section -->
<menuitem id="dashboard_menu"
name="Management" name="Management"
parent="hotel_node_dashboard_menu" parent="hotel_node_menu"
/> />
<!-- Menu item to open Hotel Nodes List -->
<menuitem id="hotel_node_menu" <!--<menuitem id="hotel_node_menu_tree"-->
name="Hotels" <!--name="Hotels"-->
action="hotel_node_action" <!--parent="hotel_node_menu_dashboard"-->
parent="dashboard_menu" <!--sequence="0"-->
sequence="1"
/>
<!-- Menu item to open Hotel Users List -->
<menuitem id="hotel_node_user_menu"
name="Users"
action="hotel_node_user_action"
parent="dashboard_menu"
sequence="2"
/>
<!-- Menu item to open Hotel Users List -->
<menuitem id="hotel_node_group_menu"
name="Access Groups"
action="hotel_node_group_action"
parent="dashboard_menu"
sequence="3"
/>
<!-- Menu item to open Hotel Nodes List -->
<menuitem id="hotel_node_room_type_menu"
name="Room Types"
action="hotel_node_room_type_action"
parent="dashboard_menu"
sequence="1"
/>
<!-- Menu item to open Hotel Node Reservation List -->
<!--<menuitem id="hotel_node_reservation_menu"-->
<!--name="Reservations Wizard"-->
<!--action="hotel_node_reservation_action"-->
<!--parent="dashboard_menu"-->
<!--sequence="4"-->
<!--/>--> <!--/>-->
</odoo> </odoo>

View File

@@ -11,4 +11,19 @@
</tree> </tree>
</field> </field>
</record> </record>
<record id="hotel_node_group_action" model="ir.actions.act_window">
<field name="name">List of Access Groups in Hotels</field>
<field name="res_model">hotel.node.group</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="target">main</field>
</record>
<menuitem id="hotel_node_group_menu"
name="Access Groups"
action="hotel_node_group_action"
parent="hotel_node_menu_dashboard"
sequence="3"
/>
</odoo> </odoo>

View File

@@ -54,4 +54,19 @@
</tree> </tree>
</field> </field>
</record> </record>
<record id="hotel_node_room_type_action" model="ir.actions.act_window">
<field name="name">List of Room Types in Hotels</field>
<field name="res_model">hotel.node.room.type</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="target">main</field>
</record>
<menuitem id="hotel_node_room_type_menu"
name="Room Types"
action="hotel_node_room_type_action"
parent="hotel_node_menu_dashboard"
sequence="1"
/>
</odoo> </odoo>

View File

@@ -51,4 +51,19 @@
</tree> </tree>
</field> </field>
</record> </record>
<record id="hotel_node_user_action" model="ir.actions.act_window">
<field name="name">List of Users in Hotels</field>
<field name="res_model">hotel.node.user</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="target">main</field>
</record>
<menuitem id="hotel_node_user_menu"
name="Users"
action="hotel_node_user_action"
parent="hotel_node_menu_dashboard"
sequence="2"
/>
</odoo> </odoo>

View File

@@ -2,15 +2,13 @@
# Copyright 2018 Alexandre Díaz # Copyright 2018 Alexandre Díaz
# Copyright 2018 Dario Lodeiros # Copyright 2018 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from builtins import list
import wdb import wdb
import logging import logging
import urllib.error import urllib.error
import odoorpc.odoo import odoorpc.odoo
from datetime import timedelta from datetime import timedelta
from odoo import models, fields, api, _ from odoo import models, fields, api, _
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError, UserError
from odoo.tools import ( from odoo.tools import (
DEFAULT_SERVER_DATE_FORMAT) DEFAULT_SERVER_DATE_FORMAT)
@@ -18,6 +16,7 @@ _logger = logging.getLogger(__name__)
class HotelNodeReservationWizard(models.TransientModel): class HotelNodeReservationWizard(models.TransientModel):
# TODO Rename to node.engine.reservation.wizard
_name = "hotel.node.reservation.wizard" _name = "hotel.node.reservation.wizard"
_description = "Hotel Node Reservation Wizard" _description = "Hotel Node Reservation Wizard"
@@ -35,123 +34,107 @@ class HotelNodeReservationWizard(models.TransientModel):
today = fields.Date.context_today(self.with_context()) today = fields.Date.context_today(self.with_context())
return (fields.Date.from_string(today) + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT) return (fields.Date.from_string(today) + timedelta(days=1)).strftime(DEFAULT_SERVER_DATE_FORMAT)
@api.model
def _default_room_type_wizard_ids(self):
node_id = self.env['project.project'].browse(self._context.get('node_id'))
checkin = self._default_checkin()
checkout = self._default_checkout()
room_type_wizard_ids = node_id.room_type_ids.mapped(lambda room_type_id: (0, False, {
'room_type_id': room_type_id.id,
'room_type_availability': 0,
'checkin': checkin,
'checkout': checkout,
}))
return room_type_wizard_ids
node_id = fields.Many2one('project.project', 'Hotel', required=True, default=_default_node_id) node_id = fields.Many2one('project.project', 'Hotel', required=True, default=_default_node_id)
partner_id = fields.Many2one('res.partner', string="Customer", required=True) partner_id = fields.Many2one('res.partner', string="Customer", required=True)
checkin = fields.Date('Check In', required=True, default=_default_checkin) checkin = fields.Date('Check In', required=True, default=_default_checkin)
checkout = fields.Date('Check Out', required=True, default=_default_checkout) checkout = fields.Date('Check Out', required=True, default=_default_checkout)
room_type_wizard_ids = fields.One2many('node.room.type.wizard', 'node_reservation_wizard_id', room_type_wizard_ids = fields.One2many('node.room.type.wizard', 'node_reservation_wizard_id',
string="Room Types") string="Room Types", default=_default_room_type_wizard_ids)
price_total = fields.Float(string='Total Price', compute='_compute_price_total') price_total = fields.Float(string='Total Price', compute='_compute_price_total', store=True)
@api.constrains('room_type_wizard_ids.room_qty')
def _check_room_type_wizard_ids(self):
"""
:raise: ValidationError
"""
total_qty = 0
for rec in self.room_type_wizard_ids:
total_qty += rec.room_qty
if total_qty == 0:
msg = _("It is not possible to create the reservation.") + " " + \
_("Maybe you forgot adding the quantity to at least one type of room?.")
raise ValidationError(msg)
@api.depends('room_type_wizard_ids.price_total') @api.depends('room_type_wizard_ids.price_total')
def _compute_price_total(self): def _compute_price_total(self):
_logger.info('_compute_price_total for wizard %s', self.id) _logger.info('_compute_price_total for wizard %s', self.id)
price_total = 0.0 self.price_total = 0.0
for record in self.room_type_wizard_ids: for rec in self.room_type_wizard_ids:
price_total += record.price_total self.price_total += rec.price_total
self.price_total = price_total
@api.onchange('node_id') @api.onchange('node_id')
def _onchange_node_id(self): def _onchange_node_id(self):
self.ensure_one() self.ensure_one()
if self.node_id: if self.node_id:
_logger.info('_onchange_node_id(self): %s', self) _logger.info('_onchange_node_id(self): %s', self)
# Save your credentials (session) # TODO Save your credentials (session)
@api.onchange('checkin', 'checkout') @api.onchange('checkin', 'checkout')
def _onchange_dates(self): def _onchange_dates(self):
self.ensure_one() self.ensure_one()
_logger.info('_onchange_dates(self): %s', self) _logger.info('_onchange_dates(self): %s', self)
# TODO check hotel timezone # TODO check hotel timezone
self.checkin = self._get_default_checkin() if not self.checkin \ self.checkin = self._default_checkin() if not self.checkin \
else fields.Date.from_string(self.checkin) else fields.Date.from_string(self.checkin)
self.checkout = self._get_default_checkout() if not self.checkout \ self.checkout = self._default_checkout() if not self.checkout \
else fields.Date.from_string(self.checkout) else fields.Date.from_string(self.checkout)
if fields.Date.from_string(self.checkin) >= fields.Date.from_string(self.checkout): if fields.Date.from_string(self.checkin) >= fields.Date.from_string(self.checkout):
self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime( self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime(
DEFAULT_SERVER_DATE_FORMAT) DEFAULT_SERVER_DATE_FORMAT)
try: # update room_type_wizard_ids
noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port) for rec in self.room_type_wizard_ids:
noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password) if self.checkin != rec.checkin:
_logger.warning('_onchange_dates need new data for room_type: %s', rec.room_type_id)
# free_room_ids = noderpc.env['hotel.room.type'].check_availability_room_ids(self.checkin, self.checkout)
room_type_availability = {}
# room_type_price_unit = {}
for room_type in self.node_id.room_type_ids:
room_type_availability[room_type.id] = \
noderpc.env['hotel.room.type'].get_room_type_availability(
self.checkin, self.checkout, room_type.remote_room_type_id)
# availability_real = noderpc.env['hotel.room'].search_count([
# ('id', 'in', free_room_ids),
# ('room_type_id', '=', room_type.remote_room_type_id),
# ])
# availability_plan = noderpc.env['hotel.room.type.availability'].search_read([
# ('date', '>=', self.checkin),
# ('date', '<', self.checkout),
# ('room_type_id', '=', room_type.remote_room_type_id),
#
# ], ['avail']) or float('inf')
#
# if isinstance(availability_plan, list):
# availability_plan = min([r['avail'] for r in availability_plan])
#
# room_type_availability[room_type.id] = min(
# availability_real, availability_plan)
# room_type_price_unit[room_type.id] = noderpc.env['hotel.room.type'].search_read([
# ('id', '=', room_type.remote_room_type_id),
# ], ['list_price'])[0]['list_price']
nights = (fields.Date.from_string(self.checkout) - fields.Date.from_string(self.checkin)).days
cmds = self.node_id.room_type_ids.mapped(lambda room_type_id: (0, False, {
'room_type_id': room_type_id.id,
'checkin': self.checkin,
'checkout': self.checkout,
'nights': nights,
'room_type_availability': room_type_availability[room_type_id.id],
# 'price_unit': room_type_price_unit[room_type_id.id],
'node_reservation_wizard_id': self.id,
}))
self.room_type_wizard_ids = cmds
noderpc.logout()
except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err:
raise ValidationError(err)
@api.multi @api.multi
def create_node_reservation(self): def create_node_reservation(self):
self.ensure_one()
try: try:
noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port) noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port)
noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password) noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password)
# prepare required fields for hotel folio # prepare required fields for hotel folio
remote_partner_id = noderpc.env['res.partner'].search([('email','=',self.partner_id.email)]).pop() remote_partner_id = noderpc.env['res.partner'].search([('email', '=', self.partner_id.email)]).pop()
vals = { vals = {
'partner_id': remote_partner_id, 'partner_id': remote_partner_id,
} }
# prepare hotel folio room_lines # prepare hotel folio room_lines
room_lines = [] room_lines = []
for room_type in self.room_type_wizard_ids: for rec in self.room_type_wizard_ids:
for x in range(room_type.room_qty): for x in range(rec.room_qty):
vals_reservation_lines = { # vals_reservation_lines = {
'partner_id': remote_partner_id, # 'partner_id': remote_partner_id,
'room_type_id': room_type.room_type_id.remote_room_type_id, # 'room_type_id': rec.room_type_id.remote_room_type_id,
} # }
# add discount # add discount
reservation_line_ids = noderpc.env['hotel.reservation'].prepare_reservation_lines( # reservation_line_ids = noderpc.env['hotel.reservation'].prepare_reservation_lines(
room_type.checkin, # rec.checkin,
(fields.Date.from_string(room_type.checkout) - fields.Date.from_string(room_type.checkin)).days, # (fields.Date.from_string(rec.checkout) - fields.Date.from_string(rec.checkin)).days,
vals_reservation_lines # vals_reservation_lines
) # [[5, 0, 0], ¿? # ) # [[5, 0, 0], ¿?
wdb.set_trace()
room_lines.append((0, False, { room_lines.append((0, False, {
'room_type_id': room_type.room_type_id.remote_room_type_id, 'room_type_id': rec.room_type_id.remote_room_type_id,
'checkin': room_type.checkin, 'checkin': rec.checkin,
'checkout': room_type.checkout, 'checkout': rec.checkout,
'reservation_line_ids': reservation_line_ids['reservation_line_ids'], # 'reservation_line_ids': reservation_line_ids['reservation_line_ids'],
})) }))
vals.update({'room_lines': room_lines}) vals.update({'room_lines': room_lines})
@@ -163,65 +146,289 @@ class HotelNodeReservationWizard(models.TransientModel):
self._context.get('uid'), folio_id) self._context.get('uid'), folio_id)
noderpc.logout() noderpc.logout()
# return self._open_wizard_action_search()
except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err: except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err:
raise ValidationError(err) raise ValidationError(err)
@api.multi
def _open_wizard_action_search(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'res_model': self._name,
'res_id': self.id,
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
}
class NodeRoomTypeWizard(models.TransientModel): class NodeRoomTypeWizard(models.TransientModel):
_name = "node.room.type.wizard" _name = "node.room.type.wizard"
_description = "Node Room Type Wizard" _description = "Node Room Type Wizard"
node_reservation_wizard_id = fields.Many2one('hotel.node.reservation.wizard') node_reservation_wizard_id = fields.Many2one('hotel.node.reservation.wizard')
node_id = fields.Many2one(related='node_reservation_wizard_id.node_id')
room_type_id = fields.Many2one('hotel.node.room.type', 'Rooms Type') room_type_id = fields.Many2one('hotel.node.room.type', 'Rooms Type')
room_type_name = fields.Char('Name', related='room_type_id.name') room_type_availability = fields.Integer('Availability', compute="_compute_restrictions", readonly=True, store=True)
room_type_availability = fields.Integer('Availability', readonly=True) #, compute="_compute_room_type_availability")
room_qty = fields.Integer('Quantity', default=0) room_qty = fields.Integer('Quantity', default=0)
room_type_line_ids = fields.One2many('node.room.type.line.wizard', 'node_room_type_line_wizard_id',
compute="_compute_restrictions", string="Room type detail per day.")
checkin = fields.Date('Check In', required=True) checkin = fields.Date('Check In', required=True)
checkout = fields.Date('Check Out', required=True) checkout = fields.Date('Check Out', required=True)
nights = fields.Integer('Nights', readonly=True) nights = fields.Integer('Nights', compute="_compute_nights", readonly=True, store=True)
min_stay = fields.Integer('Min. Days', compute="_compute_restrictions", readonly=True)
price_unit = fields.Float(string='Room Price', required=True, default=0.0, readonly=True) min_stay = fields.Integer('Min. Days', compute="_compute_restrictions", readonly=True, store=True)
# price_unit indicates Room Price x Nights
price_unit = fields.Float(string='Room Price', compute="_compute_restrictions", store=True)
discount = fields.Float(string='Discount (%)', default=0.0) discount = fields.Float(string='Discount (%)', default=0.0)
price_total = fields.Float(string='Total Price', compute='_compute_price_total') price_total = fields.Float(string='Total Price', compute='_compute_price_total', readonly=True, store=True)
@api.constrains('room_qty')
def _check_room_qty(self):
"""
:raise: ValidationError
"""
total_qty = 0
for rec in self:
if (rec.room_type_availability < rec.room_qty) or (rec.room_qty > 0 and rec.nights < rec.min_stay):
msg = _("At least one room type has not availability or does not meet restrictions.") + " " + \
_("Please, review room type %s between %s and %s.") % (rec.room_type_id.name, rec.checkin, rec.checkout)
_logger.warning(msg)
raise ValidationError(msg)
total_qty += rec.room_qty
@api.depends('room_qty', 'price_unit', 'discount') @api.depends('room_qty', 'price_unit', 'discount')
def _compute_price_total(self): def _compute_price_total(self):
for room_type in self: for rec in self:
_logger.info('_compute_price_total for room type %s', room_type.room_type_id) _logger.info('_compute_price_total for room type %s', rec.room_type_id)
# noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port) rec.price_total = (rec.room_qty * rec.price_unit) * (1.0 - rec.discount * 0.01)
# noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password)
# self.price_unit = noderpc.env['hotel.room.type'].search_read([
# ('id', '=', self.room_type_id.remote_room_type_id),
# ], ['list_price'])[0]['list_price']
# noderpc.logout()
room_type.price_total = (room_type.room_qty * room_type.price_unit * room_type.nights) * (1.0 - room_type.discount * 0.01) @api.depends('checkin', 'checkout')
# Unidades x precio unidad (el precio de unidad ya incluye el conjunto de días) def _compute_nights(self):
for rec in self:
rec.nights = (fields.Date.from_string(rec.checkout) - fields.Date.from_string(rec.checkin)).days
@api.depends('checkin', 'checkout') @api.depends('checkin', 'checkout')
def _compute_restrictions(self): def _compute_restrictions(self):
for room_type in self: for rec in self:
_logger.info('_compute_restrictions for room type %s', room_type.room_type_id) if rec.checkin and rec.checkout:
try:
node_id = rec.node_reservation_wizard_id.node_id
# TODO Load your credentials (session) ... should be faster?
noderpc = odoorpc.ODOO(node_id.odoo_host, node_id.odoo_protocol, node_id.odoo_port)
noderpc.login(node_id.odoo_db, node_id.odoo_user, node_id.odoo_password)
rec.room_type_availability = noderpc.env['hotel.room.type'].get_room_type_availability(
rec.checkin,
rec.checkout,
rec.room_type_id.remote_room_type_id)
_logger.warning('_compute_restrictions [availability: %s] for room type %s', rec.room_type_availability, rec.room_type_id)
# rec.room_type_line_ids = noderpc.env['hotel.room.type'].get_room_type_price_unit(
# rec.checkin,
# rec.checkout,
# rec.room_type_id.remote_room_type_id)
cmds = []
for x in range(rec.nights):
cmds.append((0, False, {
'node_room_type_line_wizard_id': rec.id,
'date': (fields.Date.from_string(rec.checkin) + timedelta(days=x)).strftime(
DEFAULT_SERVER_DATE_FORMAT),
'price': 0.0,
}))
rec.room_type_line_ids = cmds
rec.price_unit = sum(rec.room_type_line_ids.mapped('price'))
_logger.warning('_compute_restrictions [price_unit: %s] for room type %s', rec.price_unit, rec.room_type_id)
rec.min_stay = noderpc.env['hotel.room.type'].get_room_type_restrictions(
rec.checkin,
rec.checkout,
rec.room_type_id.remote_room_type_id)
_logger.warning('_compute_restrictions [min days: %s] for room type %s', rec.min_stay, rec.room_type_id)
noderpc.logout()
except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err:
raise ValidationError(err)
@api.onchange('room_qty')
def _onchange_room_qty(self):
if self.room_type_availability < self.room_qty:
msg = _("Please, review room type %s between %s and %s.") % (self.room_type_id.name, self.checkin, self.checkout)
return {
'warning': {
'title': 'Warning: Invalid room quantity',
'message': msg,
}
}
@api.onchange('checkin', 'checkout') @api.onchange('checkin', 'checkout')
def _onchange_dates(self): def _onchange_dates(self):
_logger.info('_onchange_dates for room type %s', self.room_type_id) _logger.info('+++ _onchange_dates for room type %s +++', self.room_type_id)
# recompute price unit
self.checkin = self._default_checkin() \ self.checkin = self._default_checkin() \
if not self.checkin else fields.Date.from_string(self.checkin) if not self.checkin else fields.Date.from_string(self.checkin)
self.checkout = self._default_checkout() \ self.checkout = self._default_checkout() \
if not self.checkout else fields.Date.from_string(self.checkout) if not self.checkout else fields.Date.from_string(self.checkout)
if fields.Date.from_string(self.checkin) >= fields.Date.from_string(self.checkout): if fields.Date.from_string(self.checkin) >= fields.Date.from_string(self.checkout):
self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime( self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime(
DEFAULT_SERVER_DATE_FORMAT) DEFAULT_SERVER_DATE_FORMAT)
self.nights = (fields.Date.from_string(self.checkout) - fields.Date.from_string(self.checkin)).days
# Conectar con nodo para traer dispo(availability) y precio por habitación(price_unit) class NodeRoomTypeLineWizard(models.TransientModel):
# availability: search de hotel.room.type.availability filtrando por room_type y date y escogiendo el min avail en el rango _name = "node.room.type.line.wizard"
# preci_unit y json_days: usando prepare_reservation_lines _description = "Node Room Type Detail per Day Wizard"
node_room_type_line_wizard_id = fields.Many2one('node.room.type.wizard',
ondelete='cascade', required=True)
date = fields.Date('Date')
price = fields.Float('Price')
class NodeSearchWizard(models.TransientModel):
_name = "node.search.wizard"
_description = "Node Search Wizard"
@api.model
def _default_node_id(self):
return self._context.get('node_id') or None
node_id = fields.Many2one('project.project', 'Hotel', default=_default_node_id)
folio = fields.Char('Folio Number')
partner_id = fields.Many2one('res.partner', string="Customer")
email = fields.Char('E-mail', related='partner_id.email')
checkin = fields.Date('Check In')
checkout = fields.Date('Check Out')
@api.multi
def search_node_reservation(self):
self.ensure_one()
try:
noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port)
noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password)
domain = []
if self.folio:
domain.append(('name', '=', 'F/' + self.folio))
if self.partner_id:
domain.append(('email', '=', self.email))
if self.checkin:
domain.append(('checkin', '=', self.checkin))
folio_id = noderpc.env['hotel.folio'].search(domain)
if not folio_id:
raise UserError(_("No reservations found."))
noderpc.logout()
# TODO Need to manage more than one folio
return self._open_wizard_action_edit(folio_id.pop())
except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err:
raise ValidationError(err)
@api.multi
def _open_wizard_action_edit(self, folio_id):
self.ensure_one()
return {
'name': _('Hotel Reservation Wizard Edit'),
'type': 'ir.actions.act_window',
'res_model': 'node.folio.wizard',
'view_id': self.env.ref('hotel_node_master.hotel_node_reservation_wizard_view_edit_form', False).id,
'view_type': 'form',
'view_mode': 'form',
'target': 'new',
'context': {'folio_id': folio_id},
}
class NodeFolioWizard(models.TransientModel):
_name = 'node.folio.wizard'
@api.model
def _default_node_id(self):
return self._context.get('node_id') or None
@api.model
def _default_folio_id(self):
return self._context.get('folio_id') or None
node_id = fields.Many2one('project.project', 'Hotel', required=True, default=_default_node_id)
folio_id = fields.Integer(required=True, default=_default_folio_id)
folio_name = fields.Char('Folio Number', readonly=True)
partner_id = fields.Many2one('res.partner', string="Customer", required=True)
internal_comment = fields.Text(string='Internal Folio Notes')
# For being used directly in the Folio views
email = fields.Char('E-mail', related='partner_id.email')
room_lines_wizard_ids = fields.One2many('node.reservation.wizard', 'node_folio_wizard_id')
price_total = fields.Float(string='Total Price')
@api.onchange('node_id')
def _onchange_node_id(self):
self.ensure_one()
_logger.info('_onchange_node_id(self): %s', self)
noderpc = odoorpc.ODOO(self.node_id.odoo_host, self.node_id.odoo_protocol, self.node_id.odoo_port)
noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password)
folio = noderpc.env['hotel.folio'].browse(self.folio_id)
self.folio_name = folio.name
self.partner_id = self.env['res.partner'].search([('email', '=', folio.partner_id.email)])
self.internal_comment = folio.internal_comment
self.price_total = folio.amount_total
cmds = []
for reservation in folio.room_lines:
cmds.append((0, False, {
'node_folio_wizard_id': self.id,
'room_type_id': self.env['hotel.node.room.type'].search([
('node_id', '=', self.node_id.id),
('remote_room_type_id', '=', reservation.room_type_id.id),
]).id,
'adults': reservation.adults,
'children': reservation.children,
'checkin': reservation.checkin,
'checkout': reservation.checkout,
'nights': reservation.nights,
'state': reservation.state,
'price_total': reservation.price_total,
}))
self.room_lines_wizard_ids = cmds
@api.multi
def update_node_reservation(self):
self.ensure_one()
try:
raise UserError(_("Function under development."))
except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err:
raise ValidationError(err)
class NodeReservationWizard(models.TransientModel):
_name = 'node.reservation.wizard'
node_folio_wizard_id = fields.Many2one('node.folio.wizard')
room_type_id = fields.Many2one('hotel.node.room.type', 'Rooms Type')
room_type_name = fields.Char('Name', related='room_type_id.name')
checkin = fields.Date('Check In', required=True)
checkout = fields.Date('Check Out', required=True)
nights = fields.Integer('Nights', compute="_compute_nights", readonly=True)
adults = fields.Integer('Adults', size=64, default=1)
children = fields.Integer('Children', size=64)
state = fields.Selection([('draft', 'Pre-reservation'), ('confirm', 'Pending Entry'),
('booking', 'On Board'), ('done', 'Out'),
('cancelled', 'Cancelled')], 'State')
price_total = fields.Float(string='Total Price', readonly=True)
@api.depends('checkin', 'checkout')
def _compute_nights(self):
for rec in self:
rec.nights = (fields.Date.from_string(rec.checkout) - fields.Date.from_string(rec.checkin)).days

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<record id="hotel_node_reservation_wizard_view_form" model="ir.ui.view"> <record id="hotel_node_reservation_wizard_view_form" model="ir.ui.view">
<field name="name">hotel.node.reservation.wizard</field> <field name="name">hotel.node.reservation.wizard</field>
<field name="model">hotel.node.reservation.wizard</field> <field name="model">hotel.node.reservation.wizard</field>
@@ -14,26 +13,27 @@
</h1> </h1>
</div> </div>
<group attrs="{'invisible':[('node_id','=',False)]}"> <group attrs="{'invisible':[('node_id','=',False)]}">
<group> <group name="dates">
<field name="checkin" required="1" widget="date" /> <field name="checkin" required="1" widget="date" />
<field name="checkout" required="1" widget="date" /> <field name="checkout" required="1" widget="date" />
</group> </group>
<group> <group>
<field name="partner_id"/> <field name="partner_id"/>
</group> </group>
<group colspan="2"> <group name="room_type_wizard_ids" colspan="2">
<field name="room_type_wizard_ids" nolabel="1"> <field name="room_type_wizard_ids" nolabel="1">
<tree editable="bottom" create="false" delete="false" <tree editable="bottom" create="false" delete="false"
decoration-muted="room_type_availability == 0"> decoration-muted="room_type_availability == 0">
<field name="node_reservation_wizard_id" invisible="1"/>
<field name="room_type_id" string="Room Type" readonly="1" force_save="1"/> <field name="room_type_id" string="Room Type" readonly="1" force_save="1"/>
<field name="room_type_availability" readonly="1" force_save="1"/> <field name="room_type_availability" readonly="1"/>
<field name="room_qty"/> <field name="room_qty"/>
<field name="checkin" widget="date" /> <field name="room_type_line_ids" invisible="1"/>
<field name="checkout" widget="date" /> <field name="checkin" widget="date"/>
<field name="checkout" widget="date"/>
<field name="nights"/> <field name="nights"/>
<field name="min_stay" /> <field name="min_stay" readonly="1"/>
<field name="price_unit" widget="monetary" /> <field name="price_unit" widget="monetary" readonly="1" force_save="1"/>
<!--<field name="currency_id" invisible="1"/>-->
<field name="discount"/> <field name="discount"/>
<field name="price_total" widget="monetary" readonly="1" force_save="1"/> <field name="price_total" widget="monetary" readonly="1" force_save="1"/>
</tree> </tree>
@@ -43,10 +43,6 @@
<field name="price_total" widget="monetary" readonly="1" force_save="1"/> <field name="price_total" widget="monetary" readonly="1" force_save="1"/>
</group> </group>
</group> </group>
<!--<field name="confirm" invisible="1"/>-->
<!--<group colspan="2" class="oe_subtotal_footer">-->
<!--<field name="total"/>-->
<!--</group>-->
<footer attrs="{'invisible':[('node_id','=',False)]}"> <footer attrs="{'invisible':[('node_id','=',False)]}">
<button name="create_node_reservation" string="Create Reservations" type="object" <button name="create_node_reservation" string="Create Reservations" type="object"
class="oe_highlight" /> class="oe_highlight" />
@@ -57,4 +53,91 @@
</form> </form>
</field> </field>
</record> </record>
<record id="hotel_node_reservation_wizard_view_search_form" model="ir.ui.view">
<field name="name">hotel.node.reservation.wizard.search</field>
<field name="model">node.search.wizard</field>
<field name="arch" type="xml">
<form string="Reservation Wizard Search" >
<sheet>
<div class="oe_title">
<h1>
<field name="node_id" placeholder="Hotel" required="1" force_save="1"
attrs="{'readonly': [('node_id', '!=', False)]}"/>
</h1>
</div>
<group attrs="{'invisible':[('node_id','=',False)]}">
<group>
<field name="folio"/>
<field name="checkin"/>
</group>
<group>
<field name="partner_id"/>
</group>
</group>
<footer attrs="{'invisible':[('node_id','=',False)]}">
<button name="search_node_reservation" string="Search Reservations" type="object"
class="oe_highlight" />
<button name="cancel" string="Cancel" special="cancel"
class="oe_link" />
</footer>
</sheet>
</form>
</field>
</record>
<record id="hotel_node_reservation_wizard_view_edit_form" model="ir.ui.view">
<field name="name">hotel.node.reservation.wizard.edit</field>
<field name="model">node.folio.wizard</field>
<field name="inherit_id" ref="hotel_node_reservation_wizard_view_form" />
<field name="arch" type="xml">
<xpath expr="//group[@name='dates']" position="replace">
<field name="folio_id" invisible="1"/>
<field name="folio_name"/>
</xpath>
<field name="partner_id" position="after">
<field name="email"/>
<field name="internal_comment"/>
</field>
<xpath expr="//group[@name='room_type_wizard_ids']" position="replace">
<group name="room_lines_wizard_ids" colspan="2">
<field name="room_lines_wizard_ids" nolabel="1">
<tree editable="bottom" create="false" delete="false">
<field name="state"/>
<field name="room_type_id" string="Room Type" readonly="1" force_save="1"/>
<field name="adults"/>
<field name="children"/>
<field name="checkin" widget="date"/>
<field name="checkout" widget="date"/>
<field name="nights"/>
<field name="price_total" widget="monetary"/>
</tree>
</field>
</group>
</xpath>
<xpath expr="//button[@name='create_node_reservation']" position="replace">
<button name="update_node_reservation" string="Update Reservations" type="object"
class="oe_highlight"/>
</xpath>
</field>
</record>
<record id="hotel_node_reservation_wizard_action" model="ir.actions.act_window">
<field name="name">Hotel Reservation Wizard</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">hotel.node.reservation.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<record id="hotel_node_reservation_wizard_action_search" model="ir.actions.act_window">
<field name="name">Hotel Reservation Wizard Search</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">node.search.wizard</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
</odoo> </odoo>