> */ protected array $roleAbilities = [ 'super_admin' => ['*'], 'hospital_admin' => ['*'], 'receptionist' => [ 'dashboard.view', 'patients.view', 'patients.manage', 'appointments.view', 'appointments.manage', 'queue.manage', 'service_queues.console', ], 'doctor' => [ 'dashboard.view', 'patients.view', 'appointments.view', 'consultations.view', 'consultations.manage', 'investigations.request', 'prescriptions.manage', 'lab.view', 'lab.results.view', 'assessments.view', 'assessments.capture', 'assessments.manage', 'pathways.manage', 'service_queues.console', ], 'nurse' => [ 'dashboard.view', 'patients.view', 'appointments.view', 'consultations.view', 'vitals.manage', 'service_queues.console', 'assessments.view', 'assessments.capture', ], 'lab_technician' => [ 'dashboard.view', 'patients.view', 'lab.view', 'lab.manage', 'service_queues.console', ], 'pharmacist' => [ 'dashboard.view', 'patients.view', 'prescriptions.view', 'prescriptions.dispense', 'pharmacy.view', 'pharmacy.manage', 'service_queues.console', ], // Cashiers run the Billing desk (bills / payments). They do not get // Ladill POS app access — see filterAllowedAppSlugs() / DemoWorld. 'cashier' => [ 'dashboard.view', 'patients.view', 'bills.view', 'bills.manage', 'payments.manage', 'service_queues.console', ], 'accountant' => [ 'dashboard.view', 'bills.view', 'reports.finance.view', 'reports.finance.export', 'audit.view', 'audit.export', ], ]; /** @var list */ protected array $adminAbilities = [ 'admin.branches.view', 'admin.branches.manage', 'admin.departments.view', 'admin.departments.manage', 'admin.practitioners.view', 'admin.practitioners.manage', 'admin.members.view', 'admin.members.manage', 'settings.view', 'settings.manage', 'lab.catalog.manage', 'devices.view', 'devices.manage', 'displays.view', 'displays.manage', 'audit.view', 'audit.export', ]; public function can(?Member $member, string $ability): bool { if ($member === null) { return false; } $abilities = $this->roleAbilities[$member->role] ?? []; if (in_array('*', $abilities, true)) { return true; } return in_array($ability, $abilities, true); } public function isAdmin(?Member $member): bool { return $member !== null && in_array($member->role, ['super_admin', 'hospital_admin'], true); } /** * Floor care: queues, specialty clinics, lab/pharmacy counters. * Facility admins manage settings/staff — they do not staff waiting lines. */ public function handlesFloorCare(?Member $member): bool { if ($member === null || $this->isAdmin($member)) { return false; } return in_array($member->role, [ 'doctor', 'nurse', 'lab_technician', 'pharmacist', 'receptionist', 'cashier', ], true); } /** * Dedicated patient-flow board (GP Queue home / call-next board). * Doctors only — nurses, reception, pharmacy, and other floor roles use * specialty module lists and service queues instead. * Facility admins manage settings/staff and do not staff this board. */ public function canAccessPatientQueue(?Member $member): bool { return $member !== null && $member->role === 'doctor'; } /** * Care cashiers collect via Billing, not Ladill POS. Strip POS from * Identity app grants so the suite launcher hides it. * * @param list|null $slugs Null = unrestricted owner access. * @return list|null */ public function filterAllowedAppSlugs(?Member $member, ?array $slugs): ?array { if ($slugs === null || $member?->role !== 'cashier') { return $slugs; } return array_values(array_filter( array_map('strval', $slugs), fn (string $slug) => $slug !== 'pos', )); } /** * @param array{full_access?: bool, apps?: list, show_hub?: bool, show_billing?: bool} $access * @return array{full_access?: bool, apps?: list, show_hub?: bool, show_billing?: bool} */ public function filterStaffAppAccess(?Member $member, array $access): array { if ($member?->role !== 'cashier') { return $access; } $apps = $this->filterAllowedAppSlugs($member, array_values(array_map( 'strval', (array) ($access['apps'] ?? []), ))) ?? []; $access['apps'] = $apps; if (empty($access['full_access']) && count($apps) <= 1) { $access['show_hub'] = false; } return $access; } }