251204:1700 Prepare to version 1.5.1

This commit is contained in:
admin
2025-12-04 16:50:09 +07:00
parent 05f8f4403a
commit dc8b80c5f9
34 changed files with 8518 additions and 3107 deletions
@@ -0,0 +1,70 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UseGuards,
ParseIntPipe,
Query,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiBearerAuth,
ApiQuery,
} from '@nestjs/swagger';
import { ContractService } from './contract.service.js';
import { CreateContractDto } from './dto/create-contract.dto.js';
import { UpdateContractDto } from './dto/update-contract.dto.js';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
import { RequirePermission } from '../../common/decorators/require-permission.decorator.js';
@ApiTags('Contracts')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('contracts')
export class ContractController {
constructor(private readonly contractService: ContractService) {}
@Post()
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Create Contract' })
create(@Body() dto: CreateContractDto) {
return this.contractService.create(dto);
}
@Get()
@ApiOperation({
summary: 'Get All Contracts (Optional: filter by projectId)',
})
@ApiQuery({ name: 'projectId', required: false, type: Number })
findAll(@Query('projectId') projectId?: number) {
return this.contractService.findAll(projectId);
}
@Get(':id')
@ApiOperation({ summary: 'Get Contract by ID' })
findOne(@Param('id', ParseIntPipe) id: number) {
return this.contractService.findOne(id);
}
@Patch(':id')
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Update Contract' })
update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateContractDto
) {
return this.contractService.update(id, dto);
}
@Delete(':id')
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Delete Contract' })
remove(@Param('id', ParseIntPipe) id: number) {
return this.contractService.remove(id);
}
}
@@ -0,0 +1,65 @@
import {
Injectable,
NotFoundException,
ConflictException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Contract } from './entities/contract.entity.js';
import { CreateContractDto } from './dto/create-contract.dto.js';
import { UpdateContractDto } from './dto/update-contract.dto.js';
@Injectable()
export class ContractService {
constructor(
@InjectRepository(Contract)
private readonly contractRepo: Repository<Contract>
) {}
async create(dto: CreateContractDto) {
const existing = await this.contractRepo.findOne({
where: { contractCode: dto.contractCode },
});
if (existing) {
throw new ConflictException(
`Contract Code "${dto.contractCode}" already exists`
);
}
const contract = this.contractRepo.create(dto);
return this.contractRepo.save(contract);
}
async findAll(projectId?: number) {
const query = this.contractRepo
.createQueryBuilder('c')
.leftJoinAndSelect('c.project', 'p')
.orderBy('c.contractCode', 'ASC');
if (projectId) {
query.where('c.projectId = :projectId', { projectId });
}
return query.getMany();
}
async findOne(id: number) {
const contract = await this.contractRepo.findOne({
where: { id },
relations: ['project'],
});
if (!contract) throw new NotFoundException(`Contract ID ${id} not found`);
return contract;
}
async update(id: number, dto: UpdateContractDto) {
const contract = await this.findOne(id);
Object.assign(contract, dto);
return this.contractRepo.save(contract);
}
async remove(id: number) {
const contract = await this.findOne(id);
// Schema doesn't have deleted_at for Contract either.
return this.contractRepo.remove(contract);
}
}
@@ -0,0 +1,49 @@
import {
IsString,
IsNotEmpty,
IsBoolean,
IsOptional,
Length,
IsInt,
IsDateString,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateContractDto {
@ApiProperty({ example: 1 })
@IsInt()
@IsNotEmpty()
projectId!: number;
@ApiProperty({ example: 'C-001' })
@IsString()
@IsNotEmpty()
@Length(1, 50)
contractCode!: string;
@ApiProperty({ example: 'Main Construction Contract' })
@IsString()
@IsNotEmpty()
@Length(1, 255)
contractName!: string;
@ApiProperty({ example: 'Description of the contract', required: false })
@IsOptional()
@IsString()
description?: string;
@ApiProperty({ example: '2024-01-01', required: false })
@IsOptional()
@IsDateString()
startDate?: string; // Receive as string, TypeORM handles date
@ApiProperty({ example: '2025-12-31', required: false })
@IsOptional()
@IsDateString()
endDate?: string;
@ApiProperty({ example: true, required: false })
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
@@ -0,0 +1,27 @@
import {
IsString,
IsNotEmpty,
IsBoolean,
IsOptional,
Length,
} from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class CreateOrganizationDto {
@ApiProperty({ example: 'ITD' })
@IsString()
@IsNotEmpty()
@Length(1, 20)
organizationCode!: string;
@ApiProperty({ example: 'Italian-Thai Development' })
@IsString()
@IsNotEmpty()
@Length(1, 255)
organizationName!: string;
@ApiProperty({ example: true, required: false })
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateContractDto } from './create-contract.dto.js';
export class UpdateContractDto extends PartialType(CreateContractDto) {}
@@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateOrganizationDto } from './create-organization.dto.js';
export class UpdateOrganizationDto extends PartialType(CreateOrganizationDto) {}
@@ -0,0 +1,61 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
UseGuards,
ParseIntPipe,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
import { OrganizationService } from './organization.service.js';
import { CreateOrganizationDto } from './dto/create-organization.dto.js';
import { UpdateOrganizationDto } from './dto/update-organization.dto.js';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard.js';
import { RequirePermission } from '../../common/decorators/require-permission.decorator.js';
@ApiTags('Organizations')
@ApiBearerAuth()
@UseGuards(JwtAuthGuard)
@Controller('organizations')
export class OrganizationController {
constructor(private readonly orgService: OrganizationService) {}
@Post()
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Create Organization' })
create(@Body() dto: CreateOrganizationDto) {
return this.orgService.create(dto);
}
@Get()
@ApiOperation({ summary: 'Get All Organizations' })
findAll() {
return this.orgService.findAll();
}
@Get(':id')
@ApiOperation({ summary: 'Get Organization by ID' })
findOne(@Param('id', ParseIntPipe) id: number) {
return this.orgService.findOne(id);
}
@Patch(':id')
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Update Organization' })
update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateOrganizationDto
) {
return this.orgService.update(id, dto);
}
@Delete(':id')
@RequirePermission('master_data.manage')
@ApiOperation({ summary: 'Delete Organization' })
remove(@Param('id', ParseIntPipe) id: number) {
return this.orgService.remove(id);
}
}
@@ -0,0 +1,57 @@
import {
Injectable,
NotFoundException,
ConflictException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Organization } from './entities/organization.entity.js';
import { CreateOrganizationDto } from './dto/create-organization.dto.js';
import { UpdateOrganizationDto } from './dto/update-organization.dto.js';
@Injectable()
export class OrganizationService {
constructor(
@InjectRepository(Organization)
private readonly orgRepo: Repository<Organization>
) {}
async create(dto: CreateOrganizationDto) {
const existing = await this.orgRepo.findOne({
where: { organizationCode: dto.organizationCode },
});
if (existing) {
throw new ConflictException(
`Organization Code "${dto.organizationCode}" already exists`
);
}
const org = this.orgRepo.create(dto);
return this.orgRepo.save(org);
}
async findAll() {
return this.orgRepo.find({
order: { organizationCode: 'ASC' },
});
}
async findOne(id: number) {
const org = await this.orgRepo.findOne({ where: { id } });
if (!org) throw new NotFoundException(`Organization ID ${id} not found`);
return org;
}
async update(id: number, dto: UpdateOrganizationDto) {
const org = await this.findOne(id);
Object.assign(org, dto);
return this.orgRepo.save(org);
}
async remove(id: number) {
const org = await this.findOne(id);
// Hard delete or Soft delete? Schema doesn't have deleted_at for Organization, but let's check.
// Schema says: created_at, updated_at. No deleted_at.
// So hard delete.
return this.orgRepo.remove(org);
}
}
+15 -9
View File
@@ -2,26 +2,32 @@ import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProjectService } from './project.service.js';
import { ProjectController } from './project.controller.js';
import { OrganizationService } from './organization.service.js';
import { OrganizationController } from './organization.controller.js';
import { ContractService } from './contract.service.js';
import { ContractController } from './contract.controller.js';
import { Project } from './entities/project.entity.js';
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'; // เพิ่ม
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
import { UserModule } from '../user/user.module';
@Module({
imports: [
TypeOrmModule.forFeature([
Project,
Organization,
Contract,
ProjectOrganization, // ลงทะเบียน
ContractOrganization, // ลงทะเบียน
ProjectOrganization,
ContractOrganization,
]),
UserModule, // ✅ 2. เพิ่ม UserModule เข้าไปใน imports
UserModule,
],
controllers: [ProjectController],
providers: [ProjectService],
exports: [ProjectService], // Export เผื่อ Module อื่นใช้
controllers: [ProjectController, OrganizationController, ContractController],
providers: [ProjectService, OrganizationService, ContractService],
exports: [ProjectService, OrganizationService, ContractService],
})
export class ProjectModule {}