Files
TrisolarisServer/docs/API_CATALOG.md
androidlover5842 35680287d4
All checks were successful
build-and-deploy / build-deploy (push) Successful in 17s
Generate behavior-first API catalog from controllers
2026-02-04 12:21:48 +05:30

1576 lines
76 KiB
Markdown

# 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<String>`
- 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<FileSystemResource>`
- 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<AuthResponse>`
- 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<AuthResponse>`
- 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<AuthResponse>`
- 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<BookingListItem>`
- 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<BookingCheckInStayRequest>, 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<BookingRoomRequestResponse>`
- 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<IssuedCardResponse>`
- 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<FileSystemResource>`
- 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<GuestDocumentResponse>`
- 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<FileSystemResource>`
- 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<GuestRatingResponse>`
- 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<GuestResponse>`
- 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<FileSystemResource>`
- 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<ChargeResponse>`
- 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<PaymentResponse>`
- 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<PropertyResponse>`
- 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<String>? (optional), emailAddresses:Set<String>? (optional), allowedTransportModes:Set<String>? (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<String>? (optional), emailAddresses:Set<String>? (optional), allowedTransportModes:Set<String>? (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<PropertyUserResponse>`
- 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<String> }
- 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<String> }
- 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<PropertyUserDetailsResponse>`
- 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<AppUserSummaryResponse>`
- 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<RatePlanResponse>`
- 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<RateCalendarResponse>`
- 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<RazorpayPaymentRequestResponse>`
- 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<RazorpayQrRecordResponse>`
- 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<RazorpayQrEventResponse>`
- 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<AmenityResponse>`
- 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<RoomImageTagResponse>`
- 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<RoomImageResponse>`
- 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<UUID>? (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<UUID> }
- 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<UUID> }
- 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<FileSystemResource>`
- 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<UUID> }
- 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<ActiveRoomStayResponse>`
- 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<RoomImageResponse>`
- 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<RoomTypeResponse>`
- 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<String>? (optional), amenityIds:Set<UUID>? (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<String>? (optional), amenityIds:Set<UUID>? (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<RoomResponse>`
- 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<RoomAvailabilityResponse>`
- 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<RoomAvailabilityRangeResponse>`
- 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<RoomResponse>`
- 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<RoomAvailabilityWithRateResponse>`
- 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<RoomBoardResponse>`
- 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<RoomResponse>`
- 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<String, String>`
- 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<String, String>`
- 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<TransportModeStatusResponse>`
- Common errors: 401 (Missing principal), 403 (Property membership required)