From a23604570eb39192950bdbefe46cd31d4a707548 Mon Sep 17 00:00:00 2001 From: isaacclad Date: Mon, 20 Jul 2026 17:43:35 +0000 Subject: [PATCH] Allow MAR visit charts for nurses on duty outside their home branch. Station could list clinic patients on a covered unit while authorizeBranch 404'd the MAR link; duty-unit census now grants access too. Co-authored-by: Cursor --- app/Http/Controllers/Care/MarController.php | 39 ++++++++++- tests/Feature/CareOutpatientCensusTest.php | 72 +++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Care/MarController.php b/app/Http/Controllers/Care/MarController.php index 0542a0b..99968e6 100644 --- a/app/Http/Controllers/Care/MarController.php +++ b/app/Http/Controllers/Care/MarController.php @@ -8,7 +8,10 @@ use App\Models\CareUnit; use App\Models\MarOrder; use App\Models\Visit; use App\Services\Care\CarePermissions; +use App\Services\Care\CareUnitCensusService; use App\Services\Care\MarService; +use App\Services\Care\NurseStationService; +use App\Services\Care\OrganizationResolver; use Carbon\Carbon; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; @@ -47,7 +50,7 @@ class MarController extends Controller { $this->authorizeAbility($request, 'mar.view'); $this->authorizeOwner($request, $visit); - $this->authorizeBranch($request, (int) $visit->branch_id); + $this->authorizeMarVisitAccess($request, $visit); $day = $request->filled('date') ? Carbon::parse($request->input('date'))->startOfDay() @@ -109,4 +112,38 @@ class MarController extends Controller return back()->with('success', 'MAR entry recorded.'); } + + /** + * Branch-scoped staff may open MAR for home-branch visits, or for patients + * on a unit they are rostered/placed on today (coverage across branches). + */ + protected function authorizeMarVisitAccess(Request $request, Visit $visit): void + { + $member = $this->member($request); + $resolver = app(OrganizationResolver::class); + + if ($resolver->mayAccessBranch($member, (int) $visit->branch_id)) { + return; + } + + $permissions = app(CarePermissions::class); + if (! $permissions->can($member, 'nursing.station.view')) { + abort(404); + } + + $dutyUnits = app(NurseStationService::class)->dutyUnits( + $this->organization($request), + $member, + $this->ownerRef($request), + ); + + $census = app(CareUnitCensusService::class); + foreach ($dutyUnits as $unit) { + if ($census->visitBelongsToUnit($visit, $unit)) { + return; + } + } + + abort(404); + } } diff --git a/tests/Feature/CareOutpatientCensusTest.php b/tests/Feature/CareOutpatientCensusTest.php index 579575c..a0fb02e 100644 --- a/tests/Feature/CareOutpatientCensusTest.php +++ b/tests/Feature/CareOutpatientCensusTest.php @@ -303,4 +303,76 @@ class CareOutpatientCensusTest extends TestCase $visits = app(CareUnitCensusService::class)->visitsForUnit($ward, $this->owner->public_id); $this->assertCount(0, $visits); } + + public function test_nurse_rostered_on_other_branch_unit_can_open_mar(): void + { + $homeBranch = Branch::create([ + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'name' => 'Home Branch', + 'is_active' => true, + ]); + + $user = User::create([ + 'public_id' => 'opd-cover-nurse', + 'name' => 'Cover Nurse', + 'email' => 'opd-cover-nurse@example.com', + ]); + $member = Member::create([ + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'user_ref' => $user->public_id, + 'role' => 'nurse', + 'branch_id' => $homeBranch->id, + ]); + + $roster = app(RosterService::class); + $day = $roster->ensureDefaultShifts($this->organization, $this->owner->public_id)->firstWhere('code', 'day'); + $roster->assign( + $this->opd, + $day, + $member, + now()->toDateString(), + $this->owner->public_id, + $this->owner->public_id, + ); + + $patient = Patient::create([ + 'uuid' => (string) Str::uuid(), + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'branch_id' => $this->branch->id, + 'patient_number' => 'P-OPD-MAR', + 'first_name' => 'Mansa', + 'last_name' => 'Addo', + ]); + + $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::create([ + 'uuid' => (string) Str::uuid(), + 'owner_ref' => $this->owner->public_id, + 'organization_id' => $this->organization->id, + 'branch_id' => $this->branch->id, + 'department_id' => $this->department->id, + 'patient_id' => $patient->id, + 'visit_id' => $visit->id, + 'type' => Appointment::TYPE_WALK_IN, + 'status' => Appointment::STATUS_WAITING, + 'checked_in_at' => now(), + ]); + + // Home-branch gate alone would 404; duty on OPD must allow the chart. + $this->actingAs($user) + ->get(route('care.visits.mar', $visit)) + ->assertOk() + ->assertSee('Mansa Addo'); + } }