*/ 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 $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'); }); } }