603 lines
14 KiB
Markdown
603 lines
14 KiB
Markdown
# TrisolarisPMS API Usage
|
|
|
|
## API Docs Path
|
|
|
|
- `/home/androidlover5842/IdeaProjects/TrisolarisServer/docs`
|
|
|
|
## 1) Booking
|
|
|
|
### Create booking
|
|
|
|
POST /properties/{propertyId}/bookings
|
|
Auth: ADMIN/MANAGER/STAFF
|
|
|
|
Body (JSON)
|
|
Required:
|
|
|
|
- expectedCheckInAt (String, ISO-8601, required)
|
|
- expectedCheckOutAt (String, ISO-8601, required)
|
|
|
|
Optional:
|
|
|
|
- source (String, default "WALKIN")
|
|
- transportMode (String enum)
|
|
- adultCount (Int)
|
|
- totalGuestCount (Int)
|
|
- notes (String)
|
|
|
|
{
|
|
"source": "WALKIN",
|
|
"expectedCheckInAt": "2026-01-28T12:00:00+05:30",
|
|
"expectedCheckOutAt": "2026-01-29T10:00:00+05:30",
|
|
"transportMode": "CAR",
|
|
"adultCount": 2,
|
|
"totalGuestCount": 3,
|
|
"notes": "Late arrival"
|
|
}
|
|
|
|
Behavior
|
|
If expectedCheckInAt >= now(property timezone) -> booking becomes CHECKED_IN, and checkinAt is set, expected fields are null.
|
|
|
|
Response
|
|
|
|
{
|
|
"id": "uuid",
|
|
"status": "OPEN|CHECKED_IN",
|
|
"checkInAt": "2026-01-28T12:00:00+05:30" | null,
|
|
"expectedCheckInAt": "..." | null,
|
|
"expectedCheckOutAt": "..." | null
|
|
}
|
|
|
|
---
|
|
|
|
### List bookings
|
|
|
|
GET /properties/{propertyId}/bookings
|
|
Optional query param:
|
|
|
|
- status (comma-separated), e.g. status=OPEN,CHECKED_IN
|
|
|
|
Behavior:
|
|
- If status is omitted, returns all bookings for the property (newest first).
|
|
|
|
Response: List of BookingListItem with id, status, guestId, guestName, guestPhone, roomNumbers, source, times, counts, expectedGuestCount, notes.
|
|
|
|
Notes:
|
|
- It returns active room stays (toAt = null) for each booking.
|
|
|
|
---
|
|
|
|
### Booking details
|
|
|
|
GET /properties/{propertyId}/bookings/{bookingId}
|
|
|
|
Includes:
|
|
|
|
- Guest info (name/phone/nationality/address/signatureUrl)
|
|
- Room numbers (active stays if present; otherwise all stays)
|
|
- Travel fields (fromCity/toCity/memberRelation)
|
|
- Transport mode, expected/actual times
|
|
- Counts (male/female/child/total/expected)
|
|
- Registered by (createdBy name/phone)
|
|
- totalNightlyRate (sum of nightlyRate across shown rooms)
|
|
- balance: expectedPay, amountCollected, pending
|
|
|
|
---
|
|
|
|
### Check-in (creates RoomStay)
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/check-in
|
|
Auth: ADMIN/MANAGER
|
|
|
|
Body
|
|
Required:
|
|
|
|
- roomIds (List<UUID>)
|
|
|
|
Optional:
|
|
|
|
- checkInAt (String)
|
|
- transportMode (String enum)
|
|
- nightlyRate (Long)
|
|
- rateSource (MANUAL|RATE_PLAN|OTA)
|
|
- ratePlanCode (String)
|
|
- currency (String)
|
|
- notes (String)
|
|
|
|
{
|
|
"roomIds": ["uuid1","uuid2"],
|
|
"checkInAt": "2026-01-28T12:00:00+05:30",
|
|
"nightlyRate": 2500,
|
|
"rateSource": "MANUAL",
|
|
"ratePlanCode": "EP",
|
|
"currency": "INR",
|
|
"notes": "Late arrival"
|
|
}
|
|
|
|
---
|
|
|
|
### Pre-assign room stay
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/room-stays
|
|
Auth: ADMIN/MANAGER
|
|
|
|
Body
|
|
Required:
|
|
|
|
- roomId (UUID)
|
|
- fromAt (String)
|
|
- toAt (String)
|
|
|
|
Optional:
|
|
|
|
- nightlyRate (Long)
|
|
- rateSource (MANUAL|RATE_PLAN|OTA)
|
|
- ratePlanCode (String)
|
|
- currency (String)
|
|
- notes (String)
|
|
|
|
{
|
|
"roomId": "uuid",
|
|
"fromAt": "2026-01-29T12:00:00+05:30",
|
|
"toAt": "2026-01-30T10:00:00+05:30",
|
|
"nightlyRate": 2800,
|
|
"rateSource": "RATE_PLAN",
|
|
"ratePlanCode": "EP",
|
|
"currency": "INR"
|
|
}
|
|
|
|
---
|
|
|
|
### Active room stays
|
|
|
|
GET /properties/{propertyId}/room-stays/active
|
|
Auth: any member except AGENT-only
|
|
|
|
Response: list of ActiveRoomStayResponse
|
|
|
|
[
|
|
{
|
|
"roomStayId":"uuid",
|
|
"bookingId":"uuid",
|
|
"guestId":"uuid-or-null",
|
|
"guestName":"Name",
|
|
"guestPhone":"+9111...",
|
|
"roomId":"uuid",
|
|
"roomNumber":"101",
|
|
"roomTypeName":"DELUXE",
|
|
"fromAt":"2026-01-29T12:00:00+05:30",
|
|
"checkinAt":"2026-01-29T12:05:00+05:30",
|
|
"expectedCheckoutAt":"2026-01-30T10:00:00+05:30"
|
|
}
|
|
]
|
|
|
|
---
|
|
|
|
### Change room (move guest)
|
|
|
|
POST /properties/{propertyId}/room-stays/{roomStayId}/change-room
|
|
Auth: ADMIN/MANAGER/STAFF
|
|
|
|
Body
|
|
|
|
{
|
|
"newRoomId":"uuid",
|
|
"movedAt":"2026-01-30T15:00:00+05:30",
|
|
"idempotencyKey":"any-unique-string"
|
|
}
|
|
|
|
Response
|
|
|
|
{
|
|
"oldRoomStayId":"uuid",
|
|
"newRoomStayId":"uuid",
|
|
"oldRoomId":"uuid",
|
|
"newRoomId":"uuid",
|
|
"movedAt":"2026-01-30T15:00:00+05:30"
|
|
}
|
|
|
|
---
|
|
|
|
### Update expected dates
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/expected-dates
|
|
|
|
Rules:
|
|
|
|
- OPEN → can update expectedCheckInAt and/or expectedCheckOutAt
|
|
- CHECKED_IN → can update only expectedCheckOutAt
|
|
- CHECKED_OUT / CANCELLED / NO_SHOW → forbidden
|
|
|
|
Body
|
|
|
|
{
|
|
"expectedCheckInAt": "2026-01-29T12:00:00+05:30",
|
|
"expectedCheckOutAt": "2026-01-30T10:00:00+05:30"
|
|
}
|
|
|
|
---
|
|
|
|
## 2) Guests
|
|
|
|
### Create guest + link to booking
|
|
|
|
POST /properties/{propertyId}/guests
|
|
Auth: property member
|
|
Body (required):
|
|
|
|
- bookingId (UUID)
|
|
|
|
Optional:
|
|
|
|
- phoneE164 (String)
|
|
- name (String)
|
|
- nationality (String)
|
|
- addressText (String)
|
|
|
|
{
|
|
"bookingId": "uuid",
|
|
"phoneE164": "+911111111111",
|
|
"name": "John",
|
|
"nationality": "IN",
|
|
"addressText": "Varanasi"
|
|
}
|
|
|
|
Behavior:
|
|
|
|
- If phone already exists -> links existing guest to booking and returns it.
|
|
- If booking already has a guest -> 409.
|
|
|
|
Response (GuestResponse)
|
|
|
|
{
|
|
"id": "uuid",
|
|
"name": "John",
|
|
"phoneE164": "+911111111111",
|
|
"nationality": "IN",
|
|
"addressText": "Varanasi",
|
|
"signatureUrl": "/properties/{propertyId}/guests/{guestId}/signature/file",
|
|
"vehicleNumbers": [],
|
|
"averageScore": null
|
|
}
|
|
|
|
---
|
|
|
|
### Add guest vehicle + link to booking
|
|
|
|
POST /properties/{propertyId}/guests/{guestId}/vehicles
|
|
Auth: property member
|
|
Body:
|
|
|
|
{ "vehicleNumber": "UP32AB1234", "bookingId": "uuid" }
|
|
|
|
---
|
|
|
|
### Upload signature (SVG only)
|
|
|
|
POST /properties/{propertyId}/guests/{guestId}/signature
|
|
Auth: ADMIN/MANAGER
|
|
Multipart:
|
|
|
|
- file (SVG)
|
|
|
|
---
|
|
|
|
### Download signature
|
|
|
|
GET /properties/{propertyId}/guests/{guestId}/signature/file
|
|
Auth: property member
|
|
Returns image/svg+xml.
|
|
|
|
---
|
|
|
|
### Search guest by phone
|
|
|
|
GET /properties/{propertyId}/guests/search?phone=+911111111111
|
|
|
|
---
|
|
|
|
## 3) Room Types (default rate + rate resolve)
|
|
|
|
### Room type create/update
|
|
|
|
Fields now include defaultRate:
|
|
|
|
RoomTypeUpsertRequest
|
|
|
|
{
|
|
"code": "DELUX",
|
|
"name": "Deluxe",
|
|
"baseOccupancy": 2,
|
|
"maxOccupancy": 3,
|
|
"sqFeet": 150,
|
|
"bathroomSqFeet": 30,
|
|
"defaultRate": 2500,
|
|
"active": true,
|
|
"otaAliases": [],
|
|
"amenityIds": []
|
|
}
|
|
|
|
### Resolve preset rate for date
|
|
|
|
GET /properties/{propertyId}/room-types/{roomTypeCode}/rate?date=YYYY-MM-DD&ratePlanCode=optional
|
|
Auth: public if no auth, or member
|
|
|
|
Response
|
|
|
|
{
|
|
"roomTypeCode": "DELUX",
|
|
"rateDate": "2026-02-01",
|
|
"rate": 2800,
|
|
"currency": "INR",
|
|
"ratePlanCode": "WEEKEND"
|
|
}
|
|
|
|
---
|
|
|
|
## 4) Rate Plans + Calendar
|
|
|
|
### Create rate plan
|
|
|
|
POST /properties/{propertyId}/rate-plans
|
|
Auth: ADMIN/MANAGER
|
|
|
|
Body
|
|
Required:
|
|
|
|
- code (String)
|
|
- name (String)
|
|
- roomTypeCode (String)
|
|
- baseRate (Long)
|
|
|
|
Optional:
|
|
|
|
- currency (String, default property currency)
|
|
|
|
{ "code":"WEEKEND", "name":"Weekend", "roomTypeCode":"DELUX", "baseRate":2800, "currency":"INR" }
|
|
|
|
Response RatePlanResponse
|
|
|
|
### List plans
|
|
|
|
GET /properties/{propertyId}/rate-plans?roomTypeCode=optional
|
|
Auth: member
|
|
|
|
### Update
|
|
|
|
PUT /properties/{propertyId}/rate-plans/{ratePlanId}
|
|
Body:
|
|
|
|
{ "name":"Weekend", "baseRate":3000, "currency":"INR" }
|
|
|
|
### Delete
|
|
|
|
DELETE /properties/{propertyId}/rate-plans/{ratePlanId}
|
|
|
|
### Calendar upsert (batch)
|
|
|
|
POST /properties/{propertyId}/rate-plans/{ratePlanId}/calendar
|
|
Body: Array
|
|
|
|
[
|
|
{ "rateDate":"2026-02-01", "rate":3200 },
|
|
{ "rateDate":"2026-02-02", "rate":3500 }
|
|
]
|
|
|
|
### Calendar list
|
|
|
|
GET /properties/{propertyId}/rate-plans/{ratePlanId}/calendar?from=YYYY-MM-DD&to=YYYY-MM-DD
|
|
|
|
### Calendar delete
|
|
|
|
DELETE /properties/{propertyId}/rate-plans/{ratePlanId}/calendar/{rateDate}
|
|
|
|
---
|
|
|
|
## 5) RoomStay rate change (mid-stay renegotiation)
|
|
|
|
POST /properties/{propertyId}/room-stays/{roomStayId}/change-rate
|
|
Auth: ADMIN/MANAGER
|
|
|
|
Body
|
|
Required:
|
|
|
|
- effectiveAt (String, ISO-8601)
|
|
- nightlyRate (Long)
|
|
- rateSource (MANUAL|RATE_PLAN|OTA)
|
|
|
|
Optional:
|
|
|
|
- ratePlanCode (String)
|
|
- currency (String)
|
|
|
|
{
|
|
"effectiveAt": "2026-01-30T12:00:00+05:30",
|
|
"nightlyRate": 2000,
|
|
"rateSource": "MANUAL",
|
|
"currency": "INR"
|
|
}
|
|
|
|
Response
|
|
|
|
{ "oldRoomStayId":"uuid", "newRoomStayId":"uuid", "effectiveAt":"..." }
|
|
|
|
---
|
|
|
|
### Check-out (closes all active stays on booking)
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/check-out
|
|
Auth: ADMIN/MANAGER
|
|
|
|
Body
|
|
|
|
{ "checkOutAt":"2026-01-30T10:00:00+05:30", "notes":"optional" }
|
|
|
|
Response: 204 No Content
|
|
|
|
---
|
|
|
|
### Bulk check-in (creates multiple room stays)
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/check-in/bulk
|
|
|
|
Body:
|
|
|
|
{
|
|
"stays": [
|
|
{
|
|
"roomId": "uuid",
|
|
"checkInAt": "2026-01-29T12:00:00+05:30",
|
|
"checkOutAt": "2026-01-30T10:00:00+05:30",
|
|
"nightlyRate": 6000,
|
|
"rateSource": "MANUAL",
|
|
"ratePlanCode": "EP",
|
|
"currency": "INR"
|
|
},
|
|
{
|
|
"roomId": "uuid",
|
|
"checkInAt": "2026-01-29T12:00:00+05:30",
|
|
"checkOutAt": "2026-01-30T10:00:00+05:30",
|
|
"nightlyRate": 8000,
|
|
"rateSource": "MANUAL",
|
|
"ratePlanCode": "EP",
|
|
"currency": "INR"
|
|
}
|
|
]
|
|
}
|
|
|
|
Behavior
|
|
|
|
- Creates one RoomStay per stay with its own rate.
|
|
- Sets booking CHECKED_IN, checkinAt = earliest stay check-in.
|
|
- If any checkOutAt provided, booking expectedCheckoutAt = latest of those.
|
|
- Rejects duplicate room IDs.
|
|
- Rejects invalid stay date range (checkOutAt <= checkInAt).
|
|
- Blocks occupied rooms.
|
|
|
|
## 6) Payments + Balance
|
|
|
|
### Add payment
|
|
|
|
POST /properties/{propertyId}/bookings/{bookingId}/payments
|
|
Auth: ADMIN/MANAGER/STAFF
|
|
|
|
Body
|
|
Required:
|
|
|
|
- amount (Long)
|
|
- method (CASH|CARD|UPI|BANK|ONLINE)
|
|
|
|
Optional:
|
|
|
|
- currency (String, default property currency)
|
|
- reference (String)
|
|
- notes (String)
|
|
- receivedAt (String)
|
|
|
|
{
|
|
"amount": 1200,
|
|
"method": "CASH",
|
|
"currency": "INR",
|
|
"reference": "RCP-123",
|
|
"notes": "Advance"
|
|
}
|
|
|
|
Response
|
|
|
|
{
|
|
"id":"uuid",
|
|
"bookingId":"uuid",
|
|
"amount":1200,
|
|
"currency":"INR",
|
|
"method":"CASH",
|
|
"reference":"RCP-123",
|
|
"notes":"Advance",
|
|
"receivedAt":"2026-01-28T12:00:00+05:30",
|
|
"receivedByUserId":"uuid"
|
|
}
|
|
|
|
### List payments
|
|
|
|
GET /properties/{propertyId}/bookings/{bookingId}/payments
|
|
|
|
### Booking balance
|
|
|
|
GET /properties/{propertyId}/bookings/{bookingId}/balance
|
|
|
|
{ "expectedPay": 2745, "amountCollected": 1200, "pending": 1545 }
|
|
|
|
---
|
|
|
|
## 7) Compose Notes
|
|
|
|
- Use `androidx.compose.foundation.text.KeyboardOptions` for keyboard options imports.
|
|
|
|
---
|
|
|
|
## 8) Engineering Structure & Anti-Boilerplate Rules
|
|
|
|
### Non-negotiable coding rules
|
|
|
|
- Duplicate code is forbidden.
|
|
- Never add duplicate business logic in multiple files.
|
|
- Never copy-paste permission checks, navigation decisions, mapping logic, or API call patterns.
|
|
- If similar logic appears 2+ times, extract shared function/class immediately.
|
|
- Prefer typed models/enums over raw strings for roles/status/flags.
|
|
- Keep files small and purpose-driven; split before a file becomes hard to scan.
|
|
|
|
### Required project structure (current baseline)
|
|
|
|
- `core/` -> cross-cutting business primitives/policies (e.g., auth policy, role enum).
|
|
- `core/viewmodel/` -> shared ViewModel execution helpers (loading/error wrappers, common request runners).
|
|
- `data/api/core/` -> API client, constants, token providers, aggregated API service.
|
|
- `data/api/service/` -> Retrofit endpoint interfaces only.
|
|
- `data/api/model/` -> DTO/request/response models.
|
|
- `ui/navigation/` -> route model, navigation orchestrators, back-navigation rules.
|
|
- `ui/<feature>/` -> screen + state + viewmodel for that feature.
|
|
|
|
### How to implement future logic (mandatory workflow)
|
|
|
|
1. Define/extend domain type first (enum/data model/policy) instead of raw literals.
|
|
2. Add/extend API contract in `data/api/service` and models in `data/api/model`.
|
|
3. Add shared logic once (policy/helper/mapper) in `core` or feature-common layer.
|
|
4. Keep ViewModel thin: orchestrate calls, state, and errors only.
|
|
5. Keep UI dumb: consume state and callbacks; avoid business rules in composables.
|
|
6. If navigation changes, update `ui/navigation` only (single source of truth).
|
|
7. Before finishing, remove any newly introduced duplication and compile-check.
|
|
8. If 2+ ViewModels repeat loading/error coroutine flow, extract/use shared helper in `core/viewmodel`.
|
|
9. If Add/Edit screens differ only by initialization + submit callback, extract a feature-local shared form screen.
|
|
10. Prefer dedupe/organization improvements even if net LOC does not decrease, as long as behavior remains unchanged.
|
|
|
|
### PR/refactor acceptance checklist
|
|
|
|
- No repeated role/permission checks across screens.
|
|
- No repeated model mapping blocks (extract mapper/helper).
|
|
- No giant god-file when it can be split by domain responsibility.
|
|
- Imports/packages follow the structure above.
|
|
- Build passes: `./gradlew :app:compileDebugKotlin`.
|
|
|
|
### Room DB synchronization rule (mandatory)
|
|
|
|
- For any editable API-backed field, follow this write path only: `UI action -> server request -> Room DB update -> UI reacts from Room observers`.
|
|
- Server is source of truth; do not bypass server by writing final business state directly from UI.
|
|
- UI must render from Room-backed state, not from one-off API responses or direct text mutation.
|
|
- Avoid forced full refresh after each mutation unless strictly required by backend limitations; prefer targeted Room updates for linked entities.
|
|
- On mutation failure, keep prior DB state unchanged and surface error state to UI.
|
|
|
|
### Guest Documents Authorization (mandatory)
|
|
|
|
- View access: `ADMIN`, `MANAGER` (and super admin).
|
|
- Modify access (upload/delete): allowed only when booking status is `OPEN` or `CHECKED_IN`.
|
|
- For `CHECKED_OUT`, `CANCELLED`, `NO_SHOW`: documents are read-only.
|
|
- Never couple guest document permissions with Razorpay/settings permissions.
|
|
|
|
### Permission design guardrail
|
|
|
|
- Do not reuse one feature's permission gate for another unrelated feature.
|
|
- Add explicit policy methods in `core/auth/AuthzPolicy` for each feature capability.
|
|
|
|
### Refactor safety rule
|
|
|
|
- Any package/file movement must include import updates in same change.
|
|
- After refactor, compile check is mandatory: `./gradlew :app:compileDebugKotlin`.
|