fix bug of pargar 4
This commit is contained in:
@@ -85,6 +85,38 @@ export const syncAllWalletsFromPargarChunk = async (params?: {
|
|||||||
force: params?.force ?? false,
|
force: params?.force ?? false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export interface StuckOutboundRow {
|
||||||
|
id: number
|
||||||
|
export_id: string
|
||||||
|
type: string
|
||||||
|
amount: number | null
|
||||||
|
desc: string
|
||||||
|
order_id: number | null
|
||||||
|
withdraw_id: number | null
|
||||||
|
customer_id: number | null
|
||||||
|
customer_name?: string | null
|
||||||
|
customer_mobile?: string | null
|
||||||
|
pargar_account_id?: string | number | null
|
||||||
|
created_at?: string | null
|
||||||
|
waited_minutes: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface StuckOutboundSummary {
|
||||||
|
count: number
|
||||||
|
customers: number
|
||||||
|
waited_minutes: number
|
||||||
|
oldest_at: string | null
|
||||||
|
last_confirmed_at: string | null
|
||||||
|
threshold_minutes: number
|
||||||
|
rows: StuckOutboundRow[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** اسناد پنل که پرگار هنوز تحویل نگرفته — ماندهٔ این مشتریها اعمال نمیشود */
|
||||||
|
export const getStuckOutbound = async (minutes?: number) =>
|
||||||
|
await useAxios.get<StuckOutboundSummary>('/admin/pargar/stuck-outbound', {
|
||||||
|
params: minutes === undefined ? undefined : { minutes },
|
||||||
|
})
|
||||||
|
|
||||||
export interface PanelOnlyCustomer {
|
export interface PanelOnlyCustomer {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dashboard-v2 box-border min-h-full w-full max-w-full overflow-x-hidden p-2 md:p-3">
|
<div class="dashboard-v2 box-border min-h-full w-full max-w-full overflow-x-hidden p-2 md:p-3">
|
||||||
<a-spin :spinning="loading">
|
<a-spin :spinning="loading">
|
||||||
|
<PargarStuckOutboundAlert v-if="pargarEnabled" compact class="mb-2" />
|
||||||
|
|
||||||
<!-- Hero -->
|
<!-- Hero -->
|
||||||
<section
|
<section
|
||||||
class="dashboard-hero rounded-xl bg-gradient-to-l from-[#0f766e] via-[#1e3a5f] to-[#0f172a] px-3 py-2.5 text-white shadow-md"
|
class="dashboard-hero rounded-xl bg-gradient-to-l from-[#0f766e] via-[#1e3a5f] to-[#0f172a] px-3 py-2.5 text-white shadow-md"
|
||||||
@@ -451,8 +453,9 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import DashboardLabelWithHelp from '@/pages/dashboard/components/DashboardLabelWithHelp.vue'
|
import DashboardLabelWithHelp from '@/pages/dashboard/components/DashboardLabelWithHelp.vue'
|
||||||
|
import PargarStuckOutboundAlert from '@/pages/menuPages/accounting/components/PargarStuckOutboundAlert.vue'
|
||||||
import DashboardQuickActionsBar from '@/pages/dashboard/components/DashboardQuickActionsBar.vue'
|
import DashboardQuickActionsBar from '@/pages/dashboard/components/DashboardQuickActionsBar.vue'
|
||||||
import DashboardQuickWalletModal from '@/pages/dashboard/components/DashboardQuickWalletModal.vue'
|
import DashboardQuickWalletModal from '@/pages/dashboard/components/DashboardQuickWalletModal.vue'
|
||||||
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
import DashboardSectionHeader from '@/pages/dashboard/components/DashboardSectionHeader.vue'
|
||||||
@@ -478,6 +481,13 @@ import { metalBalanceTitle, metalLabel } from '@/utils/adminMetalLabels'
|
|||||||
import { formatOrderServiceFee } from '@/utils/orderServiceFee'
|
import { formatOrderServiceFee } from '@/utils/orderServiceFee'
|
||||||
import { getMainMetal } from '@/utils/metalLabels'
|
import { getMainMetal } from '@/utils/metalLabels'
|
||||||
import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit'
|
import { TRADE_WEIGHT_UNIT_GRAM, TRADE_WEIGHT_UNIT_KILOGRAM } from '@/utils/weightUnit'
|
||||||
|
import { initAccountingSettingsFromApi, pargarEnabledSetting } from '@/utils/config'
|
||||||
|
|
||||||
|
const pargarEnabled = pargarEnabledSetting
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void initAccountingSettingsFromApi()
|
||||||
|
})
|
||||||
|
|
||||||
const copy = {
|
const copy = {
|
||||||
hero: dashboardHero,
|
hero: dashboardHero,
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<a-alert v-if="summary && summary.count > 0" type="warning" show-icon>
|
||||||
|
<template #message>
|
||||||
|
<div class="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||||
|
<span class="font-bold">{{ summary.count }} سند پنل هنوز در پرگار ثبت نشده</span>
|
||||||
|
<span v-if="summary.customers" class="text-xs text-gray-600">
|
||||||
|
ماندهٔ {{ summary.customers }} مشتری تا ثبت این اسناد روی کیف اعمال نمیشود
|
||||||
|
</span>
|
||||||
|
<a-button type="link" size="small" :loading="loading" @click="load">بروزرسانی</a-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #description>
|
||||||
|
<div class="flex flex-col gap-2 text-xs">
|
||||||
|
<div class="text-gray-600">
|
||||||
|
قدیمیترین سند {{ waitLabel(summary.waited_minutes) }} در صف است.
|
||||||
|
<template v-if="lastConfirmedLabel">
|
||||||
|
آخرین سندی که پرگار تحویل گرفته: {{ lastConfirmedLabel }}
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template v-if="!compact">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<div v-for="row in visibleRows" :key="row.id" class="stuck-row">
|
||||||
|
<span class="font-bold">{{ pargarTypeLabel(row.type) }} {{ row.export_id }}</span>
|
||||||
|
<span>{{ row.customer_name || 'بدون مشتری' }}</span>
|
||||||
|
<span dir="ltr">{{ amountLabel(row) }}</span>
|
||||||
|
<span class="text-gray-500">{{ waitLabel(row.waited_minutes) }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<a-button
|
||||||
|
v-if="summary.rows.length > PREVIEW_COUNT"
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
class="self-start"
|
||||||
|
@click="expanded = !expanded"
|
||||||
|
>
|
||||||
|
{{ expanded ? 'بستن لیست' : `${summary.rows.length - PREVIEW_COUNT} سند دیگر` }}
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<div class="text-gray-500">
|
||||||
|
پرگار این اسناد را با «دریافت اسناد» میگیرد؛ تا آن زمان ماندهٔ این مشتریها عمداً اعمال نمیشود.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-alert>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, onMounted, onUnmounted, ref } from 'vue'
|
||||||
|
import { getStuckOutbound, type StuckOutboundRow, type StuckOutboundSummary } from '@/api/pargar'
|
||||||
|
import { pargarTypeLabel } from '../utils/pargarDisplay'
|
||||||
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
|
||||||
|
const props = withDefaults(
|
||||||
|
defineProps<{
|
||||||
|
/** فقط خلاصه بدون لیست اسناد — برای داشبورد */
|
||||||
|
compact?: boolean
|
||||||
|
pollMs?: number
|
||||||
|
/** سندی که کمتر از این مدت در صف است هنوز «گیرکرده» نیست */
|
||||||
|
thresholdMinutes?: number
|
||||||
|
}>(),
|
||||||
|
{ compact: false, pollMs: 60000, thresholdMinutes: 15 }
|
||||||
|
)
|
||||||
|
|
||||||
|
const PREVIEW_COUNT = 5
|
||||||
|
|
||||||
|
const summary = ref<StuckOutboundSummary>()
|
||||||
|
const loading = ref(false)
|
||||||
|
const expanded = ref(false)
|
||||||
|
let timer: ReturnType<typeof setInterval> | undefined
|
||||||
|
|
||||||
|
const visibleRows = computed(() => {
|
||||||
|
const rows = summary.value?.rows ?? []
|
||||||
|
return expanded.value ? rows : rows.slice(0, PREVIEW_COUNT)
|
||||||
|
})
|
||||||
|
|
||||||
|
const lastConfirmedLabel = computed(() =>
|
||||||
|
summary.value?.last_confirmed_at ? toJalali(summary.value.last_confirmed_at) : ''
|
||||||
|
)
|
||||||
|
|
||||||
|
function waitLabel(minutes: number): string {
|
||||||
|
if (minutes < 60) return `${minutes} دقیقه`
|
||||||
|
const hours = Math.floor(minutes / 60)
|
||||||
|
if (hours < 24) return `${hours} ساعت`
|
||||||
|
return `${Math.floor(hours / 24)} روز`
|
||||||
|
}
|
||||||
|
|
||||||
|
function amountLabel(row: StuckOutboundRow): string {
|
||||||
|
if (row.amount == null) return '—'
|
||||||
|
if (row.type === 'wallet_cash') return usePrice(Math.abs(row.amount))
|
||||||
|
return `${Math.abs(row.amount).toFixed(4)} گرم`
|
||||||
|
}
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
try {
|
||||||
|
loading.value = true
|
||||||
|
summary.value = await getStuckOutbound(props.thresholdMinutes)
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
void load()
|
||||||
|
if (props.pollMs > 0) {
|
||||||
|
timer = setInterval(() => void load(), props.pollMs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
if (timer) clearInterval(timer)
|
||||||
|
})
|
||||||
|
|
||||||
|
defineExpose({ load })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.stuck-row {
|
||||||
|
@apply flex flex-wrap items-center gap-x-3 gap-y-1 rounded-lg bg-white/60 px-2 py-1;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -5,6 +5,7 @@
|
|||||||
class="flex h-full w-full min-w-0 max-w-full flex-col overflow-x-hidden"
|
class="flex h-full w-full min-w-0 max-w-full flex-col overflow-x-hidden"
|
||||||
:class="activeTab === 'customer-sync' || activeTab === 'pargar-customer-sync' || activeTab === 'zarin-customer-sync' ? 'overflow-y-auto' : 'overflow-hidden'"
|
:class="activeTab === 'customer-sync' || activeTab === 'pargar-customer-sync' || activeTab === 'zarin-customer-sync' ? 'overflow-y-auto' : 'overflow-hidden'"
|
||||||
>
|
>
|
||||||
|
<PargarStuckOutboundAlert v-if="pargarEnabled" class="mb-3 shrink-0" />
|
||||||
<ATabs v-model:activeKey="activeTab" class="mb-3 shrink-0">
|
<ATabs v-model:activeKey="activeTab" class="mb-3 shrink-0">
|
||||||
<ATabPane key="reports" tab="گزارشات" />
|
<ATabPane key="reports" tab="گزارشات" />
|
||||||
<ATabPane v-if="tahesabEnabled" key="tahesab" tab="اسناد تهحساب" />
|
<ATabPane v-if="tahesabEnabled" key="tahesab" tab="اسناد تهحساب" />
|
||||||
@@ -155,6 +156,7 @@ import zarinTbl from './components/zarinTable.vue'
|
|||||||
import zarinCustomerSync from './components/ZarinCustomerSync.vue'
|
import zarinCustomerSync from './components/ZarinCustomerSync.vue'
|
||||||
import pargarTbl from './components/pargarTable.vue'
|
import pargarTbl from './components/pargarTable.vue'
|
||||||
import pargarCustomerSync from './components/PargarCustomerSync.vue'
|
import pargarCustomerSync from './components/PargarCustomerSync.vue'
|
||||||
|
import PargarStuckOutboundAlert from './components/PargarStuckOutboundAlert.vue'
|
||||||
import type { FormInstance } from 'ant-design-vue'
|
import type { FormInstance } from 'ant-design-vue'
|
||||||
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
import BasicInfoModal from '@/components/BasicInfoModal/index.vue'
|
||||||
import { message } from 'ant-design-vue/es'
|
import { message } from 'ant-design-vue/es'
|
||||||
|
|||||||
Reference in New Issue
Block a user