withoutMiddleware(EnsurePlatformSession::class); $this->owner = User::create([ 'public_id' => 'shell-owner', 'name' => 'Owner', 'email' => 'shell@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->owner->public_id, 'name' => 'Shell Clinic', 'slug' => 'shell-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' => 'nurse', ]); $this->branch = Branch::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main', 'is_active' => true, ]); } public function test_activating_emergency_seeds_service_catalog_and_stage_map(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'emergency', ); $this->organization->refresh(); $services = data_get($this->organization->settings, 'specialty_module_provisioning.emergency.services'); $this->assertIsArray($services); $this->assertNotEmpty($services); $this->assertTrue(collect($services)->contains(fn ($s) => ($s['code'] ?? '') === 'er.consultation')); $shell = app(SpecialtyShellService::class); $stages = $shell->stages('emergency'); $this->assertSame('arrival', $stages[0]['code'] ?? null); $this->assertGreaterThanOrEqual(4, count($stages)); } public function test_specialty_shell_overview_and_sections_render(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'emergency', ); $this->actingAs($this->owner) ->get(route('care.specialty.show', 'emergency')) ->assertRedirect(route('care.queue.index')); $this->actingAs($this->owner) ->get(route('care.specialty.visits', 'emergency')) ->assertRedirect(route('care.queue.index')); $this->actingAs($this->owner) ->get(route('care.specialty.history', 'emergency')) ->assertOk() ->assertSee('Visit history'); $this->actingAs($this->owner) ->get(route('care.specialty.billing', 'emergency')) ->assertOk() ->assertSee('Service catalog') ->assertSee('er.consultation'); $this->actingAs($this->owner) ->get(route('care.specialty.workspace', 'emergency')) ->assertOk() ->assertSee('Back to queue'); } public function test_dentistry_shell_has_chair_stage_and_catalog(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'dentistry', ); $shell = app(SpecialtyShellService::class); $codes = collect($shell->stages('dentistry'))->pluck('code')->all(); $this->assertContains('chair', $codes); $this->assertContains('procedure', $codes); $this->actingAs($this->owner) ->get(route('care.specialty.billing', 'dentistry')) ->assertOk() ->assertSee('den.fill'); } public function test_workspace_can_add_catalog_service_to_visit_bill(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'emergency', ); $department = Department::query() ->where('branch_id', $this->branch->id) ->where('type', 'emergency') ->firstOrFail(); $patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-ER-1', 'first_name' => 'Ama', 'last_name' => 'Mensah', 'gender' => 'female', 'date_of_birth' => '1992-01-01', ]); $visit = Visit::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_OPEN, 'checked_in_at' => now(), ]); Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'department_id' => $department->id, 'visit_id' => $visit->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), ]); $this->actingAs($this->owner) ->post(route('care.specialty.services.add', ['module' => 'emergency', 'visit' => $visit]), [ 'service_code' => 'er.consultation', ]) ->assertRedirect(); $this->assertDatabaseHas('care_bills', [ 'visit_id' => $visit->id, 'patient_id' => $patient->id, ]); $bill = Bill::query()->where('visit_id', $visit->id)->first(); $this->assertNotNull($bill); $this->assertTrue( $bill->lineItems()->where('description', 'Emergency consultation')->exists() ); } public function test_specialty_start_consultation_opens_specialty_workspace(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'dentistry', ); $department = Department::query() ->where('branch_id', $this->branch->id) ->where('type', 'dental') ->firstOrFail(); $patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-DEN-1', 'first_name' => 'Abena', 'last_name' => 'Osei', 'gender' => 'female', 'date_of_birth' => '1993-01-01', ]); $visit = Visit::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_OPEN, 'checked_in_at' => now(), ]); $appointment = Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'department_id' => $department->id, 'visit_id' => $visit->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), 'checked_in_at' => now(), ]); $doctorUser = User::create([ 'public_id' => 'shell-dental-doc', 'name' => 'Dental Doctor', 'email' => 'shell-dental@example.com', ]); $doctorMember = Member::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'user_ref' => $doctorUser->public_id, 'role' => 'doctor', 'branch_id' => $this->branch->id, ]); \App\Models\Practitioner::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'department_id' => $department->id, 'member_id' => $doctorMember->id, 'user_ref' => $doctorUser->public_id, 'name' => 'Dental Doctor', 'specialty' => 'Dentistry', 'is_active' => true, ]); $response = $this->actingAs($doctorUser) ->post(route('care.specialty.consultation.start', [ 'module' => 'dentistry', 'visit' => $visit, ])); $consultation = \App\Models\Consultation::query() ->where('appointment_id', $appointment->id) ->first(); $this->assertNotNull($consultation); $response->assertRedirect(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $visit, 'tab' => 'odontogram', ])); $this->assertSame(Appointment::STATUS_IN_CONSULTATION, $appointment->fresh()->status); } public function test_workspace_includes_timeline_tab_and_complete_action(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'dentistry', ); $department = Department::query() ->where('branch_id', $this->branch->id) ->where('type', 'dental') ->firstOrFail(); $patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-DEN-TL', 'first_name' => 'Afia', 'last_name' => 'Darko', 'gender' => 'female', 'date_of_birth' => '1992-01-01', ]); $visit = Visit::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_IN_PROGRESS, 'checked_in_at' => now(), ]); $appointment = Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'department_id' => $department->id, 'visit_id' => $visit->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_IN_CONSULTATION, 'scheduled_at' => now(), 'waiting_at' => now(), 'checked_in_at' => now(), 'started_at' => now(), 'queue_ticket_status' => 'serving', ]); \App\Models\Consultation::create([ 'owner_ref' => $this->owner->public_id, 'visit_id' => $visit->id, 'appointment_id' => $appointment->id, 'patient_id' => $patient->id, 'status' => \App\Models\Consultation::STATUS_DRAFT, 'started_at' => now(), ]); $tabs = app(SpecialtyShellService::class)->workspaceTabs('dentistry'); $this->assertArrayHasKey('timeline', $tabs); $this->assertSame(['overview', 'timeline'], array_slice(array_keys($tabs), 0, 2)); $this->actingAs($this->owner) ->get(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $visit, 'tab' => 'timeline', ])) ->assertOk() ->assertSee('Timeline') ->assertSee('Patient timeline') ->assertSee('Complete consultation') ->assertDontSee('>Call again<', false); $this->actingAs($this->owner) ->get(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $visit, 'tab' => 'overview', ])) ->assertOk() ->assertDontSee('Patient timeline'); } public function test_called_workspace_shows_call_again_until_session_starts(): void { app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'dentistry', ); $department = Department::query() ->where('branch_id', $this->branch->id) ->where('type', 'dental') ->firstOrFail(); $patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-DEN-CALL', 'first_name' => 'Kofi', 'last_name' => 'Mensah', 'gender' => 'male', 'date_of_birth' => '1988-01-01', ]); $visit = Visit::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'status' => Visit::STATUS_OPEN, 'checked_in_at' => now(), ]); Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $patient->id, 'department_id' => $department->id, 'visit_id' => $visit->id, 'type' => Appointment::TYPE_WALK_IN, 'status' => Appointment::STATUS_WAITING, 'scheduled_at' => now(), 'waiting_at' => now(), 'checked_in_at' => now(), 'queue_ticket_status' => 'called', 'queue_ticket_number' => 'DEN001', ]); $this->actingAs($this->owner) ->get(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $visit, ])) ->assertOk() ->assertSee('Call again') ->assertSee('Start') ->assertDontSee('Complete consultation'); } }