[DONE] 14.0 split join swap reservations (#85)

* [REF] pms: change name to reservation wizard

* [IMP] pms: operations over reservations (split,join,swap)

* [IMP]pms, swap reservation: order reservation by rooms and readonly compute fields

* [IMP] wizard split reservation allowed_room_ids domain

* [IMP] pms: refactor swap operations and tests cases

* [FIX] pms: fix tests and & behaviour

* [FIX] rebase code review compatibility

Co-authored-by: Darío Lodeiros <dario@commitsun.com>
This commit is contained in:
Miguel Padin
2021-04-28 09:58:23 +02:00
committed by GitHub
parent e7c0c3e5bd
commit a46b68e8a5
12 changed files with 1478 additions and 61 deletions

View File

@@ -71,7 +71,7 @@
"views/account_journal_views.xml",
"views/folio_portal_templates.xml",
"views/reservation_portal_templates.xml",
"wizards/wizard_reservation.xml",
"wizards/wizard_split_join_swap_reservation.xml",
"wizards/wizard_massive_changes.xml",
"wizards/wizard_advanced_filters.xml",
],

View File

@@ -353,6 +353,7 @@ class PmsReservation(models.Model):
string="Room/s",
help="Rooms that are reserved",
compute="_compute_rooms",
store=True,
tracking=True,
)
credit_card_details = fields.Text(
@@ -1324,7 +1325,7 @@ class PmsReservation(models.Model):
"view_type": "form",
"view_mode": "form",
"name": "Unify the reservation",
"res_model": "pms.reservation.wizard",
"res_model": "pms.reservation.split.join.swap.wizard",
"target": "new",
"type": "ir.actions.act_window",
"context": {

View File

@@ -163,9 +163,13 @@ class PmsReservationLine(models.Model):
key=lambda r: (r.reservation_id, r.date)
):
reservation = line.reservation_id
if reservation.preferred_room_id != line.room_id or not line.room_id:
# If reservation has a preferred_room_id We can allow
# select room_id regardless room_type_id selected on reservation
if (
reservation.preferred_room_id
and reservation.preferred_room_id != line.room_id
) or (
(reservation.preferred_room_id or reservation.room_type_id)
and not line.room_id
):
free_room_select = True if reservation.preferred_room_id else False
# we get the rooms available for the entire stay
rooms_available = self.env["pms.availability.plan"].rooms_available(

View File

@@ -49,7 +49,8 @@ manager_access_property,manager_access_property,model_pms_property,pms.group_pms
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1
manager_access_availability,manager_access_availability,model_pms_availability_plan,pms.group_pms_manager,1,1,1,1
manager_access_pms_sale_channel,manager_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_manager,1,1,1,1
user_access_pms_reservation_wizard,user_access_pms_reservation_wizard,model_pms_reservation_wizard,pms.group_pms_user,1,1,1,1
user_access_pms_reservation_split_join_swap_wizard,user_access_pms_reservation_split_join_swap_wizard,model_pms_reservation_split_join_swap_wizard,pms.group_pms_user,1,1,1,1
user_access_pms_wizard_reservation_lines_split,user_access_pms_wizard_reservation_lines_split,model_pms_wizard_reservation_lines_split,pms.group_pms_user,1,1,1,1
user_access_pms_massive_changes_wizard,user_access_pms_massive_changes_wizard,model_pms_massive_changes_wizard,pms.group_pms_user,1,1,1,1
user_access_pms_advanced_filters_wizard,user_access_pms_advanced_filters_wizard,model_pms_advanced_filters_wizard,pms.group_pms_user,1,1,1,1
user_access_pms_folio_wizard,user_access_pms_folio_wizard,model_pms_folio_wizard,pms.group_pms_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
49 manager_access_pms_cancelation_rule manager_access_pms_cancelation_rule model_pms_cancelation_rule pms.group_pms_manager 1 1 1 1
50 manager_access_availability manager_access_availability model_pms_availability_plan pms.group_pms_manager 1 1 1 1
51 manager_access_pms_sale_channel manager_access_pms_sale_channel model_pms_sale_channel pms.group_pms_manager 1 1 1 1
52 user_access_pms_reservation_wizard user_access_pms_reservation_split_join_swap_wizard user_access_pms_reservation_wizard user_access_pms_reservation_split_join_swap_wizard model_pms_reservation_wizard model_pms_reservation_split_join_swap_wizard pms.group_pms_user 1 1 1 1
53 user_access_pms_wizard_reservation_lines_split user_access_pms_wizard_reservation_lines_split model_pms_wizard_reservation_lines_split pms.group_pms_user 1 1 1 1
54 user_access_pms_massive_changes_wizard user_access_pms_massive_changes_wizard model_pms_massive_changes_wizard pms.group_pms_user 1 1 1 1
55 user_access_pms_advanced_filters_wizard user_access_pms_advanced_filters_wizard model_pms_advanced_filters_wizard pms.group_pms_user 1 1 1 1
56 user_access_pms_folio_wizard user_access_pms_folio_wizard model_pms_folio_wizard pms.group_pms_user 1 1 1 1

View File

@@ -39,3 +39,4 @@ from . import test_pms_board_service_room_type
from . import test_pms_board_service_room_type_line
from . import test_pms_folio_invoice
from . import test_pms_folio_sale_line
from . import test_pms_wizard_split_join_swap_reservation

View File

@@ -0,0 +1,967 @@
import datetime
from odoo.exceptions import UserError
from odoo.tests import common
class TestPmsWizardMassiveChanges(common.SavepointCase):
def create_common_scenario(self):
# product.pricelist
self.test_pricelist = self.env["product.pricelist"].create(
{
"name": "test pricelist 1",
}
)
# pms.availability.plan
self.test_availability_plan = self.env["pms.availability.plan"].create(
{
"name": "Availability plan for TEST",
"pms_pricelist_ids": [(6, 0, [self.test_pricelist.id])],
}
)
# sequences
self.folio_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Folio",
"code": "pms.folio",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.reservation_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Reservation",
"code": "pms.reservation",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
self.checkin_sequence = self.env["ir.sequence"].create(
{
"name": "PMS Checkin",
"code": "pms.checkin.partner",
"padding": 4,
"company_id": self.env.ref("base.main_company").id,
}
)
# pms.property
self.test_property = self.env["pms.property"].create(
{
"name": "MY PMS TEST",
"company_id": self.env.ref("base.main_company").id,
"default_pricelist_id": self.test_pricelist.id,
"folio_sequence_id": self.folio_sequence.id,
"reservation_sequence_id": self.reservation_sequence.id,
"checkin_sequence_id": self.checkin_sequence.id,
}
)
# pms.room.type.class
self.test_room_type_class = self.env["pms.room.type.class"].create(
{"name": "Room", "default_code": "ROOM"}
)
# pms.room.type
self.test_room_type_single = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.test_property.id],
"name": "Single Test",
"default_code": "SNG_Test",
"class_id": self.test_room_type_class.id,
}
)
# pms.room.type
self.test_room_type_double = self.env["pms.room.type"].create(
{
"pms_property_ids": [self.test_property.id],
"name": "Double Test",
"default_code": "DBL_Test",
"class_id": self.test_room_type_class.id,
}
)
# create rooms
self.room1 = self.env["pms.room"].create(
{
"pms_property_id": self.test_property.id,
"name": "Double 101",
"room_type_id": self.test_room_type_double.id,
"capacity": 2,
}
)
self.room2 = self.env["pms.room"].create(
{
"pms_property_id": self.test_property.id,
"name": "Double 102",
"room_type_id": self.test_room_type_double.id,
"capacity": 2,
}
)
# self.room3 = self.env["pms.room"].create(
# {
# "pms_property_id": self.test_property.id,
# "name": "Double 103",
# "room_type_id": self.test_room_type_double.id,
# "capacity": 2,
# }
# )
# UNIFY TESTS # review
def test_unify_reservation_avail_should(self):
# TEST CASE
# Unify reservation in one room with avail for that room
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | | r1 | | | |
# | Double 102 | | r1 | | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r1.reservation_line_ids[0].room_id = self.room2
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservation_join(
r1, self.room2
)
# ASSERT
self.assertEqual(
r1.reservation_line_ids.mapped("room_id"),
self.room2,
"The unify operation should assign the indicated room to all nights",
)
def test_unify_reservation_avail_not(self):
# TEST CASE
# Unify reservation in one room and
# there's not availability for that room
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | r2 | | | |
# | Double 102 | r0 | r0 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=2),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now() + datetime.timedelta(days=2),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"room_type_id": self.test_room_type_double.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2.flush()
# ACT & ASSERT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_join(
r1, self.room1
)
def test_unify_reservation_avail_not_room_exist(self):
# TEST CASE
# Unify reservation in one room and
# the room indicated doesn't exist: pms.room()
# ARRANGE
self.create_common_scenario()
self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2.flush()
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_join(
r2, self.env["pms.room"]
)
# SWAP TESTS
def test_swap_reservation_rooms_01(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | r1 | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | r2 | r2 | | | |
# | Double 102 | r1 | r1 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room1.id,
self.room2.id,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids.room_id == self.room1
)
def test_swap_reservation_rooms_02(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | r1 | r1 | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | r2 | r2 | | | |
# | Double 102 | r2 | r1 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now() + datetime.timedelta(days=1),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room1.id,
self.room2.id,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids[1:].room_id == self.room1
)
def test_swap_reservation_rooms_03(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | r1 | r1 | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | r2 | r2 | | | |
# | Double 102 | | r1 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now() + datetime.timedelta(days=1),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room2.id,
self.room1.id,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids.room_id == self.room1
)
def test_swap_reservation_rooms_04(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | r2 | | | | |
# | Double 102 | r1 | r1 | r2 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=2),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room1.id,
self.room2.id,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids[:1].room_id == self.room1
)
def test_swap_reservation_rooms_05(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | r2 | r2 | | | |
# | Double 102 | r1 | r1 | | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=2),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room2.id,
self.room1.id,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids.room_id == self.room1
)
def test_swap_reservation_rooms_06(self):
# TEST CASE
# Swap room1 with room2 should raise an error
# because room1 has no reservation between
# checkin & checkout provided
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | | | | | |
# | Double 102 | r1 | r1 | r1 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | r1 | | | |
# | Double 102 | | | | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room2.id,
self.room1.id,
)
# ASSERT
self.assertTrue(r1.reservation_line_ids.room_id == self.room1)
def test_swap_reservation_rooms_gap_01(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r0 | | r1 | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | | r2 | | | |
# | Double 102 | r0 | r2 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r0 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now() + datetime.timedelta(days=2),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room1.id,
self.room2.id,
)
# ASSERT
self.assertTrue(
r0.reservation_line_ids.room_id == self.room2
and r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids[0].room_id == self.room1
and r2.reservation_line_ids[2].room_id == self.room1
and r2.reservation_line_ids[1].room_id == self.room2
)
def test_swap_reservation_rooms_gap_02(self):
# TEST CASE
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r0 | | r1 | | | |
# | Double 102 | r2 | r2 | r2 | | | |
# +------------+------+------+------+----+----+----+
# State after swap
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r2 | r2 | r2 | | | |
# | Double 102 | r0 | | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r0 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=1),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now() + datetime.timedelta(days=2),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r2 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
r2.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room2.id,
self.room1.id,
)
# ASSERT
self.assertTrue(
r0.reservation_line_ids.room_id == self.room2
and r1.reservation_line_ids.room_id == self.room2
and r2.reservation_line_ids.room_id == self.room1
)
# NOT VALID TEST CASES
def test_swap_reservation_not_valid_01(self):
# TEST CASE
# Swap room1 with room2 should raise an error
# because room1 has no reservation between
# checkin & checkout provided
# Initial state
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | | | | | |
# | Double 102 | r1 | r1 | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
# ASSERT & ACT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservations_swap(
datetime.datetime.now(),
datetime.datetime.now() + datetime.timedelta(days=3),
self.room1.id,
self.room2.id,
)
# SPLIT TESTS
def test_split_reservation_check_room_splitted_valid_01(self):
# TEST CASE
# A reservation is created with preferred room
# The room for 1st night is switched to another room
# Expected result:
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | | r1 | r1 | | | |
# | Double 102 | r1 | | | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1, datetime.date.today(), self.room2
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids[0].room_id == self.room2
and r1.reservation_line_ids[1:].room_id == self.room1
)
def test_split_reservation_check_room_splitted_valid_02(self):
# TEST CASE
# A reservation is created with preferred room
# The room for 1st night is switched to another room
# Expected result:
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | r1 | | | | |
# | Double 102 | | | r1 | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1,
(
datetime.datetime(
year=datetime.date.today().year,
month=datetime.date.today().month,
day=datetime.date.today().day,
)
+ datetime.timedelta(days=2)
).date(),
self.room2,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids[2].room_id == self.room2
and r1.reservation_line_ids[:1].room_id == self.room1
)
def test_split_reservation_check_room_splitted_valid_03(self):
# TEST CASE
# A reservation is created with preferred room
# The room for 1st night is switched to another room
# Expected result:
# +------------+------+------+------+----+----+----+
# | room/date | 01 | 02 | 03 | 04 | 05 | 06 |
# +------------+------+------+------+----+----+----+
# | Double 101 | r1 | | r1 | | | |
# | Double 102 | | r1 | | | | |
# +------------+------+------+------+----+----+----+
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1,
(
datetime.datetime(
year=datetime.date.today().year,
month=datetime.date.today().month,
day=datetime.date.today().day,
)
+ datetime.timedelta(days=1)
).date(),
self.room2,
)
# ASSERT
self.assertTrue(
r1.reservation_line_ids[1].room_id == self.room2
and r1.reservation_line_ids[0].room_id == self.room1
and r1.reservation_line_ids[2].room_id == self.room1
)
def test_split_reservation_check_room_splitted_not_valid_01(self):
# TEST CASE
# Try to split the reservation for one night
# and set with a non valid room
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
room_not_exist = self.room3 = self.env["pms.room"].create(
{
"pms_property_id": self.test_property.id,
"name": "Double 103",
"room_type_id": self.test_room_type_double.id,
"capacity": 2,
}
)
room_not_exist.unlink()
# ACT & ASSERT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1, datetime.datetime.now(), room_not_exist
)
def test_split_reservation_check_room_splitted_not_valid_02(self):
# TEST CASE
# Try to split the reservation for one night
# and that night doesn't belong to reservation
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT & ASSERT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1, datetime.datetime.now() + datetime.timedelta(days=100), self.room2
)
def test_split_reservation_check_room_splitted_not_valid_03(self):
# TEST CASE
# Try to split the reservation for one night
# and the reservation not exists
# ARRANGE
self.create_common_scenario()
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT & ASSERT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
self.env["pms.reservation"], datetime.datetime.now(), self.room2
)
def test_split_reservation_check_room_splitted_not_valid_04(self):
# TEST CASE
# Try to split the reservation to one room
# and the room is not available
# ARRANGE
self.create_common_scenario()
self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room2.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1 = self.env["pms.reservation"].create(
{
"pms_property_id": self.test_property.id,
"checkin": datetime.datetime.now(),
"checkout": datetime.datetime.now() + datetime.timedelta(days=3),
"adults": 2,
"preferred_room_id": self.room1.id,
"partner_id": self.env.ref("base.res_partner_12").id,
}
)
r1.flush()
# ACT & ASSERT
with self.assertRaises(UserError):
self.env["pms.reservation.split.join.swap.wizard"].reservation_split(
r1, datetime.datetime.now(), self.room2
)

