fix pargar and login
This commit is contained in:
@@ -73,13 +73,9 @@ class PargarRequeueWalletDocumentsCommand extends Command
|
|||||||
$status = 'بدون کد پرگار — رد شد';
|
$status = 'بدون کد پرگار — رد شد';
|
||||||
$skipped++;
|
$skipped++;
|
||||||
} elseif ($execute) {
|
} elseif ($execute) {
|
||||||
$log = $sync->queueWalletDocument($customer, 'cash', (float) $row->cash, [
|
$queuedCount = $sync->queueMissingCashDocuments($customer, [$row], 'requeue_missing');
|
||||||
'withdraw_id' => (int) $row->withdraw_id,
|
$status = $queuedCount > 0 ? 'ثبت/موجود شد' : 'ثبت نشد';
|
||||||
'note' => $row->note,
|
$queuedCount > 0 ? $queued++ : $skipped++;
|
||||||
'source' => 'requeue_missing',
|
|
||||||
]);
|
|
||||||
$status = $log !== null ? "ثبت شد (لاگ {$log->id})" : 'ثبت نشد';
|
|
||||||
$log !== null ? $queued++ : $skipped++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$table[] = [
|
$table[] = [
|
||||||
|
|||||||
@@ -29,6 +29,12 @@ class Kernel extends ConsoleKernel
|
|||||||
$schedule->command('prices:prune')
|
$schedule->command('prices:prune')
|
||||||
->dailyAt('00:00')
|
->dailyAt('00:00')
|
||||||
->name('prune_price_snapshots');
|
->name('prune_price_snapshots');
|
||||||
|
|
||||||
|
// ترمیم اسناد ریالی جاافتادهٔ پرگار (واریز/برداشت بدون لاگ) تا مانده قفل نماند
|
||||||
|
$schedule->command('pargar:requeue-wallet-documents --execute --days=120')
|
||||||
|
->hourly()
|
||||||
|
->name('pargar_requeue_wallet_docs')
|
||||||
|
->withoutOverlapping(50);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function commands()
|
protected function commands()
|
||||||
|
|||||||
@@ -33,17 +33,29 @@ class MontzarService
|
|||||||
|
|
||||||
public function montzarLogin(): string
|
public function montzarLogin(): string
|
||||||
{
|
{
|
||||||
$response = Http::timeout(10)
|
try {
|
||||||
->acceptJson()
|
$response = Http::timeout(10)
|
||||||
->post($this->endpoint('/server/auth/login'), [
|
->acceptJson()
|
||||||
'mobile' => $this->config()->Config('montzar_mobile'),
|
->post($this->endpoint('/server/auth/login'), [
|
||||||
'password' => $this->config()->Config('montzar_password'),
|
'mobile' => $this->config()->Config('montzar_mobile'),
|
||||||
'provider' => 'customer',
|
'password' => $this->config()->Config('montzar_password'),
|
||||||
'type_app' => 'client',
|
'provider' => 'customer',
|
||||||
])
|
'type_app' => 'client',
|
||||||
->json();
|
])
|
||||||
|
->json();
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
Log::error('Montzar login request failed', [
|
||||||
|
'base_url' => $this->baseUrl(),
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
throw new UnprocessableEntityHttpException(
|
||||||
|
'سرویس موقتا در دسترس نیست، لطفاً با پشتیبانی تماس بگیرید.'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
$token = $response['access_token'] ?? $response['token'] ?? null;
|
$token = is_array($response)
|
||||||
|
? ($response['access_token'] ?? $response['token'] ?? null)
|
||||||
|
: null;
|
||||||
|
|
||||||
if (!$token) {
|
if (!$token) {
|
||||||
Log::error('Montzar login failed', [
|
Log::error('Montzar login failed', [
|
||||||
@@ -51,7 +63,10 @@ class MontzarService
|
|||||||
'mobile' => $this->config()->Config('montzar_mobile'),
|
'mobile' => $this->config()->Config('montzar_mobile'),
|
||||||
'response' => $response,
|
'response' => $response,
|
||||||
]);
|
]);
|
||||||
throw new \Exception('Montzar login failed: ' . json_encode($response, JSON_UNESCAPED_UNICODE));
|
// 422 تا فرانت toast نشان دهد؛ جزئیات فنی فقط در لاگ میماند
|
||||||
|
throw new UnprocessableEntityHttpException(
|
||||||
|
'سرویس موقتا در دسترس نیست، لطفاً با پشتیبانی تماس بگیرید.'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Setting::updateOrCreate(
|
Setting::updateOrCreate(
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ use Rooyesh\Wallet\models\Transaction;
|
|||||||
* 4) ماندهٔ تازهٔ پرگار (از جمله صفر/تغییر دستی حسابدار) بلافاصله اعمال میشود —
|
* 4) ماندهٔ تازهٔ پرگار (از جمله صفر/تغییر دستی حسابدار) بلافاصله اعمال میشود —
|
||||||
* دیگر منتظر دو snapshot یکسان یا بلوک صفر نمیمانیم.
|
* دیگر منتظر دو snapshot یکسان یا بلوک صفر نمیمانیم.
|
||||||
* 5) اگر واریز/برداشت ریالیای اصلاً سند پرگار نداشته باشد، مانده اعمال نمیشود؛
|
* 5) اگر واریز/برداشت ریالیای اصلاً سند پرگار نداشته باشد، مانده اعمال نمیشود؛
|
||||||
* پرگار از آن پول خبر ندارد و اعمال مانده موجودی مشتری را پاک میکند.
|
* همزمان سند جاافتاده خودکار صف میشود. پرگار از آن پول خبر ندارد و اعمال مانده
|
||||||
|
* موجودی مشتری را پاک میکند.
|
||||||
* 6) دکمهٔ ادمین با force میتواند گیتهای سند pending را رد کند (ابزار اصلاح دستی).
|
* 6) دکمهٔ ادمین با force میتواند گیتهای سند pending را رد کند (ابزار اصلاح دستی).
|
||||||
*/
|
*/
|
||||||
class PargarInboundWalletService
|
class PargarInboundWalletService
|
||||||
@@ -502,11 +503,28 @@ class PargarInboundWalletService
|
|||||||
|
|
||||||
$missing = $this->unexportedCashMovement($customer, $lastConfirmedAt);
|
$missing = $this->unexportedCashMovement($customer, $lastConfirmedAt);
|
||||||
if ($missing !== null) {
|
if ($missing !== null) {
|
||||||
|
// ترمیم خودکار: سند جاافتاده را صف کن تا pending بماند و بعد از ثبت در پرگار مانده باز شود
|
||||||
|
$queued = 0;
|
||||||
|
try {
|
||||||
|
$queued = app(PargarSyncService::class)->queueMissingCashDocuments(
|
||||||
|
$customer,
|
||||||
|
$missing['rows'],
|
||||||
|
'mande_auto_missing'
|
||||||
|
);
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
}
|
||||||
|
|
||||||
|
$suffix = $queued > 0
|
||||||
|
? ' — سند در صف ثبت قرار گرفت؛ تا تأیید پرگار، مانده اعمال نمیشود'
|
||||||
|
: ' — تا ثبت سند، مانده اعمال نمیشود';
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'reason' => 'unexported_wallet_movement',
|
'reason' => 'unexported_wallet_movement',
|
||||||
'message' => 'حرکت ریالی '
|
'message' => 'حرکت ریالی '
|
||||||
. number_format($missing['amount'])
|
. number_format($missing['amount'])
|
||||||
. ' ریال هنوز سندی در پرگار ندارد — تا ثبت سند، مانده اعمال نمیشود',
|
. ' ریال هنوز سندی در پرگار ندارد'
|
||||||
|
. $suffix,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -529,7 +547,7 @@ class PargarInboundWalletService
|
|||||||
* تا وقتی پرگار از این حرکت خبر ندارد، ماندهٔ آن هم شامل این پول نیست؛ اعمال آن
|
* تا وقتی پرگار از این حرکت خبر ندارد، ماندهٔ آن هم شامل این پول نیست؛ اعمال آن
|
||||||
* موجودی مشتری را پاک میکند. پس مانده را نگه میداریم تا سند ساخته و ثبت شود.
|
* موجودی مشتری را پاک میکند. پس مانده را نگه میداریم تا سند ساخته و ثبت شود.
|
||||||
*
|
*
|
||||||
* @return array{amount: float, withdraw_ids: list<int>}|null
|
* @return array{amount: float, withdraw_ids: list<int>, rows: \Illuminate\Support\Collection}|null
|
||||||
*/
|
*/
|
||||||
private function unexportedCashMovement(Customer $customer, ?Carbon $lastConfirmedAt): ?array
|
private function unexportedCashMovement(Customer $customer, ?Carbon $lastConfirmedAt): ?array
|
||||||
{
|
{
|
||||||
@@ -552,7 +570,7 @@ class PargarInboundWalletService
|
|||||||
->whereColumn('pargar_sync_logs.withdraw_id', 'transactions.withdraw_id')
|
->whereColumn('pargar_sync_logs.withdraw_id', 'transactions.withdraw_id')
|
||||||
->where('pargar_sync_logs.type', PargarSyncService::TYPE_WALLET_CASH);
|
->where('pargar_sync_logs.type', PargarSyncService::TYPE_WALLET_CASH);
|
||||||
})
|
})
|
||||||
->get(['withdraw_id', 'cash']);
|
->get(['withdraw_id', 'cash', 'note']);
|
||||||
|
|
||||||
if ($rows->isEmpty()) {
|
if ($rows->isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
@@ -561,6 +579,7 @@ class PargarInboundWalletService
|
|||||||
return [
|
return [
|
||||||
'amount' => (float) $rows->sum(fn ($row) => abs((float) $row->cash)),
|
'amount' => (float) $rows->sum(fn ($row) => abs((float) $row->cash)),
|
||||||
'withdraw_ids' => $rows->pluck('withdraw_id')->map(fn ($id) => (int) $id)->all(),
|
'withdraw_ids' => $rows->pluck('withdraw_id')->map(fn ($id) => (int) $id)->all(),
|
||||||
|
'rows' => $rows,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -151,9 +151,18 @@ class PargarSyncService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* سقف تاریخی ستون amount وقتی هنوز DOUBLE(8,2) است (~۹۹۹٬۹۹۹٫۹۹).
|
||||||
|
* مبلغ واقعی همیشه در data.signed_amount نگه داشته میشود.
|
||||||
|
*/
|
||||||
|
private const AMOUNT_COLUMN_SAFE_MAX = 999999.99;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* صف سند کیف برای سینک مانده پرگار با اپ.
|
* صف سند کیف برای سینک مانده پرگار با اپ.
|
||||||
*
|
*
|
||||||
|
* سند pending باید حتماً ساخته شود؛ اگر ستون amount مبلغ بزرگ را نپذیرد،
|
||||||
|
* بدون amount ثبت میشود تا حرکت کیف بیسندی نماند و مانده قفل نشود.
|
||||||
|
*
|
||||||
* @param 'cash'|'gold' $kind
|
* @param 'cash'|'gold' $kind
|
||||||
* @param array{withdraw_id?:int|null,note?:string|null,product_id?:int|null,source?:string|null} $meta
|
* @param array{withdraw_id?:int|null,note?:string|null,product_id?:int|null,source?:string|null} $meta
|
||||||
*/
|
*/
|
||||||
@@ -201,9 +210,14 @@ class PargarSyncService
|
|||||||
$desc = $isDeposit ? 'شارژ طلای کیف اصلی — ارسال به پرگار' : 'برداشت طلای کیف اصلی — ارسال به پرگار';
|
$desc = $isDeposit ? 'شارژ طلای کیف اصلی — ارسال به پرگار' : 'برداشت طلای کیف اصلی — ارسال به پرگار';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$absAmount = abs($signedAmount);
|
||||||
|
// اگر هنوز مایگریشن widen اجرا نشده، مبلغ بزرگ را از همان اول در ستون نگذار
|
||||||
|
// تا insert نترکد؛ خروجی/مانده از signed_amount میخوانند.
|
||||||
|
$columnAmount = $absAmount <= self::AMOUNT_COLUMN_SAFE_MAX ? $absAmount : null;
|
||||||
|
|
||||||
$attributes = [
|
$attributes = [
|
||||||
'customer_id' => $customer->id,
|
'customer_id' => $customer->id,
|
||||||
'amount' => abs($signedAmount),
|
'amount' => $columnAmount,
|
||||||
'desc' => $desc,
|
'desc' => $desc,
|
||||||
'withdraw_id' => $withdrawId,
|
'withdraw_id' => $withdrawId,
|
||||||
'date' => now(),
|
'date' => now(),
|
||||||
@@ -219,20 +233,145 @@ class PargarSyncService
|
|||||||
], JSON_UNESCAPED_UNICODE),
|
], JSON_UNESCAPED_UNICODE),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
if ($columnAmount === null) {
|
||||||
|
$attributes['sync_log'] = 'amount بزرگتر از سقف ستون بود؛ مبلغ در data.signed_amount است';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->createWalletSyncLogSafely($attributes, $absAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ساخت لاگ سند کیف بدون امکان گمشدن بهخاطر سقف ستون amount.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $attributes
|
||||||
|
*/
|
||||||
|
private function createWalletSyncLogSafely(array $attributes, float $absAmount): PargarSyncLog
|
||||||
|
{
|
||||||
try {
|
try {
|
||||||
|
// اگر ستون widen شده، مبلغ کامل را هم در amount بنویس (نمایش ادمین)
|
||||||
|
if (($attributes['amount'] ?? null) === null && $absAmount > self::AMOUNT_COLUMN_SAFE_MAX) {
|
||||||
|
$withAmount = $attributes;
|
||||||
|
$withAmount['amount'] = $absAmount;
|
||||||
|
unset($withAmount['sync_log']);
|
||||||
|
|
||||||
|
try {
|
||||||
|
return PargarSyncLog::create($withAmount);
|
||||||
|
} catch (QueryException $e) {
|
||||||
|
report($e);
|
||||||
|
$attributes['sync_log'] = mb_substr(
|
||||||
|
'amount ثبت نشد: ' . $e->getMessage(),
|
||||||
|
0,
|
||||||
|
500
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return PargarSyncLog::create($attributes);
|
return PargarSyncLog::create($attributes);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
report($e);
|
report($e);
|
||||||
|
|
||||||
// مبلغ واقعی در data.signed_amount است و خروجی از همان میخواند؛
|
|
||||||
// پس اگر ستون amount مبلغ را نپذیرفت، سند را بدون آن ثبت میکنیم تا گم نشود.
|
|
||||||
$attributes['amount'] = null;
|
$attributes['amount'] = null;
|
||||||
$attributes['sync_log'] = mb_substr('amount ثبت نشد: ' . $e->getMessage(), 0, 500);
|
$attributes['sync_log'] = mb_substr('amount ثبت نشد: ' . $e->getMessage(), 0, 500);
|
||||||
|
|
||||||
return PargarSyncLog::create($attributes);
|
try {
|
||||||
|
return PargarSyncLog::create($attributes);
|
||||||
|
} catch (\Throwable $e2) {
|
||||||
|
report($e2);
|
||||||
|
|
||||||
|
return $this->insertWalletSyncLogRaw($attributes, $e2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* آخرین پناه: insert خام تا سند pending حتماً در DB بماند.
|
||||||
|
*
|
||||||
|
* @param array<string, mixed> $attributes
|
||||||
|
*/
|
||||||
|
private function insertWalletSyncLogRaw(array $attributes, \Throwable $previous): PargarSyncLog
|
||||||
|
{
|
||||||
|
$now = now();
|
||||||
|
$row = [
|
||||||
|
'customer_id' => $attributes['customer_id'] ?? null,
|
||||||
|
'amount' => null,
|
||||||
|
'desc' => $attributes['desc'] ?? null,
|
||||||
|
'order_id' => $attributes['order_id'] ?? null,
|
||||||
|
'withdraw_id' => $attributes['withdraw_id'] ?? null,
|
||||||
|
'date' => $attributes['date'] ?? $now,
|
||||||
|
'type' => $attributes['type'] ?? null,
|
||||||
|
'sync_status' => $attributes['sync_status'] ?? self::STATUS_PENDING,
|
||||||
|
'sync_log' => mb_substr(
|
||||||
|
(string) ($attributes['sync_log'] ?? '') . ' | raw insert: ' . $previous->getMessage(),
|
||||||
|
0,
|
||||||
|
500
|
||||||
|
),
|
||||||
|
'data' => $attributes['data'] ?? null,
|
||||||
|
'created_at' => $now,
|
||||||
|
'updated_at' => $now,
|
||||||
|
];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$id = DB::table('pargar_sync_logs')->insertGetId($row);
|
||||||
|
$log = PargarSyncLog::find($id);
|
||||||
|
if ($log !== null) {
|
||||||
|
Log::warning('Pargar wallet document saved via raw insert', [
|
||||||
|
'pargar_sync_log_id' => $id,
|
||||||
|
'withdraw_id' => $row['withdraw_id'],
|
||||||
|
'customer_id' => $row['customer_id'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
return $log;
|
||||||
|
}
|
||||||
|
} catch (\Throwable $e) {
|
||||||
|
report($e);
|
||||||
|
Log::critical('Pargar wallet document queue failed completely', [
|
||||||
|
'customer_id' => $row['customer_id'],
|
||||||
|
'withdraw_id' => $row['withdraw_id'],
|
||||||
|
'type' => $row['type'],
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new \RuntimeException(
|
||||||
|
'ساخت سند کیف پرگار ناموفق بود (withdraw_id=' . ($row['withdraw_id'] ?? 'null') . ')'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* صفکردن سند برای واریز/برداشتهایی که لاگ پرگار ندارند.
|
||||||
|
*
|
||||||
|
* @param iterable<int, object{withdraw_id?:mixed,cash?:mixed,note?:mixed}> $rows
|
||||||
|
* @return int تعداد سند تازهساخته یا موجود
|
||||||
|
*/
|
||||||
|
public function queueMissingCashDocuments(Customer $customer, iterable $rows, string $source = 'auto_missing'): int
|
||||||
|
{
|
||||||
|
if (! $this->isSyncEnabled() || ! $this->hasSyncTable()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
$queued = 0;
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$withdrawId = (int) ($row->withdraw_id ?? 0);
|
||||||
|
$cash = (float) ($row->cash ?? 0);
|
||||||
|
if ($withdrawId <= 0 || abs($cash) < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = $this->queueWalletDocument($customer, 'cash', $cash, [
|
||||||
|
'withdraw_id' => $withdrawId,
|
||||||
|
'note' => $row->note ?? null,
|
||||||
|
'source' => $source,
|
||||||
|
]);
|
||||||
|
if ($log !== null) {
|
||||||
|
$queued++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $queued;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array{
|
* @param array{
|
||||||
* payment_kind?: string,
|
* payment_kind?: string,
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ return new class extends Migration
|
|||||||
Schema::create('pargar_sync_logs', function (Blueprint $table) {
|
Schema::create('pargar_sync_logs', function (Blueprint $table) {
|
||||||
$table->id();
|
$table->id();
|
||||||
$table->unsignedBigInteger('customer_id')->nullable()->index();
|
$table->unsignedBigInteger('customer_id')->nullable()->index();
|
||||||
$table->float('amount')->nullable();
|
// decimal نه float — float() لاراول ۸ میشود DOUBLE(8,2) و مبالغ بزرگ گم میشوند
|
||||||
|
$table->decimal('amount', 20, 2)->nullable();
|
||||||
$table->string('desc')->nullable();
|
$table->string('desc')->nullable();
|
||||||
$table->unsignedBigInteger('order_id')->nullable()->index();
|
$table->unsignedBigInteger('order_id')->nullable()->index();
|
||||||
$table->unsignedBigInteger('withdraw_id')->nullable()->index();
|
$table->unsignedBigInteger('withdraw_id')->nullable()->index();
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ use Illuminate\Support\Facades\Schema;
|
|||||||
* ستون amount با float() ساخته شده بود و لاراول ۸ آن را DOUBLE(8,2) میسازد؛
|
* ستون amount با float() ساخته شده بود و لاراول ۸ آن را DOUBLE(8,2) میسازد؛
|
||||||
* سقفش ۹۹۹٬۹۹۹٫۹۹ است. هر سند ریالی بزرگتر از یک میلیون در insert خطا میداد و
|
* سقفش ۹۹۹٬۹۹۹٫۹۹ است. هر سند ریالی بزرگتر از یک میلیون در insert خطا میداد و
|
||||||
* چون فراخوانیها داخل try/catch بودند، سند بیصدا گم میشد و پرگار واریز را نمیدید.
|
* چون فراخوانیها داخل try/catch بودند، سند بیصدا گم میشد و پرگار واریز را نمیدید.
|
||||||
|
*
|
||||||
|
* DECIMAL(20,2) برای مبالغ ریالی بزرگ (میلیاردها) کافی و امن است.
|
||||||
|
* idempotent: اگر از قبل DECIMAL/DOUBLE بدون سقف باشد هم آسیب نمیزند.
|
||||||
*/
|
*/
|
||||||
return new class extends Migration
|
return new class extends Migration
|
||||||
{
|
{
|
||||||
@@ -17,7 +20,7 @@ return new class extends Migration
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
DB::statement('ALTER TABLE `pargar_sync_logs` MODIFY `amount` DOUBLE NULL');
|
DB::statement('ALTER TABLE `pargar_sync_logs` MODIFY `amount` DECIMAL(20, 2) NULL');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function down(): void
|
public function down(): void
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* اگر هنوز DOUBLE(8,2) یا DOUBLE بدون مقیاس پولپسند باشد، به DECIMAL(20,2) ببر.
|
||||||
|
* مبالغ میلیاردی ریال باید در ستون amount هم قابل ذخیره باشند.
|
||||||
|
*/
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (! Schema::hasTable('pargar_sync_logs') || ! Schema::hasColumn('pargar_sync_logs', 'amount')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = DB::selectOne(
|
||||||
|
"SELECT COLUMN_TYPE AS column_type
|
||||||
|
FROM information_schema.COLUMNS
|
||||||
|
WHERE TABLE_SCHEMA = DATABASE()
|
||||||
|
AND TABLE_NAME = 'pargar_sync_logs'
|
||||||
|
AND COLUMN_NAME = 'amount'"
|
||||||
|
);
|
||||||
|
|
||||||
|
$columnType = strtolower((string) ($type->column_type ?? ''));
|
||||||
|
if ($columnType === 'decimal(20,2)') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DB::statement('ALTER TABLE `pargar_sync_logs` MODIFY `amount` DECIMAL(20, 2) NULL');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
// برگشت عمداً انجام نمیشود.
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -2,18 +2,10 @@
|
|||||||
-- ستون pargar_sync_logs.amount با float() ساخته شده بود = DOUBLE(8,2) با سقف ۹۹۹٬۹۹۹٫۹۹
|
-- ستون pargar_sync_logs.amount با float() ساخته شده بود = DOUBLE(8,2) با سقف ۹۹۹٬۹۹۹٫۹۹
|
||||||
-- هر سند واریز/برداشت ریالی بزرگتر از این سقف در insert خطا میداد و سند اصلاً ساخته نمیشد،
|
-- هر سند واریز/برداشت ریالی بزرگتر از این سقف در insert خطا میداد و سند اصلاً ساخته نمیشد،
|
||||||
-- پس پرگار واریز مشتری را نمیدید و در تطبیق مانده، موجودی ریالی کیف صفر میشد.
|
-- پس پرگار واریز مشتری را نمیدید و در تطبیق مانده، موجودی ریالی کیف صفر میشد.
|
||||||
|
-- DECIMAL(20,2) برای مبالغ میلیاردی کافی است.
|
||||||
-- idempotent: چند بار قابل اجرا
|
-- idempotent: چند بار قابل اجرا
|
||||||
|
|
||||||
SET @db = DATABASE();
|
ALTER TABLE `pargar_sync_logs` MODIFY `amount` DECIMAL(20, 2) NULL;
|
||||||
|
|
||||||
SET @sql = IF(
|
|
||||||
(SELECT COUNT(*) FROM information_schema.COLUMNS
|
|
||||||
WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'pargar_sync_logs'
|
|
||||||
AND COLUMN_NAME = 'amount' AND UPPER(COLUMN_TYPE) <> 'DOUBLE') = 1,
|
|
||||||
'ALTER TABLE `pargar_sync_logs` MODIFY `amount` DOUBLE NULL',
|
|
||||||
'SELECT ''skip pargar_sync_logs.amount'' AS info'
|
|
||||||
);
|
|
||||||
PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
|
||||||
|
|
||||||
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE
|
SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE
|
||||||
FROM information_schema.COLUMNS
|
FROM information_schema.COLUMNS
|
||||||
|
|||||||
Reference in New Issue
Block a user