isSyncEnabled()) { $this->error('همگام‌سازی پرگار فعال نیست.'); return self::FAILURE; } if (! $sync->hasSyncTable()) { $this->error('جدول pargar_sync_logs موجود نیست.'); return self::FAILURE; } $scanAll = (bool) $this->option('all'); $days = max(1, (int) $this->option('days')); $execute = (bool) $this->option('execute'); $customerId = (int) $this->option('customer'); $query = Transaction::query() ->whereIn('type', [3, 4]) ->whereNotNull('withdraw_id') ->where(function ($q) { $q->where('cash', '>=', 1)->orWhere('cash', '<=', -1); }) // فقط بعد از شروع اتصال همین مشتری به پرگار ->whereRaw( 'transactions.created_at > ( SELECT MIN(psl.created_at) FROM pargar_sync_logs AS psl WHERE psl.customer_id = transactions.customer_id )' ) ->whereNotExists(function ($q) { $q->select(DB::raw(1)) ->from('pargar_sync_logs') ->whereColumn('pargar_sync_logs.withdraw_id', 'transactions.withdraw_id') ->where('pargar_sync_logs.type', PargarSyncService::TYPE_WALLET_CASH); }) ->orderBy('id'); if (! $scanAll) { $query->where('created_at', '>=', now()->subDays($days)); } if ($customerId > 0) { $query->where('customer_id', $customerId); } $rows = $query->get(['id', 'customer_id', 'withdraw_id', 'cash', 'note', 'created_at']); if ($rows->isEmpty()) { $this->info('سند جاافتاده‌ای پیدا نشد.'); return self::SUCCESS; } $byCustomer = $rows->groupBy('customer_id'); $this->info(sprintf( 'پیدا شد: %d تراکنش روی %d مشتری%s', $rows->count(), $byCustomer->count(), $scanAll ? ' (اسکن کامل)' : " (آخرین {$days} روز)" )); $customers = Customer::whereIn('id', $byCustomer->keys())->get()->keyBy('id'); $table = []; $queued = 0; $skipped = 0; $affectedCustomerIds = []; foreach ($rows as $row) { $customer = $customers->get((int) $row->customer_id); $status = 'آماده'; if ($customer === null || ! $customer->hasValidPargarAccountId()) { $status = 'بدون کد پرگار — رد شد'; $skipped++; } elseif ($execute) { $queuedCount = $sync->queueMissingCashDocuments($customer, [$row], 'requeue_missing'); $status = $queuedCount > 0 ? 'ثبت/موجود شد' : 'ثبت نشد'; if ($queuedCount > 0) { $queued++; $affectedCustomerIds[(int) $row->customer_id] = true; } else { $skipped++; } } else { $affectedCustomerIds[(int) $row->customer_id] = true; } $table[] = [ $row->id, $row->customer_id, $customer->name ?? '—', $row->withdraw_id, number_format((float) $row->cash), (string) $row->created_at, $status, ]; } // جدول کامل ممکن است خیلی بلند باشد؛ تا ۲۰۰ ردیف نشان بده $this->table( ['تراکنش', 'مشتری', 'نام', 'سند برداشت', 'مبلغ', 'تاریخ', 'وضعیت'], array_slice($table, 0, 200) ); if (count($table) > 200) { $this->warn('فقط ۲۰۰ ردیف اول نمایش داده شد از ' . count($table) . ' ردیف.'); } $summary = []; foreach ($byCustomer as $cid => $customerRows) { $c = $customers->get((int) $cid); $summary[] = [ $cid, $c->name ?? '—', $customerRows->count(), number_format((float) $customerRows->sum(fn ($r) => abs((float) $r->cash))), ($c !== null && $c->hasValidPargarAccountId()) ? 'دارد' : 'ندارد', ]; } $this->info('خلاصه به تفکیک مشتری:'); $this->table(['مشتری', 'نام', 'تعداد سند جاافتاده', 'جمع قدرمطلق ریال', 'کد پرگار'], $summary); if (! $execute) { $this->warn('پیش‌نمایش بود. برای ثبت واقعی همه: php artisan pargar:requeue-wallet-documents --all --execute'); return self::SUCCESS; } $this->info(sprintf( 'ثبت‌شده: %d — ردشده: %d — مشتریان ترمیم‌شده: %d', $queued, $skipped, count($affectedCustomerIds) )); return self::SUCCESS; } }