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:
@@ -15,12 +15,12 @@ import { Prisma } from '@prisma/client';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import {
|
||||
type Actor,
|
||||
canReassignOwner,
|
||||
dealAccessWhere,
|
||||
ownerWhere,
|
||||
projectCardAccessWhere,
|
||||
resolveOwnerId,
|
||||
} from '../common/record-scope';
|
||||
import { actorCanAssignToOthers, assertAssigneeSet } from '../common/assign-access';
|
||||
import { normalizeTags } from '../common/tags';
|
||||
|
||||
const DEFAULT_STAGES = ['صف', 'در حال انجام', 'بازبینی', 'انجامشده'] as const;
|
||||
@@ -206,6 +206,13 @@ export class ProjectBoardsService {
|
||||
});
|
||||
|
||||
const assigneeIds = [...new Set((dto.assigneeIds ?? []).map((id) => id.trim()).filter(Boolean))];
|
||||
const assignActor = { userId: actor.userId, role: actor.role, organizationId };
|
||||
const ownerId = resolveOwnerId(
|
||||
actor,
|
||||
dto.ownerId,
|
||||
await actorCanAssignToOthers(this.prisma, assignActor, 'projects'),
|
||||
);
|
||||
await assertAssigneeSet(this.prisma, assignActor, 'projects', [ownerId, ...assigneeIds]);
|
||||
const created = await this.prisma.projectCard.create({
|
||||
data: {
|
||||
organizationId,
|
||||
@@ -217,7 +224,7 @@ export class ProjectBoardsService {
|
||||
personId: emptyToNull(dto.personId),
|
||||
companyId: emptyToNull(dto.companyId),
|
||||
dealId: emptyToNull(dto.dealId),
|
||||
ownerId: resolveOwnerId(actor, dto.ownerId),
|
||||
ownerId,
|
||||
position: (maxPos._max.position ?? -1) + 1,
|
||||
...(assigneeIds.length
|
||||
? { assignees: { connect: assigneeIds.map((id) => ({ id })) } }
|
||||
@@ -257,24 +264,40 @@ export class ProjectBoardsService {
|
||||
if ('companyId' in dto) data.companyId = emptyToNull(dto.companyId ?? null);
|
||||
if ('dealId' in dto) data.dealId = emptyToNull(dto.dealId ?? null);
|
||||
if ('ownerId' in dto) {
|
||||
if (!canReassignOwner(actor.role) && dto.ownerId !== existing.ownerId) {
|
||||
throw new BadRequestException('تغییر مسئول مجاز نیست');
|
||||
}
|
||||
data.ownerId = emptyToNull(dto.ownerId ?? null);
|
||||
const nextOwner = emptyToNull(dto.ownerId ?? null);
|
||||
await assertAssigneeSet(
|
||||
this.prisma,
|
||||
{ userId: actor.userId, role: actor.role, organizationId },
|
||||
'projects',
|
||||
[nextOwner],
|
||||
[existing.ownerId],
|
||||
);
|
||||
data.ownerId = nextOwner;
|
||||
}
|
||||
|
||||
const nextAssigneeIds = dto.assigneeIds
|
||||
? [...new Set(dto.assigneeIds.map((x) => x.trim()).filter(Boolean))]
|
||||
: undefined;
|
||||
if (nextAssigneeIds) {
|
||||
const currentAssignees = await this.prisma.projectCard.findFirst({
|
||||
where: { id },
|
||||
select: { assignees: { select: { id: true } } },
|
||||
});
|
||||
await assertAssigneeSet(
|
||||
this.prisma,
|
||||
{ userId: actor.userId, role: actor.role, organizationId },
|
||||
'projects',
|
||||
nextAssigneeIds,
|
||||
currentAssignees?.assignees.map((item) => item.id) ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
const row = await this.prisma.projectCard.update({
|
||||
where: { id },
|
||||
data: {
|
||||
...data,
|
||||
...(dto.assigneeIds
|
||||
? {
|
||||
assignees: {
|
||||
set: [...new Set(dto.assigneeIds.map((x) => x.trim()).filter(Boolean))].map(
|
||||
(uid) => ({ id: uid }),
|
||||
),
|
||||
},
|
||||
}
|
||||
...(nextAssigneeIds
|
||||
? { assignees: { set: nextAssigneeIds.map((uid) => ({ id: uid })) } }
|
||||
: {}),
|
||||
},
|
||||
include: cardInclude,
|
||||
|
||||
Reference in New Issue
Block a user