Files
lcbp3/backend/src/modules/response-code/response-code.service.ts
T
admin 0240d80da5
CI / CD Pipeline / build (push) Successful in 6m1s
CI / CD Pipeline / deploy (push) Failing after 6m42s
690514:2019 204-rfa-approval-refactor #01
2026-05-14 20:19:21 +07:00

184 lines
5.5 KiB
TypeScript

// File: src/modules/response-code/response-code.service.ts
// Change Log:
// - 2026-05-13: Add basic CRUD methods for response codes to support controller mutations.
import {
ConflictException,
Injectable,
Logger,
NotFoundException,
BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, IsNull } from 'typeorm';
import { ResponseCode } from './entities/response-code.entity';
import { ResponseCodeRule } from './entities/response-code-rule.entity';
import { ResponseCodeCategory } from '../common/enums/review.enums';
import { CreateResponseCodeDto } from './dto/create-response-code.dto';
import { UpdateResponseCodeDto } from './dto/update-response-code.dto';
@Injectable()
export class ResponseCodeService {
private readonly logger = new Logger(ResponseCodeService.name);
constructor(
@InjectRepository(ResponseCode)
private readonly responseCodeRepo: Repository<ResponseCode>,
@InjectRepository(ResponseCodeRule)
private readonly responseCodeRuleRepo: Repository<ResponseCodeRule>
) {}
/**
* ดึง Response Codes ทั้งหมดที่ active
*/
async findAll(): Promise<ResponseCode[]> {
return this.responseCodeRepo.find({
where: { isActive: true },
order: { category: 'ASC', code: 'ASC' },
});
}
/**
* ดึง Response Codes ตาม Category (FR-006)
* ใช้สำหรับแสดงผลใน Review page ตามประเภทเอกสาร
*/
async findByCategory(
category: ResponseCodeCategory
): Promise<ResponseCode[]> {
return this.responseCodeRepo.find({
where: { category, isActive: true },
order: { code: 'ASC' },
});
}
/**
* ดึง Response Codes ที่ใช้ได้กับ document type + project
* รองรับ Global default + Project override (ADR-019 Q1 clarification)
*/
async findByDocumentType(
documentTypeId: number,
projectId?: number
): Promise<ResponseCode[]> {
// ดึง Rules ระดับ Project (ถ้ามี) หรือ Global default
const rules = await this.responseCodeRuleRepo.find({
where: [
{ documentTypeId, projectId: projectId ?? IsNull(), isEnabled: true },
{ documentTypeId, projectId: IsNull(), isEnabled: true },
],
relations: ['responseCode'],
});
// Project rules override global rules
const codeMap = new Map<number, ResponseCode>();
for (const rule of rules) {
if (rule.responseCode?.isActive) {
codeMap.set(rule.responseCodeId, rule.responseCode);
}
}
return Array.from(codeMap.values()).sort((a, b) =>
a.code.localeCompare(b.code)
);
}
/**
* ดึง ResponseCode โดย publicId (ADR-019)
*/
async findByPublicId(publicId: string): Promise<ResponseCode> {
const code = await this.responseCodeRepo.findOne({
where: { publicId },
});
if (!code) {
throw new NotFoundException(`Response Code not found: ${publicId}`);
}
return code;
}
/**
* สร้าง Response Code ใหม่สำหรับ Master Approval Matrix
*/
async create(dto: CreateResponseCodeDto): Promise<ResponseCode> {
const existing = await this.responseCodeRepo.findOne({
where: {
code: dto.code,
category: dto.category,
},
});
if (existing) {
throw new ConflictException(
`Response Code already exists for code=${dto.code}, category=${dto.category}`
);
}
const entity = this.responseCodeRepo.create({
code: dto.code,
subStatus: dto.subStatus,
category: dto.category,
descriptionTh: dto.descriptionTh,
descriptionEn: dto.descriptionEn,
implications: dto.implications,
notifyRoles: dto.notifyRoles,
isActive: dto.isActive ?? true,
isSystem: false,
});
return this.responseCodeRepo.save(entity);
}
/**
* อัปเดต Response Code ตาม publicId
*/
async update(
publicId: string,
dto: UpdateResponseCodeDto
): Promise<ResponseCode> {
const entity = await this.findByPublicId(publicId);
if (
(dto.code && dto.code !== entity.code) ||
(dto.category && dto.category !== entity.category)
) {
const existing = await this.responseCodeRepo.findOne({
where: {
code: dto.code ?? entity.code,
category: dto.category ?? entity.category,
},
});
if (existing && existing.publicId !== entity.publicId) {
throw new ConflictException(
`Response Code already exists for code=${dto.code ?? entity.code}, category=${dto.category ?? entity.category}`
);
}
}
Object.assign(entity, dto);
return this.responseCodeRepo.save(entity);
}
/**
* ปิดการใช้งาน Response Code โดยไม่ลบข้อมูล
*/
async deactivate(publicId: string): Promise<void> {
const entity = await this.findByPublicId(publicId);
if (entity.isSystem) {
throw new BadRequestException('Cannot deactivate a system response code');
}
entity.isActive = false;
await this.responseCodeRepo.save(entity);
}
/**
* ตรวจสอบว่า Response Code triggers notification หรือไม่ (FR-007)
* Code 1C, 1D, 3 → trigger notification
*/
async getNotifyRoles(responseCodePublicId: string): Promise<string[]> {
const code = await this.findByPublicId(responseCodePublicId);
return code.notifyRoles ?? [];
}
}