with = ['wallet', 'product']; $select = ['*']; $search = ['title']; $request->request->add(['customer_id' => auth('customer')->id()]); $query = Fund::with($this->with)->where('customer_id', auth('customer')->id()); parent::__construct($model, $this->with, $select, null, $query, $search); } public function store(Request $request) { $this->assertProductFundsEnabled(); $data = $request->all(); $productId = (int) ($data['product_id'] ?? 0); if ($productId <= 0) { throw new UnprocessableEntityHttpException('انتخاب محصول برای قلک الزامی است'); } $product = Product::query()->find($productId); if (! $product) { throw new UnprocessableEntityHttpException('محصول یافت نشد'); } $title = trim((string) ($data['title'] ?? '')); if ($title === '') { throw new UnprocessableEntityHttpException('عنوان قلک الزامی است'); } $customerId = (int) auth('customer')->id(); $fund = Fund::create([ 'title' => $title, 'file_id' => $data['file_id'] ?? null, 'customer_id' => $customerId, 'product_id' => $productId, ]); // کیف قلک را از همان ابتدا با product_id بساز (بدون سند حسابداری) new WalletInterface( $customerId, auth('customer')->user()->type, $fund->id, $productId ); return response()->json($fund->load(['wallet', 'product'])); } public function update(Request $request, $id) { $this->assertProductFundsEnabled(); $fund = $this->ownedFund((int) $id); $title = trim((string) ($request->input('title') ?? $fund->title)); if ($title === '') { throw new UnprocessableEntityHttpException('عنوان قلک الزامی است'); } // product_id بعد از ساخت عوض نمی‌شود $fund->update([ 'title' => $title, 'file_id' => $request->input('file_id', $fund->file_id), ]); return response()->json($fund->fresh(['wallet', 'product'])); } public function deposit(Request $request) { $this->assertProductFundsEnabled(); $data = $request->all(); $fund = $this->ownedFund((int) ($data['fund_id'] ?? 0)); $value = (float) ($data['value'] ?? 0); if ($value <= 0) { throw new UnprocessableEntityHttpException('مقدار نامعتبر است'); } $customer = auth('customer')->user(); $customerId = (int) $customer->id; $ownerType = (int) $customer->type; $productId = (int) ($fund->product_id ?? 0); $balanceService = app(WalletBalanceService::class); $free = $productId > 0 ? $balanceService->freeProductGold($customerId, $productId, $ownerType) : $balanceService->effectiveMainWalletGold($customerId, $ownerType); if ($free < $value) { throw new UnprocessableEntityHttpException('موجودی کمتر از مقدار وارد شده است'); } [$sourceFundId, $sourceProductId] = $this->sourceWalletKeys($productId); $source = new WalletInterface($customerId, $ownerType, $sourceFundId, $sourceProductId); $dest = new WalletInterface($customerId, $ownerType, $fund->id, $productId > 0 ? $productId : null); // فقط جابه‌جایی داخلی — بدون Accounting* new TransactionInterface($source, [ 'gold' => $value, 'fund_id' => $fund->id, 'product_id' => $productId > 0 ? $productId : null, 'expire_at' => now(), 'active' => true, ], 6, false); new TransactionInterface($dest, [ 'gold' => $value, 'fund_id' => $fund->id, 'product_id' => $productId > 0 ? $productId : null, 'expire_at' => now(), 'active' => true, ], 5, true); return response()->json(true); } public function withdraw(Request $request) { $this->assertProductFundsEnabled(); $data = $request->all(); $fund = $this->ownedFund((int) ($data['fund_id'] ?? 0)); $value = (float) ($data['value'] ?? 0); if ($value <= 0) { throw new UnprocessableEntityHttpException('مقدار نامعتبر است'); } $customer = auth('customer')->user(); $customerId = (int) $customer->id; $ownerType = (int) $customer->type; $productId = (int) ($fund->product_id ?? 0); $source = new WalletInterface($customerId, $ownerType, $fund->id, $productId > 0 ? $productId : null); if ((float) $source->wallet->gold < $value) { throw new UnprocessableEntityHttpException('موجودی کمتر از مقدار وارد شده است'); } [$destFundId, $destProductId] = $this->sourceWalletKeys($productId); $dest = new WalletInterface($customerId, $ownerType, $destFundId, $destProductId); new TransactionInterface($source, [ 'gold' => $value, 'fund_id' => $fund->id, 'product_id' => $productId > 0 ? $productId : null, 'expire_at' => now(), 'active' => true, ], 6, false); new TransactionInterface($dest, [ 'gold' => $value, 'fund_id' => $fund->id, 'product_id' => $productId > 0 ? $productId : null, 'expire_at' => now(), 'active' => true, ], 5, true); return response()->json(true); } public function show($id) { $fund = $this->ownedFund((int) $id); return response()->json($fund->load(['wallet', 'product'])); } public function destroy($id) { $this->assertProductFundsEnabled(); $fund = $this->ownedFund((int) $id); $wallet = Wallet::where('owner', auth('customer')->id()) ->where('fund_id', $fund->id) ->first(); if ($wallet && (float) $wallet->gold > 0) { throw new UnprocessableEntityHttpException('به علت داشتن موجودی امکان حذف قلک وجود ندارد'); } Fund::destroy($fund->id); return response()->json(true); } private function assertProductFundsEnabled(): void { if (! app(ConfigService::class)->isProductFundsEnabled()) { throw new UnprocessableEntityHttpException('قابلیت قلک محصول غیرفعال است'); } } private function ownedFund(int $id): Fund { if ($id <= 0) { throw new UnprocessableEntityHttpException('قلک نامعتبر است'); } $fund = Fund::with(['wallet', 'product']) ->where('customer_id', auth('customer')->id()) ->find($id); if (! $fund) { throw new UnprocessableEntityHttpException('قلک یافت نشد'); } return $fund; } /** @return array{0: null, 1: int|null} */ private function sourceWalletKeys(int $productId): array { if ($productId <= 0) { return [null, null]; } if (app(ConfigService::class)->isMainWalletProduct($productId)) { return [null, null]; } return [null, $productId]; } }