diff --git a/apps/api/src/routines/routine-mapper.ts b/apps/api/src/routines/routine-mapper.ts index c3be77a..4b28edb 100644 --- a/apps/api/src/routines/routine-mapper.ts +++ b/apps/api/src/routines/routine-mapper.ts @@ -185,6 +185,7 @@ export function toRoutineDto( entityLink: normalizeEntityLink(row.entityLink), reminderMinutesBefore: row.reminderMinutesBefore, isActive: row.isActive, + createdById: row.createdById, recurrenceLabel: describeRecurrence(rule), executionRate: execution.total ? Math.round((execution.done / execution.total) * 100) : null, executionTotal: execution.total, diff --git a/apps/api/src/routines/routines.service.ts b/apps/api/src/routines/routines.service.ts index c8410b6..e12698b 100644 --- a/apps/api/src/routines/routines.service.ts +++ b/apps/api/src/routines/routines.service.ts @@ -200,8 +200,7 @@ export class RoutinesService { async create(actor: Actor, dto: SaveRoutineDto): Promise { const scope = await this.scope(actor); - assertManager(scope); - const data = await this.buildData(actor, scope, dto); + const data = await this.buildData(actor, scope, this.ownRoutineInput(actor, scope, dto)); const row = await this.prisma.routine.create({ data: { ...data, @@ -216,10 +215,9 @@ export class RoutinesService { async update(actor: Actor, id: string, dto: SaveRoutineDto): Promise { const scope = await this.scope(actor); - assertManager(scope); const existing = await this.findRoutine(actor.organizationId, id); - this.assertCanManageRoutine(scope, existing); - const data = await this.buildData(actor, scope, dto); + this.assertCanWriteRoutine(scope, existing); + const data = await this.buildData(actor, scope, this.ownRoutineInput(actor, scope, dto)); const row = await this.prisma.routine.update({ where: { id }, data: data as Prisma.RoutineUncheckedUpdateInput, @@ -232,9 +230,8 @@ export class RoutinesService { async toggle(actor: Actor, id: string, isActive: boolean): Promise { const scope = await this.scope(actor); - assertManager(scope); const existing = await this.findRoutine(actor.organizationId, id); - this.assertCanManageRoutine(scope, existing); + this.assertCanWriteRoutine(scope, existing); const row = await this.prisma.routine.update({ where: { id }, data: { isActive }, @@ -251,9 +248,8 @@ export class RoutinesService { dto: ChangeRoutineAssigneeDto, ): Promise { const scope = await this.scope(actor); - assertManager(scope); const existing = await this.findRoutine(actor.organizationId, id); - this.assertCanManageRoutine(scope, existing); + this.assertCanWriteRoutine(scope, existing); const target = await this.resolveTarget(actor, scope, dto.assigneeId, dto.teamId); await this.cancelTodayPending(id, 'تغییر مسئول روتین'); const row = await this.prisma.routine.update({ @@ -268,9 +264,8 @@ export class RoutinesService { async duplicate(actor: Actor, id: string): Promise { const scope = await this.scope(actor); - assertManager(scope); const src = await this.findRoutine(actor.organizationId, id); - this.assertCanManageRoutine(scope, src); + this.assertCanWriteRoutine(scope, src); const { id: _id, createdAt: _c, @@ -295,9 +290,8 @@ export class RoutinesService { async remove(actor: Actor, id: string) { const scope = await this.scope(actor); - assertManager(scope); const existing = await this.findRoutine(actor.organizationId, id); - this.assertCanManageRoutine(scope, existing); + this.assertCanWriteRoutine(scope, existing); await this.prisma.routine.update({ where: { id }, data: { deletedAt: new Date(), isActive: false }, @@ -345,6 +339,27 @@ export class RoutinesService { return row; } + /** عضو عادی فقط روتینی را می‌نویسد که خودش برای خودش ساخته است. */ + private ownRoutineInput(actor: Actor, scope: RoutineScope, dto: SaveRoutineDto): SaveRoutineDto { + if (scope.isManager) return dto; + if (dto.teamId || (dto.assigneeId && dto.assigneeId !== actor.userId)) { + throw new ForbiddenException('فقط می‌توانید برای خودتان روتین بسازید'); + } + return { ...dto, assigneeId: actor.userId, teamId: null }; + } + + private assertCanWriteRoutine( + scope: RoutineScope, + row: { assigneeId: string | null; teamId: string | null; createdById: string | null }, + ) { + if (scope.isManager) { + this.assertCanManageRoutine(scope, row); + return; + } + if (row.createdById === scope.userId && row.assigneeId === scope.userId && !row.teamId) return; + throw new ForbiddenException('فقط روتینی که برای خودتان ساخته‌اید را می‌توانید تغییر دهید'); + } + private assertCanManageRoutine( scope: RoutineScope, row: { assigneeId: string | null; teamId: string | null; createdById: string | null }, diff --git a/apps/web/src/views/routines/RoutineFormView.vue b/apps/web/src/views/routines/RoutineFormView.vue index 10955ba..deab2c7 100644 --- a/apps/web/src/views/routines/RoutineFormView.vue +++ b/apps/web/src/views/routines/RoutineFormView.vue @@ -1,5 +1,5 @@