Fix Tahesab empty-range 502 by using a single ByCode call.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-05 10:41:12 +03:30
co-authored by Cursor
parent 38f4b75a78
commit 43526cb980
3 changed files with 35 additions and 19 deletions
@@ -18,8 +18,9 @@ import {
import { normalizeIranMobile, SanadBuilders, type TahesabApi } from './tahesab-methods';
const LIST_ALL_CHUNK = 2000;
const LIST_ALL_MAX_CODE = 50_000;
const LIST_ALL_EMPTY_STREAK = 3;
/** سقف «همه» در یک درخواست ByCode — تکه‌تکه باعث ۵۰۲ پروکسی می‌شود */
const LIST_ALL_MAX_CODE = 5_000;
const LIST_ALL_TIMEOUT_MS = 55_000;
function asMoshtariList(raw: unknown): Array<Record<string, unknown>> {
if (!raw) return [];
@@ -316,21 +317,26 @@ export class TahesabPersonLinkService {
return this.linkDto(link);
}
/**
* لیست همهٔ مشتریان در یک درخواست (بازه ۱…LIST_ALL_MAX_CODE).
* تکه‌تکه کردن پشت‌سرهم معمولاً از مهلت nginx بیشتر می‌شود و ۵۰۲ می‌دهد.
*/
private async listAllMoshtari(api: TahesabApi) {
const merged: Array<Record<string, unknown>> = [];
let emptyStreak = 0;
for (let from = 1; from <= LIST_ALL_MAX_CODE; from += LIST_ALL_CHUNK) {
const to = Math.min(from + LIST_ALL_CHUNK - 1, LIST_ALL_MAX_CODE);
const rows = asMoshtariList(await api.doListMoshtariByCode(from, to));
if (!rows.length) {
emptyStreak += 1;
if (emptyStreak >= LIST_ALL_EMPTY_STREAK) break;
continue;
}
emptyStreak = 0;
merged.push(...rows);
try {
return asMoshtariList(
await api.doListMoshtariByCode(1, LIST_ALL_MAX_CODE, {
timeoutMs: LIST_ALL_TIMEOUT_MS,
}),
);
} catch (err) {
// اگر بازهٔ بزرگ سمت ته‌حساب رد شد، همان سقف دستی ۲۰۰۰ را یک‌بار امتحان کن
if (LIST_ALL_MAX_CODE <= LIST_ALL_CHUNK) throw err;
return asMoshtariList(
await api.doListMoshtariByCode(1, LIST_ALL_CHUNK, {
timeoutMs: LIST_ALL_TIMEOUT_MS,
}),
);
}
return merged;
}
async search(organizationId: string, dto: SearchTahesabDto) {
@@ -366,7 +372,8 @@ export class TahesabPersonLinkService {
moshtariCode: pickCode(row),
name: pickName(row),
tel: pickTel(row),
raw: row,
// raw فقط برای جستجوی محدود؛ در listAll حجم پاسخ را باد می‌کند و پروکسی را می‌ترکاند
...(dto.listAll ? {} : { raw: row }),
}));
const codes = mapped
@@ -145,6 +145,7 @@ export class TahesabApi {
bodyKey?: string,
args?: unknown[],
method?: 'GET' | 'POST',
timeoutMs?: number,
) {
return this.http.call<T>({
...this.creds,
@@ -152,6 +153,7 @@ export class TahesabApi {
bodyKey,
args,
method,
timeoutMs,
});
}
@@ -182,12 +184,18 @@ export class TahesabApi {
return this.call('DoEditMoshtari', 'DoEditMoshtari', args);
}
doListMoshtariByCode(fromCode: number, toCode: number) {
doListMoshtariByCode(
fromCode: number,
toCode: number,
opts?: { timeoutMs?: number },
) {
// OpenAPI/Postman: کلید body برای ByCode دقیقاً «Dolistmoshtari» است (حساس به حروف)
return this.call(
'DoListMoshtari-ByCode',
'Dolistmoshtari',
[fromCode, toCode],
'POST',
opts?.timeoutMs,
);
}
+3 -2
View File
@@ -261,6 +261,7 @@ async function searchTahesab(mode: 'filter' | 'range' = 'filter') {
const { data } = await api.post<{ results: SyncResult[] }>(
'/integrations/tahesab/sync/search',
body,
{ timeout: 60_000 },
);
syncResults.value = data.results ?? [];
if (!syncResults.value.length) {
@@ -502,8 +503,8 @@ onMounted(load);
<h3 class="subhead">۱) وارد کردن دسته‌ای با بازهٔ کد مشتری</h3>
<p class="muted small">
اگر «از» و «تا» را خالی بگذارید و نمایش بازه را بزنید، همهٔ مشتریان ته‌حساب لیست می‌شوند.
برای محدود کردن، بازه بگذارید (حداکثر ۲۰۰۰ کد در هر درخواست محدود).
اگر «از» و «تا» خالی باشند، مشتریان با کد ۱ تا ۵۰۰۰ از ته‌حساب لیست می‌شوند.
برای کدهای بالاتر بازه را دستی بگذارید (حداکثر ۲۰۰۰ کد در هر درخواست محدود).
</p>
<div class="sync-grid range">
<input v-model="sync.fromCode" type="number" min="1" dir="ltr" placeholder="از کد (خالی = همه)" />