fix price
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* استخراج مقدار فلز/تعداد و مبلغ ریالی از ردیف اسناد تهحساب / زرین.
|
||||
* منبع: ستون amount + JSON فیلد data (بدون ستون DB جدا).
|
||||
*/
|
||||
|
||||
export type AccountingSyncDocRecord = {
|
||||
amount?: number | string | null
|
||||
type?: string | null
|
||||
desc?: string | null
|
||||
data?: string | null
|
||||
}
|
||||
|
||||
function parseNum(value: unknown): number {
|
||||
if (value == null || value === '') return NaN
|
||||
const n = Number(String(value).replace(/,/g, '').replace('/', '.'))
|
||||
return Number.isFinite(n) ? n : NaN
|
||||
}
|
||||
|
||||
export function parseDocPayload(data?: string | null): Record<string, unknown> | null {
|
||||
if (!data?.trim()) return null
|
||||
try {
|
||||
const parsed = JSON.parse(data)
|
||||
return parsed && typeof parsed === 'object' && !Array.isArray(parsed)
|
||||
? (parsed as Record<string, unknown>)
|
||||
: null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeType(type?: string | null): string {
|
||||
return String(type ?? '')
|
||||
.trim()
|
||||
.replace(/ي/g, 'ی')
|
||||
.replace(/ك/g, 'ک')
|
||||
}
|
||||
|
||||
/** سند خالص ریالی (شارژ/برداشت کیف) */
|
||||
export function isRialOnlyDoc(record: AccountingSyncDocRecord): boolean {
|
||||
const type = normalizeType(record.type)
|
||||
if (type === 'ریال' || type === 'ريال') return true
|
||||
|
||||
const payload = parseDocPayload(record.data)
|
||||
const handler = String(payload?.handler ?? '')
|
||||
return handler === 'wallet'
|
||||
}
|
||||
|
||||
/**
|
||||
* مقدار فلز/تعداد برای نمایش در ستون «مقدار».
|
||||
* ردیف ریالی → null (نشان `-`).
|
||||
*/
|
||||
export function resolveMetalAmount(record: AccountingSyncDocRecord): number | null {
|
||||
if (isRialOnlyDoc(record)) return null
|
||||
|
||||
const n = parseNum(record.amount)
|
||||
if (!Number.isFinite(n)) return null
|
||||
return n
|
||||
}
|
||||
|
||||
/**
|
||||
* مبلغ ریالی برای ستون «مبلغ ریالی».
|
||||
* - wallet: همان amount
|
||||
* - storeTrade: total_price یا amount×price
|
||||
* - walletGold / goldReward: معمولاً نیست مگر در data
|
||||
*/
|
||||
export function resolveRialAmount(record: AccountingSyncDocRecord): number | null {
|
||||
if (isRialOnlyDoc(record)) {
|
||||
const n = parseNum(record.amount)
|
||||
return Number.isFinite(n) ? n : null
|
||||
}
|
||||
|
||||
const payload = parseDocPayload(record.data)
|
||||
if (!payload) return null
|
||||
|
||||
const handler = String(payload.handler ?? '')
|
||||
|
||||
const totalPrice = parseNum(payload.total_price)
|
||||
if (Number.isFinite(totalPrice) && Math.abs(totalPrice) >= 1) {
|
||||
return Math.round(totalPrice)
|
||||
}
|
||||
|
||||
// value فقط برای wallet ریالی معنا دارد؛ برای فلز نادیده
|
||||
if (handler === 'wallet') {
|
||||
const value = parseNum(payload.value)
|
||||
if (Number.isFinite(value)) return Math.round(value)
|
||||
}
|
||||
|
||||
if (handler === 'storeTrade' || handler === '') {
|
||||
const price = parseNum(payload.price)
|
||||
const qty = parseNum(payload.amount ?? record.amount)
|
||||
if (Number.isFinite(price) && Number.isFinite(qty) && price > 0 && Math.abs(qty) > 0) {
|
||||
return Math.round(price * qty)
|
||||
}
|
||||
}
|
||||
|
||||
const cash = parseNum(payload.cash ?? payload.mablagh ?? payload.mablagh_kol)
|
||||
if (Number.isFinite(cash) && Math.abs(cash) >= 1) {
|
||||
return Math.round(cash)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export function formatMetalAmountLabel(value: number | null | undefined): string {
|
||||
if (value == null || !Number.isFinite(value)) return '-'
|
||||
// تعداد صحیح (سکه و …) بدون اعشار اضافی؛ وزن با حداکثر ۶ رقم اعشار مفید
|
||||
if (Math.abs(value - Math.round(value)) < 1e-9) {
|
||||
return String(Math.round(value))
|
||||
}
|
||||
const trimmed = Number(value.toFixed(6))
|
||||
return String(trimmed)
|
||||
}
|
||||
|
||||
export function formatRialAmountLabel(value: number | null | undefined): string {
|
||||
if (value == null || !Number.isFinite(value)) return '-'
|
||||
return Math.round(value).toLocaleString('fa-IR')
|
||||
}
|
||||
|
||||
export function metalAmountLabel(record: AccountingSyncDocRecord): string {
|
||||
return formatMetalAmountLabel(resolveMetalAmount(record))
|
||||
}
|
||||
|
||||
export function rialAmountLabel(record: AccountingSyncDocRecord): string {
|
||||
return formatRialAmountLabel(resolveRialAmount(record))
|
||||
}
|
||||
@@ -70,40 +70,86 @@ export function pargarCustomerLabel(record: PargarRecord): string {
|
||||
}
|
||||
|
||||
export function pargarAmountLabel(record: PargarRecord): string {
|
||||
if (record.amount != null && record.amount !== 0) {
|
||||
return formatPargarAmount(record.amount, record)
|
||||
}
|
||||
|
||||
const payload = parsePargarPayload(record.data)
|
||||
if (!payload?.transactions.length) return '-'
|
||||
|
||||
let maxGold = 0
|
||||
let maxRial = 0
|
||||
for (const tx of payload.transactions) {
|
||||
maxGold = Math.max(maxGold, Math.abs(parseNum(tx.bedGold)), Math.abs(parseNum(tx.besGold)))
|
||||
maxRial = Math.max(maxRial, Math.abs(parseNum(tx.bedRial)), Math.abs(parseNum(tx.besRial)))
|
||||
}
|
||||
|
||||
if (maxGold >= 0.01) return `${maxGold} گرم`
|
||||
if (maxRial >= 1) return `${maxRial.toLocaleString('fa-IR')} ریال`
|
||||
const metal = pargarMetalAmountLabel(record)
|
||||
const rial = pargarRialAmountLabel(record)
|
||||
if (metal !== '-' && rial !== '-') return `${metal} / ${rial}`
|
||||
if (metal !== '-') return metal
|
||||
if (rial !== '-') return rial
|
||||
return '-'
|
||||
}
|
||||
|
||||
function formatPargarAmount(amount: number, record: PargarRecord): string {
|
||||
function resolvePargarSummaryUnit(record: PargarRecord): 'rial' | 'gold' | null {
|
||||
try {
|
||||
const raw = record.data ? (JSON.parse(record.data) as { _summary?: { amount_unit?: string } }) : null
|
||||
const unit = raw?._summary?.amount_unit
|
||||
if (unit === 'rial' || unit === 'gold') return unit
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
if (record.type === 'wallet_cash') return 'rial'
|
||||
if (record.type === 'wallet_gold' || record.type === 'mande') return 'gold'
|
||||
return null
|
||||
}
|
||||
|
||||
function maxGoldFromPayload(record: PargarRecord): number {
|
||||
const payload = parsePargarPayload(record.data)
|
||||
let summaryUnit: string | undefined
|
||||
if (payload && '_summary' in (JSON.parse(record.data || '{}') as Record<string, unknown>)) {
|
||||
const raw = JSON.parse(record.data || '{}') as { _summary?: { amount_unit?: string } }
|
||||
summaryUnit = raw._summary?.amount_unit
|
||||
if (!payload?.transactions.length) return 0
|
||||
let maxGold = 0
|
||||
for (const tx of payload.transactions) {
|
||||
maxGold = Math.max(maxGold, Math.abs(parseNum(tx.bedGold)), Math.abs(parseNum(tx.besGold)))
|
||||
}
|
||||
return maxGold
|
||||
}
|
||||
|
||||
function maxRialFromPayload(record: PargarRecord): number {
|
||||
const payload = parsePargarPayload(record.data)
|
||||
if (!payload?.transactions.length) return 0
|
||||
let maxRial = 0
|
||||
for (const tx of payload.transactions) {
|
||||
maxRial = Math.max(maxRial, Math.abs(parseNum(tx.bedRial)), Math.abs(parseNum(tx.besRial)))
|
||||
}
|
||||
return maxRial
|
||||
}
|
||||
|
||||
/** ستون مقدار — فلز/گرم (نه ریال) */
|
||||
export function pargarMetalAmountLabel(record: PargarRecord): string {
|
||||
if (record.type === 'wallet_cash') return '-'
|
||||
|
||||
const unit = resolvePargarSummaryUnit(record)
|
||||
if (record.amount != null && record.amount !== 0) {
|
||||
if (unit === 'rial') {
|
||||
// amount ریالی است؛ فلز را از تراکنشها بگیر
|
||||
} else if (unit === 'gold' || unit == null) {
|
||||
if (unit === 'gold' || record.type !== 'document') {
|
||||
const n = Number(record.amount)
|
||||
if (Number.isFinite(n) && Math.abs(n) >= 0.000001) {
|
||||
return Math.abs(n - Math.round(n)) < 1e-9 ? String(Math.round(n)) : String(Number(n.toFixed(6)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (summaryUnit === 'rial' || record.type === 'wallet_cash') {
|
||||
return `${amount.toLocaleString('fa-IR')} ریال`
|
||||
const maxGold = maxGoldFromPayload(record)
|
||||
if (maxGold >= 0.01) {
|
||||
return Math.abs(maxGold - Math.round(maxGold)) < 1e-9
|
||||
? String(Math.round(maxGold))
|
||||
: String(Number(maxGold.toFixed(6)))
|
||||
}
|
||||
if (summaryUnit === 'gold' || record.type === 'wallet_gold' || record.type === 'mande') {
|
||||
return `${amount} گرم`
|
||||
return '-'
|
||||
}
|
||||
|
||||
/** ستون مبلغ ریالی */
|
||||
export function pargarRialAmountLabel(record: PargarRecord): string {
|
||||
const unit = resolvePargarSummaryUnit(record)
|
||||
if (record.amount != null && record.amount !== 0) {
|
||||
if (unit === 'rial' || record.type === 'wallet_cash') {
|
||||
return Math.round(Number(record.amount)).toLocaleString('fa-IR')
|
||||
}
|
||||
}
|
||||
return String(amount)
|
||||
|
||||
const maxRial = maxRialFromPayload(record)
|
||||
if (maxRial >= 1) return Math.round(maxRial).toLocaleString('fa-IR')
|
||||
return '-'
|
||||
}
|
||||
|
||||
export function pargarDescLabel(record: PargarRecord): string {
|
||||
|
||||
Reference in New Issue
Block a user