111 خطوط
4.8 KiB
PHP
111 خطوط
4.8 KiB
PHP
<?php
|
||
|
||
namespace App\Services;
|
||
|
||
use App\Models\Company;
|
||
use App\Models\Contact;
|
||
use App\Models\Lead;
|
||
use App\Models\MergeHistory;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Str;
|
||
|
||
class DuplicateService
|
||
{
|
||
public static function normalizePhone(?string $phone): ?string
|
||
{
|
||
if (!$phone) return null;
|
||
$digits = preg_replace('/\D+/', '', strtr($phone, ['۰'=>'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']);
|
||
});
|
||
}
|
||
}
|