diff --git a/app/Http/Controllers/Pargar/PargarInboundController.php b/app/Http/Controllers/Pargar/PargarInboundController.php index bf507f2..8ba79bb 100644 --- a/app/Http/Controllers/Pargar/PargarInboundController.php +++ b/app/Http/Controllers/Pargar/PargarInboundController.php @@ -336,7 +336,7 @@ class PargarInboundController extends Controller $desc = $pendingOutbound ? 'مانده دریافت شد — منتظر ثبت سند پنل در پرگار' : ($hasDiff - ? 'اختلاف مانده پرگار با اپ — پس از تأیید پایداری اعمال می‌شود' + ? 'اختلاف مانده پرگار با اپ — در حال اعمال روی کیف' : 'دریافت مانده از پرگار — بدون اختلاف'); PargarSyncLog::create([ @@ -356,17 +356,33 @@ class PargarInboundController extends Controller ]), JSON_UNESCAPED_UNICODE), ]); - // همیشه صف همگام‌سازی: اگر سند pending نباشد اعمال می‌شود؛ وگرنه بعد از تأیید سند اعمال می‌شود - $this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id); + // سند pending نباشد → مانده را همین‌جا اعمال کن تا تغییر دستی حسابدار فوری روی اپ بنشیند + // (صف afterResponse برای وقتی است که هنوز باید منتظر ثبت سند پنل بمانیم) + if ($pendingOutbound) { + $this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id); + $appliedNow = false; + } else { + $appliedNow = false; + try { + $syncResult = $this->inboundWallet()->syncWalletFromPargar($customer, forceMande: false, auto: true); + $appliedNow = (bool) ($syncResult['mande']['applied'] ?? false); + } catch (\Throwable $e) { + report($e); + $this->inboundWallet()->queueWalletSyncAfterResponse((int) $customer->id); + } + } return response()->json([ 'success' => true, 'message' => $pendingOutbound ? 'مانده ذخیره شد؛ بعد از ثبت سند در پرگار خودکار سینک می‌شود' - : 'مانده دریافت شد؛ همگام‌سازی خودکار در صف است', + : ($appliedNow + ? 'مانده دریافت و روی کیف اپ اعمال شد' + : 'مانده دریافت شد؛ همگام‌سازی انجام شد'), 'data' => [ 'awaiting_outbound_confirm' => $pendingOutbound, - 'auto_sync_queued' => true, + 'wallet_applied' => $appliedNow, + 'auto_sync_queued' => $pendingOutbound, ], ]); } diff --git a/app/Services/PargarInboundWalletService.php b/app/Services/PargarInboundWalletService.php index 508ca1e..8953730 100644 --- a/app/Services/PargarInboundWalletService.php +++ b/app/Services/PargarInboundWalletService.php @@ -22,11 +22,11 @@ use Rooyesh\Wallet\models\Transaction; * محصولات خارج از کیف اصلی هرگز از مانده اثر نمی‌گیرند. * 3) ماندهٔ کهنه دور ریخته می‌شود، نه معوق: اگر سند ما هنوز pending باشد یا snapshot * پیش از ثبت سند گرفته شده باشد، اعمال نمی‌شود و منتظر ماندهٔ بعدی می‌مانیم. - * 4) اعمال فقط با دو snapshot پیاپیِ هم‌مقدار — تا مانده‌ای که وسط ثبت اسناد گرفته شده - * موجودی مشتری را عقب نبرد. + * 4) ماندهٔ تازهٔ پرگار (از جمله صفر/تغییر دستی حسابدار) بلافاصله اعمال می‌شود — + * دیگر منتظر دو snapshot یکسان یا بلوک صفر نمی‌مانیم. * 5) اگر واریز/برداشت ریالی‌ای اصلاً سند پرگار نداشته باشد، مانده اعمال نمی‌شود؛ * پرگار از آن پول خبر ندارد و اعمال مانده موجودی مشتری را پاک می‌کند. - * 6) دکمهٔ ادمین با force می‌تواند همهٔ گیت‌های بالا را رد کند (ابزار اصلاح دستی). + * 6) دکمهٔ ادمین با force می‌تواند گیت‌های سند pending را رد کند (ابزار اصلاح دستی). */ class PargarInboundWalletService { @@ -461,9 +461,12 @@ class PargarInboundWalletService } /** - * ماندهٔ پرگار فقط وقتی معتبر است که همهٔ اسناد ما در پرگار ثبت شده باشند و - * snapshot بعد از آن ثبت گرفته شده باشد. اعمال ماندهٔ قدیمی یعنی برگرداندن - * موجودی مشتری به قبل از سند — همان «وزن/ریالم کم شد». + * ماندهٔ پرگار منبع حقیقت است — از جمله تغییر دستی حسابدار در پنل پرگار. + * فقط وقتی نگه می‌داریم که هنوز سندِ ساخته‌شدهٔ خودمان در پرگار ثبت نشده باشد؛ + * وگرنه موجودی مشتری به قبل از آن سند برمی‌گردد («وزن/ریالم کم شد»). + * + * دیگر منتظر «دو snapshot یکسان» یا بلوک صفر نمی‌مانیم: حسابدار ممکن است عمداً + * مانده را صفر/عوض کند و باید همان لحظه روی اپ بنشیند. * * @param array $data * @return array{reason: string, message: string}|null null یعنی معتبر @@ -499,28 +502,6 @@ class PargarInboundWalletService ]; } - $previous = $this->previousComparableMande($log, $lastConfirmedAt); - if ($previous === null) { - return [ - 'reason' => 'awaiting_second_snapshot', - 'message' => 'منتظر ماندهٔ بعدی برای تأیید پایداری', - ]; - } - - if (! $this->mandeValuesMatch($data, $previous)) { - return [ - 'reason' => 'unstable_snapshot', - 'message' => 'ماندهٔ پرگار هنوز در حال تغییر است — منتظر مقدار پایدار', - ]; - } - - if ($this->isSuspiciousZeroSnapshot($customer, $data)) { - return [ - 'reason' => 'zero_snapshot', - 'message' => 'ماندهٔ پرگار صفر است ولی کیف مشتری خالی نیست — برای جلوگیری از صفر شدن اشتباه اعمال نشد', - ]; - } - return null; } @@ -595,29 +576,6 @@ class PargarInboundWalletService return $lastConfirmedAt ?? $integrationStart; } - /** - * ماندهٔ صفرِ هر دو فیلد برای مشتری‌ای که کیفش خالی نیست، تقریباً همیشه یعنی - * پرگار حساب را پیدا نکرده — نه اینکه مشتری تسویه کرده باشد. - * - * @param array $data - */ - private function isSuspiciousZeroSnapshot(Customer $customer, array $data): bool - { - $goldInv = $this->parseNumber($data['GoldInv'] ?? 0); - $rialInv = $this->parseNumber($data['RialInv'] ?? 0); - - if (abs($goldInv) >= 0.001 || abs($rialInv) >= 1) { - return false; - } - - $ownerType = (int) ($customer->type ?? 2); - $mainGold = app(WalletBalanceService::class) - ->effectiveMainWalletGold((int) $customer->id, $ownerType); - $cash = (float) ((new WalletInterface((int) $customer->id, $ownerType))->wallet->cash ?? 0); - - return abs($mainGold) >= 0.01 || abs($cash) >= 1; - } - /** * آخرین لحظه‌ای که پرگار یکی از اسناد ما را ثبت کرده است. */ @@ -640,50 +598,6 @@ class PargarInboundWalletService return $value !== null ? Carbon::parse($value) : null; } - /** - * ماندهٔ قبلی که آن هم بعد از ثبت آخرین سند گرفته شده باشد. - * - * @return array|null - */ - private function previousComparableMande(PargarSyncLog $log, ?Carbon $lastConfirmedAt): ?array - { - $query = PargarSyncLog::query() - ->where('customer_id', $log->customer_id) - ->where('type', PargarSyncService::TYPE_MANDE) - ->where('id', '<', $log->id) - ->orderByDesc('id'); - - if ($lastConfirmedAt !== null) { - $query->where('created_at', '>', $lastConfirmedAt); - } - - $previous = $query->first(); - if ($previous === null) { - return null; - } - - $data = json_decode((string) $previous->data, true); - - return is_array($data) ? $data : null; - } - - /** - * @param array $current - * @param array $previous - */ - private function mandeValuesMatch(array $current, array $previous): bool - { - $goldMatch = abs( - $this->parseNumber($current['GoldInv'] ?? 0) - $this->parseNumber($previous['GoldInv'] ?? 0) - ) < 0.001; - - $rialMatch = abs( - $this->parseNumber($current['RialInv'] ?? 0) - $this->parseNumber($previous['RialInv'] ?? 0) - ) < 1; - - return $goldMatch && $rialMatch; - } - /** * @param array $data */