2026-06-28 16:46:42 +03:30
|
|
|
import {
|
|
|
|
|
Body,
|
|
|
|
|
Controller,
|
2026-08-19 23:59:37 +03:30
|
|
|
Delete,
|
2026-06-28 16:46:42 +03:30
|
|
|
Get,
|
|
|
|
|
Param,
|
|
|
|
|
Patch,
|
2026-08-19 15:05:58 +03:30
|
|
|
Post,
|
|
|
|
|
Put,
|
2026-06-28 16:46:42 +03:30
|
|
|
Query,
|
|
|
|
|
Req,
|
2026-07-07 18:43:10 +03:30
|
|
|
Res,
|
2026-08-19 15:05:58 +03:30
|
|
|
UploadedFiles,
|
2026-06-28 16:46:42 +03:30
|
|
|
UseGuards,
|
2026-08-19 15:05:58 +03:30
|
|
|
UseInterceptors,
|
2026-06-28 16:46:42 +03:30
|
|
|
} from '@nestjs/common';
|
2026-08-19 15:05:58 +03:30
|
|
|
import { FilesInterceptor } from '@nestjs/platform-express';
|
|
|
|
|
import { memoryStorage } from 'multer';
|
2026-07-07 18:43:10 +03:30
|
|
|
import type { Response } from 'express';
|
2026-08-19 15:05:58 +03:30
|
|
|
import { ApiBearerAuth, ApiConsumes, ApiOperation, ApiTags } from '@nestjs/swagger';
|
2026-06-28 16:46:42 +03:30
|
|
|
import { LabOrgGuard } from '../../common/guards/lab-org.guard';
|
|
|
|
|
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
|
|
|
|
|
import { CasesService } from './cases.service';
|
2026-08-19 15:05:58 +03:30
|
|
|
import {
|
|
|
|
|
ListLabCasesDto,
|
|
|
|
|
UpdateLabCaseImportantDto,
|
|
|
|
|
UpdateLabCaseExternalCodeDto,
|
|
|
|
|
AssignLabCaseTaskDto,
|
|
|
|
|
CreateLabInternalCaseDto,
|
|
|
|
|
UpdateLabInternalCaseDto,
|
|
|
|
|
} from './dto/cases.dto';
|
2026-06-28 16:46:42 +03:30
|
|
|
|
|
|
|
|
@ApiTags('cases')
|
|
|
|
|
@ApiBearerAuth('JWT-auth')
|
|
|
|
|
@UseGuards(JwtAuthGuard, LabOrgGuard)
|
|
|
|
|
@Controller('cases')
|
|
|
|
|
export class CasesController {
|
|
|
|
|
constructor(private readonly casesService: CasesService) {}
|
|
|
|
|
|
|
|
|
|
@Get()
|
|
|
|
|
@ApiOperation({ summary: 'List lab cases received by this organization' })
|
|
|
|
|
list(@Query() query: ListLabCasesDto, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.list(organizationId, req.user.id, query);
|
2026-06-28 17:49:40 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-19 15:05:58 +03:30
|
|
|
@Post()
|
|
|
|
|
@ApiOperation({ summary: 'Create a lab-origin draft case (TAB_CASES_EDIT)' })
|
|
|
|
|
create(@Body() dto: CreateLabInternalCaseDto, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.createInternal(dto, organizationId, req.user.id, req.user.language);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 15:47:32 +03:30
|
|
|
@Get('assignable-staff')
|
|
|
|
|
@ApiOperation({ summary: 'Staff who can be assigned lab tasks' })
|
|
|
|
|
listAssignableStaff(@Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.listAssignableStaff(organizationId, req.user.id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 17:49:40 +03:30
|
|
|
@Get('filter-options')
|
|
|
|
|
@ApiOperation({ summary: 'Clinics and treatment types for inbox filters' })
|
|
|
|
|
listFilterOptions(@Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.listFilterOptions(organizationId, req.user.id);
|
2026-06-28 16:46:42 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-19 15:05:58 +03:30
|
|
|
@Get('linked-clinics')
|
|
|
|
|
@ApiOperation({ summary: 'Active linked clinic organizations for lab-origin cases' })
|
|
|
|
|
listLinkedClinics(@Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.listLinkedClinics(organizationId, req.user.id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-06-28 16:46:42 +03:30
|
|
|
@Get(':id')
|
|
|
|
|
@ApiOperation({ summary: 'Get one lab case with tasks grouped by tooth' })
|
|
|
|
|
getOne(@Param('id') id: string, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
2026-07-06 20:40:19 +03:30
|
|
|
return this.casesService.getOne(id, organizationId, req.user.id, req.user.language);
|
2026-06-28 16:46:42 +03:30
|
|
|
}
|
|
|
|
|
|
2026-08-19 15:05:58 +03:30
|
|
|
@Put(':id')
|
|
|
|
|
@ApiOperation({ summary: 'Update a lab-origin draft case and its lines' })
|
|
|
|
|
update(@Param('id') id: string, @Body() dto: UpdateLabInternalCaseDto, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.updateInternal(id, dto, organizationId, req.user.id, req.user.language);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Post(':id/start')
|
|
|
|
|
@ApiOperation({ summary: 'Start a lab-origin draft (generate tasks, no clinic notify)' })
|
|
|
|
|
start(@Param('id') id: string, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.startInternal(id, organizationId, req.user.id, req.user.language);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 23:59:37 +03:30
|
|
|
@Delete(':id')
|
|
|
|
|
@ApiOperation({ summary: 'Delete an unstarted lab-origin draft (TAB_CASES_EDIT)' })
|
|
|
|
|
deleteDraft(@Param('id') id: string, @Req() req) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.deleteInternalDraft(id, organizationId, req.user.id);
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-19 15:05:58 +03:30
|
|
|
@Post(':id/lines/:lineClientKey/attachments')
|
|
|
|
|
@ApiOperation({ summary: 'Upload attachments for a lab-origin draft line' })
|
|
|
|
|
@ApiConsumes('multipart/form-data')
|
|
|
|
|
@UseInterceptors(
|
|
|
|
|
FilesInterceptor('files', 20, {
|
|
|
|
|
storage: memoryStorage(),
|
|
|
|
|
}),
|
|
|
|
|
)
|
|
|
|
|
uploadAttachments(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Param('lineClientKey') lineClientKey: string,
|
|
|
|
|
@UploadedFiles() files: Express.Multer.File[],
|
|
|
|
|
@Req() req,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.uploadInternalAttachments(
|
|
|
|
|
id,
|
|
|
|
|
lineClientKey,
|
|
|
|
|
files,
|
|
|
|
|
organizationId,
|
|
|
|
|
req.user.id,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-07 18:43:10 +03:30
|
|
|
@Get(':id/attachments/:attachmentId/file')
|
|
|
|
|
@ApiOperation({ summary: 'Download an attachment shared with this lab case' })
|
|
|
|
|
async downloadAttachment(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Param('attachmentId') attachmentId: string,
|
|
|
|
|
@Req() req,
|
|
|
|
|
@Res() res: Response,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
const file = await this.casesService.streamCaseAttachment(
|
|
|
|
|
id,
|
|
|
|
|
attachmentId,
|
|
|
|
|
organizationId,
|
|
|
|
|
req.user.id,
|
|
|
|
|
);
|
|
|
|
|
res.setHeader('Content-Type', file.mimeType);
|
|
|
|
|
res.setHeader('Content-Disposition', `inline; filename="${file.fileName}"`);
|
|
|
|
|
file.stream.pipe(res);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-08 02:53:47 +03:30
|
|
|
@Patch(':id/important')
|
|
|
|
|
@ApiOperation({ summary: 'Toggle the important flag for a whole case' })
|
|
|
|
|
setCaseImportant(
|
2026-06-28 16:46:42 +03:30
|
|
|
@Param('id') id: string,
|
2026-07-08 02:53:47 +03:30
|
|
|
@Body() dto: UpdateLabCaseImportantDto,
|
2026-06-28 16:46:42 +03:30
|
|
|
@Req() req,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
2026-07-08 02:53:47 +03:30
|
|
|
return this.casesService.setCaseImportant(id, dto, organizationId, req.user.id, req.user.language);
|
2026-06-28 16:46:42 +03:30
|
|
|
}
|
2026-07-13 15:47:32 +03:30
|
|
|
|
2026-07-18 22:00:44 +03:30
|
|
|
@Patch(':id/external-code')
|
|
|
|
|
@ApiOperation({ summary: 'Set or clear the optional external order code for a case' })
|
|
|
|
|
setCaseExternalCode(
|
|
|
|
|
@Param('id') id: string,
|
|
|
|
|
@Body() dto: UpdateLabCaseExternalCodeDto,
|
|
|
|
|
@Req() req,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.setCaseExternalCode(id, dto, organizationId, req.user.id, req.user.language);
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-13 15:47:32 +03:30
|
|
|
@Patch(':caseId/tasks/:taskId/assign')
|
|
|
|
|
@ApiOperation({ summary: 'Assign or unassign a task to lab staff' })
|
|
|
|
|
assignTask(
|
|
|
|
|
@Param('caseId') caseId: string,
|
|
|
|
|
@Param('taskId') taskId: string,
|
|
|
|
|
@Body() dto: AssignLabCaseTaskDto,
|
|
|
|
|
@Req() req,
|
|
|
|
|
) {
|
|
|
|
|
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
|
|
|
|
|
return this.casesService.assignTask(
|
|
|
|
|
caseId,
|
|
|
|
|
taskId,
|
|
|
|
|
dto,
|
|
|
|
|
organizationId,
|
|
|
|
|
req.user.id,
|
|
|
|
|
req.user.language,
|
|
|
|
|
);
|
|
|
|
|
}
|
2026-06-28 16:46:42 +03:30
|
|
|
}
|