From fce2dbc8de5464c16b6f58749d1e17c11cf81c2d Mon Sep 17 00:00:00 2001 From: Jonas Linter <{email_address}> Date: Tue, 9 Dec 2025 15:29:35 +0100 Subject: [PATCH] Fixed incorrect overlap detection --- src/alpine_bits_python/free_rooms_action.py | 23 +- tests/test_data/ClosingSeasons.xml | 549 ++++++++++++++++++++ tests/test_free_rooms_action.py | 31 ++ 3 files changed, 595 insertions(+), 8 deletions(-) create mode 100644 tests/test_data/ClosingSeasons.xml diff --git a/src/alpine_bits_python/free_rooms_action.py b/src/alpine_bits_python/free_rooms_action.py index 645c411..8285773 100644 --- a/src/alpine_bits_python/free_rooms_action.py +++ b/src/alpine_bits_python/free_rooms_action.py @@ -300,15 +300,22 @@ class FreeRoomsAction(AlpineBitsAction): # Validate date range start_date, end_date = self._parse_date_range(sac.start, sac.end) + # Check if this inventory entry has any counts (available rooms) + # Entries without counts represent unavailable rooms + has_availability = inventory.inv_counts is not None and inventory.inv_counts.inv_count + # Check for overlap with closing seasons - for closing_start, closing_end in closing_season_ranges: - if self._date_ranges_overlap(start_date, end_date, closing_start, closing_end): - error_message = f"Inventory entry ({start_date} to {end_date}) overlaps with closing season ({closing_start} to {closing_end})" - _LOGGER.info(error_message) - raise FreeRoomsProcessingError( - error_message, - HttpStatusCode.BAD_REQUEST, - ) + # Only entries with availability (counts) cannot overlap with closing seasons + # Entries without counts (unavailable rooms) can overlap with closing seasons + if has_availability: + for closing_start, closing_end in closing_season_ranges: + if self._date_ranges_overlap(start_date, end_date, closing_start, closing_end): + error_message = f"Inventory entry ({start_date} to {end_date}) overlaps with closing season ({closing_start} to {closing_end})" + _LOGGER.info(error_message) + raise FreeRoomsProcessingError( + error_message, + HttpStatusCode.BAD_REQUEST, + ) # Check for overlap with other inventory entries for the same room/category inv_code = sac.inv_code.strip() if sac.inv_code else None diff --git a/tests/test_data/ClosingSeasons.xml b/tests/test_data/ClosingSeasons.xml new file mode 100644 index 0000000..c96303a --- /dev/null +++ b/tests/test_data/ClosingSeasons.xml @@ -0,0 +1,549 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/test_free_rooms_action.py b/tests/test_free_rooms_action.py index a1215b1..988827f 100644 --- a/tests/test_free_rooms_action.py +++ b/tests/test_free_rooms_action.py @@ -3,6 +3,7 @@ from __future__ import annotations from datetime import UTC, datetime +from pathlib import Path import pytest import pytest_asyncio @@ -231,6 +232,36 @@ async def test_closing_season_entries_marked_correctly(db_session: AsyncSession) assert len(closing_rows) == 2 assert all(row.bookable_type_2 is None for row in closing_rows) +@pytest.mark.asyncio +async def test_closing_seasons_test_file(db_session: AsyncSession): + await insert_test_hotel(db_session) + action = make_action() + + Path(__file__).parent / "test_data" / "ClosingSeasons.xml" + + xml = (Path(__file__).parent / "test_data" / "ClosingSeasons.xml").read_text() + + response = await action.handle( + "OTA_HotelInvCountNotif:FreeRooms", + xml, + Version.V2024_10, + make_client_info(), + db_session, + ) + assert response.status_code == HttpStatusCode.OK, f"Response was not OK {response.xml_content}" + + inventories = (await db_session.execute(select(HotelInventory))).scalars().all() + closing_inventory = next(inv for inv in inventories if inv.inv_type_code == "__CLOSE") + assert closing_inventory.inv_code is None + + rows = ( + await db_session.execute(select(RoomAvailability).order_by(RoomAvailability.date)) + ).scalars().all() + closing_rows = [row for row in rows if row.is_closing_season] + # Closing season from 2025-12-20 to 2025-12-23 = 4 days + assert len(closing_rows) == 4 + assert all(row.bookable_type_2 is None for row in closing_rows) + @pytest.mark.asyncio async def test_closing_season_not_allowed_in_delta(db_session: AsyncSession):