Files
ghatreh_user/src/utils/productPrice.ts
T

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'