'طلا', 2 => 'نقره', 3 => 'مس', ]; public function buildForCustomer(int $customerId, int $ownerType): array { $catalog = Product::query() ->where('active', 1) ->with('file') ->get(); $userWallets = Wallet::query() ->with(['product.file']) ->where('owner', $customerId) ->where('owner_type', $ownerType) ->get(); $mainWallet = $userWallets->first(fn (Wallet $w) => $w->product_id === null); $metalIds = $this->catalogMetalIds($catalog); $weightUnit = Controller::tradeWeightUnit(); $hasCountBased = $this->catalogHasCountBased($catalog); $hasWeightBased = $this->catalogHasWeightBased($catalog); $inventoryCards = $this->buildInventoryCards($userWallets); $metalTotals = $this->buildMetalTotals($userWallets, $mainWallet, $metalIds, $weightUnit); $cashWallet = (float) ($mainWallet->cash ?? 0); $assetsRialValue = $this->sumAssetsRialValue($userWallets); $totalRialValue = $cashWallet + $assetsRialValue; return [ 'meta' => [ 'metal_ids' => $metalIds, 'metal_count' => count($metalIds), 'has_count_based_products' => $hasCountBased, 'has_weight_based_products' => $hasWeightBased, 'weight_unit' => $weightUnit, 'single_metal' => count($metalIds) === 1, ], 'cash_wallet' => round($cashWallet, 0), 'inventory_cards' => $inventoryCards, 'count_products' => $hasCountBased ? array_values(array_filter($inventoryCards, fn (array $row) => ($row['display_type'] ?? '') === 'count')) : [], 'metal_totals' => $metalTotals, 'assets_rial_value' => round($assetsRialValue, 0), 'total_rial_value' => round($totalRialValue, 0), ]; } private function catalogMetalIds(Collection $catalog): array { return $catalog ->pluck('metal_id') ->filter(fn ($id) => (int) $id > 0) ->map(fn ($id) => (int) $id) ->unique() ->sort() ->values() ->all(); } private function catalogHasCountBased(Collection $catalog): bool { return $catalog->contains(fn (Product $product) => $this->isCountBasedProduct($product)); } private function catalogHasWeightBased(Collection $catalog): bool { return $catalog->contains(fn (Product $product) => !$this->isCountBasedProduct($product)); } private function buildInventoryCards(Collection $userWallets): array { $cards = []; foreach ($userWallets as $walletLine) { if ($walletLine->product_id === null || (float) $walletLine->gold <= 0) { continue; } $product = $walletLine->product; if (!$product || !$this->shouldShowInAssets($product)) { continue; } $productPayload = $this->productPayload($product); $isCountBased = $this->isCountBasedProduct($product); $cards[] = [ 'product_id' => (int) $product->id, 'title' => (string) $product->title, 'group_type' => (string) ($product->group_type ?? ''), 'metal_id' => (int) ($product->metal_id ?? 0), 'quantity' => round((float) $walletLine->gold, 3), 'unit' => Controller::productQuantityUnit($productPayload), 'display_type' => $isCountBased ? 'count' : 'weight', 'unit_weight' => $this->productUnitWeightLabel($product), 'show_in_assets' => true, 'file' => $this->filePayload($product), ]; } return $cards; } private function buildMetalTotals( Collection $userWallets, ?Wallet $mainWallet, array $metalIds, string $weightUnit ): array { if ($metalIds === []) { return []; } $mainWalletGrams = Controller::weightToGrams((float) ($mainWallet->gold ?? 0)); $totals = []; foreach ($metalIds as $metalId) { $productGrams = 0.0; foreach ($userWallets as $walletLine) { if ($walletLine->product_id === null || !$walletLine->product) { continue; } if ((int) $walletLine->product->metal_id !== (int) $metalId) { continue; } if ((string) ($walletLine->product->group_type ?? '') === 'سکه') { continue; } $productGrams += Controller::productLineWeightToGrams( $walletLine->product, $walletLine->gold ); } $meltedGrams = $this->meltedGramsForMetal($mainWalletGrams, $metalId, $metalIds); $productWeight = round(Controller::gramsToTradeUnit($productGrams), 3); $meltedWeight = round(Controller::gramsToTradeUnit($meltedGrams), 3); $totals[] = [ 'metal_id' => (int) $metalId, 'metal_label' => self::METAL_LABELS[$metalId] ?? 'فلز', 'label' => 'کل دارایی ' . (self::METAL_LABELS[$metalId] ?? 'فلز'), 'product_weight' => $productWeight, 'melted_weight' => $meltedWeight, 'total_weight' => round($productWeight + $meltedWeight, 3), 'unit' => $weightUnit, ]; } return $totals; } private function meltedGramsForMetal(float $mainWalletGrams, int $metalId, array $catalogMetalIds): float { if ($mainWalletGrams <= 0) { return 0.0; } if (count($catalogMetalIds) === 1) { return (int) $catalogMetalIds[0] === (int) $metalId ? $mainWalletGrams : 0.0; } return (int) $metalId === 1 ? $mainWalletGrams : 0.0; } private function sumAssetsRialValue(Collection $userWallets): float { $total = 0.0; foreach ($userWallets as $walletLine) { if ($walletLine->product_id === null || !$walletLine->product) { continue; } $product = $walletLine->product; $qty = (float) $walletLine->gold; $metalId = (int) ($product->metal_id ?? 0); if ($metalId === 1) { $total += (float) ($product->price ?? 0) * $qty; } elseif ($metalId === 2 || $metalId === 3) { $total += ((float) ($product->price ?? 0) * (float) ($product->amount ?? 1)) * $qty; } } return $total; } private function shouldShowInAssets(Product $product): bool { if (Product::hasShowInAssetsColumn()) { return (bool) ($product->show_in_assets ?? true); } return true; } private function isCountBasedProduct(Product $product): bool { return Controller::isCountBasedProduct($this->productPayload($product)); } private function productPayload(Product $product): array { return [ 'unit' => $product->unit ?? null, 'direct_price' => $product->direct_price ?? null, 'group_type' => $product->group_type ?? null, 'amount' => $product->amount ?? null, ]; } private function productUnitWeightLabel(Product $product): ?string { if (!Controller::hasProductUnitWeight($this->productPayload($product))) { return null; } $amount = (float) ($product->amount ?? 0); $unit = Controller::productStorageWeightUnit($product); return trim($amount . ' ' . $unit); } private function filePayload(Product $product): ?array { if (!$product->relationLoaded('file') || !$product->file) { return null; } $file = $product->file->toArray(); $file['full_src'] = $product->file->src ?? null; return $file; } }