'date', 'consultation_type' => ConsultationType::class, 'payment_status' => PaymentStatus::class, 'status' => ConsultationStatus::class, 'payment_amount' => 'decimal:2', 'payment_received_at' => 'datetime', 'admin_notes' => 'array', ]; } /** * Get the user that owns the consultation. */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Mark consultation as completed. * * @throws \InvalidArgumentException */ public function markAsCompleted(): void { if ($this->status !== ConsultationStatus::Approved) { throw new \InvalidArgumentException( __('messages.invalid_status_transition', ['from' => $this->status->value, 'to' => 'completed']) ); } $this->update(['status' => ConsultationStatus::Completed]); } /** * Mark consultation as no-show. * * @throws \InvalidArgumentException */ public function markAsNoShow(): void { if ($this->status !== ConsultationStatus::Approved) { throw new \InvalidArgumentException( __('messages.invalid_status_transition', ['from' => $this->status->value, 'to' => 'no_show']) ); } $this->update(['status' => ConsultationStatus::NoShow]); } /** * Cancel the consultation. * * @throws \InvalidArgumentException */ public function cancel(): void { if (! in_array($this->status, [ConsultationStatus::Pending, ConsultationStatus::Approved])) { throw new \InvalidArgumentException( __('messages.cannot_cancel_consultation') ); } $this->update(['status' => ConsultationStatus::Cancelled]); } /** * Mark payment as received. * * @throws \InvalidArgumentException */ public function markPaymentReceived(): void { if ($this->consultation_type !== ConsultationType::Paid) { throw new \InvalidArgumentException(__('messages.not_paid_consultation')); } if ($this->payment_status === PaymentStatus::Received) { throw new \InvalidArgumentException(__('messages.payment_already_received')); } $this->update([ 'payment_status' => PaymentStatus::Received, 'payment_received_at' => now(), ]); } /** * Reschedule the consultation to a new date and time. */ public function reschedule(string $newDate, string $newTime): void { $this->update([ 'booking_date' => $newDate, 'booking_time' => $newTime, ]); } /** * Add an admin note to the consultation. */ public function addNote(string $note, int $adminId): void { $notes = $this->admin_notes ?? []; $notes[] = [ 'text' => $note, 'admin_id' => $adminId, 'created_at' => now()->toISOString(), ]; $this->update(['admin_notes' => $notes]); } /** * Update an admin note at the given index. */ public function updateNote(int $index, string $newText): void { $notes = $this->admin_notes ?? []; if (isset($notes[$index])) { $notes[$index]['text'] = $newText; $notes[$index]['updated_at'] = now()->toISOString(); $this->update(['admin_notes' => $notes]); } } /** * Delete an admin note at the given index. */ public function deleteNote(int $index): void { $notes = $this->admin_notes ?? []; if (isset($notes[$index])) { array_splice($notes, $index, 1); $this->update(['admin_notes' => array_values($notes)]); } } /** * Scope for upcoming approved consultations. */ public function scopeUpcoming(Builder $query): Builder { return $query->where('booking_date', '>=', today()) ->where('status', ConsultationStatus::Approved); } /** * Scope for past consultations. */ public function scopePast(Builder $query): Builder { return $query->where(function ($q) { $q->where('booking_date', '<', today()) ->orWhereIn('status', [ ConsultationStatus::Completed, ConsultationStatus::Cancelled, ConsultationStatus::NoShow, ]); }); } }