Files
ladill-care/app/Services/Care/Dentistry/DentalLabCaseService.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.5 KiB
PHP

<?php
namespace App\Services\Care\Dentistry;
use App\Models\DentalLabCase;
use App\Models\Organization;
use App\Models\Patient;
use App\Models\Visit;
use App\Services\Care\AuditLogger;
use Illuminate\Support\Collection;
class DentalLabCaseService
{
/**
* @return Collection<int, DentalLabCase>
*/
public function casesForPatient(Organization $organization, Patient $patient, string $ownerRef, int $limit = 30): Collection
{
return DentalLabCase::owned($ownerRef)
->where('organization_id', $organization->id)
->where('patient_id', $patient->id)
->latest('id')
->limit($limit)
->get();
}
/**
* @param array{case_type: string, lab_name?: ?string, tooth_codes?: list<string>, plan_item_id?: ?int, due_at?: ?string, notes?: ?string, status?: string} $data
*/
public function create(
Organization $organization,
Patient $patient,
string $ownerRef,
array $data,
?Visit $visit = null,
?string $actorRef = null,
): DentalLabCase {
$type = (string) ($data['case_type'] ?? '');
if (! array_key_exists($type, DentalCatalog::labCaseTypes())) {
throw new \InvalidArgumentException("Invalid lab case type [{$type}].");
}
$status = (string) ($data['status'] ?? DentalLabCase::STATUS_DRAFT);
if (! array_key_exists($status, DentalCatalog::labCaseStatuses())) {
throw new \InvalidArgumentException("Invalid lab case status [{$status}].");
}
$toothCodes = array_values(array_filter(
$data['tooth_codes'] ?? [],
fn ($c) => DentalCatalog::isValidToothCode((string) $c),
));
$case = DentalLabCase::create([
'owner_ref' => $ownerRef,
'organization_id' => $organization->id,
'patient_id' => $patient->id,
'visit_id' => $visit?->id,
'plan_item_id' => $data['plan_item_id'] ?? null,
'tooth_codes' => $toothCodes,
'case_type' => $type,
'lab_name' => $data['lab_name'] ?? null,
'status' => $status,
'ordered_at' => in_array($status, [DentalLabCase::STATUS_ORDERED, DentalLabCase::STATUS_SENT], true) ? now() : null,
'due_at' => $data['due_at'] ?? null,
'notes' => $data['notes'] ?? null,
'created_by' => $actorRef ?? $ownerRef,
]);
AuditLogger::record($ownerRef, 'dental.lab.created', $organization->id, $actorRef, DentalLabCase::class, $case->id);
return $case;
}
public function transition(
DentalLabCase $case,
string $status,
string $ownerRef,
?string $actorRef = null,
): DentalLabCase {
if (! array_key_exists($status, DentalCatalog::labCaseStatuses())) {
throw new \InvalidArgumentException("Invalid lab case status [{$status}].");
}
$updates = ['status' => $status];
if (in_array($status, [DentalLabCase::STATUS_ORDERED, DentalLabCase::STATUS_SENT], true) && ! $case->ordered_at) {
$updates['ordered_at'] = now();
}
if (in_array($status, [DentalLabCase::STATUS_RECEIVED, DentalLabCase::STATUS_FITTED], true) && ! $case->received_at) {
$updates['received_at'] = now();
}
$case->update($updates);
AuditLogger::record($ownerRef, 'dental.lab.transitioned', $case->organization_id, $actorRef, DentalLabCase::class, $case->id, [
'status' => $status,
]);
return $case->fresh();
}
}