import type { Product } from '@/models/products' import { getTradeBuyPrice, getTradeSellPrice, lastPrice } from '@/utils/price' import { resolveProductCashFeeBuyRial, resolveProductCashFeeSellRial } from '@/utils/productCashFee' import { isAppPriceProduct, isColleaguePriceProduct, isMeltedProductUsingAppPrice, isMeltedTradeGroup, isMetalCommodityGroup, usesQuantityTrade, } from '@/utils/productCategories' import { isProductTradingHoursOpen } from '@/utils/productTradeOpen' import { lastPriceIncludesSpecial } from '@/utils/specialPricing' /** * قیمت‌گیری محصول — هم‌تراز با khaneghi_user: * آبشده / قیمت‌اپ از مظنه زنده؛ قیمت ثابت و مرجع از سرور. */ function productUsesLastPriceIfZero(product: Product): boolean { return Boolean(product.use_last_price_if_zero) } function hasReferenceReadingMethod(product: Product): boolean { return Boolean(product.reading_method) } /** پایه از buy/sell مظنه آمده — special همان‌جا اعمال شده و نباید دوباره جمع شود */ function productUsesLiveBoardQuote(product: Product, side: 'buy' | 'sell'): boolean { if (isColleaguePriceProduct(product)) { const stored = side === 'sell' ? Number(product.sellPrice ?? product.price ?? 0) : Number(product.price ?? 0) if (stored > 0) { return false } const live = side === 'buy' ? getTradeBuyPrice(lastPrice.value) : getTradeSellPrice(lastPrice.value) return isMeltedTradeGroup(product.group_type) && live > 0 } if (isMeltedProductUsingAppPrice(product) || isAppPriceProduct(product)) { const live = side === 'buy' ? getTradeBuyPrice(lastPrice.value) : getTradeSellPrice(lastPrice.value) return live > 0 } return false } function cashFeeOptions(product: Product, side: 'buy' | 'sell') { if (lastPriceIncludesSpecial(lastPrice.value) && productUsesLiveBoardQuote(product, side)) { return { includeSpecial: false as const } } return undefined } function buyCashFee(product: Product, base: number) { return resolveProductCashFeeBuyRial(product, base, cashFeeOptions(product, 'buy')) } function sellCashFee(product: Product, base: number) { return resolveProductCashFeeSellRial(product, base, cashFeeOptions(product, 'sell')) } /** سمت خرید/فروش همکار؛ اگر سرور نفرستاده باشد همان قیمت وسط قبلی */ function colleagueSideUnit(product: Product, side: 'buy' | 'sell', live: number): number { const sided = side === 'buy' ? Number(product.price_buy ?? 0) : Number(product.price_sell ?? 0) if (sided > 0) { return sided } return getColleagueSourcedUnitPrice(product, live) } /** قیمت منبع همکار؛ اگر خالی بود آبشده همان مظنهٔ صفحه اصلی را می‌گیرد */ function getColleagueSourcedUnitPrice(product: Product, live: number, apiPrice = Number(product.price ?? 0)): number { if (apiPrice > 0) { return apiPrice } if (isMeltedTradeGroup(product.group_type) && live > 0) { return applyPriceOffsetPercent(live, product.price_offset_percent) } return 0 } function hasProductUnitWeight(product?: Product | null) { if (!product || isMeltedTradeGroup(product.group_type)) { return false } return Number(product.amount ?? 0) > 0 } function productPriceAmountFactor(product?: Product | null) { if (!product || product.reading_method || isColleaguePriceProduct(product)) { return 1 } if (isAppPriceProduct(product)) { const amount = Number(product.amount ?? 0) return amount > 0 ? amount : 1 } if (hasProductUnitWeight(product) && Number(product.direct_price ?? 0) === 0) { const amount = Number(product.amount ?? 0) return amount > 0 ? amount : 1 } return 1 } function getReferenceProductBaseBuyPrice(product: Product): number { const storedUnit = Number(product.price ?? 0) const useLast = productUsesLastPriceIfZero(product) if (!useLast) { return storedUnit > 0 ? storedUnit : 0 } return storedUnit } export function resolvePriceOffsetRatio(offsetPercent?: number | null) { const offset = Number(offsetPercent ?? 0) if (!offset) { return 0 } if (Math.abs(offset) > 0 && Math.abs(offset) < 1) { return offset } return offset / 100 } export function applyPriceOffsetPercent(basePrice: number, offsetPercent?: number | null) { const ratio = resolvePriceOffsetRatio(offsetPercent) if (!ratio) { return basePrice } return Math.round(basePrice * (1 + ratio)) } /** قیمت پایه بدون کارمزد */ export function getProductBaseBuyPrice(product?: Product | null): number { if (!product) { return 0 } if (isColleaguePriceProduct(product)) { return colleagueSideUnit(product, 'buy', getTradeBuyPrice(lastPrice.value)) } if (isMeltedProductUsingAppPrice(product) || isAppPriceProduct(product)) { const liveBuy = getTradeBuyPrice(lastPrice.value) if (liveBuy > 0) { return applyPriceOffsetPercent(liveBuy, product.price_offset_percent) } if (productUsesLastPriceIfZero(product) && Number(product.price ?? 0) > 0) { return Number(product.price ?? 0) } return 0 } if (isMeltedTradeGroup(product.group_type)) { return Number(product.price ?? 0) } if (isMetalCommodityGroup(product.group_type)) { return Number(product.price_profit ?? 0) + Number(product.price ?? 0) } if (hasReferenceReadingMethod(product)) { return getReferenceProductBaseBuyPrice(product) } return Number(product.price ?? 0) } /** قیمت خرید نمایشی */ export function getProductBuyPrice(product?: Product | null): number { if (!product) { return 0 } const base = getProductBaseBuyPrice(product) if (base <= 0) { return 0 } const fee = buyCashFee(product, base) const withFee = base + fee if (isColleaguePriceProduct(product)) { return withFee } if (isAppPriceProduct(product)) { return withFee * productPriceAmountFactor(product) } if ( isMeltedProductUsingAppPrice(product) || isMeltedTradeGroup(product.group_type) || isMetalCommodityGroup(product.group_type) ) { return withFee } if (usesQuantityTrade(product) && hasProductUnitWeight(product)) { return withFee * productPriceAmountFactor(product) } if (usesQuantityTrade(product)) { return withFee } return withFee * productPriceAmountFactor(product) } /** قیمت پایه فروش */ export function getProductBaseSellPrice(product?: Product | null): number { if (!product) { return 0 } if (isColleaguePriceProduct(product)) { return colleagueSideUnit(product, 'sell', getTradeSellPrice(lastPrice.value)) } if (isMeltedProductUsingAppPrice(product) || isAppPriceProduct(product)) { const liveSell = getTradeSellPrice(lastPrice.value) if (liveSell > 0) { return applyPriceOffsetPercent(liveSell, product.price_offset_percent) } if (productUsesLastPriceIfZero(product) && Number(product.price ?? 0) > 0) { return Number(product.price ?? 0) } return 0 } if (isMeltedTradeGroup(product.group_type)) { return Number(product.sellPrice ?? product.price ?? 0) } const serverSell = Number(product.sellPrice ?? 0) if (serverSell > 0) { return serverSell } return getProductBaseBuyPrice(product) } /** قیمت فروش نمایشی */ export function getProductSellPrice(product?: Product | null): number { if (!product) { return 0 } const base = getProductBaseSellPrice(product) if (base <= 0) { return 0 } if (isColleaguePriceProduct(product)) { return base - sellCashFee(product, base) } if (isAppPriceProduct(product)) { const withFee = base - sellCashFee(product, base) return withFee * productPriceAmountFactor(product) } if (isMeltedProductUsingAppPrice(product) || isMeltedTradeGroup(product.group_type)) { return base - sellCashFee(product, base) } if (usesQuantityTrade(product) && hasProductUnitWeight(product)) { const withFee = base - sellCashFee(product, base) return withFee * productPriceAmountFactor(product) } if (usesQuantityTrade(product)) { return base - sellCashFee(product, base) } return base - sellCashFee(product, base) } /** کارت فروشگاه: قیمت نمایشی صفر → «بدون قیمت» */ export function isProductWithoutDisplayPrice(product?: Product | null): boolean { return getProductBuyPrice(product) <= 0 } export function isProductPriceInactive(product?: Product | null): boolean { if (!product) { return true } if (!isProductTradingHoursOpen(product)) { return true } if ( hasReferenceReadingMethod(product) && !productUsesLastPriceIfZero(product) && product.reference_provider_active === false ) { return true } return getProductBaseBuyPrice(product) <= 0 } function tvBuyUnitPrice(product: Product): number { return Number(product.tv_price ?? 0) } function tvSellUnitPrice(product: Product): number { const sell = Number(product.tv_sell_price ?? 0) if (sell > 0) { return sell } return tvBuyUnitPrice(product) } export function getTvProductBuyPrice(product?: Product | null): number { if (!product) { return 0 } const unit = tvBuyUnitPrice(product) if (unit > 0) { return unit } return getProductBuyPrice(product) } export function getTvProductSellPrice(product?: Product | null): number { if (!product) { return 0 } const unit = tvSellUnitPrice(product) if (unit > 0) { return unit } return getProductSellPrice(product) } export function isTvProductPriceInactive(product?: Product | null): boolean { if (!product) { return true } if (tvBuyUnitPrice(product) > 0 || tvSellUnitPrice(product) > 0) { return false } return isProductPriceInactive(product) } /** * نمایش محصول در فروشگاه — اولویت با `show` سرور. * آبشدهٔ قیمت‌اپ با مظنه زنده حتی اگر show هنوز false باشد. */ export function isProductVisible(product?: Product | null): boolean { if (!product || product.active === false) { return false } if (product.show) { return true } return isMeltedProductUsingAppPrice(product) && getProductBaseBuyPrice(product) > 0 } export { isMeltedTradeGroup, isMeltedProductUsingAppPrice } from '@/utils/productCategories'