View File

@@ -77,15 +77,18 @@
style="margin-bottom:0px;"
attrs="{'invisible': [('splitted','=',False)]}"
>
This reservation is part of splitted reservation, you can try to
unify the reservation here
This reservation is part of a splitted reservation, you can try to
join the reservation here
<bold>
<button
class="alert-link"
type="object"
name="open_reservation_wizard"
string="Unify the reservation"
string="Join the reservation"
context="{'default_operation': 'join'}"
/>
</bold>
</div>
<sheet>
@@ -949,4 +952,15 @@
sequence="5"
parent="pms.menu_reservations"
/>
<record id="action_view_reservation_operations" model="ir.actions.act_window">
<field name="name">Reservation operations</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">pms.reservation.split.join.swap.wizard</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="binding_model_id" ref="pms.model_pms_reservation" />
<field name="binding_view_types">form</field>
</record>
</odoo>

View File

@@ -1,4 +1,4 @@
from . import wizard_reservation
from . import wizard_split_join_swap_reservation
from . import wizard_massive_changes
from . import wizard_advanced_filters
from . import wizard_folio

View File

@@ -1,20 +0,0 @@
from odoo import fields, models
class ReservationWizard(models.TransientModel):
_name = "pms.reservation.wizard"
allowed_rooms = fields.One2many("pms.room", compute="_compute_allowed_rooms")
options = fields.Many2one("pms.room", string="Room")
def _compute_allowed_rooms(self):
for record in self:
record.allowed_rooms = self._context.get("rooms_available")
def unify(self):
if self.options:
for line in (
self.env["pms.reservation"]
.search([("id", "=", self._context.get("active_id"))])
.reservation_line_ids
):
line.room_id = self.options

