Start outbound/inbound on the server; hydrate after refresh so the transfer modal returns without opening accounting. Co-authored-by: Cursor <cursoragent@cursor.com>
340 lines
9.3 KiB
TypeScript
340 lines
9.3 KiB
TypeScript
import useAxios from '@core/utils/useAxios'
|
|
import type { TableInput } from '@core/type/baseTable'
|
|
|
|
export interface TahesabRecord {
|
|
id?: number | null
|
|
customer_id?: number
|
|
amount?: number
|
|
desc?: string
|
|
type?: string
|
|
sync_status?: 'pending' | 'success' | 'failed' | 'missing'
|
|
sync_log?: string | null
|
|
tahesab_id?: string | null
|
|
data?: string | null
|
|
res?: string | null
|
|
created_at?: string
|
|
withdraw_id?: number | null
|
|
order_id?: number | null
|
|
issue_kind?: 'failed' | 'missing'
|
|
sync_target?: { type: 'row' | 'withdraw' | 'order'; id: number }
|
|
customer?: {
|
|
id?: number
|
|
name?: string
|
|
mobile?: string
|
|
moshtari_code?: string | number | null
|
|
}
|
|
}
|
|
|
|
export const getTahesabTable = async (params: TableInput & { view?: string }) =>
|
|
await useAxios.post<TahesabRecord[]>('/admin/tahesab/table', params)
|
|
|
|
export const getTahesabDetail = async (id: number) =>
|
|
await useAxios.get<TahesabRecord>(`/admin/tahesab/${id}`)
|
|
|
|
export const retryTahesabSanad = async (id: number) =>
|
|
await useAxios.post<{ ok: boolean; message: string; tahesab?: TahesabRecord }>(`/admin/tahesab/${id}/retry`)
|
|
|
|
export interface TahesabDocSyncState {
|
|
running: boolean
|
|
finished: boolean
|
|
stopped: boolean
|
|
stage?: 'docs' | 'customers_out' | 'customers_in'
|
|
percent: number
|
|
total: number
|
|
processed: number
|
|
success: number
|
|
failed: number
|
|
current: string
|
|
errors: { label?: string; error?: string }[]
|
|
doc_total?: number
|
|
doc_success?: number
|
|
doc_failed?: number
|
|
started_at?: string | null
|
|
finished_at?: string | null
|
|
}
|
|
|
|
export interface TahesabPendingSummary {
|
|
failed_rows: number
|
|
missing_withdraws: number
|
|
missing_orders: number
|
|
total: number
|
|
doc_sync?: TahesabDocSyncState
|
|
}
|
|
|
|
export interface TahesabSyncAllResult {
|
|
ok: boolean
|
|
retried: number
|
|
backfilled_withdraws: number
|
|
backfilled_orders: number
|
|
failed: number
|
|
errors: string[]
|
|
summary: TahesabPendingSummary
|
|
}
|
|
|
|
export const getTahesabPendingSummary = async (customerId?: number) =>
|
|
await useAxios.get<TahesabPendingSummary>('/admin/tahesab/pending-summary', {
|
|
params: customerId ? { customer_id: customerId } : undefined
|
|
})
|
|
|
|
export const getTahesabPendingDocs = async (customerId?: number, limit = 200) =>
|
|
await useAxios.get<{ rows: TahesabRecord[]; summary: TahesabPendingSummary }>(
|
|
'/admin/tahesab/pending-docs',
|
|
{ params: { ...(customerId ? { customer_id: customerId } : {}), limit } }
|
|
)
|
|
|
|
export const syncOneTahesab = async (type: 'row' | 'withdraw' | 'order', id: number) =>
|
|
await useAxios.post<{ ok: boolean; message: string }>('/admin/tahesab/sync-one', { type, id })
|
|
|
|
export const syncAllTahesabPending = async (limit = 80, customerId?: number) =>
|
|
await useAxios.post<TahesabSyncAllResult>('/admin/tahesab/sync-all-pending', {
|
|
limit,
|
|
...(customerId ? { customer_id: customerId } : {})
|
|
})
|
|
|
|
export const startTahesabDocSync = async () =>
|
|
await useAxios.post<{ ok: boolean; message: string; doc_sync: TahesabDocSyncState }>(
|
|
'/admin/tahesab/doc-sync/start'
|
|
)
|
|
|
|
export const stopTahesabDocSync = async () =>
|
|
await useAxios.post<{ ok: boolean; message: string; doc_sync: TahesabDocSyncState }>(
|
|
'/admin/tahesab/doc-sync/stop'
|
|
)
|
|
|
|
export const startTahesabCustomerOutbound = async () =>
|
|
await useAxios.post<{ ok: boolean; message: string; doc_sync: TahesabDocSyncState }>(
|
|
'/admin/tahesab/customer-sync/outbound/start'
|
|
)
|
|
|
|
export const startTahesabCustomerInbound = async () =>
|
|
await useAxios.post<{ ok: boolean; message: string; doc_sync: TahesabDocSyncState }>(
|
|
'/admin/tahesab/customer-sync/inbound/start'
|
|
)
|
|
|
|
export const registerCustomerInTahesab = async (customerId: number) =>
|
|
await useAxios.post<{ ok: boolean; message: string; moshtari_code?: string | number }>(
|
|
`/admin/customer/${customerId}/tahesab/register`
|
|
)
|
|
|
|
export interface PanelOnlyCustomer {
|
|
id: number
|
|
name: string
|
|
mobile: string
|
|
created_at: string
|
|
}
|
|
|
|
export interface TahesabOnlyCustomer {
|
|
code: number
|
|
name: string
|
|
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 {
|
|
panel_total: number
|
|
panel_registered: number
|
|
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
|
|
last_page: number
|
|
total: number
|
|
}
|
|
tahesab_only_list: {
|
|
data: TahesabOnlyCustomer[]
|
|
current_page: number
|
|
last_page: number
|
|
total: number
|
|
}
|
|
doc_sync?: TahesabDocSyncState
|
|
}
|
|
|
|
export const getCustomerSyncStatus = async (params?: {
|
|
search?: string
|
|
page?: number
|
|
tahesab_page?: number
|
|
include_tahesab?: number | boolean
|
|
}) => await useAxios.get<CustomerSyncStatus>('/admin/tahesab/customer-sync/status', { params })
|
|
|
|
export interface SyncPanelResult {
|
|
ok: boolean
|
|
success: number
|
|
failed: number
|
|
errors: { name: string; mobile: string; error: string }[]
|
|
}
|
|
|
|
export const syncPanelToTahesab = async () =>
|
|
await useAxios.post<SyncPanelResult>('/admin/tahesab/customer-sync/panel-to-tahesab')
|
|
|
|
export const importFromTahesab = async (customers: TahesabOnlyCustomer[]) =>
|
|
await useAxios.post<SyncPanelResult>('/admin/tahesab/customer-sync/import-from-tahesab', {
|
|
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<string, { gold: number; cash: number }> }>(
|
|
'/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
|
|
message: string
|
|
result?: {
|
|
applied: boolean
|
|
needs_decision?: boolean
|
|
skipped?: boolean
|
|
gold_delta?: number
|
|
rial_delta?: number
|
|
app_gold?: number
|
|
app_cash?: number
|
|
tahesab_gold?: number
|
|
tahesab_cash?: number
|
|
zarin_gold?: number
|
|
zarin_cash?: number
|
|
wallet_docs?: TahesabPreviewDoc[]
|
|
tahesab_docs?: TahesabPreviewDoc[]
|
|
message: string
|
|
}
|
|
}
|
|
|
|
/** همگامسازی مانده یک مشتری با تهحساب */
|
|
export const syncCustomerWalletFromTahesab = async (
|
|
customerId: number,
|
|
opts?: { overwrite?: boolean; keep?: boolean; ask_if_conflict?: boolean; always_ask?: boolean }
|
|
) =>
|
|
await useAxios.post<TahesabWalletSyncResult>(
|
|
`/admin/customer/${customerId}/tahesab/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 previewCustomerWalletFromTahesab = async (customerId: number, code?: string | number) =>
|
|
await useAxios.post<TahesabWalletSyncResult>(
|
|
`/admin/customer/${customerId}/tahesab/preview-wallet`,
|
|
{ code },
|
|
)
|
|
|
|
export interface TahesabBulkWalletSyncChunk {
|
|
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 syncAllWalletsFromTahesabChunk = async (params?: {
|
|
offset?: number
|
|
limit?: number
|
|
overwrite?: boolean
|
|
}) =>
|
|
await useAxios.post<TahesabBulkWalletSyncChunk>('/admin/tahesab/wallet-sync/all', {
|
|
offset: params?.offset ?? 0,
|
|
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<TahesabWalletLinkedList>('/admin/tahesab/wallet-sync/list', {
|
|
params: {
|
|
search: params?.search || undefined,
|
|
page: params?.page ?? 1,
|
|
per_page: params?.per_page ?? 20,
|
|
},
|
|
})
|