# API Catalog Behavior-first catalog generated from controller source. - Total endpoints: **125** - Notes: validations/errors are extracted from explicit `ResponseStatusException` checks and shared helper guards. - Regenerate: `python scripts/generate_api_docs.py` ## `src/main/kotlin/com/android/trisolarisserver/controller/assets/IconFiles.kt` ### `GET /icons/png` - Handler: `listPng` (`src/main/kotlin/com/android/trisolarisserver/controller/assets/IconFiles.kt:23`) - Behavior: List resources (list png). - Body: none - Auth: Public/unspecified - Response: `200` `List` - Common errors: none observed in controller checks ### `GET /icons/png/{filename}` - Handler: `getPng` (`src/main/kotlin/com/android/trisolarisserver/controller/assets/IconFiles.kt:39`) - Behavior: Get resource (get png). - Path params: filename:String - Body: none - Auth: Public/unspecified - Response: `200` `ResponseEntity` - Common errors: none observed in controller checks ## `src/main/kotlin/com/android/trisolarisserver/controller/auth/Auth.kt` ### `GET /auth/me` - Handler: `me` (`src/main/kotlin/com/android/trisolarisserver/controller/auth/Auth.kt:44`) - Behavior: Me. - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `ResponseEntity` - Common errors: none observed in controller checks ### `PUT /auth/me` - Handler: `updateMe` (`src/main/kotlin/com/android/trisolarisserver/controller/auth/Auth.kt:54`) - Behavior: Update resource (update me). - Body: UpdateMeRequest { name:String? (optional) } - Auth: Authenticated user (Firebase) - Response: `200` `ResponseEntity` - Common errors: 401 (User not found) ### `POST /auth/verify` - Handler: `verify` (`src/main/kotlin/com/android/trisolarisserver/controller/auth/Auth.kt:33`) - Behavior: Verify. - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `ResponseEntity` - Common errors: none observed in controller checks ## `src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingBalances.kt` ### `GET /properties/{propertyId}/bookings/{bookingId}/balance` - Handler: `getBalance` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingBalances.kt:32`) - Behavior: Get resource (get balance). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Any property member - Response: `200` `BookingBalanceResponse` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Booking not found; Booking not found for property) ## `src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt` ### `GET /properties/{propertyId}/bookings` - Handler: `listBookings` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:187`) - Behavior: List resources (list bookings). - Path params: propertyId:UUID - Query params: status:String? (optional) - Body: none - Auth: Roles: ADMIN, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `List` - Validation/guard checks: - 400: Invalid status: $value - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found), 400 (Invalid status: $value) ### `POST /properties/{propertyId}/bookings` - Handler: `createBooking` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:99`) - Behavior: Create resource (create booking). - Path params: propertyId:UUID - Body: BookingCreateRequest { source:String? (optional), expectedCheckInAt:String, expectedCheckOutAt:String, billingMode:String? (optional), billingCheckoutTime:String? (optional), guestPhoneE164:String? (optional), fromCity:String? (optional), toCity:String? (optional), memberRelation:String? (optional), transportMode:String? (optional), childCount:Int? (optional), maleCount:Int? (optional), femaleCount:Int? (optional), expectedGuestCount:Int? (optional), notes:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `201` `BookingCreateResponse` - Validation/guard checks: - 400: expectedCheckInAt required - 400: expectedCheckOutAt required - 400: Invalid date range - 400: Transport mode disabled - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Property not found), 400 (expectedCheckInAt required; expectedCheckOutAt required) ### `GET /properties/{propertyId}/bookings/{bookingId}` - Handler: `getBooking` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:282`) - Behavior: Get resource (get booking). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `BookingDetailResponse` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/billing-policy` - Handler: `updateBillingPolicy` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:476`) - Behavior: Update resource (update billing policy). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingBillingPolicyUpdateRequest { billingMode:String, billingCheckoutTime:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 400: $fieldName required - 400: $fieldName must be HH:mm - 400: Unknown billing mode - 409: Booking closed - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 ($fieldName required; $fieldName must be HH:mm), 409 (Booking closed) ### `POST /properties/{propertyId}/bookings/{bookingId}/cancel` - Handler: `cancel` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:695`) - Behavior: Cancel flow (cancel). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingCancelRequest { cancelledAt:String? (optional), reason:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 409: Cannot cancel checked-in booking - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 409 (Cannot cancel checked-in booking) ### `POST /properties/{propertyId}/bookings/{bookingId}/check-in/bulk` - Handler: `bulkCheckIn` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:353`) - Behavior: Bulk check in. - Path params: propertyId:UUID, bookingId:UUID - Body: BookingBulkCheckInRequest { stays:List, transportMode:String? (optional), notes:String? (optional) } - Side effects: Emits booking SSE updates. Emits room board SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `201` `Unit` - Validation/guard checks: - 400: stays required - 400: Duplicate roomId in stays - 400: Transport mode disabled - 400: Unknown transport mode - 409: Booking not open - 409: Room not available - 409: Room already occupied - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Room not found; Booking not found), 400 (stays required; Duplicate roomId in stays), 409 (Booking not open; Room not available) ### `POST /properties/{propertyId}/bookings/{bookingId}/check-out` - Handler: `checkOut` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:527`) - Behavior: Check out flow (check out). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingCheckOutRequest { checkOutAt:String? (optional), notes:String? (optional) } - Side effects: Emits booking SSE updates. Emits room board SSE updates. Writes room-stay audit log. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 400: Invalid timestamp - 409: Booking not checked in - 409: Room stay amount is outside allowed range - 409: Ledger mismatch: collected amount must be within 20% of expected amount before checkout - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Invalid timestamp), 409 (Booking not checked in; Room stay amount is outside allowed range) ### `POST /properties/{propertyId}/bookings/{bookingId}/expected-dates` - Handler: `updateExpectedDates` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:430`) - Behavior: Update resource (update expected dates). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingExpectedDatesUpdateRequest { expectedCheckInAt:String? (optional), expectedCheckOutAt:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 400: Invalid date range - 400: Invalid timestamp - 409: Cannot change expected check-in after check-in - 409: Booking closed - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Invalid date range; Invalid timestamp), 409 (Cannot change expected check-in after check-in; Booking closed) ### `POST /properties/{propertyId}/bookings/{bookingId}/link-guest` - Handler: `linkGuest` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:326`) - Behavior: Link guest. - Path params: propertyId:UUID, bookingId:UUID - Body: BookingLinkGuestRequest { guestId:UUID } - Side effects: Emits booking SSE updates. - Auth: Any property member - Response: `204` `Unit` - Validation/guard checks: - 400: Guest not in property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Guest not found; Booking not found), 400 (Guest not in property) ### `POST /properties/{propertyId}/bookings/{bookingId}/no-show` - Handler: `noShow` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:720`) - Behavior: No-show flow (no show). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingNoShowRequest { noShowAt:String? (optional), reason:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 409: Booking not open - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 409 (Booking not open) ### `POST /properties/{propertyId}/bookings/{bookingId}/room-stays/{roomStayId}/check-out` - Handler: `checkOutRoomStay` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:583`) - Behavior: Check out flow (check out room stay). - Path params: propertyId:UUID, bookingId:UUID, roomStayId:UUID - Body: BookingCheckOutRequest { checkOutAt:String? (optional), notes:String? (optional) } - Side effects: Emits booking SSE updates. Emits room board SSE updates. Writes room-stay audit log. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 400: Invalid timestamp - 409: Booking not checked in - 409: Room stay amount is outside allowed range - 409: Minimum stay duration is 1 hour - 409: Ledger mismatch: collected amount must be within 20% of expected amount before checkout - Common errors: 401 (User not found; Missing principal), 403 (Required property role not granted), 404 (Room stay not found for booking; Booking not found), 400 (Invalid timestamp), 409 (Booking not checked in; Room stay amount is outside allowed range) ### `GET /properties/{propertyId}/bookings/{bookingId}/stream` - Handler: `streamBooking` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingFlow.kt:302`) - Behavior: Stream events/data (stream booking). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Side effects: Streams SSE events. - Auth: Roles: ADMIN, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `org.springframework.web.servlet.mvc.method.annotation.SseEmitter` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ## `src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingRoomRequests.kt` ### `GET /properties/{propertyId}/bookings/{bookingId}/room-requests` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingRoomRequests.kt:103`) - Behavior: List resources (list). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/room-requests` - Handler: `create` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingRoomRequests.kt:48`) - Behavior: Create resource (create). - Path params: propertyId:UUID, bookingId:UUID - Body: BookingRoomRequestCreateRequest { roomTypeCode:String, quantity:Int, fromAt:String, toAt:String } - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `201` `BookingRoomRequestResponse` - Validation/guard checks: - 400: quantity must be > 0 - 400: fromAt required - 400: toAt required - 400: Invalid date range - 409: Booking closed - 409: Insufficient room type availability - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (quantity must be > 0; fromAt required), 409 (Booking closed; Insufficient room type availability) ### `DELETE /properties/{propertyId}/bookings/{bookingId}/room-requests/{requestId}` - Handler: `cancel` (`src/main/kotlin/com/android/trisolarisserver/controller/booking/BookingRoomRequests.kt:121`) - Behavior: Cancel flow (cancel). - Path params: propertyId:UUID, bookingId:UUID, requestId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `204` `Unit` - Validation/guard checks: - 409: Cannot cancel fulfilled room request - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 409 (Cannot cancel fulfilled room request) ## `src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt` ### `GET /properties/{propertyId}/room-stays/cards/{cardIndex}` - Handler: `getCardByIndex` (`src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt:158`) - Behavior: Get resource (get card by index). - Path params: propertyId:UUID, cardIndex:Int - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `IssuedCardResponse` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Card not found) ### `POST /properties/{propertyId}/room-stays/cards/{cardIndex}/revoke` - Handler: `revoke` (`src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt:138`) - Behavior: Revoke. - Path params: propertyId:UUID, cardIndex:Int - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `CardRevokeResponse` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Card not found) ### `GET /properties/{propertyId}/room-stays/{roomStayId}/cards` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt:125`) - Behavior: List resources (list). - Path params: propertyId:UUID, roomStayId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF, SUPERVISOR - Response: `200` `List` - Common errors: 401 (Missing principal), 404 (Room stay not found for property) ### `POST /properties/{propertyId}/room-stays/{roomStayId}/cards` - Handler: `issue` (`src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt:83`) - Behavior: Issue. - Path params: propertyId:UUID, roomStayId:UUID - Body: IssueCardRequest { cardId:String, cardIndex:Int, issuedAt:String? (optional), expiresAt:String } - Auth: Roles: ADMIN, MANAGER - Response: `201` `IssuedCardResponse` - Validation/guard checks: - 400: cardId required - 400: cardIndex required - 400: expiresAt required - 400: expiresAt must be after issuedAt - 409: Active card already exists for room stay - 409: Active card already exists for room - 409: Room stay is already closed - Common errors: 401 (Missing principal; User not found), 403 (Property membership required), 404 (Room stay not found for property), 400 (cardId required; cardIndex required), 409 (Active card already exists for room stay; Active card already exists for room) ### `POST /properties/{propertyId}/room-stays/{roomStayId}/cards/prepare` - Handler: `prepare` (`src/main/kotlin/com/android/trisolarisserver/controller/card/IssuedCards.kt:50`) - Behavior: Prepare. - Path params: propertyId:UUID, roomStayId:UUID - Body: CardPrepareRequest { expiresAt:String? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `201` `CardPrepareResponse` - Validation/guard checks: - 400: expiresAt required - 400: expiresAt must be after issuedAt - 400: Invalid timestamp - 409: Room stay is already closed - Common errors: 401 (Missing principal; User not found), 403 (Property membership required), 404 (Room stay not found for property; Property not found), 400 (expiresAt required; expiresAt must be after issuedAt), 409 (Room stay is already closed) ## `src/main/kotlin/com/android/trisolarisserver/controller/card/TemporaryRoomCards.kt` ### `POST /properties/{propertyId}/rooms/{roomId}/cards/prepare-temp` - Handler: `prepareTemporary` (`src/main/kotlin/com/android/trisolarisserver/controller/card/TemporaryRoomCards.kt:46`) - Behavior: Prepare temporary. - Path params: propertyId:UUID, roomId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `201` `CardPrepareResponse` - Common errors: 401 (Missing principal; User not found), 403 (Property membership required), 404 (Room not found; Property not found) ### `POST /properties/{propertyId}/rooms/{roomId}/cards/temp` - Handler: `issueTemporary` (`src/main/kotlin/com/android/trisolarisserver/controller/card/TemporaryRoomCards.kt:74`) - Behavior: Issue temporary. - Path params: propertyId:UUID, roomId:UUID - Body: IssueTempCardRequest { cardId:String, cardIndex:Int, issuedAt:String? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `201` `IssuedCardResponse` - Validation/guard checks: - 400: cardId required - 400: cardIndex required - 400: Invalid timestamp - 409: Active card already exists for room - Common errors: 401 (Missing principal; User not found), 403 (Property membership required), 404 (Room not found), 400 (cardId required; cardIndex required), 409 (Active card already exists for room) ## `src/main/kotlin/com/android/trisolarisserver/controller/email/InboundEmailManual.kt` ### `POST /properties/{propertyId}/inbound-emails/manual` - Handler: `uploadManualPdf` (`src/main/kotlin/com/android/trisolarisserver/controller/email/InboundEmailManual.kt:41`) - Behavior: Upload manual pdf. - Path params: propertyId:UUID - Query params: file:MultipartFile (required) - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `201` `ManualInboundResponse` - Validation/guard checks: - 400: File is empty - 400: Only PDF is supported - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (File is empty; Only PDF is supported) ## `src/main/kotlin/com/android/trisolarisserver/controller/email/InboundEmails.kt` ### `GET /properties/{propertyId}/inbound-emails/{emailId}/file` - Handler: `downloadEmailPdf` (`src/main/kotlin/com/android/trisolarisserver/controller/email/InboundEmails.kt:32`) - Behavior: Download email pdf. - Path params: propertyId:UUID, emailId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `ResponseEntity` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Email not found; Email PDF missing) ## `src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt` ### `GET /properties/{propertyId}/guests/{guestId}/documents` - Handler: `listDocuments` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt:125`) - Behavior: List resources (list documents). - Path params: propertyId:UUID, guestId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Required property role not granted) ### `POST /properties/{propertyId}/guests/{guestId}/documents` - Handler: `uploadDocument` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt:65`) - Behavior: Upload document. - Path params: propertyId:UUID, guestId:UUID - Query params: bookingId:UUID (required) - Body: none - Side effects: Stores/updates guest document metadata. - Auth: Roles: ADMIN, MANAGER - Response: `201` `GuestDocumentResponse` - Validation/guard checks: - 400: File is empty - 400: Video files are not allowed - 400: Booking not in property - 400: Booking not linked to guest - 409: Duplicate document - Common errors: 401 (Missing principal; User not found), 403 (Property membership required), 404 (Booking not found; Property or guest not found), 400 (File is empty; Video files are not allowed), 409 (Duplicate document) ### `GET /properties/{propertyId}/guests/{guestId}/documents/stream` - Handler: `streamDocuments` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt:138`) - Behavior: Stream events/data (stream documents). - Path params: propertyId:UUID, guestId:UUID - Body: none - Side effects: Streams SSE events. - Auth: Roles: ADMIN, MANAGER - Response: `200` `org.springframework.web.servlet.mvc.method.annotation.SseEmitter` - Common errors: 401 (Missing principal), 403 (Required property role not granted) ### `DELETE /properties/{propertyId}/guests/{guestId}/documents/{documentId}` - Handler: `deleteDocument` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt:184`) - Behavior: Delete resource (delete document). - Path params: propertyId:UUID, guestId:UUID, documentId:UUID - Body: none - Side effects: Deletes guest document metadata. - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Validation/guard checks: - 400: Documents can only be deleted for OPEN or CHECKED_IN bookings - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Document not found), 400 (Documents can only be deleted for OPEN or CHECKED_IN bookings), 500 (Failed to delete file) ### `GET /properties/{propertyId}/guests/{guestId}/documents/{documentId}/file` - Handler: `downloadDocument` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestDocuments.kt:152`) - Behavior: Download document. - Path params: propertyId:UUID, guestId:UUID, documentId:UUID - Query params: token:String? (optional) - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `ResponseEntity` - Common errors: 401 (Invalid token; Missing principal), 403 (Required property role not granted), 404 (Document not found; File missing) ## `src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestRatings.kt` ### `GET /properties/{propertyId}/guests/{guestId}/ratings` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestRatings.kt:79`) - Behavior: List resources (list). - Path params: propertyId:UUID, guestId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Validation/guard checks: - 400: Guest not in property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property or guest not found), 400 (Guest not in property) ### `POST /properties/{propertyId}/guests/{guestId}/ratings` - Handler: `create` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/GuestRatings.kt:42`) - Behavior: Create resource (create). - Path params: propertyId:UUID, guestId:UUID - Body: GuestRatingCreateRequest { bookingId:UUID, score:String, notes:String? (optional) } - Auth: Any property member - Response: `201` `GuestRatingResponse` - Validation/guard checks: - 400: Booking not in property - 400: Booking not linked to guest - 400: score must be GOOD/OK/TROUBLE or 1/2/3 - 400: Guest not in property - 409: Rating already exists for booking - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Booking not found; Property or guest not found), 400 (Booking not in property; Booking not linked to guest), 409 (Rating already exists for booking) ## `src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt` ### `GET /properties/{propertyId}/guests/search` - Handler: `search` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:75`) - Behavior: Search. - Path params: propertyId:UUID - Query params: phone:String? (optional), vehicleNumber:String? (optional) - Body: none - Auth: Any property member - Response: `200` `List` - Validation/guard checks: - 400: phone or vehicleNumber required - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (phone or vehicleNumber required) ### `GET /properties/{propertyId}/guests/visit-count` - Handler: `getVisitCount` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:113`) - Behavior: Get resource (get visit count). - Path params: propertyId:UUID - Query params: phone:String (required) - Body: none - Auth: Any property member - Response: `200` `GuestVisitCountResponse` - Validation/guard checks: - 400: phone required - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (phone required) ### `GET /properties/{propertyId}/guests/{guestId}` - Handler: `getGuest` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:102`) - Behavior: Get resource (get guest). - Path params: propertyId:UUID, guestId:UUID - Body: none - Auth: Any property member - Response: `200` `GuestResponse` - Validation/guard checks: - 400: Guest not in property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property or guest not found), 400 (Guest not in property) ### `PUT /properties/{propertyId}/guests/{guestId}` - Handler: `updateGuest` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:45`) - Behavior: Update resource (update guest). - Path params: propertyId:UUID, guestId:UUID - Body: GuestUpdateRequest { phoneE164:String? (optional), name:String? (optional), nationality:String? (optional), addressText:String? (optional) } - Auth: Any property member - Response: `200` `GuestResponse` - Validation/guard checks: - 400: Guest not in property - 409: Phone number already exists - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property or guest not found), 400 (Guest not in property), 409 (Phone number already exists) ### `POST /properties/{propertyId}/guests/{guestId}/signature` - Handler: `uploadSignature` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:170`) - Behavior: Upload signature. - Path params: propertyId:UUID, guestId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `201` `GuestResponse` - Validation/guard checks: - 400: File is empty - 400: Only SVG allowed - 400: Guest not in property - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property or guest not found), 400 (File is empty; Only SVG allowed) ### `GET /properties/{propertyId}/guests/{guestId}/signature/file` - Handler: `downloadSignature` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:194`) - Behavior: Download signature. - Path params: propertyId:UUID, guestId:UUID - Body: none - Auth: Any property member - Response: `200` `ResponseEntity` - Validation/guard checks: - 400: Guest not in property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Signature not found; Property or guest not found), 400 (Guest not in property) ### `POST /properties/{propertyId}/guests/{guestId}/vehicles` - Handler: `addVehicle` (`src/main/kotlin/com/android/trisolarisserver/controller/guest/Guests.kt:130`) - Behavior: Add vehicle. - Path params: propertyId:UUID, guestId:UUID - Body: GuestVehicleRequest { vehicleNumber:String, bookingId:UUID } - Auth: Any property member - Response: `201` `GuestResponse` - Validation/guard checks: - 400: Booking not in property - 400: Guest not in property - 409: Booking linked to different guest - 409: Vehicle number already exists - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Booking not found; Property or guest not found), 400 (Booking not in property; Guest not in property), 409 (Booking linked to different guest; Vehicle number already exists) ## `src/main/kotlin/com/android/trisolarisserver/controller/payment/Charges.kt` ### `GET /properties/{propertyId}/bookings/{bookingId}/charges` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/payment/Charges.kt:80`) - Behavior: List resources (list). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/charges` - Handler: `create` (`src/main/kotlin/com/android/trisolarisserver/controller/payment/Charges.kt:44`) - Behavior: Create resource (create). - Path params: propertyId:UUID, bookingId:UUID - Body: ChargeCreateRequest { type:String, amount:Long, currency:String, occurredAt:String? (optional), notes:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, FINANCE, MANAGER - Response: `201` `ChargeResponse` - Validation/guard checks: - 400: amount must be > 0 - 400: Invalid timestamp - 400: Unknown charge type - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (amount must be > 0; Invalid timestamp) ## `src/main/kotlin/com/android/trisolarisserver/controller/payment/Payments.kt` ### `GET /properties/{propertyId}/bookings/{bookingId}/payments` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/payment/Payments.kt:86`) - Behavior: List resources (list). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/payments` - Handler: `create` (`src/main/kotlin/com/android/trisolarisserver/controller/payment/Payments.kt:47`) - Behavior: Create resource (create). - Path params: propertyId:UUID, bookingId:UUID - Body: PaymentCreateRequest { amount:Long, method:String? (optional), currency:String? (optional), reference:String? (optional), notes:String? (optional), receivedAt:String? (optional) } - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `201` `PaymentResponse` - Validation/guard checks: - 400: amount must be > 0 - 400: Invalid timestamp - 400: Unknown payment method - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found; Booking not found), 400 (amount must be > 0; Invalid timestamp) ### `DELETE /properties/{propertyId}/bookings/{bookingId}/payments/{paymentId}` - Handler: `delete` (`src/main/kotlin/com/android/trisolarisserver/controller/payment/Payments.kt:103`) - Behavior: Delete resource (delete). - Path params: propertyId:UUID, bookingId:UUID, paymentId:UUID - Body: none - Side effects: Emits booking SSE updates. - Auth: Roles: ADMIN - Response: `204` `Unit` - Validation/guard checks: - 400: Cash payments can only be deleted for OPEN or CHECKED_IN bookings - 400: Only CASH payments can be deleted - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Cash payments can only be deleted for OPEN or CHECKED_IN bookings; Only CASH payments can be deleted) ## `src/main/kotlin/com/android/trisolarisserver/controller/property/CancellationPolicies.kt` ### `GET /properties/{propertyId}/cancellation-policy` - Handler: `get` (`src/main/kotlin/com/android/trisolarisserver/controller/property/CancellationPolicies.kt:34`) - Behavior: Get resource (get). - Path params: propertyId:UUID - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `CancellationPolicyResponse` - Common errors: none observed in controller checks ### `PUT /properties/{propertyId}/cancellation-policy` - Handler: `upsert` (`src/main/kotlin/com/android/trisolarisserver/controller/property/CancellationPolicies.kt:53`) - Behavior: Upsert. - Path params: propertyId:UUID - Body: CancellationPolicyUpsertRequest { freeDaysBeforeCheckin:Int (optional), penaltyMode:String } - Auth: Roles: ADMIN - Response: `200` `CancellationPolicyResponse` - Validation/guard checks: - 400: freeDaysBeforeCheckin must be >= 0 - 400: Unknown penaltyMode - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found), 400 (freeDaysBeforeCheckin must be >= 0; Unknown penaltyMode) ## `src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt` ### `GET /properties` - Handler: `listProperties` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:92`) - Behavior: List resources (list properties). - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `List` - Common errors: 401 (User not found) ### `POST /properties` - Handler: `createProperty` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:53`) - Behavior: Create resource (create property). - Body: PropertyCreateRequest { name:String, addressText:String? (optional), timezone:String? (optional), currency:String? (optional), billingCheckinTime:String? (optional), billingCheckoutTime:String? (optional), active:Boolean? (optional), otaAliases:Set? (optional), emailAddresses:Set? (optional), allowedTransportModes:Set? (optional) } - Auth: Roles: ADMIN - Response: `201` `PropertyResponse` - Validation/guard checks: - 400: Unknown transport mode - 400: $fieldName must be HH:mm - 409: Unable to generate property code - Common errors: 401 (User id missing; User not found), 400 (Unknown transport mode; $fieldName must be HH:mm), 409 (Unable to generate property code) ### `PUT /properties/{propertyId}` - Handler: `updateProperty` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:306`) - Behavior: Update resource (update property). - Path params: propertyId:UUID - Body: PropertyUpdateRequest { code:String, name:String, addressText:String? (optional), timezone:String? (optional), currency:String? (optional), billingCheckinTime:String? (optional), billingCheckoutTime:String? (optional), active:Boolean? (optional), otaAliases:Set? (optional), emailAddresses:Set? (optional), allowedTransportModes:Set? (optional) } - Auth: Roles: ADMIN - Response: `200` `PropertyResponse` - Validation/guard checks: - 400: Unknown transport mode - 400: $fieldName must be HH:mm - 409: Property code already exists - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (Unknown transport mode; $fieldName must be HH:mm), 409 (Property code already exists) ### `GET /properties/{propertyId}/billing-policy` - Handler: `getBillingPolicy` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:118`) - Behavior: Get resource (get billing policy). - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `PropertyBillingPolicyResponse` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found) ### `PUT /properties/{propertyId}/billing-policy` - Handler: `updateBillingPolicy` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:135`) - Behavior: Update resource (update billing policy). - Path params: propertyId:UUID - Body: PropertyBillingPolicyRequest { billingCheckinTime:String, billingCheckoutTime:String } - Auth: Roles: ADMIN - Response: `200` `PropertyBillingPolicyResponse` - Validation/guard checks: - 400: $fieldName must be HH:mm - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 ($fieldName must be HH:mm) ### `GET /properties/{propertyId}/code` - Handler: `getPropertyCode` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:105`) - Behavior: Get resource (get property code). - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `PropertyCodeResponse` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found) ### `GET /properties/{propertyId}/users` - Handler: `listPropertyUsers` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:165`) - Behavior: List resources (list property users). - Path params: propertyId:UUID - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, GUIDE, HOUSEKEEPING, MANAGER, STAFF, SUPERVISOR - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `DELETE /properties/{propertyId}/users/{userId}` - Handler: `deletePropertyUser` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:290`) - Behavior: Delete resource (delete property user). - Path params: propertyId:UUID, userId:UUID - Body: none - Auth: Roles: ADMIN - Response: `204` `Unit` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `PUT /properties/{propertyId}/users/{userId}/disabled` - Handler: `updatePropertyUserDisabled` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:240`) - Behavior: Update resource (update property user disabled). - Path params: propertyId:UUID, userId:UUID - Body: PropertyUserDisableRequest { disabled:Boolean } - Auth: Roles: ADMIN, AGENT, FINANCE, GUIDE, HOUSEKEEPING, MANAGER, STAFF, SUPERVISOR - Response: `200` `PropertyUserResponse` - Common errors: 401 (Missing principal), 403 (Role not allowed; Property membership required), 404 (User not found in property) ### `PUT /properties/{propertyId}/users/{userId}/roles` - Handler: `upsertPropertyUserRoles` (`src/main/kotlin/com/android/trisolarisserver/controller/property/Properties.kt:189`) - Behavior: Upsert property user roles. - Path params: propertyId:UUID, userId:UUID - Body: PropertyUserRoleRequest { roles:Set } - Auth: Roles: ADMIN, AGENT, MANAGER, STAFF - Response: `200` `PropertyUserResponse` - Validation/guard checks: - 400: Unknown role - Common errors: 401 (Missing principal), 403 (Missing role; Role not allowed), 404 (Property not found; User not found), 400 (Unknown role) ## `src/main/kotlin/com/android/trisolarisserver/controller/property/PropertyAccessCodes.kt` ### `POST /properties/access-codes/join` - Handler: `joinWithAccessCode` (`src/main/kotlin/com/android/trisolarisserver/controller/property/PropertyAccessCodes.kt:91`) - Behavior: Join with access code. - Body: PropertyAccessCodeJoinRequest { propertyCode:String? (optional), propertyId:String? (optional), code:String } - Auth: Authenticated user (Firebase) - Response: `200` `PropertyUserResponse` - Validation/guard checks: - 400: Property code required - 409: User already a member - Common errors: 401 (User not found; Missing principal), 404 (Invalid code; Property not found), 400 (Property code required), 409 (User already a member) ### `POST /properties/{propertyId}/access-codes` - Handler: `createAccessCode` (`src/main/kotlin/com/android/trisolarisserver/controller/property/PropertyAccessCodes.kt:45`) - Behavior: Create resource (create access code). - Path params: propertyId:UUID - Body: PropertyAccessCodeCreateRequest { roles:Set } - Auth: Roles: ADMIN - Response: `201` `PropertyAccessCodeResponse` - Validation/guard checks: - 400: ADMIN cannot be invited by code - 400: At least one role is required - 400: Unknown role - 409: Unable to generate code, try again - Common errors: 401 (User not found; Missing principal), 403 (Property membership required), 404 (Property not found), 400 (ADMIN cannot be invited by code; At least one role is required), 409 (Unable to generate code, try again) ## `src/main/kotlin/com/android/trisolarisserver/controller/property/UserDirectory.kt` ### `GET /properties/{propertyId}/users/search` - Handler: `searchPropertyUsers` (`src/main/kotlin/com/android/trisolarisserver/controller/property/UserDirectory.kt:50`) - Behavior: Search property users. - Path params: propertyId:UUID - Query params: phone:String? (optional) - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, GUIDE, HOUSEKEEPING, MANAGER, STAFF, SUPERVISOR - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `GET /users` - Handler: `listAppUsers` (`src/main/kotlin/com/android/trisolarisserver/controller/property/UserDirectory.kt:27`) - Behavior: List resources (list app users). - Query params: phone:String? (optional) - Body: none - Auth: SUPER_ADMIN - Response: `200` `List` - Common errors: 401 (User not found), 403 (Super admin only) ## `src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt` ### `GET /properties/{propertyId}/rate-plans` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:79`) - Behavior: List resources (list). - Path params: propertyId:UUID - Query params: roomTypeCode:String? (optional) - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `POST /properties/{propertyId}/rate-plans` - Handler: `create` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:51`) - Behavior: Create resource (create). - Path params: propertyId:UUID - Body: RatePlanCreateRequest { code:String, name:String, roomTypeCode:String, baseRate:Long, currency:String? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `201` `RatePlanResponse` - Validation/guard checks: - 409: Rate plan code already exists for room type - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found; Room type not found), 409 (Rate plan code already exists for room type) ### `DELETE /properties/{propertyId}/rate-plans/{ratePlanId}` - Handler: `delete` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:114`) - Behavior: Delete resource (delete). - Path params: propertyId:UUID, ratePlanId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Rate plan not found) ### `PUT /properties/{propertyId}/rate-plans/{ratePlanId}` - Handler: `update` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:95`) - Behavior: Update resource (update). - Path params: propertyId:UUID, ratePlanId:UUID - Body: RatePlanUpdateRequest { name:String, baseRate:Long, currency:String? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `200` `RatePlanResponse` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Rate plan not found) ### `GET /properties/{propertyId}/rate-plans/{ratePlanId}/calendar` - Handler: `listCalendar` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:170`) - Behavior: List resources (list calendar). - Path params: propertyId:UUID, ratePlanId:UUID - Query params: from:String (required), to:String (required) - Body: none - Auth: Any property member - Response: `200` `RateCalendarAverageResponse` - Validation/guard checks: - 400: to must be on/after from - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Rate plan not found), 400 (to must be on/after from; Invalid date format) ### `POST /properties/{propertyId}/rate-plans/{ratePlanId}/calendar` - Handler: `upsertCalendar` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:129`) - Behavior: Upsert calendar. - Path params: propertyId:UUID, ratePlanId:UUID - Body: RateCalendarRangeUpsertRequest { from:String, to:String, rate:Long } - Auth: Roles: ADMIN, MANAGER - Response: `201` `List` - Validation/guard checks: - 400: to must be on/after from - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Rate plan not found), 400 (to must be on/after from; Invalid date format) ### `DELETE /properties/{propertyId}/rate-plans/{ratePlanId}/calendar/{rateDate}` - Handler: `deleteCalendar` (`src/main/kotlin/com/android/trisolarisserver/controller/rate/RatePlans.kt:206`) - Behavior: Delete resource (delete calendar). - Path params: propertyId:UUID, ratePlanId:UUID, rateDate:String - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Validation/guard checks: - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Rate plan not found), 400 (Invalid date format) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayPaymentLinksController.kt` ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/payment-link` - Handler: `createPaymentLink` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayPaymentLinksController.kt:47`) - Behavior: Create resource (create payment link). - Path params: propertyId:UUID, bookingId:UUID - Body: RazorpayPaymentLinkCreateRequest { amount:Long? (optional), isPartialPaymentAllowed:Boolean? (optional), minAmountForCustomer:Long? (optional), description:String? (optional), expiryDate:String? (optional), successUrl:String? (optional), failureUrl:String? (optional), viaEmail:Boolean? (optional), viaSms:Boolean? (optional) } - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayPaymentLinkCreateResponse` - Validation/guard checks: - 400: Booking is not active - 400: Razorpay settings not configured - 400: amount must be > 0 - 400: Razorpay test keys not configured - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Booking is not active; Razorpay settings not configured), 502 (Razorpay request failed) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayPaymentRequestsController.kt` ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/close` - Handler: `closeRequest` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayPaymentRequestsController.kt:93`) - Behavior: Close request. - Path params: propertyId:UUID, bookingId:UUID - Body: RazorpayPaymentRequestCloseRequest { qrId:String? (optional), paymentLinkId:String? (optional) } - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayPaymentRequestCloseResponse` - Validation/guard checks: - 400: Provide exactly one of qrId or paymentLinkId - 400: Razorpay settings not configured - 400: Razorpay test keys not configured - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Provide exactly one of qrId or paymentLinkId; Razorpay settings not configured), 502 (Razorpay close request failed; Razorpay cancel request failed) ### `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/requests` - Handler: `listRequests` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayPaymentRequestsController.kt:45`) - Behavior: List resources (list requests). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt` ### `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr` - Handler: `listQr` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:292`) - Behavior: List resources (list qr). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr` - Handler: `createQr` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:56`) - Behavior: Create resource (create qr). - Path params: propertyId:UUID, bookingId:UUID - Body: RazorpayQrGenerateRequest { amount:Long? (optional), customerName:String? (optional), customerEmail:String? (optional), customerPhone:String? (optional), expiryMinutes:Int? (optional), expirySeconds:Int? (optional) } - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayQrGenerateResponse` - Validation/guard checks: - 400: Booking is not active - 400: Razorpay settings not configured - 400: amount must be > 0 - 400: Razorpay test keys not configured - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Booking is not active; Razorpay settings not configured), 502 (Razorpay request failed) ### `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/active` - Handler: `getActiveQr` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:157`) - Behavior: Get resource (get active qr). - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayQrGenerateResponse?` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property) ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/close` - Handler: `closeActiveQr` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:180`) - Behavior: Close active qr. - Path params: propertyId:UUID, bookingId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayQrGenerateResponse?` - Validation/guard checks: - 400: Razorpay settings not configured - 400: Razorpay test keys not configured - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Razorpay settings not configured; Razorpay test keys not configured), 502 (Razorpay close request failed) ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/close` - Handler: `closeQrById` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:212`) - Behavior: Close qr by id. - Path params: propertyId:UUID, bookingId:UUID, qrId:String - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `RazorpayQrGenerateResponse?` - Validation/guard checks: - 400: Razorpay settings not configured - 400: Razorpay test keys not configured - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (Razorpay settings not configured; Razorpay test keys not configured), 502 (Razorpay close request failed) ### `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/events` - Handler: `qrEvents` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:244`) - Behavior: Qr events. - Path params: propertyId:UUID, qrId:String - Body: none - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Required property role not granted) ### `GET /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/qr/{qrId}/events/stream` - Handler: `streamQrEvents` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayQrPayments.kt:273`) - Behavior: Stream events/data (stream qr events). - Path params: propertyId:UUID, bookingId:UUID, qrId:String - Body: none - Side effects: Streams SSE events. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `SseEmitter` - Common errors: 401 (Missing principal), 403 (Required property role not granted) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayRefundsController.kt` ### `POST /properties/{propertyId}/bookings/{bookingId}/payments/razorpay/refund` - Handler: `refund` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayRefundsController.kt:42`) - Behavior: Refund. - Path params: propertyId:UUID, bookingId:UUID - Body: RazorpayRefundRequest { paymentId:UUID? (optional), amount:Long? (optional), notes:String? (optional) } - Auth: Roles: ADMIN, FINANCE, MANAGER - Response: `200` `RazorpayRefundResponse` - Validation/guard checks: - 400: paymentId is required - 400: amount must be <= payment amount - 400: Payment is missing gateway id - 400: Payment is not a Razorpay payment - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Booking not found; Booking not found for property), 400 (paymentId is required; amount must be <= payment amount), 502 (Razorpay refund request failed) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayReturnController.kt` ### `POST /properties/{propertyId}/razorpay/return/failure` - Handler: `failure` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayReturnController.kt:22`) - Behavior: Failure. - Path params: propertyId:UUID - Body: none - Auth: Public/unspecified - Response: `204` `Unit` - Common errors: none observed in controller checks ### `POST /properties/{propertyId}/razorpay/return/success` - Handler: `success` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayReturnController.kt:16`) - Behavior: Success. - Path params: propertyId:UUID - Body: none - Auth: Public/unspecified - Response: `204` `Unit` - Common errors: none observed in controller checks ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpaySettingsController.kt` ### `GET /properties/{propertyId}/razorpay-settings` - Handler: `getSettings` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpaySettingsController.kt:33`) - Behavior: Get resource (get settings). - Path params: propertyId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `200` `RazorpaySettingsResponse` - Common errors: 401 (Missing principal), 403 (Required property role not granted) ### `PUT /properties/{propertyId}/razorpay-settings` - Handler: `upsertSettings` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpaySettingsController.kt:57`) - Behavior: Upsert settings. - Path params: propertyId:UUID - Body: RazorpaySettingsUpsertRequest { keyId:String? (optional), keySecret:String? (optional), webhookSecret:String? (optional), keyIdTest:String? (optional), keySecretTest:String? (optional), webhookSecretTest:String? (optional), isTest:Boolean? (optional), merchantKey:String? (optional), salt32:String? (optional), salt256:String? (optional), useSalt256:Boolean? (optional) } - Auth: Roles: ADMIN - Response: `200` `RazorpaySettingsResponse` - Validation/guard checks: - 400: keyId and keySecret must be provided together - 400: keyIdTest and keySecretTest must be provided together - 400: keyId/keySecret required - 400: keyIdTest/keySecretTest required when isTest=true - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found), 400 (keyId and keySecret must be provided together; keyIdTest and keySecretTest must be provided together) ## `src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayWebhookCapture.kt` ### `POST /properties/{propertyId}/razorpay/webhook` - Handler: `capture` (`src/main/kotlin/com/android/trisolarisserver/controller/razorpay/RazorpayWebhookCapture.kt:53`) - Behavior: Capture. - Path params: propertyId:UUID - Body: String? - Side effects: Emits booking SSE updates. - Auth: Public/unspecified - Response: `204` `Unit` - Validation/guard checks: - 400: Razorpay settings not configured - 400: Webhook secret not configured - Common errors: 401 (Missing signature; Invalid signature), 404 (Property not found), 400 (Razorpay settings not configured; Webhook secret not configured) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomAmenities.kt` ### `GET /amenities` - Handler: `listAmenities` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomAmenities.kt:40`) - Behavior: List resources (list amenities). - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `List` - Common errors: 401 (Missing principal) ### `POST /amenities` - Handler: `createAmenity` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomAmenities.kt:49`) - Behavior: Create resource (create amenity). - Body: AmenityUpsertRequest { name:String, category:String? (optional), iconKey:String? (optional) } - Auth: SUPER_ADMIN - Response: `201` `AmenityResponse` - Validation/guard checks: - 400: Icon key not found - 409: Amenity already exists - Common errors: 401 (User not found), 403 (Super admin only), 400 (Icon key not found), 409 (Amenity already exists) ### `DELETE /amenities/{amenityId}` - Handler: `deleteAmenity` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomAmenities.kt:92`) - Behavior: Delete resource (delete amenity). - Path params: amenityId:UUID - Body: none - Auth: SUPER_ADMIN - Response: `204` `Unit` - Common errors: 401 (User not found), 403 (Super admin only), 404 (Amenity not found) ### `PUT /amenities/{amenityId}` - Handler: `updateAmenity` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomAmenities.kt:68`) - Behavior: Update resource (update amenity). - Path params: amenityId:UUID - Body: AmenityUpsertRequest { name:String, category:String? (optional), iconKey:String? (optional) } - Auth: SUPER_ADMIN - Response: `200` `AmenityResponse` - Validation/guard checks: - 400: Icon key not found - 409: Amenity already exists - Common errors: 401 (User not found), 403 (Super admin only), 404 (Amenity not found), 400 (Icon key not found), 409 (Amenity already exists) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImageTags.kt` ### `GET /image-tags` - Handler: `listTags` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImageTags.kt:35`) - Behavior: List resources (list tags). - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `List` - Common errors: none observed in controller checks ### `POST /image-tags` - Handler: `createTag` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImageTags.kt:43`) - Behavior: Create resource (create tag). - Body: RoomImageTagUpsertRequest { name:String } - Auth: SUPER_ADMIN - Response: `201` `RoomImageTagResponse` - Validation/guard checks: - 409: Tag already exists - Common errors: 401 (User not found), 403 (Super admin only), 409 (Tag already exists) ### `DELETE /image-tags/{tagId}` - Handler: `deleteTag` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImageTags.kt:74`) - Behavior: Delete resource (delete tag). - Path params: tagId:UUID - Body: none - Auth: SUPER_ADMIN - Response: `204` `Unit` - Common errors: 401 (User not found), 403 (Super admin only), 404 (Tag not found) ### `PUT /image-tags/{tagId}` - Handler: `updateTag` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImageTags.kt:56`) - Behavior: Update resource (update tag). - Path params: tagId:UUID - Body: RoomImageTagUpsertRequest { name:String } - Auth: SUPER_ADMIN - Response: `200` `RoomImageTagResponse` - Validation/guard checks: - 409: Tag already exists - Common errors: 401 (User not found), 403 (Super admin only), 404 (Tag not found), 409 (Tag already exists) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt` ### `GET /properties/{propertyId}/rooms/{roomId}/images` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:55`) - Behavior: List resources (list). - Path params: propertyId:UUID, roomId:UUID - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `List` - Common errors: 404 (Room not found) ### `POST /properties/{propertyId}/rooms/{roomId}/images` - Handler: `upload` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:85`) - Behavior: Upload. - Path params: propertyId:UUID, roomId:UUID - Query params: file:MultipartFile (required), tagIds:List? (optional) - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `201` `RoomImageResponse` - Validation/guard checks: - 400: File is empty - 409: Duplicate image for room - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Tag not found; Room not found), 400 (File is empty), 409 (Duplicate image for room) ### `PUT /properties/{propertyId}/rooms/{roomId}/images/reorder-room` - Handler: `reorderRoomImages` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:199`) - Behavior: Reorder room images. - Path params: propertyId:UUID, roomId:UUID - Body: RoomImageReorderRequest { imageIds:List } - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Validation/guard checks: - 400: Images do not belong to room - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Image not found; Room not found), 400 (Images do not belong to room) ### `PUT /properties/{propertyId}/rooms/{roomId}/images/reorder-room-type` - Handler: `reorderRoomTypeImages` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:227`) - Behavior: Reorder room type images. - Path params: propertyId:UUID, roomId:UUID - Body: RoomImageReorderRequest { imageIds:List } - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Validation/guard checks: - 400: Images do not belong to room type - 400: Images do not belong to property - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Image not found; Room not found), 400 (Images do not belong to room type; Images do not belong to property) ### `DELETE /properties/{propertyId}/rooms/{roomId}/images/{imageId}` - Handler: `delete` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:129`) - Behavior: Delete resource (delete). - Path params: propertyId:UUID, roomId:UUID, imageId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Image not found; Room not found), 500 (Failed to delete image files) ### `GET /properties/{propertyId}/rooms/{roomId}/images/{imageId}/file` - Handler: `file` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:257`) - Behavior: File. - Path params: propertyId:UUID, roomId:UUID, imageId:UUID - Query params: size:String (optional) - Body: none - Auth: Any property member - Response: `200` `ResponseEntity` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Image not found; File missing) ### `PUT /properties/{propertyId}/rooms/{roomId}/images/{imageId}/tags` - Handler: `updateTags` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomImages.kt:180`) - Behavior: Update resource (update tags). - Path params: propertyId:UUID, roomId:UUID, imageId:UUID - Body: RoomImageTagUpdateRequest { tagIds:Set } - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Image not found; Tag not found) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt` ### `GET /properties/{propertyId}/room-stays/active` - Handler: `listActiveRoomStays` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt:42`) - Behavior: List resources (list active room stays). - Path params: propertyId:UUID - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, GUIDE, HOUSEKEEPING, MANAGER, STAFF, SUPERVISOR - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Agents cannot view active stays; Property membership required) ### `POST /properties/{propertyId}/room-stays/{roomStayId}/void` - Handler: `voidRoomStay` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomStays.kt:78`) - Behavior: Void room stay. - Path params: propertyId:UUID, roomStayId:UUID - Body: RoomStayVoidRequest { reason:String? (optional) } - Side effects: Writes room-stay audit log. - Auth: Roles: ADMIN, MANAGER, STAFF - Response: `200` `Unit` - Validation/guard checks: - 409: Cannot void checked-out room stay - Common errors: 401 (Missing principal), 403 (Missing role; Cannot void stay after first payment), 404 (Room stay not found for property), 409 (Cannot void checked-out room stay) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypeImages.kt` ### `GET /properties/{propertyId}/room-types/{roomTypeCode}/images` - Handler: `listByRoomType` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypeImages.kt:29`) - Behavior: List resources (list by room type). - Path params: propertyId:UUID, roomTypeCode:String - Body: none - Auth: Public/unspecified - Response: `200` `List` - Common errors: 404 (Room type not found) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt` ### `GET /properties/{propertyId}/room-types` - Handler: `listRoomTypes` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt:49`) - Behavior: List resources (list room types). - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `POST /properties/{propertyId}/room-types` - Handler: `createRoomType` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt:107`) - Behavior: Create resource (create room type). - Path params: propertyId:UUID - Body: RoomTypeUpsertRequest { code:String, name:String, baseOccupancy:Int? (optional), maxOccupancy:Int? (optional), sqFeet:Int? (optional), bathroomSqFeet:Int? (optional), defaultRate:Long? (optional), active:Boolean? (optional), otaAliases:Set? (optional), amenityIds:Set? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `201` `RoomTypeResponse` - Validation/guard checks: - 409: Room type code already exists for property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found; Amenity not found), 409 (Room type code already exists for property) ### `GET /properties/{propertyId}/room-types/{roomTypeCode}/rate` - Handler: `resolveRate` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt:60`) - Behavior: Resolve rate. - Path params: propertyId:UUID, roomTypeCode:String - Query params: date:String (required), ratePlanCode:String? (optional) - Body: none - Auth: Any property member - Response: `200` `RateResolveResponse` - Validation/guard checks: - 400: Rate plan not for room type - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found; Room type not found), 400 (Rate plan not for room type; Invalid date format) ### `DELETE /properties/{propertyId}/room-types/{roomTypeId}` - Handler: `deleteRoomType` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt:189`) - Behavior: Delete resource (delete room type). - Path params: propertyId:UUID, roomTypeId:UUID - Body: none - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Room type not found) ### `PUT /properties/{propertyId}/room-types/{roomTypeId}` - Handler: `updateRoomType` (`src/main/kotlin/com/android/trisolarisserver/controller/room/RoomTypes.kt:153`) - Behavior: Update resource (update room type). - Path params: propertyId:UUID, roomTypeId:UUID - Body: RoomTypeUpsertRequest { code:String, name:String, baseOccupancy:Int? (optional), maxOccupancy:Int? (optional), sqFeet:Int? (optional), bathroomSqFeet:Int? (optional), defaultRate:Long? (optional), active:Boolean? (optional), otaAliases:Set? (optional), amenityIds:Set? (optional) } - Auth: Roles: ADMIN, MANAGER - Response: `200` `RoomTypeResponse` - Validation/guard checks: - 409: Room type code already exists for property - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Room type not found; Amenity not found), 409 (Room type code already exists for property) ## `src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt` ### `GET /properties/{propertyId}/rooms` - Handler: `listRooms` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:66`) - Behavior: List resources (list rooms). - Path params: propertyId:UUID - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `POST /properties/{propertyId}/rooms` - Handler: `createRoom` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:278`) - Behavior: Create resource (create room). - Path params: propertyId:UUID - Body: RoomUpsertRequest { roomNumber:Int, floor:Int? (optional), roomTypeCode:String, hasNfc:Boolean, active:Boolean, maintenance:Boolean, notes:String? (optional) } - Side effects: Emits room board SSE updates. - Auth: Roles: ADMIN - Response: `201` `RoomResponse` - Validation/guard checks: - 400: roomTypeCode required - 409: Room number already exists for property - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Property not found; Room type not found), 400 (roomTypeCode required), 409 (Room number already exists for property) ### `GET /properties/{propertyId}/rooms/availability` - Handler: `roomAvailability` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:126`) - Behavior: Room availability. - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `GET /properties/{propertyId}/rooms/availability-range` - Handler: `roomAvailabilityRange` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:184`) - Behavior: Room availability range. - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Validation/guard checks: - 400: Invalid date range - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (Invalid date range; Invalid date format) ### `GET /properties/{propertyId}/rooms/available` - Handler: `availableRooms` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:147`) - Behavior: Available rooms. - Path params: propertyId:UUID - Body: none - Auth: Authenticated user (Firebase) - Response: `200` `List` - Common errors: none observed in controller checks ### `GET /properties/{propertyId}/rooms/available-range-with-rate` - Handler: `availableRoomsWithRate` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:221`) - Behavior: Available rooms with rate. - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Validation/guard checks: - 400: Invalid date range - 400: Invalid date format - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Property not found), 400 (Invalid date range; Invalid date format) ### `GET /properties/{propertyId}/rooms/board` - Handler: `roomBoard` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:86`) - Behavior: Room board. - Path params: propertyId:UUID - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `GET /properties/{propertyId}/rooms/board/stream` - Handler: `roomBoardStream` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:114`) - Behavior: Room board stream. - Path params: propertyId:UUID - Body: none - Side effects: Streams SSE events. - Auth: Any property member - Response: `200` `SseEmitter` - Common errors: 401 (Missing principal), 403 (Property membership required) ### `GET /properties/{propertyId}/rooms/by-type/{roomTypeCode}` - Handler: `roomsByType` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:160`) - Behavior: Rooms by type. - Path params: propertyId:UUID, roomTypeCode:String - Body: none - Auth: Roles: ADMIN, AGENT, FINANCE, HOUSEKEEPING, MANAGER, STAFF - Response: `200` `List` - Common errors: 404 (Room type not found) ### `DELETE /properties/{propertyId}/rooms/{roomId}` - Handler: `deleteRoom` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:367`) - Behavior: Delete resource (delete room). - Path params: propertyId:UUID, roomId:UUID - Body: none - Side effects: Emits room board SSE updates. - Auth: Roles: ADMIN, MANAGER - Response: `204` `Unit` - Validation/guard checks: - 409: Cannot delete room with stays - Common errors: 401 (Missing principal), 403 (Property membership required), 404 (Room not found for property), 409 (Cannot delete room with stays), 500 (Failed to delete room image files) ### `PUT /properties/{propertyId}/rooms/{roomId}` - Handler: `updateRoom` (`src/main/kotlin/com/android/trisolarisserver/controller/room/Rooms.kt:323`) - Behavior: Update resource (update room). - Path params: propertyId:UUID, roomId:UUID - Body: RoomUpsertRequest { roomNumber:Int, floor:Int? (optional), roomTypeCode:String, hasNfc:Boolean, active:Boolean, maintenance:Boolean, notes:String? (optional) } - Side effects: Emits room board SSE updates. - Auth: Roles: ADMIN - Response: `200` `RoomResponse` - Validation/guard checks: - 400: roomTypeCode required - 409: Room number already exists for property - Common errors: 401 (Missing principal), 403 (Required property role not granted), 404 (Room not found for property; Room type not found), 400 (roomTypeCode required), 409 (Room number already exists for property) ## `src/main/kotlin/com/android/trisolarisserver/controller/system/Health.kt` ### `GET /` - Handler: `root` (`src/main/kotlin/com/android/trisolarisserver/controller/system/Health.kt:14`) - Behavior: Root. - Body: none - Auth: Public/unspecified - Response: `200` `Map` - Common errors: none observed in controller checks ### `GET /health` - Handler: `health` (`src/main/kotlin/com/android/trisolarisserver/controller/system/Health.kt:9`) - Behavior: Health. - Body: none - Auth: Public/unspecified - Response: `200` `Map` - Common errors: none observed in controller checks ## `src/main/kotlin/com/android/trisolarisserver/controller/transport/TransportModes.kt` ### `GET /properties/{propertyId}/transport-modes` - Handler: `list` (`src/main/kotlin/com/android/trisolarisserver/controller/transport/TransportModes.kt:26`) - Behavior: List resources (list). - Path params: propertyId:UUID - Body: none - Auth: Any property member - Response: `200` `List` - Common errors: 401 (Missing principal), 403 (Property membership required)