# PMS Integration Layer

This project uses a lightweight Python HTTP server, SQLite, and vanilla HTML/CSS/JS. The PMS layer follows the same stack and keeps PMS code separate from the guest/staff/admin UI.

## Architecture

`pms/base.py` defines the connector interface:

- `getGuestByRoom(room_id)`
- `getReservation(room_id)`
- `verifyGuest(room_id, secure_room_id=None)`
- `getCheckedInGuests()`
- `getRoomStatus(room_id)`
- `postChargeToRoom(charge)`
- `getCheckInStatus(room_id)`
- `getCheckOutStatus(room_id)`

`pms/service.py` is the orchestration layer used by `server.py`. It handles configuration, audit logs, idempotency, guest sessions, charge transactions, webhook storage, and retry queue records.

`pms/mock_connector.py` is safe for demos and tests. It verifies rooms from the hotel room list and never calls an external PMS.

`pms/opera_cloud.py` is a placeholder connector for Oracle OPERA Cloud. It intentionally does not hardcode or invent Oracle OHIP endpoint URLs. Official Oracle OHIP documentation, scopes, application keys, hotel credentials, enterprise ID, and property ID are required before real API calls are added.

## Environment Variables

Required before enabling real Oracle OPERA Cloud production calls:

- `OPERA_BASE_URL`
- `OPERA_CLIENT_ID`
- `OPERA_CLIENT_SECRET`
- `OPERA_APP_KEY`
- `OPERA_ENTERPRISE_ID`
- `OPERA_PROPERTY_ID`

Do not save these values in frontend JavaScript, localStorage, screenshots, or audit logs.

## Database Tables

`pms_connections`

Stores the hotel PMS provider, mode, property ID, hotel code, and enabled flag. Credentials stay in environment variables.

`pms_sync_logs`

Stores PMS requests, responses, failed authentication, guest verification, charge posting, sync events, and queued retry attempts. Sensitive values are redacted before writing.

`pms_guest_sessions`

Stores verified room sessions without unnecessary guest personal data.

`pms_charge_transactions`

Stores room charge transactions with an `idempotency_key` so the same order cannot be charged twice.

`pms_webhook_events`

Stores future PMS events such as guest checked in, guest checked out, room changed, and reservation updated.

## API Endpoints

- `POST /api/pms/verify-room`
- `GET /api/pms/reservation/:roomId`
- `POST /api/pms/post-charge`
- `POST /api/pms/webhook`
- `GET /api/pms/connection-status`
- `PUT /api/pms/config`

All endpoints are hotel-scoped through the existing `?hotel=` query parameter that `app.js` already adds.

## Authentication Flow

1. Hotel admin configures PMS provider and mode from the admin dashboard.
2. Real Oracle credentials are loaded from server environment variables.
3. Oracle access-token handling must be implemented only after reviewing official Oracle OHIP authentication documentation.
4. Tokens and client secrets must never be returned to the browser or written to logs.

## Guest Verification Flow

1. Guest scans a QR code in the room.
2. Production QR codes should contain a secure room identifier, not only a visible room number.
3. The backend calls `PMSService.verify_room()`.
4. The configured connector checks that the room exists and has a checked-in guest.
5. The frontend receives only minimal data: room ID, reservation ID, check-in/check-out status, expiry, and session ID.
6. When PMS checkout sync is implemented, checked-out rooms should be disabled.

## Room Charge Flow

1. A chargeable service creates an order.
2. The backend receives `POST /api/pms/post-charge`.
3. The request includes an `idempotencyKey`.
4. If the key already exists, the existing transaction is returned and no duplicate PMS charge is sent.
5. If PMS posting fails, the transaction is marked queued and a retry log entry is created.

## Error Handling

The PMS layer has typed errors for:

- PMS unavailable
- Expired token
- Invalid room
- No checked-in guest
- Failed room charge
- Duplicate charge
- Network timeout

The API returns safe error codes and messages. It does not expose tokens, passports, or payment data.

## Queue And Retry

Failed PMS requests are recorded in `pms_sync_logs` with:

- `status = queued`
- `attempt_count`
- `next_retry_at`
- `error_code`

A future worker can read queued rows and retry them. This is intentionally separated from the frontend so staff pages remain fast.

## Webhooks

`POST /api/pms/webhook` stores incoming events for future processing:

- `guest_checked_in`
- `guest_checked_out`
- `room_changed`
- `reservation_updated`

Before production, webhook signature validation must be implemented using the PMS provider's official documentation.

## Adding Another PMS Connector

1. Create a new file in `pms/`, for example `pms/new_provider.py`.
2. Subclass `PMSConnector`.
3. Implement every interface method.
4. Add provider selection in `PMSService.connector()`.
5. Add tests using a fake HTTP client or mock connector.
6. Document required environment variables and permissions.

## Oracle OPERA Cloud Notes

No Oracle OHIP endpoint URL is implemented yet. The real connector must be completed only after the hotel or Oracle partner provides:

- Official OHIP API documentation
- Application key
- Client ID and client secret
- Enterprise ID
- Property ID
- Allowed API scopes/permissions
- Rate limits
- Webhook signing instructions
