diff --git a/docs/API_REFERENCE.txt b/docs/API_REFERENCE.txt index b08f84f..4e51b94 100644 --- a/docs/API_REFERENCE.txt +++ b/docs/API_REFERENCE.txt @@ -1850,6 +1850,8 @@ BOOKING APIS - 404 Not Found - 409 Conflict - Booking not checked in + - Primary guest missing / guest name missing / guest phone missing / guest signature missing + - checkOutAt must be after checkInAt - Room stay amount outside allowed range - Ledger mismatch tolerance failure @@ -1878,7 +1880,11 @@ BOOKING APIS - 401 Unauthorized - 403 Forbidden - 404 Not Found (booking/stay) - - 409 Conflict (booking not checked in / amount or minimum duration invalid) + - 409 Conflict + - booking not checked in + - Primary guest missing / guest name missing / guest phone missing / guest signature missing + - checkOutAt must be after checkInAt + - amount or minimum duration invalid - Cancel booking API is this one: 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 f12ac3c..1870405 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt @@ -651,6 +651,7 @@ class BookingFlow( } val now = OffsetDateTime.now() val checkOutAt = parseOffset(request.checkOutAt) ?: now + validateCheckoutPrerequisites(booking, checkOutAt) val stays = roomStayRepo.findActiveByBookingId(bookingId) if (stays.any { !isCheckoutAmountValid(it) || !isMinimumStayDurationValid(it, checkOutAt) }) { @@ -723,6 +724,7 @@ class BookingFlow( val now = OffsetDateTime.now() val checkOutAt = parseOffset(request.checkOutAt) ?: now + validateCheckoutPrerequisites(booking, checkOutAt) if (!isCheckoutAmountValid(stay)) { throw ResponseStatusException(HttpStatus.CONFLICT, "Room stay amount is outside allowed range") } @@ -1060,6 +1062,29 @@ class BookingFlow( return java.time.Duration.between(stay.fromAt, checkOutAt).toMinutes() >= 60 } + private fun validateCheckoutPrerequisites( + booking: com.android.trisolarisserver.models.booking.Booking, + checkOutAt: OffsetDateTime + ) { + val guest = booking.primaryGuest + ?: throw ResponseStatusException(HttpStatus.CONFLICT, "Primary guest required before checkout") + if (guest.name.isNullOrBlank()) { + throw ResponseStatusException(HttpStatus.CONFLICT, "Guest name required before checkout") + } + if (guest.phoneE164.isNullOrBlank()) { + throw ResponseStatusException(HttpStatus.CONFLICT, "Guest phone required before checkout") + } + if (guest.signaturePath.isNullOrBlank()) { + throw ResponseStatusException(HttpStatus.CONFLICT, "Guest signature required before checkout") + } + + val checkInAt = booking.checkinAt ?: booking.expectedCheckinAt + ?: throw ResponseStatusException(HttpStatus.CONFLICT, "Check-in time missing for booking") + if (!checkOutAt.isAfter(checkInAt)) { + throw ResponseStatusException(HttpStatus.CONFLICT, "checkOutAt must be after checkInAt") + } + } + private fun ensureLedgerToleranceForCheckout( bookingId: UUID, timezone: String?,