From 1c4b2d443587db786bbfe398da589d28cf41bf32 Mon Sep 17 00:00:00 2001 From: adonis Date: Sun, 30 Aug 2026 21:18:26 +0330 Subject: [PATCH] Require review before Tahesab wallet sync and harden accounting admin UX. Single-customer and row sync preview balances with documents before overwrite/keep; bulk sync keeps confirm. Also ship Zarin wallet sync actions, field help, and get_user cache improvements. Co-authored-by: Cursor --- components.d.ts | 16 - src/api/tahesab.ts | 98 ++- src/api/zarin.ts | 106 +++ src/components/BasicShowContext.vue | 12 +- src/config-loader.js | 174 ++++- .../components/PargarCustomerSync.vue | 11 +- .../components/PargarWalletBulkSync.vue | 11 +- .../components/TahesabCustomerSync.vue | 609 +++++++++++++----- .../components/TahesabWalletBulkSync.vue | 417 ++++++++++-- .../components/ZarinCustomerSync.vue | 287 +++++++-- .../components/ZarinWalletBulkSync.vue | 368 +++++++++++ .../accounting/components/pargarTable.vue | 3 +- .../menuPages/accounting/components/table.vue | 3 +- .../accounting/components/tahesabTable.vue | 6 +- .../accounting/components/zarinTable.vue | 6 +- src/pages/menuPages/accounting/index.vue | 15 +- .../appNotifications/components/table.vue | 7 +- .../automations/components/table.vue | 3 +- src/pages/menuPages/banners/index.vue | 8 +- src/pages/menuPages/captureSites/index.vue | 3 +- .../menuPages/cards/components/table.vue | 3 +- .../ColleagueAccountingAccounts.vue | 117 +--- .../colleagueOrders/components/table.vue | 3 +- .../conditionalOrders/components/table.vue | 3 +- src/pages/menuPages/configuration/index.vue | 3 +- .../menuPages/configuration/settingGroups.ts | 4 +- .../menuPages/configuration/settingTypes.ts | 28 +- .../customers/archive/components/table.vue | 3 +- .../customers/authentication/index.vue | 9 +- .../CustomerAccountingCodeModal.vue | 116 ++-- .../components/CustomerPargarLogsTable.vue | 3 +- .../components/TahesabMandeDecisionModal.vue | 110 +++- .../components/TahesabWalletSyncAction.vue | 38 +- .../base/components/ZarinWalletSyncAction.vue | 72 +++ .../customers/base/components/fundsTable.vue | 3 +- .../goldCreditInstallmentsTable.vue | 3 +- .../base/components/piggyFundsTable.vue | 3 +- .../base/components/referralsTable.vue | 3 +- .../customers/base/components/table.vue | 3 +- src/pages/menuPages/customers/base/index.vue | 113 +++- .../menuPages/customers/goldCredit/index.vue | 3 +- .../menuPages/customers/online/index.vue | 7 +- .../specialPrice/components/table.vue | 3 +- .../menuPages/discount/components/table.vue | 3 +- .../menuPages/giftCards/components/table.vue | 3 +- .../menuPages/orders/components/table.vue | 3 +- .../physicalWithdraw/components/table.vue | 6 +- .../priceTriggers/components/table.vue | 14 +- .../menuPages/pricing/components/table.vue | 8 +- .../products/base/components/table.vue | 3 +- .../menuPages/products/categories/index.vue | 6 + src/pages/menuPages/sms/components/table.vue | 3 +- src/pages/menuPages/smsPanel/index.vue | 7 +- .../transactions/components/table.vue | 3 +- .../menuPages/users/components/table.vue | 3 +- .../menuPages/wallet/components/table.vue | 3 +- .../menuPages/withdraw/components/table.vue | 3 +- src/store/customerImportStore.ts | 8 +- src/utils/adminHelpCopy.ts | 117 ++++ src/utils/config.ts | 7 + 60 files changed, 2464 insertions(+), 552 deletions(-) create mode 100644 src/pages/menuPages/accounting/components/ZarinWalletBulkSync.vue create mode 100644 src/pages/menuPages/customers/base/components/ZarinWalletSyncAction.vue diff --git a/components.d.ts b/components.d.ts index c0b91db..cab823d 100644 --- a/components.d.ts +++ b/components.d.ts @@ -11,7 +11,6 @@ declare module '@vue/runtime-core' { export interface GlobalComponents { _index: typeof import('./src/core/components/_sample/_index.vue')['default'] AAlert: typeof import('ant-design-vue/es')['Alert'] - AAvatar: typeof import('ant-design-vue/es')['Avatar'] ABadge: typeof import('ant-design-vue/es')['Badge'] AButton: typeof import('ant-design-vue/es')['Button'] ACard: typeof import('ant-design-vue/es')['Card'] @@ -25,42 +24,27 @@ declare module '@vue/runtime-core' { AdminHelpTooltipContent: typeof import('./src/components/AdminHelpTooltipContent.vue')['default'] AdminLabelWithHelp: typeof import('./src/components/AdminLabelWithHelp.vue')['default'] ADrawer: typeof import('ant-design-vue/es')['Drawer'] - ADropdown: typeof import('ant-design-vue/es')['Dropdown'] AEmpty: typeof import('ant-design-vue/es')['Empty'] AForm: typeof import('ant-design-vue/es')['Form'] AFormItem: typeof import('ant-design-vue/es')['FormItem'] - AImage: typeof import('ant-design-vue/es')['Image'] AInput: typeof import('ant-design-vue/es')['Input'] - AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] - AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] ALayout: typeof import('ant-design-vue/es')['Layout'] ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] - ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader'] - ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] - AMenu: typeof import('ant-design-vue/es')['Menu'] - AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider'] - AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] AModal: typeof import('ant-design-vue/es')['Modal'] - APagination: typeof import('ant-design-vue/es')['Pagination'] APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] - APopover: typeof import('ant-design-vue/es')['Popover'] - ARadio: typeof import('ant-design-vue/es')['Radio'] ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] ARow: typeof import('ant-design-vue/es')['Row'] ASelect: typeof import('ant-design-vue/es')['Select'] ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] ASpin: typeof import('ant-design-vue/es')['Spin'] - ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] ASwitch: typeof import('ant-design-vue/es')['Switch'] ATable: typeof import('ant-design-vue/es')['Table'] ATabPane: typeof import('ant-design-vue/es')['TabPane'] ATabs: typeof import('ant-design-vue/es')['Tabs'] ATag: typeof import('ant-design-vue/es')['Tag'] ATextarea: typeof import('ant-design-vue/es')['Textarea'] - ATimePicker: typeof import('ant-design-vue/es')['TimePicker'] ATooltip: typeof import('ant-design-vue/es')['Tooltip'] - AUpload: typeof import('ant-design-vue/es')['Upload'] BaseButton: typeof import('./src/core/components/BaseButton/index.ts')['default'] BaseCard: typeof import('./src/core/components/BaseCard/index.ts')['default'] BaseCollapse: typeof import('./src/components/BaseCollapse.vue')['default'] diff --git a/src/api/tahesab.ts b/src/api/tahesab.ts index 9c4c838..b3c4224 100644 --- a/src/api/tahesab.ts +++ b/src/api/tahesab.ts @@ -89,6 +89,19 @@ export interface TahesabOnlyCustomer { tel: string gid: number | null group: string + has_phone?: boolean + gold?: number | null + cash?: number | null +} + +export interface AccountingPhoneMatch { + code: number + name: string + tel: string + panel_id: number + panel_name: string + panel_code?: string | null + error?: string } export interface CustomerSyncStatus { @@ -97,6 +110,13 @@ export interface CustomerSyncStatus { panel_only: number tahesab_total: number tahesab_only_count: number + tahesab_with_phone_count?: number | null + tahesab_without_phone_count?: number | null + tahesab_importable_count?: number | null + phone_match_count?: number | null + phone_conflict_count?: number | null + phone_matches?: AccountingPhoneMatch[] + phone_conflicts?: AccountingPhoneMatch[] panel_only_list: { data: PanelOnlyCustomer[] current_page: number @@ -115,6 +135,7 @@ export const getCustomerSyncStatus = async (params?: { search?: string page?: number tahesab_page?: number + include_tahesab?: number | boolean }) => await useAxios.get('/admin/tahesab/customer-sync/status', { params }) export interface SyncPanelResult { @@ -132,6 +153,33 @@ export const importFromTahesab = async (customers: TahesabOnlyCustomer[]) => customers, }) +export const linkTahesabMatchingPhones = async () => + await useAxios.post<{ + ok: boolean + linked: number + conflicts: number + message: string + linked_rows: AccountingPhoneMatch[] + conflict_rows: AccountingPhoneMatch[] + }>('/admin/tahesab/customer-sync/link-by-phone') + +export const fetchTahesabMandeByCodes = async (codes: number[]) => + await useAxios.post<{ ok: boolean; mandes: Record }>( + '/admin/tahesab/mande-by-codes', + { codes }, + ) + +export interface TahesabPreviewDoc { + id: number + title?: string + gold?: number + cash?: number + amount?: number + type?: string + date?: string + created_at?: string | null +} + export interface TahesabWalletSyncResult { ok: boolean needs_decision?: boolean @@ -146,6 +194,10 @@ export interface TahesabWalletSyncResult { app_cash?: number tahesab_gold?: number tahesab_cash?: number + zarin_gold?: number + zarin_cash?: number + wallet_docs?: TahesabPreviewDoc[] + tahesab_docs?: TahesabPreviewDoc[] message: string } } @@ -153,7 +205,7 @@ export interface TahesabWalletSyncResult { /** همگام‌سازی مانده یک مشتری با ته‌حساب */ export const syncCustomerWalletFromTahesab = async ( customerId: number, - opts?: { overwrite?: boolean; keep?: boolean; ask_if_conflict?: boolean } + opts?: { overwrite?: boolean; keep?: boolean; ask_if_conflict?: boolean; always_ask?: boolean } ) => await useAxios.post( `/admin/customer/${customerId}/tahesab/sync-wallet`, @@ -161,9 +213,16 @@ export const syncCustomerWalletFromTahesab = async ( overwrite: opts?.overwrite ?? false, keep: opts?.keep ?? false, ask_if_conflict: opts?.ask_if_conflict ?? true, + always_ask: opts?.always_ask ?? false, } ) +export const previewCustomerWalletFromTahesab = async (customerId: number, code?: string | number) => + await useAxios.post( + `/admin/customer/${customerId}/tahesab/preview-wallet`, + { code }, + ) + export interface TahesabBulkWalletSyncChunk { ok: boolean message: string @@ -200,3 +259,40 @@ export const syncAllWalletsFromTahesabChunk = async (params?: { limit: params?.limit ?? 20, overwrite: params?.overwrite ?? false, }) + +export interface TahesabWalletLinkedRow { + id: number + name: string + mobile: string + moshtari_code: number | null + app_gold: number + app_cash: number + tahesab_gold: number | null + tahesab_cash: number | null + gold_delta?: number | null + rial_delta?: number | null + status: 'same' | 'conflict' | 'ready' | 'missing' + message: string +} + +export interface TahesabWalletLinkedList { + ok: boolean + total: number + page: number + per_page: number + last_page: number + rows: TahesabWalletLinkedRow[] +} + +export const getTahesabWalletLinkedList = async (params?: { + search?: string + page?: number + per_page?: number +}) => + await useAxios.get('/admin/tahesab/wallet-sync/list', { + params: { + search: params?.search || undefined, + page: params?.page ?? 1, + per_page: params?.per_page ?? 20, + }, + }) diff --git a/src/api/zarin.ts b/src/api/zarin.ts index 9758395..0ff0970 100644 --- a/src/api/zarin.ts +++ b/src/api/zarin.ts @@ -89,6 +89,19 @@ export interface ZarinOnlyCustomer { tel: string gid: number | null group: string + has_phone?: boolean + gold?: number | null + cash?: number | null +} + +export interface AccountingPhoneMatch { + code: number + name: string + tel: string + panel_id: number + panel_name: string + panel_code?: string | null + error?: string } export interface CustomerSyncStatus { @@ -97,6 +110,13 @@ export interface CustomerSyncStatus { panel_only: number zarin_total: number | null zarin_only_count: number | null + zarin_with_phone_count?: number | null + zarin_without_phone_count?: number | null + zarin_importable_count?: number | null + phone_match_count?: number | null + phone_conflict_count?: number | null + phone_matches?: AccountingPhoneMatch[] + phone_conflicts?: AccountingPhoneMatch[] /** اگر دریافت لیست شکست بخورد */ zarin_fetch_error?: string | null zarin_http_status?: number | null @@ -148,3 +168,89 @@ export const importFromZarin = async (customers: ZarinOnlyCustomer[]) => await useAxios.post('/admin/zarin/customer-sync/import-from-zarin', { customers, }) + +export const linkZarinMatchingPhones = async () => + await useAxios.post<{ + ok: boolean + linked: number + conflicts: number + message: string + linked_rows: AccountingPhoneMatch[] + conflict_rows: AccountingPhoneMatch[] + }>('/admin/zarin/customer-sync/link-by-phone') + +export interface ZarinWalletSyncResult { + ok: boolean + needs_decision?: boolean + message: string + result?: { + applied: boolean + needs_decision?: boolean + skipped?: boolean + gold_delta?: number + rial_delta?: number + app_gold?: number + app_cash?: number + zarin_gold?: number + zarin_cash?: number + tahesab_gold?: number + tahesab_cash?: number + message: string + } +} + +export const syncCustomerWalletFromZarin = async ( + customerId: number, + opts?: { overwrite?: boolean; keep?: boolean; ask_if_conflict?: boolean; always_ask?: boolean } +) => + await useAxios.post( + `/admin/customer/${customerId}/zarin/sync-wallet`, + { + overwrite: opts?.overwrite ?? false, + keep: opts?.keep ?? false, + ask_if_conflict: opts?.ask_if_conflict ?? true, + always_ask: opts?.always_ask ?? false, + } + ) + +export const previewCustomerWalletFromZarin = async (customerId: number, code?: string | number) => + await useAxios.post( + `/admin/customer/${customerId}/zarin/preview-wallet`, + { code }, + ) + +export interface ZarinBulkWalletSyncChunk { + ok: boolean + message: string + total: number + offset: number + processed: number + next_offset: number + done: boolean + success: number + changed: number + needs_decision: number + failed: number + errors: { id: number; name: string; mobile?: string; error: string }[] + decisions: { + id: number + name: string + mobile?: string + app_gold?: number + app_cash?: number + tahesab_gold?: number + tahesab_cash?: number + message: string + }[] +} + +export const syncAllWalletsFromZarinChunk = async (params?: { + offset?: number + limit?: number + overwrite?: boolean +}) => + await useAxios.post('/admin/zarin/wallet-sync/all', { + offset: params?.offset ?? 0, + limit: params?.limit ?? 20, + overwrite: params?.overwrite ?? false, + }) diff --git a/src/components/BasicShowContext.vue b/src/components/BasicShowContext.vue index 3cf29b9..3e04154 100644 --- a/src/components/BasicShowContext.vue +++ b/src/components/BasicShowContext.vue @@ -1,7 +1,10 @@ diff --git a/src/config-loader.js b/src/config-loader.js index 1b6c19d..cbacdf1 100644 --- a/src/config-loader.js +++ b/src/config-loader.js @@ -14,6 +14,9 @@ const VERSION_SUCCESS_URL_KEY = 'VERSION_Success_URL'; const CONFIG_SUCCESS_URL_KEY = 'CONFIG_Success_URL'; const CONFIG_CACHE_KEY = 'APP_CONFIG_CACHE'; + const CONFIG_IDB_NAME = 'abshode_admin_cfg'; + const CONFIG_IDB_STORE = 'kv'; + const CONFIG_CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; const VERSION_STORAGE_KEY = 'version'; const VERSION_UPDATE_LOCK_KEY = 'version_update_lock'; const VERSION_LAST_RELOAD_KEY = 'version_last_reload'; @@ -130,13 +133,13 @@ return readUserVersion(response.user); } - function readCachedAppConfig() { + function readCachedAppConfigPayload() { try { const raw = localStorage.getItem(CONFIG_CACHE_KEY); if (!raw) return null; const payload = JSON.parse(raw); if (!payload || typeof payload !== 'object' || !payload.config) return null; - return payload.config; + return payload; } catch (error) { console.warn('Failed to read cached get_user:', error); localStorage.removeItem(CONFIG_CACHE_KEY); @@ -144,18 +147,125 @@ } } - function writeCachedAppConfig(config) { - try { - localStorage.setItem( - CONFIG_CACHE_KEY, - JSON.stringify({ - cached_at: Date.now(), - config: config - }) - ); - } catch (error) { - console.warn('Failed to write get_user cache:', error); + function readCachedAppConfig() { + const payload = readCachedAppConfigPayload(); + return payload && payload.config ? payload.config : null; + } + + function isFreshGetUserCache(payload, remoteVersion, domain) { + if (!payload || !payload.config) return false; + if (isServerUnreachable(payload.config)) return false; + const cachedDomain = String(payload.domain || '').trim(); + if (cachedDomain && cachedDomain !== domain) return false; + const age = Date.now() - Number(payload.cached_at || 0); + if (!payload.cached_at || age < 0 || age > CONFIG_CACHE_MAX_AGE_MS) return false; + const cachedVersion = normalizeVersion(payload.version); + if (remoteVersion) { + return cachedVersion === remoteVersion; } + return Boolean(cachedVersion); + } + + function openConfigIdb() { + return new Promise(function (resolve, reject) { + if (!window.indexedDB) { + reject(new Error('indexedDB unavailable')); + return; + } + const req = window.indexedDB.open(CONFIG_IDB_NAME, 1); + req.onupgradeneeded = function () { + if (!req.result.objectStoreNames.contains(CONFIG_IDB_STORE)) { + req.result.createObjectStore(CONFIG_IDB_STORE); + } + }; + req.onsuccess = function () { + resolve(req.result); + }; + req.onerror = function () { + reject(req.error); + }; + }); + } + + function writeCachedAppConfigToIdb(payload) { + openConfigIdb() + .then(function (db) { + return new Promise(function (resolve, reject) { + const tx = db.transaction(CONFIG_IDB_STORE, 'readwrite'); + tx.oncomplete = function () { + resolve(); + }; + tx.onerror = function () { + reject(tx.error); + }; + tx.objectStore(CONFIG_IDB_STORE).put(payload, CONFIG_CACHE_KEY); + }); + }) + .catch(function (error) { + console.warn('Failed to write get_user IndexedDB cache:', error); + }); + } + + function readCachedAppConfigFromIdb() { + return openConfigIdb() + .then(function (db) { + return new Promise(function (resolve, reject) { + const tx = db.transaction(CONFIG_IDB_STORE, 'readonly'); + const req = tx.objectStore(CONFIG_IDB_STORE).get(CONFIG_CACHE_KEY); + req.onsuccess = function () { + const value = req.result; + if (!value || typeof value !== 'object' || !value.config) { + resolve(null); + return; + } + resolve(value); + }; + req.onerror = function () { + reject(req.error); + }; + }); + }) + .catch(function () { + return null; + }); + } + + function clearCachedAppConfigIdb() { + openConfigIdb() + .then(function (db) { + const tx = db.transaction(CONFIG_IDB_STORE, 'readwrite'); + tx.objectStore(CONFIG_IDB_STORE).delete(CONFIG_CACHE_KEY); + }) + .catch(function () { + /* ignore */ + }); + } + + function writeCachedAppConfig(config, version) { + const payload = { + cached_at: Date.now(), + version: normalizeVersion(version || getStoredAppVersion()), + domain: resolveTenantDomain(), + config: config + }; + try { + localStorage.setItem(CONFIG_CACHE_KEY, JSON.stringify(payload)); + clearCachedAppConfigIdb(); + } catch (error) { + console.warn('get_user too large for localStorage; using IndexedDB', error); + try { + localStorage.removeItem(CONFIG_CACHE_KEY); + } catch { + /* ignore */ + } + writeCachedAppConfigToIdb(payload); + } + } + + async function resolveCachedPayload() { + const fromLs = readCachedAppConfigPayload(); + if (fromLs) return fromLs; + return await readCachedAppConfigFromIdb(); } function getStoredAppVersion() { @@ -238,6 +348,7 @@ localStorage.removeItem(CONFIG_CACHE_KEY); localStorage.removeItem(CONFIG_SUCCESS_URL_KEY); localStorage.removeItem(VERSION_SUCCESS_URL_KEY); + clearCachedAppConfigIdb(); restoreAuthToken(token); } @@ -311,7 +422,7 @@ return null; } - async function fetchConfig(rounds) { + async function fetchConfig(rounds, version) { rounds = rounds || CONFIG_RETRY_ROUNDS; const urls = sortUrls(getConfigUrls(), CONFIG_SUCCESS_URL_KEY); @@ -328,7 +439,7 @@ } localStorage.setItem(CONFIG_SUCCESS_URL_KEY, url); - writeCachedAppConfig(config); + writeCachedAppConfig(config, version); return { config: config, url: url }; } catch (error) { clearStaleSuccessUrl(CONFIG_SUCCESS_URL_KEY, url); @@ -413,15 +524,15 @@ startVersionUpdateLock(serverVersion); try { - const { config } = await fetchConfig(); + const { config } = await fetchConfig(undefined, serverVersion); if (isServerUnreachable(config)) { - writeCachedAppConfig(config); + writeCachedAppConfig(config, serverVersion); showServerUnavailable(); cancelVersionUpdateLock(); return true; } clearBootstrapCachesKeepToken(); - writeCachedAppConfig(config); + writeCachedAppConfig(config, serverVersion); applyConfig(config); finishVersionUpdateBeforeReload(serverVersion); console.log('App version changed — hard refresh', serverVersion); @@ -441,8 +552,8 @@ let needsFreshForVersion = false; try { - // کش قدیمی را فقط اگر خودش قفل باشد اعمال/قطع کن؛ وگرنه تا get_user تازه صبر کن - const cachedConfig = readCachedAppConfig(); + const cachedPayload = await resolveCachedPayload(); + const cachedConfig = cachedPayload && cachedPayload.config ? cachedPayload.config : null; if (cachedConfig && isServerUnreachable(cachedConfig)) { showServerUnavailable(); return; @@ -461,9 +572,23 @@ } } - const { config, url } = await fetchConfig(); + const domain = resolveTenantDomain(); + if ( + !needsFreshForVersion && + isFreshGetUserCache(cachedPayload, remoteVersion, domain) + ) { + applyConfig(cachedPayload.config); + if (remoteVersion) { + writeStoredAppVersion(remoteVersion); + } + console.log('Config loaded from cache; get_user skipped'); + finishConfigLoad(cachedPayload.config); + return; + } + + const { config, url } = await fetchConfig(undefined, remoteVersion); applyConfig(config); - writeCachedAppConfig(config); + writeCachedAppConfig(config, remoteVersion); if (remoteVersion) { writeStoredAppVersion(remoteVersion); } @@ -477,13 +602,13 @@ console.warn('Config loader: Could not fetch config from any endpoint'); console.warn('Error details:', error && error.message ? error.message : error); - // اگر نسخه عوض شده (قفل/آنلاک)، هرگز با کش قدیمی reachable ادمین را بالا نیاور if (versionChanged || needsFreshForVersion) { showServerUnavailable(); return; } - const cachedConfig = readCachedAppConfig(); + const cachedPayload = await resolveCachedPayload(); + const cachedConfig = cachedPayload && cachedPayload.config ? cachedPayload.config : null; if (cachedConfig) { try { applyConfig(cachedConfig); @@ -519,7 +644,6 @@ showServerUnavailable(); return; } - // بدون تأیید تازه از get_user، ادمین را با کش بالا نیاور (دور زدن قفل) showServerUnavailable(); } }, CONFIG_TIMEOUT); diff --git a/src/pages/menuPages/accounting/components/PargarCustomerSync.vue b/src/pages/menuPages/accounting/components/PargarCustomerSync.vue index c26a7c9..70c4469 100644 --- a/src/pages/menuPages/accounting/components/PargarCustomerSync.vue +++ b/src/pages/menuPages/accounting/components/PargarCustomerSync.vue @@ -1,13 +1,14 @@ + + diff --git a/src/pages/menuPages/accounting/components/pargarTable.vue b/src/pages/menuPages/accounting/components/pargarTable.vue index 2621e7a..b511be9 100644 --- a/src/pages/menuPages/accounting/components/pargarTable.vue +++ b/src/pages/menuPages/accounting/components/pargarTable.vue @@ -1,6 +1,6 @@