Support Didar activity Excel exports without header rows via positional columns.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -29,34 +29,47 @@ import {
|
||||
} from '../common/excel-import-session';
|
||||
import type { ImportActivityRowsDto } from './dto';
|
||||
|
||||
const ACTIVITY_COLUMNS = [
|
||||
{ header: '_type', key: '_type', width: 12 },
|
||||
{ header: 'نوع فعالیت', key: 'kind', width: 16 },
|
||||
{ header: 'عنوان فعالیت', key: 'title', width: 24 },
|
||||
{ header: 'متن فعالیت', key: 'body', width: 36 },
|
||||
{ header: 'نتیجه فعالیت', key: 'result', width: 20 },
|
||||
{ header: 'مسئول اجرا فعالیت', key: 'assignee', width: 18 },
|
||||
{ header: 'ثبت کننده فعالیت', key: 'creator', width: 18 },
|
||||
{ header: 'تاریخ برنامه ریزی شده اجرا فعالیت', key: 'dueDate', width: 18 },
|
||||
{ header: 'زمان برنامه ریزی شده اجرا فعالیت', key: 'dueTime', width: 14 },
|
||||
{ header: 'وضعیت اجرای فعالیت', key: 'status', width: 16 },
|
||||
{ header: 'تاریخ انجام شدن فعالیت', key: 'doneDate', width: 16 },
|
||||
{ header: 'کد معامله فعالیت', key: 'dealCode', width: 14 },
|
||||
{ header: 'کد کارت فعالیت', key: 'cardCode', width: 14 },
|
||||
{ header: 'زمان انجام شدن فعالیت', key: 'doneTime', width: 14 },
|
||||
{ header: 'اشخاص فعالیت', key: 'personName', width: 18 },
|
||||
{ header: 'کد اشخاص فعالیت', key: 'personCode', width: 14 },
|
||||
{ header: 'موبایل اشخاص فعالیت', key: 'personMobile', width: 16 },
|
||||
{ header: 'شرکت های فعالیت', key: 'companyName', width: 20 },
|
||||
{ header: 'کد شرکت های فعالیت', key: 'companyCode', width: 14 },
|
||||
{ header: 'معامله فعالیت_1', key: 'dealTitle', width: 24 },
|
||||
{ header: 'کارت فعالیت_1', key: 'cardTitle', width: 20 },
|
||||
{ header: 'هشتگ', key: 'hashtag', width: 16 },
|
||||
];
|
||||
/** ترتیب دقیق خروجی فعالیت دیدار — برای فایلهای بدون ردیف هدر */
|
||||
const DIDAR_ACTIVITY_POSITIONAL = [
|
||||
{ key: '_type', label: '_type' },
|
||||
{ key: 'kind', label: 'نوع فعالیت' },
|
||||
{ key: 'title', label: 'عنوان فعالیت' },
|
||||
{ key: 'body', label: 'متن فعالیت' },
|
||||
{ key: 'result', label: 'نتیجه فعالیت' },
|
||||
{ key: 'assignee', label: 'مسئول اجرا فعالیت' },
|
||||
{ key: 'creator', label: 'ثبت کننده فعالیت' },
|
||||
{ key: 'dueDate', label: 'تاریخ برنامه ریزی شده اجرا فعالیت' },
|
||||
{ key: 'dueTime', label: 'زمان برنامه ریزی شده اجرا فعالیت' },
|
||||
{ key: 'status', label: 'وضعیت اجرای فعالیت' },
|
||||
{ key: 'doneDate', label: 'تاریخ انجام شدن فعالیت' },
|
||||
{ key: 'dealCode', label: 'کد معامله فعالیت' },
|
||||
{ key: 'cardCode', label: 'کد کارت فعالیت' },
|
||||
{ key: 'doneTime', label: 'زمان انجام شدن فعالیت' },
|
||||
{ key: 'personName', label: 'اشخاص فعالیت' },
|
||||
{ key: 'personCode', label: 'کد اشخاص فعالیت' },
|
||||
{ key: 'personMobile', label: 'موبایل اشخاص فعالیت' },
|
||||
{ key: 'companyName', label: 'شرکت های فعالیت' },
|
||||
{ key: 'companyCode', label: 'کد شرکت های فعالیت' },
|
||||
{ key: 'dealTitle', label: 'معامله فعالیت_1' },
|
||||
{ key: 'cardTitle', label: 'کارت فعالیت_1' },
|
||||
{ key: 'hashtag', label: 'هشتگ' },
|
||||
] as const;
|
||||
|
||||
const ACTIVITY_COLUMNS = DIDAR_ACTIVITY_POSITIONAL.map((c) => ({
|
||||
header: c.label,
|
||||
key: c.key,
|
||||
width:
|
||||
c.key === 'body' || c.key === 'result'
|
||||
? 36
|
||||
: c.key === 'title' || c.key === 'dealTitle'
|
||||
? 24
|
||||
: 16,
|
||||
}));
|
||||
|
||||
const ACTIVITY_ALIASES: Record<string, string> = {
|
||||
_type: '_type',
|
||||
type: '_type',
|
||||
activity: '_type',
|
||||
نوعفعالیت: 'kind',
|
||||
عنوانفعالیت: 'title',
|
||||
عنوان: 'title',
|
||||
@@ -77,10 +90,27 @@ const ACTIVITY_ALIASES: Record<string, string> = {
|
||||
شرکتهایفعالیت: 'companyName',
|
||||
کدشرکتهایفعالیت: 'companyCode',
|
||||
معاملهفعالیت1: 'dealTitle',
|
||||
معاملهفعالیت_1: 'dealTitle',
|
||||
کارتفعالیت1: 'cardTitle',
|
||||
کارتفعالیت_1: 'cardTitle',
|
||||
هشتگ: 'hashtag',
|
||||
};
|
||||
|
||||
function looksLikeDidarActivityHeaderless(firstRow: string[]) {
|
||||
const first = (firstRow[0] ?? '').trim().toLowerCase();
|
||||
if (first !== 'activity' && first !== '_type') return false;
|
||||
const kind = (firstRow[1] ?? '').trim();
|
||||
return /تماس|پیگیری|جلسه|یادداشت|ملاقات|call|task|meeting|note|پیام/i.test(kind);
|
||||
}
|
||||
|
||||
const ACTIVITY_EXCEL_OPTIONS = {
|
||||
positionalColumns: DIDAR_ACTIVITY_POSITIONAL.map((c) => ({
|
||||
key: c.key,
|
||||
label: c.label,
|
||||
})),
|
||||
detectHeaderless: looksLikeDidarActivityHeaderless,
|
||||
};
|
||||
|
||||
const TYPE_LABEL: Record<ActivityType, string> = {
|
||||
call: 'تماس با مشتری',
|
||||
meeting: 'جلسه',
|
||||
@@ -163,7 +193,12 @@ export class ActivitiesExcelService {
|
||||
}
|
||||
|
||||
preview(file: Express.Multer.File): Promise<ExcelPreviewResult> {
|
||||
return previewExcel(file, ACTIVITY_ALIASES, excelFieldsFromColumns(ACTIVITY_COLUMNS, ['title']));
|
||||
return previewExcel(
|
||||
file,
|
||||
ACTIVITY_ALIASES,
|
||||
excelFieldsFromColumns(ACTIVITY_COLUMNS, ['title']),
|
||||
ACTIVITY_EXCEL_OPTIONS,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -182,7 +217,8 @@ export class ActivitiesExcelService {
|
||||
const sessionId = beginExcelImportPrepare(
|
||||
actor.organizationId,
|
||||
file.buffer,
|
||||
(f) => readMappedRows(f, ACTIVITY_ALIASES, mapping),
|
||||
(f) =>
|
||||
readMappedRows(f, ACTIVITY_ALIASES, mapping, ACTIVITY_EXCEL_OPTIONS),
|
||||
);
|
||||
return {
|
||||
created: 0,
|
||||
@@ -221,7 +257,12 @@ export class ActivitiesExcelService {
|
||||
offset: chunk?.offset,
|
||||
limit: chunk?.limit,
|
||||
loadRows: () =>
|
||||
readMappedRows(file!, ACTIVITY_ALIASES, parseExcelMapping(mappingRaw)),
|
||||
readMappedRows(
|
||||
file!,
|
||||
ACTIVITY_ALIASES,
|
||||
parseExcelMapping(mappingRaw),
|
||||
ACTIVITY_EXCEL_OPTIONS,
|
||||
),
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof BadRequestException) throw e;
|
||||
|
||||
Reference in New Issue
Block a user