Files
TrisolarisPMS/AGENTS.md
androidlover5842 1000f2411c add room db
2026-02-08 19:21:07 +05:30

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`.