fix(shayegan): save customer accounting codes and align the document table
Shayegan was rejected as inactive when editing moshtari_code. The document list now uses the shared table format. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+3
-7
@@ -1,4 +1,5 @@
|
|||||||
import useAxios from '@core/utils/useAxios'
|
import useAxios from '@core/utils/useAxios'
|
||||||
|
import type { TableInput, TableOutput } from '@core/type/baseTable'
|
||||||
|
|
||||||
export interface ShayeganRecord {
|
export interface ShayeganRecord {
|
||||||
id?: number | null
|
id?: number | null
|
||||||
@@ -25,13 +26,8 @@ export const shayeganHealth = async () =>
|
|||||||
body?: unknown
|
body?: unknown
|
||||||
}>('/admin/shayegan/health')
|
}>('/admin/shayegan/health')
|
||||||
|
|
||||||
export const shayeganTable = async (params: Record<string, unknown>) =>
|
export const shayeganTable = async (params: TableInput) =>
|
||||||
await useAxios.post<{
|
await useAxios.post<TableOutput<ShayeganRecord>>('/admin/shayegan/table', params)
|
||||||
data: ShayeganRecord[]
|
|
||||||
total: number
|
|
||||||
current_page?: number
|
|
||||||
per_page?: number
|
|
||||||
}>('/admin/shayegan/table', params)
|
|
||||||
|
|
||||||
export const shayeganShow = async (id: number | string) =>
|
export const shayeganShow = async (id: number | string) =>
|
||||||
await useAxios.get<ShayeganRecord>(`/admin/shayegan/${id}`)
|
await useAxios.get<ShayeganRecord>(`/admin/shayegan/${id}`)
|
||||||
|
|||||||
@@ -1,110 +1,182 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full min-h-0 w-full flex-col gap-2">
|
<div class="min-w-0 w-full max-w-full overflow-x-hidden">
|
||||||
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between shrink-0">
|
<BasicShowContext title="اسناد شایگان" :help="help">
|
||||||
<div class="min-w-0">
|
<template #extra>
|
||||||
<div class="font-medium">اسناد شایگان</div>
|
<a-button size="small" :loading="healthLoading" @click="runHealth">تست اتصال</a-button>
|
||||||
<div class="text-xs opacity-70">
|
|
||||||
وضعیت ارسال به حسابگر. تست اتصال فقط خواندنی است و سند آزمایشی در CYPRSD04 نمیسازد.
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="grid grid-cols-2 gap-2 sm:flex sm:w-auto">
|
|
||||||
<a-button :loading="healthLoading" @click="runHealth">تست اتصال</a-button>
|
|
||||||
<a-button type="primary" :loading="loading" @click="fetch">بروزرسانی</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<a-alert
|
|
||||||
v-if="healthMessage"
|
|
||||||
:type="healthOk ? 'success' : 'error'"
|
|
||||||
:message="healthMessage"
|
|
||||||
show-icon
|
|
||||||
closable
|
|
||||||
class="shrink-0"
|
|
||||||
@close="healthMessage = ''"
|
|
||||||
/>
|
|
||||||
<a-table
|
|
||||||
class="min-h-0 flex-1"
|
|
||||||
size="small"
|
|
||||||
:loading="loading"
|
|
||||||
:columns="columns"
|
|
||||||
:data-source="rows"
|
|
||||||
:pagination="pagination"
|
|
||||||
row-key="id"
|
|
||||||
@change="onTableChange"
|
|
||||||
>
|
|
||||||
<template #bodyCell="{ column, record }">
|
|
||||||
<template v-if="column.key === 'sync_status'">
|
|
||||||
<a-tag :color="statusColor(record.sync_status)">{{ record.sync_status }}</a-tag>
|
|
||||||
</template>
|
|
||||||
<template v-else-if="column.key === 'actions'">
|
|
||||||
<a-button
|
|
||||||
size="small"
|
|
||||||
:disabled="record.sync_status === 'success'"
|
|
||||||
@click="retry(record)"
|
|
||||||
>
|
|
||||||
ارسال مجدد
|
|
||||||
</a-button>
|
|
||||||
</template>
|
|
||||||
</template>
|
</template>
|
||||||
</a-table>
|
|
||||||
|
<a-alert
|
||||||
|
v-if="healthMessage"
|
||||||
|
:type="healthOk ? 'success' : 'error'"
|
||||||
|
:message="healthMessage"
|
||||||
|
show-icon
|
||||||
|
closable
|
||||||
|
class="mb-2"
|
||||||
|
@close="healthMessage = ''"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="min-w-0 w-full max-w-full overflow-x-auto">
|
||||||
|
<BaseTable v-bind="tblProps" @register="submit">
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-tag :color="statusColor(record.sync_status)">{{ statusLabel(record.sync_status) }}</a-tag>
|
||||||
|
</template>
|
||||||
|
<template #ops="{ record }">
|
||||||
|
<div class="flex justify-center">
|
||||||
|
<a-tooltip
|
||||||
|
v-if="record.sync_status !== 'success'"
|
||||||
|
placement="bottom"
|
||||||
|
class="cursor-pointer"
|
||||||
|
@click="retry(record)"
|
||||||
|
>
|
||||||
|
<template #title><span>ارسال مجدد</span></template>
|
||||||
|
<Icon icon="vuesax-linear:send-2" color="#CA8A04" />
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</BaseTable>
|
||||||
|
</div>
|
||||||
|
</BasicShowContext>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, reactive, ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { Modal, message } from 'ant-design-vue'
|
import { Modal, message } from 'ant-design-vue'
|
||||||
|
import useBaseTable from '@core/components/BaseTable/hooks/useBaseTable'
|
||||||
|
import type { TableInput } from '@core/type/baseTable'
|
||||||
|
import Icon from '@core/components/BaseIcon/src/Icon.vue'
|
||||||
|
import { toJalali } from '@core/utils/useDataTime'
|
||||||
|
import { usePrice } from '@core/utils/usePrice'
|
||||||
import { shayeganHealth, shayeganRetry, shayeganTable } from '@/api/shayegan'
|
import { shayeganHealth, shayeganRetry, shayeganTable } from '@/api/shayegan'
|
||||||
|
|
||||||
const loading = ref(false)
|
const help =
|
||||||
|
'وضعیت ارسال به حسابگر. تست اتصال فقط خواندنی است و سند آزمایشی در CYPRSD04 نمیسازد.'
|
||||||
|
|
||||||
|
const TYPE_LABELS: Record<string, string> = {
|
||||||
|
customer: 'ثبت مشتری',
|
||||||
|
trade: 'معامله',
|
||||||
|
wallet_cash: 'کیف ریالی',
|
||||||
|
wallet_gold: 'کیف محصول'
|
||||||
|
}
|
||||||
|
|
||||||
|
const STATUS_LABELS: Record<string, string> = {
|
||||||
|
success: 'موفق',
|
||||||
|
failed: 'ناموفق',
|
||||||
|
timeout: 'تایماوت',
|
||||||
|
pending: 'در انتظار'
|
||||||
|
}
|
||||||
|
|
||||||
|
const [submit, reload] = useBaseTable()
|
||||||
const healthLoading = ref(false)
|
const healthLoading = ref(false)
|
||||||
const healthOk = ref(false)
|
const healthOk = ref(false)
|
||||||
const healthMessage = ref('')
|
const healthMessage = ref('')
|
||||||
const rows = ref<any[]>([])
|
|
||||||
const pagination = reactive({
|
|
||||||
current: 1,
|
|
||||||
pageSize: 20,
|
|
||||||
total: 0,
|
|
||||||
showSizeChanger: true
|
|
||||||
})
|
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{ title: 'شناسه', dataIndex: 'id', key: 'id', width: 70 },
|
{ title: 'شناسه', dataIndex: 'id', key: 'id', width: 80, type: 'Number', filterable: false, sortable: false },
|
||||||
{ title: 'نوع', dataIndex: 'type', key: 'type', width: 110 },
|
{
|
||||||
{ title: 'مبلغ/مقدار', dataIndex: 'amount', key: 'amount', width: 110 },
|
title: 'نوع',
|
||||||
{ title: 'وضعیت', dataIndex: 'sync_status', key: 'sync_status', width: 100 },
|
dataIndex: 'type',
|
||||||
{ title: 'شماره شایگان', dataIndex: 'shayegan_number', key: 'shayegan_number', width: 120 },
|
key: 'type',
|
||||||
{ title: 'توضیح', dataIndex: 'desc', key: 'desc' },
|
type: 'Text',
|
||||||
{ title: 'عملیات', key: 'actions', width: 120 }
|
customRender: (r: any) => typeLabel(r.record?.type)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'مبلغ/مقدار',
|
||||||
|
dataIndex: 'amount',
|
||||||
|
key: 'amount',
|
||||||
|
type: 'Number',
|
||||||
|
filterable: false,
|
||||||
|
customRender: (r: any) => (r.record?.amount == null || r.record?.amount === '' ? '-' : usePrice(r.record.amount, true, ''))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'وضعیت',
|
||||||
|
dataIndex: 'sync_status',
|
||||||
|
key: 'sync_status',
|
||||||
|
slot: 'status',
|
||||||
|
type: 'Text'
|
||||||
|
},
|
||||||
|
{ title: 'شماره شایگان', dataIndex: 'shayegan_number', key: 'shayegan_number', type: 'Text' },
|
||||||
|
{ title: 'توضیح', dataIndex: 'desc', key: 'desc', type: 'Text' },
|
||||||
|
{
|
||||||
|
title: 'زمان',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
key: 'created_at',
|
||||||
|
type: 'DateTime',
|
||||||
|
filterable: false,
|
||||||
|
customRender: (r: any) => (r.record?.created_at ? toJalali(r.record.created_at) : '-')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'عملیات',
|
||||||
|
dataIndex: 'ops',
|
||||||
|
key: 'ops',
|
||||||
|
slot: 'ops',
|
||||||
|
width: 110,
|
||||||
|
filterable: false,
|
||||||
|
sortable: false
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
function statusColor(status: string) {
|
function asTable(res: any, params: TableInput) {
|
||||||
|
if (res?.query) {
|
||||||
|
return {
|
||||||
|
rows: Array.isArray(res.rows) ? res.rows : [],
|
||||||
|
query: {
|
||||||
|
...params,
|
||||||
|
...res.query,
|
||||||
|
filters: res.query.filters ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const rows = Array.isArray(res)
|
||||||
|
? res
|
||||||
|
: Array.isArray(res?.data)
|
||||||
|
? res.data
|
||||||
|
: Array.isArray(res?.rows)
|
||||||
|
? res.rows
|
||||||
|
: []
|
||||||
|
return {
|
||||||
|
rows,
|
||||||
|
query: {
|
||||||
|
page: Number(res?.current_page ?? params.page ?? 1),
|
||||||
|
limit: Number(res?.per_page ?? params.limit ?? 20),
|
||||||
|
total: Number(res?.total ?? rows.length),
|
||||||
|
filters: params.filters ?? [],
|
||||||
|
search: params.search ?? '',
|
||||||
|
select: params.select ?? []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tblProps = {
|
||||||
|
columns,
|
||||||
|
defaultPageLimit: 20,
|
||||||
|
api: async (params: TableInput) => {
|
||||||
|
try {
|
||||||
|
return asTable(await shayeganTable(params), params)
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e?.message || 'خطا در دریافت اسناد شایگان')
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function typeLabel(type?: string | null) {
|
||||||
|
if (!type) return '-'
|
||||||
|
return TYPE_LABELS[type] ?? type
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusLabel(status?: string | null) {
|
||||||
|
if (!status) return '-'
|
||||||
|
return STATUS_LABELS[status] ?? status
|
||||||
|
}
|
||||||
|
|
||||||
|
function statusColor(status?: string) {
|
||||||
if (status === 'success') return 'green'
|
if (status === 'success') return 'green'
|
||||||
if (status === 'failed') return 'red'
|
if (status === 'failed') return 'red'
|
||||||
if (status === 'timeout') return 'orange'
|
if (status === 'timeout') return 'orange'
|
||||||
return 'blue'
|
return 'blue'
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetch() {
|
|
||||||
loading.value = true
|
|
||||||
try {
|
|
||||||
const res: any = await shayeganTable({
|
|
||||||
page: pagination.current,
|
|
||||||
per_page: pagination.pageSize
|
|
||||||
})
|
|
||||||
rows.value = res?.data ?? []
|
|
||||||
pagination.total = res?.total ?? 0
|
|
||||||
} catch (e: any) {
|
|
||||||
message.error(e?.message || 'خطا در دریافت اسناد شایگان')
|
|
||||||
} finally {
|
|
||||||
loading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onTableChange(pag: any) {
|
|
||||||
pagination.current = pag.current
|
|
||||||
pagination.pageSize = pag.pageSize
|
|
||||||
fetch()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runHealth() {
|
async function runHealth() {
|
||||||
healthLoading.value = true
|
healthLoading.value = true
|
||||||
healthMessage.value = ''
|
healthMessage.value = ''
|
||||||
@@ -127,11 +199,13 @@ function retry(record: any) {
|
|||||||
content: isTimeout
|
content: isTimeout
|
||||||
? 'این سند timeout شده. فقط اگر در حسابگر ثبت نشده، تأیید کنید.'
|
? 'این سند timeout شده. فقط اگر در حسابگر ثبت نشده، تأیید کنید.'
|
||||||
: 'سند دوباره به شایگان ارسال شود؟',
|
: 'سند دوباره به شایگان ارسال شود؟',
|
||||||
|
okText: 'ارسال',
|
||||||
|
cancelText: 'انصراف',
|
||||||
onOk: async () => {
|
onOk: async () => {
|
||||||
try {
|
try {
|
||||||
await shayeganRetry(record.id, isTimeout)
|
await shayeganRetry(record.id, isTimeout)
|
||||||
message.success('انجام شد')
|
message.success('انجام شد')
|
||||||
fetch()
|
await reload()
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
message.error(e?.response?.data?.message || e?.message || 'خطا')
|
message.error(e?.response?.data?.message || e?.message || 'خطا')
|
||||||
}
|
}
|
||||||
@@ -139,7 +213,9 @@ function retry(record: any) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(fetch)
|
async function fetch() {
|
||||||
|
await reload()
|
||||||
|
}
|
||||||
|
|
||||||
defineExpose({ fetch })
|
defineExpose({ fetch })
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -97,7 +97,9 @@ const visibleProxy = computed({
|
|||||||
function currentCodeOf(customer: Customer | null): string {
|
function currentCodeOf(customer: Customer | null): string {
|
||||||
if (!customer) return ''
|
if (!customer) return ''
|
||||||
if (props.provider === 'pargar') return displayAccountingCode(customer.pargar_account_id)
|
if (props.provider === 'pargar') return displayAccountingCode(customer.pargar_account_id)
|
||||||
if (props.provider === 'tahesab' || props.provider === 'zarin') return displayAccountingCode(customer.moshtari_code)
|
if (props.provider === 'tahesab' || props.provider === 'zarin' || props.provider === 'shayegan') {
|
||||||
|
return displayAccountingCode(customer.moshtari_code)
|
||||||
|
}
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +165,7 @@ async function onDecide(mode: 'overwrite' | 'keep' | 'push') {
|
|||||||
|
|
||||||
async function submit() {
|
async function submit() {
|
||||||
if (!props.customer?.id) return
|
if (!props.customer?.id) return
|
||||||
if (props.provider !== 'pargar' && props.provider !== 'tahesab' && props.provider !== 'zarin') {
|
if (props.provider !== 'pargar' && props.provider !== 'tahesab' && props.provider !== 'zarin' && props.provider !== 'shayegan') {
|
||||||
message.error('حسابداری فعال نیست.')
|
message.error('حسابداری فعال نیست.')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user