Allow access code join by property code or id
All checks were successful
build-and-deploy / build-deploy (push) Successful in 34s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 34s
This commit is contained in:
@@ -192,6 +192,10 @@ Notes / constraints
|
|||||||
- Super admin can create properties and assign users to properties.
|
- Super admin can create properties and assign users to properties.
|
||||||
- Admin can assign ADMIN/MANAGER/STAFF/AGENT; Manager can assign STAFF/AGENT.
|
- Admin can assign ADMIN/MANAGER/STAFF/AGENT; Manager can assign STAFF/AGENT.
|
||||||
- Agents can only see free rooms.
|
- Agents can only see free rooms.
|
||||||
|
- Role hierarchy for visibility/management: SUPER_ADMIN > ADMIN > MANAGER > STAFF/HOUSEKEEPING/FINANCE/SUPERVISOR/GUIDE > AGENT. Users cannot see anyone above their rank in property user lists. Access code invites cannot assign ADMIN.
|
||||||
|
- Property code is auto-generated (7-char random, no fixed prefix). Property create no longer accepts `code` in request. Join-by-code uses property code, not propertyId.
|
||||||
|
- Property access codes: 6-digit PIN, 1-minute expiry, single-use. Admin generates; staff joins with property code + PIN.
|
||||||
|
- Property user disable is property-scoped (not global); hierarchy applies for who can disable.
|
||||||
|
|
||||||
Operational notes
|
Operational notes
|
||||||
- Payment provider migrated: PayU removed; Razorpay now used for settings, QR, payment links, and webhooks.
|
- Payment provider migrated: PayU removed; Razorpay now used for settings, QR, payment links, and webhooks.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ data class PropertyAccessCodeResponse(
|
|||||||
)
|
)
|
||||||
|
|
||||||
data class PropertyAccessCodeJoinRequest(
|
data class PropertyAccessCodeJoinRequest(
|
||||||
val propertyCode: String,
|
val propertyCode: String? = null,
|
||||||
|
val propertyId: String? = null,
|
||||||
val code: String
|
val code: String
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import com.android.trisolarisserver.controller.dto.property.PropertyAccessCodeJo
|
|||||||
import com.android.trisolarisserver.controller.dto.property.PropertyAccessCodeResponse
|
import com.android.trisolarisserver.controller.dto.property.PropertyAccessCodeResponse
|
||||||
import com.android.trisolarisserver.controller.dto.property.PropertyUserResponse
|
import com.android.trisolarisserver.controller.dto.property.PropertyUserResponse
|
||||||
import com.android.trisolarisserver.models.property.PropertyAccessCode
|
import com.android.trisolarisserver.models.property.PropertyAccessCode
|
||||||
|
import com.android.trisolarisserver.models.property.Property
|
||||||
import com.android.trisolarisserver.models.property.PropertyUser
|
import com.android.trisolarisserver.models.property.PropertyUser
|
||||||
import com.android.trisolarisserver.models.property.PropertyUserId
|
import com.android.trisolarisserver.models.property.PropertyUserId
|
||||||
import com.android.trisolarisserver.models.property.Role
|
import com.android.trisolarisserver.models.property.Role
|
||||||
@@ -97,8 +98,7 @@ class PropertyAccessCodes(
|
|||||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid code")
|
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid code")
|
||||||
}
|
}
|
||||||
val now = OffsetDateTime.now()
|
val now = OffsetDateTime.now()
|
||||||
val property = propertyRepo.findByCode(request.propertyCode.trim())
|
val property = resolveProperty(request)
|
||||||
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
|
||||||
val accessCode = accessCodeRepo.findActiveByPropertyAndCode(property.id!!, code, now)
|
val accessCode = accessCodeRepo.findActiveByPropertyAndCode(property.id!!, code, now)
|
||||||
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid code")
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Invalid code")
|
||||||
|
|
||||||
@@ -130,6 +130,27 @@ class PropertyAccessCodes(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun resolveProperty(request: PropertyAccessCodeJoinRequest): Property {
|
||||||
|
val code = request.propertyCode?.trim().orEmpty()
|
||||||
|
if (code.isNotBlank()) {
|
||||||
|
return propertyRepo.findByCode(code)
|
||||||
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
||||||
|
}
|
||||||
|
val rawId = request.propertyId?.trim().orEmpty()
|
||||||
|
if (rawId.isBlank()) {
|
||||||
|
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Property code required")
|
||||||
|
}
|
||||||
|
val asUuid = runCatching { UUID.fromString(rawId) }.getOrNull()
|
||||||
|
return if (asUuid != null) {
|
||||||
|
propertyRepo.findById(asUuid).orElseThrow {
|
||||||
|
ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
propertyRepo.findByCode(rawId)
|
||||||
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Property not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun parseRoles(input: Set<String>): Set<Role> {
|
private fun parseRoles(input: Set<String>): Set<Role> {
|
||||||
return try {
|
return try {
|
||||||
input.map { Role.valueOf(it) }.toSet()
|
input.map { Role.valueOf(it) }.toSet()
|
||||||
|
|||||||
Reference in New Issue
Block a user