This commit is contained in:
+20
-3
@@ -88,14 +88,31 @@ export class DocumentNumberingController {
|
||||
})
|
||||
@RequirePermission('correspondence.read')
|
||||
async previewNumber(@Body() dto: PreviewNumberDto) {
|
||||
// ADR-019: Resolve UUID→INT for project and organization IDs
|
||||
const resolvedProjectId = await this.numberingService.resolveIdForPreview(
|
||||
'project',
|
||||
dto.projectId
|
||||
);
|
||||
const resolvedOriginatorId =
|
||||
await this.numberingService.resolveIdForPreview(
|
||||
'organization',
|
||||
dto.originatorOrganizationId
|
||||
);
|
||||
const resolvedRecipientId = dto.recipientOrganizationId
|
||||
? await this.numberingService.resolveIdForPreview(
|
||||
'organization',
|
||||
dto.recipientOrganizationId
|
||||
)
|
||||
: undefined;
|
||||
|
||||
return this.numberingService.previewNumber({
|
||||
projectId: dto.projectId,
|
||||
originatorOrganizationId: dto.originatorOrganizationId,
|
||||
projectId: resolvedProjectId,
|
||||
originatorOrganizationId: resolvedOriginatorId,
|
||||
typeId: dto.correspondenceTypeId,
|
||||
subTypeId: dto.subTypeId,
|
||||
rfaTypeId: dto.rfaTypeId,
|
||||
disciplineId: dto.disciplineId,
|
||||
recipientOrganizationId: dto.recipientOrganizationId,
|
||||
recipientOrganizationId: resolvedRecipientId,
|
||||
year: dto.year,
|
||||
customTokens: dto.customTokens,
|
||||
});
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
// File: src/modules/document-numbering/dto/preview-number.dto.ts
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { IsInt, IsOptional, IsObject } from 'class-validator';
|
||||
import { IsInt, IsNotEmpty, IsOptional, IsObject } from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class PreviewNumberDto {
|
||||
@ApiProperty({ description: 'Project ID' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
projectId!: number;
|
||||
@ApiProperty({ description: 'Project ID or UUID' })
|
||||
@IsNotEmpty()
|
||||
projectId!: number | string;
|
||||
|
||||
@ApiProperty({ description: 'Originator organization ID' })
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
originatorOrganizationId!: number;
|
||||
@ApiProperty({ description: 'Originator organization ID or UUID' })
|
||||
@IsNotEmpty()
|
||||
originatorOrganizationId!: number | string;
|
||||
|
||||
@ApiProperty({ description: 'Correspondence type ID' })
|
||||
@IsInt()
|
||||
@@ -43,11 +41,9 @@ export class PreviewNumberDto {
|
||||
@Type(() => Number)
|
||||
year?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Recipient organization ID' })
|
||||
@ApiPropertyOptional({ description: 'Recipient organization ID or UUID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
recipientOrganizationId?: number;
|
||||
recipientOrganizationId?: number | string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Custom tokens' })
|
||||
@IsOptional()
|
||||
|
||||
@@ -26,6 +26,7 @@ import { GenerateNumberContext } from '../interfaces/document-numbering.interfac
|
||||
import { ReserveNumberDto } from '../dto/reserve-number.dto';
|
||||
import { ConfirmReservationDto } from '../dto/confirm-reservation.dto';
|
||||
import { Project } from '../../project/entities/project.entity';
|
||||
import { Organization } from '../../organization/entities/organization.entity';
|
||||
|
||||
@Injectable()
|
||||
export class DocumentNumberingService {
|
||||
@@ -66,6 +67,33 @@ export class DocumentNumberingService {
|
||||
return project.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* ADR-019: Public facade for controllers to resolve project/organization IDs
|
||||
*/
|
||||
async resolveIdForPreview(
|
||||
type: 'project' | 'organization',
|
||||
id: number | string
|
||||
): Promise<number> {
|
||||
if (type === 'project') return this.resolveProjectId(id);
|
||||
return this.resolveOrganizationId(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveOrganizationId(orgId: number | string): Promise<number> {
|
||||
if (typeof orgId === 'number') return orgId;
|
||||
const num = Number(orgId);
|
||||
if (!isNaN(num)) return num;
|
||||
const org = await this.entityManager.findOne(Organization, {
|
||||
where: { uuid: orgId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!org)
|
||||
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
|
||||
return org.id;
|
||||
}
|
||||
|
||||
async generateNextNumber(
|
||||
ctx: GenerateNumberContext
|
||||
): Promise<{ number: string; auditId: number }> {
|
||||
|
||||
@@ -13,6 +13,7 @@ import { AsBuiltDrawingRevision } from './entities/asbuilt-drawing-revision.enti
|
||||
import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
|
||||
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
|
||||
// DTOs
|
||||
import { CreateAsBuiltDrawingDto } from './dto/create-asbuilt-drawing.dto';
|
||||
@@ -39,6 +40,22 @@ export class AsBuiltDrawingService {
|
||||
private dataSource: DataSource
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveProjectId(projectId: number | string): Promise<number> {
|
||||
if (typeof projectId === 'number') return projectId;
|
||||
const num = Number(projectId);
|
||||
if (!isNaN(num)) return num;
|
||||
const project = await this.dataSource.manager.findOne(Project, {
|
||||
where: { uuid: projectId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!project)
|
||||
throw new NotFoundException(`Project with UUID ${projectId} not found`);
|
||||
return project.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง AS Built Drawing ใหม่ พร้อม Revision แรก (Rev 0)
|
||||
*/
|
||||
@@ -73,9 +90,14 @@ export class AsBuiltDrawingService {
|
||||
});
|
||||
}
|
||||
|
||||
// ADR-019: Resolve UUID→INT
|
||||
const internalProjectId = await this.resolveProjectId(
|
||||
createDto.projectId
|
||||
);
|
||||
|
||||
// 3. Create Master AS Built Drawing
|
||||
const asBuiltDrawing = queryRunner.manager.create(AsBuiltDrawing, {
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
drawingNumber: createDto.drawingNumber,
|
||||
mainCategoryId: createDto.mainCategoryId,
|
||||
subCategoryId: createDto.subCategoryId,
|
||||
|
||||
@@ -12,6 +12,7 @@ import { ContractDrawing } from './entities/contract-drawing.entity';
|
||||
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
import { Contract } from '../contract/entities/contract.entity';
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
|
||||
// DTOs
|
||||
import { CreateContractDrawingDto } from './dto/create-contract-drawing.dto';
|
||||
@@ -36,6 +37,22 @@ export class ContractDrawingService {
|
||||
private dataSource: DataSource
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveProjectId(projectId: number | string): Promise<number> {
|
||||
if (typeof projectId === 'number') return projectId;
|
||||
const num = Number(projectId);
|
||||
if (!isNaN(num)) return num;
|
||||
const project = await this.dataSource.manager.findOne(Project, {
|
||||
where: { uuid: projectId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!project)
|
||||
throw new NotFoundException(`Project with UUID ${projectId} not found`);
|
||||
return project.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve issueDate from contract.startDate for file storage path
|
||||
* Fallback: contract.startDate → current date
|
||||
@@ -54,10 +71,13 @@ export class ContractDrawingService {
|
||||
* - ผูกไฟล์แนบและ Commit ไฟล์จาก Temp -> Permanent
|
||||
*/
|
||||
async create(createDto: CreateContractDrawingDto, user: User) {
|
||||
// ADR-019: Resolve UUID→INT for projectId
|
||||
const internalProjectId = await this.resolveProjectId(createDto.projectId);
|
||||
|
||||
// 1. ตรวจสอบเลขที่แบบซ้ำ (Unique per Project)
|
||||
const exists = await this.drawingRepo.findOne({
|
||||
where: {
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
contractDrawingNo: createDto.contractDrawingNo,
|
||||
},
|
||||
});
|
||||
@@ -83,7 +103,7 @@ export class ContractDrawingService {
|
||||
|
||||
// 3. สร้าง Entity
|
||||
const drawing = queryRunner.manager.create(ContractDrawing, {
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
contractDrawingNo: createDto.contractDrawingNo,
|
||||
title: createDto.title,
|
||||
mapCatId: createDto.mapCatId, // Updated
|
||||
@@ -98,9 +118,8 @@ export class ContractDrawingService {
|
||||
// 4. Commit Files (ย้ายไฟล์จริง)
|
||||
if (createDto.attachmentIds?.length) {
|
||||
// ✅ FIX TS2345: แปลง number[] เป็น string[] ก่อนส่ง
|
||||
const issueDate = await this.resolveIssueDateByProject(
|
||||
createDto.projectId
|
||||
);
|
||||
const issueDate =
|
||||
await this.resolveIssueDateByProject(internalProjectId);
|
||||
await this.fileStorageService.commit(
|
||||
createDto.attachmentIds.map(String),
|
||||
{ issueDate, documentType: 'ContractDrawing' }
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ShopDrawingRevision } from './entities/shop-drawing-revision.entity';
|
||||
import { ContractDrawing } from './entities/contract-drawing.entity';
|
||||
import { Attachment } from '../../common/file-storage/entities/attachment.entity';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
|
||||
// DTOs
|
||||
import { CreateShopDrawingDto } from './dto/create-shop-drawing.dto';
|
||||
@@ -39,6 +40,22 @@ export class ShopDrawingService {
|
||||
private dataSource: DataSource
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveProjectId(projectId: number | string): Promise<number> {
|
||||
if (typeof projectId === 'number') return projectId;
|
||||
const num = Number(projectId);
|
||||
if (!isNaN(num)) return num;
|
||||
const project = await this.dataSource.manager.findOne(Project, {
|
||||
where: { uuid: projectId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!project)
|
||||
throw new NotFoundException(`Project with UUID ${projectId} not found`);
|
||||
return project.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง Shop Drawing ใหม่ พร้อม Revision แรก (Rev 0)
|
||||
*/
|
||||
@@ -73,9 +90,14 @@ export class ShopDrawingService {
|
||||
});
|
||||
}
|
||||
|
||||
// ADR-019: Resolve UUID→INT
|
||||
const internalProjectId = await this.resolveProjectId(
|
||||
createDto.projectId
|
||||
);
|
||||
|
||||
// 3. Create Master Shop Drawing
|
||||
const shopDrawing = queryRunner.manager.create(ShopDrawing, {
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
drawingNumber: createDto.drawingNumber,
|
||||
mainCategoryId: createDto.mainCategoryId,
|
||||
subCategoryId: createDto.subCategoryId,
|
||||
|
||||
@@ -11,10 +11,18 @@ import {
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateRfaDto {
|
||||
@ApiProperty({ description: 'ID ของโครงการ', example: 1 })
|
||||
@IsInt()
|
||||
@ApiProperty({ description: 'ID or UUID ของโครงการ', example: 1 })
|
||||
@IsNotEmpty()
|
||||
projectId!: number;
|
||||
projectId!: number | string;
|
||||
|
||||
@ApiProperty({ description: 'Contract ID or UUID', required: false })
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
contractId?: string;
|
||||
|
||||
@ApiProperty({ description: 'To Organization ID or UUID', required: false })
|
||||
@IsOptional()
|
||||
toOrganizationId?: number | string;
|
||||
|
||||
@ApiProperty({ description: 'ID ของประเภท RFA', example: 1 })
|
||||
@IsInt()
|
||||
|
||||
@@ -12,6 +12,8 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { DataSource, In, Repository } from 'typeorm';
|
||||
|
||||
// Entities
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
import { Organization } from '../organization/entities/organization.entity';
|
||||
import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity';
|
||||
import { Correspondence } from '../correspondence/entities/correspondence.entity';
|
||||
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
|
||||
@@ -81,7 +83,42 @@ export class RfaService {
|
||||
private searchService: SearchService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveProjectId(projectId: number | string): Promise<number> {
|
||||
if (typeof projectId === 'number') return projectId;
|
||||
const num = Number(projectId);
|
||||
if (!isNaN(num)) return num;
|
||||
const project = await this.dataSource.manager.findOne(Project, {
|
||||
where: { uuid: projectId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!project)
|
||||
throw new NotFoundException(`Project with UUID ${projectId} not found`);
|
||||
return project.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveOrganizationId(orgId: number | string): Promise<number> {
|
||||
if (typeof orgId === 'number') return orgId;
|
||||
const num = Number(orgId);
|
||||
if (!isNaN(num)) return num;
|
||||
const org = await this.dataSource.manager.findOne(Organization, {
|
||||
where: { uuid: orgId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!org)
|
||||
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
|
||||
return org.id;
|
||||
}
|
||||
|
||||
async create(createDto: CreateRfaDto, user: User) {
|
||||
// ADR-019: Resolve UUID→INT for projectId
|
||||
const internalProjectId = await this.resolveProjectId(createDto.projectId);
|
||||
|
||||
const rfaType = await this.rfaTypeRepo.findOne({
|
||||
where: { id: createDto.rfaTypeId },
|
||||
});
|
||||
@@ -115,7 +152,7 @@ export class RfaService {
|
||||
|
||||
// [UPDATED] Generate Document Number with Discipline
|
||||
const docNumber = await this.numberingService.generateNextNumber({
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
originatorOrganizationId: userOrgId,
|
||||
typeId: createDto.rfaTypeId,
|
||||
disciplineId: createDto.disciplineId ?? 0, // ✅ ส่ง disciplineId ไปด้วย (0 ถ้าไม่มี)
|
||||
@@ -142,7 +179,7 @@ export class RfaService {
|
||||
const correspondence = queryRunner.manager.create(Correspondence, {
|
||||
correspondenceNumber: docNumber.number,
|
||||
correspondenceTypeId: createDto.rfaTypeId,
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
originatorId: userOrgId,
|
||||
isInternal: false,
|
||||
createdBy: user.user_id,
|
||||
@@ -219,7 +256,7 @@ export class RfaService {
|
||||
'rfa',
|
||||
savedRfa.id.toString(),
|
||||
{
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
originatorId: userOrgId,
|
||||
disciplineId: createDto.disciplineId,
|
||||
initiatorId: user.user_id,
|
||||
@@ -240,7 +277,7 @@ export class RfaService {
|
||||
title: createDto.subject,
|
||||
description: createDto.description,
|
||||
status: 'DRAFT',
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
createdAt: new Date(),
|
||||
})
|
||||
.catch((err) => this.logger.error(`Indexing failed: ${err}`));
|
||||
|
||||
@@ -16,6 +16,7 @@ import { Correspondence } from '../correspondence/entities/correspondence.entity
|
||||
import { CorrespondenceRevision } from '../correspondence/entities/correspondence-revision.entity';
|
||||
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
|
||||
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
|
||||
import { Project } from '../project/entities/project.entity';
|
||||
|
||||
@Injectable()
|
||||
export class TransmittalService {
|
||||
@@ -34,6 +35,22 @@ export class TransmittalService {
|
||||
private dataSource: DataSource
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve projectId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveProjectId(projectId: number | string): Promise<number> {
|
||||
if (typeof projectId === 'number') return projectId;
|
||||
const num = Number(projectId);
|
||||
if (!isNaN(num)) return num;
|
||||
const project = await this.dataSource.manager.findOne(Project, {
|
||||
where: { uuid: projectId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!project)
|
||||
throw new NotFoundException(`Project with UUID ${projectId} not found`);
|
||||
return project.id;
|
||||
}
|
||||
|
||||
async create(createDto: CreateTransmittalDto, user: User) {
|
||||
// 1. Get Transmittal Type (Assuming Code '901' or 'TRN')
|
||||
const type = await this.typeRepo.findOne({
|
||||
@@ -58,9 +75,14 @@ export class TransmittalService {
|
||||
}
|
||||
|
||||
try {
|
||||
// ADR-019: Resolve UUID→INT for projectId
|
||||
const internalProjectId = await this.resolveProjectId(
|
||||
createDto.projectId
|
||||
);
|
||||
|
||||
// 2. Generate Number
|
||||
const docNumber = await this.numberingService.generateNextNumber({
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
originatorOrganizationId: user.primaryOrganizationId,
|
||||
typeId: type.id,
|
||||
year: new Date().getFullYear(),
|
||||
@@ -74,7 +96,7 @@ export class TransmittalService {
|
||||
const correspondence = queryRunner.manager.create(Correspondence, {
|
||||
correspondenceNumber: docNumber.number,
|
||||
correspondenceTypeId: type.id,
|
||||
projectId: createDto.projectId,
|
||||
projectId: internalProjectId,
|
||||
originatorId: user.primaryOrganizationId,
|
||||
isInternal: false,
|
||||
createdBy: user.user_id,
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
MinLength,
|
||||
IsOptional,
|
||||
IsBoolean,
|
||||
IsInt,
|
||||
} from 'class-validator';
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
|
||||
@@ -44,10 +43,12 @@ export class CreateUserDto {
|
||||
@IsOptional()
|
||||
lineId?: string;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Primary Organization ID', example: 1 })
|
||||
@IsInt()
|
||||
@ApiPropertyOptional({
|
||||
description: 'Primary Organization ID or UUID',
|
||||
example: 1,
|
||||
})
|
||||
@IsOptional()
|
||||
primaryOrganizationId?: number; // รับเป็น ID ของ Organization
|
||||
primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
|
||||
|
||||
@ApiPropertyOptional({ description: 'Is user active?', default: true })
|
||||
@IsBoolean()
|
||||
|
||||
@@ -16,11 +16,9 @@ export class SearchUserDto {
|
||||
@Type(() => Number)
|
||||
roleId?: number;
|
||||
|
||||
@ApiPropertyOptional({ description: 'Filter by Organization ID' })
|
||||
@ApiPropertyOptional({ description: 'Filter by Organization ID or UUID' })
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
primaryOrganizationId?: number;
|
||||
primaryOrganizationId?: number | string; // ADR-019: Accept INT or UUID
|
||||
|
||||
@ApiPropertyOptional({ description: 'Page number', default: 1 })
|
||||
@IsOptional()
|
||||
|
||||
@@ -17,6 +17,7 @@ import { Role } from './entities/role.entity';
|
||||
import { Permission } from './entities/permission.entity';
|
||||
import { CreateUserDto } from './dto/create-user.dto';
|
||||
import { UpdateUserDto } from './dto/update-user.dto';
|
||||
import { Organization } from '../organization/entities/organization.entity';
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
@@ -30,13 +31,35 @@ export class UserService {
|
||||
@Inject(CACHE_MANAGER) private cacheManager: Cache
|
||||
) {}
|
||||
|
||||
/**
|
||||
* ADR-019: Resolve organizationId (INT or UUID string) to internal INT ID
|
||||
*/
|
||||
private async resolveOrganizationId(orgId: number | string): Promise<number> {
|
||||
if (typeof orgId === 'number') return orgId;
|
||||
const num = Number(orgId);
|
||||
if (!isNaN(num)) return num;
|
||||
const org = await this.usersRepository.manager.findOne(Organization, {
|
||||
where: { uuid: orgId },
|
||||
select: ['id'],
|
||||
});
|
||||
if (!org)
|
||||
throw new NotFoundException(`Organization with UUID ${orgId} not found`);
|
||||
return org.id;
|
||||
}
|
||||
|
||||
// 1. สร้างผู้ใช้ (Hash Password ก่อนบันทึก)
|
||||
async create(createUserDto: CreateUserDto): Promise<User> {
|
||||
const salt = await bcrypt.genSalt();
|
||||
const hashedPassword = await bcrypt.hash(createUserDto.password, salt);
|
||||
|
||||
// ADR-019: Resolve UUID→INT for primaryOrganizationId
|
||||
const resolvedOrgId = createUserDto.primaryOrganizationId
|
||||
? await this.resolveOrganizationId(createUserDto.primaryOrganizationId)
|
||||
: undefined;
|
||||
|
||||
const newUser = this.usersRepository.create({
|
||||
...createUserDto,
|
||||
primaryOrganizationId: resolvedOrgId,
|
||||
password: hashedPassword,
|
||||
});
|
||||
|
||||
@@ -91,8 +114,12 @@ export class UserService {
|
||||
}
|
||||
|
||||
if (primaryOrganizationId) {
|
||||
// ADR-019: Resolve UUID→INT for filtering
|
||||
const resolvedOrgId = await this.resolveOrganizationId(
|
||||
primaryOrganizationId
|
||||
);
|
||||
query.andWhere('user.primaryOrganizationId = :orgId', {
|
||||
orgId: primaryOrganizationId,
|
||||
orgId: resolvedOrgId,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -164,7 +191,18 @@ export class UserService {
|
||||
updateUserDto.password = await bcrypt.hash(updateUserDto.password, salt);
|
||||
}
|
||||
|
||||
const updatedUser = this.usersRepository.merge(user, updateUserDto);
|
||||
// ADR-019: Resolve UUID→INT for primaryOrganizationId before merge
|
||||
const resolvedDto: Record<string, unknown> = { ...updateUserDto };
|
||||
if (updateUserDto.primaryOrganizationId !== undefined) {
|
||||
resolvedDto.primaryOrganizationId = await this.resolveOrganizationId(
|
||||
updateUserDto.primaryOrganizationId
|
||||
);
|
||||
}
|
||||
|
||||
const updatedUser = this.usersRepository.merge(
|
||||
user,
|
||||
resolvedDto as Partial<User>
|
||||
);
|
||||
const savedUser = await this.usersRepository.save(updatedUser);
|
||||
|
||||
// ⚠️ สำคัญ: เมื่อมีการแก้ไขข้อมูล User ต้องเคลียร์ Cache สิทธิ์เสมอ
|
||||
|
||||
Reference in New Issue
Block a user