feat(tahesab): poll customer sync from any page and reuse the progress modal

Start outbound/inbound on the server; hydrate after refresh so the transfer modal returns without opening accounting.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-03 10:09:54 +03:30
co-authored by Cursor
parent 6ab14313dd
commit 62a6a91455
6 changed files with 197 additions and 64 deletions
@@ -105,8 +105,8 @@
<a-button
type="primary"
size="small"
:loading="syncingPanel"
:disabled="!status?.panel_only"
:loading="docSyncStore.starting || (docSyncStore.active && docSyncStore.stage === 'customers_out')"
:disabled="!status?.panel_only && !docSyncStore.active"
@click="syncAllPanel"
>
<template #icon><Icon icon="vuesax-linear:send-2" :size="13" /></template>
@@ -243,11 +243,15 @@
</a-button>
<a-button
size="small"
:loading="importStore.active"
:disabled="!importableCount"
:loading="docSyncStore.starting || (docSyncStore.active && docSyncStore.stage === 'customers_in')"
:disabled="!importableCount && !docSyncStore.active"
@click="importAll"
>
{{ importStore.active ? 'در حال…' : `وارد کردن همه (${importableCount})` }}
{{
docSyncStore.active && docSyncStore.stage === 'customers_in'
? 'در حال…'
: `وارد کردن همه (${importableCount})`
}}
</a-button>
</div>
</div>
@@ -364,7 +368,6 @@ import Icon from '@core/components/BaseIcon/src/Icon.vue'
import { toJalali } from '@core/utils/useDataTime'
import {
getCustomerSyncStatus,
syncPanelToTahesab,
importFromTahesab,
registerCustomerInTahesab,
linkTahesabMatchingPhones,
@@ -374,14 +377,12 @@ import {
type TahesabOnlyCustomer,
type SyncPanelResult,
} from '@/api/tahesab'
import { useCustomerImportStore } from '@/store/customerImportStore'
import { useTahesabDocSyncStore } from '@/store/tahesabDocSyncStore'
import ColleagueAccountingAccounts from '@/pages/menuPages/colleagueOrders/components/ColleagueAccountingAccounts.vue'
import TahesabDocSyncBox from '@/pages/menuPages/accounting/components/TahesabDocSyncBox.vue'
import { accountingTahesabCustomerSyncIntro } from '@/utils/adminHelpCopy'
import AdminFieldHelp from '@/components/AdminFieldHelp.vue'
const importStore = useCustomerImportStore()
const docSyncStore = useTahesabDocSyncStore()
// ─── state ───
@@ -600,18 +601,15 @@ async function registerOnePanel(customer: PanelOnlyCustomer) {
}
async function syncAllPanel() {
syncingPanel.value = true
panelSyncResult.value = null
showPanelErrors.value = false
if (docSyncStore.active) {
docSyncStore.expand()
return
}
try {
const res = await syncPanelToTahesab()
panelSyncResult.value = res
if (res.success) message.success(`${res.success} مشتری ثبت شد`)
if (res.failed) message.warning(`${res.failed} مشتری ثبت نشد`)
await load(1)
panelDone.clear()
} catch { message.error('خطا در ارتباط با سرور') }
finally { syncingPanel.value = false }
await docSyncStore.startOutbound()
} catch (e: any) {
message.error(e?.response?.data?.message || e?.message || 'شروع ثبت ناموفق بود')
}
}
// ─── تب ۲: عملیات ───
@@ -703,11 +701,15 @@ async function importSelected() {
async function importAll() {
const pending = importableList.value
if (!pending.length) return
if (importStore.active) {
importStore.expand()
if (docSyncStore.active) {
docSyncStore.expand()
return
}
confirmImport(pending, () => importStore.start(pending, 'tahesab'))
confirmImport(pending, () => {
void docSyncStore.startInbound().catch((e: any) => {
message.error(e?.response?.data?.message || e?.message || 'شروع ورود ناموفق بود')
})
})
}
async function confirmLinkPhones() {
@@ -771,26 +773,21 @@ async function enrichPageMande() {
watch([tahesabPage, tahesabSearch], () => { void enrichPageMande() })
// با پیشرفت عملیات سراسری، ردیف‌های وارد شده را همین‌جا هم تیک بزن
watch(() => importStore.doneCodes.length, () => {
importStore.doneCodes.forEach(code => tahesabDone.add(code))
})
// پس از پایان کامل، آمار و لیست را تازه کن
watch(() => importStore.status, async (s, prev) => {
if (prev === 'running' && (s === 'done' || s === 'canceled')) {
selectedTahesab.clear()
if (status.value?.tahesab_only_count != null) {
status.value.tahesab_only_count = Math.max(0, status.value.tahesab_only_count - importStore.success)
watch(
() => docSyncStore.finished,
async (done, wasDone) => {
if (!done || wasDone) return
await load(1)
panelDone.clear()
if (docSyncStore.stage === 'customers_in') {
tahesabFetched.value = false
if (activeTab.value === 'tahesab') await loadTahesab()
}
await loadPanel(panelPagination.value.page)
}
})
},
)
onMounted(() => {
load(1)
// اگر عملیات سراسری در حال اجراست، ردیف‌های انجام‌شده را همگام کن
importStore.doneCodes.forEach(code => tahesabDone.add(code))
})
</script>
@@ -6,7 +6,7 @@
<p class="m-0 text-sm font-800 text-gray-800">ارسال سندها به ته‌حساب</p>
<p class="mb-0 mt-1 text-xs leading-relaxed text-gray-500">
با یک کلیک همهٔ سفارش‌ها و شارژهای جاافتاده یکی‌یکی و با فاصله به ته‌حساب می‌روند.
سندهایی که قبلاً رفته‌اند دوباره ارسال نمی‌شوند؛ می‌توانید پیشرفت را کوچک کنید.
بعد از سندها، مشتریان پنلِ بدون کد هم همان‌جا ثبت می‌شوند. پیشرفت را می‌توانید کوچک کنید.
</p>
</div>
<a-button