From bd12337f6ec15d04e3efad40eb0695aca7d200aa Mon Sep 17 00:00:00 2001 From: adonis Date: Wed, 9 Sep 2026 20:19:01 +0330 Subject: [PATCH] fix pluggin bug 2 --- src/plugins/plugin-catalog.seed.ts | 14 +++++++++-- src/plugins/plugin-store.service.ts | 38 +++++++++++++++++++---------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/plugins/plugin-catalog.seed.ts b/src/plugins/plugin-catalog.seed.ts index e2cb14c..f28787b 100644 --- a/src/plugins/plugin-catalog.seed.ts +++ b/src/plugins/plugin-catalog.seed.ts @@ -1,4 +1,4 @@ -export const PLUGIN_STORE_DURATIONS = [30, 90, 180, 360] as const; +export const PLUGIN_STORE_DURATIONS = [30, 90, 180, 360, 0] as const; export type PluginStoreDuration = (typeof PLUGIN_STORE_DURATIONS)[number]; @@ -90,13 +90,23 @@ export const PLUGIN_CATALOG_SEED: Array<{ }, ]; -export function daysToPlanMonths(days: number): 1 | 3 | 6 | 12 { +export function daysToPlanMonths(days: number): 0 | 1 | 3 | 6 | 12 { + if (days === 0) return 0; if (days === 90) return 3; if (days === 180) return 6; if (days === 360) return 12; return 1; } +export function pluginStoreDurationLabel(days: number): string { + if (Number(days) === 0) return 'یکبار پرداخت'; + if (days === 30) return '۳۰ روزه'; + if (days === 90) return '۹۰ روزه'; + if (days === 180) return '۱۸۰ روزه'; + if (days === 360) return '۳۶۰ روزه'; + return `${days} روزه`; +} + export function inferSettingKeyFromName(name: string): string | null { const n = String(name || '').trim(); if (!n) return null; diff --git a/src/plugins/plugin-store.service.ts b/src/plugins/plugin-store.service.ts index b1849d1..d10c193 100644 --- a/src/plugins/plugin-store.service.ts +++ b/src/plugins/plugin-store.service.ts @@ -38,6 +38,7 @@ import { inferSettingKeyFromName, PLUGIN_CATALOG_SEED, PLUGIN_STORE_DURATIONS, + pluginStoreDurationLabel, } from './plugin-catalog.seed'; import { ApprovePurchaseDto, @@ -380,6 +381,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { } const months = daysToPlanMonths(req.durationDays); + const oneTime = req.durationDays === 0; const existing = await this.prisma.pluginSubscription.findFirst({ where: { tenantId: req.tenantId, @@ -393,10 +395,12 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { existing?.expiresAt && existing.expiresAt.getTime() > now.getTime() ? existing.expiresAt : now; - const expiresAt = - existing?.expiresAt && existing.expiresAt.getTime() > now.getTime() + const expiresAt = oneTime + ? null + : existing?.expiresAt && existing.expiresAt.getTime() > now.getTime() ? addIranMonths(from, months) : addIranPlanExpiry(now, months); + const chargeExpiresAt = expiresAt ?? new Date('9999-12-31T00:00:00.000Z'); const plugin = existing ? await this.prisma.pluginSubscription.update({ @@ -410,7 +414,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { chargedAt: now, expiresAt, isActive: true, - autoCutOnExpiry: dto.autoCutOnExpiry, + autoCutOnExpiry: oneTime ? false : dto.autoCutOnExpiry, lastAlertDate: null, alertMarks: Prisma.DbNull, }, @@ -426,7 +430,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { chargedAt: now, expiresAt, isActive: true, - autoCutOnExpiry: dto.autoCutOnExpiry, + autoCutOnExpiry: oneTime ? false : dto.autoCutOnExpiry, }, }); @@ -436,7 +440,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { chargedAt: now, planMonths: months, priceRials: req.finalPriceRials, - expiresAt, + expiresAt: chargeExpiresAt, note: 'خرید از فروشگاه', createdById: userId, }, @@ -460,10 +464,13 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { entityId: id, tenantId: req.tenantId, userId, - after: { settingKey: req.catalogItem.settingKey, expiresAt: expiresAt.toISOString() } as Prisma.InputJsonValue, + after: { + settingKey: req.catalogItem.settingKey, + expiresAt: expiresAt?.toISOString() ?? null, + } as Prisma.InputJsonValue, }); - return { ok: true, expiresAt: expiresAt.toISOString() }; + return { ok: true, expiresAt: expiresAt?.toISOString() ?? null }; } async rejectRequest(id: string, userId?: string) { @@ -544,12 +551,14 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { .sort((a, b) => (b.expiresAt?.getTime() || 0) - (a.expiresAt?.getTime() || 0))[0]; const daysLeft = sub?.expiresAt ? iranDaysUntil(sub.expiresAt) : null; const hasPending = pendingIds.has(item.id); + const lifetime = Boolean(sub?.isActive && !sub.expiresAt); const lockedByDays = - sub?.isActive && - sub.expiresAt && - sub.expiresAt.getTime() > now && - daysLeft != null && - daysLeft > 3; + lifetime || + (sub?.isActive && + sub.expiresAt && + sub.expiresAt.getTime() > now && + daysLeft != null && + daysLeft > 3); return { id: item.id, settingKey: item.settingKey, @@ -640,6 +649,9 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { where: { tenantId, settingKey: item.settingKey, isActive: true }, orderBy: { expiresAt: 'desc' }, }); + if (sub?.isActive && !sub.expiresAt) { + throw new ConflictException('این افزونه یکبار پرداخت شده و فعال است'); + } if (sub?.expiresAt && sub.expiresAt.getTime() > Date.now()) { const daysLeft = iranDaysUntil(sub.expiresAt); if (daysLeft > 3) { @@ -663,7 +675,7 @@ export class PluginStoreService implements OnModuleInit, OnModuleDestroy { }); this.notify.notifyAdmins( - `درخواست افزونه: «${created.catalogItem.titleFa}» ${dto.durationDays}روزه برای «${created.tenant.nameFa}» (${created.tenant.domain}) — ${price.finalPriceRials.toLocaleString('fa-IR')} ریال`, + `درخواست افزونه: «${created.catalogItem.titleFa}» ${pluginStoreDurationLabel(dto.durationDays)} برای «${created.tenant.nameFa}» (${created.tenant.domain}) — ${price.finalPriceRials.toLocaleString('fa-IR')} ریال`, ); return this.toRequestRow({