PROJECT CONTEXT / SYSTEM BRIEF This is a hotel-grade Property Management System (PMS) being rebuilt from scratch. This AGENTS file captures product rules + current codebase state. Tech stack - Spring Boot monolith - Kotlin only - JPA / Hibernate - PostgreSQL - Flyway deps present but disabled (no migrations during dev) - Single API domain api.hoteltrisolaris.in Server specs (current) - CPU: i5-8400 - RAM: 48 GB - GPU: RTX 3060 (used for llama.cpp) Core principles - Server is source of truth; clients send intent. - Ledger-based design: never store totals; append rows only. - Occupancy = RoomStay. Billing = Charge. Payments = Payment. Invoices are derived. - Room availability by room number; toAt=null means occupied. - Room change = close old RoomStay + open new one. - Multi-property: every domain object scoped to property_id. - AppUser is global; access granted per property. Immutable rules - Use Kotlin only; no microservices. - Flyway must remain disabled until schema stabilizes. - Canonical staff roles: ADMIN, MANAGER, STAFF, HOUSEKEEPING, FINANCE. - Booking does not own rooms or money. - Realtime events must be derived, not raw DB changes. - Ask before touching auth or payment logic. =============================================================================== CURRENT CODEBASE UNDERSTANDING (TrisolarisServer) =============================================================================== Repository - Root: /home/androidlover5842/IdeaProjects/TrisolarisServer - Entry: src/main/kotlin/com/android/trisolarisserver/TrisolarisServerApplication.kt - Scheduling enabled (@EnableScheduling) - Package layout (domain subpackages; keep top-level grouping): - controller/{auth,booking,guest,room,rate,property,payment,card,email,document,common,system,assets,transport,razorpay} - controller/dto/{booking,guest,payment,property,rate,room,razorpay} - repo/{booking,guest,room,rate,property,card,email,razorpay} - component/{ai,auth,booking,document,geo,room,sse,storage,razorpay} - config/{core,db,booking,room,rate,guest,payment,card,razorpay} - service/email Security/Auth - Firebase Admin auth for every request; Firebase UID required. - /auth/verify and /auth/me. Domain entities - Property: code, name, addressText, emailAddresses, otaAliases, allowedTransportModes. - AppUser (global, superAdmin), PropertyUser (roles per property). - RoomType: code/name/occupancy + otaAliases + defaultRate. - Room: roomNumber, floor, hasNfc, active, maintenance, notes. - Booking: status, expected check-in/out, emailAuditPdfUrl, transportMode. - Guest (property-scoped). - RoomStay (rate fields stored on stay). - RoomStayChange (idempotent room move). - IssuedCard (cardId, cardIndex, issuedAt, expiresAt, issuedBy, revokedAt). - PropertyCardCounter (per-property cardIndex counter). - RatePlan + RateCalendar. - Payment (ledger). - GuestDocument (files + AI-extracted json). - GuestVehicle (property-scoped vehicle numbers). - InboundEmail (audit PDF + raw EML, extracted json, status). - RoomImage (original + thumbnail). Key modules Auth - /auth/verify - /auth/me Properties / Users - POST /properties (creator becomes ADMIN on that property) - GET /properties (super admin gets all; others get memberships) - PUT /properties/{propertyId} - GET /properties/{propertyId}/users - PUT /properties/{propertyId}/users/{userId}/roles - DELETE /properties/{propertyId}/users/{userId} (ADMIN only) Rooms / inventory - /properties/{propertyId}/rooms - /properties/{propertyId}/rooms/board - /properties/{propertyId}/rooms/board/stream (SSE) - /properties/{propertyId}/rooms/availability - /properties/{propertyId}/rooms/availability-range?from=YYYY-MM-DD&to=YYYY-MM-DD - Public availability: - GET /properties/{propertyId}/rooms/available - GET /properties/{propertyId}/rooms/by-type/{roomTypeCode}?availableOnly=true|false Room types - POST /properties/{propertyId}/room-types - GET /properties/{propertyId}/room-types - GET /properties/{propertyId}/room-types/{roomTypeCode}/rate?date=YYYY-MM-DD&ratePlanCode=optional - PUT /properties/{propertyId}/room-types/{roomTypeId} - DELETE /properties/{propertyId}/room-types/{roomTypeId} Properties - Property create/update accepts addressText, otaAliases, emailAddresses, allowedTransportModes. Booking flow - POST /properties/{propertyId}/bookings (create booking) - /properties/{propertyId}/bookings/{bookingId}/check-in/bulk (creates RoomStay rows with per-stay rates) - /properties/{propertyId}/bookings/{bookingId}/check-out (closes RoomStay) - /properties/{propertyId}/bookings/{bookingId}/room-stays/{roomStayId}/check-out (closes specific stay; single-stay booking auto-closes booking) - /properties/{propertyId}/bookings/{bookingId}/cancel - /properties/{propertyId}/bookings/{bookingId}/no-show - /properties/{propertyId}/bookings/{bookingId}/room-requests (room-type quantity reservation) - /properties/{propertyId}/bookings/{bookingId}/room-requests/{requestId} (cancel reservation) - /properties/{propertyId}/room-stays/{roomStayId}/void (soft-void active stay) - /properties/{propertyId}/cancellation-policy (get/update policy) Card issuing - /properties/{propertyId}/room-stays/{roomStayId}/cards/prepare -> returns cardIndex + sector0 payload - /properties/{propertyId}/room-stays/{roomStayId}/cards -> store issued card - /properties/{propertyId}/room-stays/{roomStayId}/cards (list) - /properties/{propertyId}/room-stays/cards/{cardIndex}/revoke (ADMIN; MANAGER allowed only for temp cards) - Temp cards (room-only, 7 min expiry): - POST /properties/{propertyId}/rooms/{roomId}/cards/prepare-temp - POST /properties/{propertyId}/rooms/{roomId}/cards/temp Guest APIs - POST /properties/{propertyId}/guests - /properties/{propertyId}/guests/search?phone=... or ?vehicleNumber=... - /properties/{propertyId}/guests/{guestId}/vehicles (add vehicle) - POST /properties/{propertyId}/guests/{guestId}/signature - GET /properties/{propertyId}/guests/{guestId}/signature/file Room stays - POST /properties/{propertyId}/room-stays/{roomStayId}/change-rate Rate plans - POST /properties/{propertyId}/rate-plans - GET /properties/{propertyId}/rate-plans - PUT /properties/{propertyId}/rate-plans/{ratePlanId} - DELETE /properties/{propertyId}/rate-plans/{ratePlanId} - POST /properties/{propertyId}/rate-plans/{ratePlanId}/calendar - GET /properties/{propertyId}/rate-plans/{ratePlanId}/calendar?from=YYYY-MM-DD&to=YYYY-MM-DD - DELETE /properties/{propertyId}/rate-plans/{ratePlanId}/calendar/{rateDate} Payments - POST /properties/{propertyId}/bookings/{bookingId}/payments - GET /properties/{propertyId}/bookings/{bookingId}/payments - GET /properties/{propertyId}/bookings/{bookingId}/balance - DELETE /properties/{propertyId}/bookings/{bookingId}/payments/{paymentId} (ADMIN/super admin; CASH only; booking OPEN or CHECKED_IN) Guest documents - /properties/{propertyId}/guests/{guestId}/documents (upload/list) - /properties/{propertyId}/guests/{guestId}/documents/{documentId}/file - AI extraction with strict system prompt. - DELETE /properties/{propertyId}/guests/{guestId}/documents/{documentId} (ADMIN/MANAGER; booking OPEN or CHECKED_IN; deletes file + row) - Document file endpoint accepts Firebase auth (ADMIN/MANAGER) or token query param. - AI extraction is queued (single-thread) to limit concurrency. - storage.documents.aiBaseUrl supported for llama fetch (defaults to publicBaseUrl; use http for llama). Room images - /properties/{propertyId}/rooms/{roomId}/images (upload/list) - /properties/{propertyId}/rooms/{roomId}/images/{imageId}/file - Thumbnails generated (320px). Transport modes - /properties/{propertyId}/transport-modes -> returns enabled list (property or default all). Inbound email ingestion - IMAP poller (1 min) with enable flag. - Saves audit PDF + raw .eml under /home/androidlover5842/docs/emails. - Property match: To/CC email first; fallback to name/code/address/otaAliases. - AI extracts booking fields; creates/cancels Booking. - /properties/{propertyId}/inbound-emails/{emailId}/file (audit PDF) - POST /properties/{propertyId}/inbound-emails/manual (PDF upload) Realtime - SSE room board events with heartbeat, on room create/update, check-in/out, and room change. AI integration - Base URL per profile: dev=https://ai.hoteltrisolaris.in/v1/chat/completions, prod=http://localhost:8089/v1/chat/completions - LlamaClient uses strict system prompt (no guessing). - Read timeout 5 minutes. Config - storage.documents.root=/home/androidlover5842/docs - storage.emails.root=/home/androidlover5842/docs/emails - storage.rooms.root=/home/androidlover5842/docs/rooms - publicBaseUrl entries for docs/emails/rooms - mail.imap.enabled=false by default Notes / constraints - Users are created by app; API only manages roles. - Super admin can create properties and assign users to properties. - Admin can assign ADMIN/MANAGER/STAFF/AGENT; Manager can assign STAFF/AGENT. - Agents can only see free rooms. - Role hierarchy for visibility/management: SUPER_ADMIN > ADMIN > MANAGER > STAFF/HOUSEKEEPING/FINANCE/SUPERVISOR/GUIDE > AGENT. Users cannot see anyone above their rank in property user lists. Access code invites cannot assign ADMIN. - Property code is auto-generated (7-char random, no fixed prefix). Property create no longer accepts `code` in request. Join-by-code uses property code, not propertyId. - Property access codes: 6-digit PIN, 1-minute expiry, single-use. Admin generates; staff joins with property code + PIN. - Property user disable is property-scoped (not global); hierarchy applies for who can disable. - Room stay lifecycle: `RoomStay` now supports soft void (`is_voided`), and room-stay audit events are written to `room_stay_audit_log`. - Checkout supports both booking-level and specific room-stay checkout; specific checkout endpoint: `POST /properties/{propertyId}/bookings/{bookingId}/room-stays/{roomStayId}/check-out`. - Staff can change/void stays only before first payment on booking; manager/admin can act after payments. - Checkout validation: nightly rate must be within +/-20% of room type default rate (when default rate exists), and minimum stay duration must be at least 1 hour. - Room-type reservations: use booking room requests (`booking_room_request`) for quantity holds without room numbers; availability checks include active requests + occupied stays. - Cancellation policy engine (advance bookings): policy per property with `freeDaysBeforeCheckin` + `penaltyMode` (`NO_CHARGE`, `ONE_NIGHT`, `FULL_STAY`). On cancel/no-show, penalty charge ledger rows are auto-created (`CANCELLATION_PENALTY` / `NO_SHOW_PENALTY`) when within penalty window. Operational notes - Payment provider migrated: PayU removed; Razorpay now used for settings, QR, payment links, and webhooks. - Server access: SSH host alias `hotel` is available for server operations (e.g., `ssh hotel`). Use carefully; DB changes were done via `sudo -u postgres psql` on the server when needed. - Schema changes: schema fix classes have been removed. If a new column/table is required, apply it manually on the server using `ssh hotel` and `sudo -u postgres psql -d trisolaris`, e.g. `alter table ... add column ...`. Keep a note of the exact SQL applied. - Agent workflow expectation: when schema/runtime issues require server-side SQL or service checks, execute the required `ssh hotel` operations directly and report what was changed; do not block on asking for confirmation in normal flow. Access / ops notes (prod) - Service: `TrisolarisServer.service` (systemd). `systemctl cat TrisolarisServer.service`. - Deploy path: `/opt/deploy/TrisolarisServer` (runs `build/libs/*.jar`). - Active profile: `prod` (see service Environment=SPRING_PROFILES_ACTIVE=prod). - DB (prod): PostgreSQL `trisolaris` on `localhost:5432` (see `/opt/deploy/TrisolarisServer/src/main/resources/application-prod.properties` on the server). - DB (dev): PostgreSQL `trisolaris` on `192.168.1.53:5432` (see `application-dev.properties` in the repo). - DB access (server): `sudo -u postgres psql -d trisolaris`. - Workflow: Always run build, commit, and push for changes unless explicitly told otherwise. - API docs policy (mandatory): - For every API change (`add`, `update`, `delete`, path change, request/response change, role change, validation/error change), update `docs/API_REFERENCE.txt` in the same endpoint-by-endpoint text format already used there. - Keep each API block in this style: `" API is this one:"` -> `METHOD /path` -> `What it does` -> `Request body` -> `Allowed roles` -> `Error Codes`. - Script-generated API docs are forbidden. Documentation updates must be manual edits only. - Android workflow note: user always runs Shift+F10 in Android Studio to deploy updates.