fix(wallet): absorb cash left on old owner type
Deposits credited before customer type was set stayed on owner_type 0 and assets ignored them. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
فرمت هر خط:
|
فرمت هر خط:
|
||||||
- YYYY-MM-DD | موضوع | مسیر فایل(ها) | نکتهٔ ۱ خطی
|
- YYYY-MM-DD | موضوع | مسیر فایل(ها) | نکتهٔ ۱ خطی
|
||||||
|
|
||||||
|
- 2026-11-14 | کیف ریال owner_type ناهماهنگ | `WalletBalanceService::absorbMismatchedOwnerTypeBalances` + `WalletInterface` | واریز روی owner_type=0 در دارایی نوع مشتری دیده نمیشود؛ با باز شدن کیف به نوع فعلی منتقل میشود
|
||||||
- 2026-11-05 | کد کاربری مشتری بعدی | `Customer::nextSequentialId` در `creating` | کد کاربری=`customers.id`؛ بعد از UPDATE دستی id، AUTO_INCREMENT پایین نمیآید (۵۵۱ بهجای ۲۴). ایجاد مشتری = اولین id خالی، نه شمارندهٔ InnoDB
|
- 2026-11-05 | کد کاربری مشتری بعدی | `Customer::nextSequentialId` در `creating` | کد کاربری=`customers.id`؛ بعد از UPDATE دستی id، AUTO_INCREMENT پایین نمیآید (۵۵۱ بهجای ۲۴). ایجاد مشتری = اولین id خالی، نه شمارندهٔ InnoDB
|
||||||
- 2026-11-04 | فرمت کد تفصیلی شایگان | `ShayeganSyncService::customerTafsilNumber` | کل `shayegan_kol_customer` (121) + کد کاربر ۷رقمی: مشتری 1 → `1210000001`. کد ذخیرهشدهٔ قبلی عوض نمیشود
|
- 2026-11-04 | فرمت کد تفصیلی شایگان | `ShayeganSyncService::customerTafsilNumber` | کل `shayegan_kol_customer` (121) + کد کاربر ۷رقمی: مشتری 1 → `1210000001`. کد ذخیرهشدهٔ قبلی عوض نمیشود
|
||||||
- 2026-11-02 | جدول اسناد شایگان | `shayeganTable.vue` + `ShayeganController::table` | BaseTable؛ پاسخ بدون `query` را خود فرانت به `{rows,query}` برمیگرداند. ستون عملیات `ops` است تا در RTL هشدار fixed ندهد
|
- 2026-11-02 | جدول اسناد شایگان | `shayeganTable.vue` + `ShayeganController::table` | BaseTable؛ پاسخ بدون `query` را خود فرانت به `{rows,query}` برمیگرداند. ستون عملیات `ops` است تا در RTL هشدار fixed ندهد
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class WalletController extends Controller
|
|||||||
$customer = auth('customer')->user();
|
$customer = auth('customer')->user();
|
||||||
$customerId = (int) $customer->id;
|
$customerId = (int) $customer->id;
|
||||||
$ownerType = (int) $customer->type;
|
$ownerType = (int) $customer->type;
|
||||||
|
$balanceService->absorbMismatchedOwnerTypeBalances($customerId, $ownerType);
|
||||||
|
|
||||||
$wallets = Wallet::query()
|
$wallets = Wallet::query()
|
||||||
->where('owner', $customerId)
|
->where('owner', $customerId)
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ class AssetSummaryService
|
|||||||
|
|
||||||
public function buildForCustomer(int $customerId, int $ownerType, bool $includeInactiveProducts = false): array
|
public function buildForCustomer(int $customerId, int $ownerType, bool $includeInactiveProducts = false): array
|
||||||
{
|
{
|
||||||
|
if ($customerId > 0 && $ownerType > 0) {
|
||||||
|
$this->walletBalanceService->absorbMismatchedOwnerTypeBalances($customerId, $ownerType);
|
||||||
|
}
|
||||||
|
|
||||||
$catalogQuery = Product::query()->with('file');
|
$catalogQuery = Product::query()->with('file');
|
||||||
if (!$includeInactiveProducts) {
|
if (!$includeInactiveProducts) {
|
||||||
$catalogQuery->where('active', 1);
|
$catalogQuery->where('active', 1);
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ use App\Models\Price;
|
|||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
||||||
|
use Rooyesh\Wallet\interfaces\WalletInterface;
|
||||||
use Rooyesh\Wallet\models\Wallet;
|
use Rooyesh\Wallet\models\Wallet;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,6 +19,11 @@ use Rooyesh\Wallet\models\Wallet;
|
|||||||
*/
|
*/
|
||||||
class WalletBalanceService
|
class WalletBalanceService
|
||||||
{
|
{
|
||||||
|
/** @var array<string, true> */
|
||||||
|
private static array $absorbedOwnerTypes = [];
|
||||||
|
|
||||||
|
private static bool $absorbingOwnerType = false;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private ConfigService $configService,
|
private ConfigService $configService,
|
||||||
private BalanceReportService $balanceReportService
|
private BalanceReportService $balanceReportService
|
||||||
@@ -161,6 +168,85 @@ class WalletBalanceService
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ماندهٔ کیفهایی که owner_typeشان با نوع فعلی مشتری یکی نیست
|
||||||
|
* (مثلاً واریز وقتی type هنوز ۰ بوده) به کیف همان محصول/قلک روی نوع فعلی منتقل میشود.
|
||||||
|
*/
|
||||||
|
public function absorbMismatchedOwnerTypeBalances(int $ownerId, int $canonicalType): bool
|
||||||
|
{
|
||||||
|
if ($ownerId <= 0 || $canonicalType <= 0 || self::$absorbingOwnerType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$key = $ownerId.':'.$canonicalType;
|
||||||
|
if (isset(self::$absorbedOwnerTypes[$key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self::$absorbedOwnerTypes[$key] = true;
|
||||||
|
|
||||||
|
$foreign = Wallet::query()
|
||||||
|
->where('owner', $ownerId)
|
||||||
|
->where('owner_type', '!=', $canonicalType)
|
||||||
|
->where(function ($query) {
|
||||||
|
$query->where('cash', '!=', 0)->orWhere('gold', '!=', 0);
|
||||||
|
})
|
||||||
|
->get();
|
||||||
|
|
||||||
|
if ($foreign->isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$absorbingOwnerType = true;
|
||||||
|
try {
|
||||||
|
foreach ($foreign as $row) {
|
||||||
|
DB::transaction(function () use ($row, $ownerId, $canonicalType) {
|
||||||
|
$locked = Wallet::query()->whereKey($row->id)->lockForUpdate()->first();
|
||||||
|
if (!$locked) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cash = (int) round((float) $locked->cash);
|
||||||
|
$gold = (float) $locked->gold;
|
||||||
|
if ($cash === 0 && abs($gold) < 0.0000001) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$from = new WalletInterface($ownerId, (int) $locked->owner_type, $locked->fund_id, $locked->product_id);
|
||||||
|
$to = new WalletInterface($ownerId, $canonicalType, $locked->fund_id, $locked->product_id);
|
||||||
|
$note = 'انتقال مانده به کیف اصلی مشتری';
|
||||||
|
|
||||||
|
if ($cash !== 0) {
|
||||||
|
$payload = [
|
||||||
|
'cash' => abs($cash),
|
||||||
|
'gold' => 0,
|
||||||
|
'note' => $note,
|
||||||
|
'expire_at' => now(),
|
||||||
|
'active' => true,
|
||||||
|
];
|
||||||
|
new TransactionInterface($from, $payload, 7, false, $cash < 0);
|
||||||
|
new TransactionInterface($to, $payload, 7, false, $cash > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (abs($gold) >= 0.0000001) {
|
||||||
|
$payload = [
|
||||||
|
'gold' => abs($gold),
|
||||||
|
'cash' => 0,
|
||||||
|
'note' => $note,
|
||||||
|
'expire_at' => now(),
|
||||||
|
'active' => true,
|
||||||
|
];
|
||||||
|
new TransactionInterface($from, $payload, 7, $gold < 0, false);
|
||||||
|
new TransactionInterface($to, $payload, 7, $gold > 0, false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
self::$absorbingOwnerType = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** ادغام همهٔ کیفهای legacy محصولات main_wallet_products برای یک کاربر */
|
/** ادغام همهٔ کیفهای legacy محصولات main_wallet_products برای یک کاربر */
|
||||||
public function consolidateAllMainWalletProductsForOwner(int $ownerId, int $ownerType): void
|
public function consolidateAllMainWalletProductsForOwner(int $ownerId, int $ownerType): void
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -44,6 +44,22 @@ class WalletInterface
|
|||||||
]);
|
]);
|
||||||
$this->set_hash();
|
$this->set_hash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->absorbMismatchedOwnerTypeBalances();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** واریزِ مانده روی owner_type قدیمی را به نوع فعلی مشتری برمیگرداند. */
|
||||||
|
private function absorbMismatchedOwnerTypeBalances(): void
|
||||||
|
{
|
||||||
|
$ownerType = (int) $this->owner_type;
|
||||||
|
$ownerId = (int) $this->owner_id;
|
||||||
|
if ($ownerType <= 0 || $ownerId <= 0 || !$this->wallet) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
app(\App\Services\WalletBalanceService::class)
|
||||||
|
->absorbMismatchedOwnerTypeBalances($ownerId, $ownerType);
|
||||||
|
$this->wallet->refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validate(): bool
|
public function validate(): bool
|
||||||
|
|||||||
Reference in New Issue
Block a user