Files
ladill-care/app/Services/Care/Dentistry/DentalPerioService.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

95 lines
3.3 KiB
PHP

<?php
namespace App\Services\Care\Dentistry;
use App\Models\DentalPerioExam;
use App\Models\DentalPerioSite;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\Visit;
use App\Services\Care\AuditLogger;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
class DentalPerioService
{
/**
* @return Collection<int, DentalPerioExam>
*/
public function examsForPatient(Organization $organization, Patient $patient, string $ownerRef, int $limit = 10): Collection
{
return DentalPerioExam::owned($ownerRef)
->where('organization_id', $organization->id)
->where('patient_id', $patient->id)
->with('sites')
->latest('id')
->limit($limit)
->get();
}
public function latestForPatient(Organization $organization, Patient $patient, string $ownerRef): ?DentalPerioExam
{
return $this->examsForPatient($organization, $patient, $ownerRef, 1)->first();
}
/**
* @param list<array{tooth_code: string, surface: string, probing_mm?: ?int, bleeding?: bool, plaque?: bool, recession_mm?: ?int}> $sites
*/
public function saveExam(
Organization $organization,
Patient $patient,
string $ownerRef,
array $sites,
?Visit $visit = null,
?string $notes = null,
?string $actorRef = null,
): DentalPerioExam {
return DB::transaction(function () use ($organization, $patient, $ownerRef, $sites, $visit, $notes, $actorRef) {
$exam = DentalPerioExam::create([
'owner_ref' => $ownerRef,
'organization_id' => $organization->id,
'patient_id' => $patient->id,
'visit_id' => $visit?->id,
'exam_date' => now()->toDateString(),
'examiner' => $actorRef ?? $ownerRef,
'notes' => $notes,
]);
foreach ($sites as $row) {
$tooth = (string) ($row['tooth_code'] ?? '');
$surface = strtoupper((string) ($row['surface'] ?? ''));
if (! DentalCatalog::isValidToothCode($tooth) || ! in_array($surface, DentalCatalog::perioSurfaces(), true)) {
continue;
}
if (($row['probing_mm'] ?? null) === null
&& empty($row['bleeding'])
&& empty($row['plaque'])
&& ($row['recession_mm'] ?? null) === null) {
continue;
}
DentalPerioSite::create([
'perio_exam_id' => $exam->id,
'tooth_code' => $tooth,
'surface' => $surface,
'probing_mm' => isset($row['probing_mm']) ? (int) $row['probing_mm'] : null,
'bleeding' => (bool) ($row['bleeding'] ?? false),
'plaque' => (bool) ($row['plaque'] ?? false),
'recession_mm' => isset($row['recession_mm']) ? (int) $row['recession_mm'] : null,
]);
}
AuditLogger::record(
$ownerRef,
'dental.perio.saved',
$organization->id,
$actorRef,
DentalPerioExam::class,
$exam->id,
);
return $exam->fresh('sites');
});
}
}