import { QUERY_CONSTANTS } from '@/common/queryConstants' import mapConsumerWithLicenseUtil from '@/common/utils/mappers/consumer_mappers.util' import { PasswordUtil } from '@/common/utils/password.util' import { RedisKeyMaker } from '@/common/utils/redisKeyMaker' import { AccountStatus, AccountType, ConsumerRole, ConsumerStatus, ConsumerType, PartnerStatus, } from '@/generated/prisma/enums' import { ConsumerCreateInput, ConsumerSelect, ConsumerWhereInput, } from '@/generated/prisma/models' import { PrismaService } from '@/prisma/prisma.service' import { RedisService } from '@/redis/redis.service' import { BadRequestException, Injectable } from '@nestjs/common' import { ResponseMapper } from 'common/response/response-mapper' import { PartnersCacheInvalidationService } from '../cache/partners-cache-invalidation.service' import { CreateConsumerDto, UpdateConsumerDto } from './dto/create-consumers.dto' @Injectable() export class PartnerConsumersService { constructor( private readonly prisma: PrismaService, private readonly redisService: RedisService, private readonly partnersCacheInvalidationService: PartnersCacheInvalidationService, ) {} private readonly infoCacheTtlSeconds = 300 private readonly setExpireDate = (starts_at: Date) => { return new Date( new Date(starts_at).setFullYear(new Date(starts_at).getFullYear() + 1), ).toISOString() } defaultSelect: ConsumerSelect = { id: true, status: true, created_at: true, type: true, ...QUERY_CONSTANTS.CONSUMER.infoSelect, ...QUERY_CONSTANTS.CONSUMER.activeBusinessCount, } private defaultWhere = (partner_id: string): ConsumerWhereInput => ({ OR: [ { legal: { partner_id, }, }, { individual: { partner_id, }, }, ], }) async findAll(partner_id: string, page = 1, perPage = 10) { const cacheKey = RedisKeyMaker.partnerConsumersList(partner_id, page, perPage) return await this.redisService.getAndSet(cacheKey, 'paginate', async () => { const [consumers, total] = await this.prisma.$transaction(async tx => [ await tx.consumer.findMany({ where: this.defaultWhere(partner_id), select: this.defaultSelect, skip: (page - 1) * perPage, take: 10, }), await tx.consumer.count({ where: this.defaultWhere(partner_id), }), ]) const mappedConsumers = consumers.map(mapConsumerWithLicenseUtil) return { items: mappedConsumers, total, page, perPage, } }) } async findOne(partner_id: string, consumer_id: string) { const cacheKey = RedisKeyMaker.partnerConsumerInfo(partner_id, consumer_id) return await this.redisService.getAndSet(cacheKey, 'single', async () => { const consumer = await this.prisma.consumer.findUniqueOrThrow({ where: { ...(this.defaultWhere(partner_id) as any), id: consumer_id, }, select: this.defaultSelect, }) return mapConsumerWithLicenseUtil(consumer) }) } async create(partner_id: string, data: CreateConsumerDto) { const consumer = await this.prisma.$transaction(async tx => { const partnerLicense = await tx.license.findFirst({ where: { charge_transaction: { partner: { id: partner_id, status: PartnerStatus.ACTIVE, }, }, activation: null, }, orderBy: { charge_transaction: { activation_expires_at: 'asc', }, }, select: { id: true, charge_transaction: { select: { partner: { select: { id: true, name: true, status: true, }, }, }, }, }, }) if (!partnerLicense) { throw new BadRequestException(`تعداد لایسنس‌های فعلی شما به پایان رسیده است.`) } const { username, password, individual, legal, ...rest } = data const createConsumerData: ConsumerCreateInput = { ...rest, accounts: { create: { role: ConsumerRole.OWNER, account: { create: { username, password: await PasswordUtil.hash(password), type: AccountType.CONSUMER, status: AccountStatus.ACTIVE, }, }, }, }, status: ConsumerStatus.ACTIVE, } const partner = { partner: { connect: { id: partner_id } }, } if (!legal && !individual) { throw new BadRequestException( `برای ایجاد مصرف کننده باید یکی از اطلاعات فردی یا حقوقی وارد شود.`, ) } if (data.type === ConsumerType.INDIVIDUAL && !individual) { throw new BadRequestException( `برای ایجاد مصرف کننده با نوع حقیقی باید اطلاعات فردی وارد شود.`, ) } if (data.type === ConsumerType.LEGAL && !legal) { throw new BadRequestException( `برای ایجاد مصرف کننده با نوع حقوقی باید اطلاعات حقوقی وارد شود.`, ) } let consumerExistError = '' if (legal) { const existingConsumer = await tx.consumer.findFirst({ where: { legal: { registration_code: legal.registration_code, partner_id }, }, select: { id: true, legal: { select: { name: true, }, }, }, }) createConsumerData.legal = { create: { ...legal, ...partner, }, } if (existingConsumer) { consumerExistError = `مشتری با نام ${existingConsumer.legal!.name} با این شماره ثبت، از قبل توسط شما ایجاد شده است.` } } else if (individual) { const existingConsumer = await tx.consumer.findFirst({ where: { individual: { national_code: individual.national_code, partner_id }, }, select: { id: true, individual: { select: { first_name: true, last_name: true, }, }, }, }) createConsumerData.individual = { create: { ...individual, ...partner, }, } if (existingConsumer) { consumerExistError = `مشتری با نام ${existingConsumer.individual!.first_name} ${existingConsumer.individual!.last_name} یا این کد ملی، از قبل توسط شما ایجاد شده است.` } } if (consumerExistError) { throw new BadRequestException(consumerExistError) } return await tx.consumer.create({ data: createConsumerData, select: this.defaultSelect, }) }) await this.partnersCacheInvalidationService.invalidatePartnerConsumerReadModels( partner_id, consumer.id, ) return ResponseMapper.single(mapConsumerWithLicenseUtil(consumer)) } async update(id: string, partner_id: string, data: UpdateConsumerDto) { const updatedConsumer = await this.prisma.consumer.update({ where: { ...(this.defaultWhere(partner_id) as any), id, }, data: { status: data.status, legal: { update: data.legal, }, individual: { update: data.individual, }, }, }) await this.partnersCacheInvalidationService.invalidatePartnerConsumerReadModels( partner_id, id, ) await this.redisService.delete(RedisKeyMaker.consumerInfo(id)) return updatedConsumer } }