ultimatepos/Modules/Crm/Utils/PhoneListUtil.php

210 lines
6.5 KiB
PHP

<?php
namespace Modules\Crm\Utils;
use App\User;
use Carbon\Carbon;
use Carbon\CarbonInterval;
use Modules\Crm\Entities\CrmCallLog;
use Modules\Crm\Entities\CrmPhoneFollowUp;
use Modules\Crm\Entities\CrmPhoneFollowUpLog;
class PhoneListUtil
{
public static function normalizeNationalNumber(?string $number): string
{
if (empty($number)) {
return '';
}
$number = trim($number);
$first_character = substr($number, 0, 1);
if ($first_character === '0') {
return ltrim($number, '0');
}
if ($first_character === '+') {
try {
$phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance();
$number_obj = $phoneUtil->parse($number);
return (string) $number_obj->getNationalNumber();
} catch (\Exception $e) {
return preg_replace('/\D+/', '', $number);
}
}
return preg_replace('/\D+/', '', $number);
}
public function numbersMatch(?string $a, ?string $b): bool
{
$na = self::normalizeNationalNumber($a);
$nb = self::normalizeNationalNumber($b);
return ! empty($na) && $na === $nb;
}
public function findActiveByNumber(int $business_id, string $number): ?CrmPhoneFollowUp
{
$phones = CrmPhoneFollowUp::where('business_id', $business_id)
->where('status', '!=', 'converted')
->get();
foreach ($phones as $phone) {
if ($this->numbersMatch($phone->mobile_number, $number)) {
return $phone;
}
}
return null;
}
public function createFromImportRow(int $business_id, int $user_id, array $row): array
{
$mobile = trim($row['mobile_number'] ?? '');
if (empty($mobile)) {
return ['status' => 'invalid', 'msg' => __('crm::lang.phone_number_required')];
}
$exists = CrmPhoneFollowUp::where('business_id', $business_id)
->where('mobile_number', $mobile)
->where('status', '!=', 'converted')
->exists();
if ($exists) {
return ['status' => 'skipped'];
}
$assigned_to = null;
if (! empty($row['assigned_to'])) {
$assigned_to = $this->resolveAssignedUserId($business_id, $row['assigned_to']);
}
CrmPhoneFollowUp::create([
'business_id' => $business_id,
'mobile_number' => $mobile,
'contact_name' => $row['contact_name'] ?? null,
'company_name' => $row['company_name'] ?? null,
'city' => $row['city'] ?? null,
'province' => $row['province'] ?? null,
'source' => $row['source'] ?? null,
'priority' => ! empty($row['priority']) ? (int) $row['priority'] : 3,
'notes' => $row['notes'] ?? null,
'status' => 'pending',
'assigned_to' => $assigned_to,
'created_by' => $user_id,
]);
return ['status' => 'created'];
}
public function resolveAssignedUserId(int $business_id, string $value): ?int
{
$value = trim($value);
if (empty($value)) {
return null;
}
if (is_numeric($value)) {
$user = User::where('business_id', $business_id)->find((int) $value);
return $user?->id;
}
$user = User::where('business_id', $business_id)
->where(function ($q) use ($value) {
$q->where('username', $value)
->orWhere('email', $value)
->orWhereRaw("CONCAT(COALESCE(first_name, ''), ' ', COALESCE(last_name, '')) like ?", ["%{$value}%"]);
})
->first();
return $user?->id;
}
public function syncFromCallLog(array $callLogData, ?int $call_log_id = null): void
{
if (empty($callLogData['business_id']) || empty($callLogData['mobile_number'])) {
return;
}
$phone = $this->findActiveByNumber($callLogData['business_id'], $callLogData['mobile_number']);
if (empty($phone)) {
return;
}
$duration = ! empty($callLogData['duration']) ? (int) $callLogData['duration'] : 0;
$call_type = $callLogData['call_type'] ?? 'call';
$duration_text = $duration > 0
? CarbonInterval::seconds($duration)->cascade()->forHumans()
: '-';
$note = __('crm::lang.auto_synced_from_call_log', [
'type' => $call_type,
'duration' => $duration_text,
]);
CrmPhoneFollowUpLog::create([
'phone_follow_up_id' => $phone->id,
'status' => 'contacted',
'note' => $note,
'created_by' => $callLogData['created_by'],
'call_log_id' => $call_log_id,
'is_auto' => true,
]);
$phone->update([
'status' => $phone->status === 'pending' ? 'contacted' : $phone->status,
'last_follow_up_at' => $callLogData['start_time'] ?? now(),
'follow_up_count' => $phone->follow_up_count + 1,
]);
}
public function getCallLogCount(int $business_id, string $mobile_number): int
{
if (! config('constants.enable_crm_call_log')) {
return 0;
}
$normalized = self::normalizeNationalNumber($mobile_number);
if (empty($normalized)) {
return 0;
}
return CrmCallLog::where('business_id', $business_id)
->where(function ($q) use ($normalized, $mobile_number) {
$q->where('mobile_number', $mobile_number)
->orWhere('mobile_number', 'like', "%{$normalized}");
})
->count();
}
public function getCallLogsForPhone(CrmPhoneFollowUp $phone)
{
if (! config('constants.enable_crm_call_log')) {
return collect();
}
$logs = CrmCallLog::where('business_id', $phone->business_id)
->latest('start_time')
->limit(100)
->get();
return $logs->filter(function ($log) use ($phone) {
return $this->numbersMatch($log->mobile_number, $phone->mobile_number);
})->values();
}
public function getReminderSettings(int $business_id): array
{
$crmUtil = new CrmUtil();
$settings = $crmUtil->getCrmSettings($business_id);
return [
'enabled' => ! empty($settings['enable_phone_list_reminder']),
'days' => max(1, (int) ($settings['phone_list_reminder_days'] ?? 1)),
];
}
}