Files
CRM_Montzar/apps/api/src/common/record-scope.ts
T

113 lines
3.5 KiB
TypeScript

import type { DataVisibility, UserRole } from '@mento/shared';
import { canViewAllRecords } from '@mento/shared';
import type { Prisma } from '@prisma/client';
export type Actor = {
userId: string;
role: string;
organizationId: string;
/** own | team | org — از Membership */
dataVisibility?: DataVisibility;
/**
* شناسه کاربرانی که پرونده‌شان برای این Actor قابل‌مشاهده است.
* اگر undefined باشد فقط خود کاربر؛ اگر null یعنی همه (elevated/org).
*/
visibleUserIds?: string[] | null;
};
export function elevated(role: string | null | undefined): boolean {
return canViewAllRecords(role);
}
export function seesAllRecords(
actor: Pick<Actor, 'role' | 'dataVisibility' | 'visibleUserIds'>,
): boolean {
if (elevated(actor.role)) return true;
if (actor.dataVisibility === 'org') return true;
if (actor.visibleUserIds === null) return true;
return false;
}
function scopedOwnerIds(
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
): string[] | null {
if (seesAllRecords(actor)) return null;
const ids = actor.visibleUserIds?.length
? actor.visibleUserIds
: [actor.userId];
return [...new Set(ids.filter(Boolean))];
}
/** فیلتر مالکیت برای Deal / Quote / Invoice / Person / Company */
export function ownerWhere(
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
): { ownerId: string } | { ownerId: { in: string[] } } | Record<string, never> {
const ids = scopedOwnerIds(actor);
if (!ids) return {};
if (ids.length === 1) return { ownerId: ids[0]! };
return { ownerId: { in: ids } };
}
/** فعالیت‌ها بر اساس مسئول */
export function assigneeWhere(
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
):
| { assigneeId: string }
| { assigneeId: { in: string[] } }
| Record<string, never> {
const ids = scopedOwnerIds(actor);
if (!ids) return {};
if (ids.length === 1) return { assigneeId: ids[0]! };
return { assigneeId: { in: ids } };
}
/** یادداشت: نویسنده یا پرونده‌های تحت مالکیت در محدوده دید */
export function noteAccessWhere(
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
): Prisma.NoteWhereInput {
const ids = scopedOwnerIds(actor);
if (!ids) return {};
return {
OR: [
{ authorId: { in: ids } },
{ deal: { ownerId: { in: ids } } },
{ person: { ownerId: { in: ids } } },
{ company: { ownerId: { in: ids } } },
],
};
}
export function resolveOwnerId(
actor: Pick<Actor, 'userId' | 'role'>,
requested?: string | null,
): string {
if (elevated(actor.role) && requested?.trim()) {
return requested.trim();
}
return actor.userId;
}
/** تیکت: مسئول یا ثبت‌کننده در محدوده دید */
export function ticketAccessWhere(
actor: Pick<Actor, 'userId' | 'role' | 'dataVisibility' | 'visibleUserIds'>,
): Prisma.TicketWhereInput {
const ids = scopedOwnerIds(actor);
if (!ids) return {};
return {
OR: [{ assigneeId: { in: ids } }, { reporterId: { in: ids } }],
};
}
export function canReassignOwner(role: string | null | undefined): boolean {
return elevated(role);
}
export type OrgRole = UserRole;
export function normalizeDataVisibility(
value: string | null | undefined,
): DataVisibility {
if (value === 'team' || value === 'org' || value === 'own') return value;
return 'own';
}