View File

@@ -1,31 +0,0 @@
<?xml version="1.0" ?>
<odoo>
<record id="reservation_wizard" model="ir.ui.view">
<field name="name">Reservation Wizard</field>
<field name="model">pms.reservation.wizard</field>
<field name="arch" type="xml">
<form string="Choose The Details">
<group>
<field
name="options"
string="Suggested rooms to unify the reservation:"
options="{'no_create': True,'no_open': True}"
required="1"
domain="[('id', 'in', allowed_rooms)]"
/>
<field name="allowed_rooms" invisible="1" />
</group>
<footer>
<button
name="unify"
string="Unify"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,353 @@
import datetime
from odoo import _, api, fields, models
from odoo.exceptions import UserError
class ReservationSplitJoinSwapWizard(models.TransientModel):
_name = "pms.reservation.split.join.swap.wizard"
operation = fields.Selection(
[
("swap", "Swap rooms"),
("split", "Split reservation"),
("join", "Join reservation"),
],
string="Operation",
help="Operation to be applied on the reservation",
default=lambda self: self._context.get("default_operation")
if self._context.get("default_operation")
else "swap",
)
reservation_id = fields.Many2one(
string="Reservation",
comodel_name="pms.reservation",
default=lambda self: self.env["pms.reservation"]
.browse(self._context.get("active_id"))
.id
if self._context.get("active_id")
else False,
)
checkin = fields.Date(
string="Check In",
default=lambda self: self.env["pms.reservation"]
.browse(self._context.get("active_id"))
.checkin
if self._context.get("active_id")
else False,
)
checkout = fields.Date(
string="Check Out",
default=lambda self: self.env["pms.reservation"]
.browse(self._context.get("active_id"))
.checkout
if self._context.get("active_id")
else False,
)
reservations = fields.Many2many(
string="Reservations",
comodel_name="pms.reservation",
compute="_compute_reservations",
readonly=False,
store=True,
)
room_source = fields.Many2one(
string="Room Source",
comodel_name="pms.room",
domain="[('id', 'in', allowed_rooms_sources)]",
default=lambda self: self.env["pms.reservation"]
.browse(self._context.get("active_id"))
.preferred_room_id
if self._context.get("active_id")
and not self.env["pms.reservation"]
.browse(self._context.get("active_id"))
.splitted
else False,
)
room_target = fields.Many2one(
string="Room Target",
comodel_name="pms.room",
domain="[('id', 'in', allowed_rooms_target)]",
)
allowed_rooms_sources = fields.Many2many(
string="Allowed rooms source",
comodel_name="pms.room",
relation="pms_wizard_split_join_swap_reservation_rooms_source",
column1="wizard_id",
column2="room_id",
compute="_compute_allowed_rooms_source",
store=True,
readonly=False,
)
allowed_rooms_target = fields.Many2many(
string="Allowed rooms target",
comodel_name="pms.room",
relation="pms_wizard_split_join_swap_reservation_rooms_target",
column1="wizard_id",
column2="room_id",
compute="_compute_allowed_rooms_target",
store=True,
readonly=False,
)
reservation_lines_to_change = fields.One2many(
comodel_name="pms.wizard.reservation.lines.split",
inverse_name="reservation_wizard_id",
compute="_compute_reservation_lines",
store=True,
readonly=False,
)
@api.depends("checkin", "checkout", "room_source", "room_target")
def _compute_reservations(self):
for record in self:
if record.checkin and record.checkout:
reservation_ids = list()
for date_iterator in [
record.checkin + datetime.timedelta(days=x)
for x in range(0, (record.checkout - record.checkin).days)
]:
domain_lines = []
if record.room_source and record.room_target:
domain_lines.extend(
[
"|",
("room_id", "=", record.room_source.id),
("room_id", "=", record.room_target.id),
]
)
domain_lines.append(("date", "=", date_iterator))
lines = self.env["pms.reservation.line"].search(domain_lines)
reservation_ids.extend(lines.mapped("reservation_id.id"))
reservations = (
self.env["pms.reservation"]
.search(
[
("id", "in", reservation_ids),
("rooms", "!=", False),
]
)
.sorted("rooms")
)
record.reservations = reservations
else:
record.reservations = False
@api.depends("reservation_id")
def _compute_reservation_lines(self):
for record in self:
if record.reservation_id:
cmds = [(5, 0, 0)]
for line in record.reservation_id.reservation_line_ids:
cmds.append(
(
0,
0,
{
"reservation_wizard_id": record.id,
"room_id": line.room_id,
"date": line.date,
},
)
)
record.reservation_lines_to_change = cmds
else:
record.reservation_lines_to_change = False
@api.depends("checkin", "checkout")
def _compute_allowed_rooms_source(self):
for record in self:
record.allowed_rooms_sources = (
record.reservations.reservation_line_ids.mapped("room_id")
)
@api.depends_context("default_operation")
@api.depends("checkin", "checkout", "room_source", "operation")
def _compute_allowed_rooms_target(self):
for record in self:
record.allowed_rooms_target = False
record.room_target = False
if record.checkin and record.checkout:
rooms_available = self.env["pms.availability.plan"].rooms_available(
checkin=record.checkin,
checkout=record.checkout,
room_type_id=False, # Allows to choose any available room
current_lines=record.reservation_id.reservation_line_ids.ids,
pricelist_id=record.reservation_id.pricelist_id.id,
pms_property_id=record.reservation_id.pms_property_id.id,
)
domain = [("capacity", ">=", record.reservation_id.adults)]
if record.room_source:
domain.append(("id", "!=", record.room_source.id))
if record.operation == "swap":
domain.append(
(
"id",
"in",
record.reservations.reservation_line_ids.mapped(
"room_id"
).ids,
)
)
else:
domain.extend(
[
("id", "in", rooms_available.ids),
]
)
rooms = self.env["pms.room"].search(domain)
record.allowed_rooms_target = rooms
@api.model
def reservation_split(self, reservation, date, room):
if not reservation:
raise UserError(_("Invalid reservation"))
if not reservation or not reservation.reservation_line_ids.filtered(
lambda x: x.date == date
):
raise UserError(_("Invalid date for reservation line "))
if not self.browse(room.id):
raise UserError(_("The room does not exist"))
rooms_available = self.env["pms.availability.plan"].rooms_available(
checkin=date,
checkout=(
datetime.datetime(year=date.year, month=date.month, day=date.day)
+ datetime.timedelta(days=1)
).date(),
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
pms_property_id=reservation.pms_property_id.id,
)
if room not in rooms_available:
raise UserError(_("The room is not available"))
reservation.reservation_line_ids.filtered(
lambda x: x.date == date
).room_id = room.id
@api.model
def reservation_join(self, reservation, room):
rooms_available = self.env["pms.availability.plan"].rooms_available(
checkin=reservation.checkin,
checkout=reservation.checkout,
current_lines=reservation.reservation_line_ids.ids,
pricelist_id=reservation.pricelist_id.id,
pms_property_id=reservation.pms_property_id.id,
)
if room in rooms_available:
for line in (
self.env["pms.reservation"]
.search([("id", "=", reservation.id)])
.reservation_line_ids
):
line.room_id = room.id
else:
raise UserError(_("Room {} not available.".format(room.name)))
@api.model
def reservations_swap(self, checkin, checkout, source, target):
reservations = self.env["pms.reservation"].search(
[("checkin", ">=", checkin), ("checkout", "<=", checkout)]
)
lines = self.env["pms.reservation.line"].search_count(
[("room_id", "=", source), ("reservation_id", "in", reservations.ids)]
)
if not lines:
raise UserError(_("There's no reservations lines with provided room"))
for date_iterator in [
checkin + datetime.timedelta(days=x)
for x in range(0, (checkout - checkin).days)
]:
line_room_source = self.env["pms.reservation.line"].search(
[("date", "=", date_iterator), ("room_id", "=", source)]
)
line_room_target = self.env["pms.reservation.line"].search(
[("date", "=", date_iterator), ("room_id", "=", target)]
)
if line_room_source and line_room_target:
# this causes an unique error constraint
line_room_target.occupies_availability = False
line_room_source.occupies_availability = False
line_room_target.room_id = source
line_room_source.room_id = target
self.flush()
line_room_target._compute_occupies_availability()
line_room_source._compute_occupies_availability()
else:
line_room_source.room_id = target
def action_split(self):
for record in self:
for line in record.reservation_lines_to_change:
self.reservation_split(
record.reservation_id,
line.date,
line.room_id,
)
def action_join(self):
for record in self:
self.reservation_join(record.reservation_id, record.room_target)
def action_swap(self):
self.reservations_swap(
self.checkin, self.checkout, self.room_source.id, self.room_target.id
)
class ReservationLinesToSplit(models.TransientModel):
_name = "pms.wizard.reservation.lines.split"
reservation_wizard_id = fields.Many2one(
comodel_name="pms.reservation.split.join.swap.wizard",
)
date = fields.Date(
string="Date",
)
room_id = fields.Many2one(
string="Room",
comodel_name="pms.room",
domain="[('id', 'in', allowed_room_ids)]",
)
allowed_room_ids = fields.Many2many(
string="Allowed Rooms",
help="It contains all available rooms for this line",
comodel_name="pms.room",
compute="_compute_allowed_room_ids",
store=True,
# readonly=False
)
@api.depends(
"date",
"room_id",
"reservation_wizard_id.reservation_id.pricelist_id",
)
def _compute_allowed_room_ids(self):
for line in self:
reservation = line.reservation_wizard_id.reservation_id
rooms_available = False
if line.date and reservation:
if reservation.overbooking or reservation.state in ("cancelled"):
line.allowed_room_ids = self.env["pms.room"].search(
[("active", "=", True)]
)
return
rooms_available = self.env["pms.availability.plan"].rooms_available(
checkin=line.date,
checkout=line.date + datetime.timedelta(days=1),
room_type_id=False, # Allows to choose any available room
pricelist_id=reservation.pricelist_id.id,
pms_property_id=reservation.pms_property_id.id,
)
rooms_available += line.room_id
line.allowed_room_ids = rooms_available
else:
line.allowed_room_ids = False

