207 lines
6.8 KiB
TypeScript
207 lines
6.8 KiB
TypeScript
import { ref } from 'vue'
|
|
import { getSettings } from '@/api/configuration'
|
|
|
|
export const serviceFeePercentage = ref<number>(0.01)
|
|
|
|
/** true = مبالغ رند: فروش → ریال دقیق (کارمزد از طلا)؛ خرید → وزن دقیق (کارمزد از ریال) */
|
|
export const feeDeductFromGold = ref(false)
|
|
|
|
/** آخرین فیلدی که کاربر مستقیم ویرایش کرده — مبنای بهروزرسانی هنگام تغییر قیمت */
|
|
export type TradeInputLock = 'gram' | 'rial' | null
|
|
|
|
/** حداکثر اعشار ذخیره/ارسال به سرور (مطابق orders.amount) */
|
|
export const TRADE_GRAM_STORAGE_DECIMALS = 10
|
|
|
|
/** حداکثر اعشار نمایش به کاربر در اپ */
|
|
export const TRADE_GRAM_DISPLAY_DECIMALS = 8
|
|
|
|
export function normalizeOrderGoldAmount(grams?: number | null): number {
|
|
const value = Number(grams ?? 0)
|
|
if (!Number.isFinite(value) || value === 0) return 0
|
|
return parseFloat(value.toFixed(TRADE_GRAM_STORAGE_DECIMALS))
|
|
}
|
|
|
|
export function formatTradeGramDisplay(grams?: number | null): number {
|
|
const value = Number(grams ?? 0)
|
|
if (!Number.isFinite(value)) return 0
|
|
return parseFloat(value.toFixed(TRADE_GRAM_DISPLAY_DECIMALS))
|
|
}
|
|
|
|
function feeRate() {
|
|
return Number(serviceFeePercentage.value)
|
|
}
|
|
|
|
export async function loadServiceFeeFromSettings() {
|
|
try {
|
|
const settings = await getSettings()
|
|
if (settings && Array.isArray(settings)) {
|
|
const feeSetting = settings.find(
|
|
(s) => s?.key === 'fee_service_charge_percentage' && s?.is_active === true
|
|
)
|
|
if (feeSetting?.value !== undefined && feeSetting?.value !== null) {
|
|
serviceFeePercentage.value = Number(feeSetting.value)
|
|
}
|
|
|
|
const feeModeSetting = settings.find(
|
|
(s) => s?.key === 'fee_deduct_from_gold' && s?.is_active === true
|
|
)
|
|
if (feeModeSetting?.value !== undefined && feeModeSetting?.value !== null) {
|
|
const raw = String(feeModeSetting.value).trim().toLowerCase()
|
|
feeDeductFromGold.value = raw === 'true' || raw === '1'
|
|
} else {
|
|
feeDeductFromGold.value = false
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log(e)
|
|
}
|
|
}
|
|
|
|
export function calcFeeInGrams(goldValue?: number | null) {
|
|
return parseFloat(((goldValue ?? 0) * Number(serviceFeePercentage.value)).toFixed(4))
|
|
}
|
|
|
|
/** گرم معادل برای دریافت دقیق مبلغ ریالی در فروش */
|
|
export function calcGoldAmountFromTargetRial(rialAmount: number, unitPriceRial: number) {
|
|
if (!rialAmount || !unitPriceRial) return 0
|
|
return normalizeOrderGoldAmount(rialAmount / unitPriceRial)
|
|
}
|
|
|
|
/** وقتی کاربر گرم را قفل کرده — ریال نمایشی/قابل پرداخت از همان گرم */
|
|
export function calcRialFromLockedGram(displayGram: number, unitPriceRial: number, isBuy: boolean) {
|
|
const grams = Number(displayGram)
|
|
const unit = Number(unitPriceRial)
|
|
if (!grams || !unit) return 0
|
|
const fee = feeRate()
|
|
|
|
if (isBuy) {
|
|
if (feeDeductFromGold.value) {
|
|
return Math.round(grams * unit * (1 + fee))
|
|
}
|
|
return Math.round(grams * unit)
|
|
}
|
|
|
|
if (feeDeductFromGold.value) {
|
|
return Math.round(grams * unit)
|
|
}
|
|
return Math.round(grams * unit * (1 - fee))
|
|
}
|
|
|
|
/** وقتی کاربر ریال را قفل کرده — گرم نمایشی از همان مبلغ */
|
|
export function calcGramFromLockedRial(rialAmount: number, unitPriceRial: number, isBuy: boolean) {
|
|
const rial = Number(rialAmount)
|
|
const unit = Number(unitPriceRial)
|
|
if (!rial || !unit) return 0
|
|
const fee = feeRate()
|
|
|
|
if (isBuy) {
|
|
if (feeDeductFromGold.value) {
|
|
return rial / (unit * (1 + fee))
|
|
}
|
|
return (rial / unit) * (1 - fee)
|
|
}
|
|
|
|
if (feeDeductFromGold.value) {
|
|
return rial / unit
|
|
}
|
|
return rial / (unit * (1 - fee))
|
|
}
|
|
|
|
/** مبلغ ریالی قفلشده توسط کاربر — برای ارسال به سرور */
|
|
export function calcTradeTargetCash(
|
|
goldCash?: number | null,
|
|
inputLock?: TradeInputLock,
|
|
): number | undefined {
|
|
if (inputLock !== 'rial') return undefined
|
|
const cash = Number(goldCash ?? 0)
|
|
if (cash <= 0) return undefined
|
|
return Math.round(cash)
|
|
}
|
|
|
|
/** مبلغ نهایی قابل پرداخت (خرید) یا دریافت (فروش) به ریال */
|
|
export function calcPayablePriceRial(
|
|
finalPriceRial: number,
|
|
feeInRial: number,
|
|
isBuy: boolean,
|
|
targetRialCash?: number | null
|
|
) {
|
|
if (targetRialCash != null && Number(targetRialCash) > 0) {
|
|
return Number(targetRialCash)
|
|
}
|
|
|
|
if (feeDeductFromGold.value) {
|
|
if (isBuy) return finalPriceRial + feeInRial
|
|
return finalPriceRial
|
|
}
|
|
return isBuy ? finalPriceRial : Math.max(0, finalPriceRial - feeInRial)
|
|
}
|
|
|
|
/** وزن ارسالی به سرور */
|
|
export function calcOrderGoldAmount(
|
|
goldValue?: number | null,
|
|
goldCash?: number | null,
|
|
unitPriceRial?: number,
|
|
isBuy?: boolean,
|
|
inputLock?: TradeInputLock
|
|
) {
|
|
const gold = Number(goldValue ?? 0)
|
|
const cash = Number(goldCash ?? 0)
|
|
const unit = Number(unitPriceRial ?? 0)
|
|
|
|
if (isBuy && inputLock === 'rial' && cash > 0 && unit > 0) {
|
|
if (feeDeductFromGold.value) {
|
|
const fee = feeRate()
|
|
return normalizeOrderGoldAmount(cash / (unit * (1 + fee)))
|
|
}
|
|
return normalizeOrderGoldAmount(cash / unit)
|
|
}
|
|
|
|
if (!isBuy && feeDeductFromGold.value) {
|
|
if (inputLock === 'rial' && cash > 0 && unit > 0) {
|
|
return calcGoldAmountFromTargetRial(cash, unit)
|
|
}
|
|
if (inputLock !== 'rial') {
|
|
return normalizeOrderGoldAmount(gold)
|
|
}
|
|
const targetCash = calcSellTargetCash(goldCash, goldValue, unitPriceRial, inputLock)
|
|
if (targetCash != null && unit > 0) {
|
|
return calcGoldAmountFromTargetRial(targetCash, unit)
|
|
}
|
|
}
|
|
|
|
return normalizeOrderGoldAmount(gold)
|
|
}
|
|
|
|
/** مبلغ ریالی دقیق دریافتی در فروش (فقط وقتی کاربر با ریال وارد کرده) */
|
|
export function calcSellTargetCash(
|
|
goldCash?: number | null,
|
|
goldValue?: number | null,
|
|
unitPriceRial?: number,
|
|
inputLock?: TradeInputLock
|
|
) {
|
|
if (!feeDeductFromGold.value) return undefined
|
|
if (inputLock === 'gram') return undefined
|
|
|
|
const cash = Number(goldCash ?? 0)
|
|
if (cash <= 0) return undefined
|
|
if (inputLock === 'rial') return Math.round(cash)
|
|
|
|
const gold = Number(goldValue ?? 0)
|
|
const unit = Number(unitPriceRial ?? 0)
|
|
if (gold > 0 && unit > 0) {
|
|
const derived = Math.round(gold * unit)
|
|
if (Math.abs(derived - Math.round(cash)) <= 1) return undefined
|
|
}
|
|
return Math.round(cash)
|
|
}
|
|
|
|
/** کل طلای کسرشونده از کیف در فروش (شامل کارمزد وقتی fee_deduct_from_gold روشن است) */
|
|
export function calcSellGoldDebit(goldValue?: number | null, feeInGrams?: number | null) {
|
|
const gold = Number(goldValue ?? 0)
|
|
const fee = Number(feeInGrams ?? 0)
|
|
if (feeDeductFromGold.value) {
|
|
return parseFloat((gold + fee).toFixed(4))
|
|
}
|
|
return gold
|
|
}
|