import { and, eq, isNull } from 'drizzle-orm'; import { crmRoleProfiles, memberships } from '@/db/schema'; import { db } from '@/lib/db'; import { ALL_PERMISSIONS, type CrmApprovalAuthority, type CrmOwnershipScope, type CrmScopeMode, getDefaultBusinessRole, getRoleProfileDefinition, isApprovalAuthority, isMembershipRole, isOwnershipScope, isScopeMode, resolveEffectivePermissions, type MembershipRole, type Permission, type SystemRole } from './rbac'; export interface ResolvedCrmAccess { membershipRole: MembershipRole; businessRole: string; permissions: Permission[]; branchScopeIds: string[]; productTypeScopeIds: string[]; ownershipScope: CrmOwnershipScope; branchScopeMode: CrmScopeMode; productScopeMode: CrmScopeMode; approvalAuthority: CrmApprovalAuthority; roleProfileName: string | null; } type BranchScopedAccess = Pick< ResolvedCrmAccess, 'branchScopeMode' | 'branchScopeIds' > | { branchScopeMode: string; branchScopeIds: string[]; }; type ProductScopedAccess = Pick< ResolvedCrmAccess, 'productScopeMode' | 'productTypeScopeIds' > | { productScopeMode: string; productTypeScopeIds: string[]; }; function normalizeStringArray(value: unknown) { return Array.isArray(value) ? [...new Set(value.filter((item): item is string => typeof item === 'string' && item.trim().length > 0))] : []; } export async function resolveCrmMembershipAccess(input: { systemRole: SystemRole; organizationId: string; membership: typeof memberships.$inferSelect; }): Promise { if (input.systemRole === 'super_admin') { return { membershipRole: 'admin', businessRole: input.membership.businessRole, permissions: ALL_PERMISSIONS, branchScopeIds: [], productTypeScopeIds: [], ownershipScope: 'organization', branchScopeMode: 'all', productScopeMode: 'all', approvalAuthority: 'final', roleProfileName: 'System Admin' }; } const membershipRole = isMembershipRole(input.membership.role) ? input.membership.role : 'user'; const businessRole = input.membership.businessRole || getDefaultBusinessRole(membershipRole); const [roleProfile] = await db .select() .from(crmRoleProfiles) .where( and( eq(crmRoleProfiles.organizationId, input.organizationId), eq(crmRoleProfiles.code, businessRole), eq(crmRoleProfiles.isActive, true), isNull(crmRoleProfiles.deletedAt) ) ) .limit(1); const defaultDefinition = getRoleProfileDefinition(businessRole); const ownershipScope: CrmOwnershipScope = isOwnershipScope(roleProfile?.ownershipScope ?? '') ? (roleProfile.ownershipScope as CrmOwnershipScope) : (defaultDefinition?.ownershipScope ?? 'own'); const branchScopeMode: CrmScopeMode = isScopeMode(roleProfile?.branchScopeMode ?? '') ? (roleProfile.branchScopeMode as CrmScopeMode) : (defaultDefinition?.branchScopeMode ?? 'assigned'); const productScopeMode: CrmScopeMode = isScopeMode(roleProfile?.productScopeMode ?? '') ? (roleProfile.productScopeMode as CrmScopeMode) : (defaultDefinition?.productScopeMode ?? 'assigned'); const approvalAuthority: CrmApprovalAuthority = isApprovalAuthority( roleProfile?.approvalAuthority ?? '' ) ? (roleProfile.approvalAuthority as CrmApprovalAuthority) : (defaultDefinition?.approvalAuthority ?? 'none'); return { membershipRole, businessRole, permissions: resolveEffectivePermissions({ systemRole: input.systemRole, membershipRole, businessRole, rolePermissions: roleProfile?.permissions ?? defaultDefinition?.permissions ?? [], directPermissions: input.membership.permissions }), branchScopeIds: normalizeStringArray(input.membership.branchScopeIds), productTypeScopeIds: normalizeStringArray(input.membership.productTypeScopeIds), ownershipScope, branchScopeMode, productScopeMode, approvalAuthority, roleProfileName: roleProfile?.name ?? defaultDefinition?.name ?? null }; } export function hasBranchScopeAccess(access: BranchScopedAccess, branchId?: string | null) { if (!branchId) { return true; } if (access.branchScopeMode === 'all' || access.branchScopeIds.length === 0) { return true; } return access.branchScopeIds.includes(branchId); } export function hasProductScopeAccess(access: ProductScopedAccess, productTypeId?: string | null) { if (!productTypeId) { return true; } if (access.productScopeMode === 'all' || access.productTypeScopeIds.length === 0) { return true; } return access.productTypeScopeIds.includes(productTypeId); }