Import Tahesab customers in batches with progress overlay and prefetched rows
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -10,8 +10,10 @@ import type {
|
||||
UpdateTahesabConnectionRequest,
|
||||
} from '@mento/shared';
|
||||
import { brand } from '@mento/shared';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
import {
|
||||
UiBadge,
|
||||
UiBusyOverlay,
|
||||
UiButton,
|
||||
UiCard,
|
||||
UiField,
|
||||
@@ -48,6 +50,13 @@ const sync = reactive({
|
||||
toCode: '',
|
||||
});
|
||||
const syncBusy = ref(false);
|
||||
/** تعداد مشتری در هر درخواست ورود دستهای (برای جلوگیری از تایماوت و نمایش درصد) */
|
||||
const IMPORT_CHUNK = 25;
|
||||
const importOverlay = reactive<{ open: boolean; message: string; progress: number | null }>({
|
||||
open: false,
|
||||
message: '',
|
||||
progress: null,
|
||||
});
|
||||
type SyncResult = {
|
||||
moshtariCode: number | null;
|
||||
name: string;
|
||||
@@ -475,18 +484,72 @@ async function importBulk(mode: 'results' | 'range') {
|
||||
body.toCode = toCode;
|
||||
}
|
||||
}
|
||||
syncBusy.value = true;
|
||||
importOverlay.open = true;
|
||||
importOverlay.progress = 0;
|
||||
importOverlay.message = 'در حال آمادهسازی فهرست مشتریان…';
|
||||
const totals = { imported: 0, skipped: 0, failed: 0 };
|
||||
let firstError = '';
|
||||
try {
|
||||
syncBusy.value = true;
|
||||
const { data } = await api.post<{
|
||||
imported: number;
|
||||
skipped: number;
|
||||
failed: number;
|
||||
}>('/integrations/tahesab/sync/import-bulk', body);
|
||||
ok.value = `وارد شد: ${data.imported} · قبلاً بود: ${data.skipped} · ناموفق: ${data.failed}`;
|
||||
await searchTahesab(mode === 'range' ? 'range' : 'filter');
|
||||
let codes: number[];
|
||||
if (Array.isArray(body.moshtariCodes)) {
|
||||
codes = body.moshtariCodes as number[];
|
||||
} else {
|
||||
const { data } = await api.post<{ results: SyncResult[] }>(
|
||||
'/integrations/tahesab/sync/search',
|
||||
body,
|
||||
{ timeout: 90_000 },
|
||||
);
|
||||
const found = data.results ?? [];
|
||||
totals.skipped += found.filter((r) => r.alreadyLinked).length;
|
||||
codes = found
|
||||
.filter((r) => r.moshtariCode != null && !r.alreadyLinked)
|
||||
.map((r) => r.moshtariCode as number);
|
||||
}
|
||||
|
||||
const total = codes.length;
|
||||
for (let i = 0; i < total; i += IMPORT_CHUNK) {
|
||||
const chunk = codes.slice(i, i + IMPORT_CHUNK);
|
||||
importOverlay.message = `در حال وارد کردن مشتریان… ${toFaDigits(Math.min(i + chunk.length, total))} از ${toFaDigits(total)}`;
|
||||
try {
|
||||
const { data } = await api.post<{
|
||||
imported: number;
|
||||
skipped: number;
|
||||
failed: number;
|
||||
items?: Array<{ moshtariCode: number; ok: boolean; personId?: string; error?: string }>;
|
||||
}>(
|
||||
'/integrations/tahesab/sync/import-bulk',
|
||||
{ connectionId: sync.connectionId, moshtariCodes: chunk },
|
||||
{ timeout: 120_000 },
|
||||
);
|
||||
totals.imported += data.imported;
|
||||
totals.skipped += data.skipped;
|
||||
totals.failed += data.failed;
|
||||
for (const item of data.items ?? []) {
|
||||
if (!item.ok) {
|
||||
if (!firstError && item.error) firstError = item.error;
|
||||
continue;
|
||||
}
|
||||
const row = syncResults.value.find((r) => r.moshtariCode === item.moshtariCode);
|
||||
if (row) {
|
||||
row.alreadyLinked = true;
|
||||
if (item.personId) row.personId = item.personId;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
totals.failed += chunk.length;
|
||||
if (!firstError) firstError = apiErr(e, 'خطا در ارسال');
|
||||
}
|
||||
importOverlay.progress = total ? ((i + chunk.length) / total) * 100 : 100;
|
||||
}
|
||||
|
||||
ok.value = `وارد شد: ${toFaDigits(totals.imported)} · قبلاً بود: ${toFaDigits(totals.skipped)} · ناموفق: ${toFaDigits(totals.failed)}`;
|
||||
if (firstError) error.value = `برخی موارد وارد نشدند: ${firstError}`;
|
||||
} catch (e) {
|
||||
error.value = apiErr(e, 'وارد کردن دستهای ناموفق');
|
||||
} finally {
|
||||
importOverlay.open = false;
|
||||
importOverlay.progress = null;
|
||||
syncBusy.value = false;
|
||||
}
|
||||
}
|
||||
@@ -980,6 +1043,12 @@ onMounted(load);
|
||||
</div>
|
||||
</div>
|
||||
</UiCard>
|
||||
|
||||
<UiBusyOverlay
|
||||
:open="importOverlay.open"
|
||||
:message="importOverlay.message"
|
||||
:progress="importOverlay.progress"
|
||||
/>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user