Add booking details endpoint with guest, rooms, balance
All checks were successful
build-and-deploy / build-deploy (push) Successful in 1m34s

This commit is contained in:
androidlover5842
2026-01-29 21:20:21 +05:30
parent 790a74c40d
commit b765f041f7
2 changed files with 129 additions and 1 deletions

View File

@@ -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<RoomStay>, 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

View File

@@ -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<Int>,
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
)