'0','۱'=>'1','۲'=>'2','۳'=>'3','۴'=>'4','۵'=>'5','۶'=>'6','۷'=>'7','۸'=>'8','۹'=>'9','٠'=>'0','١'=>'1','٢'=>'2','٣'=>'3','٤'=>'4','٥'=>'5','٦'=>'6','٧'=>'7','٨'=>'8','٩'=>'9'])); if (!$digits) return null; if (str_starts_with($digits, '0098')) $digits = '0' . substr($digits, 4); if (str_starts_with($digits, '98')) $digits = '0' . substr($digits, 2); return $digits; } public static function normalizeWebsite(?string $website): ?string { if (!$website) return null; $value = strtolower(trim($website)); $value = preg_replace('#^https?://#', '', $value); $value = preg_replace('#^www\.#', '', $value); return rtrim($value, '/'); } public static function normalizeName(?string $name): ?string { return $name ? Str::of($name)->lower()->squish()->toString() : null; } public function companySuggestions(array $payload, ?int $excludeId = null): array { $name = self::normalizeName($payload['name'] ?? null); $website = self::normalizeWebsite($payload['website'] ?? null); $email = strtolower((string) ($payload['email'] ?? '')); $phone = self::normalizePhone($payload['phone'] ?? null); $query = Company::query()->with('owner:id,name')->limit(10); if ($excludeId) $query->whereKeyNot($excludeId); $query->where(function ($q) use ($name, $website, $email, $phone) { if ($name) $q->orWhere('normalized_name', $name); if ($website) $q->orWhere('normalized_website', $website); if ($email) $q->orWhereHas('contacts', fn($contact) => $contact->where('email', $email)); if ($phone) { $q->orWhereHas('contacts.phones', fn($phoneQuery) => $phoneQuery->where('phone', 'like', "%{$phone}%")); } }); return $query->get()->map(fn(Company $company) => [ 'id' => $company->id, 'type' => 'company', 'title' => $company->name, 'reason' => 'شباهت در نام شرکت، وب‌سایت، ایمیل یا شماره تماس', ])->values()->all(); } public function leadSuggestions(array $payload, ?int $excludeId = null): array { $phone = self::normalizePhone($payload['phone'] ?? null); $email = strtolower((string) ($payload['email'] ?? '')); $company = self::normalizeName($payload['company'] ?? null); $query = Lead::query()->limit(10); if ($excludeId) $query->whereKeyNot($excludeId); $query->where(function ($q) use ($phone, $email, $company) { if ($phone) $q->orWhere('phone', 'like', "%{$phone}%")->orWhere('phone_secondary', 'like', "%{$phone}%"); if ($email) $q->orWhere('email', $email); if ($company) $q->orWhereRaw('LOWER(company) = ?', [$company]); }); return $query->get()->map(fn(Lead $lead) => [ 'id' => $lead->id, 'type' => 'lead', 'title' => $lead->company ?: $lead->full_name, 'reason' => 'شباهت در تلفن، ایمیل یا نام شرکت', ])->values()->all(); } public function mergeCompanies(Company $source, Company $target, int $userId): Company { return DB::transaction(function () use ($source, $target, $userId) { Contact::where('company_id', $source->id)->update(['company_id' => $target->id]); Lead::where('company_id', $source->id)->update(['company_id' => $target->id]); \App\Models\Deal::where('company_id', $source->id)->update(['company_id' => $target->id]); \App\Models\Attachment::where('attachable_type', Company::class)->where('attachable_id', $source->id)->update(['attachable_id' => $target->id]); \App\Models\Note::where('notable_type', Company::class)->where('notable_id', $source->id)->update(['notable_id' => $target->id]); MergeHistory::create([ 'entity_type' => 'company', 'source_id' => $source->id, 'target_id' => $target->id, 'merged_fields' => $source->only(['name', 'website', 'city', 'industry']), 'merged_by' => $userId, ]); $source->delete(); ActivityLogger::log('company_merged', "Company {$source->id} merged into {$target->id}", $target); return $target->fresh(['contacts.phones', 'leads', 'deals']); }); } }