with = ['referral']; $this->select = ['*']; $search = ['name', 'mobile', 'moshtari_code', 'pargar_account_id', 'referral_id', 'n_code', 'id']; $this->query = Customer::with($this->with,'wallet')->where('is_archive',false)->where('type',1); // $this->query = Customer::where('is_archive',false)->get(); parent::__construct(Customer::class, $this->with, $this->select, 'customer', $this->query, $search); } /** * @param $id * @return \Illuminate\Http\JsonResponse */ public function listUser(){ $balanceService = app(WalletBalanceService::class); $catalogMetalIds = $balanceService->catalogMetalIds(); $users = Customer::with(['wallet'])->select('id', 'name', 'mobile', 'type')->get()->map(function ($user) use ($balanceService, $catalogMetalIds) { $wallets = collect($user->wallet ?? []); $ownerType = (int) $user->type; $user->wallet_cash = $balanceService->mainWalletCash($wallets, $ownerType); $metalMap = $balanceService->buildCustomerWalletMetalDisplayMap($wallets, $catalogMetalIds); $user->wallet_gold = array_sum($metalMap); return $user; }); return response()->json($users); } /** * مشتریان با SSE فعال در بازهٔ آنلاین بودن (last_seen_at تازه). */ public function onlineList() { $threshold = now()->subSeconds(CustomerPresenceService::ONLINE_THRESHOLD_SECONDS); $rows = Customer::query() ->with('referral') ->where('is_archive', false) ->whereNotNull('last_seen_at') ->where('last_seen_at', '>=', $threshold) ->orderByDesc('last_seen_at') ->get(); return response()->json([ 'count' => $rows->count(), 'online_within_seconds' => CustomerPresenceService::ONLINE_THRESHOLD_SECONDS, 'rows' => $rows, ]); } /** * حضور سبک برای چت پشتیبانی: chat_room_id / chat_user_id → last_seen_at * (همان منبع آنلاین بودن صفحه‌ی کاربران آنلاین). */ public function chatPresence() { $hasRoom = \Schema::hasColumn('customers', 'chat_room_id'); $hasUser = \Schema::hasColumn('customers', 'chat_user_id'); if (! $hasRoom && ! $hasUser) { return response()->json([ 'online_within_seconds' => CustomerPresenceService::ONLINE_THRESHOLD_SECONDS, 'rows' => [], ]); } $columns = ['id', 'last_seen_at']; if ($hasRoom) { $columns[] = 'chat_room_id'; } if ($hasUser) { $columns[] = 'chat_user_id'; } $query = Customer::query() ->where('is_archive', false) ->select($columns); $query->where(function ($q) use ($hasRoom, $hasUser) { if ($hasRoom) { $q->orWhere(function ($inner) { $inner->whereNotNull('chat_room_id')->where('chat_room_id', '!=', ''); }); } if ($hasUser) { $q->orWhere(function ($inner) { $inner->whereNotNull('chat_user_id')->where('chat_user_id', '!=', ''); }); } }); return response()->json([ 'online_within_seconds' => CustomerPresenceService::ONLINE_THRESHOLD_SECONDS, 'rows' => $query->get(), ]); } public function updateAdmin(Request $request , $id) { $id = (int) $id; $data = $request->only([ 'fname', 'lname', 'mobile', 'password', 'birth_date', 'phone_number', 'province', 'city', 'n_code', 'add_user_limit', 'referral_to_id', 'pargar_account_id', 'moshtari_code', 'profit_amount_by_lvl', 'gold', ]); if (array_key_exists('password', $data) && trim((string) ($data['password'] ?? '')) === '') { unset($data['password']); } if (isset($data['mobile']) && ! is_null($data['mobile']) && $data['mobile'] !== '') { $data['mobile'] = Customer::normalizeMobile($data['mobile']); Customer::assertMobileUnique($data['mobile'], $id); } if (array_key_exists('pargar_account_id', $data)) { $data['pargar_account_id'] = $this->normalizeAndAssertUniqueAccountingCode( $data['pargar_account_id'], 'pargar_account_id', $id ); } if (array_key_exists('moshtari_code', $data)) { $data['moshtari_code'] = $this->normalizeAndAssertUniqueAccountingCode( $data['moshtari_code'], 'moshtari_code', $id ); } $customer = Customer::findOrFail($id); if (array_key_exists('referral_to_id', $data)) { $incoming = is_string($data['referral_to_id']) ? trim($data['referral_to_id']) : $data['referral_to_id']; if (filled($customer->referral_to_id) || $incoming === null || $incoming === '') { unset($data['referral_to_id']); } } if (isset($data['fname'], $data['lname'])) { $data['name'] = trim($data['fname'].' '.$data['lname']); } $goldUpdated = false; if (array_key_exists('gold', $data) && $data['gold'] !== null && $data['gold'] !== '') { $customer->gold = $data['gold']; $customer->save(); $goldUpdated = true; } unset($data['gold']); $updated = $data === [] ? false : $customer->update($data); return response()->json([ 'g' => $goldUpdated, 'u' => $updated, ]); } /** * خالی / 0 / -1 → null؛ در غیر این صورت یکتا بودن کد بین مشتریان فعال. */ private function normalizeAndAssertUniqueAccountingCode(mixed $value, string $column, int $customerId): ?string { if ($value === null) { return null; } $code = trim((string) $value); if ($code === '' || in_array($code, ['0', '-1'], true)) { return null; } $exists = Customer::whereNull('deleted_at') ->where($column, $code) ->where('id', '!=', $customerId) ->exists(); if ($exists) { throw new UnprocessableEntityHttpException('این کد حسابداری قبلاً برای مشتری دیگری ثبت شده است'); } return $code; } public function referrals(Request $request,$customer_id) { $customer_referral_id = Customer::where("id",$customer_id)->first()->referral_id; $query_table = Customer::where("referral_to_id",$customer_referral_id); TableEngine::create($query_table, $request) ->initSearch($this->search) ->select(['*',Customer::$SELECT_TYPE_STR]) ->export($query_table); return response()->json($query_table); } public function funds(Request $request,$customer_id) { $mainWalletProductIds = app(\App\Services\ConfigService::class)->mainWalletProductIds(); $balanceService = app(WalletBalanceService::class); $catalogMetalIds = $balanceService->catalogMetalIds(); $mainWalletMetalId = $balanceService->resolveMainWalletMetalId(); $productCols = implode(',', self::WALLET_PRODUCT_COLUMNS); $ownerType = (int) (Customer::find($customer_id)?->type ?? 0); // فقط کیف محصول آزاد (نه کیف اصلی، نه قلک) — هر ردیف = یک دارایی $query_table = Wallet::with([ 'product' => static function ($q) use ($productCols) { $q->withTrashed()->select(explode(',', $productCols)); }, ]) ->where('owner', $customer_id) ->whereNull('fund_id') ->whereNotNull('product_id'); if ($mainWalletProductIds !== []) { // کیف‌های قدیمیِ محصولات متصل به کیف اصلی نمایش داده نمی‌شوند $query_table->whereNotIn('product_id', $mainWalletProductIds); } TableEngine::create($query_table, $request) ->initSearch($this->search) ->select(['id', 'owner', 'cash', 'gold', 'fund_id', 'product_id', 'created_at', 'updated_at']) ->export($query_table); // محصولات متصل به کیف اصلی: یک ردیف نماینده با موجودی کیف اصلی if ($mainWalletProductIds !== []) { $representative = null; foreach ($mainWalletProductIds as $pid) { $representative = \App\Models\Product::withTrashed() ->select(self::WALLET_PRODUCT_COLUMNS) ->find($pid); if ($representative) { break; } } $mainWallet = Wallet::where('owner', $customer_id) ->whereNull('fund_id') ->whereNull('product_id') ->first(); if ($representative && $mainWallet) { $freeGold = $balanceService->effectiveMainWalletGold((int) $customer_id, $ownerType); $fundGold = $balanceService->fundGoldForMainWalletProducts((int) $customer_id, $ownerType); $mainRow = [ 'id' => $mainWallet->id, 'owner' => (int) $customer_id, 'cash' => 0, 'gold' => $freeGold + $fundGold, 'free_gold' => $freeGold, 'fund_gold' => $fundGold, 'fund_id' => null, 'product_id' => (int) $representative->id, 'created_at' => $mainWallet->created_at, 'updated_at' => $mainWallet->updated_at, 'product' => $representative, 'is_main_wallet' => true, ]; $query_table['rows'] = collect($query_table['rows'] ?? [])->prepend($mainRow)->values(); $query_table['query']['total'] = (int) ($query_table['query']['total'] ?? 0) + 1; } } if (isset($query_table['rows'])) { foreach ($query_table['rows'] as &$row) { $balanceService->enrichWalletTableRow($row, $catalogMetalIds, $mainWalletMetalId); $productId = (int) (is_array($row) ? ($row['product_id'] ?? 0) : ($row->product_id ?? 0)); $hasProduct = is_array($row) ? ! empty($row['product']) : ! empty($row->product); if ($productId > 0 && ! $hasProduct) { $product = \App\Models\Product::withTrashed() ->select(self::WALLET_PRODUCT_COLUMNS) ->find($productId); if ($product) { if (is_array($row)) { $row['product'] = $product; } else { $row->setRelation('product', $product); } $balanceService->enrichWalletTableRow($row, $catalogMetalIds, $mainWalletMetalId); } } // جمع دارایی = آزاد + قلک‌های همان محصول (برای ردیف اصلی قبلاً ست شده) $isMain = is_array($row) ? ! empty($row['is_main_wallet']) : ! empty($row->is_main_wallet); if ($productId > 0 && ! $isMain) { $freeGold = $balanceService->freeProductGold((int) $customer_id, $productId, $ownerType); $fundGold = $balanceService->fundGoldForProduct((int) $customer_id, $productId, $ownerType); if (is_array($row)) { $row['free_gold'] = $freeGold; $row['fund_gold'] = $fundGold; $row['gold'] = $freeGold + $fundGold; } else { $row->free_gold = $freeGold; $row->fund_gold = $fundGold; $row->gold = $freeGold + $fundGold; } } } unset($row); } return response()->json($query_table); } /** جدول قلک‌های واقعی مشتری (مدل Fund) */ public function piggyFunds(Request $request, $customer_id) { $query_table = \App\Models\Fund::with(['wallet', 'product']) ->where('customer_id', $customer_id); TableEngine::create($query_table, $request) ->initSearch(['title']) ->select(['*']) ->export($query_table); if (isset($query_table['rows'])) { foreach ($query_table['rows'] as &$row) { $wallets = is_array($row) ? collect($row['wallet'] ?? []) : collect($row->wallet ?? []); $gold = (float) $wallets->sum('gold'); if (is_array($row)) { $row['gold'] = $gold; $row['product_title'] = $row['product']['title'] ?? ($row['product']->title ?? null); } else { $row->gold = $gold; $row->product_title = $row->product?->title; } } unset($row); } return response()->json($query_table); } /** قلک‌های یک دارایی (محصول) */ public function assetPiggyFunds(Request $request, $customer_id, $product_id) { $request->merge(['product_id' => (int) $product_id]); $query_table = \App\Models\Fund::with(['wallet', 'product']) ->where('customer_id', $customer_id) ->where('product_id', (int) $product_id); TableEngine::create($query_table, $request) ->initSearch(['title']) ->select(['*']) ->export($query_table); if (isset($query_table['rows'])) { foreach ($query_table['rows'] as &$row) { $wallets = is_array($row) ? collect($row['wallet'] ?? []) : collect($row->wallet ?? []); $gold = (float) $wallets->sum('gold'); if (is_array($row)) { $row['gold'] = $gold; } else { $row->gold = $gold; } } unset($row); } return response()->json($query_table); } /** تراکنش‌های مربوط به یک دارایی (کیف آزاد + قلک‌های همان محصول) */ public function assetTransactions(Request $request, $customer_id, $product_id) { $config = app(ConfigService::class); $productId = (int) $product_id; $walletIds = Wallet::query() ->where('owner', $customer_id) ->where(function ($q) use ($config, $productId) { if ($config->isMainWalletProduct($productId)) { $q->where(function ($inner) { $inner->whereNull('fund_id')->whereNull('product_id'); })->orWhere(function ($inner) use ($productId) { $inner->whereNotNull('fund_id') ->where(function ($f) use ($productId) { $f->where('product_id', $productId)->orWhereNull('product_id'); }); }); } else { $q->where('product_id', $productId); } }) ->pluck('id'); $records = \Rooyesh\Wallet\models\Transaction::whereIn('wallet', $walletIds); TableEngine::create($records, $request->all()) ->initSearch([]) ->select([ '*', \Rooyesh\Wallet\models\Transaction::$SELECT_CAJ, \Rooyesh\Wallet\models\Transaction::$SELECT_TYPE_STR, ])->export($records); return response()->json($records); } /** تراکنش‌های یک قلک */ public function piggyFundTransactions(Request $request, $customer_id, $fund_id) { $fund = \App\Models\Fund::where('customer_id', $customer_id)->findOrFail($fund_id); $walletIds = Wallet::query() ->where('owner', $customer_id) ->where('fund_id', $fund->id) ->pluck('id'); $records = \Rooyesh\Wallet\models\Transaction::whereIn('wallet', $walletIds); TableEngine::create($records, $request->all()) ->initSearch([]) ->select([ '*', \Rooyesh\Wallet\models\Transaction::$SELECT_CAJ, \Rooyesh\Wallet\models\Transaction::$SELECT_TYPE_STR, ])->export($records); return response()->json($records); } public function archive($id){ $query = Customer::findOrFail($id); $query->update([ 'is_archive'=>!$query->is_archive ]); return response()->json(true); } public function tableWallets(Request $request){ $productSelect = 'product:'.implode(',', self::WALLET_PRODUCT_COLUMNS); $query_table = Customer::with(['wallet.'.$productSelect, 'referral'])->where('is_archive', false); TableEngine::create($query_table, $request) ->initSearch($this->search) ->select(['*',Customer::$SELECT_TYPE_STR]) ->export($query_table); $balanceService = app(WalletBalanceService::class); $balanceReport = app(BalanceReportService::class); $catalogMetalIds = $balanceService->catalogMetalIds(); $query_table['catalogMetals'] = $balanceReport->catalogMetalsMeta(); if (isset($query_table['rows'])) { $query_table['rows'] = collect($query_table['rows'])->map(function ($row) use ($balanceService, $catalogMetalIds) { $wallets = collect($row['wallet'] ?? []); $row['wallet_cash'] = $balanceService->mainWalletCash($wallets, (int) ($row['type'] ?? 0)); $metalMap = $balanceService->buildCustomerWalletMetalDisplayMap($wallets, $catalogMetalIds); $row['wallet_metal_gold'] = $metalMap; foreach ($catalogMetalIds as $metalId) { $row['wallet_metal_gold_' . $metalId] = $metalMap[(string) $metalId] ?? 0.0; } $row['wallet_gold'] = array_sum($metalMap); return $row; })->values(); } return response()->json($query_table); } public function assetsSummary($customerId, AssetSummaryService $assetSummaryService) { $customer = Customer::findOrFail($customerId); return response()->json( $assetSummaryService->buildForCustomer((int) $customer->id, (int) $customer->type, true) ); } public function archiveTable(Request $request) { $query_table = Customer::with($this->with)->where("is_archive",true)/*->where('type',1)*/; TableEngine::create($query_table, $request) ->initSearch($this->search) ->select($this->select) ->export($query_table); return response()->json($query_table); } /** * @param $id * @return \Illuminate\Http\JsonResponse */ public function active($id){ $query = Customer::findOrFail($id); $active = !$query->active; $query->update([ 'active'=>!$query->active ]); if ($active){ $message ='اکانت شما توسط کارشناس سیستم تایید شد '."\n". 'با رفتن به لینک زیر وارد حساب کاربری خود شوید'."\n". self::publicSiteUrl(request())."\n". 'با تشکر'.$this->Config('name_fa'); } else{ $message ='اکانت شما توسط کارشناس سیستم غیر فعال شد '."\n". 'با کارشناسان ما تماس بگیرید'."\n". 'با تشکر'.$this->Config('name_fa'); } $this->Send($query->mobile,$message); return response()->json(true); } public function changeTradeActive($id){ $query = Customer::findOrFail($id); $query->update([ 'trade_active'=>!$query->trade_active ]); return response()->json(true); } public function changeConditionalOrderActive($id) { $query = Customer::findOrFail($id); $query->update([ 'conditional_order_active' => ! $query->conditional_order_active, ]); return response()->json([ 'conditional_order_active' => (bool) $query->fresh()->conditional_order_active, ]); } public function updateConditionalOrderLimit(Request $request, $id) { $query = Customer::findOrFail($id); $data = $request->validate([ 'conditional_order_limit' => 'nullable|integer|min:1', ]); $query->update([ 'conditional_order_limit' => $data['conditional_order_limit'] ?? null, ]); return response()->json([ 'conditional_order_limit' => $query->fresh()->conditional_order_limit, ]); } // single customer------------------- public function singleArchiveTable(Request $request) { $query_table = Customer::with($this->with)->where("is_archive",true)->where('type',2); TableEngine::create($query_table, $request) ->initSearch($this->search) ->select($this->select) ->export($query_table); return response()->json($query_table); } public function singleCustomerTable(Request $request) { $query_table = Customer::with($this->with)->where("is_archive",false)->where('type',2); TableEngine::create($query_table, $request) ->initSearch($this->search) ->select($this->select) ->export($query_table); return response()->json($query_table); } public function MakeValidByAdmin(Request $request,$id) { $customer = Customer::with('referral')->where('id', $id)->first(); if ($customer->is_legal == 2 or $customer->is_legal == 3){ return response()->json(true); } $customer->is_legal = 2; $customer->save(); // واریز پاداش معرف مستقل از has_page_earn_money (آن فقط منوی اپ است) $referrer = $customer->referrer(); if ($referrer) { $gift_permision = app(ReferralGiftService::class)->resolveReferrerGiftPermission($referrer); app(ReferralGiftService::class)->tryGiveOnRegistration( $customer, $referrer, $gift_permision, false ); } $companyName = $this->Config('name_fa'); $companyurl = self::publicSiteUrl(request()); $message ='حساب کاربری شما توسط کارشناس سیستم تایید شد '."\n". 'با رفتن به لینک زیر وارد حساب کاربری خود شوید'."\n". $companyurl."\n". 'با تشکر'.$this->Config('name_fa'); $this->Send($customer->mobile, $message); $referrer = $customer->referral; // if ($referrer) { // if (Customer::where('referral_to_id', $referrer->referral_id)->get()->count()<=$referrer->add_user_limit) { // $referrerWallet = Wallet::where('owner',$customer->referral->id)->first(); // $message = "سلام {$referrer->name} عزیز،\n\n{$gift_for_invite} گرم طلا بابت معرفی {$customer->name} به حساب شما واریز شد.\nموجودی فعلی: {$referrerWallet->gold} گرم.\n\nاطلاعات بیشتر: $companyurl\n\nبا احترام،\n$companyName"; // $this->Send($referrer->mobile, $message); // } // } return response()->json(true); } public function cardChecker(Request $request){ if (!app(ConfigService::class)->isJibitEnabled()) { throw new UnprocessableEntityHttpException('سرویس احراز هویت جیبیت غیرفعال است.'); } if (strlen($request->cart_number) != 16) { throw new UnprocessableEntityHttpException('شماره کارت اشتباه می‌باشد.'); } if (empty($request->national_code)) { throw new UnprocessableEntityHttpException('لطفا شماره ملی وارد کنید.'); } if(!empty($request->birth_date)){ $birth_date = Verta::instance(Carbon::parse($request->birth_date))->format('Ymd'); } $card_number = preg_replace('/\D/', '', $request->cart_number); $response = $this->jibit("cartMatching", $card_number, $request->national_code, $birth_date ?? null); if(isset($response['refresh'])){ unset($response['refresh']); } if(!empty($response)){ if(!empty($response['message'])){ throw new UnprocessableEntityHttpException($response['message']); }else{ return response()->json($response); } }else{ throw new UnprocessableEntityHttpException('ارتباط با سامانه قطع میباشد.'); } } }