From 01535f71850e29213465357ec60ee638db14e4bc Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 25 Oct 2018 20:36:01 +0200 Subject: [PATCH 1/8] [WIP] Wizard Node Reservation Price unit is calculated in the node using hotel functions --- .../models/inherited_hotel_room_type.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hotel_node_helper/models/inherited_hotel_room_type.py b/hotel_node_helper/models/inherited_hotel_room_type.py index f0e9bd96b..41d2c2b62 100644 --- a/hotel_node_helper/models/inherited_hotel_room_type.py +++ b/hotel_node_helper/models/inherited_hotel_room_type.py @@ -39,3 +39,20 @@ class HotelRoomType(models.Model): availability_plan = min([r['avail'] for r in 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 + # price_unit = self.browse(room_type_id).list_price + 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'] + + price_unit = 0.0 + for x in range(1, len(reservation_line_ids)): + price_unit = price_unit + reservation_line_ids[x][2]['price'] + + return price_unit From 30e2ca99e04677f86c16ca95bfff29b63e8c1f87 Mon Sep 17 00:00:00 2001 From: Pablo Date: Thu, 25 Oct 2018 20:36:12 +0200 Subject: [PATCH 2/8] [WIP] Wizard Node Reservation --- .../wizards/wizard_hotel_node_reservation.py | 143 +++++++----------- 1 file changed, 57 insertions(+), 86 deletions(-) diff --git a/hotel_node_master/wizards/wizard_hotel_node_reservation.py b/hotel_node_master/wizards/wizard_hotel_node_reservation.py index 411365908..053f081aa 100644 --- a/hotel_node_master/wizards/wizard_hotel_node_reservation.py +++ b/hotel_node_master/wizards/wizard_hotel_node_reservation.py @@ -2,8 +2,6 @@ # Copyright 2018 Alexandre Díaz # Copyright 2018 Dario Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from builtins import list - import wdb import logging import urllib.error @@ -47,8 +45,8 @@ class HotelNodeReservationWizard(models.TransientModel): def _compute_price_total(self): _logger.info('_compute_price_total for wizard %s', self.id) price_total = 0.0 - for record in self.room_type_wizard_ids: - price_total += record.price_total + for rec in self.room_type_wizard_ids: + price_total += rec.price_total self.price_total = price_total @api.onchange('node_id') @@ -56,70 +54,34 @@ class HotelNodeReservationWizard(models.TransientModel): self.ensure_one() if self.node_id: _logger.info('_onchange_node_id(self): %s', self) - # Save your credentials (session) + # TODO Save your credentials (session) @api.onchange('checkin', 'checkout') def _onchange_dates(self): self.ensure_one() _logger.info('_onchange_dates(self): %s', self) + # TODO check hotel timezone self.checkin = self._get_default_checkin() if not self.checkin \ else fields.Date.from_string(self.checkin) self.checkout = self._get_default_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): self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime( DEFAULT_SERVER_DATE_FORMAT) - 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) + 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': (fields.Date.from_string(self.checkout) - fields.Date.from_string(self.checkin)).days, + # '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, + })) - # 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) + self.room_type_wizard_ids = cmds @api.multi def create_node_reservation(self): @@ -128,29 +90,29 @@ class HotelNodeReservationWizard(models.TransientModel): noderpc.login(self.node_id.odoo_db, self.node_id.odoo_user, self.node_id.odoo_password) # 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 = { 'partner_id': remote_partner_id, } # prepare hotel folio room_lines room_lines = [] - for room_type in self.room_type_wizard_ids: - for x in range(room_type.room_qty): + for rec in self.room_type_wizard_ids: + for x in range(rec.room_qty): vals_reservation_lines = { '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 reservation_line_ids = noderpc.env['hotel.reservation'].prepare_reservation_lines( - room_type.checkin, - (fields.Date.from_string(room_type.checkout) - fields.Date.from_string(room_type.checkin)).days, + rec.checkin, + (fields.Date.from_string(rec.checkout) - fields.Date.from_string(rec.checkin)).days, vals_reservation_lines - ) # [[5, 0, 0], ¿? + ) # [[5, 0, 0], ¿? room_lines.append((0, False, { - 'room_type_id': room_type.room_type_id.remote_room_type_id, - 'checkin': room_type.checkin, - 'checkout': room_type.checkout, + 'room_type_id': rec.room_type_id.remote_room_type_id, + 'checkin': rec.checkin, + 'checkout': rec.checkout, 'reservation_line_ids': reservation_line_ids['reservation_line_ids'], })) vals.update({'room_lines': room_lines}) @@ -176,52 +138,61 @@ class NodeRoomTypeWizard(models.TransientModel): 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', readonly=True) #, compute="_compute_room_type_availability") + room_type_availability = fields.Integer('Availability', compute="_compute_restrictions", readonly=True) room_qty = fields.Integer('Quantity', default=0) checkin = fields.Date('Check In', required=True) checkout = fields.Date('Check Out', required=True) nights = fields.Integer('Nights', readonly=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) + # price_unit indicates Room Price x Nights + price_unit = fields.Float(string='Room Price', compute="_compute_restrictions", readonly=True) discount = fields.Float(string='Discount (%)', default=0.0) price_total = fields.Float(string='Total Price', compute='_compute_price_total') - @api.depends('room_qty', 'price_unit', 'discount') + @api.depends('room_qty', 'price_unit', 'discount', 'nights') def _compute_price_total(self): - for room_type in self: - _logger.info('_compute_price_total for room type %s', room_type.room_type_id) - # 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) - # 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) - # Unidades x precio unidad (el precio de unidad ya incluye el conjunto de días) + for rec in self: + _logger.info('_compute_price_total for room type %s', rec.room_type_id) + rec.price_total = (rec.room_qty * rec.price_unit) * (1.0 - rec.discount * 0.01) @api.depends('checkin', 'checkout') def _compute_restrictions(self): - for room_type in self: - _logger.info('_compute_restrictions for room type %s', room_type.room_type_id) + for rec in self: + try: + # TODO Load your credentials (session) ... should be faster? + noderpc = odoorpc.ODOO(rec.node_id.odoo_host, rec.node_id.odoo_protocol, rec.node_id.odoo_port) + noderpc.login(rec.node_id.odoo_db, rec.node_id.odoo_user, rec.node_id.odoo_password) + + _logger.info('_compute_restrictions [availability] for room type %s', rec.room_type_id) + 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.info('_compute_restrictions [price_unit] for room type %s', rec.room_type_id) + rec.price_unit = noderpc.env['hotel.room.type'].get_room_type_price_unit( + rec.checkin, + rec.checkout, + rec.room_type_id.remote_room_type_id) + + _logger.info('_compute_restrictions [min days] for room type %s', rec.room_type_id) + + noderpc.logout() + except (odoorpc.error.RPCError, odoorpc.error.InternalError, urllib.error.URLError) as err: + raise ValidationError(err) @api.onchange('checkin', 'checkout') def _onchange_dates(self): _logger.info('_onchange_dates for room type %s', self.room_type_id) - # recompute price unit + self.checkin = self._default_checkin() \ if not self.checkin else fields.Date.from_string(self.checkin) self.checkout = self._default_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): self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime( 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) - # availability: search de hotel.room.type.availability filtrando por room_type y date y escogiendo el min avail en el rango - # preci_unit y json_days: usando prepare_reservation_lines - From b127892a37851c1f2fa25782950b249238266c17 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 26 Oct 2018 16:15:51 +0200 Subject: [PATCH 3/8] [WIP] Simplify Code --- .../models/inherited_hotel_room_type.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/hotel_node_helper/models/inherited_hotel_room_type.py b/hotel_node_helper/models/inherited_hotel_room_type.py index 41d2c2b62..b3a4c4213 100644 --- a/hotel_node_helper/models/inherited_hotel_room_type.py +++ b/hotel_node_helper/models/inherited_hotel_room_type.py @@ -32,11 +32,10 @@ class HotelRoomType(models.Model): ('date', '>=', dfrom), ('date', '<', dto), ('room_type_id', '=', room_type_id), + ], ['avail']) or [{'avail': availability_real}] - ], ['avail']) or float('inf') - - if isinstance(availability_plan, list): - 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) @@ -56,3 +55,15 @@ class HotelRoomType(models.Model): price_unit = price_unit + reservation_line_ids[x][2]['price'] return price_unit + + @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 From ca9caaae301644c12dc4a6d2ce31a434a4b170d9 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 26 Oct 2018 16:16:43 +0200 Subject: [PATCH 4/8] [WIP] Wizard Node Reservation Create reservation from central node --- .../wizards/wizard_hotel_node_reservation.py | 70 ++++++++++++++++--- .../wizards/wizard_hotel_node_reservation.xml | 8 +-- 2 files changed, 63 insertions(+), 15 deletions(-) diff --git a/hotel_node_master/wizards/wizard_hotel_node_reservation.py b/hotel_node_master/wizards/wizard_hotel_node_reservation.py index 053f081aa..9116d83e7 100644 --- a/hotel_node_master/wizards/wizard_hotel_node_reservation.py +++ b/hotel_node_master/wizards/wizard_hotel_node_reservation.py @@ -41,13 +41,26 @@ class HotelNodeReservationWizard(models.TransientModel): string="Room Types") price_total = fields.Float(string='Total Price', compute='_compute_price_total') + @api.constrains('room_type_wizard_ids') + 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') def _compute_price_total(self): _logger.info('_compute_price_total for wizard %s', self.id) - price_total = 0.0 + self.price_total = 0.0 for rec in self.room_type_wizard_ids: - price_total += rec.price_total - self.price_total = price_total + self.price_total += rec.price_total @api.onchange('node_id') def _onchange_node_id(self): @@ -85,6 +98,7 @@ class HotelNodeReservationWizard(models.TransientModel): @api.multi def create_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) @@ -143,18 +157,38 @@ class NodeRoomTypeWizard(models.TransientModel): checkin = fields.Date('Check In', required=True) checkout = fields.Date('Check Out', required=True) - nights = fields.Integer('Nights', readonly=True) + nights = fields.Integer('Nights', compute="_compute_nights", readonly=True) min_stay = fields.Integer('Min. Days', compute="_compute_restrictions", readonly=True) # price_unit indicates Room Price x Nights - price_unit = fields.Float(string='Room Price', compute="_compute_restrictions", readonly=True) + price_unit = fields.Float(string='Room Price', compute="_compute_restrictions", readonly=True, store=True) 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.depends('room_qty', 'price_unit', 'discount', 'nights') + @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_name, rec.checkin, rec.checkout) + _logger.warning(msg) + raise ValidationError(msg) + total_qty += rec.room_qty + + @api.depends('room_qty', 'price_unit', 'discount') def _compute_price_total(self): for rec in self: _logger.info('_compute_price_total for room type %s', rec.room_type_id) rec.price_total = (rec.room_qty * rec.price_unit) * (1.0 - rec.discount * 0.01) + # TODO rec.price unit trigger _compute_restriction ¿? store = 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 @api.depends('checkin', 'checkout') def _compute_restrictions(self): @@ -164,24 +198,39 @@ class NodeRoomTypeWizard(models.TransientModel): noderpc = odoorpc.ODOO(rec.node_id.odoo_host, rec.node_id.odoo_protocol, rec.node_id.odoo_port) noderpc.login(rec.node_id.odoo_db, rec.node_id.odoo_user, rec.node_id.odoo_password) - _logger.info('_compute_restrictions [availability] for room type %s', rec.room_type_id) + _logger.warning('_compute_restrictions [availability] for room type %s', rec.room_type_id) 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.info('_compute_restrictions [price_unit] for room type %s', rec.room_type_id) + _logger.warning('_compute_restrictions [price_unit] for room type %s', rec.room_type_id) rec.price_unit = noderpc.env['hotel.room.type'].get_room_type_price_unit( rec.checkin, rec.checkout, rec.room_type_id.remote_room_type_id) - _logger.info('_compute_restrictions [min days] for room type %s', rec.room_type_id) + _logger.warning('_compute_restrictions [min days] for room type %s', 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) 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_name, self.checkin, self.checkout) + return { + 'warning': { + 'title': 'Warning: Invalid room quantity', + 'message': msg, + } + } + @api.onchange('checkin', 'checkout') def _onchange_dates(self): _logger.info('_onchange_dates for room type %s', self.room_type_id) @@ -195,4 +244,3 @@ class NodeRoomTypeWizard(models.TransientModel): self.checkout = (fields.Date.from_string(self.checkin) + timedelta(days=1)).strftime( DEFAULT_SERVER_DATE_FORMAT) - self.nights = (fields.Date.from_string(self.checkout) - fields.Date.from_string(self.checkin)).days diff --git a/hotel_node_master/wizards/wizard_hotel_node_reservation.xml b/hotel_node_master/wizards/wizard_hotel_node_reservation.xml index 0a34e5c1d..82f73ae83 100644 --- a/hotel_node_master/wizards/wizard_hotel_node_reservation.xml +++ b/hotel_node_master/wizards/wizard_hotel_node_reservation.xml @@ -28,14 +28,14 @@ - - + + - + - + From db1cc94147009a255873e345ce6cd8f04fcda994 Mon Sep 17 00:00:00 2001 From: Pablo Date: Fri, 26 Oct 2018 20:35:57 +0200 Subject: [PATCH 5/8] [WIP] Rearrange menu items and actions --- hotel_node_master/views/hotel_node.xml | 82 ++----------------- hotel_node_master/views/hotel_node_group.xml | 15 ++++ .../views/hotel_node_room_type.xml | 15 ++++ hotel_node_master/views/hotel_node_user.xml | 15 ++++ .../wizards/wizard_hotel_node_reservation.xml | 29 +++++-- 5 files changed, 75 insertions(+), 81 deletions(-) diff --git a/hotel_node_master/views/hotel_node.xml b/hotel_node_master/views/hotel_node.xml index f30470568..bbc065ca1 100644 --- a/hotel_node_master/views/hotel_node.xml +++ b/hotel_node_master/views/hotel_node.xml @@ -8,7 +8,7 @@
- +
- + @@ -24,17 +24,18 @@ + - + + - - - + + - + @@ -54,15 +55,70 @@ - hotel.node.reservation.wizard.search - hotel.node.reservation.wizard + hotel.node.reservation.wizard.search + node.search.wizard + +
+ +
+

+ +

+
+ + + + + + + + + +
+
+
+
+
+
+ + + hotel.node.reservation.wizard.edit + node.folio.wizard + + + + + + + + - - + + + + + + + + + + + + + + +