Files
ladill-care/app/Services/Care/Dentistry/DentalRecallService.php
T
isaaccladandCursor ce782382c5
Deploy Ladill Care / deploy (push) Successful in 31s
Complete Dentistry commercial suite with PMS depth.
Add plan lifecycle, visit stages with chair/recovery points, primary dentition charting, imaging void, revenue reports, perio exams, lab cases, and recalls.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-18 17:11:15 +00:00

101 lines
3.2 KiB
PHP

<?php
namespace App\Services\Care\Dentistry;
use App\Models\DentalRecall;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\Visit;
use App\Services\Care\AuditLogger;
use Illuminate\Support\Collection;
class DentalRecallService
{
/**
* @return Collection<int, DentalRecall>
*/
public function recallsForPatient(Organization $organization, Patient $patient, string $ownerRef, int $limit = 30): Collection
{
return DentalRecall::owned($ownerRef)
->where('organization_id', $organization->id)
->where('patient_id', $patient->id)
->orderBy('due_on')
->limit($limit)
->get();
}
/**
* @param array{due_on: string, reason?: ?string, notes?: ?string} $data
*/
public function schedule(
Organization $organization,
Patient $patient,
string $ownerRef,
array $data,
?string $actorRef = null,
): DentalRecall {
$dueOn = (string) ($data['due_on'] ?? '');
if ($dueOn === '') {
throw new \InvalidArgumentException('Recall due date is required.');
}
$status = DentalRecall::STATUS_SCHEDULED;
if (now()->startOfDay()->gte(\Carbon\Carbon::parse($dueOn)->startOfDay())) {
$status = DentalRecall::STATUS_DUE;
}
$recall = DentalRecall::create([
'owner_ref' => $ownerRef,
'organization_id' => $organization->id,
'patient_id' => $patient->id,
'due_on' => $dueOn,
'reason' => $data['reason'] ?? null,
'status' => $status,
'notes' => $data['notes'] ?? null,
'created_by' => $actorRef ?? $ownerRef,
]);
AuditLogger::record($ownerRef, 'dental.recall.scheduled', $organization->id, $actorRef, DentalRecall::class, $recall->id);
return $recall;
}
public function complete(
DentalRecall $recall,
string $ownerRef,
?Visit $visit = null,
?string $actorRef = null,
): DentalRecall {
$recall->update([
'status' => DentalRecall::STATUS_COMPLETED,
'linked_visit_id' => $visit?->id ?? $recall->linked_visit_id,
]);
AuditLogger::record($ownerRef, 'dental.recall.completed', $recall->organization_id, $actorRef, DentalRecall::class, $recall->id);
return $recall->fresh();
}
public function cancel(DentalRecall $recall, string $ownerRef, ?string $actorRef = null): DentalRecall
{
$recall->update(['status' => DentalRecall::STATUS_CANCELLED]);
AuditLogger::record($ownerRef, 'dental.recall.cancelled', $recall->organization_id, $actorRef, DentalRecall::class, $recall->id);
return $recall->fresh();
}
/**
* @return Collection<int, DentalRecall>
*/
public function openForPatient(Organization $organization, Patient $patient, string $ownerRef): Collection
{
return DentalRecall::owned($ownerRef)
->where('organization_id', $organization->id)
->where('patient_id', $patient->id)
->whereIn('status', [DentalRecall::STATUS_SCHEDULED, DentalRecall::STATUS_DUE])
->orderBy('due_on')
->get();
}
}