diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/Guests.kt b/src/main/kotlin/com/android/trisolarisserver/controller/Guests.kt index e05778f..c736e04 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/Guests.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/Guests.kt @@ -8,6 +8,7 @@ import com.android.trisolarisserver.controller.dto.GuestVehicleRequest import com.android.trisolarisserver.models.booking.Guest import com.android.trisolarisserver.models.booking.GuestVehicle import com.android.trisolarisserver.db.repo.BookingRepo +import com.android.trisolarisserver.db.repo.GuestDocumentRepo import com.android.trisolarisserver.db.repo.GuestRepo import com.android.trisolarisserver.db.repo.GuestRatingRepo import com.android.trisolarisserver.repo.AppUserRepo @@ -19,6 +20,7 @@ import org.springframework.http.HttpStatus import org.springframework.http.MediaType import org.springframework.http.ResponseEntity import org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.transaction.annotation.Transactional import org.springframework.web.bind.annotation.* import org.springframework.web.multipart.MultipartFile import org.springframework.web.server.ResponseStatusException @@ -34,12 +36,14 @@ class Guests( private val bookingRepo: BookingRepo, private val guestVehicleRepo: GuestVehicleRepo, private val guestRatingRepo: GuestRatingRepo, + private val guestDocumentRepo: GuestDocumentRepo, private val signatureStorage: GuestSignatureStorage, private val appUserRepo: AppUserRepo ) { @PostMapping @ResponseStatus(HttpStatus.CREATED) + @Transactional fun createGuest( @PathVariable propertyId: UUID, @AuthenticationPrincipal principal: MyPrincipal?, @@ -55,15 +59,26 @@ class Guests( if (booking.property.id != property.id) { throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Booking not in property") } - if (booking.primaryGuest != null) { - throw ResponseStatusException(HttpStatus.CONFLICT, "Booking already linked to guest") + val currentGuest = booking.primaryGuest + if (currentGuest != null && phone == null) { + if (!isPlaceholderGuest(currentGuest)) { + throw ResponseStatusException(HttpStatus.CONFLICT, "Booking already linked to guest") + } + val updated = applyGuestDetails(currentGuest, request) + booking.updatedAt = OffsetDateTime.now() + bookingRepo.save(booking) + return setOf(updated).toResponse(propertyId, guestVehicleRepo, guestRatingRepo).first() } if (phone != null) { val existing = guestRepo.findByPropertyIdAndPhoneE164(propertyId, phone) if (existing != null) { + val previous = booking.primaryGuest booking.primaryGuest = existing booking.updatedAt = OffsetDateTime.now() bookingRepo.save(booking) + if (previous != null && previous.id != existing.id && isPlaceholderGuest(previous) && isSafeToDelete(previous)) { + guestRepo.delete(previous) + } return setOf(existing).toResponse(propertyId, guestVehicleRepo, guestRatingRepo).first() } } @@ -196,6 +211,33 @@ class Guests( .body(resource) } + private fun applyGuestDetails(guest: Guest, request: GuestCreateRequest): Guest { + val name = request.name?.trim()?.ifBlank { null } + val nationality = request.nationality?.trim()?.ifBlank { null } + val address = request.addressText?.trim()?.ifBlank { null } + if (name != null) guest.name = name + if (nationality != null) guest.nationality = nationality + if (address != null) guest.addressText = address + guest.updatedAt = OffsetDateTime.now() + return guestRepo.save(guest) + } + + private fun isPlaceholderGuest(guest: Guest): Boolean { + return guest.phoneE164.isNullOrBlank() && + guest.name.isNullOrBlank() && + guest.nationality.isNullOrBlank() && + guest.addressText.isNullOrBlank() && + guest.signaturePath.isNullOrBlank() + } + + private fun isSafeToDelete(guest: Guest): Boolean { + val id = guest.id ?: return false + if (bookingRepo.countByPrimaryGuestId(id) > 0) return false + if (guestVehicleRepo.existsByGuestId(id)) return false + if (guestDocumentRepo.existsByGuestId(id)) return false + if (guestRatingRepo.existsByGuestId(id)) return false + return true + } } private fun Set.toResponse( diff --git a/src/main/kotlin/com/android/trisolarisserver/db/repo/BookingRepo.kt b/src/main/kotlin/com/android/trisolarisserver/db/repo/BookingRepo.kt index 6b441fc..78a1697 100644 --- a/src/main/kotlin/com/android/trisolarisserver/db/repo/BookingRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/db/repo/BookingRepo.kt @@ -7,4 +7,5 @@ import java.util.UUID interface BookingRepo : JpaRepository { fun findByPropertyIdAndSourceBookingId(propertyId: UUID, sourceBookingId: String): Booking? fun existsByPropertyIdAndSourceBookingId(propertyId: UUID, sourceBookingId: String): Boolean + fun countByPrimaryGuestId(guestId: UUID): Long } diff --git a/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestDocumentRepo.kt b/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestDocumentRepo.kt index 034066f..bdc4b62 100644 --- a/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestDocumentRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestDocumentRepo.kt @@ -7,4 +7,5 @@ import java.util.UUID interface GuestDocumentRepo : JpaRepository { fun findByPropertyIdAndGuestIdOrderByUploadedAtDesc(propertyId: UUID, guestId: UUID): List fun findByIdAndPropertyIdAndGuestId(id: UUID, propertyId: UUID, guestId: UUID): GuestDocument? + fun existsByGuestId(guestId: UUID): Boolean } diff --git a/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestRatingRepo.kt b/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestRatingRepo.kt index f87b1e2..a8d09f6 100644 --- a/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestRatingRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/db/repo/GuestRatingRepo.kt @@ -9,6 +9,7 @@ import java.util.UUID interface GuestRatingRepo : JpaRepository { fun findByGuestIdOrderByCreatedAtDesc(guestId: UUID): List fun existsByGuestIdAndBookingId(guestId: UUID, bookingId: UUID): Boolean + fun existsByGuestId(guestId: UUID): Boolean @Query( """ diff --git a/src/main/kotlin/com/android/trisolarisserver/repo/GuestVehicleRepo.kt b/src/main/kotlin/com/android/trisolarisserver/repo/GuestVehicleRepo.kt index d3a3b1f..75f9943 100644 --- a/src/main/kotlin/com/android/trisolarisserver/repo/GuestVehicleRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/repo/GuestVehicleRepo.kt @@ -8,4 +8,5 @@ interface GuestVehicleRepo : JpaRepository { fun findByPropertyIdAndVehicleNumberIgnoreCase(propertyId: UUID, vehicleNumber: String): GuestVehicle? fun findByGuestIdIn(guestIds: List): List fun existsByPropertyIdAndVehicleNumberIgnoreCase(propertyId: UUID, vehicleNumber: String): Boolean + fun existsByGuestId(guestId: UUID): Boolean }