690329:1904 Fixing revision number Kimi K2.5 #01
This commit is contained in:
BIN
Binary file not shown.
+561
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+893
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+893
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+561
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+893
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1 +1 @@
|
||||
{"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\due-date-reminder.service.spec.ts":[1,1723],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\correspondence.service.spec.ts":[1,7901],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\auth.service.spec.ts":[1,1538],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\document-numbering\\document-numbering.service.spec.ts":[1,2037],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\casl\\ability.factory.spec.ts":[1,1294],"E:\\np-dms\\lcbp3\\backend\\src\\common\\services\\uuid-resolver.service.spec.ts":[1,5491],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\workflow-engine\\workflow-engine.service.spec.ts":[1,5639],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\workflow-engine\\dsl\\parser.service.spec.ts":[1,5818],"E:\\np-dms\\lcbp3\\backend\\src\\common\\pipes\\parse-uuid.pipe.spec.ts":[1,355],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\user\\user.service.spec.ts":[1,1270],"E:\\np-dms\\lcbp3\\backend\\src\\common\\file-storage\\file-storage.service.spec.ts":[1,1187],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\correspondence.controller.spec.ts":[1,8495],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\migration\\migration.service.spec.ts":[1,1296],"E:\\np-dms\\lcbp3\\backend\\src\\common\\entities\\uuid-base.entity.spec.ts":[1,430],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\project\\project.service.spec.ts":[1,1109],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\auth.controller.spec.ts":[1,2191],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\document-numbering\\services\\manual-override.service.spec.ts":[1,5256],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\project\\project.controller.spec.ts":[1,1827],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\migration\\migration.controller.spec.ts":[1,6380],"E:\\np-dms\\lcbp3\\backend\\src\\common\\file-storage\\file-storage.controller.spec.ts":[1,1482],"E:\\np-dms\\lcbp3\\backend\\src\\app.controller.spec.ts":[1,587],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\json-schema\\json-schema.controller.spec.ts":[1,5062]}
|
||||
{"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\due-date-reminder.service.spec.ts":[1,1723],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\correspondence.service.spec.ts":[1,10118],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\auth.service.spec.ts":[1,1538],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\document-numbering\\document-numbering.service.spec.ts":[1,2037],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\casl\\ability.factory.spec.ts":[1,1294],"E:\\np-dms\\lcbp3\\backend\\src\\common\\services\\uuid-resolver.service.spec.ts":[1,5491],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\workflow-engine\\workflow-engine.service.spec.ts":[1,5639],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\workflow-engine\\dsl\\parser.service.spec.ts":[1,5818],"E:\\np-dms\\lcbp3\\backend\\src\\common\\pipes\\parse-uuid.pipe.spec.ts":[1,355],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\user\\user.service.spec.ts":[1,1270],"E:\\np-dms\\lcbp3\\backend\\src\\common\\file-storage\\file-storage.service.spec.ts":[1,1187],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\correspondence\\correspondence.controller.spec.ts":[1,10937],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\migration\\migration.service.spec.ts":[1,1296],"E:\\np-dms\\lcbp3\\backend\\src\\common\\entities\\uuid-base.entity.spec.ts":[1,430],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\project\\project.service.spec.ts":[1,1109],"E:\\np-dms\\lcbp3\\backend\\src\\common\\auth\\auth.controller.spec.ts":[1,2191],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\document-numbering\\services\\manual-override.service.spec.ts":[1,5256],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\project\\project.controller.spec.ts":[1,1827],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\migration\\migration.controller.spec.ts":[1,6380],"E:\\np-dms\\lcbp3\\backend\\src\\common\\file-storage\\file-storage.controller.spec.ts":[1,1482],"E:\\np-dms\\lcbp3\\backend\\src\\app.controller.spec.ts":[1,587],"E:\\np-dms\\lcbp3\\backend\\src\\modules\\json-schema\\json-schema.controller.spec.ts":[1,5062]}
|
||||
@@ -427,5 +427,297 @@ describe('CorrespondenceService', () => {
|
||||
expect.objectContaining({ originatorId: 10 })
|
||||
);
|
||||
});
|
||||
|
||||
it('should set revisionLabel to "A" for RFA type', async () => {
|
||||
const mockUser = {
|
||||
user_id: 1,
|
||||
primaryOrganizationId: 10,
|
||||
} as unknown as User;
|
||||
|
||||
const createDto: CreateCorrespondenceDto = {
|
||||
projectId: 'project-uuid',
|
||||
typeId: 1,
|
||||
subject: 'Test Subject',
|
||||
recipients: [{ organizationId: 'recipient-uuid', type: 'TO' }],
|
||||
};
|
||||
|
||||
const typeRepo = testingModule.get<Repository<CorrespondenceType>>(
|
||||
getRepositoryToken(CorrespondenceType)
|
||||
);
|
||||
const statusRepo = testingModule.get<Repository<CorrespondenceStatus>>(
|
||||
getRepositoryToken(CorrespondenceStatus)
|
||||
);
|
||||
const uuidResolver =
|
||||
testingModule.get<UuidResolverService>(UuidResolverService);
|
||||
|
||||
(uuidResolver.resolveProjectId as jest.Mock).mockResolvedValue(100);
|
||||
(uuidResolver.resolveOrganizationId as jest.Mock).mockResolvedValue(20);
|
||||
|
||||
(typeRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
typeCode: 'RFA',
|
||||
});
|
||||
(statusRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
statusCode: 'DRAFT',
|
||||
});
|
||||
|
||||
(numberingService.generateNextNumber as jest.Mock).mockResolvedValue({
|
||||
number: 'DOC-001',
|
||||
});
|
||||
|
||||
mockDataSource.manager.findOne
|
||||
.mockResolvedValueOnce({ id: 10, organizationCode: 'ORG' })
|
||||
.mockResolvedValueOnce({ id: 20, organizationCode: 'REC' });
|
||||
|
||||
const queryRunner = {
|
||||
connect: jest.fn(),
|
||||
startTransaction: jest.fn(),
|
||||
commitTransaction: jest.fn(),
|
||||
rollbackTransaction: jest.fn(),
|
||||
release: jest.fn(),
|
||||
manager: {
|
||||
create: jest.fn(
|
||||
(_entity: unknown, payload: Record<string, unknown>) => payload
|
||||
),
|
||||
save: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 999, publicId: 'corr-uuid' })
|
||||
.mockResolvedValueOnce({ id: 1000 })
|
||||
.mockResolvedValueOnce([]),
|
||||
findOne: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
(mockDataSource.createQueryRunner as jest.Mock).mockReturnValue(
|
||||
queryRunner
|
||||
);
|
||||
|
||||
await service.create(createDto, mockUser);
|
||||
|
||||
expect(queryRunner.manager.create).toHaveBeenCalledWith(
|
||||
CorrespondenceRevision,
|
||||
expect.objectContaining({ revisionLabel: 'A' })
|
||||
);
|
||||
});
|
||||
|
||||
it('should set revisionLabel to "A" for RFI type', async () => {
|
||||
const mockUser = {
|
||||
user_id: 1,
|
||||
primaryOrganizationId: 10,
|
||||
} as unknown as User;
|
||||
|
||||
const createDto: CreateCorrespondenceDto = {
|
||||
projectId: 'project-uuid',
|
||||
typeId: 1,
|
||||
subject: 'Test Subject',
|
||||
recipients: [{ organizationId: 'recipient-uuid', type: 'TO' }],
|
||||
};
|
||||
|
||||
const typeRepo = testingModule.get<Repository<CorrespondenceType>>(
|
||||
getRepositoryToken(CorrespondenceType)
|
||||
);
|
||||
const statusRepo = testingModule.get<Repository<CorrespondenceStatus>>(
|
||||
getRepositoryToken(CorrespondenceStatus)
|
||||
);
|
||||
const uuidResolver =
|
||||
testingModule.get<UuidResolverService>(UuidResolverService);
|
||||
|
||||
(uuidResolver.resolveProjectId as jest.Mock).mockResolvedValue(100);
|
||||
(uuidResolver.resolveOrganizationId as jest.Mock).mockResolvedValue(20);
|
||||
|
||||
(typeRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
typeCode: 'RFI',
|
||||
});
|
||||
(statusRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
statusCode: 'DRAFT',
|
||||
});
|
||||
|
||||
(numberingService.generateNextNumber as jest.Mock).mockResolvedValue({
|
||||
number: 'DOC-001',
|
||||
});
|
||||
|
||||
mockDataSource.manager.findOne
|
||||
.mockResolvedValueOnce({ id: 10, organizationCode: 'ORG' })
|
||||
.mockResolvedValueOnce({ id: 20, organizationCode: 'REC' });
|
||||
|
||||
const queryRunner = {
|
||||
connect: jest.fn(),
|
||||
startTransaction: jest.fn(),
|
||||
commitTransaction: jest.fn(),
|
||||
rollbackTransaction: jest.fn(),
|
||||
release: jest.fn(),
|
||||
manager: {
|
||||
create: jest.fn(
|
||||
(_entity: unknown, payload: Record<string, unknown>) => payload
|
||||
),
|
||||
save: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 999, publicId: 'corr-uuid' })
|
||||
.mockResolvedValueOnce({ id: 1000 })
|
||||
.mockResolvedValueOnce([]),
|
||||
findOne: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
(mockDataSource.createQueryRunner as jest.Mock).mockReturnValue(
|
||||
queryRunner
|
||||
);
|
||||
|
||||
await service.create(createDto, mockUser);
|
||||
|
||||
expect(queryRunner.manager.create).toHaveBeenCalledWith(
|
||||
CorrespondenceRevision,
|
||||
expect.objectContaining({ revisionLabel: 'A' })
|
||||
);
|
||||
});
|
||||
|
||||
it('should set revisionLabel to null for LETTER type', async () => {
|
||||
const mockUser = {
|
||||
user_id: 1,
|
||||
primaryOrganizationId: 10,
|
||||
} as unknown as User;
|
||||
|
||||
const createDto: CreateCorrespondenceDto = {
|
||||
projectId: 'project-uuid',
|
||||
typeId: 1,
|
||||
subject: 'Test Subject',
|
||||
recipients: [{ organizationId: 'recipient-uuid', type: 'TO' }],
|
||||
};
|
||||
|
||||
const typeRepo = testingModule.get<Repository<CorrespondenceType>>(
|
||||
getRepositoryToken(CorrespondenceType)
|
||||
);
|
||||
const statusRepo = testingModule.get<Repository<CorrespondenceStatus>>(
|
||||
getRepositoryToken(CorrespondenceStatus)
|
||||
);
|
||||
const uuidResolver =
|
||||
testingModule.get<UuidResolverService>(UuidResolverService);
|
||||
|
||||
(uuidResolver.resolveProjectId as jest.Mock).mockResolvedValue(100);
|
||||
(uuidResolver.resolveOrganizationId as jest.Mock).mockResolvedValue(20);
|
||||
|
||||
(typeRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
typeCode: 'LETTER',
|
||||
});
|
||||
(statusRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
statusCode: 'DRAFT',
|
||||
});
|
||||
|
||||
(numberingService.generateNextNumber as jest.Mock).mockResolvedValue({
|
||||
number: 'DOC-001',
|
||||
});
|
||||
|
||||
mockDataSource.manager.findOne
|
||||
.mockResolvedValueOnce({ id: 10, organizationCode: 'ORG' })
|
||||
.mockResolvedValueOnce({ id: 20, organizationCode: 'REC' });
|
||||
|
||||
const queryRunner = {
|
||||
connect: jest.fn(),
|
||||
startTransaction: jest.fn(),
|
||||
commitTransaction: jest.fn(),
|
||||
rollbackTransaction: jest.fn(),
|
||||
release: jest.fn(),
|
||||
manager: {
|
||||
create: jest.fn(
|
||||
(_entity: unknown, payload: Record<string, unknown>) => payload
|
||||
),
|
||||
save: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 999, publicId: 'corr-uuid' })
|
||||
.mockResolvedValueOnce({ id: 1000 })
|
||||
.mockResolvedValueOnce([]),
|
||||
findOne: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
(mockDataSource.createQueryRunner as jest.Mock).mockReturnValue(
|
||||
queryRunner
|
||||
);
|
||||
|
||||
await service.create(createDto, mockUser);
|
||||
|
||||
expect(queryRunner.manager.create).toHaveBeenCalledWith(
|
||||
CorrespondenceRevision,
|
||||
expect.objectContaining({ revisionLabel: null })
|
||||
);
|
||||
});
|
||||
|
||||
it('should set revisionLabel to null for MEMO type', async () => {
|
||||
const mockUser = {
|
||||
user_id: 1,
|
||||
primaryOrganizationId: 10,
|
||||
} as unknown as User;
|
||||
|
||||
const createDto: CreateCorrespondenceDto = {
|
||||
projectId: 'project-uuid',
|
||||
typeId: 1,
|
||||
subject: 'Test Subject',
|
||||
recipients: [{ organizationId: 'recipient-uuid', type: 'TO' }],
|
||||
};
|
||||
|
||||
const typeRepo = testingModule.get<Repository<CorrespondenceType>>(
|
||||
getRepositoryToken(CorrespondenceType)
|
||||
);
|
||||
const statusRepo = testingModule.get<Repository<CorrespondenceStatus>>(
|
||||
getRepositoryToken(CorrespondenceStatus)
|
||||
);
|
||||
const uuidResolver =
|
||||
testingModule.get<UuidResolverService>(UuidResolverService);
|
||||
|
||||
(uuidResolver.resolveProjectId as jest.Mock).mockResolvedValue(100);
|
||||
(uuidResolver.resolveOrganizationId as jest.Mock).mockResolvedValue(20);
|
||||
|
||||
(typeRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
typeCode: 'MEMO',
|
||||
});
|
||||
(statusRepo.findOne as jest.Mock).mockResolvedValue({
|
||||
id: 1,
|
||||
statusCode: 'DRAFT',
|
||||
});
|
||||
|
||||
(numberingService.generateNextNumber as jest.Mock).mockResolvedValue({
|
||||
number: 'DOC-001',
|
||||
});
|
||||
|
||||
mockDataSource.manager.findOne
|
||||
.mockResolvedValueOnce({ id: 10, organizationCode: 'ORG' })
|
||||
.mockResolvedValueOnce({ id: 20, organizationCode: 'REC' });
|
||||
|
||||
const queryRunner = {
|
||||
connect: jest.fn(),
|
||||
startTransaction: jest.fn(),
|
||||
commitTransaction: jest.fn(),
|
||||
rollbackTransaction: jest.fn(),
|
||||
release: jest.fn(),
|
||||
manager: {
|
||||
create: jest.fn(
|
||||
(_entity: unknown, payload: Record<string, unknown>) => payload
|
||||
),
|
||||
save: jest
|
||||
.fn()
|
||||
.mockResolvedValueOnce({ id: 999, publicId: 'corr-uuid' })
|
||||
.mockResolvedValueOnce({ id: 1000 })
|
||||
.mockResolvedValueOnce([]),
|
||||
findOne: jest.fn(),
|
||||
},
|
||||
};
|
||||
|
||||
(mockDataSource.createQueryRunner as jest.Mock).mockReturnValue(
|
||||
queryRunner
|
||||
);
|
||||
|
||||
await service.create(createDto, mockUser);
|
||||
|
||||
expect(queryRunner.manager.create).toHaveBeenCalledWith(
|
||||
CorrespondenceRevision,
|
||||
expect.objectContaining({ revisionLabel: null })
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -55,6 +55,19 @@ export class CorrespondenceService {
|
||||
return permissions.includes('system.manage_all');
|
||||
}
|
||||
|
||||
/**
|
||||
* Business Rule: Revision Label Strategy
|
||||
* - RFA, RFI: Use alphabet starting with 'A' (A, B, C...)
|
||||
* - Other types (LETTER, MEMO, etc.): Use numeric (null for first, then 1, 2, 3...)
|
||||
*/
|
||||
private getInitialRevisionLabel(typeCode: string): string | null {
|
||||
const alphabetTypes = ['RFA', 'RFI'];
|
||||
if (alphabetTypes.includes(typeCode.toUpperCase())) {
|
||||
return 'A'; // Alphabet for RFA, RFI
|
||||
}
|
||||
return null; // Numeric (null for revision 0)
|
||||
}
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Correspondence)
|
||||
private correspondenceRepo: Repository<Correspondence>,
|
||||
@@ -288,7 +301,7 @@ export class CorrespondenceService {
|
||||
const revision = queryRunner.manager.create(CorrespondenceRevision, {
|
||||
correspondenceId: savedCorr.id,
|
||||
revisionNumber: 0,
|
||||
revisionLabel: 'A',
|
||||
revisionLabel: this.getInitialRevisionLabel(type.typeCode),
|
||||
isCurrent: true,
|
||||
statusId: statusDraft.id,
|
||||
subject: createDto.subject,
|
||||
|
||||
@@ -146,8 +146,14 @@ related:
|
||||
|
||||
- 1 Correspondence Master → หลาย Revision (1:N)
|
||||
- `is_current = TRUE` มีได้เพียง 1 แถวต่อ correspondence (UNIQUE constraint)
|
||||
- `revision_label`: A, B, C, ... (หรือ 1.1, 1.2 แล้วแต่ type)
|
||||
- `revision_number`: 0-based integer สำหรับ sorting
|
||||
- `revision_label`: กำหนดตามประเภทเอกสารดังนี้:
|
||||
| Type | Initial Label | ลำดับถัดไป | Format |
|
||||
|------|--------------|------------|--------|
|
||||
| **RFA, RFI** | `A` | B, C, D... | Alphabet |
|
||||
| **LETTER, MEMO, TRANSMITTAL, EMAIL, INSTRUCTION, NOTICE, MOM, OTHER** | `null` | 1, 2, 3... | Numeric |
|
||||
|
||||
**หมายเหตุ:** Shop Drawing และ As-Built Drawing ใช้ revision label แยกตามกฎของ Drawing Module (numeric '0', '1', '2'... หรือ user กำหนดเอง)
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user