189 خطوط
7.6 KiB
PHP
189 خطوط
7.6 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Resources\CallResource;
|
|
use App\Http\Responses\ApiResponse;
|
|
use App\Models\Call;
|
|
use App\Models\CallResult;
|
|
use App\Models\Lead;
|
|
use App\Services\ActivityLogger;
|
|
use App\Services\CallService;
|
|
use App\Services\VoIP\VoIPManager;
|
|
use App\Support\AccessControl;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\Gate;
|
|
|
|
class CallController extends Controller
|
|
{
|
|
public function __construct(
|
|
private CallService $callService,
|
|
private VoIPManager $voipManager
|
|
) {}
|
|
|
|
public function index(Request $request): JsonResponse
|
|
{
|
|
$user = auth()->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());
|
|
}
|
|
}
|