Filtering tasks and assigning a person in charge during Excel import
This commit is contained in:
@@ -3,7 +3,7 @@ import type { ImportContactsResult, PersonMood } from '@mento/shared';
|
||||
import { PERSON_MOOD_LABELS, PERSON_MOODS } from '@mento/shared';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import type { Actor } from '../common/record-scope';
|
||||
import { ownerWhere } from '../common/record-scope';
|
||||
import { ownerWhere, resolveOwnerId } from '../common/record-scope';
|
||||
import {
|
||||
formatJalaliDate,
|
||||
parseExcelMapping,
|
||||
@@ -99,6 +99,12 @@ const PERSON_ALIASES: Record<string, string> = {
|
||||
تلفن2: 'phone2',
|
||||
تلفنهایدیگرمشتری: 'otherPhones',
|
||||
مسئولمشتری: 'ownerName',
|
||||
مسئول: 'ownerName',
|
||||
مسئولفروش: 'ownerName',
|
||||
کارشناسفروش: 'ownerName',
|
||||
کارشناس: 'ownerName',
|
||||
owner: 'ownerName',
|
||||
ownername: 'ownerName',
|
||||
تلفنثابتشرکت: 'companyPhone',
|
||||
کددیدارشرکت: 'companyDidarId',
|
||||
مجوزمشاهدهمشتری: 'visibility',
|
||||
@@ -114,11 +120,14 @@ const PERSON_FIELDS = [
|
||||
{ key: 'firstName', label: 'نام' },
|
||||
{ key: 'lastName', label: 'نام خانوادگی' },
|
||||
{ key: 'title', label: 'نام کامل / عنوان' },
|
||||
{ key: 'didarId', label: 'کد مبدأ (کد دیدار)' },
|
||||
{ key: 'customerCode', label: 'کد مشتری' },
|
||||
{ key: 'mobile', label: 'موبایل' },
|
||||
{ key: 'phone', label: 'تلفن' },
|
||||
{ key: 'email', label: 'ایمیل' },
|
||||
{ key: 'position', label: 'سمت' },
|
||||
{ key: 'companyName', label: 'شرکت' },
|
||||
{ key: 'ownerName', label: 'کارشناس فروش' },
|
||||
{ key: 'notes', label: 'یادداشت' },
|
||||
{ key: 'mood', label: 'وضعیت مشتری' },
|
||||
];
|
||||
@@ -335,6 +344,7 @@ export class ContactsExcelService {
|
||||
): Promise<ImportContactsResult> {
|
||||
const rows = await readMappedRows(file, PERSON_ALIASES, parseExcelMapping(mappingRaw));
|
||||
const result: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
|
||||
const ownerCache = new Map<string, string | null>();
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const row = rows[i]!;
|
||||
@@ -363,6 +373,19 @@ export class ContactsExcelService {
|
||||
companyId = await this.findOrCreateCompanyId(actor, companyName);
|
||||
}
|
||||
|
||||
const ownerName = row.ownerName?.trim() ?? '';
|
||||
let ownerId: string | undefined;
|
||||
let ownerMissing = false;
|
||||
if (ownerName) {
|
||||
const key = ownerName.toLowerCase();
|
||||
if (!ownerCache.has(key)) {
|
||||
ownerCache.set(key, await this.findUserId(actor.organizationId, ownerName));
|
||||
}
|
||||
const matched = ownerCache.get(key);
|
||||
if (matched) ownerId = matched;
|
||||
else ownerMissing = true;
|
||||
}
|
||||
|
||||
const customFields: Record<string, string> = {};
|
||||
for (const key of CUSTOM_KEYS) {
|
||||
const value = row[key]?.trim();
|
||||
@@ -380,9 +403,16 @@ export class ContactsExcelService {
|
||||
notes: row.notes || undefined,
|
||||
mood: parseMood(row.mood) ?? parseMood(row.group),
|
||||
companyId,
|
||||
ownerId: resolveOwnerId(actor, ownerId ?? null),
|
||||
customFields,
|
||||
});
|
||||
result.created += 1;
|
||||
if (ownerMissing) {
|
||||
result.errors.push({
|
||||
row: rowNum,
|
||||
message: `کارشناس فروش «${ownerName}» در تیم پیدا نشد؛ به کاربر ایمپورتر اختصاص داده شد`,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof ConflictException) {
|
||||
result.skipped += 1;
|
||||
@@ -453,4 +483,25 @@ export class ContactsExcelService {
|
||||
const created = await this.companies.create(actor, { name });
|
||||
return created.id;
|
||||
}
|
||||
|
||||
private async findUserId(organizationId: string, name?: string) {
|
||||
const n = name?.trim();
|
||||
if (!n) return null;
|
||||
const exact = await this.prisma.user.findFirst({
|
||||
where: {
|
||||
fullName: { equals: n, mode: 'insensitive' },
|
||||
memberships: { some: { organizationId } },
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
if (exact) return exact.id;
|
||||
const partial = await this.prisma.user.findFirst({
|
||||
where: {
|
||||
fullName: { contains: n, mode: 'insensitive' },
|
||||
memberships: { some: { organizationId } },
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
return partial?.id ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user