feat(partners): add utility functions for partner business activity allocation limits and remaining licenses

- Implemented `getPartnerBusinessActivityAllocationLimits` to retrieve allocation limits for a partner's business activity.
- Added `ensurePartnerBusinessActivityHasRemainingAllocation` to validate remaining allocation credits.
- Created `getPartnerRemainingLicenses` to count remaining licenses for a partner.
- Developed `getPartnerFirstRemainingLicense` to fetch the first unused license for a partner.
- Introduced `ensurePartnerHasRemainingLicense` to ensure a partner has at least one unused license.
This commit is contained in:
2026-04-24 23:02:05 +03:30
parent 9b652a3603
commit 12506de863
43 changed files with 4645 additions and 2196 deletions
@@ -0,0 +1,97 @@
import { BadRequestException } from '@nestjs/common'
type PartnerBusinessActivityAllocationClient = {
licenseActivation: {
findFirst: (args: any) => Promise<any>
}
}
type GetPartnerBusinessActivityAllocationLimitsParams = {
partner_id: string
business_activity_id: string
referenceDate?: Date
}
export type PartnerBusinessActivityAllocationLimits = {
activation_id: string
total_credits: number
allocated_credits: number
remaining_credits: number
}
export const getPartnerBusinessActivityAllocationLimits = async (
prisma: PartnerBusinessActivityAllocationClient,
params: GetPartnerBusinessActivityAllocationLimitsParams,
): Promise<PartnerBusinessActivityAllocationLimits> => {
const { partner_id, business_activity_id, referenceDate = new Date() } = params
const startOfDay = new Date(referenceDate)
startOfDay.setHours(0, 0, 0, 0)
const activation = await prisma.licenseActivation.findFirst({
where: {
business_activity_id,
business_activity: {
consumer: {
partner_id,
},
},
OR: [
{
expires_at: {
gte: startOfDay,
},
},
{
license_renews: {
some: {
expires_at: {
gte: startOfDay,
},
},
},
},
],
},
select: {
id: true,
account_allocations: {
select: {
id: true,
account_id: true,
},
},
},
})
if (!activation) {
throw new BadRequestException('لایسنس فعال برای این فعالیت تجاری یافت نشد.')
}
const totalCredits = activation.account_allocations.length
const allocatedCredits = activation.account_allocations.filter(
account_allocation => account_allocation.account_id,
).length
return {
activation_id: activation.id,
total_credits: totalCredits,
allocated_credits: allocatedCredits,
remaining_credits: totalCredits - allocatedCredits,
}
}
export const ensurePartnerBusinessActivityHasRemainingAllocation = async (
prisma: PartnerBusinessActivityAllocationClient,
params: GetPartnerBusinessActivityAllocationLimitsParams,
) => {
const quota = await getPartnerBusinessActivityAllocationLimits(prisma, params)
if (quota.remaining_credits <= 0) {
throw new BadRequestException(
'محدودیت تخصیص حساب برای این فعالیت تجاری به پایان رسیده است.',
)
}
return quota
}
@@ -0,0 +1,94 @@
import { BadRequestException } from '@nestjs/common'
type PartnerRemainingLicensesClient = {
license: {
count: (args: any) => Promise<number>
findFirst: (args: any) => Promise<any>
}
}
type GetPartnerRemainingLicensesParams = {
partner_id: string
referenceDate?: Date
}
export const getPartnerRemainingLicenses = async (
prisma: PartnerRemainingLicensesClient,
params: GetPartnerRemainingLicensesParams,
) => {
const { partner_id, referenceDate = new Date() } = params
const startOfDay = new Date(referenceDate)
startOfDay.setHours(0, 0, 0, 0)
const remaining_count = await prisma.license.count({
where: {
activation: null,
charge_transaction: {
partner_id,
activation_expires_at: {
gte: startOfDay,
},
},
},
})
return {
remaining_count,
}
}
export const getPartnerFirstRemainingLicense = async (
prisma: PartnerRemainingLicensesClient,
params: GetPartnerRemainingLicensesParams,
) => {
const { partner_id, referenceDate = new Date() } = params
const startOfDay = new Date(referenceDate)
startOfDay.setHours(0, 0, 0, 0)
const license = await prisma.license.findFirst({
where: {
activation: null,
charge_transaction: {
partner_id,
activation_expires_at: {
gte: startOfDay,
},
},
},
orderBy: {
charge_transaction: {
activation_expires_at: 'asc',
},
},
select: {
id: true,
charge_transaction_id: true,
accounts_limit: true,
},
})
if (!license) {
throw new BadRequestException(
'لایسنس فعال و استفاده نشده برای این شریک تجاری وجود ندارد.',
)
}
return license
}
export const ensurePartnerHasRemainingLicense = async (
prisma: PartnerRemainingLicensesClient,
params: GetPartnerRemainingLicensesParams,
) => {
const quota = await getPartnerRemainingLicenses(prisma, params)
if (quota.remaining_count <= 0) {
throw new BadRequestException(
'لایسنس فعال و استفاده نشده برای این شریک تجاری وجود ندارد.',
)
}
return quota
}