status !== Appointment::STATUS_IN_CONSULTATION) { $this->appointments->startConsultation( $appointment, $ownerRef, $appointment->practitioner_id, $actorRef, ); $appointment->refresh(); } $existing = Consultation::where('appointment_id', $appointment->id)->first(); if ($existing) { return $existing->load(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner']); } $consultation = Consultation::create([ 'owner_ref' => $ownerRef, 'visit_id' => $appointment->visit_id, 'appointment_id' => $appointment->id, 'practitioner_id' => $appointment->practitioner_id, 'patient_id' => $appointment->patient_id, 'status' => Consultation::STATUS_DRAFT, 'started_at' => now(), ]); AuditLogger::record($ownerRef, 'consultation.started', $appointment->organization_id, $actorRef, Consultation::class, $consultation->id); return $consultation->load(['patient', 'practitioner', 'visit']); } /** * @param array $data */ public function save(Consultation $consultation, string $ownerRef, array $data, ?string $actorRef = null): Consultation { $consultation->update([ 'symptoms' => $data['symptoms'] ?? $consultation->symptoms, 'clinical_notes' => $data['clinical_notes'] ?? $consultation->clinical_notes, 'practitioner_id' => $data['practitioner_id'] ?? $consultation->practitioner_id, ]); if (array_key_exists('vitals', $data) && is_array($data['vitals'])) { $this->saveVitals($consultation, $ownerRef, $data['vitals'], $actorRef); } if (array_key_exists('diagnoses', $data)) { $this->syncDiagnoses($consultation, $ownerRef, $data['diagnoses']); } if (! empty($data['documents'])) { $this->storeDocuments($consultation, $ownerRef, $data['documents'], $actorRef); } AuditLogger::record($ownerRef, 'consultation.updated', $consultation->visit->organization_id, $actorRef, Consultation::class, $consultation->id); return $consultation->fresh(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner', 'visit', 'appointment']); } public function complete(Consultation $consultation, string $ownerRef, ?string $actorRef = null): Consultation { $consultation->update([ 'status' => Consultation::STATUS_COMPLETED, 'completed_at' => now(), 'completed_by' => $actorRef, ]); if ($consultation->appointment) { $this->appointments->complete($consultation->appointment, $ownerRef, $actorRef); } elseif ($consultation->visit) { app(VisitService::class)->complete($consultation->visit, $ownerRef, $actorRef); } AuditLogger::record($ownerRef, 'consultation.completed', $consultation->visit->organization_id, $actorRef, Consultation::class, $consultation->id); return $consultation->fresh(['vitalSigns', 'diagnoses', 'documents', 'patient', 'practitioner']); } /** * @param array $vitals * @param array{source?: string, device_id?: int|null, raw_payload?: array|null} $provenance */ public function saveVitals( Consultation $consultation, string $ownerRef, array $vitals, ?string $actorRef = null, array $provenance = [], ): ?VitalSign { $numeric = collect($vitals)->only([ 'bp_systolic', 'bp_diastolic', 'pulse', 'temperature', 'weight_kg', 'height_cm', 'spo2', 'respiratory_rate', ])->filter(fn ($v) => $v !== null && $v !== '')->all(); if ($numeric === []) { return null; } return VitalSign::create([ 'owner_ref' => $ownerRef, 'consultation_id' => $consultation->id, 'bp_systolic' => $vitals['bp_systolic'] ?? null, 'bp_diastolic' => $vitals['bp_diastolic'] ?? null, 'pulse' => $vitals['pulse'] ?? null, 'temperature' => $vitals['temperature'] ?? null, 'weight_kg' => $vitals['weight_kg'] ?? null, 'height_cm' => $vitals['height_cm'] ?? null, 'spo2' => $vitals['spo2'] ?? null, 'respiratory_rate' => $vitals['respiratory_rate'] ?? null, 'recorded_by' => $actorRef, 'recorded_at' => now(), 'source' => $provenance['source'] ?? VitalSign::SOURCE_MANUAL, 'device_id' => $provenance['device_id'] ?? null, 'raw_payload' => $provenance['raw_payload'] ?? null, ]); } /** * @param array> $rows */ protected function syncDiagnoses(Consultation $consultation, string $ownerRef, array $rows): void { $consultation->diagnoses()->delete(); foreach ($rows as $row) { if (empty($row['description'])) { continue; } Diagnosis::create([ 'owner_ref' => $ownerRef, 'consultation_id' => $consultation->id, 'code' => $row['code'] ?? null, 'description' => $row['description'], 'is_primary' => (bool) ($row['is_primary'] ?? false), 'notes' => $row['notes'] ?? null, ]); } } /** * @param array $files */ protected function storeDocuments(Consultation $consultation, string $ownerRef, array $files, ?string $actorRef): void { foreach ($files as $file) { if (! $file instanceof UploadedFile) { continue; } $path = $file->store("care/consultations/{$consultation->id}/documents", 'public'); ConsultationDocument::create([ 'owner_ref' => $ownerRef, 'consultation_id' => $consultation->id, 'file_path' => $path, 'original_name' => $file->getClientOriginalName(), 'mime_type' => $file->getMimeType(), 'uploaded_by' => $actorRef, ]); } } }