ultimatepos/Modules/Crm/Http/Controllers/CallWorkspaceController.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')]);
}
}