diff --git a/app/Http/Controllers/Pargar/PargarInboundController.php b/app/Http/Controllers/Pargar/PargarInboundController.php index 9c22eb8..f962ee1 100644 --- a/app/Http/Controllers/Pargar/PargarInboundController.php +++ b/app/Http/Controllers/Pargar/PargarInboundController.php @@ -329,7 +329,7 @@ class PargarInboundController extends Controller $appCash = (float) ($wallet->cash ?? 0); $goldDiff = $goldInv - $appGold; $rialDiff = $rialInv - $appCash; - $hasDiff = abs($goldDiff) >= 0.01 || abs($rialDiff) >= 1; + $hasDiff = PargarSyncService::isWalletDocAmount($goldDiff) || abs($rialDiff) >= 1; $pendingOutbound = $this->inboundWallet()->hasPendingOutboundToPargar($customer); @@ -687,7 +687,7 @@ class PargarInboundController extends Controller $payload = json_decode((string) $log->data, true) ?: []; $kind = (string) ($payload['kind'] ?? ($log->type === PargarSyncService::TYPE_WALLET_GOLD ? 'gold' : 'cash')); $signed = (float) ($payload['signed_amount'] ?? $log->amount ?? 0); - if (abs($signed) < ($kind === 'cash' ? 1 : 0.01)) { + if (! PargarSyncService::isWalletDocAmount($signed)) { $signed = (float) ($log->amount ?? 0); // اگر علامت نبود، از desc حدس بزن — پیش‌فرض واریز مثبت if (! isset($payload['signed_amount']) && is_string($log->desc) && str_contains($log->desc, 'برداشت')) { diff --git a/app/Services/PargarInboundWalletService.php b/app/Services/PargarInboundWalletService.php index edeada6..6297053 100644 --- a/app/Services/PargarInboundWalletService.php +++ b/app/Services/PargarInboundWalletService.php @@ -35,6 +35,9 @@ class PargarInboundWalletService public const KILL_SWITCH_CACHE_KEY = 'pargar_wallet_apply_disabled'; + /** شارژ/کسر طلای کیف ادمین + پاداش معرفی/ثبت‌نام */ + public const GOLD_DOCUMENT_TX_TYPES = [7, 8]; + /** واریز و برداشت ریالی — دو نوعی که باید سند ریالی پرگار داشته باشند */ public const CASH_DOCUMENT_TX_TYPES = [3, 4]; @@ -404,7 +407,9 @@ class PargarInboundWalletService $goldDiff = $goldInv - $mainGold; $rialDiff = $rialInv - (float) ($wallet->cash ?? 0); - if (abs($goldDiff) < 0.01 && abs($rialDiff) < 1) { + $goldSame = ! PargarSyncService::isWalletDocAmount($goldDiff); + $rialSame = abs($rialDiff) < 1; + if ($goldSame && $rialSame) { $data['wallet_applied_from_mande'] = true; unset($data['mande_skip_reason'], $data['mande_skipped_at']); $log->data = json_encode($data, JSON_UNESCAPED_UNICODE); @@ -423,7 +428,7 @@ class PargarInboundWalletService 'expire_at' => now(), 'active' => true, ]; - if (abs($goldDiff) >= 0.01) { + if (PargarSyncService::isWalletDocAmount($goldDiff)) { $input['gold'] = abs($goldDiff); } if (abs($rialDiff) >= 1) { @@ -528,6 +533,32 @@ class PargarInboundWalletService ]; } + $missingGold = $this->unexportedGoldMovement($customer, $lastConfirmedAt); + if ($missingGold !== null) { + $queued = 0; + try { + $queued = app(PargarSyncService::class)->queueMissingGoldDocuments( + $customer, + $missingGold['rows'], + 'mande_auto_missing_gold' + ); + } catch (\Throwable $e) { + report($e); + } + + $suffix = $queued > 0 + ? ' — سند در صف ثبت قرار گرفت؛ تا تأیید پرگار، مانده اعمال نمی‌شود' + : ' — تا ثبت سند، مانده اعمال نمی‌شود'; + + return [ + 'reason' => 'unexported_gold_movement', + 'message' => 'حرکت طلایی ' + . rtrim(rtrim(number_format($missingGold['amount'], 6, '.', ''), '0'), '.') + . ' گرم هنوز سندی در پرگار ندارد' + . $suffix, + ]; + } + return null; } @@ -562,7 +593,8 @@ class PargarInboundWalletService ->whereNotNull('withdraw_id') ->where('created_at', '>', $since) ->where(function ($q) { - $q->where('cash', '>=', 1)->orWhere('cash', '<=', -1); + $q->where('cash', '>=', PargarSyncService::WALLET_DOC_EPSILON) + ->orWhere('cash', '<=', -PargarSyncService::WALLET_DOC_EPSILON); }) ->whereNotExists(function ($q) { $q->select(DB::raw(1)) @@ -583,6 +615,46 @@ class PargarInboundWalletService ]; } + /** + * شارژ طلا / پاداش که سند پرگارش ساخته نشده (از جمله وزن زیر ۰٫۰۱ گرم). + * + * @return array{amount: float, withdraw_ids: list, rows: \Illuminate\Support\Collection}|null + */ + private function unexportedGoldMovement(Customer $customer, ?Carbon $lastConfirmedAt): ?array + { + $since = $this->cashMovementScanStart($customer, $lastConfirmedAt); + if ($since === null) { + return null; + } + + $rows = Transaction::query() + ->where('customer_id', $customer->id) + ->whereIn('type', self::GOLD_DOCUMENT_TX_TYPES) + ->whereNotNull('withdraw_id') + ->where('created_at', '>', $since) + ->where(function ($q) { + $q->where('gold', '>=', PargarSyncService::WALLET_DOC_EPSILON) + ->orWhere('gold', '<=', -PargarSyncService::WALLET_DOC_EPSILON); + }) + ->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_GOLD); + }) + ->get(['withdraw_id', 'gold', 'note']); + + if ($rows->isEmpty()) { + return null; + } + + return [ + 'amount' => (float) $rows->sum(fn ($row) => abs((float) $row->gold)), + 'withdraw_ids' => $rows->pluck('withdraw_id')->map(fn ($id) => (int) $id)->all(), + 'rows' => $rows, + ]; + } + /** * از کجا دنبال سند جاافتاده بگردیم: بعد از آخرین سند ثبت‌شده، و اگر نبود از * شروع اتصال همین مشتری به پرگار. تراکنش‌های قبل از اتصال سند ندارند و نباید diff --git a/app/Services/PargarSyncService.php b/app/Services/PargarSyncService.php index d3ddb18..1f5503a 100644 --- a/app/Services/PargarSyncService.php +++ b/app/Services/PargarSyncService.php @@ -28,6 +28,14 @@ class PargarSyncService public const PAYMENT_MAIN_GOLD = 'main_gold'; public const PAYMENT_OTHER_PRODUCT = 'other_product'; + /** فقط صفر اعشاری/نویز شناور رد شود — سند ریز (۰٫۰۰۴ گرم جایزه) باید برود */ + public const WALLET_DOC_EPSILON = 1.0e-12; + + public static function isWalletDocAmount(float $amount): bool + { + return abs($amount) >= self::WALLET_DOC_EPSILON; + } + public function buildLog(mixed $response = null, ?string $extra = null): string { $parts = array_filter([ @@ -176,7 +184,7 @@ class PargarSyncService return null; } - if (abs($signedAmount) < ($kind === 'cash' ? 1 : 0.01)) { + if (! self::isWalletDocAmount($signedAmount)) { return null; } @@ -353,7 +361,7 @@ class PargarSyncService foreach ($rows as $row) { $withdrawId = (int) ($row->withdraw_id ?? 0); $cash = (float) ($row->cash ?? 0); - if ($withdrawId <= 0 || abs($cash) < 1) { + if ($withdrawId <= 0 || ! self::isWalletDocAmount($cash)) { continue; } @@ -370,6 +378,38 @@ class PargarSyncService return $queued; } + /** + * صف‌کردن سند طلا برای حرکت‌هایی که لاگ پرگار ندارند (جایزه ریز و …). + * + * @param iterable $rows + */ + public function queueMissingGoldDocuments(Customer $customer, iterable $rows, string $source = 'auto_missing_gold'): int + { + if (! $this->isSyncEnabled() || ! $this->hasSyncTable()) { + return 0; + } + + $queued = 0; + foreach ($rows as $row) { + $withdrawId = (int) ($row->withdraw_id ?? 0); + $gold = (float) ($row->gold ?? 0); + if ($withdrawId <= 0 || ! self::isWalletDocAmount($gold)) { + continue; + } + + $log = $this->queueWalletDocument($customer, 'gold', $gold, [ + 'withdraw_id' => $withdrawId, + 'note' => $row->note ?? null, + 'source' => $source, + ]); + if ($log !== null) { + $queued++; + } + } + + return $queued; + } + /** * @param array{ * payment_kind?: string, @@ -818,10 +858,10 @@ class PargarSyncService $amount = null; $amountUnit = null; - if ($maxGold >= 0.01) { + if ($maxGold >= self::WALLET_DOC_EPSILON) { $amount = $maxGold; $amountUnit = 'gold'; - } elseif ($maxRial >= 1) { + } elseif ($maxRial >= self::WALLET_DOC_EPSILON) { $amount = $maxRial; $amountUnit = 'rial'; }