feat(roles): let each section decide who may assign work to others

Section access now only creates a record for yourself. Assigning it to a colleague requires a separate permission on that section.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-03 13:15:21 +03:30
co-authored by Cursor
parent 79733e6c96
commit 534dae45b2
25 changed files with 355 additions and 84 deletions
-2
View File
@@ -302,8 +302,6 @@ export class LeadsController {
}
@Post('assign')
@UseGuards(RolesGuard)
@Roles('owner', 'admin')
assign(@CurrentUser() user: Actor, @Body() dto: AssignLeadsDto) {
return this.leads
.assign(user, dto.leadIds, dto.assigneeId)
+7 -6
View File
@@ -31,6 +31,7 @@ import {
elevated,
forScope,
} from '../common/record-scope';
import { actorCanAssignToOthers, assertAssigneeSet } from '../common/assign-access';
import {
asAddresses,
collectAddresses,
@@ -379,10 +380,11 @@ export class LeadsService {
const stageId = dto.stageId || stages[0]?.id;
if (!stageId) throw new BadRequestException('مرحله‌ای تعریف نشده');
const allowOthers = await actorCanAssignToOthers(this.prisma, actor, 'leads');
let assigneeId: string | null = null;
if (elevated(actor.role) && dto.assigneeId) {
if (allowOthers && dto.assigneeId) {
assigneeId = dto.assigneeId;
} else if (!elevated(actor.role)) {
} else if (!allowOthers) {
assigneeId = actor.userId;
}
@@ -570,8 +572,9 @@ export class LeadsService {
if (typeof dto.stageId === 'string' && dto.stageId !== existing.stageId) {
data.stage = { connect: { id: dto.stageId } };
}
if (elevated(actor.role) && dto.assigneeId !== undefined) {
if (dto.assigneeId !== undefined) {
const aid = dto.assigneeId ? String(dto.assigneeId) : null;
await assertAssigneeSet(this.prisma, actor, 'leads', [aid], [existing.assigneeId]);
data.assignee = aid
? { connect: { id: aid } }
: { disconnect: true };
@@ -641,9 +644,7 @@ export class LeadsService {
leadIds: string[],
assigneeId: string,
): Promise<number> {
if (!elevated(actor.role)) {
throw new BadRequestException('فقط مدیر می‌تواند تخصیص دهد');
}
await assertAssigneeSet(this.prisma, actor, 'leads', [assigneeId]);
const user = await this.prisma.user.findFirst({
where: {
id: assigneeId,