Validate room-stay bounds on booking expected-date updates
All checks were successful
build-and-deploy / build-deploy (push) Successful in 39s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 39s
This commit is contained in:
@@ -1822,8 +1822,11 @@ BOOKING APIS
|
|||||||
What it does:
|
What it does:
|
||||||
|
|
||||||
- Updates planned dates on booking (expectedCheckInAt, expectedCheckOutAt), not actual checkout.
|
- Updates planned dates on booking (expectedCheckInAt, expectedCheckOutAt), not actual checkout.
|
||||||
|
- Updates booking-level expected dates only; does not auto-change room_stay checkout timestamps.
|
||||||
- For OPEN bookings: can update both expected check-in and expected check-out.
|
- 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).
|
- For CHECKED_IN: can update only expectedCheckOutAt (check-in change is blocked).
|
||||||
|
- For CHECKED_IN bookings with linked room stays: stay start must be >= booking check-in and < expected check-out; stay end cannot be after expected check-out.
|
||||||
- For CHECKED_OUT / CANCELLED / NO_SHOW: returns conflict (Booking closed).
|
- For CHECKED_OUT / CANCELLED / NO_SHOW: returns conflict (Booking closed).
|
||||||
- Validates range: if both are present, checkout must be after checkin.
|
- Validates range: if both are present, checkout must be after checkin.
|
||||||
- Returns 204 No Content.
|
- Returns 204 No Content.
|
||||||
@@ -1853,6 +1856,7 @@ BOOKING APIS
|
|||||||
- 409 Conflict
|
- 409 Conflict
|
||||||
- "Cannot change expected check-in after check-in"
|
- "Cannot change expected check-in after check-in"
|
||||||
- "Booking closed" for CHECKED_OUT, CANCELLED, NO_SHOW
|
- "Booking closed" for CHECKED_OUT, CANCELLED, NO_SHOW
|
||||||
|
- Room stay bounds outside allowed expected window for OPEN/CHECKED_IN booking
|
||||||
|
|
||||||
|
|
||||||
- Update booking billing policy API is this one:
|
- Update booking billing policy API is this one:
|
||||||
|
|||||||
@@ -578,12 +578,65 @@ class BookingFlow(
|
|||||||
if (expectedIn != null && expectedOut != null && !expectedOut.isAfter(expectedIn)) {
|
if (expectedIn != null && expectedOut != null && !expectedOut.isAfter(expectedIn)) {
|
||||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid date range")
|
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid date range")
|
||||||
}
|
}
|
||||||
|
validateRoomStayBoundsForExpectedDatesUpdate(booking, expectedIn, expectedOut)
|
||||||
|
|
||||||
booking.updatedAt = OffsetDateTime.now()
|
booking.updatedAt = OffsetDateTime.now()
|
||||||
bookingRepo.save(booking)
|
bookingRepo.save(booking)
|
||||||
bookingEvents.emit(propertyId, bookingId)
|
bookingEvents.emit(propertyId, bookingId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun validateRoomStayBoundsForExpectedDatesUpdate(
|
||||||
|
booking: com.android.trisolarisserver.models.booking.Booking,
|
||||||
|
expectedIn: OffsetDateTime?,
|
||||||
|
expectedOut: OffsetDateTime?
|
||||||
|
) {
|
||||||
|
if (expectedIn == null || expectedOut == null) return
|
||||||
|
val bookingId = booking.id ?: return
|
||||||
|
val stays = roomStayRepo.findByBookingId(bookingId)
|
||||||
|
when (booking.status) {
|
||||||
|
BookingStatus.OPEN -> {
|
||||||
|
stays.forEach { stay ->
|
||||||
|
if (stay.fromAt.isBefore(expectedIn) || stay.fromAt.isAfter(expectedOut)) {
|
||||||
|
throw ResponseStatusException(
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
"Room stay start must be within expected check-in/check-out window for OPEN booking"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val toAt = stay.toAt
|
||||||
|
if (toAt != null && (toAt.isBefore(expectedIn) || toAt.isAfter(expectedOut))) {
|
||||||
|
throw ResponseStatusException(
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
"Room stay end must be within expected check-in/check-out window for OPEN booking"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BookingStatus.CHECKED_IN -> {
|
||||||
|
val checkInAt = booking.checkinAt ?: expectedIn
|
||||||
|
stays.forEach { stay ->
|
||||||
|
if (stay.fromAt.isBefore(checkInAt) || !stay.fromAt.isBefore(expectedOut)) {
|
||||||
|
throw ResponseStatusException(
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
"Room stay start must be >= check-in and < expected check-out for CHECKED_IN booking"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val toAt = stay.toAt
|
||||||
|
if (toAt != null && toAt.isAfter(expectedOut)) {
|
||||||
|
throw ResponseStatusException(
|
||||||
|
HttpStatus.CONFLICT,
|
||||||
|
"Room stay end cannot be after expected check-out for CHECKED_IN booking"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
BookingStatus.CHECKED_OUT,
|
||||||
|
BookingStatus.CANCELLED,
|
||||||
|
BookingStatus.NO_SHOW -> {
|
||||||
|
// Already blocked by status guard in updateExpectedDates.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@PostMapping("/{bookingId}/billing-policy")
|
@PostMapping("/{bookingId}/billing-policy")
|
||||||
@ResponseStatus(HttpStatus.NO_CONTENT)
|
@ResponseStatus(HttpStatus.NO_CONTENT)
|
||||||
@Transactional
|
@Transactional
|
||||||
|
|||||||
Reference in New Issue
Block a user