From b765f041f75b8db5e4c6a1b5fb0795e5b3952f14 Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Thu, 29 Jan 2026 21:20:21 +0530 Subject: [PATCH] Add booking details endpoint with guest, rooms, balance --- .../controller/BookingFlow.kt | 96 ++++++++++++++++++- .../controller/dto/BookingDtos.kt | 34 +++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/BookingFlow.kt b/src/main/kotlin/com/android/trisolarisserver/controller/BookingFlow.kt index 77d3ce9..9d2f3b4 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/BookingFlow.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/BookingFlow.kt @@ -8,6 +8,7 @@ import com.android.trisolarisserver.controller.dto.BookingBulkCheckInRequest import com.android.trisolarisserver.controller.dto.BookingCheckOutRequest import com.android.trisolarisserver.controller.dto.BookingCreateRequest import com.android.trisolarisserver.controller.dto.BookingCreateResponse +import com.android.trisolarisserver.controller.dto.BookingDetailResponse import com.android.trisolarisserver.controller.dto.BookingExpectedDatesUpdateRequest import com.android.trisolarisserver.controller.dto.BookingLinkGuestRequest import com.android.trisolarisserver.controller.dto.BookingNoShowRequest @@ -25,6 +26,7 @@ import com.android.trisolarisserver.models.room.RateSource import com.android.trisolarisserver.models.property.Role import com.android.trisolarisserver.repo.AppUserRepo import com.android.trisolarisserver.repo.GuestVehicleRepo +import com.android.trisolarisserver.repo.PaymentRepo import com.android.trisolarisserver.repo.PropertyRepo import com.android.trisolarisserver.repo.RoomRepo import com.android.trisolarisserver.repo.RoomStayRepo @@ -57,7 +59,8 @@ class BookingFlow( private val roomBoardEvents: RoomBoardEvents, private val guestVehicleRepo: GuestVehicleRepo, private val guestRatingRepo: GuestRatingRepo, - private val guestDocumentRepo: GuestDocumentRepo + private val guestDocumentRepo: GuestDocumentRepo, + private val paymentRepo: PaymentRepo ) { @PostMapping @@ -193,6 +196,76 @@ class BookingFlow( } } + @GetMapping("/{bookingId}") + @Transactional + fun getBooking( + @PathVariable propertyId: UUID, + @PathVariable bookingId: UUID, + @AuthenticationPrincipal principal: MyPrincipal? + ): BookingDetailResponse { + requireRole( + propertyAccess, + propertyId, + principal, + Role.ADMIN, + Role.MANAGER, + Role.STAFF, + Role.HOUSEKEEPING, + Role.FINANCE + ) + val booking = requireBooking(propertyId, bookingId) + val stays = roomStayRepo.findByBookingId(bookingId) + val activeRooms = stays.filter { it.toAt == null } + val roomsToShow = if (activeRooms.isNotEmpty()) activeRooms else stays + val roomNumbers = roomsToShow.map { it.room.roomNumber } + .distinct() + .sorted() + + val guest = booking.primaryGuest + val signatureUrl = guest?.signaturePath?.let { + "/properties/$propertyId/guests/${guest.id}/signature/file" + } + + val totalNightlyRate = roomsToShow.sumOf { it.nightlyRate ?: 0L } + val expectedPay = computeExpectedPay(stays, booking.property.timezone) + val amountCollected = paymentRepo.sumAmountByBookingId(bookingId) + val pending = expectedPay - amountCollected + + return BookingDetailResponse( + id = booking.id!!, + status = booking.status.name, + guestId = guest?.id, + guestName = guest?.name, + guestPhone = guest?.phoneE164, + guestNationality = guest?.nationality, + guestAddressText = guest?.addressText, + guestSignatureUrl = signatureUrl, + roomNumbers = roomNumbers, + source = booking.source, + fromCity = booking.fromCity, + toCity = booking.toCity, + memberRelation = booking.memberRelation?.name, + transportMode = booking.transportMode?.name, + checkInAt = booking.checkinAt?.toString(), + checkOutAt = booking.checkoutAt?.toString(), + expectedCheckInAt = booking.expectedCheckinAt?.toString(), + expectedCheckOutAt = booking.expectedCheckoutAt?.toString(), + adultCount = booking.adultCount, + childCount = booking.childCount, + maleCount = booking.maleCount, + femaleCount = booking.femaleCount, + totalGuestCount = booking.totalGuestCount, + expectedGuestCount = booking.expectedGuestCount, + notes = booking.notes, + registeredByName = booking.createdBy?.name, + registeredByPhone = booking.createdBy?.phoneE164, + totalNightlyRate = totalNightlyRate, + expectedPay = expectedPay, + amountCollected = amountCollected, + pending = pending + ) + } + @PostMapping("/{bookingId}/link-guest") @ResponseStatus(HttpStatus.NO_CONTENT) @Transactional @@ -619,6 +692,27 @@ class BookingFlow( } } + private fun computeExpectedPay(stays: List, timezone: String?): Long { + if (stays.isEmpty()) return 0 + val now = nowForProperty(timezone) + var total = 0L + stays.forEach { stay -> + val rate = stay.nightlyRate ?: 0L + if (rate == 0L) return@forEach + val start = stay.fromAt.toLocalDate() + val endAt = stay.toAt ?: now + val end = endAt.toLocalDate() + val nights = daysBetweenInclusive(start, end) + total += rate * nights + } + return total + } + + private fun daysBetweenInclusive(start: java.time.LocalDate, end: java.time.LocalDate): Long { + val diff = end.toEpochDay() - start.toEpochDay() + return if (diff <= 0) 1L else diff + } + private fun isTransportModeAllowed( property: com.android.trisolarisserver.models.property.Property, mode: TransportMode diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/dto/BookingDtos.kt b/src/main/kotlin/com/android/trisolarisserver/controller/dto/BookingDtos.kt index 0d7c7df..ee18494 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/dto/BookingDtos.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/dto/BookingDtos.kt @@ -75,6 +75,40 @@ data class BookingListItem( val notes: String? ) +data class BookingDetailResponse( + val id: UUID, + val status: String, + val guestId: UUID?, + val guestName: String?, + val guestPhone: String?, + val guestNationality: String?, + val guestAddressText: String?, + val guestSignatureUrl: String?, + val roomNumbers: List, + val source: String?, + val fromCity: String?, + val toCity: String?, + val memberRelation: String?, + val transportMode: String?, + val checkInAt: String?, + val checkOutAt: String?, + val expectedCheckInAt: String?, + val expectedCheckOutAt: String?, + val adultCount: Int?, + val childCount: Int?, + val maleCount: Int?, + val femaleCount: Int?, + val totalGuestCount: Int?, + val expectedGuestCount: Int?, + val notes: String?, + val registeredByName: String?, + val registeredByPhone: String?, + val totalNightlyRate: Long, + val expectedPay: Long, + val amountCollected: Long, + val pending: Long +) + data class BookingLinkGuestRequest( val guestId: UUID )