# Patient portal

Run database migrations before using the portal. Staff and patient authentication use separate session guards and account tables; creating a patient does not create a staff user.

## Invite a patient
A receptionist or organisation manager opens the patient's profile, verifies identity and the patient's personal email, checks the confirmation and sends an invitation. The patient opens the emailed link, chooses a password, and signs in at /patient/{organisation-slug}/login. The staff login page also links to /patient, where patients can enter their clinic code.

Invitations expire after 48 hours. Resending rotates the link. Password reset links expire after 60 minutes, are single use and can only reset that organisation's account. An active account uses Forgot password rather than another invitation. One email cannot identify multiple patients in the same organisation; there is no automatic matching or public registration. Guardian/proxy access is not included.

## Email and queues
Configure APP_URL to the HTTPS website URL and configure MAIL_* for real delivery. Portal invitations and password reset links are sent synchronously after the account transaction commits; they do not require a queue worker. Only hashed, expiring tokens are stored in the account table. No patient health details are included in account emails. Invitations show a warning if sending fails; resending issues a new link. Password recovery keeps a generic response to protect account privacy. Log/array mail transports do not count as email delivery.

## Visibility
Patients can only read their own records within their organisation:
- Appointments: time, facility, modality and status.
- Lab results: result_recorded orders with released_at and released_by set. Unreleased results and clinical indications are hidden.
- Prescriptions: only from completed consultations, including dose and instructions. Clinical notes are hidden.
- Invoices: issued, partially paid or paid invoices with issued_at set. Excluded charges, exclusion reasons and draft invoices are hidden. Documents can be printed.

Each record URL enforces ownership again, regardless of IDs supplied by a browser. Patient responses are private and not cached, and portal pages load no external assets.

Revoking access disables credentials, active sessions and outstanding links. Changing the patient email in the staff profile also revokes access; staff must verify the new address and send a new invitation. Password resets invalidate other sessions. The portal uses the configured session idle timeout and rate-limits login and account recovery.

No automatic cPanel deployment settings are changed.


## Patient appointment booking
Organisation managers and admins configure **Appointments → Patient online booking**. Self-booking is disabled until enabled. Defaults are 2 hours' booking notice, 24 hours' notice for online changes/cancellations and a 60-day booking horizon. Facility managers may publish/close sessions in their assigned facility but cannot change organisation-wide rules.

Publish a dated session with facility, department/service (optional), clinician, consultation type, start/end and slot duration (15–120 minutes from the available choices). Sessions last at most 12 hours and have capacity for one patient per slot. Overlapping published sessions for a clinician are rejected. A clinician must accept appointments and hold a physician membership matching the facility/department. Only slots wholly inside a published shift assigned to that physician are bookable. Approved leave, existing clinician appointments and the patient's other appointments block overlapping slots.

Patients choose **Book an appointment**, filter by date/facility/department/clinician, select a session and confirm an available time. Identity is taken from the signed-in account; supplied patient or organisation IDs cannot change ownership. Availability is checked again inside the save transaction. Reception booking and rescheduling use the same patient/clinician locks and interval overlap checks (legacy appointments default to 30 minutes). Reception may still arrange times outside published online sessions.

Patients may reschedule within the configured cutoff, keeping the same clinician, facility and consultation type. They can cancel scheduled appointments before the cutoff, including when new online bookings are disabled. Checked-in/completed/cancelled appointments cannot be changed online. Contact reception for changes outside those rules. Telemedicine bookings create the usual telemedicine session. Cancellation frees the slot and cancels that session. Closing a booking session or subsequently changing shifts does not automatically cancel existing appointments; reception must review existing bookings.

All displayed times use the application's configured timezone (currently UTC). Set staff sessions in that timezone. No appointment reasons or clinical notes are sent by email.

## Automatic appointment emails
Booking, rescheduling and cancellation send confirmations/updates immediately after the appointment transaction commits, whether booked online or by reception. Visit completion and released-result notices also send immediately. Outdated unsent notifications are skipped when an appointment changes, and delivery checks for a changed patient email before sending.

The existing Laravel scheduler must run every minute on the server:

    * * * * * cd /path/to/application && /path/to/php artisan schedule:run >> /dev/null 2>&1

Use the actual cPanel application/PHP paths. The scheduler sends due scheduled mail every minute and queues reminder emails hourly for appointments in the next 24 hours (including short-notice bookings and catch-up after scheduler downtime). Each appointment revision gets at most one reminder. Configure real SMTP in MAIL_*; log transport intentionally does not send appointment notifications. Delivery status and failures remain in the existing hms_crm_email_deliveries outbox. Invitations, password links and action emails do not depend on the scheduler or queue worker. Immediate notification failures remain failed audit records; they are not silently rescheduled by the timer. Reception can retry failed immediate appointment emails from Appointments, up to three total attempts.
