Add rate plans, room stay rates, and payments ledger
All checks were successful
build-and-deploy / build-deploy (push) Successful in 34s

This commit is contained in:
androidlover5842
2026-01-29 04:56:37 +05:30
parent b6ac87d277
commit 71c70c8554
24 changed files with 902 additions and 4 deletions

View File

@@ -0,0 +1,115 @@
package com.android.trisolarisserver.controller
import com.android.trisolarisserver.component.PropertyAccess
import com.android.trisolarisserver.controller.dto.PaymentCreateRequest
import com.android.trisolarisserver.controller.dto.PaymentResponse
import com.android.trisolarisserver.db.repo.BookingRepo
import com.android.trisolarisserver.models.booking.Payment
import com.android.trisolarisserver.models.booking.PaymentMethod
import com.android.trisolarisserver.models.property.Role
import com.android.trisolarisserver.repo.AppUserRepo
import com.android.trisolarisserver.repo.PaymentRepo
import com.android.trisolarisserver.repo.PropertyRepo
import com.android.trisolarisserver.security.MyPrincipal
import org.springframework.http.HttpStatus
import org.springframework.security.core.annotation.AuthenticationPrincipal
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException
import java.time.LocalDate
import java.time.OffsetDateTime
import java.util.UUID
@RestController
@RequestMapping("/properties/{propertyId}/bookings/{bookingId}/payments")
class Payments(
private val propertyAccess: PropertyAccess,
private val propertyRepo: PropertyRepo,
private val bookingRepo: BookingRepo,
private val paymentRepo: PaymentRepo,
private val appUserRepo: AppUserRepo
) {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
fun create(
@PathVariable propertyId: UUID,
@PathVariable bookingId: UUID,
@AuthenticationPrincipal principal: MyPrincipal?,
@RequestBody request: PaymentCreateRequest
): PaymentResponse {
val actor = requireRole(propertyAccess, propertyId, principal, Role.ADMIN, Role.MANAGER, Role.STAFF)
val property = propertyRepo.findById(propertyId).orElseThrow {
ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
}
val booking = bookingRepo.findById(bookingId).orElseThrow {
ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found")
}
if (booking.property.id != propertyId) {
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found for property")
}
if (request.amount <= 0) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "amount must be > 0")
}
val method = parseMethod(request.method)
val receivedAt = parseOffset(request.receivedAt) ?: OffsetDateTime.now()
val payment = Payment(
property = property,
booking = booking,
amount = request.amount,
currency = request.currency ?: property.currency,
method = method,
reference = request.reference?.trim()?.ifBlank { null },
notes = request.notes?.trim()?.ifBlank { null },
receivedAt = receivedAt,
receivedBy = appUserRepo.findById(actor.userId).orElse(null)
)
return paymentRepo.save(payment).toResponse()
}
@GetMapping
fun list(
@PathVariable propertyId: UUID,
@PathVariable bookingId: UUID,
@AuthenticationPrincipal principal: MyPrincipal?
): List<PaymentResponse> {
requireMember(propertyAccess, propertyId, principal)
val booking = bookingRepo.findById(bookingId).orElseThrow {
ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found")
}
if (booking.property.id != propertyId) {
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found for property")
}
return paymentRepo.findByBookingIdOrderByReceivedAtDesc(bookingId).map { it.toResponse() }
}
private fun parseMethod(value: String): PaymentMethod {
return try {
PaymentMethod.valueOf(value.trim())
} catch (_: IllegalArgumentException) {
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Unknown payment method")
}
}
}
private fun Payment.toResponse(): PaymentResponse {
val id = id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Payment id missing")
val bookingId = booking.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Booking id missing")
return PaymentResponse(
id = id,
bookingId = bookingId,
amount = amount,
currency = currency,
method = method.name,
reference = reference,
notes = notes,
receivedAt = receivedAt.toString(),
receivedByUserId = receivedBy?.id
)
}