Deploy Ladill Care / deploy (push) Successful in 36s
Resolve staff memberships by invite/demo email as well as public_id so nurses stay on their employer org; block staff from owner onboarding; keep Documents viewable with module access while upload stays manage-gated. Co-authored-by: Cursor <cursoragent@cursor.com>
385 lines
13 KiB
PHP
385 lines
13 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\User;
|
|
use App\Models\Visit;
|
|
use App\Services\Care\OrganizationResolver;
|
|
use App\Services\Care\SpecialtyModuleService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Tests\TestCase;
|
|
|
|
class CareNurseOrgAccessTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_nurse_with_email_user_ref_membership_is_not_sent_to_onboarding(): void
|
|
{
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$owner = User::create([
|
|
'public_id' => 'owner-pid',
|
|
'name' => 'Owner',
|
|
'email' => 'owner@example.com',
|
|
]);
|
|
|
|
$nurse = User::create([
|
|
'public_id' => 'nurse-pid',
|
|
'name' => 'Nurse',
|
|
'email' => 'demo-pro-nurse@ladill.com',
|
|
]);
|
|
|
|
$organization = Organization::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'name' => 'Clinic',
|
|
'slug' => 'clinic',
|
|
'settings' => [
|
|
'onboarded' => true,
|
|
'plan' => 'pro',
|
|
'plan_expires_at' => now()->addMonth()->toIso8601String(),
|
|
],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $owner->public_id,
|
|
'role' => 'hospital_admin',
|
|
]);
|
|
|
|
// Demo seeder style: staff member keyed by email until SSO remaps.
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => strtolower($nurse->email),
|
|
'role' => 'nurse',
|
|
]);
|
|
|
|
$resolver = app(OrganizationResolver::class);
|
|
$this->assertTrue($resolver->isOnboarded($nurse));
|
|
$this->assertFalse($resolver->canCompleteOnboarding($nurse));
|
|
$this->assertSame($organization->id, $resolver->resolveForUser($nurse)?->id);
|
|
|
|
// Remap sticks so later requests resolve by public_id.
|
|
$this->assertDatabaseHas('care_members', [
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $nurse->public_id,
|
|
'role' => 'nurse',
|
|
]);
|
|
|
|
$this->actingAs($nurse)
|
|
->get(route('care.dashboard'))
|
|
->assertOk();
|
|
|
|
$this->actingAs($nurse)
|
|
->get(route('care.onboarding.show'))
|
|
->assertRedirect(route('care.dashboard'));
|
|
}
|
|
|
|
public function test_nurse_who_wrongly_owns_empty_org_still_uses_employer_membership(): void
|
|
{
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$owner = User::create([
|
|
'public_id' => 'employer-owner',
|
|
'name' => 'Owner',
|
|
'email' => 'employer@example.com',
|
|
]);
|
|
|
|
$nurse = User::create([
|
|
'public_id' => 'orphan-nurse',
|
|
'name' => 'Nurse',
|
|
'email' => 'orphan-nurse@example.com',
|
|
]);
|
|
|
|
$employer = Organization::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'name' => 'Employer Clinic',
|
|
'slug' => 'employer-clinic',
|
|
'settings' => [
|
|
'onboarded' => true,
|
|
'plan' => 'pro',
|
|
'plan_expires_at' => now()->addMonth()->toIso8601String(),
|
|
'specialty_modules' => ['emergency' => true],
|
|
],
|
|
]);
|
|
|
|
// Orphan org from completing owner onboarding while membership was unresolved.
|
|
Organization::create([
|
|
'owner_ref' => $nurse->public_id,
|
|
'name' => 'Nurse Own Clinic',
|
|
'slug' => 'nurse-own-clinic',
|
|
'settings' => ['onboarded' => true],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $employer->id,
|
|
'user_ref' => strtolower($nurse->email),
|
|
'role' => 'nurse',
|
|
]);
|
|
|
|
$branch = Branch::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $employer->id,
|
|
'name' => 'Main',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
app(SpecialtyModuleService::class)->activate($employer, $owner->public_id, 'emergency');
|
|
$dept = Department::query()->where('type', 'emergency')->where('branch_id', $branch->id)->firstOrFail();
|
|
|
|
$patient = Patient::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $employer->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_number' => 'P-ORPHAN',
|
|
'first_name' => 'Ama',
|
|
'last_name' => 'Patient',
|
|
'gender' => 'female',
|
|
'date_of_birth' => '1990-01-01',
|
|
]);
|
|
|
|
$visit = Visit::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $employer->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => Visit::STATUS_IN_PROGRESS,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $employer->id,
|
|
'branch_id' => $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->assertSame($employer->id, app(OrganizationResolver::class)->resolveForUser($nurse)?->id);
|
|
|
|
$this->actingAs($nurse)
|
|
->get(route('care.specialty.workspace', [
|
|
'module' => 'emergency',
|
|
'visit' => $visit,
|
|
'tab' => 'documents',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('Documents')
|
|
->assertSee('Upload document');
|
|
}
|
|
|
|
public function test_nurse_can_open_emergency_documents_tab(): void
|
|
{
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$owner = User::create([
|
|
'public_id' => 'er-docs-owner',
|
|
'name' => 'Owner',
|
|
'email' => 'er-docs-owner@example.com',
|
|
]);
|
|
|
|
$nurse = User::create([
|
|
'public_id' => 'er-docs-nurse',
|
|
'name' => 'Nurse',
|
|
'email' => 'er-docs-nurse@example.com',
|
|
]);
|
|
|
|
$organization = Organization::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'name' => 'ER Clinic',
|
|
'slug' => 'er-docs-clinic',
|
|
'settings' => [
|
|
'onboarded' => true,
|
|
'plan' => 'pro',
|
|
'plan_expires_at' => now()->addMonth()->toIso8601String(),
|
|
'specialty_modules' => ['emergency' => true],
|
|
],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $owner->public_id,
|
|
'role' => 'hospital_admin',
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $nurse->public_id,
|
|
'role' => 'nurse',
|
|
]);
|
|
|
|
$branch = Branch::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'name' => 'Main',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
app(SpecialtyModuleService::class)->activate($organization, $owner->public_id, 'emergency');
|
|
|
|
$dept = Department::query()->where('type', 'emergency')->where('branch_id', $branch->id)->firstOrFail();
|
|
|
|
$patient = Patient::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_number' => 'P-DOCS',
|
|
'first_name' => 'Ama',
|
|
'last_name' => 'Docs',
|
|
'gender' => 'female',
|
|
'date_of_birth' => '1990-01-01',
|
|
]);
|
|
|
|
$visit = Visit::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => Visit::STATUS_IN_PROGRESS,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $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($nurse)
|
|
->get(route('care.specialty.workspace', [
|
|
'module' => 'emergency',
|
|
'visit' => $visit,
|
|
'tab' => 'documents',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('Documents')
|
|
->assertSee('Upload document');
|
|
}
|
|
|
|
public function test_view_only_staff_sees_documents_without_upload(): void
|
|
{
|
|
$this->withoutMiddleware(EnsurePlatformSession::class);
|
|
|
|
$owner = User::create([
|
|
'public_id' => 'view-docs-owner',
|
|
'name' => 'Owner',
|
|
'email' => 'view-docs-owner@example.com',
|
|
]);
|
|
|
|
$lab = User::create([
|
|
'public_id' => 'view-docs-lab',
|
|
'name' => 'Lab',
|
|
'email' => 'view-docs-lab@example.com',
|
|
]);
|
|
|
|
$organization = Organization::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'name' => 'Cardio Clinic',
|
|
'slug' => 'cardio-docs-clinic',
|
|
'settings' => [
|
|
'onboarded' => true,
|
|
'plan' => 'pro',
|
|
'plan_expires_at' => now()->addMonth()->toIso8601String(),
|
|
],
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $owner->public_id,
|
|
'role' => 'hospital_admin',
|
|
]);
|
|
|
|
Member::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'user_ref' => $lab->public_id,
|
|
'role' => 'lab_technician',
|
|
]);
|
|
|
|
$branch = Branch::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'name' => 'Main',
|
|
'is_active' => true,
|
|
]);
|
|
|
|
$modules = app(SpecialtyModuleService::class);
|
|
$modules->activate($organization, $owner->public_id, 'cardiology');
|
|
|
|
$dept = Department::query()->where('type', 'cardiology')->where('branch_id', $branch->id)->firstOrFail();
|
|
|
|
$patient = Patient::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_number' => 'P-VIEW-DOCS',
|
|
'first_name' => 'Kofi',
|
|
'last_name' => 'View',
|
|
'gender' => 'male',
|
|
'date_of_birth' => '1985-01-01',
|
|
]);
|
|
|
|
$visit = Visit::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $branch->id,
|
|
'patient_id' => $patient->id,
|
|
'status' => Visit::STATUS_IN_PROGRESS,
|
|
'checked_in_at' => now(),
|
|
]);
|
|
|
|
Appointment::create([
|
|
'owner_ref' => $owner->public_id,
|
|
'organization_id' => $organization->id,
|
|
'branch_id' => $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(),
|
|
]);
|
|
|
|
$labMember = Member::query()->where('user_ref', $lab->public_id)->firstOrFail();
|
|
$this->assertSame('view', $modules->memberAccessLevel($organization, $labMember, 'cardiology'));
|
|
|
|
$this->actingAs($lab)
|
|
->get(route('care.specialty.workspace', [
|
|
'module' => 'cardiology',
|
|
'visit' => $visit,
|
|
'tab' => 'documents',
|
|
]))
|
|
->assertOk()
|
|
->assertSee('Documents')
|
|
->assertDontSee('Upload document');
|
|
}
|
|
}
|