371 lines
10 KiB
TypeScript
371 lines
10 KiB
TypeScript
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'
|