fix(server): queue tiny Pargar gold wallet documents instead of skipping them
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -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, 'برداشت')) {
|
||||
|
||||
@@ -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<int>, 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,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* از کجا دنبال سند جاافتاده بگردیم: بعد از آخرین سند ثبتشده، و اگر نبود از
|
||||
* شروع اتصال همین مشتری به پرگار. تراکنشهای قبل از اتصال سند ندارند و نباید
|
||||
|
||||
@@ -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<int, object{withdraw_id?:mixed,gold?:mixed,note?:mixed}> $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';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user