fix bug of pargar

This commit is contained in:
2026-07-26 14:15:26 +03:30
parent 667e3736fb
commit 06b3113b98
17 changed files with 561 additions and 26 deletions
+84 -2
View File
@@ -6,9 +6,11 @@ use App\Models\Customer;
use App\Models\PargarSyncLog;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Rooyesh\Wallet\interfaces\TransactionInterface;
use Rooyesh\Wallet\interfaces\WalletInterface;
use Rooyesh\Wallet\models\Transaction;
/**
* همگام‌سازی کیف با پرگار.
@@ -21,7 +23,9 @@ use Rooyesh\Wallet\interfaces\WalletInterface;
* پیش از ثبت سند گرفته شده باشد، اعمال نمی‌شود و منتظر ماندهٔ بعدی می‌مانیم.
* 4) اعمال فقط با دو snapshot پیاپیِ هم‌مقدار — تا مانده‌ای که وسط ثبت اسناد گرفته شده
* موجودی مشتری را عقب نبرد.
* 5) دکمهٔ ادمین با force می‌تواند همهٔ گیت‌های بالا را رد کند (ابزار اصلاح دستی).
* 5) اگر واریز/برداشت ریالی‌ای اصلاً سند پرگار نداشته باشد، مانده اعمال نمی‌شود؛
* پرگار از آن پول خبر ندارد و اعمال مانده موجودی مشتری را پاک می‌کند.
* 6) دکمهٔ ادمین با force می‌تواند همهٔ گیت‌های بالا را رد کند (ابزار اصلاح دستی).
*/
class PargarInboundWalletService
{
@@ -29,6 +33,9 @@ class PargarInboundWalletService
public const KILL_SWITCH_CACHE_KEY = 'pargar_wallet_apply_disabled';
/** واریز و برداشت ریالی — دو نوعی که باید سند ریالی پرگار داشته باشند */
public const CASH_DOCUMENT_TX_TYPES = [3, 4];
/** سقف تعداد دسته برای علامت‌زدن در هر کلیک */
private const MAX_BATCHES_PER_SYNC = 50;
@@ -342,12 +349,16 @@ class PargarInboundWalletService
$input['cash'] = abs($rialDiff);
}
// مانده پرگار می‌تواند منفی باشد و کیف باید همان را نشان دهد؛ کف موجودی اینجا اعمال نمی‌شود
new TransactionInterface(
$walletInterface,
$input,
self::TX_TYPE_PARGAR,
$goldDiff > 0,
$rialDiff > 0
$rialDiff > 0,
0,
[],
true
);
$data['wallet_applied_from_mande'] = true;
@@ -404,6 +415,16 @@ class PargarInboundWalletService
];
}
$missing = $this->unexportedCashMovement($customer, $lastConfirmedAt);
if ($missing !== null) {
return [
'reason' => 'unexported_wallet_movement',
'message' => 'حرکت ریالی '
. number_format($missing['amount'])
. ' ریال هنوز سندی در پرگار ندارد — تا ثبت سند، مانده اعمال نمی‌شود',
];
}
$previous = $this->previousComparableMande($log, $lastConfirmedAt);
if ($previous === null) {
return [
@@ -429,6 +450,67 @@ class PargarInboundWalletService
return null;
}
/**
* واریز/برداشت ریالی که سند پرگارش ساخته نشده است.
*
* تا وقتی پرگار از این حرکت خبر ندارد، ماندهٔ آن هم شامل این پول نیست؛ اعمال آن
* موجودی مشتری را پاک می‌کند. پس مانده را نگه می‌داریم تا سند ساخته و ثبت شود.
*
* @return array{amount: float, withdraw_ids: list<int>}|null
*/
private function unexportedCashMovement(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::CASH_DOCUMENT_TX_TYPES)
->whereNotNull('withdraw_id')
->where('created_at', '>', $since)
->where(function ($q) {
$q->where('cash', '>=', 1)->orWhere('cash', '<=', -1);
})
->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);
})
->get(['withdraw_id', 'cash']);
if ($rows->isEmpty()) {
return null;
}
return [
'amount' => (float) $rows->sum(fn ($row) => abs((float) $row->cash)),
'withdraw_ids' => $rows->pluck('withdraw_id')->map(fn ($id) => (int) $id)->all(),
];
}
/**
* از کجا دنبال سند جاافتاده بگردیم: بعد از آخرین سند ثبت‌شده، و اگر نبود از
* شروع اتصال همین مشتری به پرگار. تراکنش‌های قبل از اتصال سند ندارند و نباید
* سینک را برای همیشه قفل کنند.
*/
private function cashMovementScanStart(Customer $customer, ?Carbon $lastConfirmedAt): ?Carbon
{
$firstLogAt = PargarSyncLog::query()
->where('customer_id', $customer->id)
->min('created_at');
$integrationStart = $firstLogAt !== null ? Carbon::parse($firstLogAt) : null;
if ($lastConfirmedAt !== null && $integrationStart !== null) {
return $lastConfirmedAt->greaterThan($integrationStart) ? $lastConfirmedAt : $integrationStart;
}
return $lastConfirmedAt ?? $integrationStart;
}
/**
* ماندهٔ صفرِ هر دو فیلد برای مشتری‌ای که کیفش خالی نیست، تقریباً همیشه یعنی
* پرگار حساب را پیدا نکرده — نه اینکه مشتری تسویه کرده باشد.