diff --git a/app/Http/Controllers/Admin/PargarController.php b/app/Http/Controllers/Admin/PargarController.php index e80d5b8..2055596 100644 --- a/app/Http/Controllers/Admin/PargarController.php +++ b/app/Http/Controllers/Admin/PargarController.php @@ -127,8 +127,7 @@ class PargarController extends Controller $errors = []; foreach ($validated['customers'] as $item) { - $tel = trim((string) ($item['tel'] ?? '')); - $mobile = $tel !== '' ? '0' . ltrim($tel, '0') : ''; + $mobile = Customer::normalizeMobile($item['tel'] ?? '') ?? ''; $fullName = trim($item['name']); $accountId = (string) $item['code']; @@ -142,8 +141,19 @@ class PargarController extends Controller } if ($mobile !== '') { - $byMobile = Customer::whereNull('deleted_at')->where('mobile', $mobile)->first(); + $byMobile = Customer::findActiveByMobile($mobile); if ($byMobile) { + $currentAccount = (string) ($byMobile->pargar_account_id ?? ''); + if ($currentAccount !== '' && $currentAccount !== $accountId) { + $failed++; + $errors[] = [ + 'name' => $item['name'], + 'code' => $item['code'], + 'error' => 'شماره موبایل تکراری است', + ]; + continue; + } + $byMobile->pargar_account_id = $accountId; if ($fullName !== '') { $byMobile->fname = $fullName; @@ -155,6 +165,19 @@ class PargarController extends Controller } } + $byAccount = Customer::whereNull('deleted_at') + ->where('pargar_account_id', $accountId) + ->first(); + if ($byAccount) { + $failed++; + $errors[] = [ + 'name' => $item['name'], + 'code' => $item['code'], + 'error' => 'کد حساب پرگار قبلاً ثبت شده است', + ]; + continue; + } + try { $customer = new Customer(); $customer->fname = $fullName; diff --git a/app/Http/Controllers/Admin/TahesabController.php b/app/Http/Controllers/Admin/TahesabController.php index e3f6110..7b5789f 100644 --- a/app/Http/Controllers/Admin/TahesabController.php +++ b/app/Http/Controllers/Admin/TahesabController.php @@ -226,34 +226,70 @@ class TahesabController extends Controller $errors = []; foreach ($validated['customers'] as $item) { - // ستون mobile در دیتابیس NOT NULL است؛ پس برای تلفن خالی رشتهٔ خالی می‌گذاریم نه null - $tel = trim((string) ($item['tel'] ?? '')); - $mobile = $tel !== '' ? '0' . ltrim($tel, '0') : ''; - + $mobile = Customer::normalizeMobile($item['tel'] ?? '') ?? ''; $fullName = trim($item['name']); + $code = (int) $item['code']; - // اگر مشتری با همین کد قبلاً ثبت شده: فیلدهای ناقص (نام/موبایل/رمز) را تکمیل کن - $existing = Customer::whereNull('deleted_at')->where('moshtari_code', $item['code'])->first(); + $existing = Customer::whereNull('deleted_at')->where('moshtari_code', $code)->first(); if ($existing) { $changed = false; if (($existing->name === null || $existing->name === '') && $fullName !== '') { $existing->fname = $fullName; $existing->lname = (string) ($existing->lname ?? ''); - $existing->name = $fullName; // mutator name را از fname+lname می‌سازد + $existing->name = $fullName; $changed = true; } - if (($existing->mobile === null || $existing->mobile === '') && $mobile !== '') { + if ($mobile !== '' && ($existing->mobile === null || $existing->mobile === '')) { + if (Customer::isMobileTaken($mobile, (int) $existing->id)) { + $failed++; + $errors[] = [ + 'name' => $item['name'], + 'code' => $item['code'], + 'error' => 'شماره موبایل تکراری است', + ]; + continue; + } $existing->mobile = $mobile; if (empty($existing->password)) { - $existing->password = $mobile; // رمز = شماره موبایل + $existing->password = $mobile; } $changed = true; } - if ($changed) $existing->save(); + if ($changed) { + $existing->save(); + } $success++; continue; } + if ($mobile !== '') { + $byMobile = Customer::findActiveByMobile($mobile); + if ($byMobile) { + if (empty($byMobile->moshtari_code)) { + $byMobile->moshtari_code = $code; + if ($fullName !== '') { + $byMobile->fname = $fullName; + $byMobile->lname = ''; + $byMobile->name = $fullName; + } + if (empty($byMobile->password)) { + $byMobile->password = $mobile; + } + $byMobile->save(); + $success++; + continue; + } + + $failed++; + $errors[] = [ + 'name' => $item['name'], + 'code' => $item['code'], + 'error' => 'شماره موبایل تکراری است', + ]; + continue; + } + } + // کد معرف یکتا $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; do { @@ -284,8 +320,10 @@ class TahesabController extends Controller } catch (\Throwable $e) { $failed++; $msg = $e->getMessage(); - if ($e instanceof \Illuminate\Database\QueryException && (int) ($e->errorInfo[1] ?? 0) === 1062) { - $msg = 'این مشتری از قبل در پنل ثبت شده است'; + if ($e instanceof UnprocessableEntityHttpException) { + $msg = $e->getMessage(); + } elseif ($e instanceof \Illuminate\Database\QueryException && (int) ($e->errorInfo[1] ?? 0) === 1062) { + $msg = 'شماره موبایل تکراری است'; } $errors[] = ['name' => $item['name'], 'code' => $item['code'], 'error' => $msg]; } diff --git a/app/Http/Controllers/Pargar/PargarInboundController.php b/app/Http/Controllers/Pargar/PargarInboundController.php index 1806693..25d9505 100644 --- a/app/Http/Controllers/Pargar/PargarInboundController.php +++ b/app/Http/Controllers/Pargar/PargarInboundController.php @@ -16,6 +16,7 @@ use Illuminate\Support\Facades\Schema; use Morilog\Jalali\Jalalian; use Rooyesh\Wallet\interfaces\TransactionInterface; use Rooyesh\Wallet\interfaces\WalletInterface; +use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException; class PargarInboundController extends Controller { @@ -77,23 +78,37 @@ class PargarInboundController extends Controller $accountId = trim((string) ($row['AccountId'] ?? '')); $fullName = trim((string) ($row['FullName'] ?? '')); - $mobile = $this->normalizeMobile((string) ($row['CellPhone'] ?? '')); + $mobile = Customer::normalizeMobile((string) ($row['CellPhone'] ?? '')); - if ($accountId === '' || $mobile === '') { + if ($accountId === '' || $mobile === null) { $messages[] = 'ردیف نامعتبر: AccountId یا CellPhone خالی است'; continue; } - $customer = Customer::whereNull('deleted_at') - ->where('mobile', $mobile) + $byAccount = Customer::whereNull('deleted_at') + ->where('pargar_account_id', $accountId) ->first(); + if ($byAccount && Customer::normalizeMobile($byAccount->mobile) !== $mobile) { + $messages[] = "کد حساب {$accountId} قبلاً برای مشتری دیگری ثبت شده است"; + continue; + } + + $customer = Customer::findActiveByMobile($mobile); + if ($customer) { - if ((string) ($customer->pargar_account_id ?? '') === $accountId) { + $currentAccount = (string) ($customer->pargar_account_id ?? ''); + + if ($currentAccount === $accountId) { $messages[] = 'کد حساب مشتری وجود دارد'; continue; } + if ($currentAccount !== '' && $currentAccount !== $accountId) { + $messages[] = "شماره {$mobile} قبلاً به حساب پرگار دیگری متصل است"; + continue; + } + $customer->pargar_account_id = $accountId; if ($fullName !== '') { $customer->fname = $fullName; @@ -104,18 +119,45 @@ class PargarInboundController extends Controller continue; } - $customer = new Customer(); - $customer->fname = $fullName !== '' ? $fullName : 'مشتری'; - $customer->name = $customer->fname; - $customer->mobile = $mobile; - $customer->pargar_account_id = $accountId; - $customer->active = true; - $customer->type = 2; - $customer->password = $mobile; - $customer->referral_id = $this->uniqueReferralId(); - $customer->save(); + if ($byAccount) { + if (Customer::isMobileTaken($mobile, (int) $byAccount->id)) { + $messages[] = "شماره {$mobile} تکراری است"; + continue; + } - $messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد"; + if ($fullName !== '') { + $byAccount->fname = $fullName; + $byAccount->name = $fullName; + } + $byAccount->mobile = $mobile; + if (empty($byAccount->password)) { + $byAccount->password = $mobile; + } + $byAccount->save(); + $messages[] = "مشتری با کد حسابداری {$accountId} به‌روزرسانی شد"; + continue; + } + + try { + $customer = new Customer(); + $customer->fname = $fullName !== '' ? $fullName : 'مشتری'; + $customer->name = $customer->fname; + $customer->mobile = $mobile; + $customer->pargar_account_id = $accountId; + $customer->active = true; + $customer->type = 2; + $customer->password = $mobile; + $customer->referral_id = $this->uniqueReferralId(); + $customer->save(); + + $messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد"; + } catch (\Throwable $e) { + $messages[] = "خطا برای {$mobile}: " . ( + $e instanceof UnprocessableEntityHttpException + ? $e->getMessage() + : 'ثبت مشتری ناموفق بود' + ); + } } return response()->json([ @@ -347,16 +389,6 @@ class PargarInboundController extends Controller ]); } - private function normalizeMobile(string $mobile): string - { - $mobile = trim($mobile); - if ($mobile === '') { - return ''; - } - - return '0' . ltrim($mobile, '0'); - } - private function uniqueReferralId(): string { do { diff --git a/app/Models/Customer.php b/app/Models/Customer.php index 03ad641..f030e67 100644 --- a/app/Models/Customer.php +++ b/app/Models/Customer.php @@ -171,9 +171,39 @@ class Customer extends Authenticatable implements JWTSubject public static function normalizeMobile(?string $mobile): ?string { - $mobile = trim((string) $mobile); + $digits = preg_replace('/\D+/', '', trim((string) $mobile)); + if ($digits === '') { + return null; + } - return $mobile === '' ? null : $mobile; + if (str_starts_with($digits, '98') && strlen($digits) >= 12) { + $digits = substr($digits, 2); + } + + $digits = ltrim($digits, '0'); + if ($digits === '') { + return null; + } + + return '0' . $digits; + } + + public static function findActiveByMobile(?string $mobile, ?int $exceptId = null): ?self + { + $mobile = self::normalizeMobile($mobile); + if ($mobile === null) { + return null; + } + + $query = static::query() + ->whereNull('deleted_at') + ->where('mobile', $mobile); + + if ($exceptId !== null) { + $query->where('id', '<>', $exceptId); + } + + return $query->first(); } public static function isMobileTaken(?string $mobile, ?int $exceptId = null): bool