Files
ladill-care/app/Services/Care/CareUnitCensusService.php
T
isaaccladandCursor f1bb432324
Deploy Ladill Care / deploy (push) Successful in 2m12s
Include checked-in clinic visits on outpatient nursing boards.
OPD/service units list open department appointments without manual place; nursing actions soft-place the visit onto the unit. Inpatient wards stay placement-only.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-20 17:05:13 +00:00

145 lines
4.2 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Services\Care;
use App\Models\CareUnit;
use App\Models\Visit;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
/**
* Who appears on a care units nursing boards.
*
* Inpatient: visits placed on the unit.
* Outpatient / service: placed visits plus open clinic visits for the same
* department (checked-in appointments) so OPD nurses do not need manual place.
*/
class CareUnitCensusService
{
/** @var list<string> */
public const CLINIC_KINDS = ['outpatient', 'service'];
public function isClinicUnit(CareUnit $unit): bool
{
return in_array($unit->kind, self::CLINIC_KINDS, true);
}
/**
* Active visits nurses should see for this unit.
*
* @return Collection<int, Visit>
*/
public function visitsForUnit(CareUnit $unit, string $ownerRef): Collection
{
$unit->loadMissing('department');
return $this->queryForUnit($unit, $ownerRef)
->with(['patient', 'bed', 'appointment'])
->orderByRaw('care_unit_id is null') // placed first
->orderBy('placed_at')
->orderBy('checked_in_at')
->get();
}
public function visitBelongsToUnit(Visit $visit, CareUnit $unit): bool
{
if ((int) $visit->organization_id !== (int) $unit->organization_id) {
return false;
}
if (! in_array($visit->status, [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS], true)) {
return false;
}
if ((int) $visit->care_unit_id === (int) $unit->id) {
return true;
}
if (! $this->isClinicUnit($unit) || $visit->care_unit_id !== null) {
return false;
}
$unit->loadMissing('department');
$branchId = $unit->department?->branch_id;
if (! $branchId || (int) $visit->branch_id !== (int) $branchId) {
return false;
}
$departmentId = $unit->department_id;
if (! $departmentId) {
return false;
}
$appointment = $visit->relationLoaded('appointment')
? $visit->appointment
: $visit->appointment()->first();
return $appointment !== null && (int) $appointment->department_id === (int) $departmentId;
}
/**
* Soft-place an eligible clinic visit onto the unit when a nurse acts on it.
*/
public function ensurePlaced(
Visit $visit,
CareUnit $unit,
string $ownerRef,
?string $actorRef = null,
): Visit {
$visit->refresh();
if ((int) $visit->care_unit_id === (int) $unit->id) {
return $visit;
}
if (! $this->visitBelongsToUnit($visit, $unit)) {
abort(404);
}
if ($visit->care_unit_id !== null) {
abort(404);
}
app(VisitPlacementService::class)->place(
$visit,
$unit,
null,
$ownerRef,
$actorRef,
'Auto-placed from outpatient nursing board',
);
return $visit->fresh(['patient', 'bed', 'careUnit', 'appointment']);
}
/**
* @return Builder<Visit>
*/
protected function queryForUnit(CareUnit $unit, string $ownerRef): Builder
{
$query = Visit::owned($ownerRef)
->where('organization_id', $unit->organization_id)
->whereIn('status', [Visit::STATUS_OPEN, Visit::STATUS_IN_PROGRESS]);
if (! $this->isClinicUnit($unit)) {
return $query->where('care_unit_id', $unit->id);
}
$unit->loadMissing('department');
$branchId = $unit->department?->branch_id;
$departmentId = $unit->department_id;
return $query->where(function (Builder $q) use ($unit, $branchId, $departmentId) {
$q->where('care_unit_id', $unit->id);
if ($branchId && $departmentId) {
$q->orWhere(function (Builder $clinic) use ($branchId, $departmentId) {
$clinic->whereNull('care_unit_id')
->where('branch_id', $branchId)
->whereHas('appointment', fn (Builder $a) => $a->where('department_id', $departmentId));
});
}
});
}
}