diff --git a/src/pages/menuPages/accounting/components/PargarMobileDetailSheet.vue b/src/pages/menuPages/accounting/components/PargarMobileDetailSheet.vue index 9d5e2d9..0c66b9b 100644 --- a/src/pages/menuPages/accounting/components/PargarMobileDetailSheet.vue +++ b/src/pages/menuPages/accounting/components/PargarMobileDetailSheet.vue @@ -24,17 +24,40 @@ - + - - - + + + + + +
+
جزئیات اسناد
+
+ + + + +
+
+
emit('update:visible', value) }) -const sheetTitle = computed(() => props.item?.customer?.name || 'سند پرگار') +const sheetTitle = computed(() => + props.item ? pargarCustomerLabel(props.item) : 'سند پرگار' +) + +const parsedPayload = computed(() => parsePargarPayload(props.item?.data)) function canRetry(row: PargarRecord) { return canRetryPargar(row) diff --git a/src/pages/menuPages/accounting/components/PargarMobileListItem.vue b/src/pages/menuPages/accounting/components/PargarMobileListItem.vue index 2b92f1f..459d9d7 100644 --- a/src/pages/menuPages/accounting/components/PargarMobileListItem.vue +++ b/src/pages/menuPages/accounting/components/PargarMobileListItem.vue @@ -8,8 +8,8 @@
- نوع: {{ item.type || '-' }} - مقدار: {{ item.amount ?? '-' }} + نوع: {{ pargarTypeLabel(item.type) }} + مقدار: {{ pargarAmountLabel(item) }}
{{ createdAtLabel }}
@@ -21,6 +21,11 @@ import type { PargarRecord } from '@/api/pargar' import MobileCard from '@/components/mobile/MobileCard.vue' import { toJalali } from '@core/utils/useDataTime' import { pargarStatusLabel } from '@/pages/menuPages/accounting/utils/pargarStatus' +import { + pargarAmountLabel, + pargarCustomerLabel, + pargarTypeLabel +} from '@/pages/menuPages/accounting/utils/pargarDisplay' const props = defineProps<{ item: PargarRecord @@ -30,7 +35,7 @@ defineEmits<{ (e: 'open', item: PargarRecord): void }>() -const customerName = computed(() => props.item.customer?.name || '-') +const customerName = computed(() => pargarCustomerLabel(props.item)) const createdAtLabel = computed(() => props.item.created_at ? toJalali(props.item.created_at) : '-' ) diff --git a/src/pages/menuPages/accounting/components/pargarTable.vue b/src/pages/menuPages/accounting/components/pargarTable.vue index 094ff36..5130a4d 100644 --- a/src/pages/menuPages/accounting/components/pargarTable.vue +++ b/src/pages/menuPages/accounting/components/pargarTable.vue @@ -56,6 +56,41 @@
{{ selected.res }}
+
+
جزئیات اسناد دریافتی از پرگار
+
+
+ شرح + {{ tx.desc || tx.docType || '-' }} +
+
+ تاریخ + {{ tx.date || '-' }} +
+
+ شماره سند + {{ tx.docId || '-' }} +
+
+ مبالغ + {{ formatTransactionAmount(tx) }} +
+
+ مانده + + {{ tx.remainGold ? `${tx.remainGold} گرم` : '' }} + {{ tx.remainRial ? `${tx.remainRial} ریال` : '' }} + +
+
+
+
+ {{ selected.data }} +
{{ selected.sync_log }}
@@ -92,6 +127,14 @@ import BasicModal from '@/components/BasicModal.vue' import PargarRetryModal from '@/components/PargarRetryModal.vue' import PargarMobileCardList from '@/pages/menuPages/accounting/components/PargarMobileCardList.vue' import { canRetryPargar, pargarStatusLabel } from '@/pages/menuPages/accounting/utils/pargarStatus' +import { + formatTransactionAmount, + parsePargarPayload, + pargarAmountLabel, + pargarCustomerLabel, + pargarDescLabel, + pargarTypeLabel +} from '@/pages/menuPages/accounting/utils/pargarDisplay' import { toJalali } from '@core/utils/useDataTime' const [submit, reload] = useBaseTable() @@ -110,10 +153,10 @@ const columns = [ sortable: false, customRender: (r: any) => r.index + 1 }, - { title: 'مشتری', dataIndex: 'customer.name', key: 'customer.name', type: 'Text' }, - { title: 'نوع', dataIndex: 'type', key: 'type', type: 'Text' }, - { title: 'مقدار', dataIndex: 'amount', key: 'amount', type: 'Number' }, - { title: 'شرح', dataIndex: 'desc', key: 'desc', type: 'Text' }, + { title: 'مشتری', dataIndex: 'customer.name', key: 'customer.name', type: 'Text', customRender: (r: any) => pargarCustomerLabel(r.record) }, + { title: 'نوع', dataIndex: 'type', key: 'type', type: 'Text', customRender: (r: any) => pargarTypeLabel(r.record.type) }, + { title: 'مقدار', dataIndex: 'amount', key: 'amount', type: 'Number', customRender: (r: any) => pargarAmountLabel(r.record) }, + { title: 'شرح', dataIndex: 'desc', key: 'desc', type: 'Text', customRender: (r: any) => pargarDescLabel(r.record) }, { title: 'وضعیت', dataIndex: 'sync_status', @@ -143,23 +186,28 @@ const tblProps = ref({ api: (params: TableInput) => getPargarTable(params) }) -/** اطلاعاتی که برای هر سند حسابداری نیاز است (بدون جزئیات پرگار‌محور) */ +/** اطلاعاتی که برای هر سند حسابداری نیاز است */ const infoItems = computed(() => { const row = selected.value if (!row) return [] + const payload = parsePargarPayload(row.data) return [ { label: 'شناسه سند', value: row.id }, - { label: 'مشتری', value: row.customer?.name }, - { label: 'موبایل', value: row.customer?.mobile }, - { label: 'نوع', value: row.type }, - { label: 'مقدار', value: row.amount }, - { label: 'شرح', value: row.desc }, + { label: 'مشتری', value: pargarCustomerLabel(row) }, + { label: 'موبایل', value: row.customer?.mobile || '-' }, + { label: 'کد حساب پرگار', value: payload?.accountId || row.pargar_ref || '-' }, + { label: 'نوع', value: pargarTypeLabel(row.type) }, + { label: 'مقدار', value: pargarAmountLabel(row) }, + { label: 'شرح', value: pargarDescLabel(row) }, + { label: 'تعداد سطر سند', value: payload?.transactions.length ? String(payload.transactions.length) : '-' }, { label: 'زمان ثبت', value: row.created_at ? toJalali(row.created_at) : '-' }, { label: 'وضعیت', value: pargarStatusLabel(row.sync_status, row.pargar_ref, row.desc) }, { label: 'کد پرگار', value: row.pargar_ref || 'ثبت نشده' } ] }) +const parsedPayload = computed(() => parsePargarPayload(selected.value?.data)) + function openInfo(record: PargarRecord) { selected.value = record infoVisible.value = true diff --git a/src/pages/menuPages/accounting/utils/pargarDisplay.ts b/src/pages/menuPages/accounting/utils/pargarDisplay.ts new file mode 100644 index 0000000..3362d32 --- /dev/null +++ b/src/pages/menuPages/accounting/utils/pargarDisplay.ts @@ -0,0 +1,153 @@ +import type { PargarRecord } from '@/api/pargar' + +export interface PargarTransactionRow { + docId?: string + date?: string + docType?: string + desc?: string + by?: string + bedGold?: number | string + besGold?: number | string + bedRial?: number | string + besRial?: number | string + remainGold?: number | string + remainRial?: number | string +} + +export interface PargarParsedPayload { + accountId?: string + accountName?: string + transactions: PargarTransactionRow[] +} + +const TYPE_LABELS: Record = { + document: 'سند ورودی از پرگار', + order: 'سفارش', + mande: 'مانده حساب', + customer: 'ثبت مشتری', + wallet_cash: 'کیف ریالی', + wallet_gold: 'کیف طلا' +} + +function parseNum(value: unknown): number { + if (value == null || value === '') return 0 + const n = Number(String(value).replace('/', '.')) + return Number.isFinite(n) ? n : 0 +} + +export function pargarTypeLabel(type?: string | null): string { + if (!type) return '-' + return TYPE_LABELS[type] ?? type +} + +export function parsePargarPayload(data?: string | null): PargarParsedPayload | null { + if (!data?.trim()) return null + try { + const raw = JSON.parse(data) as Record + let transactions: PargarTransactionRow[] = [] + if (Array.isArray(raw.transactions)) { + transactions = raw.transactions as PargarTransactionRow[] + } else if (raw.transaction && typeof raw.transaction === 'object') { + transactions = [raw.transaction as PargarTransactionRow] + } + + return { + accountId: raw.accountId != null ? String(raw.accountId) : undefined, + accountName: raw.accountName != null ? String(raw.accountName) : undefined, + transactions + } + } catch { + return null + } +} + +export function pargarCustomerLabel(record: PargarRecord): string { + if (record.customer?.name?.trim()) return record.customer.name.trim() + const payload = parsePargarPayload(record.data) + if (payload?.accountName?.trim()) return payload.accountName.trim() + if (record.pargar_ref) return `کد پرگار ${record.pargar_ref}` + return '-' +} + +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')} ریال` + return '-' +} + +function formatPargarAmount(amount: number, record: PargarRecord): string { + const payload = parsePargarPayload(record.data) + let summaryUnit: string | undefined + if (payload && '_summary' in (JSON.parse(record.data || '{}') as Record)) { + const raw = JSON.parse(record.data || '{}') as { _summary?: { amount_unit?: string } } + summaryUnit = raw._summary?.amount_unit + } + + if (summaryUnit === 'rial' || record.type === 'wallet_cash') { + return `${amount.toLocaleString('fa-IR')} ریال` + } + if (summaryUnit === 'gold' || record.type === 'wallet_gold' || record.type === 'mande') { + return `${amount} گرم` + } + return String(amount) +} + +export function pargarDescLabel(record: PargarRecord): string { + const desc = record.desc?.trim() + if (desc && desc !== 'دریافت اسناد از پرگار') return desc + + try { + const raw = record.data ? JSON.parse(record.data) : null + if (raw?.wallet_applied === false && raw?.skip_reason === 'echo_outbound') { + return 'سند پرگار — از پنل ارسال شده (بدون اعمال مجدد)' + } + if (raw?.wallet_applied === true) { + return desc || 'اعمال‌شده روی کیف' + } + } catch { + // ignore + } + + const payload = parsePargarPayload(record.data) + if (!payload?.transactions.length) return desc || '-' + + const lines = payload.transactions + .map((tx) => tx.desc?.trim() || tx.docType?.trim() || '') + .filter(Boolean) + + if (lines.length) { + const head = lines.slice(0, 2).join(' | ') + return lines.length > 2 ? `${head} (+${lines.length - 2} سند دیگر)` : head + } + + return desc || '-' +} + +export function formatTransactionAmount(tx: PargarTransactionRow): string { + const parts: string[] = [] + const bedGold = parseNum(tx.bedGold) + const besGold = parseNum(tx.besGold) + const bedRial = parseNum(tx.bedRial) + const besRial = parseNum(tx.besRial) + + if (bedGold >= 0.01) parts.push(`بدهکار طلا: ${bedGold}`) + if (besGold >= 0.01) parts.push(`بستانکار طلا: ${besGold}`) + if (bedRial >= 1) parts.push(`بدهکار ریال: ${bedRial.toLocaleString('fa-IR')}`) + if (besRial >= 1) parts.push(`بستانکار ریال: ${besRial.toLocaleString('fa-IR')}`) + + return parts.length ? parts.join(' — ') : '-' +} diff --git a/src/pages/menuPages/configuration/settingGroups.ts b/src/pages/menuPages/configuration/settingGroups.ts index b9c8531..efbd644 100644 --- a/src/pages/menuPages/configuration/settingGroups.ts +++ b/src/pages/menuPages/configuration/settingGroups.ts @@ -119,7 +119,7 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [ title: 'عمومی و برند', icon: 'vuesax-linear:home-2', match: (k) => - /^(name_fa|name_en|app_url|admin_url|apk_download_link|permision__enabled|bace_url|server_url|logo_|title_pwa|title_fav|metal_type|main_metal|unit$|open_register|auto_active_customer|store_status|key$|is_store|group_sms|customer_direct_sms|has_page_earn_money|sms_panel|main_wallet_products|require_zero_main_gold_before_other_buy)/.test( + /^(name_fa|name_en|app_url|admin_url|apk_download_link|permision__enabled|bace_url|server_url|logo_|title_pwa|title_fav|metal_type|main_metal|unit$|open_register|auto_active_customer|store_status|key$|is_store|group_sms|customer_direct_sms|has_page_earn_money|sms_panel|main_wallet_products)/.test( k ) }, diff --git a/src/pages/menuPages/configuration/settingTypes.ts b/src/pages/menuPages/configuration/settingTypes.ts index 7e369a9..487e7cf 100644 --- a/src/pages/menuPages/configuration/settingTypes.ts +++ b/src/pages/menuPages/configuration/settingTypes.ts @@ -864,7 +864,6 @@ export const SETTING_LOCKED_KEYS = new Set([ 'sms_panel', 'logo_login', 'logo_register', 'title_pwa', 'title_fav', 'main_product', 'main_wallet_products', 'metal_type', 'main_metal', - 'require_zero_main_gold_before_other_buy', 'montify_name', 'montzar_base_url', 'montzar_closed', 'montzar_diff', 'montzar_last_product_log', 'montzar_live_price', 'montzar_mazaneh', @@ -895,6 +894,7 @@ export const SETTING_EDITABLE_KEYS = new Set([ 'name_fa', 'auto_active_customer', 'balance_report_exclude_customer_id', 'buy_fee', 'charge_notif', 'close_price', + 'require_zero_main_gold_before_other_buy', 'sms_notif_order_admin', 'sms_notif_order_customer', 'sms_notif_withdraw_admin', 'sms_notif_withdraw_otp', 'sms_notif_login_otp', 'sms_notif_gateway_charge_admin', 'sms_notif_register_customer', 'sms_notif_register_admin',