improvement: duedate added for shipped cases. cases and tasks ui and ux updated accordingly.

This commit is contained in:
2026-07-13 16:30:36 +03:30
parent a391eee15f
commit 08a1f34c4f
29 changed files with 502 additions and 30 deletions

View File

@@ -4,7 +4,7 @@ import {
Injectable,
NotFoundException,
} from '@nestjs/common';
import { LabTaskStatus, LinkStatus } from '@prisma/client';
import { LabTaskStatus, LinkStatus, Prisma } from '@prisma/client';
import { createReadStream, existsSync, mkdirSync } from 'fs';
import { join } from 'path';
import { randomUUID } from 'crypto';
@@ -15,7 +15,12 @@ import { TreatmentCatalogService } from '../treatment-catalog/treatment-catalog.
import {
SaveTreatmentDraftDto,
SaveTreatmentLabCasesDto,
UpdateLabCaseDueDateDto,
} from './dto/treatment.dto';
import {
isLabCaseFullyCompleted,
parseDueDateInput,
} from '../../common/lab-case-due-date';
import {
generateTreatmentTitle,
normalizeTeeth,
@@ -408,9 +413,15 @@ export class TreatmentsService {
for (const [index, lc] of dto.labCases.entries()) {
if (lc.id && sentLabCaseIds.has(lc.id)) {
if (lc.dueDate !== undefined) {
await this.updateLabCaseDueDateInTx(tx, lc.id, organizationId, lc.dueDate);
}
continue;
}
const dueDate =
lc.dueDate !== undefined ? parseDueDateInput(lc.dueDate) : undefined;
const row = lc.id
? await tx.labCase.update({
where: { id: lc.id },
@@ -418,6 +429,7 @@ export class TreatmentsService {
clientKey: lc.clientId,
sortOrder: index,
destinationOrganizationId: lc.destinationOrganizationId ?? null,
...(dueDate !== undefined ? { dueDate } : {}),
},
})
: await tx.labCase.create({
@@ -426,6 +438,7 @@ export class TreatmentsService {
clientKey: lc.clientId,
sortOrder: index,
destinationOrganizationId: lc.destinationOrganizationId ?? null,
...(dueDate !== undefined ? { dueDate } : {}),
},
});
@@ -586,6 +599,88 @@ export class TreatmentsService {
return { success: true, data: this.mapLabCase(refreshed) };
}
async updateLabCaseDueDate(
labCaseId: string,
dto: UpdateLabCaseDueDateDto,
organizationId: string,
actorUserId: string,
) {
await this.assertCanEditTreatment(actorUserId, organizationId);
const updated = await this.prisma.$transaction(async (tx) => {
await this.updateLabCaseDueDateInTx(tx, labCaseId, organizationId, dto.dueDate ?? null);
return tx.labCase.findFirstOrThrow({
where: { id: labCaseId },
include: {
details: {
include: {
detail: {
select: { id: true, clientKey: true, treatmentType: true, teeth: true },
},
},
},
sends: {
orderBy: [{ sentAt: 'asc' }],
include: { organization: { select: { id: true, name: true } } },
},
toothProsthesis: true,
tasks: { select: { id: true, status: true } },
attachments: {
include: {
attachment: {
select: {
id: true,
fileName: true,
mimeType: true,
sizeBytes: true,
createdAt: true,
},
},
},
},
},
});
});
return { success: true, data: this.mapLabCase(updated) };
}
private async updateLabCaseDueDateInTx(
tx: Prisma.TransactionClient,
labCaseId: string,
organizationId: string,
dueDateInput?: string | null,
) {
const labCase = await tx.labCase.findFirst({
where: {
id: labCaseId,
treatment: { organizationId },
sentAt: { not: null },
},
include: { tasks: { select: { status: true } } },
});
if (!labCase) {
throw new NotFoundException('Lab case not found');
}
if (isLabCaseFullyCompleted(labCase.tasks)) {
throw new BadRequestException('Due date cannot be changed after all tasks are completed');
}
let dueDate: Date | null;
try {
dueDate = parseDueDateInput(dueDateInput ?? null);
} catch {
throw new BadRequestException('Invalid due date');
}
await tx.labCase.update({
where: { id: labCaseId },
data: { dueDate },
});
}
async uploadDetailAttachments(
appointmentId: string,
detailClientKey: string,
@@ -804,6 +899,7 @@ export class TreatmentsService {
sortOrder?: number;
destinationOrganizationId?: string | null;
sentAt?: Date | null;
dueDate?: Date | null;
details?: Array<{
treatmentDetailId: string;
detail?: { id: string; clientKey: string | null; treatmentType: string; teeth: unknown };
@@ -827,12 +923,16 @@ export class TreatmentsService {
createdAt: Date;
};
}>;
tasks?: Array<{ id: string; status: LabTaskStatus }>;
}) {
const taskProgress = this.mapTaskProgress(lc.tasks ?? []);
return {
id: lc.id,
clientId: lc.clientKey ?? lc.id,
destinationOrganizationId: lc.destinationOrganizationId ?? null,
sentAt: lc.sentAt?.toISOString() ?? null,
dueDate: lc.dueDate?.toISOString() ?? null,
taskProgress,
treatmentDetailId: lc.details?.[0]?.treatmentDetailId ?? null,
detail: lc.details?.[0]
? {