626 خطوط
25 KiB
PHP
626 خطوط
25 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Api;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Call;
|
|
use App\Models\CallResult;
|
|
use App\Models\Contact;
|
|
use App\Models\ContactPhone;
|
|
use App\Models\ContactRelation;
|
|
use App\Models\FollowUp;
|
|
use App\Models\Lead;
|
|
use App\Services\ActivityLogger;
|
|
use App\Services\CallService;
|
|
use App\Services\FollowUpService;
|
|
use Illuminate\Http\JsonResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Validation\Rule;
|
|
|
|
class AgentMobileController extends Controller
|
|
{
|
|
public function __construct(private CallService $callService, private FollowUpService $followUps) {}
|
|
|
|
public function bootstrap(Request $request): JsonResponse
|
|
{
|
|
return response()->json([
|
|
'data' => [
|
|
'preferred_view' => $request->user()->sales_agent_preferred_view ?? 'auto',
|
|
'call_results' => CallResult::where('is_active', true)
|
|
->orderBy('sort_order')
|
|
->get(['name', 'slug', 'requires_follow_up', 'is_final'])
|
|
->map(fn (CallResult $result) => [
|
|
'name' => $result->name,
|
|
'slug' => $result->slug,
|
|
'requires_follow_up' => $result->requires_follow_up,
|
|
'is_final' => $result->is_final,
|
|
])
|
|
->values(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function dashboard(Request $request): JsonResponse
|
|
{
|
|
$user = $request->user();
|
|
$today = now()->toDateString();
|
|
$leadQuery = $this->assignedLeadQuery($user->id);
|
|
$callQuery = Call::where('user_id', $user->id)->whereDate('created_at', $today);
|
|
$followUpQuery = FollowUp::where('user_id', $user->id);
|
|
$nextLead = $this->queueQuery($user->id)->first();
|
|
|
|
return response()->json([
|
|
'data' => [
|
|
'stats' => [
|
|
'today_calls_count' => (clone $callQuery)->count(),
|
|
'today_follow_ups_count' => (clone $followUpQuery)->whereDate('scheduled_at', $today)->where('status', 'pending')->count(),
|
|
'overdue_follow_ups_count' => (clone $followUpQuery)->where('status', 'pending')->where('scheduled_at', '<', now())->count(),
|
|
'new_assigned_leads_count' => (clone $leadQuery)->whereDate('created_at', $today)->count(),
|
|
'successful_calls_today' => (clone $callQuery)->whereIn('result', $this->successfulResults())->count(),
|
|
'failed_calls_today' => (clone $callQuery)->whereNotNull('result')->whereNotIn('result', $this->successfulResults())->count(),
|
|
'leads_needing_action' => $this->queueQuery($user->id)->count(),
|
|
],
|
|
'suggested_call' => $nextLead ? $this->leadCard($nextLead) : null,
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function callQueue(Request $request): JsonResponse
|
|
{
|
|
$query = $this->queueQuery($request->user()->id);
|
|
$this->applyMobileFilter($query, $request->query('filter'));
|
|
|
|
return response()->json([
|
|
'data' => $query->limit(30)->get()->map(fn (Lead $lead) => $this->leadCard($lead))->values(),
|
|
]);
|
|
}
|
|
|
|
public function leads(Request $request): JsonResponse
|
|
{
|
|
$query = $this->assignedLeadQuery($request->user()->id);
|
|
$this->applyMobileFilter($query, $request->query('filter'));
|
|
|
|
if ($request->query('filter') === 'interested') {
|
|
$query->where(function ($q) {
|
|
$q->where('interest_level', 'hot')
|
|
->orWhere('last_call_result', 'علاقهمند بود')
|
|
->orWhereHas('pipelineStage', fn ($stage) => $stage->where('name', 'like', '%علاقهمند%'));
|
|
});
|
|
}
|
|
|
|
if ($request->query('filter') === 'proposal') {
|
|
$query->whereHas('pipelineStage', fn ($stage) => $stage->where('name', 'like', '%پیشنهاد%'));
|
|
}
|
|
|
|
if ($request->query('filter') === 'closed') {
|
|
$query->whereNotNull('final_result');
|
|
}
|
|
|
|
return response()->json([
|
|
'data' => $query
|
|
->orderByDesc('priority')
|
|
->orderByRaw('next_follow_up_at IS NULL')
|
|
->orderBy('next_follow_up_at')
|
|
->limit(40)
|
|
->get()
|
|
->map(fn (Lead $lead) => $this->leadCard($lead))
|
|
->values(),
|
|
]);
|
|
}
|
|
|
|
public function lead(Request $request, Lead $lead): JsonResponse
|
|
{
|
|
if ($lead->assigned_to !== $request->user()->id) {
|
|
return response()->json(['message' => 'دسترسی غیرمجاز'], 403);
|
|
}
|
|
|
|
$lead->load([
|
|
'leadStatus:id,name,color',
|
|
'pipelineStage:id,name,color',
|
|
'contacts.phones:id,contact_id,type,status,call_count,successful_call_count,failed_call_count,last_called_at,last_call_result',
|
|
'contactRelations.fromContact:id,name,role',
|
|
'contactRelations.toContact:id,name,role',
|
|
'calls' => fn ($q) => $q->with('contact:id,name,role')->latest()->limit(12),
|
|
'callLogs' => fn ($q) => $q->with('contact:id,name,role')->latest('called_at')->limit(12),
|
|
'followUps' => fn ($q) => $q->latest('scheduled_at')->limit(8),
|
|
]);
|
|
|
|
$primary = $this->primaryContact($lead);
|
|
|
|
return response()->json([
|
|
'data' => [
|
|
'card' => $this->leadCard($lead),
|
|
'primary_contact' => $primary ? $this->contactSummary($primary) : null,
|
|
'summary' => [
|
|
'id' => $lead->id,
|
|
'name' => $this->leadName($lead),
|
|
'company' => $lead->company,
|
|
'city' => $lead->city,
|
|
'province' => $lead->province,
|
|
'source' => $lead->source,
|
|
'interest_level' => $lead->interest_level,
|
|
'priority' => $lead->priority,
|
|
'score' => $lead->lead_score,
|
|
'status' => $lead->leadStatus?->name,
|
|
'stage' => $lead->pipelineStage?->name,
|
|
'notes' => $lead->notes,
|
|
'tags' => $this->tags($lead),
|
|
'final_result' => $lead->final_result,
|
|
],
|
|
'calls' => $lead->calls->map(fn (Call $call) => [
|
|
'id' => $call->id,
|
|
'contact_name' => $call->contact?->name,
|
|
'result' => $call->result,
|
|
'notes' => $call->notes,
|
|
'created_at' => optional($call->created_at)->toIso8601String(),
|
|
])->values(),
|
|
'contacts' => $lead->contacts->map(fn ($contact) => $this->contactSummary($contact))->values(),
|
|
'notes' => array_values(array_filter([
|
|
$lead->notes ? ['id' => 'lead-notes', 'text' => $lead->notes, 'created_at' => optional($lead->updated_at)->toIso8601String()] : null,
|
|
$lead->customer_notes ? ['id' => 'customer-notes', 'text' => $lead->customer_notes, 'created_at' => optional($lead->updated_at)->toIso8601String()] : null,
|
|
])),
|
|
'files' => [],
|
|
'contact_routes' => $lead->contactRelations->map(fn ($relation) => [
|
|
'id' => $relation->id,
|
|
'from' => $relation->fromContact?->name,
|
|
'to' => $relation->toContact?->name,
|
|
'type' => $relation->relation_type,
|
|
'description' => $relation->description,
|
|
])->values(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function followUps(Request $request): JsonResponse
|
|
{
|
|
$items = FollowUp::with([
|
|
'lead:id,first_name,last_name,company,lead_status_id,pipeline_stage_id,last_call_result,next_follow_up_at,assigned_to',
|
|
'lead.leadStatus:id,name,color',
|
|
'lead.pipelineStage:id,name,color',
|
|
'lead.contacts.phones:id,contact_id,type,status,call_count,last_call_result,last_called_at',
|
|
])
|
|
->where('user_id', $request->user()->id)
|
|
->where('scheduled_at', '<=', now()->addWeek())
|
|
->orderBy('scheduled_at')
|
|
->limit(80)
|
|
->get()
|
|
->map(fn (FollowUp $followUp) => $this->followUpCard($followUp));
|
|
|
|
return response()->json([
|
|
'data' => [
|
|
'overdue' => $items->where('group', 'overdue')->values(),
|
|
'today' => $items->where('group', 'today')->values(),
|
|
'tomorrow' => $items->where('group', 'tomorrow')->values(),
|
|
'week' => $items->where('group', 'week')->values(),
|
|
'done' => $items->where('group', 'done')->values(),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function preferredView(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'preferred_view' => ['required', Rule::in(['auto', 'mobile', 'desktop'])],
|
|
]);
|
|
|
|
$user = $request->user();
|
|
$user->forceFill([
|
|
'sales_agent_preferred_view' => $validated['preferred_view'],
|
|
])->save();
|
|
|
|
ActivityLogger::log('agent_mobile_preferred_view_updated', "User {$user->phone} updated Sales Agent preferred view");
|
|
|
|
$user->load('roles.permissions', 'teams');
|
|
$user->permissions = $user->getAllPermissions()->pluck('name');
|
|
|
|
return response()->json(['data' => $user]);
|
|
}
|
|
|
|
public function startCall(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']);
|
|
if (! $this->canAccessLead($request, $lead)) {
|
|
return response()->json(['message' => 'دسترسی غیرمجاز'], 403);
|
|
}
|
|
|
|
if (! empty($validated['contact_phone_id'])) {
|
|
$belongsToLead = ContactPhone::where('id', $validated['contact_phone_id'])
|
|
->whereHas('contact', fn ($query) => $query->where('lead_id', $lead->id))
|
|
->exists();
|
|
|
|
if (! $belongsToLead) {
|
|
return response()->json(['message' => 'شماره انتخابشده برای این لید معتبر نیست'], 422);
|
|
}
|
|
}
|
|
|
|
$payload = $this->callService->initiateCall(
|
|
$lead->id,
|
|
$request->user()->id,
|
|
$validated['contact_phone_id'] ?? null,
|
|
false
|
|
);
|
|
unset($payload['recording_url'], $payload['provider_call_id']);
|
|
|
|
return response()->json(['data' => $payload], 201);
|
|
}
|
|
|
|
public function callResult(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'call_id' => 'required|exists:calls,id',
|
|
'result' => ['required', Rule::in($this->mobileCallResults())],
|
|
'notes' => 'nullable|string|max:2000',
|
|
'next_follow_up_at' => 'nullable|date',
|
|
'mark_phone_wrong' => 'nullable|boolean',
|
|
'disinterest_reason' => 'nullable|string|max:500',
|
|
'close_lead' => 'nullable|boolean',
|
|
'referral' => 'nullable|array',
|
|
'referral.name' => 'required_if:result,معرفی شماره جدید|string|max:255',
|
|
'referral.phone' => 'required_if:result,معرفی شماره جدید|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|max:1000',
|
|
'referral.make_primary' => 'nullable|boolean',
|
|
'referral.next_follow_up_at' => 'nullable|date',
|
|
]);
|
|
|
|
$call = Call::with('lead')->findOrFail($validated['call_id']);
|
|
if ($call->user_id !== $request->user()->id || ! $call->lead || ! $this->canAccessLead($request, $call->lead)) {
|
|
return response()->json(['message' => 'دسترسی غیرمجاز'], 403);
|
|
}
|
|
|
|
$followUpAt = $validated['next_follow_up_at'] ?? $validated['referral']['next_follow_up_at'] ?? null;
|
|
$callResult = CallResult::where('name', $validated['result'])->first();
|
|
if ($callResult?->requires_follow_up && ! $followUpAt) {
|
|
return response()->json(['message' => 'برای این نتیجه تماس، زمان پیگیری الزامی است'], 422);
|
|
}
|
|
|
|
$registeredCall = $this->callService->registerResult(
|
|
$call->id,
|
|
$validated['result'],
|
|
$validated['notes'] ?? null,
|
|
$followUpAt,
|
|
$validated['referral'] ?? null,
|
|
[
|
|
'mark_phone_wrong' => (bool) ($validated['mark_phone_wrong'] ?? false),
|
|
'disinterest_reason' => $validated['disinterest_reason'] ?? null,
|
|
'close_lead' => (bool) ($validated['close_lead'] ?? false),
|
|
]
|
|
);
|
|
|
|
return response()->json([
|
|
'message' => 'نتیجه تماس ثبت شد',
|
|
'data' => [
|
|
'call_id' => $registeredCall->id,
|
|
'lead_id' => $registeredCall->lead_id,
|
|
'result' => $registeredCall->result,
|
|
'next_call' => optional($this->queueQuery($request->user()->id)->where('id', '!=', $registeredCall->lead_id)->first(), fn ($lead) => $this->leadCard($lead)),
|
|
],
|
|
]);
|
|
}
|
|
|
|
public function storeFollowUp(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'lead_id' => 'required|exists:leads,id',
|
|
'call_id' => 'nullable|exists:calls,id',
|
|
'scheduled_at' => 'required|date',
|
|
'notes' => 'nullable|string|max:2000',
|
|
]);
|
|
|
|
$lead = Lead::findOrFail($validated['lead_id']);
|
|
if (! $this->canAccessLead($request, $lead)) {
|
|
return response()->json(['message' => 'دسترسی غیرمجاز'], 403);
|
|
}
|
|
|
|
if (! empty($validated['call_id']) && ! Call::where('id', $validated['call_id'])->where('user_id', $request->user()->id)->where('lead_id', $lead->id)->exists()) {
|
|
return response()->json(['message' => 'تماس انتخابشده معتبر نیست'], 422);
|
|
}
|
|
|
|
$followUp = $this->followUps->schedule(
|
|
$lead,
|
|
$request->user()->id,
|
|
$validated['scheduled_at'],
|
|
$request->user(),
|
|
$validated['notes'] ?? null,
|
|
$validated['call_id'] ?? null,
|
|
'agent_mobile',
|
|
);
|
|
|
|
return response()->json(['data' => $this->followUpCard($followUp->load('lead.contacts.phones'))], 201);
|
|
}
|
|
|
|
public function newContact(Request $request): JsonResponse
|
|
{
|
|
$validated = $request->validate([
|
|
'lead_id' => 'required|exists:leads,id',
|
|
'from_contact_id' => 'nullable|exists:contacts,id',
|
|
'name' => 'required|string|max:255',
|
|
'phone' => 'required|string|max:30',
|
|
'phone_type' => 'nullable|string|in:mobile,landline,extension',
|
|
'role' => 'nullable|string|max:80',
|
|
'relation_description' => 'nullable|string|max:255',
|
|
'description' => 'nullable|string|max:1000',
|
|
'make_primary' => 'nullable|boolean',
|
|
]);
|
|
|
|
$lead = Lead::findOrFail($validated['lead_id']);
|
|
if (! $this->canAccessLead($request, $lead)) {
|
|
return response()->json(['message' => 'دسترسی غیرمجاز'], 403);
|
|
}
|
|
|
|
if (! empty($validated['from_contact_id']) && ! Contact::where('id', $validated['from_contact_id'])->where('lead_id', $lead->id)->exists()) {
|
|
return response()->json(['message' => 'مخاطب معرف برای این لید معتبر نیست'], 422);
|
|
}
|
|
|
|
$contact = DB::transaction(function () use ($validated, $lead, $request) {
|
|
if ($validated['make_primary'] ?? false) {
|
|
Contact::where('lead_id', $lead->id)->update(['is_primary' => false]);
|
|
}
|
|
|
|
$contact = Contact::create([
|
|
'lead_id' => $lead->id,
|
|
'name' => $validated['name'],
|
|
'role' => $validated['role'] ?? null,
|
|
'description' => $validated['description'] ?? null,
|
|
'status' => 'active',
|
|
'is_primary' => (bool) ($validated['make_primary'] ?? false),
|
|
'primary_reason' => ($validated['make_primary'] ?? false) ? 'ثبتشده از نسخه موبایل' : null,
|
|
'created_by' => $request->user()->id,
|
|
]);
|
|
|
|
ContactPhone::create([
|
|
'contact_id' => $contact->id,
|
|
'phone' => $validated['phone'],
|
|
'type' => $validated['phone_type'] ?? 'mobile',
|
|
'status' => 'active',
|
|
]);
|
|
|
|
ContactRelation::create([
|
|
'lead_id' => $lead->id,
|
|
'from_contact_id' => $validated['from_contact_id'] ?? null,
|
|
'to_contact_id' => $contact->id,
|
|
'relation_type' => 'introduced',
|
|
'description' => $validated['relation_description'] ?? null,
|
|
'created_by' => $request->user()->id,
|
|
]);
|
|
|
|
return $contact->load('phones');
|
|
});
|
|
|
|
ActivityLogger::log('agent_mobile_contact_created', "Mobile contact {$contact->name} created for lead {$lead->id}", $contact);
|
|
|
|
return response()->json(['data' => $this->contactSummary($contact)], 201);
|
|
}
|
|
|
|
private function assignedLeadQuery(int $userId)
|
|
{
|
|
return Lead::query()
|
|
->with([
|
|
'leadStatus:id,name,color',
|
|
'pipelineStage:id,name,color',
|
|
'contacts.phones:id,contact_id,type,status,call_count,last_call_result,last_called_at',
|
|
])
|
|
->where('assigned_to', $userId);
|
|
}
|
|
|
|
private function canAccessLead(Request $request, Lead $lead): bool
|
|
{
|
|
return (int) $lead->assigned_to === (int) $request->user()->id;
|
|
}
|
|
|
|
private function mobileCallResults(): array
|
|
{
|
|
$results = CallResult::where('is_active', true)->orderBy('sort_order')->pluck('name')->all();
|
|
|
|
return array_values(array_unique(array_merge($results, [
|
|
'پاسخ داد',
|
|
'جواب نداد',
|
|
'اشغال بود',
|
|
'بعداً تماس بگیر',
|
|
'علاقهمند است',
|
|
'پیشنهاد ارسال شد',
|
|
'شماره اشتباه',
|
|
'معرفی شماره جدید',
|
|
'عدم تمایل',
|
|
])));
|
|
}
|
|
|
|
private function queueQuery(int $userId)
|
|
{
|
|
return $this->assignedLeadQuery($userId)
|
|
->where(function ($query) {
|
|
$query->whereNull('last_call_at')
|
|
->orWhere('next_follow_up_at', '<=', now())
|
|
->orWhereHas('followUps', fn ($followUp) => $followUp->where('status', 'pending')->where('scheduled_at', '<=', now()));
|
|
})
|
|
->orderByDesc('priority')
|
|
->orderByRaw('next_follow_up_at IS NULL')
|
|
->orderBy('next_follow_up_at')
|
|
->orderBy('created_at');
|
|
}
|
|
|
|
private function applyMobileFilter($query, ?string $filter): void
|
|
{
|
|
match ($filter) {
|
|
'today' => $query->whereDate('next_follow_up_at', now()->toDateString()),
|
|
'overdue' => $query->where('next_follow_up_at', '<', now()),
|
|
'no-call' => $query->whereNull('last_call_at'),
|
|
'high-priority' => $query->where('priority', '>=', 7),
|
|
default => null,
|
|
};
|
|
}
|
|
|
|
private function leadCard(Lead $lead): array
|
|
{
|
|
$primary = $this->primaryContact($lead);
|
|
|
|
return [
|
|
'id' => $lead->id,
|
|
'lead_name' => $this->leadName($lead),
|
|
'main_contact_name' => $primary?->name,
|
|
'main_contact_role' => $primary?->role,
|
|
'main_contact_phone_id' => $primary?->phones?->first()?->id,
|
|
'lead_status' => $lead->leadStatus?->name,
|
|
'lead_status_color' => $lead->leadStatus?->color,
|
|
'stage' => $lead->pipelineStage?->name,
|
|
'stage_color' => $lead->pipelineStage?->color,
|
|
'last_call_result' => $lead->last_call_result,
|
|
'last_call_at' => optional($lead->last_call_at)->toIso8601String(),
|
|
'follow_up_time' => optional($lead->next_follow_up_at)->toIso8601String(),
|
|
'priority' => $lead->priority ?? 0,
|
|
'priority_label' => $this->priorityLabel($lead->priority ?? 0),
|
|
'short_note' => $lead->notes ? mb_strimwidth($lead->notes, 0, 120, '...') : null,
|
|
'next_action' => $this->nextAction($lead),
|
|
'follow_up_status' => $this->followUpStatus($lead),
|
|
'tags' => array_slice($this->tags($lead), 0, 4),
|
|
'call_attempts' => $lead->call_attempts ?? 0,
|
|
];
|
|
}
|
|
|
|
private function followUpCard(FollowUp $followUp): array
|
|
{
|
|
$lead = $followUp->lead;
|
|
$primary = $lead ? $this->primaryContact($lead) : null;
|
|
$scheduled = $followUp->scheduled_at;
|
|
|
|
return [
|
|
'id' => $followUp->id,
|
|
'lead_id' => $lead?->id,
|
|
'lead_name' => $lead ? $this->leadName($lead) : 'لید حذفشده',
|
|
'contact_name' => $primary?->name,
|
|
'contact_phone_id' => $primary?->phones?->first()?->id,
|
|
'follow_up_time' => optional($scheduled)->toIso8601String(),
|
|
'follow_up_type' => $followUp->call_id ? 'بعد از تماس' : 'پیگیری',
|
|
'last_call_result' => $lead?->last_call_result,
|
|
'notes' => $followUp->notes,
|
|
'status' => $followUp->status,
|
|
'group' => $this->followUpGroup($followUp),
|
|
];
|
|
}
|
|
|
|
private function contactSummary($contact): array
|
|
{
|
|
$phones = $contact->phones ?? collect();
|
|
$mainPhone = $phones->first();
|
|
|
|
return [
|
|
'id' => $contact->id,
|
|
'name' => $contact->name,
|
|
'role' => $contact->role,
|
|
'description' => $contact->description,
|
|
'status' => $contact->status,
|
|
'is_primary' => (bool) $contact->is_primary,
|
|
'phone_id' => $mainPhone?->id,
|
|
'phone_type' => $mainPhone?->type,
|
|
'phone_status' => $mainPhone?->status,
|
|
'call_count' => (int) $phones->sum('call_count'),
|
|
'successful_call_count' => (int) $phones->sum('successful_call_count'),
|
|
'failed_call_count' => (int) $phones->sum('failed_call_count'),
|
|
'last_call_result' => $mainPhone?->last_call_result,
|
|
'last_called_at' => optional($mainPhone?->last_called_at)->toIso8601String(),
|
|
];
|
|
}
|
|
|
|
private function primaryContact(Lead $lead)
|
|
{
|
|
$contacts = $lead->relationLoaded('contacts') ? $lead->contacts : $lead->contacts()->with('phones')->get();
|
|
|
|
return $contacts->firstWhere('is_primary', true) ?? $contacts->first();
|
|
}
|
|
|
|
private function leadName(Lead $lead): string
|
|
{
|
|
return trim("{$lead->first_name} {$lead->last_name}") ?: ($lead->company ?: "لید {$lead->id}");
|
|
}
|
|
|
|
private function tags(Lead $lead): array
|
|
{
|
|
if (! $lead->tags) {
|
|
return [];
|
|
}
|
|
|
|
$decoded = json_decode($lead->tags, true);
|
|
if (is_array($decoded)) {
|
|
return array_values(array_filter($decoded, fn ($tag) => is_string($tag) && $tag !== ''));
|
|
}
|
|
|
|
return array_values(array_filter(array_map('trim', explode(',', $lead->tags))));
|
|
}
|
|
|
|
private function priorityLabel(int $priority): string
|
|
{
|
|
if ($priority >= 8) {
|
|
return 'خیلی بالا';
|
|
}
|
|
if ($priority >= 5) {
|
|
return 'بالا';
|
|
}
|
|
if ($priority >= 2) {
|
|
return 'متوسط';
|
|
}
|
|
|
|
return 'عادی';
|
|
}
|
|
|
|
private function nextAction(Lead $lead): string
|
|
{
|
|
if (! $lead->last_call_at) {
|
|
return 'تماس اول';
|
|
}
|
|
if ($lead->next_follow_up_at && $lead->next_follow_up_at->isPast()) {
|
|
return 'پیگیری عقبافتاده';
|
|
}
|
|
if ($lead->next_follow_up_at && $lead->next_follow_up_at->isToday()) {
|
|
return 'پیگیری امروز';
|
|
}
|
|
|
|
return 'ادامه پیگیری';
|
|
}
|
|
|
|
private function followUpStatus(Lead $lead): string
|
|
{
|
|
if (! $lead->next_follow_up_at) {
|
|
return 'بدون زمان پیگیری';
|
|
}
|
|
if ($lead->next_follow_up_at->isPast()) {
|
|
return 'عقبافتاده';
|
|
}
|
|
if ($lead->next_follow_up_at->isToday()) {
|
|
return 'امروز';
|
|
}
|
|
|
|
return 'زمانبندیشده';
|
|
}
|
|
|
|
private function followUpGroup(FollowUp $followUp): string
|
|
{
|
|
if ($followUp->status === 'completed') {
|
|
return 'done';
|
|
}
|
|
if ($followUp->scheduled_at->isPast() && ! $followUp->scheduled_at->isToday()) {
|
|
return 'overdue';
|
|
}
|
|
if ($followUp->scheduled_at->isToday()) {
|
|
return 'today';
|
|
}
|
|
if ($followUp->scheduled_at->isTomorrow()) {
|
|
return 'tomorrow';
|
|
}
|
|
|
|
return 'week';
|
|
}
|
|
|
|
private function successfulResults(): array
|
|
{
|
|
return CallResult::where('is_positive', true)->pluck('name')->all();
|
|
}
|
|
}
|