|
|
|
@@ -50,6 +50,7 @@ import {
|
|
|
|
|
normalizePersonImportSource,
|
|
|
|
|
personImportSourceLabel,
|
|
|
|
|
} from './person-source';
|
|
|
|
|
import { BehaviorAccessService } from '../behavior/behavior-access.service';
|
|
|
|
|
|
|
|
|
|
const personInclude = {
|
|
|
|
|
company: { select: { id: true, name: true, province: true, city: true } },
|
|
|
|
@@ -98,8 +99,27 @@ type PersonRow = {
|
|
|
|
|
minPoints: number;
|
|
|
|
|
color: string | null;
|
|
|
|
|
} | null;
|
|
|
|
|
discLabel: string | null;
|
|
|
|
|
discReliable: boolean;
|
|
|
|
|
discAssessedAt: Date | null;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
function discFilter(raw: string | undefined): Prisma.PersonWhereInput | null {
|
|
|
|
|
const value = raw?.trim().toUpperCase();
|
|
|
|
|
if (!value || !/^[DISC](\/[DISC]){0,3}$/.test(value)) return null;
|
|
|
|
|
if (value.includes('/')) {
|
|
|
|
|
return { discReliable: true, discLabel: value };
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
|
discReliable: true,
|
|
|
|
|
OR: [
|
|
|
|
|
{ discLabel: value },
|
|
|
|
|
{ discLabel: { startsWith: `${value}/` } },
|
|
|
|
|
{ discLabel: { contains: `/${value}` } },
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class PeopleService implements OnModuleInit {
|
|
|
|
|
private readonly logger = new Logger(PeopleService.name);
|
|
|
|
@@ -113,6 +133,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
private readonly game: GamificationService,
|
|
|
|
|
private readonly sms: SmsService,
|
|
|
|
|
private readonly outboundSms: OutboundSmsLogService,
|
|
|
|
|
private readonly behaviorAccess: BehaviorAccessService,
|
|
|
|
|
) {}
|
|
|
|
|
|
|
|
|
|
private personListWhere(
|
|
|
|
@@ -124,6 +145,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
province?: string;
|
|
|
|
|
city?: string;
|
|
|
|
|
hasMobile?: boolean;
|
|
|
|
|
disc?: string;
|
|
|
|
|
},
|
|
|
|
|
): Prisma.PersonWhereInput {
|
|
|
|
|
const sourceFilter = opts?.importSource?.trim()
|
|
|
|
@@ -166,6 +188,8 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
if (opts?.hasMobile) {
|
|
|
|
|
and.push({ mobile: { not: null } }, { NOT: { mobile: '' } });
|
|
|
|
|
}
|
|
|
|
|
const disc = discFilter(opts?.disc);
|
|
|
|
|
if (disc) and.push(disc);
|
|
|
|
|
return {
|
|
|
|
|
organizationId: actor.organizationId,
|
|
|
|
|
...ownerWhere(actor),
|
|
|
|
@@ -187,9 +211,11 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
province?: string;
|
|
|
|
|
city?: string;
|
|
|
|
|
hasMobile?: boolean;
|
|
|
|
|
disc?: string;
|
|
|
|
|
},
|
|
|
|
|
): Promise<PaginatedResponse<PersonDto>> {
|
|
|
|
|
const where = this.personListWhere(actor, q, opts);
|
|
|
|
|
const seeDisc = await this.behaviorAccess.canView(actor);
|
|
|
|
|
const where = this.personListWhere(actor, q, seeDisc ? opts : { ...opts, disc: undefined });
|
|
|
|
|
|
|
|
|
|
const [rows, total] = await this.prisma.$transaction([
|
|
|
|
|
this.prisma.person.findMany({
|
|
|
|
@@ -206,7 +232,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
items: rows.map((row) => this.toDto(row)),
|
|
|
|
|
items: rows.map((row) => this.toDto(row, seeDisc)),
|
|
|
|
|
total,
|
|
|
|
|
page,
|
|
|
|
|
limit,
|
|
|
|
@@ -222,10 +248,12 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
province?: string;
|
|
|
|
|
city?: string;
|
|
|
|
|
hasMobile?: boolean;
|
|
|
|
|
disc?: string;
|
|
|
|
|
},
|
|
|
|
|
max = FILTERED_IDS_MAX,
|
|
|
|
|
): Promise<FilteredIdsResponse> {
|
|
|
|
|
const where = this.personListWhere(actor, q, opts);
|
|
|
|
|
const seeDisc = await this.behaviorAccess.canView(actor);
|
|
|
|
|
const where = this.personListWhere(actor, q, seeDisc ? opts : { ...opts, disc: undefined });
|
|
|
|
|
const total = await this.prisma.person.count({ where });
|
|
|
|
|
if (total > max) {
|
|
|
|
|
throw new BadRequestException(
|
|
|
|
@@ -330,7 +358,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
include: personInclude,
|
|
|
|
|
});
|
|
|
|
|
if (!row) throw new NotFoundException('شخص پیدا نشد');
|
|
|
|
|
return this.toDto(row);
|
|
|
|
|
return this.toDto(row, await this.behaviorAccess.canView(actor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async create(actor: Actor, dto: CreatePersonRequest): Promise<PersonDto> {
|
|
|
|
@@ -407,7 +435,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
where: { id: row.id },
|
|
|
|
|
include: personInclude,
|
|
|
|
|
});
|
|
|
|
|
return this.toDto(fresh ?? row);
|
|
|
|
|
return this.toDto(fresh ?? row, await this.behaviorAccess.canView(actor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async update(
|
|
|
|
@@ -473,7 +501,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
email: row.email,
|
|
|
|
|
companyId: row.companyId,
|
|
|
|
|
});
|
|
|
|
|
return this.toDto(row);
|
|
|
|
|
return this.toDto(row, await this.behaviorAccess.canView(actor));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async remove(actor: Actor, id: string): Promise<void> {
|
|
|
|
@@ -697,7 +725,7 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private toDto(row: PersonRow): PersonDto {
|
|
|
|
|
private toDto(row: PersonRow, includeDisc: boolean): PersonDto {
|
|
|
|
|
const previousFullName = `${row.previousFirstName ?? ''} ${row.previousLastName ?? ''}`.trim();
|
|
|
|
|
const importSource = row.importSource || 'manual';
|
|
|
|
|
return {
|
|
|
|
@@ -736,6 +764,10 @@ export class PeopleService implements OnModuleInit {
|
|
|
|
|
customFields: asCustomFields(row.customFields),
|
|
|
|
|
loyaltyPoints: row.loyaltyPoints ?? 0,
|
|
|
|
|
loyaltyTier: row.loyaltyTier,
|
|
|
|
|
discLabel: includeDisc && row.discReliable ? row.discLabel : null,
|
|
|
|
|
discReliable: includeDisc ? row.discReliable : false,
|
|
|
|
|
discAssessedAt:
|
|
|
|
|
includeDisc && row.discAssessedAt ? row.discAssessedAt.toISOString() : null,
|
|
|
|
|
company: row.company,
|
|
|
|
|
owner: row.owner,
|
|
|
|
|
createdAt: row.createdAt.toISOString(),
|
|
|
|
|