From 285767fcd3449f49d8848292ca93d84d461a6670 Mon Sep 17 00:00:00 2001 From: adonis Date: Wed, 10 Jun 2026 16:57:31 +0330 Subject: [PATCH] added merge wallet --- .../Controllers/Admin/CustomerController.php | 7 +- app/Services/AssetSummaryService.php | 127 ++++++++++++++++-- app/Services/ConfigService.php | 32 +++++ .../sql/add_main_wallet_products_setting.sql | 22 +++ database/sql/deploy_safe_schema.sql | 24 ++++ 5 files changed, 202 insertions(+), 10 deletions(-) create mode 100644 database/sql/add_main_wallet_products_setting.sql diff --git a/app/Http/Controllers/Admin/CustomerController.php b/app/Http/Controllers/Admin/CustomerController.php index da0c10e..4b773fa 100644 --- a/app/Http/Controllers/Admin/CustomerController.php +++ b/app/Http/Controllers/Admin/CustomerController.php @@ -74,10 +74,13 @@ class CustomerController extends SuperController public function funds(Request $request,$customer_id) { - $query_table = Wallet::with(['fund'])->where('owner', $customer_id); // Adjust columns as per your schema + // فقط کیف محصول‌ها (نه کیف اصلی) — هر ردیف = موجودی واقعی یک محصول/فلز + $query_table = Wallet::with(['product:id,title,metal_id,unit']) + ->where('owner', $customer_id) + ->whereNotNull('product_id'); TableEngine::create($query_table, $request) ->initSearch($this->search) - ->select(['id', 'owner', 'cash', 'gold','fund_id','created_at', 'updated_at']) + ->select(['id', 'owner', 'cash', 'gold', 'fund_id', 'product_id', 'created_at', 'updated_at']) ->export($query_table); return response()->json($query_table); } diff --git a/app/Services/AssetSummaryService.php b/app/Services/AssetSummaryService.php index e771a0c..9308551 100644 --- a/app/Services/AssetSummaryService.php +++ b/app/Services/AssetSummaryService.php @@ -42,11 +42,15 @@ class AssetSummaryService $catalogById = $catalog->keyBy('id'); + // محصولات متصل به کیف اصلی (main_wallet_products) — موجودی‌شان = gold کیف اصلی + $mainWalletProductIds = $this->configService->mainWalletProductIds(); + + $metalsWithProductWallet = $this->metalIdsWithProductWallet($userWallets, $catalogById, $mainWalletProductIds); $inventoryCards = array_merge( - $this->buildMeltedInventoryCards($catalog, $catalogById, $mainWallet, $metalIds), - $this->buildInventoryCards($userWallets, $catalogById) + $this->buildMeltedInventoryCards($catalog, $catalogById, $mainWallet, $metalIds, $metalsWithProductWallet, $mainWalletProductIds), + $this->buildInventoryCards($userWallets, $catalogById, $mainWalletProductIds) ); - $metalTotals = $this->buildMetalTotals($userWallets, $mainWallet, $metalIds, $weightUnit, $catalogById); + $metalTotals = $this->buildMetalTotals($userWallets, $mainWallet, $metalIds, $weightUnit, $catalogById, $metalsWithProductWallet, $mainWalletProductIds); $cashWallet = (float) ($mainWallet->cash ?? 0); $assetsRialValue = $this->sumAssetsRialValue($userWallets, $catalogById); $totalRialValue = $cashWallet + $assetsRialValue; @@ -97,15 +101,51 @@ class AssetSummaryService Collection $catalog, Collection $catalogById, ?Wallet $mainWallet, - array $metalIds + array $metalIds, + array $metalsWithProductWallet = [], + array $mainWalletProductIds = [] ): array { $cards = []; $mainWalletGrams = Controller::weightToGrams((float) ($mainWallet->gold ?? 0)); $assignedMetalIds = []; $mainProductId = $this->configService->mainProductId(); + // حالت صریح: محصولات متصل به کیف اصلی — یک کارت واحد (نماینده = اولین محصول لیست) + if ($mainWalletProductIds !== []) { + $representative = $this->resolveMainWalletRepresentative($catalogById, $mainWalletProductIds); + if (!$representative) { + return []; + } + + $mainWalletWeight = (float) ($mainWallet->gold ?? 0); + if ($mainWalletWeight <= 0) { + return []; + } + + $productPayload = $this->productPayload($representative); + + return [[ + 'product_id' => (int) $representative->id, + 'title' => (string) $representative->title, + 'group_type' => $this->meltedGroupTypeLabel($representative), + 'metal_id' => (int) ($representative->metal_id ?? 0), + 'quantity' => round($mainWalletWeight, 3), + 'unit' => Controller::productQuantityUnit($productPayload), + 'display_type' => 'weight', + 'unit_weight' => $this->productUnitWeightLabel($representative), + 'show_in_assets' => $this->shouldShowInAssets($representative), + 'is_melted' => true, + 'is_main_wallet' => true, + 'file' => $this->filePayload($representative), + ]]; + } + foreach ($metalIds as $metalId) { - if ($metalId <= 0 || in_array($metalId, $assignedMetalIds, true)) { + if ( + $metalId <= 0 + || in_array($metalId, $assignedMetalIds, true) + || in_array((int) $metalId, $metalsWithProductWallet, true) + ) { continue; } @@ -186,6 +226,48 @@ class AssetSummaryService return $group !== '' ? $group : 'آبشده'; } + /** اولین محصولِ لیست main_wallet_products که در کاتالوگ فعال است. */ + private function resolveMainWalletRepresentative(Collection $catalogById, array $mainWalletProductIds): ?Product + { + foreach ($mainWalletProductIds as $productId) { + $product = $catalogById->get((int) $productId); + if ($product) { + return $product; + } + } + + return null; + } + + /** فلزهایی که کاربر روی کیف محصول موجودی دارد — برای جلوگیری از کارت آبشدهٔ تکراری. */ + private function metalIdsWithProductWallet(Collection $userWallets, Collection $catalogById, array $mainWalletProductIds = []): array + { + $metalIds = []; + + foreach ($userWallets as $walletLine) { + if ($walletLine->product_id === null || (float) $walletLine->gold <= 0) { + continue; + } + + // کیف محصولاتِ متصل به کیف اصلی نادیده گرفته می‌شود + if (in_array((int) $walletLine->product_id, $mainWalletProductIds, true)) { + continue; + } + + $product = $walletLine->product; + if (!$product) { + continue; + } + + $metalId = (int) ($this->catalogProduct($product, $catalogById)->metal_id ?? 0); + if ($metalId > 0) { + $metalIds[] = $metalId; + } + } + + return array_values(array_unique($metalIds)); + } + /** محصول نمایشی برای موجودی کیف اصلی هر فلز. */ private function resolveDisplayProductForMetal( Collection $catalog, @@ -208,7 +290,7 @@ class AssetSummaryService return $catalog->first(fn (Product $product) => (int) ($product->metal_id ?? 0) === $metalId); } - private function buildInventoryCards(Collection $userWallets, Collection $catalogById): array + private function buildInventoryCards(Collection $userWallets, Collection $catalogById, array $mainWalletProductIds = []): array { $cards = []; @@ -217,6 +299,11 @@ class AssetSummaryService continue; } + // محصول متصل به کیف اصلی دارایی جدا ندارد — کارتش از کیف اصلی ساخته می‌شود + if (in_array((int) $walletLine->product_id, $mainWalletProductIds, true)) { + continue; + } + $product = $walletLine->product; if (!$product) { continue; @@ -248,7 +335,9 @@ class AssetSummaryService ?Wallet $mainWallet, array $metalIds, string $weightUnit, - Collection $catalogById + Collection $catalogById, + array $metalsWithProductWallet = [], + array $mainWalletProductIds = [] ): array { if ($metalIds === []) { return []; @@ -257,6 +346,13 @@ class AssetSummaryService $mainWalletGrams = Controller::weightToGrams((float) ($mainWallet->gold ?? 0)); $totals = []; + // فلز نمایندهٔ کیف اصلی در حالت main_wallet_products + $mainWalletMetalId = null; + if ($mainWalletProductIds !== []) { + $representative = $this->resolveMainWalletRepresentative($catalogById, $mainWalletProductIds); + $mainWalletMetalId = $representative ? (int) ($representative->metal_id ?? 0) : null; + } + foreach ($metalIds as $metalId) { $productGrams = 0.0; @@ -265,6 +361,11 @@ class AssetSummaryService continue; } + // کیف محصولاتِ متصل به کیف اصلی جداگانه شمرده نمی‌شود + if (in_array((int) $walletLine->product_id, $mainWalletProductIds, true)) { + continue; + } + $catalogProduct = $this->catalogProduct($walletLine->product, $catalogById); if ((int) ($catalogProduct->metal_id ?? 0) !== (int) $metalId) { continue; @@ -280,7 +381,17 @@ class AssetSummaryService ); } - $meltedGrams = $this->meltedGramsForMetalTotal($mainWalletGrams, $metalId, $metalIds); + if ($mainWalletProductIds !== []) { + // حالت صریح: کیف اصلی فقط یک‌بار، زیر فلزِ محصول نماینده + $meltedGrams = ($mainWalletMetalId !== null && (int) $metalId === $mainWalletMetalId) + ? $mainWalletGrams + : 0.0; + } else { + // کیف اصلی فقط برای فلزی که هیچ کیف محصولی ندارد به مجموع اضافه می‌شود. + $meltedGrams = in_array((int) $metalId, $metalsWithProductWallet, true) + ? 0.0 + : $this->meltedGramsForMetalTotal($mainWalletGrams, $metalId, $metalIds); + } $productWeight = round(Controller::gramsToTradeUnit($productGrams), 3); $meltedWeight = round(Controller::gramsToTradeUnit($meltedGrams), 3); $totalWeight = round($productWeight + $meltedWeight, 3); diff --git a/app/Services/ConfigService.php b/app/Services/ConfigService.php index 16d0eef..bf37cbd 100644 --- a/app/Services/ConfigService.php +++ b/app/Services/ConfigService.php @@ -116,6 +116,38 @@ class ConfigService return in_array($default, self::PRICE_PROVIDERS, true) ? $default : 'montzar'; } + /** + * محصولات متصل به کیف اصلی — setting `main_wallet_products`. + * مقدار: idها با کاما (مثل «13,25») یا آرایه JSON (مثل «[13,25]»). + * موجودی این محصولات = gold کیف اصلی (دارایی جدا ندارند). + * فقط opt-in: اگر ست نباشد آرایه خالی برمی‌گردد و رفتار قبلی حفظ می‌شود. + */ + public function mainWalletProductIds(): array + { + $raw = trim($this->getString('main_wallet_products', '')); + if ($raw === '') { + return []; + } + + $decoded = json_decode($raw, true); + $parts = is_array($decoded) ? $decoded : explode(',', $raw); + + $ids = []; + foreach ($parts as $part) { + $id = (int) trim((string) $part); + if ($id > 0) { + $ids[] = $id; + } + } + + return array_values(array_unique($ids)); + } + + public function isMainWalletProduct(int $productId): bool + { + return in_array($productId, $this->mainWalletProductIds(), true); + } + /** * شناسه محصول آبشده: اول settings، بعد const. * مقدار settings می‌تواند id عددی (مثل 13) یا reading_method (مثل estjt_...) باشد. diff --git a/database/sql/add_main_wallet_products_setting.sql b/database/sql/add_main_wallet_products_setting.sql new file mode 100644 index 0000000..369d473 --- /dev/null +++ b/database/sql/add_main_wallet_products_setting.sql @@ -0,0 +1,22 @@ +-- ============================================================================ +-- کلید main_wallet_products — اتصال محصولات به کیف اصلی (gold) +-- +-- محصولاتی که id آن‌ها در این کلید است، دارایی جدا ندارند؛ +-- موجودی‌شان همان gold کیف اصلی کاربر است (ساختار قدیم تک‌آبشده). +-- اگر چند id باشد، همگی یک موجودی مشترک (کیف اصلی) دارند و در صفحه +-- دارایی‌ها فقط یک کارت (محصول اولِ لیست) ساخته می‌شود. +-- +-- مقدار: idها با کاما (مثل «13,25») یا آرایه JSON (مثل «[13,25]») +-- خالی/غایب = رفتار قبلی بدون هیچ تغییری (بورس مس و پروژه‌های چند‌کیفه) +-- +-- نمونه اجرا برای گلد علی: +-- UPDATE `settings` SET `value` = '13,25' WHERE `key` = 'main_wallet_products'; +-- ============================================================================ + +INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) +SELECT 'main_wallet_products', '', 'محصولات متصل به کیف اصلی', 0, 1, + 'idهای محصول با کاما (مثل 13,25). موجودی این محصولات = gold کیف اصلی. خالی = غیرفعال' +WHERE NOT EXISTS (SELECT 1 FROM `settings` WHERE `key` = 'main_wallet_products' LIMIT 1); + +-- اگر کلید از قبل وجود دارد، قابل ادیت نبودنش تضمین شود +UPDATE `settings` SET `can_edit` = 0 WHERE `key` = 'main_wallet_products'; diff --git a/database/sql/deploy_safe_schema.sql b/database/sql/deploy_safe_schema.sql index 36f5aee..2cd9ce1 100644 --- a/database/sql/deploy_safe_schema.sql +++ b/database/sql/deploy_safe_schema.sql @@ -294,6 +294,30 @@ AND NOT EXISTS ( SELECT 1 FROM `settings` WHERE `key` = 'unit' LIMIT 1 ); +-- --------------------------------------------------------------------------- +-- settings — main_wallet_products: محصولات متصل به کیف اصلی (غیرقابل ادیت در پنل) +-- موجودی این محصولات = gold کیف اصلی. خالی = غیرفعال (رفتار پیش‌فرض چندکیفه) +-- مثال گلد علی: UPDATE settings SET value='13,25' WHERE `key`='main_wallet_products'; +-- --------------------------------------------------------------------------- +INSERT INTO `settings` (`key`, `value`, `title_fa`, `can_edit`, `is_active`, `description`) +SELECT + 'main_wallet_products', + '', + 'محصولات متصل به کیف اصلی', + 0, + 1, + 'idهای محصول با کاما (مثل 13,25). موجودی این محصولات = gold کیف اصلی. خالی = غیرفعال' +FROM DUAL +WHERE EXISTS ( + SELECT 1 FROM information_schema.TABLES + WHERE TABLE_SCHEMA = @db AND TABLE_NAME = 'settings' +) +AND NOT EXISTS ( + SELECT 1 FROM `settings` WHERE `key` = 'main_wallet_products' LIMIT 1 +); + +UPDATE `settings` SET `can_edit` = 0 WHERE `key` = 'main_wallet_products'; + -- --------------------------------------------------------------------------- -- پایان — برای بورس‌مس (اختیاری، جدا اجرا کنید): -- UPDATE `settings` SET `value` = 'کیلوگرم' WHERE `key` = 'unit';