fix: keep birthdays and show Shamsi dates

Imported customers keep their birthday, and assistant text plus remaining calendars use the Jalali date.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-13 18:04:53 +03:30
co-authored by Cursor
parent 5f4cc47c41
commit cf326d1ed9
34 changed files with 637 additions and 105 deletions
+2
View File
@@ -22,6 +22,7 @@ export class CreateLeadDto {
@IsOptional() @IsString() city?: string;
@IsOptional() @IsString() region?: string;
@IsOptional() @IsString() instagram?: string;
@IsOptional() @IsString() birthDate?: string | null;
@IsOptional() @IsArray() @IsString({ each: true }) addresses?: string[];
@IsOptional() @IsString() trade?: string;
@IsOptional() @IsString() source?: string;
@@ -42,6 +43,7 @@ export class UpdateLeadDto {
@IsOptional() @IsString() city?: string | null;
@IsOptional() @IsString() region?: string | null;
@IsOptional() @IsString() instagram?: string | null;
@IsOptional() @IsString() birthDate?: string | null;
@IsOptional() @IsArray() @IsString({ each: true }) addresses?: string[];
@IsOptional() @IsString() trade?: string | null;
@IsOptional() @IsString() source?: string | null;
@@ -4,6 +4,7 @@ import { Prisma } from '@prisma/client';
import type { LeadImportJobDto } from '@mento/shared';
import { PrismaService } from '../prisma/prisma.service';
import {
parseBirthDate,
parseExcelMapping,
parseJalaliDateTime,
previewExcel,
@@ -55,6 +56,12 @@ const LEAD_ALIASES: Record<string, string> = {
اینستاگرام: 'instagram',
پیجاینستاگرام: 'instagram',
آیدیاینستاگرام: 'instagram',
birthdate: 'birthDate',
dateofbirth: 'birthDate',
bdate: 'birthDate',
تاریختولد: 'birthDate',
تاریختولدمشتری: 'birthDate',
تاریختولدشمسی: 'birthDate',
registeredat: 'registeredAt',
تاریخثبت: 'registeredAt',
تاریخثبتمشتری: 'registeredAt',
@@ -81,6 +88,7 @@ const LEAD_FIELDS: ExcelFieldOption[] = [
{ key: 'homeAddress', label: 'آدرس منزل' },
{ key: 'instagram', label: 'اینستاگرام' },
{ key: 'registeredAt', label: 'تاریخ ثبت' },
{ key: 'birthDate', label: 'تاریخ تولد' },
{ key: 'trade', label: 'صنف' },
{ key: 'source', label: 'منبع' },
{ key: 'notes', label: 'توضیحات' },
@@ -284,6 +292,9 @@ export class LeadsImportService {
address2: get('address2', 'آدرس ۲', 'آدرس2', 'آدرس دوم'),
homeAddress: get('homeAddress', 'آدرس منزل', 'آدرس خانه'),
instagram: get('instagram', 'اینستاگرام', 'پیج اینستاگرام', 'آی دی اینستاگرام'),
birthDate: parseBirthDate(
get('birthDate', 'تاریخ تولد', 'تاریخ تولد مشتری', 'تاریخ تولد شمسی', 'birthdate'),
)?.toISOString() ?? '',
registeredAt,
trade: get('trade', 'صنف', 'رسته'),
source: get('source', 'منبع', 'منبع لید'),
+34 -2
View File
@@ -5,6 +5,7 @@ import {
NotFoundException,
} from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { parseBirthDate } from '../common/excel';
import * as path from 'node:path';
import { randomUUID } from 'node:crypto';
import type {
@@ -268,6 +269,7 @@ export class LeadsService {
city?: string | null;
region?: string | null;
instagram?: string | null;
birthDate?: string | null;
addresses?: string[];
trade?: string | null;
source?: string | null;
@@ -314,6 +316,7 @@ export class LeadsService {
city: emptyToNull(dto.city),
region: emptyToNull(dto.region),
instagram: emptyToNull(dto.instagram),
birthDate: parseBirthDate(dto.birthDate),
addresses: toJsonAddresses(dto.addresses),
trade: emptyToNull(dto.trade),
source: emptyToNull(dto.source),
@@ -398,6 +401,9 @@ export class LeadsService {
...(dto.instagram !== undefined
? { instagram: emptyToNull(dto.instagram as string) }
: {}),
...(dto.birthDate !== undefined
? { birthDate: parseBirthDate(dto.birthDate as string | null) }
: {}),
...(dto.addresses !== undefined
? {
addresses: toJsonAddresses(
@@ -802,6 +808,7 @@ export class LeadsService {
mobile: lead.mobile,
phone: lead.phone,
instagram: lead.instagram,
birthDate: lead.birthDate,
addresses: toJsonAddresses(asAddresses(lead.addresses)),
notes: [
lead.notes,
@@ -819,6 +826,11 @@ export class LeadsService {
},
});
createdPerson = true;
} else if (!person.birthDate && lead.birthDate) {
await this.prisma.person.update({
where: { id: person.id },
data: { birthDate: lead.birthDate },
});
}
const convertedStage = await this.prisma.leadStage.findFirst({
@@ -985,6 +997,7 @@ export class LeadsService {
address2?: string;
homeAddress?: string;
instagram?: string;
birthDate?: string;
registeredAt?: string;
trade?: string;
source?: string;
@@ -996,11 +1009,20 @@ export class LeadsService {
const title = String(row.title ?? row.contactName ?? '').trim();
if (!mobile || !title) return 'error';
const birthDate = parseBirthDate(row.birthDate);
const existingLead = await this.prisma.lead.findFirst({
where: { organizationId, mobile },
select: { id: true },
});
if (existingLead) return 'duplicate';
if (existingLead) {
if (birthDate) {
await this.prisma.lead.updateMany({
where: { id: existingLead.id, birthDate: null },
data: { birthDate },
});
}
return 'duplicate';
}
const existingPerson = await this.prisma.person.findFirst({
where: {
@@ -1009,7 +1031,15 @@ export class LeadsService {
},
select: { id: true },
});
if (existingPerson) return 'duplicate';
if (existingPerson) {
if (birthDate) {
await this.prisma.person.updateMany({
where: { id: existingPerson.id, birthDate: null },
data: { birthDate },
});
}
return 'duplicate';
}
try {
const registeredAt = row.registeredAt ? new Date(row.registeredAt) : null;
@@ -1023,6 +1053,7 @@ export class LeadsService {
city: emptyToNull(row.city),
region: emptyToNull(row.region),
instagram: emptyToNull(row.instagram),
birthDate,
addresses: toJsonAddresses(
collectAddresses(row.address, row.address2, row.homeAddress),
),
@@ -1101,6 +1132,7 @@ export class LeadsService {
city: row.city,
region: row.region,
instagram: row.instagram,
birthDate: row.birthDate?.toISOString() ?? null,
addresses: asAddresses(row.addresses),
trade: row.trade,
source: row.source,