import type { Product } from '@/models/products' import type { UserWalletLine } from '@/models/wallet' import type { ProductFile } from '@/utils/file' import type { WalletInventoryCard, WalletMetalTotal } from '@/models/walletAssets' import { getFileUrl } from '@/utils/file' import { isCountBasedProduct, getProductQuantityUnit, type ProductUnitSource } from '@/utils/productDealMethod' import { isMeltedTradeGroup, normalizeProductGroupType, LEGACY_GROUP_KATOD } from '@/utils/productCategories' import defaultImg from '@/assets/img/defaultAbshodeh.svg' import goyImg from '@/assets/img/goyImg.svg' import mokaabImg from '@/assets/img/mokaabImg.svg' import sekkeImg from '@/assets/img/sekkeImg.svg' import mesImg from '@/assets/img/mesImg.jpg' import noghrehImg from '@/assets/img/noghrehImg.webp' import parsianImg from '@/assets/img/parsianImg.svg' export const METAL_GOLD = 1 export const METAL_SILVER = 2 export const METAL_COPPER = 3 const GROUP_DEFAULT_IMAGES: Record = { گوی: goyImg, مکعب: mokaabImg, سکه: sekkeImg, مس: mesImg, [LEGACY_GROUP_KATOD]: mesImg, نقره: noghrehImg, 'سکه پارسیان': parsianImg, طلا: defaultImg, آبشده: defaultImg, } export function getMetalLabel(metalId: number): string { switch (metalId) { case METAL_GOLD: return 'طلا' case METAL_SILVER: return 'نقره' case METAL_COPPER: return 'مس' default: return 'فلز' } } export function getTotalMetalLabel(metalId: number): string { return `کل دارایی ${getMetalLabel(metalId)}` } export function sumInventoryCardsWeight( cards: Array<{ metal_id?: number; quantity?: number }>, metalId: number ): number { return cards .filter((card) => Number(card.metal_id) === metalId) .reduce((sum, card) => sum + Number(card.quantity ?? 0), 0) } /** ردیف‌های جزئی action sheet — فقط محصولاتی که show_in_assets روشن است. */ export function assetSheetInventoryCards( cards: T[], metalId?: number ): T[] { return cards.filter((card) => { if (!card.show_in_assets) { return false } if (metalId == null) { return true } return Number(card.metal_id) === metalId }) } /** همه کارت‌های صفحه دارایی‌ها — بدون فیلتر show_in_assets. */ export function fundPageInventoryCards(cards: T[]): T[] { return cards.filter((card) => Number((card as { quantity?: number }).quantity ?? 0) > 0) } export function resolveSummaryMetalWeight( metal: { total_weight: number }, _cards?: Array<{ metal_id?: number; quantity?: number }> ): number { return Number(metal.total_weight ?? 0) } /** متن توضیح مودال دارایی فلزی — بر اساس metal_totals سرور، بدون هاردکد طلا */ export function formatMetalAssetsInfoText(metal: WalletMetalTotal): string { const unit = metal.unit || 'گرم' return `${metal.label}: مجموع کیف محصولات (${metal.product_weight} ${unit}) + موجودی کیف اصلی (${metal.melted_weight} ${unit})` } function formatRialNumber(value: number): string { return Math.round(value).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') } /** متن توضیح مودال دارایی ریالی — cash_wallet + assets_rial_value */ export function formatRialAssetsInfoText(cashWallet: number, assetsRialValue: number): string { return `مجموع کیف پول ریالی (${formatRialNumber(cashWallet)} ریال) + معادل ریالی محصولات (${formatRialNumber(assetsRialValue)} ریال)` } export function formatInventoryCardValue(card: { quantity?: number unit?: string display_type?: string }): string { const quantity = Number(card.quantity ?? 0) const unit = card.unit ?? (card.display_type === 'count' ? 'عدد' : 'گرم') return `${quantity} ${unit}` } export function findProductInventoryCard( cards: WalletInventoryCard[], productId?: number | null ): WalletInventoryCard | undefined { if (!productId) { return undefined } return cards.find((item) => Number(item.product_id) === Number(productId)) } /** * موجودی کیف یک محصول از inventory_cards. * fallback: پروژه‌های ساختار قدیم (مثل گلد علی) کیف محصول ندارند و موجودی روی gold کیف اصلی است. */ export function getProductInventoryQuantity( cards: WalletInventoryCard[], productId?: number | null, mainWalletGold?: number | null ): number { const card = findProductInventoryCard(cards, productId) if (card) { return Number(card.quantity ?? 0) } return Number(mainWalletGold ?? 0) } /** برچسب موجودی کیف محصول — برای فرم خرید/فروش */ export function formatProductWalletBalanceText( product: { id?: number; title?: string | null; unit?: string | null; group_type?: string | null } | null | undefined, cards: WalletInventoryCard[] = [], mainWalletGold?: number | null ): string { const card = findProductInventoryCard(cards, product?.id) const quantity = card ? Number(card.quantity ?? 0) : Number(mainWalletGold ?? 0) const unit = card?.unit ?? getProductQuantityUnit(product) const title = product?.title?.trim() || 'محصول' return `موجودی کیف ${title}: ${quantity} ${unit}` } export function isSingleMetalCatalog(metalIds: number[]): boolean { return metalIds.length === 1 } export function getCatalogMetalIds(products?: Product[] | null): number[] { const ids = new Set() for (const product of products ?? []) { const metalId = Number(product.metal_id) if (metalId > 0) { ids.add(metalId) } } return Array.from(ids).sort((a, b) => a - b) } export function catalogHasCountBasedProducts(products?: Product[] | null): boolean { return (products ?? []).some((product) => isCountBasedProduct(product)) } export function catalogHasMeltedProducts(products?: Product[] | null): boolean { return (products ?? []).some((product) => isMeltedTradeGroup(product.group_type)) } export function enrichInventoryCardsWithCatalog( cards: T[], catalog: Product[] ): T[] { return cards.map((card) => { const fromCatalog = catalog.find((product) => product.id === card.product_id) if (!fromCatalog) { return card } return { ...card, file: card.file ?? fromCatalog.file ?? null, } }) } export function enrichInventoryWithCatalog( items: T[], catalog: Product[] ): T[] { return items.map((item) => { const fromCatalog = catalog.find((product) => product.id === item.id) if (!fromCatalog) { return item } const current = item as T & Product return { ...fromCatalog, ...current, wallet: current.wallet ?? fromCatalog.wallet, file: current.file ?? fromCatalog.file, metal_id: current.metal_id ?? fromCatalog.metal_id, group_type: current.group_type ?? fromCatalog.group_type, } }) } export function getProductDisplayImage(product?: { group_type?: string | null title?: string | null file?: ProductFile | null img?: string | null } | null): string { const fileUrl = getFileUrl(product?.file) if (fileUrl) { return fileUrl } if (product?.img && /^https?:\/\//i.test(product.img)) { return product.img } const group = normalizeProductGroupType(product?.group_type) if (group && GROUP_DEFAULT_IMAGES[group]) { return GROUP_DEFAULT_IMAGES[group] } return defaultImg } type InventoryProduct = ProductUnitSource & { metal_id?: number | null title?: string | null wallet?: { gold?: number | null } | null } export function sumCountBasedInventory(products?: InventoryProduct[] | null): number { return (products ?? []) .filter((product) => product.title !== 'آبشده نقدی' && isCountBasedProduct(product)) .reduce((sum, product) => sum + Number(product.wallet?.gold ?? 0), 0) } export function sumProductWalletByMetal( products: InventoryProduct[] | null | undefined, metalId: number ): number { return (products ?? []) .filter((product) => Number(product.metal_id) === metalId) .reduce((sum, product) => sum + Number(product.wallet?.gold ?? 0), 0) } export function getMainWalletLine(wallets?: UserWalletLine[] | null): UserWalletLine | undefined { return wallets?.find((line) => line.product_id == null) } export function getMainWalletGold(wallets?: UserWalletLine[] | null): number { return Number(getMainWalletLine(wallets)?.gold ?? 0) } export function getMainWalletCash(wallets?: UserWalletLine[] | null): number { return Number(getMainWalletLine(wallets)?.cash ?? 0) } export function sumWalletsGold( wallets?: UserWalletLine[] | null, metalId?: number | null ): number { if (!wallets?.length) { return 0 } return wallets.reduce((sum, line) => { const weight = Number(line.gold ?? 0) if (metalId == null) { return sum + weight } if (line.product_id == null) { return metalId === METAL_GOLD ? sum + weight : sum } return Number(line.product?.metal_id) === metalId ? sum + weight : sum }, 0) } export function sumProductWalletsGold( wallets?: UserWalletLine[] | null, metalId?: number | null ): number { return (wallets ?? []) .filter((line) => line.product_id != null) .reduce((sum, line) => { const weight = Number(line.gold ?? 0) if (metalId == null) { return sum + weight } return Number(line.product?.metal_id) === metalId ? sum + weight : sum }, 0) } export function sumInventoryMetalWeight(products?: InventoryProduct[] | null): number { return (products ?? []) .filter((product) => product.title !== 'آبشده نقدی') .reduce((sum, product) => sum + Number(product.wallet?.gold ?? 0), 0) } export function getTotalMetalWeight( walletInfo?: Record | null, wallets?: UserWalletLine[] | null, inventoryProducts?: InventoryProduct[] | null ): number { const fromInventory = sumInventoryMetalWeight(inventoryProducts) if (fromInventory > 0) { return fromInventory } const fromProductWallets = sumProductWalletsGold(wallets) if (fromProductWallets > 0) { return fromProductWallets } if (wallets?.length) { return getMainWalletGold(wallets) } return Number(walletInfo?.treasure ?? walletInfo?.gold_sum ?? walletInfo?.count_gold ?? 0) } /** فقط موجودی نقدی کیف اصلی — فیلد gold (مثل 0.3 کیلوگرم) جزو دارایی ریالی نیست */ export function getRialCashAmount( walletInfo?: Record | null, wallets?: UserWalletLine[] | null ): number { const mainCash = getMainWalletCash(wallets) if (mainCash > 0) { return mainCash } return Number(walletInfo?.count_cash ?? walletInfo?.cash ?? 0) } export function getTotalRialEquivalent(walletInfo?: Record | null): number { return Number(walletInfo?.count_cash_total ?? 0) } export function getMetalAssetAmount( walletInfo: Record | null | undefined, metalId: number, inventoryProducts?: InventoryProduct[] | null ): number { if (inventoryProducts?.length) { const fromProducts = sumProductWalletByMetal(inventoryProducts, metalId) if (fromProducts > 0 || inventoryProducts.some((product) => Number(product.metal_id) === metalId)) { return fromProducts } } if (!walletInfo) { return 0 } switch (metalId) { case METAL_GOLD: return Number(walletInfo.count_gold_wallets ?? walletInfo.count_gold ?? 0) case METAL_SILVER: return Number(walletInfo.count_silver ?? 0) case METAL_COPPER: return Number(walletInfo.count_copper ?? 0) default: return 0 } } export function getMetalAssetAmountForDisplay( walletInfo: Record | null | undefined, metalId: number, catalogMetalIds: number[], inventoryProducts?: InventoryProduct[] | null, wallets?: UserWalletLine[] | null ): number { if (isSingleMetalCatalog(catalogMetalIds)) { return getTotalMetalWeight(walletInfo, wallets, inventoryProducts) } const productWeight = sumProductWalletByMetal(inventoryProducts, metalId) const mainWalletWeight = wallets?.length ? getMainWalletGold(wallets) : Number(walletInfo?.count_gold_wallet ?? 0) if (catalogHasMeltedProducts(inventoryProducts as Product[] | null | undefined) && metalId === catalogMetalIds[0]) { return productWeight + mainWalletWeight } if (wallets?.length) { const walletWeight = sumWalletsGold(wallets, metalId) if (walletWeight > 0) { return walletWeight } } return productWeight || getMetalAssetAmount(walletInfo, metalId, inventoryProducts) }