251123:2300 Update T1
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
import { IsString, IsNotEmpty, IsOptional, IsBoolean } from 'class-validator';
|
||||
|
||||
export class CreateProjectDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
projectCode!: string; // รหัสโครงการ (เช่น LCBP3)
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
projectName!: string; // ชื่อโครงการ
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean; // สถานะการใช้งาน (Default: true)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { IsString, IsOptional, IsInt, IsBoolean } from 'class-validator';
|
||||
import { Type, Transform } from 'class-transformer';
|
||||
|
||||
export class SearchProjectDto {
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
search?: string; // ค้นหาจาก Project Code หรือ Name
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
@Transform(({ value }) => {
|
||||
if (value === 'true') return true;
|
||||
if (value === 'false') return false;
|
||||
return value;
|
||||
})
|
||||
isActive?: boolean; // กรองตามสถานะ Active
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
page: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@IsInt()
|
||||
@Type(() => Number)
|
||||
limit: number = 20;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateProjectDto } from './create-project.dto';
|
||||
|
||||
export class UpdateProjectDto extends PartialType(CreateProjectDto) {}
|
||||
@@ -1,4 +1,75 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Post,
|
||||
Body,
|
||||
Patch,
|
||||
Param,
|
||||
Delete,
|
||||
Query,
|
||||
UseGuards,
|
||||
ParseIntPipe,
|
||||
} from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
|
||||
@Controller('project')
|
||||
export class ProjectController {}
|
||||
import { ProjectService } from './project.service.js';
|
||||
import { CreateProjectDto } from './dto/create-project.dto.js';
|
||||
import { UpdateProjectDto } from './dto/update-project.dto.js';
|
||||
import { SearchProjectDto } from './dto/search-project.dto.js';
|
||||
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard.js';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator.js';
|
||||
|
||||
@ApiTags('Projects')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, RbacGuard)
|
||||
@Controller('projects') // แนะนำให้ใช้ plural noun (projects)
|
||||
export class ProjectController {
|
||||
constructor(private readonly projectService: ProjectService) {}
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create new Project' })
|
||||
@RequirePermission('project.create')
|
||||
create(@Body() createDto: CreateProjectDto) {
|
||||
return this.projectService.create(createDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'Search Projects' })
|
||||
@RequirePermission('project.view')
|
||||
findAll(@Query() searchDto: SearchProjectDto) {
|
||||
return this.projectService.findAll(searchDto);
|
||||
}
|
||||
|
||||
@Get('organizations')
|
||||
@ApiOperation({ summary: 'List All Organizations (Master Data)' })
|
||||
// @RequirePermission('organization.view') // หรือเปิดให้ดูได้ทั่วไปถ้าจำเป็น
|
||||
findAllOrgs() {
|
||||
return this.projectService.findAllOrganizations();
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get Project Details' })
|
||||
@RequirePermission('project.view')
|
||||
findOne(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.projectService.findOne(id);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({ summary: 'Update Project' })
|
||||
@RequirePermission('project.edit')
|
||||
update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateDto: UpdateProjectDto,
|
||||
) {
|
||||
return this.projectService.update(id, updateDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete Project (Soft Delete)' })
|
||||
@RequirePermission('project.delete')
|
||||
remove(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.projectService.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,8 @@ import { Organization } from './entities/organization.entity.js';
|
||||
import { Contract } from './entities/contract.entity.js';
|
||||
import { ProjectOrganization } from './entities/project-organization.entity.js'; // เพิ่ม
|
||||
import { ContractOrganization } from './entities/contract-organization.entity.js'; // เพิ่ม
|
||||
|
||||
// Modules
|
||||
import { UserModule } from '../user/user.module'; // ✅ 1. Import UserModule
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
@@ -17,6 +18,7 @@ import { ContractOrganization } from './entities/contract-organization.entity.js
|
||||
ProjectOrganization, // ลงทะเบียน
|
||||
ContractOrganization, // ลงทะเบียน
|
||||
]),
|
||||
UserModule, // ✅ 2. เพิ่ม UserModule เข้าไปใน imports
|
||||
],
|
||||
controllers: [ProjectController],
|
||||
providers: [ProjectService],
|
||||
|
||||
@@ -1,11 +1,25 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
ConflictException,
|
||||
Logger,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Repository, Like } from 'typeorm';
|
||||
|
||||
// Entities
|
||||
import { Project } from './entities/project.entity.js';
|
||||
import { Organization } from './entities/organization.entity.js';
|
||||
|
||||
// DTOs
|
||||
import { CreateProjectDto } from './dto/create-project.dto.js';
|
||||
import { UpdateProjectDto } from './dto/update-project.dto.js';
|
||||
import { SearchProjectDto } from './dto/search-project.dto.js';
|
||||
|
||||
@Injectable()
|
||||
export class ProjectService {
|
||||
private readonly logger = new Logger(ProjectService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(Project)
|
||||
private projectRepository: Repository<Project>,
|
||||
@@ -13,13 +27,92 @@ export class ProjectService {
|
||||
private organizationRepository: Repository<Organization>,
|
||||
) {}
|
||||
|
||||
// ดึงรายการ Project ทั้งหมด
|
||||
async findAllProjects() {
|
||||
return this.projectRepository.find();
|
||||
// --- CRUD Operations ---
|
||||
|
||||
async create(createDto: CreateProjectDto) {
|
||||
// 1. เช็คชื่อ/รหัสซ้ำ (ถ้าจำเป็น)
|
||||
const existing = await this.projectRepository.findOne({
|
||||
where: { projectCode: createDto.projectCode },
|
||||
});
|
||||
if (existing) {
|
||||
throw new ConflictException(
|
||||
`Project Code "${createDto.projectCode}" already exists`,
|
||||
);
|
||||
}
|
||||
|
||||
// 2. สร้าง Project
|
||||
const project = this.projectRepository.create(createDto);
|
||||
return this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
// ดึงรายการ Organization ทั้งหมด
|
||||
async findAll(searchDto: SearchProjectDto) {
|
||||
const { search, isActive, page = 1, limit = 20 } = searchDto;
|
||||
const skip = (page - 1) * limit;
|
||||
|
||||
// สร้าง Query Builder
|
||||
const query = this.projectRepository.createQueryBuilder('project');
|
||||
|
||||
if (isActive !== undefined) {
|
||||
query.andWhere('project.isActive = :isActive', { isActive });
|
||||
}
|
||||
|
||||
if (search) {
|
||||
query.andWhere(
|
||||
'(project.projectCode LIKE :search OR project.projectName LIKE :search)',
|
||||
{ search: `%${search}%` },
|
||||
);
|
||||
}
|
||||
|
||||
query.orderBy('project.created_at', 'DESC');
|
||||
query.skip(skip).take(limit);
|
||||
|
||||
const [items, total] = await query.getManyAndCount();
|
||||
|
||||
return {
|
||||
data: items,
|
||||
meta: {
|
||||
total,
|
||||
page,
|
||||
limit,
|
||||
totalPages: Math.ceil(total / limit),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async findOne(id: number) {
|
||||
const project = await this.projectRepository.findOne({
|
||||
where: { id },
|
||||
relations: ['contracts'], // ดึงสัญญาที่เกี่ยวข้องมาด้วย
|
||||
});
|
||||
|
||||
if (!project) {
|
||||
throw new NotFoundException(`Project ID ${id} not found`);
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
async update(id: number, updateDto: UpdateProjectDto) {
|
||||
const project = await this.findOne(id);
|
||||
|
||||
// Merge ข้อมูลใหม่ใส่ข้อมูลเดิม
|
||||
this.projectRepository.merge(project, updateDto);
|
||||
|
||||
return this.projectRepository.save(project);
|
||||
}
|
||||
|
||||
async remove(id: number) {
|
||||
const project = await this.findOne(id);
|
||||
// ใช้ Soft Delete
|
||||
return this.projectRepository.softRemove(project);
|
||||
}
|
||||
|
||||
// --- Organization Helper ---
|
||||
|
||||
async findAllOrganizations() {
|
||||
return this.organizationRepository.find();
|
||||
return this.organizationRepository.find({
|
||||
where: { isActive: true },
|
||||
order: { organizationCode: 'ASC' },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user