Configuring access rights in the CRM Super Admin

This commit is contained in:
2026-09-20 11:24:14 +03:30
parent 04d37a9d12
commit 4a9fe0b07c
29 changed files with 1235 additions and 35 deletions
+52
View File
@@ -18,12 +18,15 @@ import { normalizeMobile } from '../auth/auth.service';
import { MembershipsService } from '../memberships/memberships.service';
import { OrgRolesService } from '../org-roles/org-roles.service';
import { PipelinesService } from '../pipelines/pipelines.service';
import { BillingService } from '../billing/billing.service';
import { PrismaService } from '../prisma/prisma.service';
import type {
ApplyBillingPlanDto,
CreatePlatformAdminDto,
CreatePlatformOrganizationDto,
ResetPlatformAdminPasswordDto,
ResetPlatformUserPasswordDto,
UpdateOrgModulesDto,
UpdatePlatformOrganizationDto,
} from './dto';
@@ -45,6 +48,7 @@ export class PlatformService {
private readonly memberships: MembershipsService,
private readonly orgRoles: OrgRolesService,
private readonly pipelines: PipelinesService,
private readonly billing: BillingService,
) {}
async stats(): Promise<PlatformStatsDto> {
@@ -61,12 +65,18 @@ export class PlatformService {
this.prisma.user.count({ where: { isActive: true } }),
this.prisma.membership.count(),
]);
const [expiringSoonCount, expiredBillingCount] = await Promise.all([
this.billing.countExpiringSoon(),
this.billing.countExpired(),
]);
return {
organizationCount,
activeOrganizationCount,
userCount,
activeUserCount,
membershipCount,
expiringSoonCount,
expiredBillingCount,
};
}
@@ -89,6 +99,7 @@ export class PlatformService {
businessField: true,
isActive: true,
createdAt: true,
enabledModules: true,
_count: {
select: {
memberships: true,
@@ -111,6 +122,7 @@ export class PlatformService {
businessField: true,
isActive: true,
createdAt: true,
enabledModules: true,
_count: {
select: {
memberships: true,
@@ -213,6 +225,7 @@ export class PlatformService {
name,
slug,
businessField,
enabledModules: this.billing.defaultModulesForNewOrg(),
users: {
create: {
email: ownerEmail,
@@ -238,6 +251,7 @@ export class PlatformService {
organizationId: organization.id,
role: 'owner',
});
await this.billing.ensureTrialForOwner(owner.id, { maxOrganizations: 1 });
await this.orgRoles.ensureDefaultRoles(organization.id);
await this.pipelines.ensureDefaultPipeline(organization.id);
@@ -331,6 +345,7 @@ export class PlatformService {
orderBy: { createdAt: 'desc' },
take: 300,
include: {
billingAccount: true,
memberships: {
include: {
organization: {
@@ -356,12 +371,33 @@ export class PlatformService {
},
orderBy: { createdAt: 'asc' },
},
billingAccount: true,
},
});
if (!row) throw new NotFoundException('کاربر پیدا نشد');
return this.userToDto(row);
}
async applyBillingPlan(userId: string, dto: ApplyBillingPlanDto) {
const existing = await this.prisma.user.findUnique({
where: { id: userId },
select: { id: true },
});
if (!existing) throw new NotFoundException('کاربر پیدا نشد');
await this.billing.applyPlan(userId, dto);
return this.getUser(userId);
}
async updateOrganizationModules(id: string, dto: UpdateOrgModulesDto) {
const existing = await this.prisma.organization.findUnique({
where: { id },
select: { id: true },
});
if (!existing) throw new NotFoundException('محیط پیدا نشد');
await this.billing.setOrgModules(id, dto.enabledModules ?? []);
return this.getOrganization(id);
}
async setUserActive(id: string, isActive: boolean): Promise<PlatformUserDto> {
const existing = await this.prisma.user.findUnique({
where: { id },
@@ -609,6 +645,7 @@ export class PlatformService {
businessField: string | null;
isActive: boolean;
createdAt: Date;
enabledModules?: string[];
_count: { memberships: number; people: number; deals: number };
}): PlatformOrganizationDto {
return {
@@ -621,6 +658,7 @@ export class PlatformService {
memberCount: row._count.memberships,
personCount: row._count.people,
dealCount: row._count.deals,
enabledModules: this.billing.normalizeModules(row.enabledModules),
};
}
@@ -632,6 +670,17 @@ export class PlatformService {
isActive: boolean;
lastLoginAt: Date | null;
createdAt: Date;
billingAccount?: {
id: string;
ownerUserId: string;
planCode: string;
status: string;
startsAt: Date;
expiresAt: Date;
maxOrganizations: number;
externalPaymentRef: string | null;
notes: string | null;
} | null;
memberships: Array<{
id: string;
role: PlatformUserDto['memberships'][number]['role'];
@@ -647,6 +696,9 @@ export class PlatformService {
isActive: row.isActive,
lastLoginAt: row.lastLoginAt?.toISOString() ?? null,
createdAt: row.createdAt.toISOString(),
billing: row.billingAccount
? this.billing.toDto(row.billingAccount)
: null,
memberships: row.memberships.map((m) => ({
membershipId: m.id,
organizationId: m.organization.id,