Deploy Ladill Care / deploy (push) Successful in 31s
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>
98 lines
2.9 KiB
PHP
98 lines
2.9 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Care\Dentistry;
|
|
|
|
use App\Models\DentalImage;
|
|
use App\Models\Organization;
|
|
use App\Models\PatientAttachment;
|
|
use App\Models\Visit;
|
|
use App\Services\Care\AuditLogger;
|
|
use Illuminate\Http\UploadedFile;
|
|
use Illuminate\Support\Facades\Storage;
|
|
|
|
class DentalImagingService
|
|
{
|
|
/**
|
|
* @param array{modality: string, tooth_codes?: list<string>, 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);
|
|
}
|
|
}
|