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)), ]; } }