id) ->where('listing_id', $listing->id) ->whereIn('status', ['open', 'in_progress', 'review']) ->first(); if ($existing && empty($options['force_new'])) { return $existing->load('tasks'); } return DB::transaction(function () use ($business, $listing, $options, $user) { $case = ResearchCase::create([ 'business_id' => $business->id, 'listing_id' => $listing->id, 'case_code' => $options['case_code'] ?? $this->aexUtil->generateResearchCaseCode($business->id), 'title' => $options['title'] ?? ('پرونده تحقیق — '.$listing->title), 'status' => 'open', 'lead_researcher_id' => $options['lead_researcher_id'] ?? $listing->assigned_broker_user_id, 'started_at' => now(), 'created_by' => $user?->id, ]); if (! empty($options['spawn_templates'])) { $this->spawnTasksFromTemplates($business, $case, $options['template_keys'] ?? null); } if (! empty($options['tasks']) && is_array($options['tasks'])) { foreach ($options['tasks'] as $taskData) { $this->addTask($business, $case, $taskData); } } $this->pipelineService->logEvent( $business, ResearchCase::class, $case->id, 'research_case_opened', 'پرونده تحقیق باز شد', $user ); return $case->fresh(['tasks.assignee']); }); } public function spawnTasksFromTemplates(Business $business, ResearchCase $case, ?array $templateKeys = null): int { $query = ResearchTaskTemplate::query() ->where('is_active', true) ->where(function ($q) use ($business) { $q->whereNull('business_id')->orWhere('business_id', $business->id); }) ->orderBy('sort_order'); if ($templateKeys) { $query->whereIn('template_key', $templateKeys); } $templates = $query->get(); $created = 0; foreach ($templates as $tpl) { $exists = ResearchTask::where('research_case_id', $case->id) ->where('template_key', $tpl->template_key) ->exists(); if ($exists) { continue; } $this->addTask($business, $case, [ 'template_key' => $tpl->template_key, 'category' => $tpl->category, 'title' => $tpl->title, 'description' => $tpl->description, 'instructions' => $tpl->instructions, 'priority' => $tpl->default_priority, 'sort_order' => $tpl->sort_order, ]); $created++; } return $created; } public function addTask(Business $business, ResearchCase $case, array $data): ResearchTask { $task = ResearchTask::create([ 'business_id' => $business->id, 'research_case_id' => $case->id, 'template_key' => $data['template_key'] ?? null, 'category' => $data['category'] ?? 'general', 'title' => $data['title'], 'description' => $data['description'] ?? null, 'instructions' => $data['instructions'] ?? null, 'assignee_user_id' => $data['assignee_user_id'] ?? null, 'status' => 'pending', 'priority' => $data['priority'] ?? 'normal', 'due_date' => ! empty($data['due_date']) ? $this->aexUtil->parseInputDate($data['due_date']) : null, 'sort_order' => (int) ($data['sort_order'] ?? 0), ]); if (! empty($data['sync_crm_schedule'])) { $this->syncTaskToCrmSchedule($business, $case, $task); } return $task; } public function assignTask(ResearchTask $task, int $userId, bool $syncCrm = true): ResearchTask { $task->update(['assignee_user_id' => $userId, 'status' => 'in_progress']); $case = $task->researchCase; if ($case) { $business = Business::find($case->business_id); if ($syncCrm && $business) { $this->syncTaskToCrmSchedule($business, $case, $task->fresh()); } } return $task->fresh(['assignee']); } public function completeTask(ResearchTask $task, array $data, ?User $user = null): ResearchTask { $task->update([ 'status' => 'completed', 'findings' => $data['findings'] ?? $task->findings, 'findings_json' => $data['findings_json'] ?? $task->findings_json, 'completed_at' => now(), ]); $case = $task->researchCase; if ($case) { $open = $case->tasks()->whereNotIn('status', ['completed', 'cancelled'])->count(); if ($open === 0) { $case->update(['status' => 'review']); } else { $case->update(['status' => 'in_progress']); } } return $task->fresh(); } public function closeCase(ResearchCase $case, ?string $summary = null, ?User $user = null): ResearchCase { $case->update([ 'status' => 'closed', 'summary' => $summary ?? $case->summary, 'closed_at' => now(), ]); return $case->fresh(['tasks']); } public function syncTaskToCrmSchedule(Business $business, ResearchCase $case, ResearchTask $task): ?int { if (! class_exists(\Modules\Crm\Entities\Schedule::class)) { return null; } if (! Schema::hasTable('crm_schedules')) { return null; } $case->loadMissing('listing'); $contactId = $case->listing?->seller_contact_id; if (! $contactId) { return null; } $start = $task->due_date ? $task->due_date->copy()->setTime(9, 0)->toDateTimeString() : now()->addDay()->setTime(9, 0)->toDateTimeString(); $schedule = \Modules\Crm\Entities\Schedule::create([ 'business_id' => $business->id, 'contact_id' => $contactId, 'title' => '[AEX] '.$task->title, 'description' => trim(($task->description ?? '')."\n".($task->instructions ?? '')), 'start_datetime' => $start, 'end_datetime' => date('Y-m-d H:i:s', strtotime($start.' +1 hour')), 'schedule_type' => 'call', 'status' => 'open', 'followup_additional_info' => [ 'aex_research_task_id' => $task->id, 'aex_research_case_id' => $case->id, 'aex_listing_id' => $case->listing_id, ], 'created_by' => auth()->id(), ]); if ($task->assignee_user_id && Schema::hasTable('crm_schedule_users')) { DB::table('crm_schedule_users')->insert([ 'schedule_id' => $schedule->id, 'user_id' => $task->assignee_user_id, ]); } $task->update(['crm_schedule_id' => $schedule->id]); return $schedule->id; } }