withoutMiddleware(EnsurePlatformSession::class); $this->owner = User::create([ 'public_id' => 'bridge-owner', 'name' => 'Owner', 'email' => 'bridge@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->owner->public_id, 'name' => 'Bridge Clinic', 'slug' => 'bridge-clinic', 'settings' => [ 'onboarded' => true, 'facility_type' => 'clinic', 'plan' => 'pro', 'plan_expires_at' => now()->addMonth()->toIso8601String(), 'queue_integration_enabled' => true, ], ]); Member::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $this->owner->public_id, 'role' => 'hospital_admin', ]); $this->branch = Branch::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main', 'is_active' => true, ]); $this->patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-100', 'first_name' => 'Ada', 'last_name' => 'Lovelace', 'gender' => 'female', 'date_of_birth' => '1990-01-01', ]); } public function test_check_in_issues_consultation_ticket_when_integration_on(): void { Http::fake(); $practitioner = \App\Models\Practitioner::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Dr. Bridge', 'room' => 'Room 1', 'is_active' => true, ]); $appointment = Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $practitioner->id, 'type' => Appointment::TYPE_SCHEDULED, 'status' => Appointment::STATUS_SCHEDULED, 'scheduled_at' => now()->addHour(), ]); $result = app(AppointmentService::class)->checkIn($appointment, $this->owner->public_id, $this->owner->public_id); $this->assertNotNull($result->queue_ticket_uuid); $this->assertSame('waiting', $result->queue_ticket_status); $this->assertStringStartsWith('C', (string) $result->queue_ticket_number); $this->assertSame('Room 1', $result->queue_destination); $this->assertSame('Dr. Bridge', $result->queue_staff_display_name); $this->assertDatabaseHas('care_queue_tickets', [ 'uuid' => $result->queue_ticket_uuid, 'ticket_number' => $result->queue_ticket_number, 'status' => CareQueueTicket::STATUS_WAITING, 'care_entity' => 'appointment', 'care_entity_id' => $result->id, ]); Http::assertNothingSent(); } public function test_check_in_skips_ticket_when_integration_off(): void { Http::fake(); $settings = $this->organization->settings; $settings['queue_integration_enabled'] = false; $this->organization->update(['settings' => $settings]); $appointment = Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'type' => Appointment::TYPE_SCHEDULED, 'status' => Appointment::STATUS_SCHEDULED, 'scheduled_at' => now()->addHour(), ]); $result = app(AppointmentService::class)->checkIn( $appointment->fresh(['organization', 'patient']), $this->owner->public_id, $this->owner->public_id, ); $this->assertNull($result->queue_ticket_uuid); Http::assertNothingSent(); } public function test_waiting_list_shows_ticket_number_when_integration_on(): void { Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), 'queue_position' => 1, 'queue_ticket_uuid' => 'dddddddd-dddd-dddd-dddd-dddddddddddd', 'queue_ticket_number' => 'C042', 'queue_ticket_status' => 'waiting', ]); $this->actingAs($this->owner) ->get(route('care.queue.index')) ->assertOk() ->assertSee('C042') ->assertSee('Call next') ->assertSee('Serve'); } public function test_call_next_backfills_missing_ticket_then_calls(): void { Http::fake(); $practitioner = \App\Models\Practitioner::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Dr. Backfill', 'room' => 'Room 3', 'is_active' => true, ]); Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $practitioner->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), 'queue_position' => 1, ]); $this->actingAs($this->owner) ->from(route('care.queue.index')) ->post(route('care.queue.call-next'), [ 'branch_id' => $this->branch->id, 'practitioner_id' => $practitioner->id, ]) ->assertRedirect(route('care.queue.index')) ->assertSessionHas('success'); $appointment = Appointment::query()->where('patient_id', $this->patient->id)->first(); $this->assertNotNull($appointment?->queue_ticket_uuid); $this->assertSame('called', $appointment->queue_ticket_status); $this->assertDatabaseHas('care_queue_tickets', [ 'uuid' => $appointment->queue_ticket_uuid, 'status' => CareQueueTicket::STATUS_CALLED, ]); Http::assertNothingSent(); } public function test_call_next_empty_queue_flashes_info_not_error(): void { Http::fake(); $this->actingAs($this->owner) ->from(route('care.queue.index')) ->post(route('care.queue.call-next'), ['branch_id' => $this->branch->id]) ->assertRedirect(route('care.queue.index')) ->assertSessionHas('info', 'No patients waiting at your consultation service point.') ->assertSessionMissing('error'); Http::assertNothingSent(); } public function test_recall_reannounces_called_ticket_from_patient_queue(): void { Http::fake(); $practitioner = \App\Models\Practitioner::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'name' => 'Dr. Recall', 'room' => 'Room 2', 'is_active' => true, ]); $appointment = Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $practitioner->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_SCHEDULED, 'scheduled_at' => now(), ]); $checkedIn = app(AppointmentService::class)->checkIn( $appointment->fresh(['organization', 'patient', 'practitioner']), $this->owner->public_id, $this->owner->public_id, ); $bridge = app(\App\Services\Care\CareQueueBridge::class); $called = $bridge->callNext( $this->organization->fresh(), CareQueueContexts::CONSULTATION, (int) $this->branch->id, null, $practitioner->id, ); $this->assertNotNull($called['ticket']); $appointment = $checkedIn->fresh(); $this->assertSame('called', $appointment->queue_ticket_status); $this->actingAs($this->owner) ->from(route('care.queue.index')) ->post(route('care.queue.recall', $appointment)) ->assertRedirect(route('care.queue.index')) ->assertSessionHas('success'); $this->assertSame('called', $appointment->fresh()->queue_ticket_status); $this->actingAs($this->owner) ->get(route('care.queue.index', ['branch_id' => $this->branch->id])) ->assertOk() ->assertSee('Call again') ->assertSee($appointment->queue_ticket_number); Http::assertNothingSent(); } public function test_patient_queue_sync_issues_specialty_tickets(): void { Http::fake(); $department = \App\Models\Department::create([ 'owner_ref' => $this->owner->public_id, 'branch_id' => $this->branch->id, 'name' => 'Dentistry', 'type' => 'dental', 'is_active' => true, ]); $practitioner = \App\Models\Practitioner::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'department_id' => $department->id, 'name' => 'Dentistry Clinic', 'room' => 'Dental Bay 1', 'is_active' => true, ]); $settings = $this->organization->settings; $settings['specialty_modules'] = ['dentistry' => true]; $settings['specialty_module_provisioning'] = [ 'dentistry' => [ 'active' => true, 'department_ids' => [$department->id], 'queues' => [[ 'module' => 'dentistry', 'branch_id' => $this->branch->id, 'branch_name' => 'Main', 'name' => 'Dentistry', 'prefix' => 'DEN', 'active' => true, 'synced' => false, ]], ], ]; $this->organization->update(['settings' => $settings]); Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'practitioner_id' => $practitioner->id, 'department_id' => $department->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), 'queue_position' => 1, 'reason' => 'Toothache', ]); $this->actingAs($this->owner) ->get(route('care.queue.index', ['branch_id' => $this->branch->id])) ->assertOk() ->assertSee('Toothache'); $appointment = Appointment::query()->where('patient_id', $this->patient->id)->first(); $this->assertNotNull($appointment?->queue_ticket_uuid); $this->assertStringStartsWith('DEN', (string) $appointment->queue_ticket_number); $this->assertDatabaseHas('care_service_queues', [ 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'context' => 'dentistry', ]); $this->assertDatabaseHas('care_queue_tickets', [ 'uuid' => $appointment->queue_ticket_uuid, 'ticket_number' => $appointment->queue_ticket_number, 'status' => CareQueueTicket::STATUS_WAITING, ]); Http::assertNothingSent(); } }