improvement: task assignment flow added. cases and tasks feature updated accordingly.

This commit is contained in:
2026-07-13 15:47:32 +03:30
parent 0d073f1ec0
commit a391eee15f
21 changed files with 357 additions and 35 deletions

View File

@@ -14,8 +14,9 @@ import {
} from '../catalog/catalog-label.service';
import { TreatmentCatalogService } from '../treatment-catalog/treatment-catalog.service';
import { normalizeTeeth } from '../treatments/treatment.utils';
import { ListLabCasesDto, UpdateLabCaseImportantDto } from './dto/cases.dto';
import { ListLabCasesDto, UpdateLabCaseImportantDto, AssignLabCaseTaskDto } from './dto/cases.dto';
import { normalizeTaskTeeth } from './lab-case-task.util';
import { hasEffectivePermission } from '../../common/membership-permissions';
const labCaseListInclude = {
treatment: {
@@ -49,6 +50,7 @@ const labCaseListInclude = {
],
include: {
lastStatusChangedBy: { select: { id: true, name: true } },
assignee: { select: { id: true, name: true } },
statusEvents: {
orderBy: { changedAt: 'asc' as const },
include: { changedBy: { select: { id: true, name: true } } },
@@ -74,6 +76,7 @@ const labCaseListInclude = {
type LabCaseTaskWithRelations = Prisma.LabCaseTaskGetPayload<{
include: {
lastStatusChangedBy: { select: { id: true; name: true } };
assignee: { select: { id: true; name: true } };
statusEvents: {
include: { changedBy: { select: { id: true; name: true } } };
};
@@ -370,6 +373,95 @@ export class CasesService {
return { success: true, data: await this.mapLabCaseDetail(labCase, localeInput) };
}
async listAssignableStaff(labOrganizationId: string, actorUserId: string) {
await this.assertCanEditCases(actorUserId, labOrganizationId);
const memberships = await this.prisma.membership.findMany({
where: {
organizationId: labOrganizationId,
OR: [{ isOwner: true }, { isActive: true }],
},
include: {
permissions: { include: { permission: true } },
organization: { include: { type: true, plan: true } },
user: { select: { id: true, name: true } },
},
});
const staff = memberships
.filter((m) => hasEffectivePermission(m, 'TAB_TASKS_EDIT'))
.map((m) => ({
id: m.user.id,
name: m.user.name,
}))
.sort((a, b) => a.name.localeCompare(b.name));
return { success: true, data: staff };
}
async assignTask(
labCaseId: string,
taskId: string,
dto: AssignLabCaseTaskDto,
labOrganizationId: string,
actorUserId: string,
localeInput?: string | null,
) {
await this.assertCanEditCases(actorUserId, labOrganizationId);
const task = await this.prisma.labCaseTask.findFirst({
where: {
id: taskId,
labCaseId,
labCase: {
sentAt: { not: null },
sends: { some: { organizationId: labOrganizationId } },
},
},
select: { id: true },
});
if (!task) {
throw new NotFoundException('Task not found');
}
const assigneeUserId = dto.assigneeUserId ?? null;
if (assigneeUserId) {
const memberships = await this.prisma.membership.findMany({
where: {
organizationId: labOrganizationId,
userId: assigneeUserId,
OR: [{ isOwner: true }, { isActive: true }],
},
include: {
permissions: { include: { permission: true } },
organization: { include: { type: true, plan: true } },
},
});
const canReceive = memberships.some((m) => hasEffectivePermission(m, 'TAB_TASKS_EDIT'));
if (!canReceive) {
throw new BadRequestException('Selected user cannot be assigned tasks');
}
}
await this.prisma.labCaseTask.update({
where: { id: taskId },
data: {
assigneeUserId,
assignedAt: assigneeUserId ? new Date() : null,
},
});
const labCase = await this.prisma.labCase.findFirstOrThrow({
where: { id: labCaseId },
include: labCaseListInclude,
});
return { success: true, data: await this.mapLabCaseDetail(labCase, localeInput) };
}
private buildListWhere(
labOrganizationId: string,
query: ListLabCasesDto,
@@ -579,6 +671,10 @@ export class CasesService {
stepOrder: task.stepOrder,
stepLabel: task.stepLabel,
status: task.status,
assignee: task.assignee
? { id: task.assignee.id, name: task.assignee.name }
: null,
assignedAt: task.assignedAt?.toISOString() ?? null,
createdAt: task.createdAt.toISOString(),
lastStatusChangedAt: task.lastStatusChangedAt?.toISOString() ?? null,
lastStatusChangedBy: task.lastStatusChangedBy