user(); Gate::authorize('viewAny', Call::class); $query = Call::with('lead:id,first_name,last_name,company,phone', 'user:id,name', 'contact:id,name,role', 'contactPhone:id,phone,type,status'); AccessControl::scopeCalls($query, $user); if ($request->lead_id) { $query->where('lead_id', $request->lead_id); } if ($request->user_id) { $query->where('user_id', $request->user_id); } if ($request->direction) { $query->where('direction', $request->direction); } if ($request->result) { $query->where('result', $request->result); } if ($request->date_from) { $query->whereDate('created_at', '>=', $request->date_from); } if ($request->date_to) { $query->whereDate('created_at', '<=', $request->date_to); } if ($request->filled('search')) { $search = trim((string) $request->get('search')); $query->where(function ($searchQuery) use ($search): void { $searchQuery->where('provider_call_id', 'like', "%{$search}%") ->orWhereHas('lead', fn ($leadQuery) => $leadQuery ->where('first_name', 'like', "%{$search}%") ->orWhere('last_name', 'like', "%{$search}%") ->orWhere('company', 'like', "%{$search}%")) ->orWhereHas('contact', fn ($contactQuery) => $contactQuery->where('name', 'like', "%{$search}%")); }); } $paginator = $query->orderByDesc('created_at')->paginate(min((int) $request->get('per_page', 15), 100)); return ApiResponse::paginated($paginator, fn (Call $call) => $this->resource($call)); } public function results(): JsonResponse { return ApiResponse::success( CallResult::where('is_active', true) ->orderBy('sort_order') ->get(['id', 'name', 'slug', 'color', 'requires_follow_up', 'is_positive', 'is_negative', 'is_final']) ->toArray() ); } public function store(Request $request): JsonResponse { $validated = $request->validate([ 'lead_id' => 'required|exists:leads,id', 'contact_phone_id' => 'nullable|exists:contact_phones,id', ]); $lead = Lead::findOrFail($validated['lead_id']); Gate::authorize('create', [Call::class, $lead]); $result = $this->callService->initiateCall($validated['lead_id'], auth()->id(), $validated['contact_phone_id'] ?? null); return ApiResponse::success($result, 201, 'تماس آغاز شد.'); } public function show(Call $call): JsonResponse { Gate::authorize('view', $call); if ($call->recording_url && Gate::allows('viewRecording', $call)) { ActivityLogger::log('recording_viewed', "Recording viewed for call {$call->id}", $call); } return ApiResponse::success($this->resource($call->load('lead', 'user', 'contact', 'contactPhone'))); } public function registerResult(Request $request): JsonResponse { $validated = $request->validate([ 'call_id' => 'required|exists:calls,id', 'result' => 'required|string|max:50', 'notes' => 'nullable|string', 'next_follow_up_at' => 'nullable|date', 'referral' => 'nullable|array', 'referral.name' => 'required_with:referral|string|max:255', 'referral.phone' => 'required_with:referral|string|max:30', 'referral.phone_type' => 'nullable|string|in:mobile,landline,extension', 'referral.role' => 'nullable|string|max:80', 'referral.relation_description' => 'nullable|string|max:255', 'referral.description' => 'nullable|string', 'referral.make_primary' => 'nullable|boolean', 'referral.next_follow_up_at' => 'nullable|date', ]); $call = Call::with('lead')->findOrFail($validated['call_id']); Gate::authorize('registerResult', $call); $callResult = CallResult::where('name', $validated['result'])->first(); if ($callResult?->requires_follow_up && empty($validated['next_follow_up_at']) && empty($validated['referral']['next_follow_up_at'])) { return response()->json(['message' => 'برای این نتیجه تماس، زمان پیگیری الزامی است'], 422); } $call = $this->callService->registerResult( $validated['call_id'], $validated['result'], $validated['notes'] ?? null, $validated['next_follow_up_at'] ?? null, $validated['referral'] ?? null ); return ApiResponse::success($this->resource($call->loadMissing('lead', 'user', 'contact', 'contactPhone')), message: 'نتیجه تماس ثبت شد.'); } public function manualResult(Request $request): JsonResponse { $validated = $request->validate([ 'lead_id' => 'required|exists:leads,id', 'contact_phone_id' => 'required|exists:contact_phones,id', 'result' => 'required|string|max:50', 'notes' => 'nullable|string', 'next_follow_up_at' => 'nullable|date', 'referral' => 'nullable|array', 'referral.name' => 'required_with:referral|string|max:255', 'referral.phone' => 'required_with:referral|string|max:30', 'referral.phone_type' => 'nullable|string|in:mobile,landline,extension', 'referral.role' => 'nullable|string|max:80', 'referral.relation_description' => 'nullable|string|max:255', 'referral.description' => 'nullable|string', 'referral.make_primary' => 'nullable|boolean', 'referral.next_follow_up_at' => 'nullable|date', ]); $lead = Lead::findOrFail($validated['lead_id']); Gate::authorize('create', [Call::class, $lead]); $callResult = CallResult::where('name', $validated['result'])->first(); if ($callResult?->requires_follow_up && empty($validated['next_follow_up_at']) && empty($validated['referral']['next_follow_up_at'])) { return response()->json(['message' => 'برای این نتیجه تماس، زمان پیگیری الزامی است'], 422); } $call = $this->callService->recordManualResult( $lead->id, (int) $request->user()->id, (int) $validated['contact_phone_id'], $validated['result'], $validated['notes'] ?? null, $validated['next_follow_up_at'] ?? null, $validated['referral'] ?? null, ); return ApiResponse::success( $this->resource($call->loadMissing('lead', 'user', 'contact', 'contactPhone')), 201, 'تماس و نتیجه آن ثبت شد.' ); } private function resource(Call $call): array { return (new CallResource($call))->resolve(request()); } }