all customer from pargar fixing 3

This commit is contained in:
2026-07-22 01:05:00 +03:30
parent 01031e9e01
commit 133f4156c6
13 changed files with 214 additions and 59 deletions
+64 -28
View File
@@ -11,13 +11,15 @@ use Rooyesh\Wallet\interfaces\WalletInterface;
use Rooyesh\Wallet\models\Wallet;
/**
* همگام‌سازی کیف با پرگار — فقط با دکمهٔ ادمین.
* همگام‌سازی کیف با پرگار.
*
* قواعد سخت:
* 1) webhook اسناد (/documents) هرگز کیف را عوض نمی‌کند — فقط لاگ.
* 2) دکمه: دسته‌های سند را فقط «مصرف‌شده» علامت می‌زند (بدون دلتا روی کیف)،
* سپس مانده (mande) را به‌عنوان منبع حقیقت روی کیف می‌نشاند.
* 3) اعمال تک‌تک سطرهای تاریخچهٔ پرگار ممنوع است — باعث تورم/تایم‌اوت می‌شود.
* 1) webhook اسناد inbound فقط لاگ می‌کند؛ کیف را با replay تاریخچه عوض نمی‌کند.
* 2) مانده برای مشتریانی که سند ارسالیِ pending به پرگار ندارند → خودکار روی کیف ست می‌شود.
* 3) اگر سند پنل هنوز در پرگار ثبت نشده، مانده اعمال نمی‌شود؛ بعد از تأیید ثبت در پرگار،
* ماندهٔ جدید خودکار سینک می‌شود (تا موجودی مشتری بی‌دلیل کم/زیاد نشود).
* 4) دکمهٔ ادمین می‌تواند force کند.
* 5) اعمال تک‌تک سطرهای تاریخچهٔ پرگار ممنوع است.
*/
class PargarInboundWalletService
{
@@ -44,19 +46,36 @@ class PargarInboundWalletService
}
/**
* دکمهٔ ادمین:
* ۱) علامت مصرف دسته‌های سند (بدون نوشتن روی کیف)
* آیا سندی از پنل در صف ثبت داخل پرگار است؟ (کیف یا سفارش)
* در این حالت مانده را اعمال نمی‌کنیم تا بعد از ثبت سند، ماندهٔ درست بیاید.
*/
public function hasPendingOutboundToPargar(Customer $customer): bool
{
return PargarSyncLog::query()
->where('customer_id', $customer->id)
->whereIn('type', [
PargarSyncService::TYPE_WALLET_CASH,
PargarSyncService::TYPE_WALLET_GOLD,
PargarSyncService::TYPE_ORDER,
])
->where('sync_status', PargarSyncService::STATUS_PENDING)
->exists();
}
/**
* دکمهٔ ادمین / همگام‌سازی خودکار:
* ۱) علامت مصرف دسته‌های سند inbound (بدون نوشتن روی کیف)
* ۲) هم‌ترازی مانده با آخرین لاگ mande
*
* @param bool $forceMande ادمین: حتی با سند ارسالی pending
* @param bool $auto مسیر خودکار: سند ارسالی pending محترم بماند
* @return array{
* documents: array{applied: int, skipped: int, batches: int, acknowledged: int, errors: list<string>},
* mande: array{applied: bool, gold_delta?: float, rial_delta?: float, message: string}
* }
*/
public function syncWalletFromPargar(Customer $customer, bool $forceMande = false): array
public function syncWalletFromPargar(Customer $customer, bool $forceMande = false, bool $auto = false): array
{
unset($forceMande); // مانده در دکمه همیشه force می‌شود
if (self::isApplyFrozen()) {
return [
'documents' => [
@@ -88,11 +107,19 @@ class PargarInboundWalletService
}
try {
// ۱) تاریخچهٔ اسناد را روی کیف replay نکن — فقط مصرف‌شده علامت بزن
$this->attachOrphanDocumentLogs($customer);
$this->attachOrphanMandeLogs($customer);
// ۱) تاریخچهٔ اسناد inbound را روی کیف replay نکن — فقط مصرف‌شده علامت بزن
$documents = $this->acknowledgePendingDocumentBatches($customer);
// ۲) منبع حقیقت موجودی = ماندهٔ پرگار
$mande = $this->applyLatestMandeDifference($customer, force: true);
// خودکار: اگر سند پنل هنوز در پرگار ثبت نشده → اعمال نکن (force=false)
// ادمین: force تا بتوان اصلاح کرد
$mande = $this->applyLatestMandeDifference(
$customer,
force: $auto ? false : ($forceMande || true)
);
if (($documents['acknowledged'] ?? 0) > 0 || ($mande['applied'] ?? false)) {
$this->walletBroadcast->broadcastForCustomer(
@@ -251,11 +278,11 @@ class PargarInboundWalletService
*/
public function applyLatestMandeDifference(Customer $customer, bool $force = false): array
{
// دکمهٔ ادمین (force): حتی با سند pending خروجی، مانده را هم‌تراز کن تا کیف متورم اصلاح شود
if (! $force && $this->hasPendingOutboundWalletSync((int) $customer->id)) {
// دکمهٔ ادمین (force): حتی با سند pending خروجی، مانده را هم‌تراز کن
if (! $force && $this->hasPendingOutboundToPargar($customer)) {
return [
'applied' => false,
'message' => 'سند ارسالی از پنل هنوز تأیید نشده — مانده اعمال نشد تا شارژ برنگردد',
'message' => 'سند پنل هنوز در پرگار ثبت نشده — مانده بعد از ثبت سند اعمال می‌شود',
];
}
@@ -374,15 +401,6 @@ class PargarInboundWalletService
return round((float) $query->sum('gold'), 3);
}
private function hasPendingOutboundWalletSync(int $customerId): bool
{
return PargarSyncLog::query()
->where('customer_id', $customerId)
->whereIn('type', [PargarSyncService::TYPE_WALLET_CASH, PargarSyncService::TYPE_WALLET_GOLD])
->where('sync_status', PargarSyncService::STATUS_PENDING)
->exists();
}
private function parseNumber(mixed $value): float
{
if ($value === null || $value === '') {
@@ -393,7 +411,7 @@ class PargarInboundWalletService
}
/**
* پس از اتصال کد پرگار / claim ثبت‌نام — همگام‌سازی کیف در پس‌زمینه.
* همگام‌سازی خودکار در پس‌زمینه — بعد از مانده / تأیید سند در پرگار / import.
*/
public function queueWalletSyncAfterResponse(int $customerId): void
{
@@ -403,10 +421,8 @@ class PargarInboundWalletService
return;
}
$this->attachOrphanMandeLogs($customer);
try {
$this->syncWalletFromPargar($customer, true);
$this->syncWalletFromPargar($customer, forceMande: false, auto: true);
} catch (\Throwable $e) {
Log::warning('Pargar auto wallet sync failed', [
'customer_id' => $customerId,
@@ -435,4 +451,24 @@ class PargarInboundWalletService
})
->update(['customer_id' => $customer->id]);
}
/**
* اسناد یتیم (قبل از لینک مشتری) را به customer_id وصل می‌کند.
*/
public function attachOrphanDocumentLogs(Customer $customer): int
{
if (! $customer->hasValidPargarAccountId()) {
return 0;
}
$accountId = trim((string) $customer->pargar_account_id);
return PargarSyncLog::query()
->where('type', PargarSyncService::TYPE_DOCUMENT)
->where('pargar_ref', $accountId)
->where(function ($q) {
$q->whereNull('customer_id')->orWhere('customer_id', 0);
})
->update(['customer_id' => $customer->id]);
}
}