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:
@@ -22,11 +22,11 @@ import { WebhooksService } from '../public-api/webhooks.service';
|
||||
import { GamificationService } from '../gamification/gamification.service';
|
||||
import {
|
||||
type Actor,
|
||||
canReassignOwner,
|
||||
dealAccessWhere,
|
||||
ownerWhere,
|
||||
resolveOwnerId,
|
||||
} from '../common/record-scope';
|
||||
import { actorCanAssignToOthers, assertAssigneeSet } from '../common/assign-access';
|
||||
|
||||
const dealInclude = {
|
||||
person: { select: { id: true, firstName: true, lastName: true } },
|
||||
@@ -106,6 +106,10 @@ export class DealsService {
|
||||
organizationId,
|
||||
dto.assigneeIds,
|
||||
);
|
||||
const assignActor = { userId: actor.userId, role: actor.role, organizationId };
|
||||
const allowOthers = await actorCanAssignToOthers(this.prisma, assignActor, 'sales');
|
||||
const ownerId = resolveOwnerId(actor, dto.ownerId, allowOthers);
|
||||
await assertAssigneeSet(this.prisma, assignActor, 'sales', [ownerId, ...assigneeIds]);
|
||||
|
||||
const maxPos = await this.prisma.deal.aggregate({
|
||||
where: { organizationId, stageId: stage.id },
|
||||
@@ -156,7 +160,7 @@ export class DealsService {
|
||||
createdById: actor.userId,
|
||||
personId: emptyToNull(dto.personId ?? null),
|
||||
companyId: emptyToNull(dto.companyId ?? null),
|
||||
ownerId: resolveOwnerId(actor, dto.ownerId),
|
||||
ownerId,
|
||||
position: (maxPos._max.position ?? -1) + 1,
|
||||
...(assigneeIds.length
|
||||
? { assignees: { connect: assigneeIds.map((id) => ({ id })) } }
|
||||
@@ -259,17 +263,38 @@ export class DealsService {
|
||||
}
|
||||
if ('personId' in dto) data.personId = emptyToNull(dto.personId ?? null);
|
||||
if ('companyId' in dto) data.companyId = emptyToNull(dto.companyId ?? null);
|
||||
if (dto.ownerId !== undefined && canReassignOwner(actor.role)) {
|
||||
data.ownerId = resolveOwnerId(actor, dto.ownerId);
|
||||
}
|
||||
|
||||
let assigneeConnect: { id: string }[] | undefined;
|
||||
if (dto.assigneeIds !== undefined) {
|
||||
const assigneeIds = await this.normalizeAssigneeIds(
|
||||
organizationId,
|
||||
dto.assigneeIds,
|
||||
);
|
||||
assigneeConnect = assigneeIds.map((uid) => ({ id: uid }));
|
||||
if (dto.ownerId !== undefined || dto.assigneeIds !== undefined) {
|
||||
const current = await this.prisma.deal.findFirst({
|
||||
where: { id, organizationId },
|
||||
select: { ownerId: true, assignees: { select: { id: true } } },
|
||||
});
|
||||
const assignActor = { userId: actor.userId, role: actor.role, organizationId };
|
||||
if (dto.ownerId !== undefined) {
|
||||
const nextOwner = emptyToNull(dto.ownerId ?? null);
|
||||
await assertAssigneeSet(
|
||||
this.prisma,
|
||||
assignActor,
|
||||
'sales',
|
||||
[nextOwner],
|
||||
[current?.ownerId],
|
||||
);
|
||||
data.ownerId = nextOwner ?? actor.userId;
|
||||
}
|
||||
if (dto.assigneeIds !== undefined) {
|
||||
const assigneeIds = await this.normalizeAssigneeIds(
|
||||
organizationId,
|
||||
dto.assigneeIds,
|
||||
);
|
||||
await assertAssigneeSet(
|
||||
this.prisma,
|
||||
assignActor,
|
||||
'sales',
|
||||
assigneeIds,
|
||||
current?.assignees.map((item) => item.id) ?? [],
|
||||
);
|
||||
assigneeConnect = assigneeIds.map((uid) => ({ id: uid }));
|
||||
}
|
||||
}
|
||||
|
||||
if (pricedFromLines && dto.lineItems) {
|
||||
|
||||
Reference in New Issue
Block a user