fileStorageService = $fileStorageService; $this->walletService = $walletService; $this->shiftScheduleService = $shiftScheduleService; $this->smsNotificationService = $smsNotificationService; } public function pendingForUser(User $user): Builder { $statuses = $this->isDoctor($user) ? [DocumentStatusEnum::InProgress->value] : [DocumentStatusEnum::Registered->value, DocumentStatusEnum::InProgress->value]; return Document::with(['pet', 'requester']) ->whereIn('status', $statuses) ->where(fn ($query) => $this->scopeVisibleTo($query, $user)) ->latest('submitted_at'); } public function canAccess(Document $document, User $user): bool { return $document->requester_id === $user->id || $document->assignments()->where('user_id', $user->id)->exists(); } public function canAccessMedium(Medium $medium, User $user): bool { $documents = $medium->documents()->get() ->merge($medium->reports()->with('document')->get()->pluck('document')); return $documents->contains(fn ($document) => $this->canAccess($document, $user)); } public function canReport(Document $document, User $user): bool { return $this->isDoctor($user) && $document->status === DocumentStatusEnum::InProgress && $document->assignments()->where('user_id', $user->id)->exists(); } public function submitReport(Document $document, User $doctor, array $data, array $files = []): Report { return DB::transaction(function () use ($document, $doctor, $data, $files) { if (!$this->canReport($document, $doctor)) { throw ValidationException::withMessages(['document' => 'امکان ثبت گزارش برای این پرونده وجود ندارد.']); } $report = $document->reports()->create([ 'doctor_id' => $doctor->id, 'status' => ReportStatusEnum::Approved->value, 'technique' => $data['technique'] ?? null, 'finding' => $data['finding'] ?? null, 'diagnose' => $data['diagnose'] ?? null, 'recommendation' => $data['recommendation'] ?? null, 'submitted_at' => now(), ]); foreach ($files as $file) { $medium = $this->fileStorageService->storeFile($file); $report->media()->attach($medium->id, ['collection' => MediaCollectionEnum::Attachments->value]); } $document->update([ 'status' => DocumentStatusEnum::Completed->value, 'finalized_at' => now(), 'finalized_by' => $doctor->id, ]); $this->smsNotificationService->reportReady($document); return $report; }); } public function reject(Document $document, User $doctor, string $reason): Document { return DB::transaction(function () use ($document, $doctor, $reason) { if (!$this->canReport($document, $doctor)) { throw ValidationException::withMessages(['document' => 'امکان رد این پرونده وجود ندارد.']); } $document->update([ 'status' => DocumentStatusEnum::Rejected->value, 'finalized_at' => now(), 'finalized_by' => $doctor->id, 'rejection_reason' => $reason, ]); $invoice = $document->invoice; if ($invoice && $invoice->status === InvoiceStatusEnum::Paid) { $invoice->update(['status' => InvoiceStatusEnum::Canceled->value]); $this->walletService->deposit($document->requester, $invoice->amount, WalletTransactionReasonEnum::Refund); } return $document; }); } public function statsForUser(User $user): array { $monthStart = now()->startOfMonth(); $isDoctor = $this->isDoctor($user); $reviewedQuery = Document::where('status', DocumentStatusEnum::Completed->value) ->where('finalized_at', '>=', $monthStart); if ($isDoctor) { $reviewedQuery->where('finalized_by', $user->id); } else { $reviewedQuery->where('requester_id', $user->id); } $amountQuery = $isDoctor ? Document::where('documents.finalized_by', $user->id) ->where('documents.status', DocumentStatusEnum::Completed->value) ->where('documents.finalized_at', '>=', $monthStart) ->join('invoices', 'invoices.document_id', '=', 'documents.id') ->where('invoices.status', InvoiceStatusEnum::Paid->value) : Document::where('documents.requester_id', $user->id) ->join('invoices', 'invoices.document_id', '=', 'documents.id') ->where('invoices.status', InvoiceStatusEnum::Paid->value) ->where('invoices.paid_at', '>=', $monthStart); $nextShift = $isDoctor ? $this->shiftScheduleService->nextShiftStart($user, now()) : null; $onShift = $isDoctor && $this->shiftScheduleService->isOnShift($user, now()); return [ 'pending_cases' => $this->pendingForUser($user)->count(), 'monthly_reviewed' => $reviewedQuery->count(), 'monthly_amount' => (int) $amountQuery->sum('invoices.amount'), 'next_shift_hours' => $onShift ? 0 : ($nextShift ? (int) ceil(now()->diffInMinutes($nextShift, true) / 60) : null), 'is_doctor' => $isDoctor, ]; } protected function scopeVisibleTo($query, User $user): void { if ($this->isDoctor($user)) { $query->whereHas('assignments', fn ($assignmentQuery) => $assignmentQuery->where('user_id', $user->id)); return; } $query->where('requester_id', $user->id) ->orWhereHas('assignments', fn ($assignmentQuery) => $assignmentQuery->where('user_id', $user->id)); } public function isDoctor(User $user): bool { return $user->level === UserEnum::Doctor->value; } }