withoutMiddleware(EnsurePlatformSession::class); $this->owner = User::create([ 'public_id' => 'den-owner', 'name' => 'Owner', 'email' => 'den-owner@example.com', ]); $this->organization = Organization::create([ 'owner_ref' => $this->owner->public_id, 'name' => 'Dental Clinic', 'slug' => 'dental-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', 'branch_id' => null, ]); $this->branch = Branch::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'name' => 'Main', 'is_active' => true, ]); app(SpecialtyModuleService::class)->activate( $this->organization, $this->owner->public_id, 'dentistry', ); $department = Department::query() ->where('branch_id', $this->branch->id) ->where('type', 'dental') ->firstOrFail(); $this->patient = Patient::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_number' => 'P-DEN-SUITE', 'first_name' => 'Afia', 'last_name' => 'Darko', 'gender' => 'female', 'date_of_birth' => '1992-01-01', ]); $this->visit = Visit::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'status' => Visit::STATUS_IN_PROGRESS, 'checked_in_at' => now(), ]); Appointment::create([ 'owner_ref' => $this->owner->public_id, 'organization_id' => $this->organization->id, 'branch_id' => $this->branch->id, 'patient_id' => $this->patient->id, 'department_id' => $department->id, 'visit_id' => $this->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(), ]); } public function test_chart_tab_renders_odontogram_not_text_form(): void { $this->actingAs($this->owner) ->get(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $this->visit, 'tab' => 'odontogram', ])) ->assertOk() ->assertSee('Odontogram (FDI)') ->assertSee('18') ->assertSee('Save tooth') ->assertDontSee('Teeth affected (FDI numbers'); } public function test_updating_tooth_persists_chart_and_event(): void { $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.tooth', $this->visit), [ 'tooth_code' => '16', 'status' => 'present', 'surfaces' => ['O', 'M'], 'conditions' => ['caries'], 'notes' => 'Occlusal caries', ]) ->assertRedirect(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $this->visit, 'tab' => 'odontogram', ])); $chart = DentalChart::query()->where('patient_id', $this->patient->id)->first(); $this->assertNotNull($chart); $tooth = $chart->teeth()->where('tooth_code', '16')->first(); $this->assertSame(['O', 'M'], $tooth->surfaces); $this->assertSame(['caries'], $tooth->conditions); $this->assertDatabaseHas('care_dental_chart_events', [ 'dental_chart_id' => $chart->id, 'tooth_code' => '16', 'event_type' => 'tooth_updated', ]); $this->assertSame(1, DentalChartEvent::query()->where('dental_chart_id', $chart->id)->count()); } public function test_plan_procedure_and_bill_flow(): void { $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.plan-items', $this->visit), [ 'procedure_code' => 'den.fill', 'tooth_code' => '16', 'surfaces' => ['O'], 'priority' => 10, ]) ->assertRedirect(); $plan = DentalTreatmentPlan::query()->where('patient_id', $this->patient->id)->first(); $this->assertNotNull($plan); $item = $plan->items()->first(); $this->assertSame('den.fill', $item->procedure_code); $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.plan.accept', $this->visit)) ->assertRedirect(); $this->assertSame(DentalTreatmentPlan::STATUS_ACCEPTED, $plan->fresh()->status); $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.procedures', $this->visit), [ 'procedure_code' => 'den.fill', 'tooth_code' => '16', 'surfaces' => ['O'], 'plan_item_id' => $item->id, 'bill' => 1, 'anesthesia' => 'Local', ]) ->assertRedirect(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $this->visit, 'tab' => 'treat', ])); $procedure = DentalProcedure::query()->where('visit_id', $this->visit->id)->first(); $this->assertNotNull($procedure); $this->assertNotNull($procedure->bill_line_item_id); $this->assertSame(DentalPlanItem::STATUS_DONE, $item->fresh()->status); $this->assertDatabaseHas('care_bill_line_items', [ 'id' => $procedure->bill_line_item_id, 'source_type' => 'dental_procedure', 'source_id' => $procedure->id, ]); } public function test_imaging_upload_and_reports_page(): void { Storage::fake('public'); $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.images', $this->visit), [ 'attachment' => UploadedFile::fake()->image('xray.jpg'), 'modality' => 'pa', 'tooth_codes' => ['16'], 'caption' => 'PA 16', 'add_fee' => 0, ]) ->assertRedirect(route('care.specialty.workspace', [ 'module' => 'dentistry', 'visit' => $this->visit, 'tab' => 'imaging', ])); $this->assertDatabaseHas('care_dental_images', [ 'visit_id' => $this->visit->id, 'modality' => 'pa', ]); $this->actingAs($this->owner) ->get(route('care.specialty.dentistry.reports')) ->assertOk() ->assertSee('Dentistry reports'); } public function test_notes_and_print_views(): void { $this->actingAs($this->owner) ->post(route('care.specialty.dentistry.notes', $this->visit), [ 'chief_complaint' => 'Toothache upper right', 'soft_tissue' => 'WNL', 'occlusion' => 'Class I', 'notes' => 'Demo note', ]) ->assertRedirect(); $this->assertDatabaseHas('care_dental_visit_notes', [ 'visit_id' => $this->visit->id, 'chief_complaint' => 'Toothache upper right', ]); $this->actingAs($this->owner) ->get(route('care.specialty.dentistry.print', $this->visit)) ->assertOk() ->assertSee('Odontogram (FDI)') ->assertSee('Afia Darko'); } }