import { ForbiddenException, Injectable, NotFoundException, UnauthorizedException, } from '@nestjs/common'; import type { OrganizationMembershipDto, UserRole } from '@mento/shared'; import { Prisma } from '@prisma/client'; import { PrismaService } from '../prisma/prisma.service'; export type MembershipActor = { userId: string; organizationId: string; role: UserRole; }; @Injectable() export class MembershipsService { constructor(private readonly prisma: PrismaService) {} async listActive(userId: string) { return this.prisma.membership.findMany({ where: { userId, isActive: true, organization: { isActive: true } }, include: { organization: { select: { id: true, name: true } }, orgRole: { select: { name: true } }, }, orderBy: { createdAt: 'asc' }, }); } async requireActive( userId: string, organizationId: string, ): Promise { const row = await this.prisma.membership.findFirst({ where: { userId, organizationId, isActive: true }, select: { userId: true, organizationId: true, role: true, organization: { select: { isActive: true } }, }, }); if (!row) { throw new ForbiddenException('به این محیط دسترسی ندارید'); } if (row.organization.isActive === false) { throw new UnauthorizedException('این محیط غیرفعال است'); } return { userId: row.userId, organizationId: row.organizationId, role: row.role, }; } toChoices( rows: Array<{ organization: { id: string; name: string }; role: UserRole; orgRole: { name: string } | null; }>, ): OrganizationMembershipDto[] { return rows.map((row) => ({ id: row.organization.id, name: row.organization.name, role: row.role, orgRoleName: row.orgRole?.name, })); } async createMembership(data: { userId: string; organizationId: string; role: UserRole; orgRoleId?: string | null; commissionPercent?: number; isActive?: boolean; }) { return this.prisma.membership.create({ data: { userId: data.userId, organizationId: data.organizationId, role: data.role, orgRoleId: data.orgRoleId ?? null, isActive: data.isActive ?? true, commissionPercent: data.commissionPercent == null ? undefined : new Prisma.Decimal(data.commissionPercent), }, }); } async findInOrg(organizationId: string, userId: string) { return this.prisma.membership.findFirst({ where: { organizationId, userId }, include: { user: true, orgRole: { select: { id: true, name: true, color: true, dataAccessPolicy: true } }, }, }); } async listInOrg(organizationId: string) { return this.prisma.membership.findMany({ where: { organizationId }, include: { user: true, orgRole: { select: { id: true, name: true, color: true, dataAccessPolicy: true } }, }, orderBy: [{ user: { lastLoginAt: 'desc' } }, { user: { fullName: 'asc' } }], }); } async countActiveOwners(organizationId: string, exceptUserId?: string) { return this.prisma.membership.count({ where: { organizationId, role: 'owner', isActive: true, ...(exceptUserId ? { userId: { not: exceptUserId } } : {}), }, }); } async syncUserLastOrg( userId: string, organizationId: string, role: UserRole, orgRoleId?: string | null, ) { await this.prisma.user.update({ where: { id: userId }, data: { organizationId, role, ...(orgRoleId !== undefined ? { orgRoleId } : {}), }, }); } async findUserInOrgById(organizationId: string, userId: string) { const row = await this.findInOrg(organizationId, userId); if (!row) throw new NotFoundException('کاربر پیدا نشد'); return row; } async findUserInOrgByIdentity( organizationId: string, where: { id?: string; email?: string; mobile?: string }, ) { const user = await this.prisma.user.findFirst({ where: { ...(where.id ? { id: where.id } : {}), ...(where.email ? { email: where.email } : {}), ...(where.mobile ? { mobile: where.mobile } : {}), }, select: { id: true }, }); if (!user) return null; return this.prisma.membership.findFirst({ where: { organizationId, userId: user.id }, include: { user: true }, }); } } export function unauthorizedIfMissing(value: T | null | undefined): T { if (!value) throw new UnauthorizedException(); return value; }