import { UploadedFileTypes } from '@/common/enums/enums' import { ResponseMapper } from '@/common/response/response-mapper' import { PasswordUtil } from '@/common/utils' import { PartnersCacheInvalidationService } from '@/modules/partners/cache/partners-cache-invalidation.service' import { UploaderService } from '@/modules/uploader/uploader.service' import { PrismaService } from '@/prisma/prisma.service' import { Injectable } from '@nestjs/common' import { UpdatePartnerProfileDto } from './dto/partner.dto' import { UpdatePartnerPasswordDto } from './dto/update-password-request.dto' @Injectable() export class PartnerService { constructor( private readonly prisma: PrismaService, private readonly uploaderService: UploaderService, private readonly cacheInvalidationService: PartnersCacheInvalidationService, ) {} async me(partner_id: string, account_id: string) { const partnerAccount = await this.prisma.partnerAccount.findUniqueOrThrow({ where: { id: account_id, partner_id, }, select: { id: true, role: true, account: { select: { username: true, }, }, partner: { select: { id: true, name: true, code: true, logo_url: true, }, }, }, }) return ResponseMapper.single({ ...partnerAccount.partner, account: { role: partnerAccount.role, id: partnerAccount.id, username: partnerAccount.account.username, }, }) } async getInfo(partner_id: string) { const partner = await this.prisma.partner.findUniqueOrThrow({ where: { id: partner_id, }, select: { id: true, name: true, code: true, logo_url: true, created_at: true, license_charge_transactions: { select: { purchased_count: true, activation_expires_at: true, _count: { select: { licenses: { where: { activation: { isNot: null, }, }, }, }, }, }, }, account_quota_charge_transactions: { select: { purchased_count: true, activation_expires_at: true, _count: { select: { credits: { where: { charge_transaction: { isNot: null, }, allocation: { isNot: null, }, }, }, }, }, }, }, license_renew_charge_transactions: { select: { purchased_count: true, activation_expires_at: true, _count: { select: { license_renews: { where: { activation: { isNot: undefined, }, }, }, }, }, }, }, }, }) const { license_charge_transactions, account_quota_charge_transactions, license_renew_charge_transactions, ...partnerInfo } = partner const licenses_status = { total_purchased: 0, total_activated: 0, total_expired: 0, } const account_quotas_status = { total_purchased: 0, total_activated: 0, total_expired: 0, } const license_renews_status = { total_purchased: 0, total_activated: 0, total_expired: 0, } const now = new Date() const checkExpired = (expires_at: Date) => expires_at < now const prepareData = ( items_count: number, activation_expires_at: Date, purchased_count: number, ) => { let totalActivated = 0 let totalExpired = 0 if (!checkExpired(activation_expires_at)) { totalActivated = items_count } else { totalExpired = items_count } return { total_purchased: purchased_count, total_activated: totalActivated, total_expired: totalExpired, } } license_charge_transactions.forEach( ({ _count, activation_expires_at, purchased_count }) => { const status = prepareData( _count.licenses, activation_expires_at, purchased_count, ) licenses_status.total_purchased += status.total_purchased licenses_status.total_activated += status.total_activated licenses_status.total_expired += status.total_expired }, ) account_quota_charge_transactions.forEach( ({ _count, activation_expires_at, purchased_count }) => { const status = prepareData(_count.credits, activation_expires_at, purchased_count) account_quotas_status.total_purchased += status.total_purchased account_quotas_status.total_activated += status.total_activated account_quotas_status.total_expired += status.total_expired }, ) license_renew_charge_transactions.forEach( ({ _count, activation_expires_at, purchased_count }) => { const status = prepareData( _count.license_renews, activation_expires_at, purchased_count, ) license_renews_status.total_purchased += status.total_purchased license_renews_status.total_activated += status.total_activated license_renews_status.total_expired += status.total_expired }, ) return ResponseMapper.single({ licenses_status, license_renews_status, account_quotas_status, ...partnerInfo, }) } async updateInfo( partner_id: string, data: UpdatePartnerProfileDto, logo?: Express.Multer.File, ) { const rest = { ...(data as any) } delete rest.logo const updatedPartner = await this.prisma.$transaction(async tx => { let logo_url = '' if (logo) { const uploadedUrl = await this.uploaderService.uploadFile( logo, UploadedFileTypes.PARTNER_LOGO, ) logo_url = uploadedUrl || '' } const prevLogo = await tx.partner.findUnique({ where: { id: partner_id }, select: { logo_url: true }, }) if (logo_url && prevLogo?.logo_url) { this.uploaderService.deleteFile(prevLogo.logo_url, UploadedFileTypes.PARTNER_LOGO) } return tx.partner.update({ where: { id: partner_id, }, data: { ...rest, ...(logo_url ? { logo_url } : {}), }, }) }) await this.cacheInvalidationService.invalidatePartnersList() await this.cacheInvalidationService.invalidatePartnerDetail(partner_id) return ResponseMapper.single(updatedPartner) } async updatePassword( partner_id: string, accountId: string, data: UpdatePartnerPasswordDto, ) { const partnerAccount = await this.prisma.partnerAccount.update({ where: { id: accountId, partner_id, }, data: { account: { update: { password: await PasswordUtil.hash(data.password), }, }, }, }) return ResponseMapper.update(partnerAccount) } }