diff --git a/app/Console/Commands/PargarRequeueWalletDocumentsCommand.php b/app/Console/Commands/PargarRequeueWalletDocumentsCommand.php index afe68b7..d746c59 100644 --- a/app/Console/Commands/PargarRequeueWalletDocumentsCommand.php +++ b/app/Console/Commands/PargarRequeueWalletDocumentsCommand.php @@ -12,12 +12,16 @@ use Rooyesh\Wallet\models\Transaction; * ساخت دوبارهٔ سند ریالی برای واریز/برداشت‌هایی که سندشان به پرگار نرسیده است. * * تا وقتی سند نرسد، ماندهٔ پرگار این پول را ندارد و تطبیق مانده موجودی کیف را صفر می‌کند. + * + * فقط تراکنش‌های بعد از اولین لاگ پرگار همان مشتری بررسی می‌شوند + * (قبل از اتصال به پرگار سند نمی‌خواسته‌اند). */ class PargarRequeueWalletDocumentsCommand extends Command { protected $signature = 'pargar:requeue-wallet-documents {--customer= : فقط همین مشتری} - {--days=30 : چند روز اخیر بررسی شود} + {--days=30 : چند روز اخیر؛ با --all نادیده گرفته می‌شود} + {--all : همهٔ مشتریان از ابتدای اتصال پرگار (بدون سقف روز)} {--execute : ثبت واقعی سندها (بدون این فلگ فقط پیش‌نمایش است)}'; protected $description = 'صف‌کردن دوبارهٔ اسناد ریالی جاافتادهٔ پرگار (واریز/برداشت بدون سند)'; @@ -30,6 +34,13 @@ class PargarRequeueWalletDocumentsCommand extends Command 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'); @@ -37,10 +48,17 @@ class PargarRequeueWalletDocumentsCommand extends Command $query = Transaction::query() ->whereIn('type', [3, 4]) ->whereNotNull('withdraw_id') - ->where('created_at', '>=', now()->subDays($days)) ->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') @@ -49,6 +67,10 @@ class PargarRequeueWalletDocumentsCommand extends Command }) ->orderBy('id'); + if (! $scanAll) { + $query->where('created_at', '>=', now()->subDays($days)); + } + if ($customerId > 0) { $query->where('customer_id', $customerId); } @@ -60,10 +82,19 @@ class PargarRequeueWalletDocumentsCommand extends Command return self::SUCCESS; } - $customers = Customer::whereIn('id', $rows->pluck('customer_id')->unique())->get()->keyBy('id'); + $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); @@ -75,7 +106,14 @@ class PargarRequeueWalletDocumentsCommand extends Command } elseif ($execute) { $queuedCount = $sync->queueMissingCashDocuments($customer, [$row], 'requeue_missing'); $status = $queuedCount > 0 ? 'ثبت/موجود شد' : 'ثبت نشد'; - $queuedCount > 0 ? $queued++ : $skipped++; + if ($queuedCount > 0) { + $queued++; + $affectedCustomerIds[(int) $row->customer_id] = true; + } else { + $skipped++; + } + } else { + $affectedCustomerIds[(int) $row->customer_id] = true; } $table[] = [ @@ -89,18 +127,41 @@ class PargarRequeueWalletDocumentsCommand extends Command ]; } + // جدول کامل ممکن است خیلی بلند باشد؛ تا ۲۰۰ ردیف نشان بده $this->table( ['تراکنش', 'مشتری', 'نام', 'سند برداشت', 'مبلغ', 'تاریخ', 'وضعیت'], - $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('پیش‌نمایش بود. برای ثبت واقعی --execute بزنید.'); + $this->warn('پیش‌نمایش بود. برای ثبت واقعی همه: php artisan pargar:requeue-wallet-documents --all --execute'); return self::SUCCESS; } - $this->info("ثبت‌شده: {$queued} — ردشده: {$skipped}"); + $this->info(sprintf( + 'ثبت‌شده: %d — ردشده: %d — مشتریان ترمیم‌شده: %d', + $queued, + $skipped, + count($affectedCustomerIds) + )); return self::SUCCESS; } diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index 594be4f..6e54352 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -30,11 +30,17 @@ class Kernel extends ConsoleKernel ->dailyAt('00:00') ->name('prune_price_snapshots'); - // ترمیم اسناد ریالی جاافتادهٔ پرگار (واریز/برداشت بدون لاگ) تا مانده قفل نماند - $schedule->command('pargar:requeue-wallet-documents --execute --days=120') + // ترمیم اسناد ریالی جاافتادهٔ پرگار — سریع برای موارد تازه + $schedule->command('pargar:requeue-wallet-documents --execute --days=14') ->hourly() - ->name('pargar_requeue_wallet_docs') + ->name('pargar_requeue_wallet_docs_hourly') ->withoutOverlapping(50); + + // یک‌بار در شبانه‌روز همهٔ مشتریان از ابتدای اتصال پرگار + $schedule->command('pargar:requeue-wallet-documents --execute --all') + ->dailyAt('03:40') + ->name('pargar_requeue_wallet_docs_all') + ->withoutOverlapping(110); } protected function commands()