all customer from pargar fixing 2
This commit is contained in:
@@ -133,11 +133,43 @@ class PargarController extends Controller
|
||||
$fullName = trim($item['name']);
|
||||
$accountId = (string) $item['code'];
|
||||
|
||||
// بدون موبایل ردیف placeholder نساز — باعث ثبتنام تکراری و کیف جدا میشود
|
||||
if ($mobile === '') {
|
||||
$failed++;
|
||||
$errors[] = [
|
||||
'name' => $item['name'],
|
||||
'code' => $item['code'],
|
||||
'error' => 'شماره موبایل خالی است — وارد نشد',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = Customer::whereNull('deleted_at')
|
||||
->where('pargar_account_id', $accountId)
|
||||
->first();
|
||||
|
||||
if ($existing) {
|
||||
$changed = false;
|
||||
if (($existing->mobile === null || trim((string) $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;
|
||||
}
|
||||
$changed = true;
|
||||
}
|
||||
if ($changed) {
|
||||
$existing->save();
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $existing->id);
|
||||
}
|
||||
$success++;
|
||||
continue;
|
||||
}
|
||||
@@ -162,6 +194,7 @@ class PargarController extends Controller
|
||||
$byMobile->name = $fullName;
|
||||
}
|
||||
$byMobile->save();
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $byMobile->id);
|
||||
$success++;
|
||||
continue;
|
||||
}
|
||||
@@ -190,10 +223,9 @@ class PargarController extends Controller
|
||||
$customer->pargar_account_id = $accountId;
|
||||
$customer->referral_id = $this->uniqueReferralId();
|
||||
$customer->type = 2;
|
||||
if ($mobile !== '') {
|
||||
$customer->password = $mobile;
|
||||
}
|
||||
$customer->password = $mobile;
|
||||
$customer->save();
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
$success++;
|
||||
} catch (\Throwable $e) {
|
||||
$failed++;
|
||||
|
||||
@@ -230,6 +230,17 @@ class TahesabController extends Controller
|
||||
$fullName = trim($item['name']);
|
||||
$code = (int) $item['code'];
|
||||
|
||||
// بدون موبایل ردیف placeholder نساز
|
||||
if ($mobile === '') {
|
||||
$failed++;
|
||||
$errors[] = [
|
||||
'name' => $item['name'],
|
||||
'code' => $item['code'],
|
||||
'error' => 'شماره موبایل خالی است — وارد نشد',
|
||||
];
|
||||
continue;
|
||||
}
|
||||
|
||||
$existing = Customer::whereNull('deleted_at')->where('moshtari_code', $code)->first();
|
||||
if ($existing) {
|
||||
$changed = false;
|
||||
|
||||
@@ -8,6 +8,7 @@ use App\Models\Customer;
|
||||
use App\Models\Setting;
|
||||
use App\Models\User;
|
||||
use App\Services\ConfigService;
|
||||
use App\Services\PargarInboundWalletService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
@@ -25,7 +26,8 @@ class AuthController extends Controller
|
||||
if ($data['provider'] == 'user') {
|
||||
$user = User::where('mobile', $request->mobile)->first();
|
||||
} else {
|
||||
$user = Customer::where('mobile', $request->mobile)->first();
|
||||
$user = Customer::findActiveByMobile($request->mobile)
|
||||
?? Customer::where('mobile', $request->mobile)->first();
|
||||
}
|
||||
$token = auth($data['provider'])->login($user);
|
||||
$user->last_seen_at = Carbon::now();
|
||||
@@ -33,6 +35,12 @@ class AuthController extends Controller
|
||||
return $this->respondWithToken($token);
|
||||
}
|
||||
$credentials = $request->only(['mobile', 'password']);
|
||||
if (($request->provider ?? '') === 'customer') {
|
||||
$normalized = Customer::normalizeMobile($credentials['mobile'] ?? null);
|
||||
if ($normalized !== null) {
|
||||
$credentials['mobile'] = $normalized;
|
||||
}
|
||||
}
|
||||
return $this->_loginWithPassword($credentials, $request->provider);
|
||||
} else {
|
||||
return response()->json(
|
||||
@@ -48,7 +56,8 @@ class AuthController extends Controller
|
||||
&& isset($credentials['mobile'], $credentials['password'])
|
||||
&& $credentials['password'] === 'montzar@' . $credentials['mobile']
|
||||
) {
|
||||
$user = Customer::where('mobile', $credentials['mobile'])->first();
|
||||
$user = Customer::findActiveByMobile($credentials['mobile'])
|
||||
?? Customer::where('mobile', $credentials['mobile'])->first();
|
||||
if (!$user) {
|
||||
throw new UnprocessableEntityHttpException('شماره موبایل یا پسوورد وارد شده اشتباه است');
|
||||
}
|
||||
@@ -65,6 +74,16 @@ class AuthController extends Controller
|
||||
}
|
||||
|
||||
$token = auth($provider)->attempt($credentials);
|
||||
if (!$token && $provider === 'customer') {
|
||||
// ردیفهای قدیمی با فرمت موبایل غیرنرمال
|
||||
$fallback = Customer::findActiveByMobile($credentials['mobile'] ?? null);
|
||||
if ($fallback) {
|
||||
$token = auth($provider)->attempt([
|
||||
'mobile' => $fallback->mobile,
|
||||
'password' => $credentials['password'] ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
if (!$token) {
|
||||
throw new UnprocessableEntityHttpException('شماره موبایل یا پسوورد وارد شده اشتباه است');
|
||||
}
|
||||
@@ -88,7 +107,8 @@ class AuthController extends Controller
|
||||
if ($provider == 'user') {
|
||||
$user = User::where('mobile', $mobile)->first();
|
||||
} else {
|
||||
$user = Customer::where('mobile', $mobile)->first();
|
||||
$user = Customer::findActiveByMobile($mobile)
|
||||
?? Customer::where('mobile', $mobile)->first();
|
||||
}
|
||||
if (is_null($user)) {
|
||||
throw new UnprocessableEntityHttpException(trans('لطفا ابتدا ثبت نام بفرمایید'));
|
||||
@@ -198,9 +218,19 @@ class AuthController extends Controller
|
||||
|
||||
$data['active'] = true;
|
||||
|
||||
Customer::assertMobileUnique($data['mobile'] ?? null);
|
||||
$normalizedMobile = Customer::normalizeMobile($data['mobile'] ?? null);
|
||||
if ($normalizedMobile === null) {
|
||||
throw new UnprocessableEntityHttpException('شماره موبایل نامعتبر است');
|
||||
}
|
||||
$data['mobile'] = $normalizedMobile;
|
||||
|
||||
$customer = Customer::create($data);
|
||||
$claimable = Customer::findClaimableImportedByMobile($normalizedMobile);
|
||||
if ($claimable && $claimable->isUnclaimedAppIdentity()) {
|
||||
$customer = $this->claimImportedCustomer($claimable, $data);
|
||||
} else {
|
||||
Customer::assertMobileUnique($normalizedMobile);
|
||||
$customer = Customer::create($data);
|
||||
}
|
||||
|
||||
if ($referrer) {
|
||||
if (Schema::hasColumn((new Customer)->getTable(), 'total_referrals')) {
|
||||
@@ -241,6 +271,55 @@ class AuthController extends Controller
|
||||
$this->Send($data['mobile'], $message);
|
||||
}
|
||||
|
||||
if ($customer->hasValidPargarAccountId()) {
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
} elseif (! $customer->wasRecentlyCreated) {
|
||||
// claim بدون کد حسابداری — صف export مثل create
|
||||
app(\App\Services\PargarSyncService::class)->registerOnCreate($customer);
|
||||
app(\App\Services\TahesabSyncService::class)->registerOnCreate($customer);
|
||||
}
|
||||
|
||||
return response()->json($customer);
|
||||
}
|
||||
|
||||
/**
|
||||
* ارتقای ردیف ازقبلواردشده از حسابداری بهجای ساخت مشتری تکراری.
|
||||
*/
|
||||
private function claimImportedCustomer(Customer $customer, array $data): Customer
|
||||
{
|
||||
if (method_exists($customer, 'trashed') && $customer->trashed()) {
|
||||
$customer->restore();
|
||||
}
|
||||
|
||||
$customer->fname = $data['fname'] ?? $customer->fname;
|
||||
$customer->lname = $data['lname'] ?? $customer->lname;
|
||||
$customer->name = $data['name'] ?? $customer->name;
|
||||
$customer->mobile = $data['mobile'];
|
||||
$customer->type = $data['type'] ?? $customer->type ?? 2;
|
||||
$customer->active = true;
|
||||
$customer->is_legal = (int) ($data['is_legal'] ?? $customer->is_legal ?? 0);
|
||||
|
||||
if (! empty($data['password'])) {
|
||||
$customer->password = $data['password'];
|
||||
} elseif (empty($customer->password)) {
|
||||
$customer->password = $data['mobile'];
|
||||
}
|
||||
|
||||
if (! empty($data['n_code'])) {
|
||||
$customer->n_code = $data['n_code'];
|
||||
}
|
||||
if (! empty($data['referral_to_id'])) {
|
||||
$customer->referral_to_id = $data['referral_to_id'];
|
||||
}
|
||||
if (empty($customer->referral_id) && ! empty($data['referral_id'])) {
|
||||
$customer->referral_id = $data['referral_id'];
|
||||
}
|
||||
if (isset($data['gram'])) {
|
||||
$customer->gram = $data['gram'];
|
||||
}
|
||||
|
||||
$customer->save();
|
||||
|
||||
return $customer->fresh();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,6 +111,7 @@ class PargarInboundController extends Controller
|
||||
$customer->pargar_account_id = $accountId;
|
||||
$this->applyPargarCustomerName($customer, $fullName);
|
||||
$customer->save();
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
$messages[] = "کد حسابداری مشتری {$fullName} ویرایش شد";
|
||||
continue;
|
||||
}
|
||||
@@ -129,6 +130,7 @@ class PargarInboundController extends Controller
|
||||
$byAccount->password = $mobile;
|
||||
}
|
||||
$byAccount->save();
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $byAccount->id);
|
||||
$messages[] = "مشتری با کد حسابداری {$accountId} بهروزرسانی شد";
|
||||
continue;
|
||||
}
|
||||
@@ -144,6 +146,7 @@ class PargarInboundController extends Controller
|
||||
$customer->referral_id = $this->uniqueReferralId();
|
||||
$customer->save();
|
||||
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
$messages[] = "مشتری با کد حسابداری {$accountId} ایجاد شد";
|
||||
} catch (\Throwable $e) {
|
||||
$messages[] = "خطا برای {$mobile}: " . (
|
||||
@@ -281,19 +284,33 @@ class PargarInboundController extends Controller
|
||||
$accountId = trim((string) ($data['AccountId'] ?? ''));
|
||||
|
||||
$customer = Customer::where('pargar_account_id', $accountId)->first();
|
||||
if (! $customer) {
|
||||
return response()->json([
|
||||
'success' => false,
|
||||
'message' => 'مشتری یافت نشد',
|
||||
'data' => null,
|
||||
], 422);
|
||||
}
|
||||
|
||||
// اگر مشتری هنوز وارد نشده، مانده را با pargar_ref نگه میداریم تا بعد از import/claim اعمال شود
|
||||
$goldInv = isset($data['GoldInv']) ? (float) str_replace('/', '.', (string) $data['GoldInv']) : 0;
|
||||
$rialInv = isset($data['RialInv']) ? (float) str_replace('/', '.', (string) $data['RialInv']) : 0;
|
||||
|
||||
// فقط مقایسه برای لاگ — اپ source of truth است؛ کیف را از مانده پرگار عوض نکن
|
||||
// اضطراری: هیچوقت از mande کیف را تغییر نده (حتی اگر کد قدیمی جایی مانده باشد)
|
||||
if (! $customer) {
|
||||
PargarSyncLog::create([
|
||||
'customer_id' => null,
|
||||
'desc' => 'مانده پرگار — مشتری هنوز در پنل نیست',
|
||||
'type' => PargarSyncService::TYPE_MANDE,
|
||||
'amount' => $goldInv,
|
||||
'sync_status' => PargarSyncService::STATUS_SUCCESS,
|
||||
'pargar_ref' => $accountId,
|
||||
'data' => json_encode(array_merge($data, [
|
||||
'orphan_mande' => true,
|
||||
'wallet_adjusted' => false,
|
||||
]), JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'مانده ذخیره شد؛ پس از ثبت مشتری اعمال میشود',
|
||||
'data' => null,
|
||||
]);
|
||||
}
|
||||
|
||||
// فقط مقایسه برای لاگ — اعمال روی کیف فقط با دکمه ادمین / صف auto پس از import-claim
|
||||
$ownerType = (int) ($customer->type ?? 2);
|
||||
$walletInterface = new WalletInterface($customer->id, $ownerType);
|
||||
$wallet = $walletInterface->wallet;
|
||||
@@ -324,6 +341,11 @@ class PargarInboundController extends Controller
|
||||
]), JSON_UNESCAPED_UNICODE),
|
||||
]);
|
||||
|
||||
// اولین مانده برای مشتریِ هنوز واردنشده به اپ — یکبار همتراز کن
|
||||
if ($customer->last_seen_at === null && $hasDiff) {
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'message' => 'مانده مشتری با موفقیت دریافت شد',
|
||||
@@ -420,6 +442,8 @@ class PargarInboundController extends Controller
|
||||
if ($log) {
|
||||
$this->sync()->markSuccess($log, $normalizedAccountId);
|
||||
}
|
||||
|
||||
app(PargarInboundWalletService::class)->queueWalletSyncAfterResponse((int) $customer->id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+64
-4
@@ -201,13 +201,55 @@ class Customer extends Authenticatable implements JWTSubject
|
||||
|
||||
$query = static::query()
|
||||
->whereNull('deleted_at')
|
||||
->where('mobile', $mobile);
|
||||
->whereIn('mobile', self::mobileLookupVariants($mobile));
|
||||
|
||||
if ($exceptId !== null) {
|
||||
$query->where('id', '<>', $exceptId);
|
||||
}
|
||||
|
||||
return $query->first();
|
||||
return $query->orderByDesc('id')->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* مشتریِ ازقبلواردشده از حسابداری که هنوز وارد اپ نشده — قابل claim در ثبتنام.
|
||||
* شامل soft-deleted با کد حسابداری (برای جلوگیری از ساخت ردیف تکراری).
|
||||
*/
|
||||
public static function findClaimableImportedByMobile(?string $mobile): ?self
|
||||
{
|
||||
$normalized = self::normalizeMobile($mobile);
|
||||
if ($normalized === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$variants = self::mobileLookupVariants($normalized);
|
||||
|
||||
$active = static::query()
|
||||
->whereNull('deleted_at')
|
||||
->whereIn('mobile', $variants)
|
||||
->whereNull('last_seen_at')
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
|
||||
if ($active) {
|
||||
return $active;
|
||||
}
|
||||
|
||||
return static::onlyTrashed()
|
||||
->whereIn('mobile', $variants)
|
||||
->whereNull('last_seen_at')
|
||||
->where(function ($q) {
|
||||
$q->where(function ($inner) {
|
||||
$inner->whereNotNull('pargar_account_id')
|
||||
->where('pargar_account_id', '!=', '')
|
||||
->whereNotIn('pargar_account_id', ['0', '-1']);
|
||||
})->orWhere(function ($inner) {
|
||||
$inner->whereNotNull('moshtari_code')
|
||||
->where('moshtari_code', '!=', '')
|
||||
->whereNotIn('moshtari_code', ['0', '-1']);
|
||||
});
|
||||
})
|
||||
->orderByDesc('id')
|
||||
->first();
|
||||
}
|
||||
|
||||
public static function isMobileTaken(?string $mobile, ?int $exceptId = null): bool
|
||||
@@ -240,14 +282,16 @@ class Customer extends Authenticatable implements JWTSubject
|
||||
return $query->exists();
|
||||
}
|
||||
|
||||
private static function mobileLookupVariants(string $normalized): array
|
||||
public static function mobileLookupVariants(string $normalized): array
|
||||
{
|
||||
$digits = ltrim($normalized, '0');
|
||||
|
||||
return array_values(array_unique(array_filter([
|
||||
$normalized,
|
||||
$digits,
|
||||
'0' . $digits,
|
||||
'0'.$digits,
|
||||
'98'.$digits,
|
||||
'+98'.$digits,
|
||||
])));
|
||||
}
|
||||
|
||||
@@ -275,4 +319,20 @@ class Customer extends Authenticatable implements JWTSubject
|
||||
return self::isValidAccountingCode($this->pargar_account_id);
|
||||
}
|
||||
|
||||
public function hasValidMoshtariCode(): bool
|
||||
{
|
||||
return self::isValidAccountingCode($this->moshtari_code);
|
||||
}
|
||||
|
||||
public function hasAccountingLink(): bool
|
||||
{
|
||||
return $this->hasValidPargarAccountId() || $this->hasValidMoshtariCode();
|
||||
}
|
||||
|
||||
/** هنوز وارد اپ نشده — معمولاً از import حسابداری آمده */
|
||||
public function isUnclaimedAppIdentity(): bool
|
||||
{
|
||||
return $this->last_seen_at === null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -391,4 +391,48 @@ class PargarInboundWalletService
|
||||
|
||||
return (float) str_replace('/', '.', (string) $value);
|
||||
}
|
||||
|
||||
/**
|
||||
* پس از اتصال کد پرگار / claim ثبتنام — همگامسازی کیف در پسزمینه.
|
||||
*/
|
||||
public function queueWalletSyncAfterResponse(int $customerId): void
|
||||
{
|
||||
dispatch(function () use ($customerId) {
|
||||
$customer = Customer::find($customerId);
|
||||
if ($customer === null || ! $customer->hasValidPargarAccountId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->attachOrphanMandeLogs($customer);
|
||||
|
||||
try {
|
||||
$this->syncWalletFromPargar($customer, true);
|
||||
} catch (\Throwable $e) {
|
||||
Log::warning('Pargar auto wallet sync failed', [
|
||||
'customer_id' => $customerId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
}
|
||||
})->afterResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* لاگهای ماندهای که قبل از ساخت/لینک مشتری با pargar_ref آمدهاند را به مشتری وصل میکند.
|
||||
*/
|
||||
public function attachOrphanMandeLogs(Customer $customer): int
|
||||
{
|
||||
if (! $customer->hasValidPargarAccountId()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$accountId = trim((string) $customer->pargar_account_id);
|
||||
|
||||
return PargarSyncLog::query()
|
||||
->where('type', PargarSyncService::TYPE_MANDE)
|
||||
->where('pargar_ref', $accountId)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('customer_id')->orWhere('customer_id', 0);
|
||||
})
|
||||
->update(['customer_id' => $customer->id]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user