, caption?: ?string, bill_fee?: bool} $meta */ public function upload( Organization $organization, Visit $visit, UploadedFile $file, array $meta, string $ownerRef, ?string $actorRef = null, ): DentalImage { $visit->loadMissing('patient'); $modality = (string) ($meta['modality'] ?? ''); if (! array_key_exists($modality, DentalCatalog::modalities())) { throw new \InvalidArgumentException("Invalid imaging modality [{$modality}]."); } $toothCodes = array_values(array_filter( $meta['tooth_codes'] ?? [], fn ($c) => DentalCatalog::isValidToothCode((string) $c), )); $path = $file->store("care/patients/{$visit->patient_id}/dental", 'public'); $attachment = PatientAttachment::create([ 'owner_ref' => $ownerRef, 'patient_id' => $visit->patient_id, 'file_path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'document_type' => 'dental_imaging_'.$modality, 'uploaded_by' => $actorRef ?? $ownerRef, ]); $image = DentalImage::create([ 'owner_ref' => $ownerRef, 'organization_id' => $organization->id, 'patient_id' => $visit->patient_id, 'visit_id' => $visit->id, 'patient_attachment_id' => $attachment->id, 'modality' => $modality, 'tooth_codes' => $toothCodes, 'caption' => $meta['caption'] ?? null, 'uploaded_by' => $actorRef ?? $ownerRef, 'captured_at' => now(), ]); AuditLogger::record( $ownerRef, 'dental.image.uploaded', $organization->id, $actorRef, DentalImage::class, $image->id, ); return $image->fresh('attachment'); } public function void(DentalImage $image, string $ownerRef, ?string $actorRef = null): void { $image->delete(); AuditLogger::record( $ownerRef, 'dental.image.voided', $image->organization_id, $actorRef, DentalImage::class, $image->id, ); } public function url(DentalImage $image): ?string { $path = $image->attachment?->file_path; if (! $path) { return null; } return Storage::disk('public')->url($path); } }