Deploy Ladill Care / deploy (push) Successful in 37s
Give nurses, lab techs, pharmacists, and GPs broad access to day-to-day modules, keep restricted specialties for specialists (with support nurses), and let GPs view results or refer without full clinical edit. Co-authored-by: Cursor <cursoragent@cursor.com>
259 lines
9.9 KiB
PHP
259 lines
9.9 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use App\Http\Middleware\EnsurePlatformSession;
|
|
use App\Models\Appointment;
|
|
use App\Models\Branch;
|
|
use App\Models\Department;
|
|
use App\Models\Member;
|
|
use App\Models\Organization;
|
|
use App\Models\Patient;
|
|
use App\Models\Practitioner;
|
|
use App\Models\User;
|
|
use App\Services\Care\SpecialtyModuleService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class CareSpecialtyAccessTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected User $owner;
|
|
|
|
protected Organization $organization;
|
|
|
|
protected Branch $branch;
|
|
|
|
protected SpecialtyModuleService $modules;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$this->owner = User::create([
|
|
'public_id' => 'access-owner',
|
|
'name' => 'Owner',
|
|
'email' => 'access-owner@example.com',
|
|
]);
|
|
|
|
$this->organization = Organization::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'name' => 'Access Clinic',
|
|
'slug' => 'access-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->modules = app(SpecialtyModuleService::class);
|
|
$this->modules->ensureDefaultModulesProvisioned($this->organization, $this->owner->public_id);
|
|
foreach (['cardiology', 'infusion', 'pathology', 'dentistry'] as $key) {
|
|
$this->modules->activate($this->organization->fresh(), $this->owner->public_id, $key);
|
|
}
|
|
$this->organization->refresh();
|
|
}
|
|
|
|
protected function makeStaff(string $role, string $suffix): array
|
|
{
|
|
$user = User::create([
|
|
'public_id' => 'access-'.$suffix,
|
|
'name' => ucfirst($role).' '.$suffix,
|
|
'email' => $suffix.'@example.com',
|
|
]);
|
|
$member = Member::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'user_ref' => $user->public_id,
|
|
'role' => $role,
|
|
'branch_id' => $this->branch->id,
|
|
]);
|
|
|
|
return [$user, $member];
|
|
}
|
|
|
|
public function test_nurse_and_pharmacist_manage_general_modules(): void
|
|
{
|
|
[, $nurse] = $this->makeStaff('nurse', 'nurse1');
|
|
[, $pharmacist] = $this->makeStaff('pharmacist', 'rx1');
|
|
[, $lab] = $this->makeStaff('lab_technician', 'lab1');
|
|
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $nurse, 'emergency'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $pharmacist, 'emergency'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $pharmacist, 'infusion'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $lab, 'pathology'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $lab, 'blood_bank'));
|
|
$this->assertFalse($this->modules->memberCanManage($this->organization, $pharmacist, 'cardiology'));
|
|
}
|
|
|
|
public function test_gp_doctor_manages_general_but_only_views_restricted(): void
|
|
{
|
|
[$doctor, $member] = $this->makeStaff('doctor', 'gp1');
|
|
Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'member_id' => $member->id,
|
|
'user_ref' => $doctor->public_id,
|
|
'name' => 'Dr GP',
|
|
'specialty' => 'General Practice',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $member, 'emergency'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $member, 'dentistry'));
|
|
$this->assertFalse($this->modules->memberCanManage($this->organization, $member, 'cardiology'));
|
|
$this->assertTrue($this->modules->memberCanView($this->organization, $member, 'cardiology'));
|
|
$this->assertTrue($this->modules->memberCanRefer($this->organization, $member, 'cardiology'));
|
|
$this->assertSame('refer', $this->modules->memberAccessLevel($this->organization, $member, 'cardiology'));
|
|
}
|
|
|
|
public function test_cardiologist_manages_restricted_cardiology(): void
|
|
{
|
|
[$doctor, $member] = $this->makeStaff('doctor', 'cardio1');
|
|
$dept = Department::query()->where('type', 'cardiology')->firstOrFail();
|
|
Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'department_id' => $dept->id,
|
|
'member_id' => $member->id,
|
|
'user_ref' => $doctor->public_id,
|
|
'name' => 'Dr Cardio',
|
|
'specialty' => 'Cardiology',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $member, 'cardiology'));
|
|
$this->assertSame('manage', $this->modules->memberAccessLevel($this->organization, $member, 'cardiology'));
|
|
}
|
|
|
|
public function test_gp_can_refer_into_restricted_specialty_queue(): void
|
|
{
|
|
[$doctor, $member] = $this->makeStaff('doctor', 'gp2');
|
|
Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'member_id' => $member->id,
|
|
'user_ref' => $doctor->public_id,
|
|
'name' => 'Dr GP2',
|
|
'specialty' => 'General Practice',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$patient = Patient::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_number' => 'P-REF-1',
|
|
'first_name' => 'Ama',
|
|
'last_name' => 'Refer',
|
|
'gender' => 'female',
|
|
'date_of_birth' => '1990-01-01',
|
|
]);
|
|
|
|
$this->actingAs($doctor)
|
|
->post(route('care.specialty.refer', 'cardiology'), [
|
|
'patient_id' => $patient->id,
|
|
'branch_id' => $this->branch->id,
|
|
'reason' => 'Chest pain review',
|
|
])
|
|
->assertRedirect(route('care.queue.index'));
|
|
|
|
$this->assertDatabaseHas('care_appointments', [
|
|
'patient_id' => $patient->id,
|
|
'reason' => 'Chest pain review',
|
|
'status' => Appointment::STATUS_WAITING,
|
|
]);
|
|
}
|
|
|
|
public function test_gp_cannot_save_clinical_on_restricted_module(): void
|
|
{
|
|
[$doctor, $member] = $this->makeStaff('doctor', 'gp3');
|
|
Practitioner::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'member_id' => $member->id,
|
|
'user_ref' => $doctor->public_id,
|
|
'name' => 'Dr GP3',
|
|
'specialty' => 'General Practice',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$dept = Department::query()->where('type', 'cardiology')->firstOrFail();
|
|
$patient = Patient::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_number' => 'P-CARD-1',
|
|
'first_name' => 'Kofi',
|
|
'last_name' => 'Heart',
|
|
'gender' => 'male',
|
|
'date_of_birth' => '1980-01-01',
|
|
]);
|
|
$visit = \App\Models\Visit::create([
|
|
'owner_ref' => $this->owner->public_id,
|
|
'organization_id' => $this->organization->id,
|
|
'branch_id' => $this->branch->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => \App\Models\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' => $patient->id,
|
|
'department_id' => $dept->id,
|
|
'visit_id' => $visit->id,
|
|
'type' => Appointment::TYPE_WALK_IN,
|
|
'status' => Appointment::STATUS_IN_CONSULTATION,
|
|
'scheduled_at' => now(),
|
|
'checked_in_at' => now(),
|
|
'started_at' => now(),
|
|
]);
|
|
|
|
$this->actingAs($doctor)
|
|
->get(route('care.specialty.workspace', ['module' => 'cardiology', 'visit' => $visit]))
|
|
->assertOk()
|
|
->assertSee('view + refer');
|
|
|
|
$this->actingAs($doctor)
|
|
->post(route('care.specialty.clinical.save', ['module' => 'cardiology', 'visit' => $visit]), [
|
|
'tab' => 'exam',
|
|
'payload' => [
|
|
'chief_complaint' => 'Should not save',
|
|
],
|
|
])
|
|
->assertForbidden();
|
|
}
|
|
|
|
public function test_admin_keeps_full_manage_access(): void
|
|
{
|
|
$admin = Member::query()->where('user_ref', $this->owner->public_id)->firstOrFail();
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'cardiology'));
|
|
$this->assertTrue($this->modules->memberCanManage($this->organization, $admin, 'emergency'));
|
|
}
|
|
}
|