From b42d39d75640208b5b4005ccf021c315cdfc6969 Mon Sep 17 00:00:00 2001 From: Mahdi Date: Sat, 5 Sep 2026 09:42:08 +0330 Subject: [PATCH] Fix Tahesab ByCode list body key causing 502 on range search. Use Dolistmoshtari as documented and surface upstream error text in the UI. Co-authored-by: Cursor --- .../tahesab/tahesab-http.client.ts | 26 ++++++++++++++++++- .../integrations/tahesab/tahesab-methods.ts | 3 ++- apps/web/src/views/TahesabSettingsView.vue | 22 +++++++++------- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/apps/api/src/integrations/tahesab/tahesab-http.client.ts b/apps/api/src/integrations/tahesab/tahesab-http.client.ts index b0887f4..b26cab4 100644 --- a/apps/api/src/integrations/tahesab/tahesab-http.client.ts +++ b/apps/api/src/integrations/tahesab/tahesab-http.client.ts @@ -22,6 +22,17 @@ export type TahesabCallOptions = { timeoutMs?: number; }; +function extractTahesabError(json: unknown, text: string): string { + if (json && typeof json === 'object') { + const o = json as Record; + const err = o.ERROR ?? o.Error ?? o.error ?? o.message ?? o.Message; + if (typeof err === 'string' && err.trim()) return err.trim().slice(0, 300); + if (Array.isArray(err) && err.length) return String(err[0]).slice(0, 300); + } + const trimmed = text.trim(); + return trimmed ? trimmed.slice(0, 300) : ''; +} + @Injectable() export class TahesabHttpClient { private readonly logger = new Logger(TahesabHttpClient.name); @@ -85,9 +96,22 @@ export class TahesabHttpClient { this.logger.warn( `Tahesab ${method} ${path} → ${status}: ${text.slice(0, 400)}`, ); + const detail = extractTahesabError(json, text); reject( new BadGatewayException( - `پاسخ نامعتبر ته‌حساب (${status}) برای ${path}`, + detail + ? `ته‌حساب (${status}): ${detail}` + : `پاسخ نامعتبر ته‌حساب (${status}) برای ${path}`, + ), + ); + return; + } + // بعضی متدها با ۲۰۰ و فیلد ERROR برمی‌گردند + if (json && typeof json === 'object' && 'ERROR' in (json as object)) { + const detail = extractTahesabError(json, text); + reject( + new BadGatewayException( + detail ? `ته‌حساب: ${detail}` : `خطای ته‌حساب برای ${path}`, ), ); return; diff --git a/apps/api/src/integrations/tahesab/tahesab-methods.ts b/apps/api/src/integrations/tahesab/tahesab-methods.ts index 22702ad..68aae0f 100644 --- a/apps/api/src/integrations/tahesab/tahesab-methods.ts +++ b/apps/api/src/integrations/tahesab/tahesab-methods.ts @@ -183,9 +183,10 @@ export class TahesabApi { } doListMoshtariByCode(fromCode: number, toCode: number) { + // OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف) return this.call( 'DoListMoshtari-ByCode', - 'DoListMoshtari', + 'Dolistmoshtari', [fromCode, toCode], ); } diff --git a/apps/web/src/views/TahesabSettingsView.vue b/apps/web/src/views/TahesabSettingsView.vue index e96554c..75e0665 100644 --- a/apps/web/src/views/TahesabSettingsView.vue +++ b/apps/web/src/views/TahesabSettingsView.vue @@ -32,7 +32,7 @@ const sync = reactive({ name: '', moshtariCode: '' as string | number, fromCode: '1', - toCode: '500', + toCode: '100', }); const syncBusy = ref(false); type SyncResult = { @@ -51,6 +51,14 @@ function pendingImportCodes() { .map((r) => r.moshtariCode as number); } +function apiErr(e: unknown, fallback: string) { + if (!axios.isAxiosError(e)) return fallback; + const msg = (e.response?.data as { message?: string | string[] })?.message; + if (Array.isArray(msg)) return msg.join('، ') || fallback; + if (typeof msg === 'string' && msg.trim()) return msg; + return fallback; +} + function resetForm() { editingId.value = null; form.label = ''; @@ -222,9 +230,7 @@ async function searchTahesab(mode: 'filter' | 'range' = 'filter') { ok.value = `${syncResults.value.length} مشتری پیدا شد` + (linked ? ` (${linked} قبلاً لینک شده)` : ''); } } catch (e) { - error.value = axios.isAxiosError(e) - ? String((e.response?.data as { message?: string })?.message ?? 'جستجو ناموفق') - : 'جستجو ناموفق'; + error.value = apiErr(e, 'جستجو ناموفق'); } finally { syncBusy.value = false; } @@ -246,9 +252,7 @@ async function importOne(moshtariCode: number) { row.personId = data.personId; } } catch (e) { - error.value = axios.isAxiosError(e) - ? String((e.response?.data as { message?: string })?.message ?? 'وارد کردن ناموفق') - : 'وارد کردن ناموفق'; + error.value = apiErr(e, 'وارد کردن ناموفق'); } finally { syncBusy.value = false; } @@ -303,9 +307,7 @@ async function importBulk(mode: 'results' | 'range') { ok.value = `وارد شد: ${data.imported} · قبلاً بود: ${data.skipped} · ناموفق: ${data.failed}`; await searchTahesab(mode === 'range' ? 'range' : 'filter'); } catch (e) { - error.value = axios.isAxiosError(e) - ? String((e.response?.data as { message?: string })?.message ?? 'وارد کردن دسته‌ای ناموفق') - : 'وارد کردن دسته‌ای ناموفق'; + error.value = apiErr(e, 'وارد کردن دسته‌ای ناموفق'); } finally { syncBusy.value = false; }