From 98099e97e7f276022e7b4fc27e33ac038fed0e14 Mon Sep 17 00:00:00 2001 From: ahasani194 Date: Tue, 19 May 2026 20:34:05 +0330 Subject: [PATCH] feat: add field validators for username and password, and integrate them into DTOs --- .../fiscalId-validator.ts | 14 ++++ .../utils/field-validator.util/index.ts | 3 + .../utils/field-validator.util/password.ts | 10 +++ .../utils/field-validator.util/username.ts | 14 ++++ src/common/utils/index.ts | 1 + .../consumers/accounts/dto/account.dto.ts | 3 + .../admin/consumers/dto/consumer.dto.ts | 3 + src/modules/admin/partners/dto/partner.dto.ts | 3 + .../partnerAccounts/dto/account.dto.ts | 3 + .../admin/users/accounts/dto/account.dto.ts | 3 + src/modules/auth/dto/login.dto.ts | 7 +- src/modules/catalog/catalog.service.ts | 18 ++--- .../consumer/accounts/dto/account.dto.ts | 3 + .../consumers/accounts/dto/account.dto.ts | 3 + .../business-activities.service.ts | 68 ++++++++++++++----- .../dto/create-business-activities.dto.ts | 10 ++- .../consumers/dto/create-consumers.dto.ts | 3 + 17 files changed, 136 insertions(+), 33 deletions(-) create mode 100644 src/common/utils/field-validator.util/fiscalId-validator.ts create mode 100644 src/common/utils/field-validator.util/index.ts create mode 100644 src/common/utils/field-validator.util/password.ts create mode 100644 src/common/utils/field-validator.util/username.ts diff --git a/src/common/utils/field-validator.util/fiscalId-validator.ts b/src/common/utils/field-validator.util/fiscalId-validator.ts new file mode 100644 index 0000000..ca308c1 --- /dev/null +++ b/src/common/utils/field-validator.util/fiscalId-validator.ts @@ -0,0 +1,14 @@ +import { Matches, MinLength } from 'class-validator' + +const MIN_LENGTH = 6 +const REGEX = /^[a-zA-Z0-9]*$/ + +export const FiscalIdFieldValidator = () => + function (target: object, propertyKey: string) { + MinLength(MIN_LENGTH, { + message: `شناسه یکتا باید حداقل ${MIN_LENGTH} کاراکتر باشد`, + })(target, propertyKey) + Matches(REGEX, { + message: 'شناسه یکتا فقط می‌تواند شامل حروف و اعداد باشد', + })(target, propertyKey) + } diff --git a/src/common/utils/field-validator.util/index.ts b/src/common/utils/field-validator.util/index.ts new file mode 100644 index 0000000..a4277c4 --- /dev/null +++ b/src/common/utils/field-validator.util/index.ts @@ -0,0 +1,3 @@ +export { FiscalIdFieldValidator } from './fiscalId-validator' +export { PasswordFieldValidator, PASSWORD_MIN_LENGTH } from './password' +export { UsernameFieldValidator } from './username' diff --git a/src/common/utils/field-validator.util/password.ts b/src/common/utils/field-validator.util/password.ts new file mode 100644 index 0000000..23cb738 --- /dev/null +++ b/src/common/utils/field-validator.util/password.ts @@ -0,0 +1,10 @@ +import { MinLength } from 'class-validator' + +export const PASSWORD_MIN_LENGTH = 6 + +export const PasswordFieldValidator = () => + function (target: object, propertyKey: string) { + MinLength(PASSWORD_MIN_LENGTH, { + message: `رمز عبور باید حداقل ${PASSWORD_MIN_LENGTH} کاراکتر باشد`, + })(target, propertyKey) + } diff --git a/src/common/utils/field-validator.util/username.ts b/src/common/utils/field-validator.util/username.ts new file mode 100644 index 0000000..36456f8 --- /dev/null +++ b/src/common/utils/field-validator.util/username.ts @@ -0,0 +1,14 @@ +import { Matches, MinLength } from 'class-validator' + +const USERNAME_REGEX = /^[a-zA-Z0-9_-]*$/ +const USERNAME_MIN_LENGTH = 6 + +export const UsernameFieldValidator = () => + function (target: object, propertyKey: string) { + MinLength(USERNAME_MIN_LENGTH, { + message: `نام کاربری باید حداقل ${USERNAME_MIN_LENGTH} کاراکتر باشد`, + })(target, propertyKey) + Matches(USERNAME_REGEX, { + message: 'نام کاربری فقط می‌تواند شامل حروف، اعداد، زیرخط و خط تیره باشد', + })(target, propertyKey) + } diff --git a/src/common/utils/index.ts b/src/common/utils/index.ts index 6cc9ba7..41505bb 100644 --- a/src/common/utils/index.ts +++ b/src/common/utils/index.ts @@ -1,4 +1,5 @@ export * from './enum-translator.util' +export * from './field-validator.util' export * from './http-client.util' export * from './jwt-user.util' export * from './mappers/consumer_mappers.util' diff --git a/src/modules/admin/consumers/accounts/dto/account.dto.ts b/src/modules/admin/consumers/accounts/dto/account.dto.ts index 4cd146e..a83cf8e 100644 --- a/src/modules/admin/consumers/accounts/dto/account.dto.ts +++ b/src/modules/admin/consumers/accounts/dto/account.dto.ts @@ -1,13 +1,16 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' import { AccountStatus, ConsumerRole } from 'generated/prisma/enums' export class CreateConsumerAccountDto { @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string diff --git a/src/modules/admin/consumers/dto/consumer.dto.ts b/src/modules/admin/consumers/dto/consumer.dto.ts index 5ebfdb8..1b0f6cb 100644 --- a/src/modules/admin/consumers/dto/consumer.dto.ts +++ b/src/modules/admin/consumers/dto/consumer.dto.ts @@ -1,3 +1,4 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ConsumerStatus } from '@/generated/prisma/enums' import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' @@ -16,10 +17,12 @@ export class CreateConsumerDto { last_name: string @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string diff --git a/src/modules/admin/partners/dto/partner.dto.ts b/src/modules/admin/partners/dto/partner.dto.ts index 08f4a32..13b99c9 100644 --- a/src/modules/admin/partners/dto/partner.dto.ts +++ b/src/modules/admin/partners/dto/partner.dto.ts @@ -1,3 +1,4 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { PartnerStatus, TspProviderType } from '@/generated/prisma/enums' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator' @@ -21,10 +22,12 @@ export class CreatePartnerDto { // license_quota?: number @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string diff --git a/src/modules/admin/partners/partnerAccounts/dto/account.dto.ts b/src/modules/admin/partners/partnerAccounts/dto/account.dto.ts index f6b5810..990d425 100644 --- a/src/modules/admin/partners/partnerAccounts/dto/account.dto.ts +++ b/src/modules/admin/partners/partnerAccounts/dto/account.dto.ts @@ -1,3 +1,4 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' import { PartnerRole } from 'generated/prisma/enums' @@ -5,9 +6,11 @@ import { PartnerRole } from 'generated/prisma/enums' export class CreatePartnerAccountDto { @IsString() @ApiProperty({}) + @UsernameFieldValidator() username: string @IsString() + @PasswordFieldValidator() @ApiProperty({}) password: string } diff --git a/src/modules/admin/users/accounts/dto/account.dto.ts b/src/modules/admin/users/accounts/dto/account.dto.ts index 28ffdb3..d0ff779 100644 --- a/src/modules/admin/users/accounts/dto/account.dto.ts +++ b/src/modules/admin/users/accounts/dto/account.dto.ts @@ -1,13 +1,16 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' import { AccountStatus, AccountType } from 'generated/prisma/enums' export class CreateAccountDto { @IsString() + @UsernameFieldValidator() @ApiProperty() username: string @IsString() + @PasswordFieldValidator() @ApiProperty() password: string diff --git a/src/modules/auth/dto/login.dto.ts b/src/modules/auth/dto/login.dto.ts index c73d77c..03328d5 100644 --- a/src/modules/auth/dto/login.dto.ts +++ b/src/modules/auth/dto/login.dto.ts @@ -2,11 +2,14 @@ import { ApiProperty } from '@nestjs/swagger' import { IsBoolean, IsOptional, IsString } from 'class-validator' export class LoginDto { - @ApiProperty({ description: 'Mobile number used as username', example: '09120258156' }) + @ApiProperty({ + description: 'you can use your mobile number as username', + example: '09120258156', + }) @IsString() username: string - @ApiProperty({ description: 'OTP password (one-time password)', example: '11111' }) + @ApiProperty({ example: '11111' }) @IsString() password: string diff --git a/src/modules/catalog/catalog.service.ts b/src/modules/catalog/catalog.service.ts index a950360..aa07b0b 100644 --- a/src/modules/catalog/catalog.service.ts +++ b/src/modules/catalog/catalog.service.ts @@ -52,20 +52,14 @@ export class CatalogsService { async getSKU(search: string) { const items = await this.prisma.stockKeepingUnits.findMany({ - // where: { - // OR: [{ code: { contains: search } }, { name: { contains: search } }], - // }, - select: { - id: true, - code: true, - name: true, - is_domestic: true, - is_public: true, - VAT: true, - }, take: 100, }) - return ResponseMapper.list(items) + + const mappedItems = items.map(item => ({ + ...item, + fullname: `${item.code} - ${item.name}`, + })) + return ResponseMapper.list(mappedItems) } async getGuildGoodCategories(guild_id: string) { diff --git a/src/modules/consumer/accounts/dto/account.dto.ts b/src/modules/consumer/accounts/dto/account.dto.ts index 11d7d94..8635ebc 100644 --- a/src/modules/consumer/accounts/dto/account.dto.ts +++ b/src/modules/consumer/accounts/dto/account.dto.ts @@ -1,13 +1,16 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' import { AccountStatus } from 'generated/prisma/enums' export class CreateConsumerAccountDto { @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string diff --git a/src/modules/partners/consumers/accounts/dto/account.dto.ts b/src/modules/partners/consumers/accounts/dto/account.dto.ts index 4cd146e..a83cf8e 100644 --- a/src/modules/partners/consumers/accounts/dto/account.dto.ts +++ b/src/modules/partners/consumers/accounts/dto/account.dto.ts @@ -1,13 +1,16 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' import { IsEnum, IsOptional, IsString } from 'class-validator' import { AccountStatus, ConsumerRole } from 'generated/prisma/enums' export class CreateConsumerAccountDto { @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string diff --git a/src/modules/partners/consumers/business-activities/business-activities.service.ts b/src/modules/partners/consumers/business-activities/business-activities.service.ts index 53f79e8..1a706eb 100644 --- a/src/modules/partners/consumers/business-activities/business-activities.service.ts +++ b/src/modules/partners/consumers/business-activities/business-activities.service.ts @@ -1,9 +1,12 @@ import { RedisKeyMaker } from '@/common/utils' -import { BusinessActivitySelect } from '@/generated/prisma/models' +import { + BusinessActivityCreateInput, + BusinessActivitySelect, +} from '@/generated/prisma/models' import { getPartnerFirstRemainingLicense } from '@/modules/partners/utils/getPartnerRemainingLicenses.util' import { PrismaService } from '@/prisma/prisma.service' import { RedisService } from '@/redis/redis.service' -import { BadRequestException, Injectable } from '@nestjs/common' +import { Injectable } from '@nestjs/common' import { ResponseMapper } from 'common/response/response-mapper' import { PartnersCacheInvalidationService } from '../../cache/partners-cache-invalidation.service' import { CreateBusinessActivitiesDto } from './dto/create-business-activities.dto' @@ -158,6 +161,37 @@ export class BusinessActivitiesService { const license = await getPartnerFirstRemainingLicense(tx, { partner_id }) const { guild_id, license_starts_at, expires_at, ...rest } = data + + const dataToCreate: BusinessActivityCreateInput = { + ...rest, + economic_code: String(data.economic_code), + guild: { + connect: { + id: guild_id, + }, + }, + consumer: { + connect: { + id: consumer_id, + }, + }, + } + + if (license_starts_at) { + dataToCreate.license_activation = { + create: { + starts_at: license_starts_at ?? new Date(), + expires_at: + expires_at ?? this.setExpireDate(new Date(license_starts_at || '')), + license: { + connect: { + id: license.id, + }, + }, + }, + } + } + const createdBusinessActivity = await tx.businessActivity.create({ data: { ...rest, @@ -196,25 +230,23 @@ export class BusinessActivitiesService { }) const activationId = createdBusinessActivity.license_activation?.id - if (!activationId) { - throw new BadRequestException('لایسنس برای این فعالیت تجاری ایجاد نشد.') - } - - const licenseAllocationCreation = Array.from({ - length: license.accounts_limit, - }).map(() => - tx.licenseAccountAllocation.create({ - data: { - license_activation: { - connect: { - id: activationId, + if (activationId) { + const licenseAllocationCreation = Array.from({ + length: license.accounts_limit, + }).map(() => + tx.licenseAccountAllocation.create({ + data: { + license_activation: { + connect: { + id: activationId, + }, }, }, - }, - }), - ) + }), + ) - await Promise.all(licenseAllocationCreation) + await Promise.all(licenseAllocationCreation) + } return tx.businessActivity.findUniqueOrThrow({ where: { diff --git a/src/modules/partners/consumers/business-activities/dto/create-business-activities.dto.ts b/src/modules/partners/consumers/business-activities/dto/create-business-activities.dto.ts index d343314..6de78d1 100644 --- a/src/modules/partners/consumers/business-activities/dto/create-business-activities.dto.ts +++ b/src/modules/partners/consumers/business-activities/dto/create-business-activities.dto.ts @@ -1,5 +1,6 @@ +import { FiscalIdFieldValidator } from '@/common/utils' import { ApiProperty, PartialType } from '@nestjs/swagger' -import { IsDateString, IsOptional, IsString } from 'class-validator' +import { IsDateString, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator' export class CreateBusinessActivitiesDto { @IsString() @@ -11,6 +12,7 @@ export class CreateBusinessActivitiesDto { economic_code: string @IsString() + @FiscalIdFieldValidator() @ApiProperty({ required: true }) fiscal_id: string @@ -22,6 +24,12 @@ export class CreateBusinessActivitiesDto { @ApiProperty({ required: true }) guild_id: string + @ApiProperty({ required: false, default: '1' }) + @IsNumber() + @Min(0) + @Max(1_000_000_000) + invoice_number_sequence: number + @IsDateString() @IsOptional() @ApiProperty({ required: false }) diff --git a/src/modules/partners/consumers/dto/create-consumers.dto.ts b/src/modules/partners/consumers/dto/create-consumers.dto.ts index 50d9e11..cf45443 100644 --- a/src/modules/partners/consumers/dto/create-consumers.dto.ts +++ b/src/modules/partners/consumers/dto/create-consumers.dto.ts @@ -1,3 +1,4 @@ +import { PasswordFieldValidator, UsernameFieldValidator } from '@/common/utils' import { ConsumerIndividual, ConsumerLegal } from '@/generated/prisma/client' import { ConsumerStatus, ConsumerType } from '@/generated/prisma/enums' import { ApiProperty, OmitType, PartialType } from '@nestjs/swagger' @@ -9,10 +10,12 @@ export class CreateConsumerDto { type: ConsumerType @IsString() + @UsernameFieldValidator() @ApiProperty({ required: true }) username: string @IsString() + @PasswordFieldValidator() @ApiProperty({ required: true }) password: string