2026-01-24 14:03:11 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers\Customer;
|
|
|
|
|
|
|
|
|
|
use App\DB\Te\TableEngine;
|
|
|
|
|
use App\Http\Controllers\Controller;
|
|
|
|
|
use App\Models\Address;
|
|
|
|
|
use App\Models\Customer;
|
|
|
|
|
use App\Models\Order;
|
2026-06-09 13:34:43 +03:30
|
|
|
use App\Models\Product;
|
2026-01-24 14:03:11 +00:00
|
|
|
use App\Models\User;
|
|
|
|
|
use App\Models\Withdraw;
|
|
|
|
|
use App\Models\BankAccount;
|
|
|
|
|
use App\Models\Price;
|
2026-06-05 16:24:15 +03:30
|
|
|
use App\Models\Setting;
|
2026-01-24 14:03:11 +00:00
|
|
|
use App\Http\Controllers\Admin\TahesabController;
|
2026-07-05 16:43:34 +03:30
|
|
|
use App\Services\ConfigService;
|
2026-06-09 16:44:02 +03:30
|
|
|
use App\Services\AssetSummaryService;
|
2026-07-05 13:04:00 +03:30
|
|
|
use App\Services\WalletBalanceService;
|
2026-06-02 12:24:54 +03:30
|
|
|
use App\Services\ZaarboxPriceService;
|
2026-01-24 14:03:11 +00:00
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Illuminate\Support\Facades\Http;
|
|
|
|
|
|
|
|
|
|
use Rooyesh\Wallet\interfaces\TransactionInterface;
|
|
|
|
|
use Rooyesh\Wallet\interfaces\WalletInterface;
|
|
|
|
|
use Rooyesh\Wallet\models\Transaction;
|
|
|
|
|
use Rooyesh\Wallet\models\Wallet;
|
|
|
|
|
use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
|
|
|
|
|
|
|
|
|
|
class WalletController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function withdrawTable(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$records = Withdraw::with(['transaction'])->where('customer_id', auth('customer')->id())->whereIn('sts', [2,1])->orderBy('id','desc');
|
|
|
|
|
TableEngine::create($records, $request->all())
|
|
|
|
|
->initSearch(['customer.name'])
|
|
|
|
|
->select([
|
|
|
|
|
'*',Withdraw::$SELECT_STS_STR,Withdraw::$SELECT_DAJ
|
|
|
|
|
])->export($records);
|
|
|
|
|
return response()->json($records);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-05 13:04:00 +03:30
|
|
|
public function walletMe(WalletBalanceService $balanceService)
|
2026-01-24 14:03:11 +00:00
|
|
|
{
|
2026-07-05 13:04:00 +03:30
|
|
|
$customer = auth('customer')->user();
|
|
|
|
|
$customerId = (int) $customer->id;
|
|
|
|
|
$ownerType = (int) $customer->type;
|
|
|
|
|
|
|
|
|
|
$wallets = Wallet::query()
|
2026-06-11 21:17:40 +03:30
|
|
|
->where('owner', $customerId)
|
2026-07-05 13:04:00 +03:30
|
|
|
->where('owner_type', $ownerType)
|
|
|
|
|
->get();
|
2026-01-24 14:03:11 +00:00
|
|
|
|
2026-07-05 13:04:00 +03:30
|
|
|
$mainWallet = $wallets->first(
|
|
|
|
|
fn (Wallet $row) => $row->fund_id === null && $row->product_id === null
|
|
|
|
|
);
|
2026-06-11 21:17:40 +03:30
|
|
|
|
2026-07-05 13:04:00 +03:30
|
|
|
return response()->json([
|
|
|
|
|
'owner' => $customerId,
|
|
|
|
|
'id' => $mainWallet?->id,
|
|
|
|
|
'gold' => $balanceService->mainWalletGold($wallets),
|
|
|
|
|
'cash' => $balanceService->mainWalletCash($wallets),
|
|
|
|
|
'gold_sum' => round($balanceService->totalGoldWeight($wallets), 2),
|
|
|
|
|
]);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-09 16:44:02 +03:30
|
|
|
public function assetsSummary(AssetSummaryService $assetSummaryService)
|
|
|
|
|
{
|
|
|
|
|
$user = auth('customer')->user();
|
|
|
|
|
|
|
|
|
|
return response()->json(
|
|
|
|
|
$assetSummaryService->buildForCustomer((int) $user->id, (int) $user->type)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-02 12:24:54 +03:30
|
|
|
public function priceData(Request $request, ZaarboxPriceService $zaarboxPriceService)
|
2026-01-24 14:03:11 +00:00
|
|
|
{
|
2026-06-02 12:24:54 +03:30
|
|
|
return response()->json([
|
|
|
|
|
'code' => 0,
|
|
|
|
|
'message' => 'عملیات با موفقیت انجام شد.',
|
|
|
|
|
'data' => [
|
|
|
|
|
'prices' => $zaarboxPriceService->getAllPrices(),
|
|
|
|
|
],
|
|
|
|
|
]);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function transactionTable(Request $request)
|
|
|
|
|
{
|
2026-06-30 10:24:42 +03:30
|
|
|
$customerId = (int) auth('customer')->id();
|
|
|
|
|
$this->repairMisclassifiedRialTransactions($customerId);
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
if (isset($request->wallet_id)){
|
|
|
|
|
$wallet = Wallet::with('product')->where('id', $request->wallet_id)->where('owner', auth('customer')->id())->get();
|
|
|
|
|
}else{
|
|
|
|
|
$wallet = Wallet::with('product')->where('owner', auth('customer')->id())->get();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($wallet->isEmpty()) {
|
|
|
|
|
$records = ['query' => ['total' => 0], 'rows' => []];
|
|
|
|
|
} else {
|
|
|
|
|
$records = Transaction::with([
|
|
|
|
|
'order' => function ($q) {
|
|
|
|
|
$q->select(['*', Order::$SELECT_STS_STR]);
|
|
|
|
|
},
|
|
|
|
|
'withdraw' => function ($q) {
|
2026-05-23 13:30:05 +03:30
|
|
|
$q->select(['*', Withdraw::$SELECT_STS_STR]);
|
2026-01-24 14:03:11 +00:00
|
|
|
},
|
2026-06-09 13:34:43 +03:30
|
|
|
'wallet.product',
|
|
|
|
|
'product',
|
2026-01-24 14:03:11 +00:00
|
|
|
])->whereIn('wallet', $wallet->pluck('id'))->whereNull('fund_id');
|
|
|
|
|
|
|
|
|
|
TableEngine::create($records, $request->all())
|
|
|
|
|
->initSearch(['customer.name'])
|
|
|
|
|
->select([
|
|
|
|
|
'*', Transaction::$SELECT_CAJ, Transaction::$SELECT_TYPE_STR, Transaction::$SELECT_SAJ, Transaction::$SELECT_EAJ
|
|
|
|
|
])->export($records);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
foreach ($records['rows'] as &$record) {
|
2026-06-04 19:22:01 +03:30
|
|
|
if ($this->isShopGoldTransaction($record)) {
|
|
|
|
|
$this->applyShopTransactionPresentation($record);
|
2026-06-09 13:34:43 +03:30
|
|
|
$this->applyAbshodeProductTransactionTitle($record);
|
2026-06-04 19:22:01 +03:30
|
|
|
$this->applyMetalTypeTransactionTitle($record);
|
|
|
|
|
$this->applyProductTransactionTitle($record);
|
|
|
|
|
$this->applyProductOrderDisplayFields($record);
|
|
|
|
|
$this->applyProductTransactionUnit($record);
|
2026-06-30 10:24:42 +03:30
|
|
|
self::normalizeRialTransactionPresentation($record);
|
2026-06-04 19:22:01 +03:30
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-24 12:30:39 +03:30
|
|
|
// بقیه رکوردها فقط اگر title ندارند یا تعریف نشده هستند اصلاح شوند
|
|
|
|
|
if (empty($record->title) || $record->title == "تعریف نشده") {
|
|
|
|
|
|
|
|
|
|
$record->title = $record->type_str;
|
|
|
|
|
$record->unit = "گرم";
|
|
|
|
|
|
|
|
|
|
if (isset($record->wallet)) {
|
|
|
|
|
$wallet = Wallet::with('product')->where('id', $record->wallet)->first();
|
|
|
|
|
|
|
|
|
|
if (isset($wallet->product)) {
|
2026-06-03 21:37:05 +03:30
|
|
|
$direction = $record->order->direction ?? null;
|
|
|
|
|
if ((int) $direction === 1) {
|
|
|
|
|
$record->title = self::productBuyTitle($wallet->product->title);
|
|
|
|
|
} elseif ((int) $direction === 2) {
|
|
|
|
|
$record->title = self::productSellTitle($wallet->product->title);
|
2026-05-24 12:30:39 +03:30
|
|
|
} else {
|
2026-06-03 21:37:05 +03:30
|
|
|
$record->title = ($record->gold > 0 ? self::productBuyTitle($wallet->product->title) : self::productSellTitle($wallet->product->title));
|
2026-05-24 12:30:39 +03:30
|
|
|
}
|
2026-06-22 21:29:25 +03:30
|
|
|
$record->unit = self::productQuantityUnit(self::productUnitPayload($wallet->product));
|
2026-06-03 21:37:05 +03:30
|
|
|
} else {
|
|
|
|
|
$record->title = $this->resolveMainWalletTransactionTitle($record);
|
2026-05-24 12:30:39 +03:30
|
|
|
}
|
2026-06-03 21:37:05 +03:30
|
|
|
} else {
|
|
|
|
|
$record->title = $record->type_str;
|
2026-05-24 12:30:39 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$record->save();
|
|
|
|
|
}
|
2026-06-03 20:24:07 +03:30
|
|
|
|
2026-06-09 13:34:43 +03:30
|
|
|
$this->applyAbshodeProductTransactionTitle($record);
|
2026-06-03 20:24:07 +03:30
|
|
|
$this->applyMetalTypeTransactionTitle($record);
|
2026-06-03 21:37:05 +03:30
|
|
|
$this->applyProductTransactionTitle($record);
|
|
|
|
|
$this->applyProductOrderDisplayFields($record);
|
2026-06-03 21:52:37 +03:30
|
|
|
$this->applyProductTransactionUnit($record);
|
2026-06-30 10:24:42 +03:30
|
|
|
self::normalizeRialTransactionPresentation($record);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
$records['rows'] = $this->serializeTransactionRowsForApi($records['rows']);
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
return response()->json($records);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-30 10:24:42 +03:30
|
|
|
/**
|
|
|
|
|
* دادهٔ قدیمی: type=4 با cash مثبت (واریز ادمین) — قبل از فیلتر جدول اصلاح میشود.
|
|
|
|
|
*/
|
|
|
|
|
private function repairMisclassifiedRialTransactions(int $customerId): void
|
|
|
|
|
{
|
|
|
|
|
if ($customerId <= 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Transaction::query()
|
|
|
|
|
->where('customer_id', $customerId)
|
|
|
|
|
->whereNull('fund_id')
|
|
|
|
|
->where('type', 4)
|
|
|
|
|
->where('cash', '>', 0)
|
|
|
|
|
->update([
|
|
|
|
|
'type' => 3,
|
|
|
|
|
'title' => self::rialDepositTitle(),
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
Transaction::query()
|
|
|
|
|
->where('customer_id', $customerId)
|
|
|
|
|
->whereNull('fund_id')
|
|
|
|
|
->where('type', 3)
|
|
|
|
|
->where('cash', '<', 0)
|
|
|
|
|
->update([
|
|
|
|
|
'type' => 4,
|
|
|
|
|
'title' => self::rialWithdrawTitle(),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
public function transactionTableb(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$wallet = Wallet::with('product')->where('owner', auth('customer')->id())->whereNull('fund_id')->get();
|
|
|
|
|
if (is_null($wallet)) {
|
|
|
|
|
$records = ['query' => ['total' => 0], 'rows' => []];
|
|
|
|
|
} else {
|
|
|
|
|
$records = Transaction::with(['order' => function ($q) {
|
|
|
|
|
return $q->select(['*', Order::$SELECT_STS_STR]);
|
|
|
|
|
}, 'withdraw' => function ($q) {
|
|
|
|
|
return $q->select(['*', Order::$SELECT_STS_STR]);
|
|
|
|
|
}, 'wallet.product'])->whereIn('wallet', $wallet->pluck('id'))->whereNull('fund_id');
|
|
|
|
|
TableEngine::create($records, $request->all())
|
|
|
|
|
->initSearch(['customer.name'])
|
|
|
|
|
->select([
|
|
|
|
|
'*', Transaction::$SELECT_CAJ, Transaction::$SELECT_TYPE_STR, Transaction::$SELECT_SAJ, Transaction::$SELECT_EAJ
|
|
|
|
|
])->export($records);
|
|
|
|
|
}
|
|
|
|
|
foreach ($records['rows'] as &$record) {
|
|
|
|
|
return $record['wallet'];
|
|
|
|
|
if (isset($record->wallet->product->title)) {
|
|
|
|
|
$record->title = $record->wallet->product->title."22";
|
|
|
|
|
$record->save();
|
|
|
|
|
} else {
|
|
|
|
|
$record->title = $record->type_str."22";
|
|
|
|
|
$record->save();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json($records);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function withdraw(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$customer = auth('customer')->user();
|
|
|
|
|
$data = $request->all();
|
|
|
|
|
if (!isset($data['card_id'])) {
|
2026-05-23 13:30:05 +03:30
|
|
|
return response()->json(['message' => 'لطفا کارت بانکی را وارد کنید'], 422);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
$bCart = BankAccount::where('id', $data['card_id'])->first();
|
|
|
|
|
if ($bCart->sts != 2) {
|
2026-05-23 13:30:05 +03:30
|
|
|
return response()->json(['message' => 'کارت انتخاب شده نا معتبر است'], 422);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
|
|
|
|
if ($data['value'] < 0) {
|
|
|
|
|
throw new UnprocessableEntityHttpException('مقدار برداشت باید مثبت باشد');
|
|
|
|
|
}
|
2026-06-05 16:24:15 +03:30
|
|
|
|
2026-06-05 16:45:40 +03:30
|
|
|
$this->validateWithdrawAmount((int) $data['value']);
|
2026-06-05 16:24:15 +03:30
|
|
|
|
2026-06-18 19:32:28 +03:30
|
|
|
$extraNote = trim((string) ($data['extra_note'] ?? ''));
|
|
|
|
|
if (strlen($extraNote) > 500) {
|
|
|
|
|
return response()->json(['message' => 'توضیحات اضافی حداکثر ۵۰۰ کاراکتر است'], 422);
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
$Withdraw = Withdraw::create([
|
|
|
|
|
'customer_id' => auth('customer')->id(),
|
|
|
|
|
"cash" => $data['value'],
|
|
|
|
|
"note" => "برداشت ریالی",
|
2026-06-18 19:32:28 +03:30
|
|
|
'extra_note' => $extraNote !== '' ? $extraNote : null,
|
2026-01-24 14:03:11 +00:00
|
|
|
"bank_account_id" => $data['card_id'],
|
|
|
|
|
'sts' => 1,
|
|
|
|
|
'type' => 2
|
|
|
|
|
]);
|
|
|
|
|
$transaction = new TransactionInterface($wallet_interface_fund, $_inp = [
|
|
|
|
|
"cash" => $data['value'],
|
|
|
|
|
'expire_at' => now(),
|
|
|
|
|
'withdraw_id' => $Withdraw->id,
|
|
|
|
|
'active' => true
|
|
|
|
|
], 4, false, false);
|
|
|
|
|
$userMobile = $customer->mobile; // User's mobile number
|
|
|
|
|
$userName = $customer->name; // User's name
|
|
|
|
|
$withdrawAmount = $data['value']; // amount
|
2026-07-05 16:43:34 +03:30
|
|
|
if (app(ConfigService::class)->isTahesabAccounting()) {
|
2026-06-25 22:20:40 +03:30
|
|
|
try {
|
|
|
|
|
$result = app(TahesabController::class)->wallet([
|
|
|
|
|
"customer_id" => auth('customer')->id(),
|
|
|
|
|
"value" => $data['value'],
|
|
|
|
|
"direction" => 1,
|
|
|
|
|
]);
|
2026-05-29 21:28:50 +03:30
|
|
|
|
2026-06-25 22:20:40 +03:30
|
|
|
\Log::info('Tahesab withdraw result', [
|
|
|
|
|
'customer_id' => auth('customer')->id(),
|
|
|
|
|
'value' => $data['value'],
|
|
|
|
|
'result' => $result,
|
|
|
|
|
]);
|
|
|
|
|
} catch (\Exception $e) {
|
|
|
|
|
report($e);
|
|
|
|
|
}
|
2026-04-08 06:24:46 +00:00
|
|
|
}
|
2026-01-24 14:03:11 +00:00
|
|
|
$companyName = $this->Config('name_fa'); // Company name from configuration
|
|
|
|
|
$message = "کاربر $userName با شماره موبایل $userMobile درخواست برداشت مبلغ " . number_format($withdrawAmount) . " ریال را دارد. با تشکر، $companyName";
|
|
|
|
|
if (isset($request->otp)) {
|
|
|
|
|
if (Customer::where("id", $customer->id)->first()->token_sms != $request->otp) {
|
|
|
|
|
return response()->json(['message' => 'رمز دوم اشتباه میباشد'], 422);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
User::where('send_sms', 1)->get()->each(function ($admin) use ($message) {
|
|
|
|
|
$this->Send($admin->mobile, $message . ' ' . now()->format('H:i:s'));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return response()->json($Withdraw);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function withdraw_physical(Request $request)
|
|
|
|
|
{
|
|
|
|
|
$data = $request->all();
|
|
|
|
|
|
|
|
|
|
$data['customer'] = Customer::with([/*"wallet"*/])->where('id', auth('customer')->id())->first();
|
|
|
|
|
$data['customer']['wallet_interface'] = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
|
|
|
|
|
|
|
|
|
if (!isset($data['address_id'])) {
|
|
|
|
|
return response()->json(['message' => 'آدرس انتخاب نشده است'], 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data['adress'] = Address::find($data['address_id']);
|
|
|
|
|
if (!$data['adress'] || $data['adress']->customer_id != $data['customer']->id) {
|
|
|
|
|
return response()->json(['message' => 'آدرس معتبر نیست یا متعلق به شما نمیباشد'], 403);
|
|
|
|
|
}
|
|
|
|
|
foreach ($data['wallets'] as $key => $wallet) {
|
|
|
|
|
$data['wallets'][$key]['wallet'] = Wallet::with(["product"])->find($wallet['wallet_id']);
|
|
|
|
|
if (!$data['wallets'][$key]['wallet']) {
|
|
|
|
|
return response()->json(['message' => 'ولت مورد نظر یافت نشد.'], 422);
|
|
|
|
|
}
|
|
|
|
|
if ($data['wallets'][$key]['wallet']->owner != $data['customer']['id']) {
|
|
|
|
|
return response()->json(['message' => 'ولت انتخاب شده متعلق به شما نمیباشد.'], 422);
|
|
|
|
|
}
|
|
|
|
|
// بررسی وجود محصول در ولت
|
|
|
|
|
if (!$data['wallets'][$key]['wallet']['product']) {
|
|
|
|
|
return response()->json(['message' => 'محصولی در ولت مورد نظر یافت نشد.'], 422);
|
|
|
|
|
}
|
2026-06-10 17:31:38 +03:30
|
|
|
|
|
|
|
|
// محصول متصل به کیف اصلی (main_wallet_products): موجودی و تراکنش روی کیف اصلی است
|
|
|
|
|
$effectiveProductId = self::effectiveProductWalletId((int) $data['wallets'][$key]['wallet']['product_id']);
|
|
|
|
|
$availableGold = $data['wallets'][$key]['wallet']['gold'];
|
|
|
|
|
if ($effectiveProductId === null) {
|
|
|
|
|
$mainWallet = Wallet::where('owner', auth('customer')->id())
|
|
|
|
|
->whereNull('fund_id')
|
|
|
|
|
->whereNull('product_id')
|
|
|
|
|
->first();
|
|
|
|
|
$availableGold = (float) ($mainWallet->gold ?? 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($data['wallets'][$key]['value'] > $availableGold){
|
2026-01-24 14:03:11 +00:00
|
|
|
return response()->json([
|
|
|
|
|
'message' => "شما به اندازه کافی از {$data['wallets'][$key]['wallet']['product']['title']} ندارید"
|
|
|
|
|
], 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($data['wallets'][$key]['value'] == 0) {
|
|
|
|
|
return response()->json([
|
|
|
|
|
'message' => "مقدار انتخابی نمیتواند صفر باشد. لطفاً مقدار صحیحی را برای {$data['wallets'][$key]['wallet']['product']['title']} وارد کنید."
|
|
|
|
|
], 422);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2026-06-10 17:31:38 +03:30
|
|
|
$data['wallets'][$key]['wallet_interface'] = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type, null, $effectiveProductId);
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|
|
|
|
|
$data['withdraw'] = new Withdraw(); // Create a new instance of the Withdraw model
|
|
|
|
|
$data['withdraw']->note = "برداشت فیزیکی"; // 50 for phisicals
|
|
|
|
|
$data['withdraw']->address_id = $data['address_id'];
|
|
|
|
|
$data['withdraw']->customer_id = $data['customer']['id'];
|
|
|
|
|
$data['withdraw']->sts = 50;
|
|
|
|
|
$data['withdraw']->save();
|
|
|
|
|
foreach ($data['wallets'] as $key => $wallet) {
|
|
|
|
|
$data['wallets'][$key]['transaction'][] = new TransactionInterface($data['wallets'][$key]['wallet_interface'], $_inp = [
|
|
|
|
|
"gold" => $data['wallets'][$key]['value'],
|
|
|
|
|
"number" => $data['wallets'][$key]['wallet']['product']['amount']*$data['wallets'][$key]['value'],
|
|
|
|
|
"product_id" => $data['wallets'][$key]['wallet']['product']['id'],
|
|
|
|
|
'expire_at' => now(),
|
|
|
|
|
'active' => true,
|
|
|
|
|
'title' => " برداشت {$data['wallets'][$key]['wallet']['product']['title']}",
|
|
|
|
|
'note' => " برداشت محصول {$data['wallets'][$key]['wallet']['product']['title']} شماره سند {$data['withdraw']->id}",
|
|
|
|
|
'withdraw_id' => $data['withdraw']->id,
|
|
|
|
|
], 6, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->json($data['withdraw']['id']);
|
|
|
|
|
}
|
|
|
|
|
public function indexWithdraw(Request $request){
|
|
|
|
|
$records = Withdraw::with(['customer','bank_account','Address',"Transaction"])->where("customer_id",auth('customer')->id());
|
|
|
|
|
TableEngine::create($records, $request->all())
|
|
|
|
|
->initSearch(['customer.name'])
|
|
|
|
|
->select([
|
|
|
|
|
'*',Withdraw::$SELECT_STS_STR,Withdraw::$SELECT_DAJ
|
|
|
|
|
])->export($records);
|
|
|
|
|
return response()->json($records);
|
|
|
|
|
}
|
|
|
|
|
public function index_withdraw_physical(Request $request){
|
2026-07-01 14:15:21 +03:30
|
|
|
$records = Withdraw::with(['customer','bank_account','Address',"Transaction.product.file"])->where("customer_id",auth('customer')->id())->whereBetween('sts', [49, 70]);
|
2026-01-24 14:03:11 +00:00
|
|
|
TableEngine::create($records, $request->all())
|
|
|
|
|
->initSearch(['customer.name'])
|
|
|
|
|
->select([
|
|
|
|
|
'*',Withdraw::$SELECT_STS_STR,Withdraw::$SELECT_DAJ
|
|
|
|
|
])->export($records);
|
|
|
|
|
return response()->json($records);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public function withdrawOtp(Request $request)
|
|
|
|
|
{
|
2026-06-05 16:45:40 +03:30
|
|
|
$data = $request->all();
|
|
|
|
|
if (!isset($data['value'])) {
|
|
|
|
|
return response()->json(['message' => 'مبلغ برداشت مشخص نشده است'], 422);
|
|
|
|
|
}
|
|
|
|
|
if ($data['value'] < 0) {
|
|
|
|
|
throw new UnprocessableEntityHttpException('مقدار برداشت باید مثبت باشد');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$this->validateWithdrawAmount((int) $data['value']);
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
$customer = auth('customer')->user();
|
|
|
|
|
$otp = rand(1000, 9999);
|
|
|
|
|
$customer->token_sms = $otp;
|
|
|
|
|
$customer->save();
|
|
|
|
|
$this->Send(
|
|
|
|
|
$customer->mobile,
|
|
|
|
|
'رمز عبور یکبار مصرف شما جهت برداشت پول از اپلیکیشن ' . $this->Config('name_fa') . ' می باشد: ' . $otp
|
|
|
|
|
);
|
|
|
|
|
return response()->json(true);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-05 16:45:40 +03:30
|
|
|
private function validateWithdrawAmount(int $value): void
|
|
|
|
|
{
|
|
|
|
|
$wallet_interface_fund = new WalletInterface(auth('customer')->id(), auth('customer')->user()->type);
|
|
|
|
|
$wallet = $wallet_interface_fund->wallet;
|
|
|
|
|
|
|
|
|
|
if ($value > $wallet->cash) {
|
|
|
|
|
throw new UnprocessableEntityHttpException('مقدار برداشت بیشتر از موجودی است');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$min_withdraw = Setting::where('is_active', 1)
|
|
|
|
|
->where('key', 'min_withdraw')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if ($min_withdraw && (int) $min_withdraw->value > $value) {
|
|
|
|
|
throw new UnprocessableEntityHttpException('حداقل مقدار برداشت ' . number_format((int) $min_withdraw->value) . ' ریال است');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 13:34:43 +03:30
|
|
|
private function applyAbshodeProductTransactionTitle($record): void
|
|
|
|
|
{
|
|
|
|
|
if (self::isCashTransactionTitle($record->title ?? null)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-29 22:15:48 +03:30
|
|
|
$product = $this->resolveTransactionProductForLedgerTitle($record);
|
2026-06-22 21:42:48 +03:30
|
|
|
if (!$product || trim((string) $product->title) === '') {
|
2026-06-09 13:34:43 +03:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
$productTitle = trim((string) $product->title);
|
2026-06-09 13:34:43 +03:30
|
|
|
$direction = (int) ($record->order->direction ?? 0);
|
|
|
|
|
if ($direction === 1 || ($direction === 0 && (float) ($record->gold ?? 0) > 0)) {
|
|
|
|
|
$record->title = self::productBuyTitle($productTitle);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($direction === 2 || ($direction === 0 && (float) ($record->gold ?? 0) < 0)) {
|
|
|
|
|
$record->title = self::productSellTitle($productTitle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function resolveAbshodeProductId($record): ?int
|
|
|
|
|
{
|
|
|
|
|
if (!empty($record->product_id)) {
|
|
|
|
|
return (int) $record->product_id;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
$noteProductId = $this->productIdFromTransactionNote($record->note ?? null);
|
|
|
|
|
if ($noteProductId) {
|
|
|
|
|
return $noteProductId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$wallet = $this->resolveTransactionWallet($record);
|
|
|
|
|
if ($wallet && !empty($wallet->product_id)) {
|
|
|
|
|
return (int) $wallet->product_id;
|
2026-06-09 13:34:43 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (empty($record->order_id)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$siblingProductId = Transaction::query()
|
|
|
|
|
->where('order_id', $record->order_id)
|
|
|
|
|
->whereNotNull('product_id')
|
|
|
|
|
->where('product_id', '>', 0)
|
|
|
|
|
->value('product_id');
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
if ($siblingProductId) {
|
|
|
|
|
return (int) $siblingProductId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$siblingNotes = Transaction::query()
|
|
|
|
|
->where('order_id', $record->order_id)
|
|
|
|
|
->whereNotNull('note')
|
|
|
|
|
->pluck('note');
|
|
|
|
|
|
|
|
|
|
foreach ($siblingNotes as $note) {
|
|
|
|
|
$productId = $this->productIdFromTransactionNote($note);
|
|
|
|
|
if ($productId) {
|
|
|
|
|
return $productId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function productIdFromTransactionNote(?string $note): ?int
|
|
|
|
|
{
|
|
|
|
|
$note = (string) ($note ?? '');
|
|
|
|
|
if ($note === '' || !preg_match('/(?:^|;)product_id:(\d+)/', $note, $matches)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (int) $matches[1];
|
2026-06-09 13:34:43 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-03 20:24:07 +03:30
|
|
|
private function applyMetalTypeTransactionTitle($record): void
|
|
|
|
|
{
|
2026-06-09 13:34:43 +03:30
|
|
|
if ($this->resolveAbshodeProductId($record)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:37:05 +03:30
|
|
|
if (!empty($record->product_id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-06-03 20:24:07 +03:30
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
$wallet = $this->resolveTransactionWallet($record);
|
2026-06-03 21:37:05 +03:30
|
|
|
if ($wallet && !empty($wallet->product)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self::isCashTransactionTitle($record->title)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!self::isGenericMetalTransactionTitle($record->title)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (preg_match('/^(موجودی|واریز)/u', (string) $record->title)) {
|
2026-06-24 01:17:34 +03:30
|
|
|
if ((int) ($record->type ?? 0) === 7) {
|
|
|
|
|
$record->title = self::mainMetalDepositTitle();
|
|
|
|
|
} else {
|
|
|
|
|
$record->title = self::meltedGoldDepositTitle();
|
|
|
|
|
}
|
2026-06-03 21:37:05 +03:30
|
|
|
} elseif (preg_match('/^برداشت /u', (string) $record->title)) {
|
2026-06-24 01:17:34 +03:30
|
|
|
if ((int) ($record->type ?? 0) === 10) {
|
|
|
|
|
$record->title = self::mainMetalWithdrawTitle();
|
|
|
|
|
} else {
|
|
|
|
|
$record->title = self::meltedGoldWithdrawTitle();
|
|
|
|
|
}
|
2026-06-03 21:37:05 +03:30
|
|
|
} elseif (preg_match('/^خرید /u', (string) $record->title)) {
|
|
|
|
|
$record->title = self::meltedGoldBuyTitle();
|
|
|
|
|
} elseif (preg_match('/^فروش /u', (string) $record->title)) {
|
|
|
|
|
$record->title = self::meltedGoldSellTitle();
|
2026-06-03 20:24:07 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:37:05 +03:30
|
|
|
private function applyProductTransactionTitle($record): void
|
|
|
|
|
{
|
|
|
|
|
if (self::isCashTransactionTitle($record->title)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-29 22:15:48 +03:30
|
|
|
$product = $this->resolveTransactionProductForLedgerTitle($record);
|
2026-06-22 21:42:48 +03:30
|
|
|
if (!$product || trim((string) $product->title) === '') {
|
2026-06-03 21:37:05 +03:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
$productTitle = trim((string) $product->title);
|
2026-06-03 21:37:05 +03:30
|
|
|
$expectedBuy = self::productBuyTitle($productTitle);
|
|
|
|
|
$expectedSell = self::productSellTitle($productTitle);
|
|
|
|
|
|
2026-06-09 13:34:43 +03:30
|
|
|
if (in_array($record->title, [$expectedBuy, $expectedSell], true)) {
|
2026-06-03 21:37:05 +03:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 13:34:43 +03:30
|
|
|
$direction = (int) ($record->order->direction ?? 0);
|
|
|
|
|
if ($direction === 1 || ($direction === 0 && (float) ($record->gold ?? 0) > 0)) {
|
2026-06-03 21:37:05 +03:30
|
|
|
$record->title = $expectedBuy;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-09 13:34:43 +03:30
|
|
|
if ($direction === 2 || ($direction === 0 && (float) ($record->gold ?? 0) < 0)) {
|
2026-06-03 21:37:05 +03:30
|
|
|
$record->title = $expectedSell;
|
2026-06-22 21:42:48 +03:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (self::isGenericMetalTransactionTitle($record->title)) {
|
|
|
|
|
$record->title = $expectedBuy;
|
2026-06-03 21:37:05 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function resolveMainWalletTransactionTitle($record): string
|
|
|
|
|
{
|
2026-06-22 21:42:48 +03:30
|
|
|
$product = $this->resolveTransactionProduct($record);
|
|
|
|
|
if ($product && trim((string) $product->title) !== '') {
|
|
|
|
|
$productTitle = trim((string) $product->title);
|
|
|
|
|
if ((int) $record->type === 1) {
|
|
|
|
|
return self::productBuyTitle($productTitle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((int) $record->type === 2) {
|
|
|
|
|
return self::productSellTitle($productTitle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:37:05 +03:30
|
|
|
if ((int) $record->type === 1) {
|
2026-06-24 01:17:34 +03:30
|
|
|
return self::mainMetalBuyTitle();
|
2026-06-03 21:37:05 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((int) $record->type === 2) {
|
2026-06-24 01:17:34 +03:30
|
|
|
return self::mainMetalSellTitle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((int) $record->type === 7) {
|
|
|
|
|
return self::mainMetalDepositTitle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((int) $record->type === 10) {
|
|
|
|
|
return self::mainMetalWithdrawTitle();
|
2026-06-03 21:37:05 +03:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $record->type_str;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function applyProductOrderDisplayFields($record): void
|
|
|
|
|
{
|
|
|
|
|
if (empty($record->order_id) || empty($record->order) || empty($record->product_id)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$order = $record->order;
|
|
|
|
|
$totalPrice = (int) ($order->total_price ?? $order->payment_amount ?? 0);
|
|
|
|
|
if ($totalPrice <= 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((int) ($record->cash ?? 0) === 0) {
|
|
|
|
|
$direction = (int) ($order->direction ?? 0);
|
|
|
|
|
$record->cash = $direction === 1 ? -$totalPrice : $totalPrice;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($this->isProductWalletTransaction($record)) {
|
|
|
|
|
$mainBalanceCash = $this->resolveMainWalletBalanceCashForOrder($record);
|
|
|
|
|
if ($mainBalanceCash !== null) {
|
|
|
|
|
$record->balance_cash = $mainBalanceCash;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!empty($record->wallet?->product)) {
|
2026-06-22 21:29:25 +03:30
|
|
|
$this->applyProductTransactionUnit($record);
|
2026-06-03 21:37:05 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
private function resolveTransactionWallet($record): ?Wallet
|
|
|
|
|
{
|
|
|
|
|
if ($record instanceof Transaction) {
|
|
|
|
|
$relation = $record->getRelationValue('wallet');
|
|
|
|
|
if ($relation instanceof Wallet) {
|
|
|
|
|
return $relation;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$walletId = (int) ($record->getAttributes()['wallet'] ?? 0);
|
|
|
|
|
if ($walletId <= 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Wallet::with('product')->find($walletId);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-29 22:15:48 +03:30
|
|
|
/**
|
|
|
|
|
* عنوان هر سطر دفترکل — محصول همان کیف پول (مثلاً کاتد در سطر برداشت کاتد برای خرید مفتول).
|
|
|
|
|
*/
|
|
|
|
|
private function resolveTransactionProductForLedgerTitle($record): ?Product
|
|
|
|
|
{
|
|
|
|
|
$wallet = $this->resolveTransactionWallet($record);
|
|
|
|
|
if ($wallet?->product instanceof Product && trim((string) $wallet->product->title) !== '') {
|
|
|
|
|
return $wallet->product;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $this->resolveTransactionProduct($record);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:29:25 +03:30
|
|
|
private function resolveTransactionProduct($record): ?Product
|
|
|
|
|
{
|
2026-06-22 21:42:48 +03:30
|
|
|
$productId = $this->resolveAbshodeProductId($record) ?? 0;
|
|
|
|
|
$wallet = $this->resolveTransactionWallet($record);
|
2026-06-22 21:29:25 +03:30
|
|
|
|
|
|
|
|
if ($productId <= 0) {
|
2026-06-22 21:42:48 +03:30
|
|
|
$partial = $wallet?->product ?? $record->product ?? null;
|
|
|
|
|
|
2026-06-22 21:29:25 +03:30
|
|
|
return $partial instanceof Product ? $partial : null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:42:48 +03:30
|
|
|
$partial = $record->product ?? $wallet?->product ?? null;
|
|
|
|
|
if ($partial instanceof Product && (int) $partial->id === $productId && trim((string) $partial->title) !== '') {
|
|
|
|
|
return $partial;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:29:25 +03:30
|
|
|
return Product::query()
|
|
|
|
|
->select([
|
|
|
|
|
'id',
|
|
|
|
|
'title',
|
|
|
|
|
'group_type',
|
|
|
|
|
'unit',
|
|
|
|
|
'direct_price',
|
|
|
|
|
'amount',
|
|
|
|
|
'trade_by_quantity',
|
|
|
|
|
'metal_id',
|
|
|
|
|
])
|
|
|
|
|
->find($productId) ?? ($partial instanceof Product ? $partial : null);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:52:37 +03:30
|
|
|
private function applyProductTransactionUnit($record): void
|
|
|
|
|
{
|
2026-06-22 21:29:25 +03:30
|
|
|
$product = $this->resolveTransactionProduct($record);
|
|
|
|
|
|
2026-06-03 21:52:37 +03:30
|
|
|
if (!$product) {
|
2026-06-22 21:29:25 +03:30
|
|
|
if (!empty($record->gold)) {
|
2026-06-09 11:17:07 +03:30
|
|
|
$record->unit = self::tradeWeightUnit();
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:52:37 +03:30
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-22 21:29:25 +03:30
|
|
|
$payload = self::productUnitPayload($product);
|
|
|
|
|
$onMainWallet = empty($record->wallet?->product_id);
|
|
|
|
|
[$displayGold, $unit] = self::resolveTransactionDisplayGoldAndUnit(
|
|
|
|
|
$payload,
|
|
|
|
|
(float) ($record->gold ?? 0),
|
|
|
|
|
$onMainWallet
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$record->gold = $displayGold;
|
|
|
|
|
$record->unit = $unit;
|
2026-06-03 21:52:37 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:37:05 +03:30
|
|
|
private function isProductWalletTransaction($record): bool
|
|
|
|
|
{
|
2026-06-22 21:42:48 +03:30
|
|
|
$wallet = $this->resolveTransactionWallet($record);
|
|
|
|
|
|
|
|
|
|
return !empty($wallet?->product);
|
2026-06-03 21:37:05 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-04 18:53:06 +03:30
|
|
|
/**
|
2026-06-04 19:22:01 +03:30
|
|
|
* عنوان/وضعیت تراکنشهای API فروشگاه — فقط نمایش؛ واریز/برداشت عادی دست نخورده.
|
2026-06-04 18:53:06 +03:30
|
|
|
*/
|
2026-06-04 19:22:01 +03:30
|
|
|
private function applyShopTransactionPresentation($record): void
|
2026-06-04 18:53:06 +03:30
|
|
|
{
|
2026-06-04 19:09:58 +03:30
|
|
|
if ($this->isShopGoldTransaction($record)) {
|
2026-06-04 19:22:01 +03:30
|
|
|
if (empty($record->title) || $record->title === 'تعریف نشده') {
|
|
|
|
|
$record->title = 'برداشت طلایی';
|
|
|
|
|
}
|
2026-06-04 19:09:58 +03:30
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
/**
|
|
|
|
|
* خروجی JSON — withdraw/order را صریحاً میگذارد (setRelation گاهی در JSON نمیآید).
|
|
|
|
|
*/
|
|
|
|
|
private function serializeTransactionRowsForApi($rows): array
|
|
|
|
|
{
|
|
|
|
|
$out = [];
|
|
|
|
|
|
|
|
|
|
foreach ($rows as $record) {
|
|
|
|
|
$row = $record instanceof Transaction ? $record->toArray() : (array) $record;
|
|
|
|
|
|
2026-07-02 21:11:37 +03:30
|
|
|
$this->attachMissingOrderForApi($row);
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
if ($this->isShopRialTransaction($record)) {
|
|
|
|
|
$row['withdraw'] = ['sts_str' => 'برداشت ریالی توسط فروشگاه'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($this->isShopGoldTransaction($record)) {
|
|
|
|
|
$order = $row['order'] ?? [];
|
|
|
|
|
if (!is_array($order)) {
|
|
|
|
|
$order = $order instanceof Order ? $order->toArray() : [];
|
|
|
|
|
}
|
|
|
|
|
$order['sts_str'] = 'برداشت طلایی توسط فروشگاه';
|
|
|
|
|
if (empty($order['id']) && !empty($record->order_id)) {
|
|
|
|
|
$order['id'] = $record->order_id;
|
|
|
|
|
}
|
|
|
|
|
$row['order'] = $order;
|
|
|
|
|
$row['title'] = 'برداشت طلایی';
|
|
|
|
|
$row['type_str'] = 'برداشت طلایی';
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-02 20:59:26 +03:30
|
|
|
$this->normalizeApiRelationStsStr($row, 'order', Order::$STS);
|
|
|
|
|
$this->normalizeApiRelationStsStr($row, 'withdraw', Withdraw::$STS);
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
$out[] = $row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** برداشت ریال API فروشگاه — type=4، بدون withdraw_id، مبلغ منفی */
|
2026-06-04 19:16:37 +03:30
|
|
|
private function isShopRialTransaction($record): bool
|
|
|
|
|
{
|
2026-06-04 19:22:01 +03:30
|
|
|
if ((int) $record->type !== 4 || !empty($record->withdraw_id)) {
|
2026-06-04 19:16:37 +03:30
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
return (int) ($record->cash ?? 0) < 0;
|
2026-06-04 19:16:37 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
/** برداشت طلا API فروشگاه — type=10 + order_id، بدون withdraw */
|
2026-06-04 19:09:58 +03:30
|
|
|
private function isShopGoldTransaction($record): bool
|
|
|
|
|
{
|
2026-06-04 19:22:01 +03:30
|
|
|
if ((int) $record->type !== 10 || empty($record->order_id) || !empty($record->withdraw_id)) {
|
2026-06-04 19:09:58 +03:30
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
$order = $this->resolveOrderForTransaction($record);
|
|
|
|
|
if (!$order) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:09:58 +03:30
|
|
|
if ((int) ($order->direction ?? 0) !== 2) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$montify = trim((string) $this->Config('montify_name'));
|
|
|
|
|
if ($montify !== '' && str_contains((string) ($order->description ?? ''), $montify)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
return (int) ($order->sts ?? 0) === 2 && (int) ($order->payment_sts ?? 0) === 4;
|
2026-06-04 19:09:58 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
private function resolveOrderForTransaction($record): ?Order
|
2026-06-04 19:09:58 +03:30
|
|
|
{
|
2026-06-04 19:22:01 +03:30
|
|
|
if ($record->order instanceof Order) {
|
|
|
|
|
return $record->order;
|
2026-06-04 18:53:06 +03:30
|
|
|
}
|
2026-06-04 19:16:37 +03:30
|
|
|
|
2026-06-04 19:22:01 +03:30
|
|
|
if (empty($record->order_id)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Order::query()
|
|
|
|
|
->select(['*', Order::$SELECT_STS_STR])
|
|
|
|
|
->find($record->order_id);
|
2026-06-04 18:53:06 +03:30
|
|
|
}
|
|
|
|
|
|
2026-06-03 21:37:05 +03:30
|
|
|
private function resolveMainWalletBalanceCashForOrder($record): ?int
|
|
|
|
|
{
|
|
|
|
|
$customerId = $record->customer_id ?? auth('customer')->id();
|
|
|
|
|
$mainWalletIds = Wallet::where('owner', $customerId)
|
|
|
|
|
->whereNull('product_id')
|
|
|
|
|
->whereNull('fund_id')
|
|
|
|
|
->pluck('id');
|
|
|
|
|
|
|
|
|
|
if ($mainWalletIds->isEmpty()) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$mainTx = Transaction::where('order_id', $record->order_id)
|
|
|
|
|
->whereIn('wallet', $mainWalletIds)
|
|
|
|
|
->whereNotNull('balance_cash')
|
|
|
|
|
->orderBy('id')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
if ($mainTx) {
|
|
|
|
|
return (int) $mainTx->balance_cash;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$previousTx = Transaction::whereIn('wallet', $mainWalletIds)
|
|
|
|
|
->where('id', '<', $record->id)
|
|
|
|
|
->whereNotNull('balance_cash')
|
|
|
|
|
->orderByDesc('id')
|
|
|
|
|
->first();
|
|
|
|
|
|
|
|
|
|
return $previousTx ? (int) $previousTx->balance_cash : null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-02 20:59:26 +03:30
|
|
|
private function normalizeApiRelationStsStr(array &$row, string $key, array $stsMap): void
|
|
|
|
|
{
|
|
|
|
|
if (empty($row[$key]) || !is_array($row[$key])) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$relation = &$row[$key];
|
|
|
|
|
$sts = $relation['sts'] ?? null;
|
|
|
|
|
if ($sts === null && isset($relation['sts_str']) && is_numeric($relation['sts_str'])) {
|
|
|
|
|
$sts = (int) $relation['sts_str'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($sts !== null && isset($stsMap[$sts])) {
|
|
|
|
|
$relation['sts_str'] = $stsMap[$sts];
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$current = trim((string) ($relation['sts_str'] ?? ''));
|
|
|
|
|
if ($current !== '' && $current !== 'بدون مقدار') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($key === 'order' && (int) ($relation['payment_sts'] ?? 0) === 4) {
|
|
|
|
|
$relation['sts_str'] = 'موفق';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-02 21:11:37 +03:30
|
|
|
/**
|
|
|
|
|
* بعضی تراکنشهای قدیمی order_id دارند ولی ردیف orders حذف شده — برای UI وضعیت/قیمت را بازسازی میکنیم.
|
|
|
|
|
*/
|
|
|
|
|
private function attachMissingOrderForApi(array &$row): void
|
|
|
|
|
{
|
|
|
|
|
$orderId = (int) ($row['order_id'] ?? 0);
|
|
|
|
|
if ($orderId <= 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$order = $row['order'] ?? null;
|
|
|
|
|
if (is_array($order) && !empty($order['id'])) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$loaded = Order::query()->find($orderId);
|
|
|
|
|
if ($loaded) {
|
|
|
|
|
$row['order'] = $loaded->toArray();
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$type = (int) ($row['type'] ?? 0);
|
|
|
|
|
if (!in_array($type, [1, 2, 10], true)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$row['order'] = [
|
|
|
|
|
'id' => $orderId,
|
|
|
|
|
'sts' => 2,
|
|
|
|
|
'payment_sts' => 4,
|
|
|
|
|
'sts_str' => 'موفق',
|
|
|
|
|
'price' => $this->inferTradePriceFromTransactionRow($row),
|
|
|
|
|
'amount' => abs((float) ($row['gold'] ?? 0)),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function inferTradePriceFromTransactionRow(array $row): int
|
|
|
|
|
{
|
|
|
|
|
$cash = abs((int) ($row['cash'] ?? 0));
|
|
|
|
|
$gold = abs((float) ($row['gold'] ?? 0));
|
|
|
|
|
if ($gold > 0 && $cash > 0) {
|
|
|
|
|
return (int) round($cash / $gold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-24 14:03:11 +00:00
|
|
|
}
|