feat(accounting): Shayegan docs tab and fixed provider UI
Show Shayegan sync table/health in admin and always treat boursecu accounting as Shayegan. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
import request from '@/utils/http'
|
||||||
|
|
||||||
|
export function shayeganHealth() {
|
||||||
|
return request.get({ url: '/admin/shayegan/health' })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shayeganTable(data: Record<string, unknown>) {
|
||||||
|
return request.post({ url: '/admin/shayegan/table', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shayeganShow(id: number | string) {
|
||||||
|
return request.get({ url: `/admin/shayegan/${id}` })
|
||||||
|
}
|
||||||
|
|
||||||
|
export function shayeganRetry(id: number | string, force = false) {
|
||||||
|
return request.post({ url: `/admin/shayegan/${id}/retry`, data: { force } })
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex h-full min-h-0 w-full flex-col gap-2">
|
||||||
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between shrink-0">
|
||||||
|
<div class="min-w-0">
|
||||||
|
<div class="font-medium">اسناد شایگان</div>
|
||||||
|
<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>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onMounted, reactive, ref } from 'vue'
|
||||||
|
import { Modal, message } from 'ant-design-vue'
|
||||||
|
import { shayeganHealth, shayeganRetry, shayeganTable } from '@/api/shayegan'
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const healthLoading = ref(false)
|
||||||
|
const healthOk = ref(false)
|
||||||
|
const healthMessage = ref('')
|
||||||
|
const rows = ref<any[]>([])
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 20,
|
||||||
|
total: 0,
|
||||||
|
showSizeChanger: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ title: 'شناسه', dataIndex: 'id', key: 'id', width: 70 },
|
||||||
|
{ title: 'نوع', dataIndex: 'type', key: 'type', width: 110 },
|
||||||
|
{ title: 'مبلغ/مقدار', dataIndex: 'amount', key: 'amount', width: 110 },
|
||||||
|
{ title: 'وضعیت', dataIndex: 'sync_status', key: 'sync_status', width: 100 },
|
||||||
|
{ title: 'شماره شایگان', dataIndex: 'shayegan_number', key: 'shayegan_number', width: 120 },
|
||||||
|
{ title: 'توضیح', dataIndex: 'desc', key: 'desc' },
|
||||||
|
{ title: 'عملیات', key: 'actions', width: 120 }
|
||||||
|
]
|
||||||
|
|
||||||
|
function statusColor(status: string) {
|
||||||
|
if (status === 'success') return 'green'
|
||||||
|
if (status === 'failed') return 'red'
|
||||||
|
if (status === 'timeout') return 'orange'
|
||||||
|
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() {
|
||||||
|
healthLoading.value = true
|
||||||
|
healthMessage.value = ''
|
||||||
|
try {
|
||||||
|
const res: any = await shayeganHealth()
|
||||||
|
healthOk.value = !!res?.ok
|
||||||
|
healthMessage.value = res?.message || (res?.ok ? 'اتصال برقرار است' : 'اتصال ناموفق')
|
||||||
|
} catch (e: any) {
|
||||||
|
healthOk.value = false
|
||||||
|
healthMessage.value = e?.response?.data?.message || e?.message || 'خطای تست اتصال'
|
||||||
|
} finally {
|
||||||
|
healthLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function retry(record: any) {
|
||||||
|
const isTimeout = record.sync_status === 'timeout'
|
||||||
|
Modal.confirm({
|
||||||
|
title: 'ارسال مجدد به شایگان',
|
||||||
|
content: isTimeout
|
||||||
|
? 'این سند timeout شده. فقط اگر در حسابگر ثبت نشده، تأیید کنید.'
|
||||||
|
: 'سند دوباره به شایگان ارسال شود؟',
|
||||||
|
onOk: async () => {
|
||||||
|
try {
|
||||||
|
await shayeganRetry(record.id, isTimeout)
|
||||||
|
message.success('انجام شد')
|
||||||
|
fetch()
|
||||||
|
} catch (e: any) {
|
||||||
|
message.error(e?.response?.data?.message || e?.message || 'خطا')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(fetch)
|
||||||
|
|
||||||
|
defineExpose({ fetch })
|
||||||
|
</script>
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
<ATabPane v-if="pargarEnabled" key="pargar" tab="اسناد پرگار" />
|
<ATabPane v-if="pargarEnabled" key="pargar" tab="اسناد پرگار" />
|
||||||
<ATabPane v-if="pargarEnabled" key="pargar-customer-sync" tab="همگام سازی مشتریان در پرگار" />
|
<ATabPane v-if="pargarEnabled" key="pargar-customer-sync" tab="همگام سازی مشتریان در پرگار" />
|
||||||
<ATabPane v-if="pargarEnabled" key="pargar-wallet-sync" tab="مانده کیف پرگار" />
|
<ATabPane v-if="pargarEnabled" key="pargar-wallet-sync" tab="مانده کیف پرگار" />
|
||||||
|
<ATabPane v-if="shayeganEnabled" key="shayegan" tab="اسناد شایگان" />
|
||||||
</ATabs>
|
</ATabs>
|
||||||
<tbl v-if="activeTab === 'reports'" ref="table" class="min-h-0 flex-1" />
|
<tbl v-if="activeTab === 'reports'" ref="table" class="min-h-0 flex-1" />
|
||||||
<tahesabTbl v-else-if="tahesabEnabled && activeTab === 'tahesab'" ref="tahesabTable" class="min-h-0 flex-1" />
|
<tahesabTbl v-else-if="tahesabEnabled && activeTab === 'tahesab'" ref="tahesabTable" class="min-h-0 flex-1" />
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
<pargarTbl v-else-if="pargarEnabled && activeTab === 'pargar'" ref="pargarTable" class="min-h-0 flex-1" />
|
<pargarTbl v-else-if="pargarEnabled && activeTab === 'pargar'" ref="pargarTable" class="min-h-0 flex-1" />
|
||||||
<pargarCustomerSync v-else-if="pargarEnabled && activeTab === 'pargar-customer-sync'" class="shrink-0" />
|
<pargarCustomerSync v-else-if="pargarEnabled && activeTab === 'pargar-customer-sync'" class="shrink-0" />
|
||||||
<pargarWalletSync v-else-if="pargarEnabled && activeTab === 'pargar-wallet-sync'" class="shrink-0" />
|
<pargarWalletSync v-else-if="pargarEnabled && activeTab === 'pargar-wallet-sync'" class="shrink-0" />
|
||||||
|
<shayeganTbl v-else-if="shayeganEnabled && activeTab === 'shayegan'" ref="shayeganTable" class="min-h-0 flex-1" />
|
||||||
</div>
|
</div>
|
||||||
</BasicMainCard>
|
</BasicMainCard>
|
||||||
<BasicModal
|
<BasicModal
|
||||||
@@ -162,6 +164,7 @@ import tahesabWalletSync from './components/TahesabWalletBulkSync.vue'
|
|||||||
import zarinTbl from './components/zarinTable.vue'
|
import zarinTbl from './components/zarinTable.vue'
|
||||||
import zarinCustomerSync from './components/ZarinCustomerSync.vue'
|
import zarinCustomerSync from './components/ZarinCustomerSync.vue'
|
||||||
import zarinWalletSync from './components/ZarinWalletBulkSync.vue'
|
import zarinWalletSync from './components/ZarinWalletBulkSync.vue'
|
||||||
|
import shayeganTbl from './components/shayeganTable.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 pargarWalletSync from './components/PargarWalletBulkSync.vue'
|
import pargarWalletSync from './components/PargarWalletBulkSync.vue'
|
||||||
@@ -183,15 +186,16 @@ import type { last } from '@/model/price'
|
|||||||
import { createOrder } from '@/api/order'
|
import { createOrder } from '@/api/order'
|
||||||
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
import { getCashBalance, getGoldBalance } from '@/components/mobile/utils/wallet'
|
||||||
import { metalLabel } from '@/utils/adminMetalLabels'
|
import { metalLabel } from '@/utils/adminMetalLabels'
|
||||||
import { initAccountingSettingsFromApi, tahesabEnabledSetting, zarinEnabledSetting, pargarEnabledSetting } from '@/utils/config'
|
import { initAccountingSettingsFromApi, tahesabEnabledSetting, zarinEnabledSetting, pargarEnabledSetting, shayeganEnabledSetting } from '@/utils/config'
|
||||||
|
|
||||||
//loading
|
//loading
|
||||||
const loading = ref<boolean>(false)
|
const loading = ref<boolean>(false)
|
||||||
const submitError = ref('')
|
const submitError = ref('')
|
||||||
const activeTab = ref<'reports' | 'tahesab' | 'customer-sync' | 'tahesab-wallet-sync' | 'zarin' | 'zarin-customer-sync' | 'zarin-wallet-sync' | 'pargar' | 'pargar-customer-sync' | 'pargar-wallet-sync'>('reports')
|
const activeTab = ref<'reports' | 'tahesab' | 'customer-sync' | 'tahesab-wallet-sync' | 'zarin' | 'zarin-customer-sync' | 'zarin-wallet-sync' | 'pargar' | 'pargar-customer-sync' | 'pargar-wallet-sync' | 'shayegan'>('reports')
|
||||||
const tahesabEnabled = tahesabEnabledSetting
|
const tahesabEnabled = tahesabEnabledSetting
|
||||||
const zarinEnabled = zarinEnabledSetting
|
const zarinEnabled = zarinEnabledSetting
|
||||||
const pargarEnabled = pargarEnabledSetting
|
const pargarEnabled = pargarEnabledSetting
|
||||||
|
const shayeganEnabled = shayeganEnabledSetting
|
||||||
|
|
||||||
//define store
|
//define store
|
||||||
const store = useStaticApiListsStore()
|
const store = useStaticApiListsStore()
|
||||||
|
|||||||
@@ -265,6 +265,12 @@ export const SYSTEM_SETTING_GROUPS: SettingGroupDef[] = [
|
|||||||
icon: 'vuesax-linear:document-text',
|
icon: 'vuesax-linear:document-text',
|
||||||
match: (k) => k.startsWith('pargar_') || k === 'send_to_pargar'
|
match: (k) => k.startsWith('pargar_') || k === 'send_to_pargar'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'accounting_shayegan',
|
||||||
|
title: 'حسابداری شایگان',
|
||||||
|
icon: 'vuesax-linear:receipt-item',
|
||||||
|
match: (k) => k.startsWith('shayegan_')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'price_channel',
|
id: 'price_channel',
|
||||||
title: 'ارسال قیمت به کانال',
|
title: 'ارسال قیمت به کانال',
|
||||||
|
|||||||
+19
-15
@@ -1,7 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { setCachedMainMetal } from '@/utils/metalLabels'
|
import { setCachedMainMetal } from '@/utils/metalLabels'
|
||||||
|
|
||||||
export type AccountingProvider = 'none' | 'pargar' | 'tahesab' | 'zarin'
|
export type AccountingProvider = 'none' | 'pargar' | 'tahesab' | 'zarin' | 'shayegan'
|
||||||
|
|
||||||
export function getConfigApp(key: string) {
|
export function getConfigApp(key: string) {
|
||||||
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
const config = window.__APP_CONFIG__ ? window.__APP_CONFIG__.user : {}
|
||||||
@@ -47,6 +47,9 @@ export const zarinEnabledSetting = ref(false)
|
|||||||
/** reactive — نمایش UI پرگار در حسابداری و ... */
|
/** reactive — نمایش UI پرگار در حسابداری و ... */
|
||||||
export const pargarEnabledSetting = ref(false)
|
export const pargarEnabledSetting = ref(false)
|
||||||
|
|
||||||
|
/** reactive — نمایش UI شایگان در حسابداری و ... */
|
||||||
|
export const shayeganEnabledSetting = ref(false)
|
||||||
|
|
||||||
/** reactive — دکمه پیامک تکی در لیست مشتریان */
|
/** reactive — دکمه پیامک تکی در لیست مشتریان */
|
||||||
export const customerDirectSmsEnabledSetting = ref(false)
|
export const customerDirectSmsEnabledSetting = ref(false)
|
||||||
|
|
||||||
@@ -100,6 +103,7 @@ export const accountingProviderSetting = ref<AccountingProvider>('none')
|
|||||||
let tahesabEnabledFromSettings: boolean | null = null
|
let tahesabEnabledFromSettings: boolean | null = null
|
||||||
let zarinEnabledFromSettings: boolean | null = null
|
let zarinEnabledFromSettings: boolean | null = null
|
||||||
let pargarEnabledFromSettings: boolean | null = null
|
let pargarEnabledFromSettings: boolean | null = null
|
||||||
|
let shayeganEnabledFromSettings: boolean | null = null
|
||||||
let customerDirectSmsFromSettings: boolean | null = null
|
let customerDirectSmsFromSettings: boolean | null = null
|
||||||
let accountingProviderFromSettings: AccountingProvider | null = null
|
let accountingProviderFromSettings: AccountingProvider | null = null
|
||||||
|
|
||||||
@@ -113,25 +117,15 @@ function isSwitchOn(value: string | number | boolean | undefined | null): boolea
|
|||||||
|
|
||||||
function normalizeAccountingProvider(value: string | undefined | null): AccountingProvider {
|
function normalizeAccountingProvider(value: string | undefined | null): AccountingProvider {
|
||||||
const v = (value ?? '').trim().toLowerCase()
|
const v = (value ?? '').trim().toLowerCase()
|
||||||
if (v === 'pargar' || v === 'tahesab' || v === 'zarin') return v
|
if (v === 'pargar' || v === 'tahesab' || v === 'zarin' || v === 'shayegan') return v
|
||||||
return 'none'
|
return 'none'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** بورس مس: UI حسابداری همیشه شایگان — settings/legacy نادیده گرفته میشود. */
|
||||||
function resolveAccountingProviderFromRows(
|
function resolveAccountingProviderFromRows(
|
||||||
list: Array<{ key?: string; value?: string | null }>
|
_list: Array<{ key?: string; value?: string | null }>
|
||||||
): AccountingProvider {
|
): AccountingProvider {
|
||||||
const row = list.find((item) => item.key === 'accounting_provider')
|
return 'shayegan'
|
||||||
if (row?.value !== undefined && row?.value !== null && String(row.value).trim() !== '') {
|
|
||||||
return normalizeAccountingProvider(row.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
const pargarLegacy = list.find((item) => item.key === 'send_to_pargar')
|
|
||||||
if (pargarLegacy && isSwitchOn(pargarLegacy.value)) return 'pargar'
|
|
||||||
|
|
||||||
const tahesabLegacy = list.find((item) => item.key === 'send_to_tahesab')
|
|
||||||
if (tahesabLegacy && isSwitchOn(tahesabLegacy.value)) return 'tahesab'
|
|
||||||
|
|
||||||
return 'none'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyAccountingProviderFromRows(
|
export function applyAccountingProviderFromRows(
|
||||||
@@ -143,6 +137,7 @@ export function applyAccountingProviderFromRows(
|
|||||||
setTahesabEnabledFromSettings(provider === 'tahesab')
|
setTahesabEnabledFromSettings(provider === 'tahesab')
|
||||||
setZarinEnabledFromSettings(provider === 'zarin')
|
setZarinEnabledFromSettings(provider === 'zarin')
|
||||||
setPargarEnabledFromSettings(provider === 'pargar')
|
setPargarEnabledFromSettings(provider === 'pargar')
|
||||||
|
setShayeganEnabledFromSettings(provider === 'shayegan')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setStoreModeFromSettings(value: boolean): void {
|
export function setStoreModeFromSettings(value: boolean): void {
|
||||||
@@ -172,6 +167,11 @@ export function setPargarEnabledFromSettings(value: boolean): void {
|
|||||||
pargarEnabledSetting.value = value
|
pargarEnabledSetting.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setShayeganEnabledFromSettings(value: boolean): void {
|
||||||
|
shayeganEnabledFromSettings = value
|
||||||
|
shayeganEnabledSetting.value = value
|
||||||
|
}
|
||||||
|
|
||||||
export function setCustomerDirectSmsEnabledFromSettings(value: boolean): void {
|
export function setCustomerDirectSmsEnabledFromSettings(value: boolean): void {
|
||||||
customerDirectSmsFromSettings = value
|
customerDirectSmsFromSettings = value
|
||||||
customerDirectSmsEnabledSetting.value = value
|
customerDirectSmsEnabledSetting.value = value
|
||||||
@@ -491,6 +491,10 @@ export function isPargarEnabled(): boolean {
|
|||||||
return pargarEnabledFromSettings === true
|
return pargarEnabledFromSettings === true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isShayeganEnabled(): boolean {
|
||||||
|
return shayeganEnabledFromSettings === true
|
||||||
|
}
|
||||||
|
|
||||||
export function getAccountingProvider(): AccountingProvider {
|
export function getAccountingProvider(): AccountingProvider {
|
||||||
return accountingProviderFromSettings ?? 'none'
|
return accountingProviderFromSettings ?? 'none'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user