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 <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 09:42:08 +03:30
co-authored by Cursor
parent 15a0afae5c
commit b42d39d756
3 changed files with 39 additions and 12 deletions
@@ -22,6 +22,17 @@ export type TahesabCallOptions = {
timeoutMs?: number; timeoutMs?: number;
}; };
function extractTahesabError(json: unknown, text: string): string {
if (json && typeof json === 'object') {
const o = json as Record<string, unknown>;
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() @Injectable()
export class TahesabHttpClient { export class TahesabHttpClient {
private readonly logger = new Logger(TahesabHttpClient.name); private readonly logger = new Logger(TahesabHttpClient.name);
@@ -85,9 +96,22 @@ export class TahesabHttpClient {
this.logger.warn( this.logger.warn(
`Tahesab ${method} ${path} → ${status}: ${text.slice(0, 400)}`, `Tahesab ${method} ${path} → ${status}: ${text.slice(0, 400)}`,
); );
const detail = extractTahesabError(json, text);
reject( reject(
new BadGatewayException( 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; return;
@@ -183,9 +183,10 @@ export class TahesabApi {
} }
doListMoshtariByCode(fromCode: number, toCode: number) { doListMoshtariByCode(fromCode: number, toCode: number) {
// OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف)
return this.call( return this.call(
'DoListMoshtari-ByCode', 'DoListMoshtari-ByCode',
'DoListMoshtari', 'Dolistmoshtari',
[fromCode, toCode], [fromCode, toCode],
); );
} }
+12 -10
View File
@@ -32,7 +32,7 @@ const sync = reactive({
name: '', name: '',
moshtariCode: '' as string | number, moshtariCode: '' as string | number,
fromCode: '1', fromCode: '1',
toCode: '500', toCode: '100',
}); });
const syncBusy = ref(false); const syncBusy = ref(false);
type SyncResult = { type SyncResult = {
@@ -51,6 +51,14 @@ function pendingImportCodes() {
.map((r) => r.moshtariCode as number); .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() { function resetForm() {
editingId.value = null; editingId.value = null;
form.label = ''; form.label = '';
@@ -222,9 +230,7 @@ async function searchTahesab(mode: 'filter' | 'range' = 'filter') {
ok.value = `${syncResults.value.length} مشتری پیدا شد` + (linked ? ` (${linked} قبلاً لینک شده)` : ''); ok.value = `${syncResults.value.length} مشتری پیدا شد` + (linked ? ` (${linked} قبلاً لینک شده)` : '');
} }
} catch (e) { } catch (e) {
error.value = axios.isAxiosError(e) error.value = apiErr(e, 'جستجو ناموفق');
? String((e.response?.data as { message?: string })?.message ?? 'جستجو ناموفق')
: 'جستجو ناموفق';
} finally { } finally {
syncBusy.value = false; syncBusy.value = false;
} }
@@ -246,9 +252,7 @@ async function importOne(moshtariCode: number) {
row.personId = data.personId; row.personId = data.personId;
} }
} catch (e) { } catch (e) {
error.value = axios.isAxiosError(e) error.value = apiErr(e, 'وارد کردن ناموفق');
? String((e.response?.data as { message?: string })?.message ?? 'وارد کردن ناموفق')
: 'وارد کردن ناموفق';
} finally { } finally {
syncBusy.value = false; syncBusy.value = false;
} }
@@ -303,9 +307,7 @@ async function importBulk(mode: 'results' | 'range') {
ok.value = `وارد شد: ${data.imported} · قبلاً بود: ${data.skipped} · ناموفق: ${data.failed}`; ok.value = `وارد شد: ${data.imported} · قبلاً بود: ${data.skipped} · ناموفق: ${data.failed}`;
await searchTahesab(mode === 'range' ? 'range' : 'filter'); await searchTahesab(mode === 'range' ? 'range' : 'filter');
} catch (e) { } catch (e) {
error.value = axios.isAxiosError(e) error.value = apiErr(e, 'وارد کردن دسته‌ای ناموفق');
? String((e.response?.data as { message?: string })?.message ?? 'وارد کردن دسته‌ای ناموفق')
: 'وارد کردن دسته‌ای ناموفق';
} finally { } finally {
syncBusy.value = false; syncBusy.value = false;
} }