flowrise-hms/appointment

Appointment scheduling module for FlowRise HMS providing staff-facing booking, rescheduling, cancellation, check-in, and no-show tracking. Includes waitlist queue management with priority scoring, recurring appointment support via iCalendar RRULE, and resilient sync via outbox pattern.

Maintainers

Package info

github.com/Flowrise-HMS/Appointment

Type:laravel-module

pkg:composer/flowrise-hms/appointment

Transparency log

Statistics

Installs: 1

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 1

dev-main 2026-07-26 01:16 UTC

This package is auto-updated.

Last update: 2026-08-02 12:19:58 UTC


README

Scheduling, waitlist, outbound sync outbox, and Filament admin surfaces for FlowRise HMS.

Operator documentation (staff-facing)

End-user workflows for reception, clinical staff, and administrators: docs/user-guide/appointments.md.

Branch context (required reading)

All domain models extend Modules\Core\Models\BaseModel, which applies BelongsToBranch:

  • Global scope: queries default to branch_id = current_branch_id from Laravel Context or Auth::user()->branch_id.
  • Creating: branch_id is auto-filled from that context when absent.

Console commands, queues, and tests must set branch context or call Model::withoutGlobalScopes() when operating across branches.

Entity overview

Model Table Purpose
Appointment appointments Core booked encounter shell (patient, location, status, times).
AppointmentParticipant appointment_participants FHIR-style participants (participant_type, actor_reference string).
AppointmentResource appointment_resources Room/equipment allocations with time windows; feeds conflict checks.
AppointmentRecurrenceRule appointment_recurrence_rules Metadata only — no engine expands occurrences yet.
AppointmentAudit appointment_audits Optional domain audit rows (manual / Filament); not auto-written by scheduling.
ScheduleBlock appointment_schedule_blocks Practitioner/location blocks for conflict detection.
WaitlistEntry appointment_waitlist_entries Prioritized wait queue per branch/patient preferences.
AppointmentSyncOutbox appointment_sync_outbox Reliable outbound events for integrations.

Relationships quick reference

Appointment

  • patient, location, department, branch (explicit branch; matches trait).
  • primaryPractitionerStaff via practitioner_primary_id (UUID, no FK in DB).
  • creator / updaterApp\Models\User via created_by / updated_by.
  • Children: participants, resources, recurrenceRules, appointmentAudits, syncOutboxEntries (filtered to aggregate_type = appointment).

AppointmentSyncOutbox

  • branch() from trait; appointment()Appointment on aggregate_id when aggregate_type is appointment (AppointmentSyncOutbox::AGGREGATE_TYPE_APPOINTMENT).
  • Query helpers: scopeForAppointmentAggregate(), scopeDue() (pending + available_at elapsed).

WaitlistEntry

  • patient, preferredPractitioner (Staff, nullable UUID no FK), preferredLocation, preferredDepartment, branch (trait).

AppointmentAudit

  • appointment, branch (trait), actorUser when actor_id points at a web user.

ScheduleBlock

  • branch (trait), practitioner → Staff, location, department.

Child rows (Participant, Resource, RecurrenceRule)

  • appointment, branch (trait).

Soft UUID references

These columns are strings without FK until interoperability stabilizes:

  • appointments.practitioner_primary_id
  • appointment_participants.actor_reference
  • appointment_schedule_blocks.practitioner_id (nullable)
  • appointment_waitlist_entries.preferred_practitioner_id

Prefer Staff UUIDs where Staff exists; document external FHIR ids separately if needed.

Services and workflows

AppointmentSchedulingService

  • schedule / reschedule / checkIn / cancel mutate Appointment and enqueue AppointmentSyncOutbox rows.
  • Practitioner conflicts: AppointmentConflictService checks overlapping Appointment rows and ScheduleBlock.
  • Resource conflicts: hasResourceConflict when AppointmentResource rows exist.

Outbox producer

Events emitted today:

event_name When
appointment.booked After create
appointment.rescheduled After reschedule (+ version bump)
appointment.checked_in After check-in
appointment.cancelled After cancel

Idempotency: idempotency_key is sha256("{appointment_id}|{event_name}|{version}"). AppointmentSyncOutbox::firstOrCreate skips duplicates for the same triple.

Payload (JSON): appointment_id, status, ISO8601 start_at, end_at.

Outbox consumer (stub)

Artisan command appointment:process-sync-outbox (Modules\Appointment\Console\Commands\ProcessAppointmentSyncOutboxCommand) selects due pending rows (respects withoutGlobalScopes) and marks them completed — replace internals with real HTTP / message-bus dispatch.

Scheduler entry (project routes/console.php from repository root):

Schedule::command('appointment:process-sync-outbox')->everyMinute();

Ensure host cron runs php artisan schedule:run every minute in production.

Filament: no manual create route for outbox rows — entries are system-generated; operators may view/edit for troubleshooting.

Waitlist scoring

WaitlistScoringService returns a numeric score only; persist computed_priority_score from Filament or a future job.

Recurrence rules

Rules stored on AppointmentRecurrenceRule are not expanded into additional appointments. Admin CRUD is informational until a recurrence engine is implemented.

Appointment audits vs activity log

  • Spatie activity log runs on all BaseModel children (configurable per model).
  • AppointmentAudit is not populated automatically by scheduling today — use Filament for explicit audit rows or add observers later.

Operator notes

  • Maintain schedule blocks for clinicians/locations that must not receive bookings.
  • Attach resources when rooms/assets must participate in conflict detection.
  • Monitor sync outbox for stuck failed / high attempts rows after real integrations ship.

Developer commands

php artisan appointment:process-sync-outbox --limit=50

Tests

./vendor/bin/pest Modules/Appointment/tests