690513:0920 Refactor Workflow module: Lint error #01
CI / CD Pipeline / build (push) Failing after 10m44s
CI / CD Pipeline / deploy (push) Has been skipped

This commit is contained in:
2026-05-13 09:20:49 +07:00
parent e218fc826c
commit 5537d20152
299 changed files with 27326 additions and 2501 deletions
@@ -56,6 +56,9 @@ export class ResponseCode extends UuidBaseEntity {
createdAt!: Date;
// Relations
@OneToMany(() => ResponseCodeRule, (rule: ResponseCodeRule) => rule.responseCode)
@OneToMany(
() => ResponseCodeRule,
(rule: ResponseCodeRule) => rule.responseCode
)
rules?: ResponseCodeRule[];
}
@@ -34,11 +34,11 @@ export class ResponseCodeController {
@Get('document-type/:documentTypeId')
findByDocumentType(
@Param('documentTypeId') documentTypeId: string,
@Query('projectId') projectId?: string,
@Query('projectId') projectId?: string
) {
return this.responseCodeService.findByDocumentType(
Number(documentTypeId),
projectId ? Number(projectId) : undefined,
projectId ? Number(projectId) : undefined
);
}
@@ -14,7 +14,7 @@ export class ResponseCodeService {
@InjectRepository(ResponseCode)
private readonly responseCodeRepo: Repository<ResponseCode>,
@InjectRepository(ResponseCodeRule)
private readonly responseCodeRuleRepo: Repository<ResponseCodeRule>,
private readonly responseCodeRuleRepo: Repository<ResponseCodeRule>
) {}
/**
@@ -31,7 +31,9 @@ export class ResponseCodeService {
* ดึง Response Codes ตาม Category (FR-006)
* ใช้สำหรับแสดงผลใน Review page ตามประเภทเอกสาร
*/
async findByCategory(category: ResponseCodeCategory): Promise<ResponseCode[]> {
async findByCategory(
category: ResponseCodeCategory
): Promise<ResponseCode[]> {
return this.responseCodeRepo.find({
where: { category, isActive: true },
order: { code: 'ASC' },
@@ -44,7 +46,7 @@ export class ResponseCodeService {
*/
async findByDocumentType(
documentTypeId: number,
projectId?: number,
projectId?: number
): Promise<ResponseCode[]> {
// ดึง Rules ระดับ Project (ถ้ามี) หรือ Global default
const rules = await this.responseCodeRuleRepo.find({
@@ -64,7 +66,7 @@ export class ResponseCodeService {
}
return Array.from(codeMap.values()).sort((a, b) =>
a.code.localeCompare(b.code),
a.code.localeCompare(b.code)
);
}
@@ -13,7 +13,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติเพื่อก่อสร้าง',
descriptionEn: 'Approved for Construction',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -21,8 +25,13 @@ export const responseCodeSeedData = [
code: '1B',
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติเพื่อก่อสร้าง พร้อมความเห็น (แก้ไขไม่ต้องส่งกลับ)',
descriptionEn: 'Approved for Construction with Comments (No Resubmission Required)',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
descriptionEn:
'Approved for Construction with Comments (No Resubmission Required)',
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -31,7 +40,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติ — มีผลต่อสัญญา/Change Order',
descriptionEn: 'Approved — Contract Implications / Change Order Required',
implications: { affectsSchedule: true, affectsCost: true, requiresContractReview: true },
implications: {
affectsSchedule: true,
affectsCost: true,
requiresContractReview: true,
},
notifyRoles: ['CONTRACT_MANAGER', 'QS_MANAGER'],
isSystem: true,
},
@@ -40,7 +53,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติทางเลือก — แตกต่างจากแบบสัญญา',
descriptionEn: 'Approved Alternative — Differs from Contract Drawing',
implications: { affectsSchedule: false, affectsCost: true, requiresContractReview: true },
implications: {
affectsSchedule: false,
affectsCost: true,
requiresContractReview: true,
},
notifyRoles: ['CONTRACT_MANAGER', 'DESIGN_MANAGER'],
isSystem: true,
},
@@ -49,7 +66,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติเพื่อวัตถุประสงค์การออกแบบเท่านั้น',
descriptionEn: 'Approved for Design Purpose Only',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -58,7 +79,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติเพื่ออ้างอิงเท่านั้น',
descriptionEn: 'Approved for Reference Only',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -67,7 +92,12 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติพร้อมเงื่อนไข ESG',
descriptionEn: 'Approved with ESG Conditions',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false, requiresEiaAmendment: true },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
requiresEiaAmendment: true,
},
notifyRoles: ['EIA_OFFICER', 'HSE_MANAGER'],
isSystem: true,
},
@@ -76,7 +106,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'อนุมัติตามหมายเหตุ — ต้องแก้ไขและส่งกลับเพื่อตรวจสอบ',
descriptionEn: 'Approved as Noted — Revise and Resubmit for Review',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -85,7 +119,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'ปฏิเสธ — ต้องแก้ไขและส่งใหม่',
descriptionEn: 'Rejected — Revise and Resubmit',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: ['PROJECT_MANAGER', 'DESIGN_MANAGER'],
isSystem: true,
},
@@ -94,7 +132,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ENGINEERING,
descriptionTh: 'ไม่เกี่ยวข้อง / ถอนคืน',
descriptionEn: 'Not Applicable / Withdrawn',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -105,7 +147,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'อนุมัติวัสดุ/อุปกรณ์เพื่อจัดซื้อ',
descriptionEn: 'Approved for Procurement',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -114,7 +160,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'อนุมัติวัสดุ/อุปกรณ์ พร้อมความเห็น',
descriptionEn: 'Approved for Procurement with Comments',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -123,7 +173,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'อนุมัติ — มีผลต่อค่าใช้จ่าย',
descriptionEn: 'Approved — Cost Implications',
implications: { affectsSchedule: false, affectsCost: true, requiresContractReview: true },
implications: {
affectsSchedule: false,
affectsCost: true,
requiresContractReview: true,
},
notifyRoles: ['QS_MANAGER', 'PROCUREMENT_MANAGER'],
isSystem: true,
},
@@ -132,7 +186,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'ส่งข้อมูลเพิ่มเติม',
descriptionEn: 'Provide Additional Information',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -141,7 +199,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'ปฏิเสธ — ไม่เป็นไปตามสัญญา',
descriptionEn: 'Rejected — Non-Compliant with Contract',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: ['PROJECT_MANAGER', 'PROCUREMENT_MANAGER'],
isSystem: true,
},
@@ -150,7 +212,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.MATERIAL,
descriptionTh: 'ไม่เกี่ยวข้อง / ถอนคืน',
descriptionEn: 'Not Applicable / Withdrawn',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -161,7 +227,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.CONTRACT,
descriptionTh: 'อนุมัติ — ไม่มีผลต่อสัญญา',
descriptionEn: 'Approved — No Contract Implication',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -170,7 +240,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.CONTRACT,
descriptionTh: 'อนุมัติ — ต้องออก Change Order',
descriptionEn: 'Approved — Change Order Required',
implications: { affectsSchedule: true, affectsCost: true, requiresContractReview: true },
implications: {
affectsSchedule: true,
affectsCost: true,
requiresContractReview: true,
},
notifyRoles: ['CONTRACT_MANAGER', 'QS_MANAGER', 'PROJECT_MANAGER'],
isSystem: true,
},
@@ -179,7 +253,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.CONTRACT,
descriptionTh: 'อยู่ระหว่างการพิจารณา — ต้องการข้อมูลเพิ่มเติม',
descriptionEn: 'Under Review — Additional Information Required',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -188,7 +266,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.CONTRACT,
descriptionTh: 'ปฏิเสธ — ขัดต่อเงื่อนไขสัญญา',
descriptionEn: 'Rejected — Contradicts Contract Terms',
implications: { affectsSchedule: true, affectsCost: true, requiresContractReview: true },
implications: {
affectsSchedule: true,
affectsCost: true,
requiresContractReview: true,
},
notifyRoles: ['CONTRACT_MANAGER', 'LEGAL_TEAM', 'PROJECT_MANAGER'],
isSystem: true,
},
@@ -199,7 +281,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.TESTING,
descriptionTh: 'อนุมัติผลการทดสอบ / ส่งมอบ',
descriptionEn: 'Approved — Test Results / Handover Accepted',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -208,7 +294,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.TESTING,
descriptionTh: 'ผ่านพร้อมข้อบกพร่องเล็กน้อย — ต้องแก้ไขและรายงาน',
descriptionEn: 'Passed with Minor Defects — Rectify and Report',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: ['QA_MANAGER'],
isSystem: true,
},
@@ -217,7 +307,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.TESTING,
descriptionTh: 'ไม่ผ่าน — ต้องทดสอบซ้ำ',
descriptionEn: 'Failed — Retest Required',
implications: { affectsSchedule: true, affectsCost: true, requiresContractReview: false },
implications: {
affectsSchedule: true,
affectsCost: true,
requiresContractReview: false,
},
notifyRoles: ['PROJECT_MANAGER', 'QA_MANAGER'],
isSystem: true,
},
@@ -228,7 +322,11 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ESG,
descriptionTh: 'อนุมัติ — เป็นไปตามมาตรฐาน ESG',
descriptionEn: 'Approved — ESG Compliant',
implications: { affectsSchedule: false, affectsCost: false, requiresContractReview: false },
implications: {
affectsSchedule: false,
affectsCost: false,
requiresContractReview: false,
},
notifyRoles: [],
isSystem: true,
},
@@ -237,7 +335,12 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ESG,
descriptionTh: 'อนุมัติพร้อมเงื่อนไขด้านสิ่งแวดล้อม',
descriptionEn: 'Approved with Environmental Conditions',
implications: { affectsSchedule: true, affectsCost: false, requiresContractReview: false, requiresEiaAmendment: true },
implications: {
affectsSchedule: true,
affectsCost: false,
requiresContractReview: false,
requiresEiaAmendment: true,
},
notifyRoles: ['EIA_OFFICER', 'HSE_MANAGER'],
isSystem: true,
},
@@ -246,7 +349,12 @@ export const responseCodeSeedData = [
category: ResponseCodeCategory.ESG,
descriptionTh: 'ปฏิเสธ — ไม่เป็นไปตามข้อกำหนด EIA/ESG',
descriptionEn: 'Rejected — Non-Compliant with EIA/ESG Requirements',
implications: { affectsSchedule: true, affectsCost: true, requiresContractReview: false, requiresEiaAmendment: true },
implications: {
affectsSchedule: true,
affectsCost: true,
requiresContractReview: false,
requiresEiaAmendment: true,
},
notifyRoles: ['EIA_OFFICER', 'HSE_MANAGER', 'PROJECT_MANAGER'],
isSystem: true,
},
@@ -261,13 +369,16 @@ export async function seedResponseCodes(dataSource: DataSource): Promise<void> {
for (const data of responseCodeSeedData) {
const exists = await repo.findOne({
where: { code: data.code, category: data.category as ResponseCodeCategory },
where: {
code: data.code,
category: data.category,
},
});
if (!exists) {
const entity = repo.create({
code: data.code,
category: data.category as ResponseCodeCategory,
category: data.category,
descriptionTh: data.descriptionTh,
descriptionEn: data.descriptionEn,
implications: data.implications,
@@ -35,14 +35,14 @@ export class ImplicationsService {
responseCode.code,
affectsSchedule,
affectsCost,
requiresContractReview,
requiresContractReview
);
const actionRequired = this.buildActionList(
responseCode.code,
requiresContractReview,
requiresEiaAmendment,
affectsCost,
affectsCost
);
return {
@@ -60,7 +60,7 @@ export class ImplicationsService {
code: string,
affectsSchedule: boolean,
affectsCost: boolean,
requiresContractReview: boolean,
requiresContractReview: boolean
): CodeImplicationResult['severity'] {
// Code 3 (Rejected) = CRITICAL เสมอ
if (code === '3') return 'CRITICAL';
@@ -72,7 +72,8 @@ export class ImplicationsService {
if (affectsSchedule && affectsCost) return 'HIGH';
// มีผลต่ออย่างใดอย่างหนึ่ง
if (requiresContractReview || affectsSchedule || affectsCost) return 'MEDIUM';
if (requiresContractReview || affectsSchedule || affectsCost)
return 'MEDIUM';
return 'LOW';
}
@@ -81,7 +82,7 @@ export class ImplicationsService {
code: string,
requiresContractReview: boolean,
requiresEiaAmendment: boolean,
affectsCost: boolean,
affectsCost: boolean
): string[] {
const actions: string[] = [];
@@ -22,7 +22,7 @@ export class InheritanceService {
constructor(
@InjectRepository(ResponseCodeRule)
private readonly ruleRepo: Repository<ResponseCodeRule>,
private readonly ruleRepo: Repository<ResponseCodeRule>
) {}
/**
@@ -34,7 +34,7 @@ export class InheritanceService {
*/
async resolveMatrix(
documentTypeId: number,
projectId?: number,
projectId?: number
): Promise<ResolvedMatrix[]> {
// ดึง global rules (projectId IS NULL)
const globalRules = await this.ruleRepo.find({
@@ -63,7 +63,7 @@ export class InheritanceService {
// Build map: responseCodeId → project rule
const projectRuleMap = new Map(
projectRules.map((r) => [r.responseCodeId, r]),
projectRules.map((r) => [r.responseCodeId, r])
);
// Merge: project overrides global
@@ -96,7 +96,7 @@ export class InheritanceService {
// เพิ่ม project-only rules (ไม่มี global parent)
for (const projectRule of projectRules) {
const alreadyMerged = globalRules.some(
(g) => g.responseCodeId === projectRule.responseCodeId,
(g) => g.responseCodeId === projectRule.responseCodeId
);
if (!alreadyMerged) {
merged.push({
@@ -113,7 +113,7 @@ export class InheritanceService {
}
this.logger.debug(
`Resolved ${merged.length} rules for docType=${documentTypeId}, project=${projectId}`,
`Resolved ${merged.length} rules for docType=${documentTypeId}, project=${projectId}`
);
return merged;
@@ -1,6 +1,11 @@
// File: src/modules/response-code/services/matrix-management.service.ts
// CRUD สำหรับ ResponseCodeRule (global + project overrides) (T061, FR-022)
import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import {
Injectable,
Logger,
NotFoundException,
BadRequestException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ResponseCodeRule } from '../entities/response-code-rule.entity';
@@ -23,7 +28,7 @@ export class MatrixManagementService {
@InjectRepository(ResponseCodeRule)
private readonly ruleRepo: Repository<ResponseCodeRule>,
@InjectRepository(ResponseCode)
private readonly codeRepo: Repository<ResponseCode>,
private readonly codeRepo: Repository<ResponseCode>
) {}
/**
@@ -35,7 +40,9 @@ export class MatrixManagementService {
});
if (!code) {
throw new NotFoundException(`ResponseCode not found: ${dto.responseCodePublicId}`);
throw new NotFoundException(
`ResponseCode not found: ${dto.responseCodePublicId}`
);
}
if (code.isSystem && !dto.isEnabled) {
@@ -52,8 +59,10 @@ export class MatrixManagementService {
if (existing) {
existing.isEnabled = dto.isEnabled;
existing.requiresComments = dto.requiresComments ?? existing.requiresComments;
existing.triggersNotification = dto.triggersNotification ?? existing.triggersNotification;
existing.requiresComments =
dto.requiresComments ?? existing.requiresComments;
existing.triggersNotification =
dto.triggersNotification ?? existing.triggersNotification;
return this.ruleRepo.save(existing);
}
@@ -74,7 +83,7 @@ export class MatrixManagementService {
*/
async getRulesByDocType(
documentTypeId: number,
projectId?: number,
projectId?: number
): Promise<ResponseCodeRule[]> {
const where: Record<string, unknown> = { documentTypeId };
if (projectId !== undefined) {
@@ -93,10 +102,14 @@ export class MatrixManagementService {
* ลบ project override (หวนกลับใช้ global default)
*/
async deleteProjectOverride(rulePublicId: string): Promise<void> {
const rule = await this.ruleRepo.findOne({ where: { publicId: rulePublicId } });
const rule = await this.ruleRepo.findOne({
where: { publicId: rulePublicId },
});
if (!rule) throw new NotFoundException(rulePublicId);
if (!rule.projectId) {
throw new BadRequestException('Cannot delete a global rule — disable it instead');
throw new BadRequestException(
'Cannot delete a global rule — disable it instead'
);
}
await this.ruleRepo.remove(rule);
}
@@ -19,7 +19,7 @@ export class NotificationTriggerService {
@InjectRepository(User)
private readonly userRepo: Repository<User>,
private readonly notificationService: NotificationService,
private readonly implicationsService: ImplicationsService,
private readonly implicationsService: ImplicationsService
) {}
/**
@@ -30,14 +30,16 @@ export class NotificationTriggerService {
responseCodePublicId: string,
rfaPublicId: string,
documentNumber: string,
reviewerUserId: number,
_reviewerUserId: number
): Promise<void> {
const responseCode = await this.responseCodeRepo.findOne({
where: { publicId: responseCodePublicId },
});
if (!responseCode) {
this.logger.warn(`Response code not found for notification trigger: ${responseCodePublicId}`);
this.logger.warn(
`Response code not found for notification trigger: ${responseCodePublicId}`
);
return;
}
@@ -75,12 +77,12 @@ export class NotificationTriggerService {
type: 'SYSTEM',
entityType: 'rfa',
entityId: rfaPublicId as unknown as number,
}),
),
})
)
);
this.logger.log(
`Triggered ${notifyRoles.length} role notifications for code ${codeLabel} on document ${documentNumber}`,
`Triggered ${notifyRoles.length} role notifications for code ${codeLabel} on document ${documentNumber}`
);
}
}