all(); $customer = Customer::findOrFail($data['customer_id']); $deposit = false; if ($data['value'] > 0) { $deposit = true; } $wallet_interface = new WalletInterface($data['customer_id'], (int) $customer->type); $wallet = $wallet_interface->wallet; if (is_null($wallet) && !$deposit) { throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); } if (!is_null($wallet) && $wallet->cash < abs($data['value']) && !$deposit) { throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); } $orders = Order::where('customer_id', $data['customer_id'])->where('sts', 1)->get(); $gold_block = 0; if (count($orders) > 0) { $value = $data['value']; foreach ($orders as $order) { if ($value < $order->payment_remind) { $order->payment_amount += $value; $order->payment_remind -= $value; $order->save(); break; } else { $value -= $order->payment_remind; $order->payment_amount = $order->total_price; $order->payment_remind = 0; $order->sts = 2; $gold_block -= $order->amount; $order->save(); } } } $user = auth()->user(); $customer = Customer::findOrFail($data["customer_id"]); $Withdraw = Withdraw::create([ 'user_id' => $user->id, 'customer_id' => $customer->id, "cash" => $data['value'], 'sts' => 4, ]); new TransactionInterface($wallet_interface, $_inp = [ 'cash' => $data['value'], 'expire_at' => now(), 'withdraw_id' => $Withdraw->id, 'active' => true ], 3, false, true, $gold_block); $message = 'کیف پول شما به مبلغ ' . self::formatSmsRialAmount($data['value']) . " ریال در تاریخ " . verta()->format('Y/n/j H:i:s') . " شارژ شد " . "\n" . $this->Config('name_fa'); $this->Send($customer->mobile, $message); return response()->json(true); } public function charge(Request $request) { $data = $request->all(); $customer = Customer::findOrFail($data['customer_id']); $deposit = false; if ($data['value'] > 0) { $deposit = true; } $wallet_interface = new WalletInterface($data['customer_id'], (int) $customer->type); $wallet = $wallet_interface->wallet; if (is_null($wallet) && !$deposit) { throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); } if (!is_null($wallet) && $wallet->cash < abs($data['value']) && !$deposit) { throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); } $orders = Order::where('customer_id', $data['customer_id'])->where('sts', 1)->get(); $gold_block = 0; $user = auth()->user(); $customer = Customer::findOrFail($data["customer_id"]); $Withdraw = Withdraw::create([ 'user_id' => $user->id, 'customer_id' => $customer->id, "cash" => $data['value'], "note" => $data['note'], 'sts' => 4, ]); $type = $deposit ? 3 : 4; new TransactionInterface($wallet_interface, $_inp = [ 'gold' => 0, 'cash' => $data['value'], 'expire_at' => now(), "note" => $data['note'], 'withdraw_id' => $Withdraw->id, 'active' => true ], $type, true, $deposit, $gold_block); $this->recordAccountingTransaction($customer, $Withdraw, (int) $user->id, (float) $data['value'], 0, $data['note'] ?? null); if($this->Config('send_to_tahesab') == "true"){ try { $data['direction'] = $deposit ? 0 : 1; $data['withdraw_id'] = $Withdraw->id; app(TahesabController::class)->wallet($data); } catch (\Exception $e) { report($e); } } //send sms to users and customer $status = $data['value'] > 0 ?" شارژ شد ": " کسر شد "; if($this->Config('charge_notif') == "true"){ $message = 'کیف پول کاربر ' . $customer->name . ' به مقدار ' . self::formatSmsRialAmount($data['value']) . " ریال در تاریخ " . verta()->format('Y/n/j H:i:s') .$status . "بابت" . "\n" . $data['note'] . "\n" . $this->Config('name_fa'); User::where('send_sms', 1)->get()->each(function ($admin) use ($message, $data) { $this->send($admin->mobile, $message); }); } $message = 'کیف پول شما به اندازه ' . self::formatSmsRialAmount($data['value']) . " ریال در تاریخ " . verta()->format('Y/n/j H:i:s') .$status . "بابت" . "\n" . $data['note'] . "\n" . $this->Config('name_fa'); $this->Send($customer->mobile, $message); $this->broadcastCustomerWalletAssets($customer); return response()->json(true); } public function charge_gold(Request $request) { $data = $request->all(); $customer = Customer::findOrFail($data['customer_id']); $deposit = false; if ($data['value'] > 0) { $deposit = true; } $wallet_interface = new WalletInterface($data['customer_id'], (int) $customer->type); $wallet = $wallet_interface->wallet; // if (is_null($wallet) && !$deposit) { // throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); // } // if (!is_null($wallet) && $wallet->cash < abs($data['value']) && !$deposit) { // throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); // } $gold_block = 0; $user = auth()->user(); $customer = Customer::findOrFail($data["customer_id"]); $Withdraw = Withdraw::create([ 'user_id' => $user->id, 'customer_id' => $customer->id, "gold" => $data['value'], "note" => $data['note'], 'sts' => 4, ]); $representative = app(ConfigService::class)->mainWalletRepresentativeProduct(); new TransactionInterface($wallet_interface, $_inp = [ 'cash' => 0, 'gold' => $data['value'], 'expire_at' => now(), "note" => $data['note'], 'withdraw_id' => $Withdraw->id, 'product_id' => $representative?->id, 'active' => true ], 7, $deposit, false, $gold_block); $this->recordAccountingTransaction($customer, $Withdraw, (int) $user->id, 0, (float) $data['value'], $data['note'] ?? null); //send to tahesab if($this->Config('send_to_tahesab') == "true"){ try { $data['direction'] = $deposit ? 1 : 0; $data['withdraw_id'] = $Withdraw->id; app(TahesabController::class)->walletGold($data); } catch (\Exception $e) { report($e); } } //send sms to users and customer $status = $data['value'] > 0 ?" شارژ شد ": " کسر شد "; $weightUnit = self::tradeWeightUnit(); if($this->Config('charge_notif') == "true"){ $message = 'کیف پول کاربر ' . $customer->name . ' به مقدار ' . self::formatSmsGoldWeight($data['value']) . " {$weightUnit} در تاریخ " . verta()->format('Y/n/j H:i:s') .$status . "بابت" . "\n" . $data['note'] . "\n" . $this->Config('name_fa'); User::where('send_sms', 1)->get()->each(function ($admin) use ($message, $data) { $this->send($admin->mobile, $message); }); } $message = 'کیف پول شما به اندازه ' . self::formatSmsGoldWeight($data['value']) . " {$weightUnit} در تاریخ " . verta()->format('Y/n/j H:i:s') . $status . " بابت" . "\n" . $data['note'] . $this->Config('name_fa'); $this->Send($customer->mobile, $message); $this->broadcastCustomerWalletAssets($customer); return response()->json(true); } /** گزینه‌های شارژ کیف پول — نقدی + محصولات فعال */ public function chargeTargets() { $configService = app(ConfigService::class); $mainWalletProductIds = $configService->mainWalletProductIds(); $balanceReport = app(BalanceReportService::class); $targets = [ [ 'key' => 'money', 'kind' => 'money', 'label' => 'نقدی (ریال)', 'unit' => 'ریال', ], ]; $products = Product::query() ->where('active', 1) ->orderBy('metal_id') ->orderBy('id') ->get(['id', 'title', 'unit', 'metal_id', 'group_type', 'trade_by_quantity', 'amount', 'direct_price']); foreach ($products as $product) { $usesMainWallet = in_array((int) $product->id, $mainWalletProductIds, true); $label = (string) $product->title; if ($usesMainWallet) { $label .= ' (کیف اصلی)'; } $targets[] = [ 'key' => 'product:' . $product->id, 'kind' => 'metal', 'product_id' => (int) $product->id, 'label' => $label, 'unit' => $this->resolveProductChargeUnit($product), 'metal_id' => max(1, (int) ($product->metal_id ?? 1)), 'uses_main_wallet' => $usesMainWallet, 'trade_by_quantity' => (bool) $product->trade_by_quantity, 'group_type' => (string) ($product->group_type ?? ''), ]; } return response()->json([ 'targets' => $targets, 'catalogMetals' => $balanceReport->catalogMetalsMeta(), ]); } /** * شارژ یکپارچه کیف پول — نقدی یا محصول (با رعایت کیف اصلی برای آبشده). */ public function charge_wallet(Request $request) { $productId = (int) $request->input('product_id', 0); if ($productId > 0) { return $this->chargeProductMetal($request, $productId); } return $this->charge($request); } private function chargeProductMetal(Request $request, int $productId) { $data = $request->all(); $product = Product::findOrFail($productId); $customer = Customer::findOrFail($data['customer_id']); $deposit = ((float) ($data['value'] ?? 0)) > 0; $effectiveProductId = Controller::effectiveProductWalletId($productId); $wallet_interface = new WalletInterface( (int) $data['customer_id'], (int) $customer->type, null, $effectiveProductId ); $wallet = $wallet_interface->wallet; if (!$deposit && $wallet !== null && (float) $wallet->gold < abs((float) $data['value'])) { throw new UnprocessableEntityHttpException('مقدار موجودی کمتر از درخواست شماست'); } $goldBlock = 0; $user = auth()->user(); $unitLabel = $this->resolveProductChargeUnit($product); $Withdraw = Withdraw::create([ 'user_id' => $user->id, 'customer_id' => $customer->id, 'gold' => $data['value'], 'note' => $data['note'] ?? null, 'sts' => 4, ]); new TransactionInterface($wallet_interface, [ 'cash' => 0, 'gold' => $data['value'], 'product_id' => $productId, 'expire_at' => now(), 'note' => $data['note'] ?? null, 'withdraw_id' => $Withdraw->id, 'active' => true, ], 7, $deposit, false, $goldBlock); $this->recordAccountingTransaction( $customer, $Withdraw, (int) $user->id, 0, (float) $data['value'], ($data['note'] ?? null) ?: ('شارژ ' . $product->title) ); if ($this->Config('send_to_tahesab') == 'true') { try { $data['direction'] = $deposit ? 1 : 0; $data['withdraw_id'] = $Withdraw->id; app(TahesabController::class)->walletGold($data); } catch (\Exception $e) { report($e); } } $status = $deposit ? ' شارژ شد ' : ' کسر شد '; $formatted = self::formatSmsGoldWeight($data['value']); if ($this->Config('charge_notif') == 'true') { $message = 'کیف پول کاربر ' . $customer->name . ' به مقدار ' . $formatted . ' ' . $unitLabel . ' در تاریخ ' . verta()->format('Y/n/j H:i:s') . $status . 'بابت' . "\n" . ($data['note'] ?? '') . "\n" . $this->Config('name_fa'); User::where('send_sms', 1)->get()->each(function ($admin) use ($message) { $this->send($admin->mobile, $message); }); } $message = 'کیف پول شما به اندازه ' . $formatted . ' ' . $unitLabel . ' در تاریخ ' . verta()->format('Y/n/j H:i:s') . $status . ' بابت' . "\n" . ($data['note'] ?? '') . "\n" . $this->Config('name_fa'); $this->Send($customer->mobile, $message); $this->broadcastCustomerWalletAssets($customer); return response()->json(true); } private function resolveProductChargeUnit(Product $product): string { $payload = Controller::productUnitPayload($product); if ( Controller::usesQuantityTrade($payload) && !Controller::isWeightBasedProductUnit($payload['unit'] ?? null) ) { $unit = trim((string) ($payload['unit'] ?? '')); return $unit !== '' ? $unit : 'عدد'; } if (Controller::isWeightBasedProductUnit($payload['unit'] ?? null)) { return trim((string) $payload['unit']); } return Controller::productStorageWeightUnit($product); } public function index(Request $request) { $balanceReport = app(BalanceReportService::class); $balanceService = app(WalletBalanceService::class); $catalogMetalIds = $balanceService->catalogMetalIds(); $records = Transaction::with([ 'customer', 'wallet.product:id,title,metal_id', 'product:id,title,metal_id,group_type,unit,amount,trade_by_quantity,direct_price', ])->whereNull('fund_id'); TableEngine::create($records, $request->all()) ->initSearch(['order_id', 'customer.name']) ->select([ '*', Transaction::$SELECT_CAJ, Transaction::$SELECT_TYPE_STR, Transaction::$SELECT_SAJ, Transaction::$SELECT_EAJ ])->export($records); if (isset($records['rows'])) { $records['rows'] = collect($records['rows'])->map(function ($row) use ($balanceReport, $catalogMetalIds) { $balanceReport->enrichTransactionTableRow($row, $catalogMetalIds); if ($row instanceof Transaction) { $array = $row->toArray(); $rawTypeStr = $row->getAttributes()['type_str'] ?? null; if (is_string($rawTypeStr) && !is_numeric(trim($rawTypeStr))) { $array['type_str'] = $rawTypeStr; } return $array; } return is_array($row) ? $row : (array) $row; })->values()->all(); } $records['catalogMetals'] = $balanceReport->catalogMetalsMeta(); return response()->json($records); } public function updateTransaction(Request $request) { $message = 'تراکنش تغیری نکرده.'; $transaction = Transaction::find($request->id); $product = Product::find($transaction->product_id); if (!$transaction) return response()->json(['error' => 'تراکنش یافت نشد.'], 404); $difference = $request->input('number') - $transaction->number; if (abs($difference) < 0.0001) { return response()->json(['message' => $message], 422); } $transaction->number = $request->input('number'); $wallet_interface = new WalletInterface($transaction->customer_id, 2); if ($wallet_interface->wallet->gold < $difference ) { if (!(isset($request->force) && $request->force === true)){ return response()->json(['message' => "کاربر مورد نظر موجودی ندارد. موجودی فعلی: " . $wallet_interface->wallet->gold . " مقدار مورد نیاز: " . $difference,'type'=>406],422); } } $isDiposit = $difference > 0 ? 0 : 1; new TransactionInterface($wallet_interface, [ "title" => "اصلاح سند " . $transaction->withdraw_id, "note" => $product->title, "product_id" => $product->id, "number" => 0, "gold" => $difference, 'expire_at' => now(), 'active' => true, 'withdraw_id' => $transaction->withdraw_id, 'start_at' => now(), ], 51, $isDiposit, false); $transaction->save(); $message = 'تراکنش با موفقیت به‌روزرسانی شد.'; $customer = Customer::find($transaction->customer_id); if ($customer) { $this->broadcastCustomerWalletAssets($customer); } return response()->json([ 'message' => $message, 'transaction' => $transaction, // 'previous_amount' => $previousAmount, // 'new_amount' => $newAmount, 'difference' => $difference, 'wallet' => $wallet_interface->wallet, ]); } public function indexWallet(Request $request) { $lastPrice = (float) (Price::latest()->first()?->price ?? 0); $balanceService = app(WalletBalanceService::class); $catalogMetalIds = $balanceService->catalogMetalIds(); $mainWalletMetalId = $balanceService->resolveMainWalletMetalId(); // همان فیلتر/جستجوی جدول — بدون صفحه‌بندی — برای مجموع ردیف‌های قابل مشاهده $sumQuery = Wallet::query(); TableEngine::create($sumQuery, $request->all()) ->initSearch(['customer.name']); $sumRows = (clone $sumQuery)->with('product:id,metal_id,unit,amount,group_type,trade_by_quantity,direct_price,title')->get(); $metalBalances = $balanceService->buildWalletMetalSums($sumRows, $catalogMetalIds); $totalGold = array_sum(array_column($metalBalances, 'gold')); $totalGoldToCash = array_sum(array_column($metalBalances, 'goldToCash')); $totalCash = (float) (clone $sumQuery)->sum('cash'); $sums = [ 'gold' => round($totalGold, 2), 'goldToCash' => round($totalGoldToCash, 2), 'cash' => round($totalCash, 2), 'cashToGold' => $lastPrice > 0 ? round($totalCash / $lastPrice, 2) : 0, 'metalBalances' => $metalBalances, ]; $records = Wallet::with(['customer', 'product:id,metal_id,unit,amount,group_type,trade_by_quantity,direct_price,title']); TableEngine::create($records, $request->all()) ->initSearch(['customer.name']) ->select(['*']) ->export($records); if (isset($records['rows'])) { foreach ($records['rows'] as &$row) { $balanceService->enrichWalletTableRow($row, $catalogMetalIds, $mainWalletMetalId); } unset($row); } $records['sums'] = $sums; return response()->json($records); } public function indexWithdraw(Request $request) { $records = Withdraw::with(['customer', 'bank_account', 'Address', "Transaction"]); Withdraw::applyFlowFilter($records, $request->input('flow_type', 'all')); TableEngine::create($records, $request->all()) ->initSearch(['customer.name']) ->select([ '*', Withdraw::$SELECT_STS_STR, Withdraw::$SELECT_DAJ ])->export($records); if ($this->Config('send_to_tahesab') === 'true' && isset($records['rows']) && is_array($records['rows'])) { $withdrawIds = collect($records['rows'])->pluck('id')->filter()->values()->all(); if ($withdrawIds !== []) { $failedByWithdraw = \App\Models\Tahesab::query() ->whereIn('withdraw_id', $withdrawIds) ->where(function ($query) { $query->whereNull('tahesab_id'); if (\Illuminate\Support\Facades\Schema::hasColumn('tahesab', 'sync_status')) { $query->orWhere('sync_status', \App\Services\TahesabSyncService::STATUS_FAILED); } }) ->orderByDesc('id') ->get() ->unique('withdraw_id') ->keyBy('withdraw_id'); foreach ($records['rows'] as &$row) { $tahesab = $failedByWithdraw->get($row['id'] ?? null); if ($tahesab) { $row['tahesab_failed_id'] = $tahesab->id; } } unset($row); } } return response()->json($records); } public function exportWithdrawReport(Request $request) { $flowType = $request->input('flow_type', 'all'); $query = Withdraw::with(['customer', 'bank_account']); Withdraw::applyFlowFilter($query, $flowType); TableEngine::create($query, $request->all()) ->initSearch(['customer.name']); $rows = $query->orderBy('id', 'desc')->get(); $titles = [ 'all' => 'گزارش برداشت و واریز', 'withdraw' => 'گزارش برداشت', 'deposit' => 'گزارش واریز', ]; return response()->view('admin.withdrawReport', [ 'rows' => $rows, 'title' => $titles[$flowType] ?? $titles['all'], 'flowType' => $flowType, 'generatedAt' => Verta::now()->format('Y/n/j H:i:s'), ])->header('Content-Type', 'text/html; charset=UTF-8'); } public function updateWithdraw(Request $request, $id) { // پیدا کردن رکورد Withdraw با استفاده از شناسه $withdraw = Withdraw::find($id); // بررسی وجود رکورد if (!$withdraw) { return response()->json(['error' => 'Withdraw not found'], 404); } $withdraw->sts = $request->sts; $withdraw->save(); return response()->json(['message' => 'Withdraw updated successfully', 'withdraw' => $withdraw]); } public function getWithdraw(Request $request, $id) { $withdraw = Withdraw::with(['customer', 'bank_account', 'Address', "Transaction"])->find($id); if (!$withdraw) { return response()->json(['error' => 'Withdraw not found'], 404); } return response()->json($withdraw); } public function completeWithdraw(Request $request) { $request->validate([ 'id' => 'required|integer|exists:withdraws,id', 'tracked_number' => 'required|string|max:50', 'date_at' => 'nullable|date', ]); $data = $request->only(['tracked_number', 'date_at']); $data['user_id'] = auth('user')->id(); $data['sts'] = 2; $data['tracked_number'] = (string) $data['tracked_number']; if (empty($data['date_at'])) { $data['date_at'] = now(); } $withdraw = Withdraw::findOrFail($request->id); $withdraw->update($data); // $tahesab = new TahesabController(); // $tahesab->wallet([ // 'customer_id' => $withdraw->customer_id, // 'value' => $withdraw->cash, // 'direction' => 1, // 'note' => 'برداشت از کیف پول شماره ' . $withdraw->id, // ]); return response()->json(true); } private function recordAccountingTransaction( Customer $customer, Withdraw $withdraw, int $adminUserId, float $cash = 0, float $gold = 0, ?string $note = null ): void { $transaction = Transaction::where('withdraw_id', $withdraw->id)->latest('id')->first(); TransactionsAdmin::create([ 'customer_id' => $customer->id, 'customer_to_id' => $customer->id, 'user_id' => $adminUserId, 'wallet' => null, 'transaction_id' => $transaction?->id, 'cash' => $cash, 'note' => $note ?? $withdraw->note, 'type' => 1, 'gold' => $gold, 'balance_cash' => 0, 'withdraw_id' => $withdraw->id, ]); } private function broadcastCustomerWalletAssets(Customer $customer): void { app(WalletBroadcastService::class)->broadcastForCustomer( (int) $customer->id, (int) $customer->type ); } }