feat(assessments): layered clinical assessment engine end-to-end
Deploy Ladill Care / deploy (push) Successful in 1m26s
Deploy Ladill Care / deploy (push) Successful in 1m26s
Add a template-driven assessment system with universal intake, clinical pathways, disease instruments (stroke MVP + extended, diabetes, and ten specialty packs), scoring, patient outcome trends, REST/API + FHIR export, and Enterprise org-level assessment analytics. Seed packs and design/licensing docs ship for deploy and pre-GA review.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Care;
|
||||
|
||||
use App\Http\Controllers\Care\Concerns\ScopesToAccount;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Patient;
|
||||
use App\Services\Care\AssessmentService;
|
||||
use App\Services\Care\CareFeatures;
|
||||
use App\Services\Care\CarePermissions;
|
||||
use App\Services\Care\OrganizationResolver;
|
||||
use App\Services\Care\OutcomeTrendService;
|
||||
use App\Services\Care\PlanService;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class OutcomeController extends Controller
|
||||
{
|
||||
use ScopesToAccount;
|
||||
|
||||
public function __construct(
|
||||
protected OutcomeTrendService $trends,
|
||||
protected AssessmentService $assessments,
|
||||
protected CareFeatures $features,
|
||||
protected CarePermissions $permissions,
|
||||
protected PlanService $plans,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Per-patient outcome history + instrument score trends (free).
|
||||
*/
|
||||
public function index(Request $request, Patient $patient): View
|
||||
{
|
||||
$this->assertEngineEnabled($request);
|
||||
$this->authorizeAbility($request, 'assessments.view');
|
||||
$this->authorizePatient($request, $patient);
|
||||
|
||||
$branchScope = app(OrganizationResolver::class)->branchScope($this->member($request));
|
||||
$ownerRef = $this->ownerRef($request);
|
||||
|
||||
$outcomeSeries = $this->trends->outcomeSeries($patient, $ownerRef, $branchScope);
|
||||
$scores = $this->trends->instrumentScores($patient, $ownerRef, $branchScope);
|
||||
$vitals = $this->trends->recentVitals($patient, $ownerRef);
|
||||
|
||||
$canCapture = $this->permissions->can($this->member($request), 'assessments.capture')
|
||||
|| $this->permissions->can($this->member($request), 'assessments.manage');
|
||||
|
||||
// Org-level multi-patient analytics is Enterprise (KD-18); surface a notice only.
|
||||
$canOrgAnalytics = $this->plans->isEnterprise($this->organization($request));
|
||||
|
||||
return view('care.outcomes.index', [
|
||||
'patient' => $patient,
|
||||
'outcomeSeries' => $outcomeSeries,
|
||||
'scores' => $scores,
|
||||
'vitals' => $vitals,
|
||||
'canCapture' => $canCapture,
|
||||
'canOrgAnalytics' => $canOrgAnalytics,
|
||||
'qolSeries' => $this->trends->numericOutcomeSeries($patient, $ownerRef, 'quality_of_life', $branchScope),
|
||||
'painSeries' => $this->trends->numericOutcomeSeries($patient, $ownerRef, 'pain_score', $branchScope),
|
||||
'statuses' => config('care.assessment_statuses'),
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a new outcome_core draft for the patient.
|
||||
*/
|
||||
public function store(Request $request, Patient $patient): RedirectResponse
|
||||
{
|
||||
$this->assertEngineEnabled($request);
|
||||
abort_unless(
|
||||
$this->permissions->can($this->member($request), 'assessments.capture')
|
||||
|| $this->permissions->can($this->member($request), 'assessments.manage'),
|
||||
403,
|
||||
);
|
||||
$this->authorizePatient($request, $patient);
|
||||
|
||||
$assessment = $this->assessments->start(
|
||||
$patient,
|
||||
OutcomeTrendService::OUTCOME_TEMPLATE_CODE,
|
||||
$this->ownerRef($request),
|
||||
$this->member($request),
|
||||
['actor' => $this->ownerRef($request)],
|
||||
);
|
||||
|
||||
return redirect()
|
||||
->route('care.assessments.show', $assessment)
|
||||
->with('success', 'Outcome assessment started.');
|
||||
}
|
||||
|
||||
protected function assertEngineEnabled(Request $request): void
|
||||
{
|
||||
abort_unless(
|
||||
$this->features->enabled($this->organization($request), CareFeatures::ASSESSMENTS_ENGINE),
|
||||
404,
|
||||
);
|
||||
}
|
||||
|
||||
protected function authorizePatient(Request $request, Patient $patient): void
|
||||
{
|
||||
$this->authorizeOwner($request, $patient);
|
||||
abort_unless($patient->organization_id === $this->organization($request)->id, 404);
|
||||
|
||||
$branchId = app(OrganizationResolver::class)->branchScope($this->member($request));
|
||||
if ($branchId !== null && $patient->branch_id !== $branchId) {
|
||||
abort(404);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user