Files
ladill-care/app/Http/Controllers/Care/InvestigationController.php
T
isaaccladandCursor 015a4cc7fe
Deploy Ladill Care / deploy (push) Successful in 1m45s
Wire Care lists into Ladill Queue workflows instead of reception panels.
When Queue integration is on, role pages issue tickets into their own
department queues and drive Call next → Serve → Complete on the native
lists. Integration off leaves existing UI unchanged.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 16:27:48 +00:00

276 lines
11 KiB
PHP

<?php
namespace App\Http\Controllers\Care;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
use App\Models\Branch;
use App\Models\Consultation;
use App\Models\InvestigationRequest;
use App\Models\InvestigationType;
use App\Models\Member;
use App\Services\Care\CareQueueBridge;
use App\Services\Care\CareQueueContexts;
use App\Services\Care\InvestigationService;
use App\Services\Care\OrganizationResolver;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class InvestigationController extends Controller
{
use ScopesToAccount;
public function __construct(
protected InvestigationService $investigations,
protected CareQueueBridge $queueBridge,
) {}
public function index(Request $request): View
{
$this->authorizeAbility($request, 'lab.view');
$organization = $this->organization($request);
$branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));
$requests = $this->investigations->list(
$this->ownerRef($request),
$organization->id,
$request->only(['status', 'patient_id']),
$branchScope,
);
return view('care.lab.requests.index', [
'organization' => $organization,
'requests' => $requests,
'statuses' => config('care.investigation_statuses'),
]);
}
public function queue(Request $request): View
{
$this->authorizeAbility($request, 'lab.manage');
$organization = $this->organization($request);
$branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));
$branches = Branch::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->where('is_active', true)
->when($branchScope, fn ($q) => $q->where('id', $branchScope))
->orderBy('name')
->get();
$branchId = (int) ($request->input('branch_id') ?: $branchScope ?: $branches->first()?->id);
$status = $request->input('status');
$queue = $branchId
? $this->investigations->workQueue($this->ownerRef($request), $branchId, $status)
: collect();
return view('care.lab.queue.index', [
'organization' => $organization,
'branches' => $branches,
'branchId' => $branchId,
'status' => $status,
'queue' => $queue,
'statuses' => config('care.investigation_statuses'),
'members' => Member::owned($this->ownerRef($request))
->where('organization_id', $organization->id)
->orderBy('role')
->get(),
'queueIntegration' => $branchId
? $this->queueBridge->listMeta($organization, CareQueueContexts::LABORATORY, $branchId)
: ['enabled' => false],
]);
}
public function callNext(Request $request): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$organization = $this->organization($request);
$member = $this->member($request);
$branchScope = app(OrganizationResolver::class)->branchScope($member);
$branchId = (int) ($request->input('branch_id') ?: $branchScope);
abort_unless($branchId > 0, 422);
abort_unless($this->queueBridge->isEnabled($organization), 404);
$result = $this->queueBridge->callNext($organization, CareQueueContexts::LABORATORY, $branchId);
$ticket = $result['ticket'] ?? null;
if (! $ticket) {
return back()->with('error', 'No lab work waiting in the laboratory queue.');
}
return back()->with('success', 'Called '.$ticket['ticket_number'].'.');
}
public function serve(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$organization = $this->organization($request);
abort_unless($this->queueBridge->isEnabled($organization), 404);
$ticket = $this->queueBridge->serve($organization, $investigation);
if (! $ticket) {
return back()->with('error', 'Could not serve lab ticket.');
}
return back()->with('success', 'Now serving '.$ticket['ticket_number'].'.');
}
public function requestFromConsultation(Request $request, Consultation $consultation): RedirectResponse
{
$this->authorizeAbility($request, 'investigations.request');
$this->authorizeConsultation($request, $consultation);
$validated = $request->validate([
'investigation_type_ids' => ['required', 'array', 'min:1'],
'investigation_type_ids.*' => ['integer', 'exists:care_investigation_types,id'],
'clinical_notes' => ['nullable', 'string', 'max:2000'],
'priority' => ['nullable', 'string', 'in:routine,urgent'],
]);
$this->investigations->requestFromConsultation(
$consultation,
$this->ownerRef($request),
$validated['investigation_type_ids'],
$validated['clinical_notes'] ?? null,
$validated['priority'] ?? 'routine',
$this->ownerRef($request),
);
return back()->with('success', 'Investigation request(s) submitted.');
}
public function show(Request $request, InvestigationRequest $investigation): View
{
$this->authorizeAbility($request, 'lab.view');
$this->authorizeInvestigation($request, $investigation);
$investigation->load([
'patient', 'investigationType', 'practitioner', 'branch',
'result.values', 'result.attachments', 'assignedMember',
]);
$canManage = app(\App\Services\Care\CarePermissions::class)
->can($this->member($request), 'lab.manage');
$canViewResults = app(\App\Services\Care\CarePermissions::class)
->can($this->member($request), 'lab.results.view');
return view('care.lab.requests.show', [
'investigation' => $investigation,
'statuses' => config('care.investigation_statuses'),
'canManage' => $canManage,
'canViewResults' => $canViewResults,
]);
}
public function collectSample(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$validated = $request->validate(['sample_barcode' => ['nullable', 'string', 'max:100']]);
$this->investigations->collectSample(
$investigation,
$this->ownerRef($request),
$validated['sample_barcode'] ?? null,
$this->ownerRef($request),
);
return back()->with('success', 'Sample collected.');
}
public function startProcessing(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$validated = $request->validate(['assigned_member_id' => ['nullable', 'integer', 'exists:care_members,id']]);
$this->investigations->startProcessing(
$investigation,
$this->ownerRef($request),
$validated['assigned_member_id'] ?? null,
$this->ownerRef($request),
);
return redirect()->route('care.lab.requests.show', $investigation)
->with('success', 'Processing started.');
}
public function enterResults(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$validated = $request->validate([
'value' => ['nullable', 'string', 'max:255'],
'result_summary' => ['nullable', 'string', 'max:5000'],
'interpretation' => ['nullable', 'string', 'max:5000'],
'values' => ['nullable', 'array'],
'values.*.parameter' => ['nullable', 'string', 'max:255'],
'values.*.value' => ['nullable', 'string', 'max:255'],
'attachments' => ['nullable', 'array'],
'attachments.*' => ['file', 'max:10240', 'mimes:pdf,jpeg,png,jpg,webp'],
]);
$this->investigations->enterResults(
$investigation,
$this->ownerRef($request),
$validated,
$this->ownerRef($request),
);
return back()->with('success', 'Results submitted for review.');
}
public function approve(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$this->investigations->approve($investigation, $this->ownerRef($request), $this->ownerRef($request));
return back()->with('success', 'Results approved.');
}
public function deliver(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$this->investigations->deliver($investigation, $this->ownerRef($request), $this->ownerRef($request));
return back()->with('success', 'Results delivered to patient record.');
}
public function cancel(Request $request, InvestigationRequest $investigation): RedirectResponse
{
$this->authorizeAbility($request, 'lab.manage');
$this->authorizeInvestigation($request, $investigation);
$this->investigations->cancel($investigation, $this->ownerRef($request), $this->ownerRef($request));
return back()->with('success', 'Investigation cancelled.');
}
protected function authorizeConsultation(Request $request, Consultation $consultation): void
{
$this->authorizeOwner($request, $consultation);
$consultation->loadMissing('visit');
abort_unless($consultation->visit->organization_id === $this->organization($request)->id, 404);
}
protected function authorizeInvestigation(Request $request, InvestigationRequest $investigation): void
{
$this->authorizeOwner($request, $investigation);
abort_unless($investigation->organization_id === $this->organization($request)->id, 404);
$branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
if ($branchId !== null && $investigation->branch_id !== $branchId) {
abort(404);
}
}
}