From 189fdb33de9c27810b4c0330f3d37638c7a9c25f Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Sun, 8 Feb 2026 18:01:36 +0530 Subject: [PATCH] Sync active room stays when booking expected dates change --- docs/API_REFERENCE.txt | 7 ++++- .../controller/booking/BookingFlow.kt | 28 +++++++++++++++++++ .../controller/room/RoomStays.kt | 2 +- .../trisolarisserver/models/room/RoomStay.kt | 3 ++ .../repo/room/RoomStayRepo.kt | 2 +- 5 files changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/API_REFERENCE.txt b/docs/API_REFERENCE.txt index 6b726f0..83d8a7b 100644 --- a/docs/API_REFERENCE.txt +++ b/docs/API_REFERENCE.txt @@ -1361,6 +1361,7 @@ ROOM STAYS + CARDS APIS What it does: - Returns active stays with booking and guest details. + - expectedCheckoutAt is resolved per active stay (room_stay.planned_checkout_at), with booking expected checkout as fallback. - Includes per-stay nightlyRate (nullable when rate was not set on room stay). - AGENT-only users are blocked. @@ -1822,7 +1823,11 @@ BOOKING APIS What it does: - Updates planned dates on booking (expectedCheckInAt, expectedCheckOutAt), not actual checkout. - - Updates booking-level expected dates only; does not auto-change room_stay checkout timestamps. + - Updates booking-level expected dates. + - After validation, auto-syncs linked active room stays (checked-out stays are not modified): + - OPEN booking: active stay fromAt is synced to expectedCheckInAt (when provided). + - OPEN/CHECKED_IN booking: active stay planned checkout is synced to expectedCheckOutAt (when provided). + - Does not auto-close room stays and does not overwrite actual room_stay.to_at timestamps. - For OPEN bookings: can update both expected check-in and expected check-out. - For OPEN bookings with linked room stays: each stay start/end must remain inside expected booking window. - For CHECKED_IN: can update only expectedCheckOutAt (check-in change is blocked). diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt b/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt index e2b349e..01b233d 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt @@ -511,6 +511,7 @@ class BookingFlow( room = room, fromAt = checkInAt, toAt = null, + plannedCheckoutAt = booking.expectedCheckoutAt, rateSource = parseRateSource(stay.rateSource), nightlyRate = stay.nightlyRate, ratePlanCode = stay.ratePlanCode, @@ -579,6 +580,7 @@ class BookingFlow( throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid date range") } validateRoomStayBoundsForExpectedDatesUpdate(booking, expectedIn, expectedOut) + syncActiveRoomStaysWithBookingExpectedDates(booking, expectedIn, expectedOut) booking.updatedAt = OffsetDateTime.now() bookingRepo.save(booking) @@ -637,6 +639,32 @@ class BookingFlow( } } + private fun syncActiveRoomStaysWithBookingExpectedDates( + booking: com.android.trisolarisserver.models.booking.Booking, + expectedIn: OffsetDateTime?, + expectedOut: OffsetDateTime? + ) { + val bookingId = booking.id ?: return + if (expectedIn == null && expectedOut == null) return + val activeStays = roomStayRepo.findActiveByBookingId(bookingId) + if (activeStays.isEmpty()) return + + var changed = false + activeStays.forEach { stay -> + if (booking.status == BookingStatus.OPEN && expectedIn != null && stay.fromAt != expectedIn) { + stay.fromAt = expectedIn + changed = true + } + if (expectedOut != null && stay.plannedCheckoutAt != expectedOut) { + stay.plannedCheckoutAt = expectedOut + changed = true + } + } + if (changed) { + roomStayRepo.saveAll(activeStays) + } + } + @PostMapping("/{bookingId}/billing-policy") @ResponseStatus(HttpStatus.NO_CONTENT) @Transactional diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt b/src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt index 89a3f9a..378f447 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt @@ -69,7 +69,7 @@ class RoomStays( roomTypeName = roomType.name, fromAt = stay.fromAt.toString(), checkinAt = booking.checkinAt?.toString(), - expectedCheckoutAt = booking.expectedCheckoutAt?.toString(), + expectedCheckoutAt = (stay.plannedCheckoutAt ?: booking.expectedCheckoutAt)?.toString(), nightlyRate = stay.nightlyRate ) } diff --git a/src/main/kotlin/com/android/trisolarisserver/models/room/RoomStay.kt b/src/main/kotlin/com/android/trisolarisserver/models/room/RoomStay.kt index fa47a7e..2e6da9c 100644 --- a/src/main/kotlin/com/android/trisolarisserver/models/room/RoomStay.kt +++ b/src/main/kotlin/com/android/trisolarisserver/models/room/RoomStay.kt @@ -33,6 +33,9 @@ class RoomStay( @Column(name = "to_at", columnDefinition = "timestamptz") var toAt: OffsetDateTime? = null, // null = active + @Column(name = "planned_checkout_at", columnDefinition = "timestamptz") + var plannedCheckoutAt: OffsetDateTime? = null, + @Column(name = "is_voided", nullable = false) var isVoided: Boolean = false, diff --git a/src/main/kotlin/com/android/trisolarisserver/repo/room/RoomStayRepo.kt b/src/main/kotlin/com/android/trisolarisserver/repo/room/RoomStayRepo.kt index 103e433..bf12566 100644 --- a/src/main/kotlin/com/android/trisolarisserver/repo/room/RoomStayRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/repo/room/RoomStayRepo.kt @@ -48,7 +48,7 @@ interface RoomStayRepo : JpaRepository { @Query(""" select rs.room.id as roomId, rs.fromAt as fromAt, - b.expectedCheckoutAt as expectedCheckoutAt + coalesce(rs.plannedCheckoutAt, b.expectedCheckoutAt) as expectedCheckoutAt from RoomStay rs join rs.booking b where rs.property.id = :propertyId