fix(routines): keep the daily desk for internal team work

Customer-linked tasks stay on their record and no longer appear in today, calendar, score, or reminders.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-29 20:53:26 +03:30
co-authored by Cursor
parent 99d686fefb
commit a17c7dbabc
11 changed files with 115 additions and 90 deletions
+16 -42
View File
@@ -47,6 +47,7 @@ import {
tehranDateTime,
tehranDayKey,
} from './routine-dates';
import { deskRoutineTaskWhere, internalRoutineTaskWhere } from './internal-routine';
const OPEN_STATUSES: RoutineTaskStatus[] = ['todo', 'in_progress', 'overdue'];
@@ -84,6 +85,7 @@ export class RoutineTasksService {
assigneeId: target,
scheduledFor: dayDate,
status: { not: 'cancelled' },
...internalRoutineTaskWhere,
...deep,
},
include: routineTaskInclude,
@@ -95,6 +97,7 @@ export class RoutineTasksService {
assigneeId: target,
status: 'overdue',
scheduledFor: { lt: dayDate },
...internalRoutineTaskWhere,
...deep,
},
include: { ...routineTaskInclude, routine: { select: { ...routineTaskInclude.routine.select, carryOverOverdue: true } } },
@@ -141,10 +144,16 @@ export class RoutineTasksService {
assigneeId: actor.userId,
scheduledFor: day,
status: { in: ['todo', 'in_progress'] },
...internalRoutineTaskWhere,
},
}),
this.prisma.routineTask.count({
where: { organizationId: actor.organizationId, assigneeId: actor.userId, status: 'overdue' },
where: {
organizationId: actor.organizationId,
assigneeId: actor.userId,
status: 'overdue',
...internalRoutineTaskWhere,
},
}),
]);
return { remaining, overdue };
@@ -158,6 +167,7 @@ export class RoutineTasksService {
where: {
organizationId: actor.organizationId,
status: 'overdue',
...internalRoutineTaskWhere,
...filter,
...this.deepWhere(actor),
},
@@ -179,9 +189,9 @@ export class RoutineTasksService {
scheduledFor: { gte: dayKeyToDate(from), lte: dayKeyToDate(to) },
...filter,
...this.deepWhere(actor),
...deskRoutineTaskWhere(q.source),
...(q.categoryId ? { categoryId: q.categoryId } : {}),
...(q.status ? { status: q.status } : {}),
...(q.source ? { source: q.source } : {}),
};
const rows = await this.prisma.routineTask.findMany({
where,
@@ -311,46 +321,10 @@ export class RoutineTasksService {
return toTaskDto(row);
}
async createFromActivity(actor: Actor, activityId: string): Promise<RoutineTaskDto> {
const activity = await this.prisma.activity.findFirst({
where: { id: activityId, organizationId: actor.organizationId },
select: {
id: true,
title: true,
description: true,
dueAt: true,
priority: true,
personId: true,
companyId: true,
dealId: true,
assigneeId: true,
},
});
if (!activity) throw new NotFoundException('پیگیری پیدا نشد');
const existing = await this.prisma.routineTask.findFirst({
where: { organizationId: actor.organizationId, activityId, status: { not: 'cancelled' } },
include: routineTaskInclude,
});
if (existing) return toTaskDto(existing);
const scope = await this.scope(actor);
const assigneeId =
activity.assigneeId && canManage(scope, activity.assigneeId)
? activity.assigneeId
: actor.userId;
return this.create(actor, {
title: activity.title,
description: activity.description,
dueAt: activity.dueAt?.toISOString() ?? null,
priority: activity.priority,
assigneeId,
personId: activity.personId,
companyId: activity.companyId,
dealId: activity.dealId,
activityId: activity.id,
source: 'follow_up',
});
async createFromActivity(_actor: Actor, _activityId: string): Promise<RoutineTaskDto> {
throw new BadRequestException(
'پیگیری مشتری در کارهای روتین ثبت نمی‌شود. کارهای روتین فقط برای کار داخلی تیم است.',
);
}
async update(actor: Actor, id: string, dto: UpdateRoutineTaskDto): Promise<RoutineTaskDto> {