294 lines
12 KiB
PHP
294 lines
12 KiB
PHP
<?php
|
|
|
|
namespace Modules\Crm\Http\Controllers;
|
|
|
|
use App\Utils\ModuleUtil;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Routing\Controller;
|
|
use Carbon\Carbon;
|
|
use Modules\Crm\Entities\CrmCallAssessment;
|
|
use Modules\Crm\Entities\CrmCallQueue;
|
|
use Modules\Crm\Entities\CrmCallScript;
|
|
use Modules\Crm\Entities\CrmCallSession;
|
|
use Modules\Crm\Entities\CrmCompany;
|
|
use Modules\Crm\Entities\CrmCompanyContact;
|
|
use Modules\Crm\Entities\CrmCompanyOffer;
|
|
use Modules\Crm\Entities\CrmCompanyRequirement;
|
|
use Modules\Crm\Entities\CrmPhoneFollowUp;
|
|
use Modules\Crm\Services\CallQueueService;
|
|
use Modules\Crm\Services\CallSessionCompletionService;
|
|
use Modules\Crm\Services\CompanyProfileService;
|
|
use Modules\Crm\Utils\PhoneListUtil;
|
|
use Modules\Crm\Utils\TelemarketingUtil;
|
|
|
|
class CallWorkspaceController extends Controller
|
|
{
|
|
protected $moduleUtil;
|
|
|
|
protected $callQueueService;
|
|
|
|
protected $companyProfileService;
|
|
|
|
protected $completionService;
|
|
|
|
public function __construct(
|
|
ModuleUtil $moduleUtil,
|
|
CallQueueService $callQueueService,
|
|
CompanyProfileService $companyProfileService,
|
|
CallSessionCompletionService $completionService
|
|
) {
|
|
$this->moduleUtil = $moduleUtil;
|
|
$this->callQueueService = $callQueueService;
|
|
$this->companyProfileService = $companyProfileService;
|
|
$this->completionService = $completionService;
|
|
}
|
|
|
|
protected function authorizeAccess(): void
|
|
{
|
|
$business_id = request()->session()->get('user.business_id');
|
|
TelemarketingUtil::authorizeTelemarketing($this->moduleUtil, $business_id, 'crm.telemarketing.access');
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
|
|
if (! TelemarketingUtil::isEnabled($business_id)) {
|
|
return redirect()->action([CrmSettingsController::class, 'index'])
|
|
->with('status', ['success' => 0, 'msg' => __('crm::lang.enable_telemarketing_first')]);
|
|
}
|
|
|
|
$activeSession = $this->callQueueService->getActiveSession($business_id, auth()->id());
|
|
$activeQueue = empty($activeSession)
|
|
? $this->callQueueService->getActiveQueue($business_id, auth()->id())
|
|
: $activeSession->queue;
|
|
$script = CrmCallScript::where('business_id', $business_id)->where('is_default', true)->first();
|
|
if (empty($script)) {
|
|
$script = CrmCallScript::where('business_id', $business_id)->latest()->first();
|
|
}
|
|
$scripts = CrmCallScript::where('business_id', $business_id)->get();
|
|
$statuses = CrmPhoneFollowUp::statusDropdown();
|
|
$outcomes = CrmCallSession::outcomes();
|
|
$sentiments = CrmCallAssessment::sentiments();
|
|
$contactRoles = CrmCompanyContact::roles();
|
|
$companyTypes = CrmCompany::companyTypes();
|
|
$requirementTypes = CrmCompanyRequirement::requirementTypes();
|
|
$objections = $this->objectionOptions();
|
|
$partnerPotentials = ['none' => __('crm::lang.partner_none'), 'low' => __('crm::lang.partner_low'), 'medium' => __('crm::lang.partner_medium'), 'high' => __('crm::lang.partner_high')];
|
|
$workspaceStats = $this->buildWorkspaceStats($business_id, auth()->id());
|
|
$crm_settings = TelemarketingUtil::mergeSettings((new \Modules\Crm\Utils\CrmUtil())->getCrmSettings($business_id));
|
|
|
|
return view('crm::telemarketing.workspace')->with(compact(
|
|
'activeSession', 'activeQueue', 'script', 'scripts', 'statuses', 'outcomes', 'sentiments',
|
|
'contactRoles', 'companyTypes', 'requirementTypes', 'objections', 'partnerPotentials',
|
|
'workspaceStats', 'crm_settings'
|
|
));
|
|
}
|
|
|
|
public function workspaceStats()
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
|
|
return response()->json($this->buildWorkspaceStats($business_id, auth()->id()));
|
|
}
|
|
|
|
protected function buildWorkspaceStats(int $business_id, int $user_id): array
|
|
{
|
|
$today = Carbon::today();
|
|
$this->callQueueService->syncQueueForBusiness($business_id);
|
|
|
|
$todaySessions = CrmCallSession::where('business_id', $business_id)
|
|
->where('user_id', $user_id)
|
|
->whereDate('started_at', $today)
|
|
->get();
|
|
|
|
$settings = TelemarketingUtil::mergeSettings((new \Modules\Crm\Utils\CrmUtil())->getCrmSettings($business_id));
|
|
$target = (int) ($settings['daily_call_target_per_agent'] ?? 50);
|
|
$done = $todaySessions->whereNotNull('ended_at')->count();
|
|
|
|
return [
|
|
'queue_remaining' => CrmCallQueue::where('business_id', $business_id)
|
|
->whereIn('status', ['available', 'locked'])
|
|
->whereHas('phoneFollowUp', function ($q) {
|
|
$q->where('status', 'pending');
|
|
})
|
|
->count(),
|
|
'today_calls' => $done,
|
|
'today_answered' => $todaySessions->where('outcome', 'answered')->count(),
|
|
'today_interested' => CrmPhoneFollowUp::where('business_id', $business_id)->where('assigned_to', $user_id)->where('status', 'interested')->whereDate('last_follow_up_at', $today)->count(),
|
|
'daily_target' => $target,
|
|
'target_progress' => $target > 0 ? min(100, round(($done / $target) * 100)) : 0,
|
|
'avg_lead_score' => round((float) CrmCallAssessment::whereHas('callSession', function ($q) use ($business_id, $user_id, $today) {
|
|
$q->where('business_id', $business_id)->where('user_id', $user_id)->whereDate('started_at', $today);
|
|
})->avg('cooperation_score') ?: 0, 1),
|
|
];
|
|
}
|
|
|
|
protected function objectionOptions(): array
|
|
{
|
|
return [
|
|
'price' => __('crm::lang.objection_price'),
|
|
'competitor' => __('crm::lang.objection_competitor'),
|
|
'timing' => __('crm::lang.objection_timing'),
|
|
'no_need' => __('crm::lang.objection_no_need'),
|
|
'no_budget' => __('crm::lang.objection_no_budget'),
|
|
'send_email' => __('crm::lang.objection_send_email'),
|
|
];
|
|
}
|
|
|
|
public function releaseQueue(Request $request)
|
|
{
|
|
$this->authorizeAccess();
|
|
$released = $this->callQueueService->release((int) $request->input('queue_id'), auth()->id());
|
|
|
|
return response()->json([
|
|
'success' => $released,
|
|
'msg' => $released ? __('crm::lang.queue_released') : __('messages.something_went_wrong'),
|
|
]);
|
|
}
|
|
|
|
public function acquireNext(Request $request)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
|
|
$queue = $this->callQueueService->acquireNext(
|
|
$business_id,
|
|
auth()->id(),
|
|
$request->input('campaign_id')
|
|
);
|
|
|
|
if (empty($queue)) {
|
|
return response()->json(['success' => false, 'msg' => __('crm::lang.no_numbers_in_queue')]);
|
|
}
|
|
|
|
return response()->json([
|
|
'success' => true,
|
|
'queue' => $queue,
|
|
'phone' => $queue->phoneFollowUp,
|
|
]);
|
|
}
|
|
|
|
public function startSession(Request $request)
|
|
{
|
|
$this->authorizeAccess();
|
|
$session = $this->callQueueService->startSession((int) $request->input('queue_id'), auth()->id());
|
|
|
|
if (empty($session)) {
|
|
return response()->json(['success' => false, 'msg' => __('messages.something_went_wrong')]);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'session' => $session]);
|
|
}
|
|
|
|
public function workspaceData($phone_id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
|
|
$phone = CrmPhoneFollowUp::where('business_id', $business_id)->findOrFail($phone_id);
|
|
$phoneListUtil = new PhoneListUtil();
|
|
$logs = $phone->logs()->with('createdBy')->limit(30)->get();
|
|
$callLogs = $phoneListUtil->getCallLogsForPhone($phone);
|
|
$assessments = CrmCallAssessment::where('phone_follow_up_id', $phone->id)->with('assessedBy')->latest()->limit(10)->get();
|
|
$statuses = CrmPhoneFollowUp::statusDropdown();
|
|
|
|
$company = null;
|
|
$suggestedFields = [];
|
|
$contacts = [];
|
|
if (! empty($phone->company_id)) {
|
|
$company = CrmCompany::with(['contacts', 'branches', 'phones', 'requirements.product'])->find($phone->company_id);
|
|
}
|
|
if (empty($company)) {
|
|
$company = $this->companyProfileService->findOrCreateForPhone($phone, auth()->id());
|
|
$company->load(['contacts', 'branches', 'phones', 'requirements']);
|
|
}
|
|
$suggestedFields = $this->companyProfileService->getSuggestedFieldsForCall($company);
|
|
$contacts = $company->contacts;
|
|
|
|
return response()->json([
|
|
'phone' => $phone,
|
|
'statuses' => $statuses,
|
|
'logs' => $logs->map(function ($log) use ($statuses) {
|
|
return [
|
|
'status' => $statuses[$log->status] ?? $log->status,
|
|
'note' => $log->note,
|
|
'created_at' => $log->created_at?->format('Y-m-d H:i'),
|
|
'user' => $log->createdBy?->user_full_name,
|
|
'is_auto' => $log->is_auto,
|
|
];
|
|
}),
|
|
'call_logs' => $callLogs,
|
|
'assessments' => $assessments,
|
|
'company' => $company,
|
|
'suggested_fields' => $suggestedFields,
|
|
'contacts' => $contacts,
|
|
'company_url' => action([CompanyProfileController::class, 'show'], $company->id),
|
|
]);
|
|
}
|
|
|
|
public function completeSession(Request $request, $session_id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id') ?? auth()->user()->business_id;
|
|
|
|
$request->validate([
|
|
'outcome' => 'required|string',
|
|
'status' => 'nullable|string',
|
|
'note' => 'nullable|string',
|
|
'reception_score' => 'nullable|integer|min:1|max:5',
|
|
'tone_score' => 'nullable|integer|min:1|max:5',
|
|
'cooperation_score' => 'nullable|integer|min:1|max:5',
|
|
]);
|
|
|
|
$result = $this->completionService->complete(
|
|
(int) $session_id,
|
|
auth()->id(),
|
|
$business_id,
|
|
$request->all()
|
|
);
|
|
|
|
return response()->json($result);
|
|
}
|
|
|
|
public function previewOffers($company_id)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
$company = CrmCompany::where('business_id', $business_id)->findOrFail($company_id);
|
|
$matcher = new \Modules\Crm\Services\OfferMatchingService();
|
|
|
|
return response()->json(['success' => true, 'offers' => $matcher->matchForCompany($company)]);
|
|
}
|
|
|
|
public function sendOffers(Request $request)
|
|
{
|
|
$this->authorizeAccess();
|
|
$business_id = request()->session()->get('user.business_id');
|
|
|
|
$request->validate([
|
|
'company_id' => 'required|integer',
|
|
'offer_ids' => 'required|array',
|
|
]);
|
|
|
|
foreach ($request->input('offer_ids') as $offerId) {
|
|
if (empty($offerId)) {
|
|
continue;
|
|
}
|
|
CrmCompanyOffer::create([
|
|
'company_id' => $request->input('company_id'),
|
|
'offer_catalog_id' => $offerId,
|
|
'call_session_id' => $request->input('call_session_id'),
|
|
'status' => 'draft',
|
|
'sent_via' => 'manual',
|
|
'sent_by' => auth()->id(),
|
|
'sent_at' => now(),
|
|
]);
|
|
}
|
|
|
|
return response()->json(['success' => true, 'msg' => __('crm::lang.offers_saved')]);
|
|
}
|
|
}
|