View File

@@ -0,0 +1,127 @@
<?xml version="1.0" ?>
<odoo>
<record id="reservation_wizard" model="ir.ui.view">
<field name="name">Split, join or swap reservations</field>
<field name="model">pms.reservation.split.join.swap.wizard</field>
<field name="arch" type="xml">
<form>
<div class="row">
<div class="col-3">
<label for="operation" class="font-weight-bold" />
<br />
<field name="operation" widget="radio" />
<field name="allowed_rooms_sources" invisible="1" />
<field name="allowed_rooms_target" invisible="1" />
</div>
<div class="col-5">
<span attrs="{'invisible': [('operation','=','swap')]}">
<label for="reservation_id" /> <br />
<field name="reservation_id" readonly="1" /><br />
</span>
<span attrs="{'invisible': [('operation','!=','swap')]}">
<div class="row">
<div class="col-6">
<label for="checkin" /> <br />
<field
name="checkin"
widget="daterange"
options="{'related_end_date': 'checkout'}"
/><br />
<label for="checkout" /> <br />
<field
name="checkout"
widget="daterange"
options="{'related_start_date': 'checkin'}"
/><br />
</div>
</div>
</span>
</div>
<div class="col-4">
<div>
<span attrs="{'invisible': [('operation','!=','swap')]}">
<label for="room_source" />
<br />
<field name="room_source" />
<br />
</span>
<label
attrs="{'invisible': [('operation','!=','swap')]}"
for="room_target"
/>
<label
attrs="{'invisible': [('operation','!=','join')]}"
for="room_target"
string="Room"
/>
<br />
<field
name="room_target"
attrs="{'invisible': [('operation','=','split')]}"
/>
</div>
</div>
<div
class="col-12"
attrs="{'invisible': [('operation','!=','swap')]}"
>
<field name="reservations" nolabel="1" readonly="1">
<tree>
<field string="Reservation" name="name" />
<field name="checkin" />
<field name="checkout" />
<field name="rooms" />
</tree>
</field>
</div>
<div
class="col-12"
attrs="{'invisible': [('operation','!=','split')]}"
>
<field
name="reservation_lines_to_change"
nolabel="1"
default_order="rooms"
>
<tree editable="bottom" create="false" delete="false">
<field name="reservation_wizard_id" invisible="1" />
<field name="allowed_room_ids" invisible="1" />
<field name="date" readonly="0" />
<field
name="room_id"
domain="[('id', 'in', allowed_room_ids)]"
/>
</tree>
</field>
</div>
</div>
<footer class="text-right">
<button string="Cancel" class="oe_link border" special="cancel" />
or
<button
name="action_join"
attrs="{'invisible': [('operation','!=','join')]}"
string="Join reservation"
type="object"
class="oe_highlight"
/>
<button
name="action_split"
attrs="{'invisible': [('operation','!=','split')]}"
string="Split reservation"
type="object"
class="oe_highlight"
/>
<button
name="action_swap"
attrs="{'invisible': [('operation','!=','swap')]}"
string="Swap reservation rooms"
type="object"
class="oe_highlight"
/>
</footer>
</form>
</field>
</record>
</odoo>