feat(contacts): keep Tahesab honorifics with person gender
Male and female names can store the title when requested, and Tahesab import and export use the same gender. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import {
|
||||
ArrayMaxSize,
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsEmail,
|
||||
IsIn,
|
||||
IsInt,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
MinLength,
|
||||
ValidateIf,
|
||||
} from 'class-validator';
|
||||
import { PERSON_IMPORT_SOURCES, PERSON_MOODS } from '@mento/shared';
|
||||
import { PERSON_GENDERS, PERSON_IMPORT_SOURCES, PERSON_MOODS } from '@mento/shared';
|
||||
|
||||
export class BirthdaySmsDto {
|
||||
@IsString()
|
||||
@@ -282,6 +283,14 @@ export class CreatePersonDto {
|
||||
@MaxLength(80)
|
||||
lastName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(PERSON_GENDERS)
|
||||
gender?: (typeof PERSON_GENDERS)[number];
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
showGenderInName?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
@@ -401,6 +410,14 @@ export class UpdatePersonDto {
|
||||
@MaxLength(80)
|
||||
lastName?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(PERSON_GENDERS)
|
||||
gender?: (typeof PERSON_GENDERS)[number];
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
showGenderInName?: boolean;
|
||||
|
||||
@IsOptional()
|
||||
@ValidateIf((_, v) => v !== '' && v != null)
|
||||
@IsString()
|
||||
|
||||
@@ -18,7 +18,12 @@ import type {
|
||||
PersonMood,
|
||||
UpdatePersonRequest,
|
||||
} from '@mento/shared';
|
||||
import { FILTERED_IDS_MAX, PERSON_MOODS } from '@mento/shared';
|
||||
import {
|
||||
FILTERED_IDS_MAX,
|
||||
PERSON_MOODS,
|
||||
applyPersonGenderTitle,
|
||||
splitPersonHonorific,
|
||||
} from '@mento/shared';
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { OutboundSmsLogService } from '../outbound-sms/outbound-sms-log.service';
|
||||
@@ -73,6 +78,8 @@ type PersonRow = {
|
||||
id: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
gender: string;
|
||||
showGenderInName: boolean;
|
||||
previousFirstName: string | null;
|
||||
previousLastName: string | null;
|
||||
importSource: string;
|
||||
@@ -417,10 +424,18 @@ export class PeopleService implements OnModuleInit {
|
||||
}
|
||||
|
||||
const registeredAt = dto.registeredAt ? new Date(dto.registeredAt) : null;
|
||||
const named = applyPersonGenderTitle({
|
||||
firstName: dto.firstName ?? '',
|
||||
lastName: dto.lastName ?? '',
|
||||
gender: dto.gender,
|
||||
showGenderInName: dto.showGenderInName,
|
||||
});
|
||||
const data: Prisma.PersonUncheckedCreateInput = {
|
||||
organizationId: actor.organizationId,
|
||||
firstName: (dto.firstName ?? '').trim(),
|
||||
lastName: (dto.lastName ?? '').trim(),
|
||||
firstName: named.firstName,
|
||||
lastName: named.lastName,
|
||||
gender: named.gender,
|
||||
showGenderInName: named.showGenderInName,
|
||||
previousFirstName: emptyToNull(dto.previousFirstName),
|
||||
previousLastName: emptyToNull(dto.previousLastName),
|
||||
importSource: normalizePersonImportSource(dto.importSource),
|
||||
@@ -497,8 +512,32 @@ export class PeopleService implements OnModuleInit {
|
||||
await this.ensureExists(actor, id);
|
||||
|
||||
const data: Prisma.PersonUncheckedUpdateInput = {};
|
||||
if (dto.firstName !== undefined) data.firstName = dto.firstName.trim();
|
||||
if (dto.lastName !== undefined) data.lastName = dto.lastName.trim();
|
||||
const touchesName =
|
||||
dto.firstName !== undefined ||
|
||||
dto.lastName !== undefined ||
|
||||
dto.gender !== undefined ||
|
||||
dto.showGenderInName !== undefined;
|
||||
if (touchesName) {
|
||||
const existing = await this.prisma.person.findFirst({
|
||||
where: { id, organizationId: actor.organizationId },
|
||||
select: { firstName: true, lastName: true, gender: true, showGenderInName: true },
|
||||
});
|
||||
if (!existing) throw new NotFoundException('شخص پیدا نشد');
|
||||
const bare = splitPersonHonorific(existing.firstName, existing.lastName);
|
||||
const storedGender =
|
||||
existing.gender === 'male' || existing.gender === 'female' ? existing.gender : 'none';
|
||||
const named = applyPersonGenderTitle({
|
||||
firstName: dto.firstName !== undefined ? dto.firstName : bare.firstName,
|
||||
lastName: dto.lastName !== undefined ? dto.lastName : bare.lastName,
|
||||
gender: dto.gender !== undefined ? dto.gender : storedGender,
|
||||
showGenderInName:
|
||||
dto.showGenderInName !== undefined ? dto.showGenderInName : existing.showGenderInName,
|
||||
});
|
||||
data.firstName = named.firstName;
|
||||
data.lastName = named.lastName;
|
||||
data.gender = named.gender;
|
||||
data.showGenderInName = named.showGenderInName;
|
||||
}
|
||||
if ('previousFirstName' in dto) {
|
||||
data.previousFirstName = emptyToNull(dto.previousFirstName);
|
||||
}
|
||||
@@ -809,6 +848,8 @@ export class PeopleService implements OnModuleInit {
|
||||
previousImportSource: row.previousImportSource,
|
||||
previousSourceRef: row.previousSourceRef,
|
||||
fullName: `${row.firstName} ${row.lastName}`.trim(),
|
||||
gender: row.gender === 'male' || row.gender === 'female' ? row.gender : 'none',
|
||||
showGenderInName: row.showGenderInName,
|
||||
email: row.email,
|
||||
phone: row.phone,
|
||||
mobile: row.mobile,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../../prisma/prisma.service';
|
||||
import { carryPreviousSource } from '../../contacts/person-source';
|
||||
import { applyPersonGenderTitle } from '@mento/shared';
|
||||
import { TahesabConnectionsService } from './connections.service';
|
||||
import {
|
||||
CreateInTahesabDto,
|
||||
@@ -573,9 +574,9 @@ export class TahesabPersonLinkService {
|
||||
}
|
||||
const name = pickName(row) || `مشتری تهحساب ${dto.moshtariCode}`;
|
||||
const tel = pickTel(row);
|
||||
const parts = name.trim().split(/\s+/);
|
||||
const firstName = parts[0] || name;
|
||||
const lastName = parts.slice(1).join(' ') || '—';
|
||||
const named = applyPersonGenderTitle({ firstName: name, lastName: '' });
|
||||
const firstName = named.firstName;
|
||||
const lastName = named.lastName;
|
||||
const mobile = tel || null;
|
||||
const sourceRef = String(dto.moshtariCode);
|
||||
|
||||
@@ -591,6 +592,8 @@ export class TahesabPersonLinkService {
|
||||
data: {
|
||||
firstName,
|
||||
lastName,
|
||||
gender: named.gender,
|
||||
showGenderInName: named.showGenderInName,
|
||||
...(nameChanged
|
||||
? {
|
||||
previousFirstName: prevFirst || null,
|
||||
@@ -631,6 +634,8 @@ export class TahesabPersonLinkService {
|
||||
data: {
|
||||
firstName,
|
||||
lastName,
|
||||
gender: named.gender,
|
||||
showGenderInName: named.showGenderInName,
|
||||
...(nameChanged
|
||||
? {
|
||||
previousFirstName: byMobile.firstName || null,
|
||||
@@ -650,6 +655,8 @@ export class TahesabPersonLinkService {
|
||||
organizationId,
|
||||
firstName,
|
||||
lastName,
|
||||
gender: named.gender,
|
||||
showGenderInName: named.showGenderInName,
|
||||
mobile,
|
||||
phone: mobile,
|
||||
notes: `واردشده از تهحساب (کد ${dto.moshtariCode})`,
|
||||
|
||||
Reference in New Issue
Block a user