Edit import excel
This commit is contained in:
@@ -56,13 +56,20 @@ export class CompaniesController {
|
||||
return new StreamableFile(buffer);
|
||||
}
|
||||
|
||||
@Post('import/preview')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
previewImport(@UploadedFile() file: Express.Multer.File) {
|
||||
return this.excelService.previewCompanies(file);
|
||||
}
|
||||
|
||||
@Post('import')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
import(
|
||||
@CurrentUser() user: Actor,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@Body('mapping') mapping?: string,
|
||||
) {
|
||||
return this.excelService.importCompanies(user, file);
|
||||
return this.excelService.importCompanies(user, file, mapping);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
|
||||
@@ -6,8 +6,11 @@ import type { Actor } from '../common/record-scope';
|
||||
import { ownerWhere } from '../common/record-scope';
|
||||
import {
|
||||
formatJalaliDate,
|
||||
parseExcelMapping,
|
||||
previewExcel,
|
||||
readMappedRows,
|
||||
writeXlsx,
|
||||
type ExcelPreviewResult,
|
||||
} from '../common/excel';
|
||||
import { CompaniesService } from './companies.service';
|
||||
import { PeopleService } from './people.service';
|
||||
@@ -107,6 +110,19 @@ const PERSON_ALIASES: Record<string, string> = {
|
||||
ستاره: 'star',
|
||||
};
|
||||
|
||||
const PERSON_FIELDS = [
|
||||
{ key: 'firstName', label: 'نام' },
|
||||
{ key: 'lastName', label: 'نام خانوادگی' },
|
||||
{ key: 'title', label: 'نام کامل / عنوان' },
|
||||
{ key: 'mobile', label: 'موبایل' },
|
||||
{ key: 'phone', label: 'تلفن' },
|
||||
{ key: 'email', label: 'ایمیل' },
|
||||
{ key: 'position', label: 'سمت' },
|
||||
{ key: 'companyName', label: 'شرکت' },
|
||||
{ key: 'notes', label: 'یادداشت' },
|
||||
{ key: 'mood', label: 'وضعیت مشتری' },
|
||||
];
|
||||
|
||||
const COMPANY_ALIASES: Record<string, string> = {
|
||||
name: 'name',
|
||||
نام: 'name',
|
||||
@@ -126,6 +142,16 @@ const COMPANY_ALIASES: Record<string, string> = {
|
||||
یادداشت: 'notes',
|
||||
};
|
||||
|
||||
const COMPANY_FIELDS = [
|
||||
{ key: 'name', label: 'نام شرکت', required: true },
|
||||
{ key: 'nationalId', label: 'شناسه ملی' },
|
||||
{ key: 'phone', label: 'تلفن' },
|
||||
{ key: 'email', label: 'ایمیل' },
|
||||
{ key: 'website', label: 'وبسایت' },
|
||||
{ key: 'address', label: 'آدرس' },
|
||||
{ key: 'notes', label: 'یادداشت' },
|
||||
];
|
||||
|
||||
const CONTACT_COLUMNS = [
|
||||
{ header: '_type', key: '_type', width: 12 },
|
||||
{ header: 'کد دیدار مشتری', key: 'didarId', width: 16 },
|
||||
@@ -294,11 +320,20 @@ export class ContactsExcelService {
|
||||
);
|
||||
}
|
||||
|
||||
previewPeople(file: Express.Multer.File): Promise<ExcelPreviewResult> {
|
||||
return previewExcel(file, PERSON_ALIASES, PERSON_FIELDS);
|
||||
}
|
||||
|
||||
previewCompanies(file: Express.Multer.File): Promise<ExcelPreviewResult> {
|
||||
return previewExcel(file, COMPANY_ALIASES, COMPANY_FIELDS);
|
||||
}
|
||||
|
||||
async importPeople(
|
||||
actor: Actor,
|
||||
file: Express.Multer.File,
|
||||
mappingRaw?: string,
|
||||
): Promise<ImportContactsResult> {
|
||||
const rows = await readMappedRows(file, PERSON_ALIASES);
|
||||
const rows = await readMappedRows(file, PERSON_ALIASES, parseExcelMapping(mappingRaw));
|
||||
const result: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
@@ -307,9 +342,18 @@ export class ContactsExcelService {
|
||||
if ((row._type || '').trim() && row._type.trim().toLowerCase() !== 'contact') {
|
||||
continue;
|
||||
}
|
||||
const names = splitDidarName(row.firstName ?? '', row.lastName ?? '', row.title);
|
||||
if (!names) {
|
||||
result.errors.push({ row: rowNum, message: 'نام مشتری الزامی است' });
|
||||
const names = splitDidarName(row.firstName ?? '', row.lastName ?? '', row.title) ?? {
|
||||
firstName: (row.firstName ?? '').trim().slice(0, 80),
|
||||
lastName: (row.lastName ?? '').trim().slice(0, 80),
|
||||
};
|
||||
if (
|
||||
!names.firstName &&
|
||||
!names.lastName &&
|
||||
!row.mobile?.trim() &&
|
||||
!row.email?.trim() &&
|
||||
!row.phone?.trim()
|
||||
) {
|
||||
result.errors.push({ row: rowNum, message: 'حداقل نام، موبایل، تلفن یا ایمیل لازم است' });
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -357,8 +401,9 @@ export class ContactsExcelService {
|
||||
async importCompanies(
|
||||
actor: Actor,
|
||||
file: Express.Multer.File,
|
||||
mappingRaw?: string,
|
||||
): Promise<ImportContactsResult> {
|
||||
const rows = await readMappedRows(file, COMPANY_ALIASES);
|
||||
const rows = await readMappedRows(file, COMPANY_ALIASES, parseExcelMapping(mappingRaw));
|
||||
const result: ImportContactsResult = { created: 0, skipped: 0, errors: [] };
|
||||
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
|
||||
@@ -135,15 +135,15 @@ export class UpdateCompanyDto {
|
||||
}
|
||||
|
||||
export class CreatePersonDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(80)
|
||||
firstName!: string;
|
||||
firstName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(80)
|
||||
lastName!: string;
|
||||
lastName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@@ -192,13 +192,11 @@ export class CreatePersonDto {
|
||||
export class UpdatePersonDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(80)
|
||||
firstName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@MinLength(1)
|
||||
@MaxLength(80)
|
||||
lastName?: string;
|
||||
|
||||
|
||||
@@ -57,13 +57,22 @@ export class PeopleController {
|
||||
return new StreamableFile(buffer);
|
||||
}
|
||||
|
||||
@Post('import/preview')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
previewImport(
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
) {
|
||||
return this.excelService.previewPeople(file);
|
||||
}
|
||||
|
||||
@Post('import')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
import(
|
||||
@CurrentUser() user: Actor,
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@Body('mapping') mapping?: string,
|
||||
) {
|
||||
return this.excelService.importPeople(user, file);
|
||||
return this.excelService.importPeople(user, file, mapping);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
|
||||
@@ -134,8 +134,8 @@ export class PeopleService {
|
||||
|
||||
const data: Prisma.PersonUncheckedCreateInput = {
|
||||
organizationId: actor.organizationId,
|
||||
firstName: dto.firstName.trim(),
|
||||
lastName: dto.lastName.trim(),
|
||||
firstName: (dto.firstName ?? '').trim(),
|
||||
lastName: (dto.lastName ?? '').trim(),
|
||||
email: normalizeEmail(dto.email),
|
||||
phone: normalizePhone(dto.phone),
|
||||
mobile: normalizePhone(dto.mobile),
|
||||
|
||||
Reference in New Issue
Block a user