251124:1702 Ready to Phase 7
This commit is contained in:
@@ -1,9 +1,22 @@
|
|||||||
// File: src/modules/master/master.controller.ts
|
// File: src/modules/master/master.controller.ts
|
||||||
|
|
||||||
import { Controller, Get, Post, Body, UseGuards } from '@nestjs/common';
|
import {
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
Post,
|
||||||
|
Body,
|
||||||
|
Patch,
|
||||||
|
Param,
|
||||||
|
Delete,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
|
ParseIntPipe,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||||
import { MasterService } from './master.service';
|
import { MasterService } from './master.service';
|
||||||
import { CreateTagDto } from './dto/create-tag.dto';
|
import { CreateTagDto } from './dto/create-tag.dto';
|
||||||
|
import { UpdateTagDto } from './dto/update-tag.dto';
|
||||||
|
import { SearchTagDto } from './dto/search-tag.dto';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||||
|
|
||||||
@@ -13,6 +26,10 @@ import { RequirePermission } from '../../common/decorators/require-permission.de
|
|||||||
export class MasterController {
|
export class MasterController {
|
||||||
constructor(private readonly masterService: MasterService) {}
|
constructor(private readonly masterService: MasterService) {}
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// 📦 Dropdowns Endpoints (Read-Only for Frontend)
|
||||||
|
// =================================================================
|
||||||
|
|
||||||
@Get('correspondence-types')
|
@Get('correspondence-types')
|
||||||
@ApiOperation({ summary: 'Get all active correspondence types' })
|
@ApiOperation({ summary: 'Get all active correspondence types' })
|
||||||
getCorrespondenceTypes() {
|
getCorrespondenceTypes() {
|
||||||
@@ -49,16 +66,40 @@ export class MasterController {
|
|||||||
return this.masterService.findAllCirculationStatuses();
|
return this.masterService.findAllCirculationStatuses();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =================================================================
|
||||||
|
// 🏷️ Tag Management Endpoints
|
||||||
|
// =================================================================
|
||||||
|
|
||||||
@Get('tags')
|
@Get('tags')
|
||||||
@ApiOperation({ summary: 'Get all tags' })
|
@ApiOperation({ summary: 'Get all tags (supports search & pagination)' })
|
||||||
getTags() {
|
getTags(@Query() query: SearchTagDto) {
|
||||||
return this.masterService.findAllTags();
|
return this.masterService.findAllTags(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('tags/:id')
|
||||||
|
@ApiOperation({ summary: 'Get a tag by ID' })
|
||||||
|
getTagById(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.masterService.findOneTag(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('tags')
|
@Post('tags')
|
||||||
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์จัดการ Tag
|
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์ (Admin/Doc Control)
|
||||||
@ApiOperation({ summary: 'Create a new tag (Admin only)' })
|
@ApiOperation({ summary: 'Create a new tag' })
|
||||||
createTag(@Body() dto: CreateTagDto) {
|
createTag(@Body() dto: CreateTagDto) {
|
||||||
return this.masterService.createTag(dto);
|
return this.masterService.createTag(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Patch('tags/:id')
|
||||||
|
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์
|
||||||
|
@ApiOperation({ summary: 'Update a tag' })
|
||||||
|
updateTag(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateTagDto) {
|
||||||
|
return this.masterService.updateTag(id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('tags/:id')
|
||||||
|
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์
|
||||||
|
@ApiOperation({ summary: 'Delete a tag' })
|
||||||
|
deleteTag(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.masterService.deleteTag(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
// File: src/modules/master/master.service.ts
|
// File: src/modules/master/master.service.ts
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
// Import Entities จาก Module อื่นๆ (ตามโครงสร้างที่มีอยู่แล้ว)
|
// Import Entities
|
||||||
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
|
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
|
||||||
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
|
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
|
||||||
import { RfaType } from '../rfa/entities/rfa-type.entity';
|
import { RfaType } from '../rfa/entities/rfa-type.entity';
|
||||||
import { RfaStatusCode } from '../rfa/entities/rfa-status-code.entity';
|
import { RfaStatusCode } from '../rfa/entities/rfa-status-code.entity';
|
||||||
import { RfaApproveCode } from '../rfa/entities/rfa-approve-code.entity';
|
import { RfaApproveCode } from '../rfa/entities/rfa-approve-code.entity';
|
||||||
import { CirculationStatusCode } from '../circulation/entities/circulation-status-code.entity';
|
import { CirculationStatusCode } from '../circulation/entities/circulation-status-code.entity';
|
||||||
import { Tag } from './entities/tag.entity'; // Entity ของ Module นี้เอง
|
import { Tag } from './entities/tag.entity';
|
||||||
|
|
||||||
|
// Import DTOs
|
||||||
import { CreateTagDto } from './dto/create-tag.dto';
|
import { CreateTagDto } from './dto/create-tag.dto';
|
||||||
|
import { UpdateTagDto } from './dto/update-tag.dto';
|
||||||
|
import { SearchTagDto } from './dto/search-tag.dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MasterService {
|
export class MasterService {
|
||||||
@@ -40,58 +43,123 @@ export class MasterService {
|
|||||||
private readonly tagRepo: Repository<Tag>,
|
private readonly tagRepo: Repository<Tag>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// --- Correspondence ---
|
// =================================================================
|
||||||
findAllCorrespondenceTypes() {
|
// ✉️ Correspondence Master Data
|
||||||
|
// =================================================================
|
||||||
|
|
||||||
|
async findAllCorrespondenceTypes() {
|
||||||
return this.corrTypeRepo.find({
|
return this.corrTypeRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findAllCorrespondenceStatuses() {
|
async findAllCorrespondenceStatuses() {
|
||||||
return this.corrStatusRepo.find({
|
return this.corrStatusRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- RFA ---
|
// =================================================================
|
||||||
findAllRfaTypes() {
|
// 📐 RFA Master Data
|
||||||
|
// =================================================================
|
||||||
|
|
||||||
|
async findAllRfaTypes() {
|
||||||
return this.rfaTypeRepo.find({
|
return this.rfaTypeRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findAllRfaStatuses() {
|
async findAllRfaStatuses() {
|
||||||
return this.rfaStatusRepo.find({
|
return this.rfaStatusRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
findAllRfaApproveCodes() {
|
async findAllRfaApproveCodes() {
|
||||||
return this.rfaApproveRepo.find({
|
return this.rfaApproveRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Circulation ---
|
// =================================================================
|
||||||
findAllCirculationStatuses() {
|
// 🔄 Circulation Master Data
|
||||||
|
// =================================================================
|
||||||
|
|
||||||
|
async findAllCirculationStatuses() {
|
||||||
return this.circulationStatusRepo.find({
|
return this.circulationStatusRepo.find({
|
||||||
where: { is_active: true },
|
where: { is_active: true },
|
||||||
order: { sort_order: 'ASC' },
|
order: { sort_order: 'ASC' },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Tags ---
|
// =================================================================
|
||||||
findAllTags() {
|
// 🏷️ Tag Management (CRUD)
|
||||||
return this.tagRepo.find({ order: { tag_name: 'ASC' } });
|
// =================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ค้นหา Tag ทั้งหมด พร้อมรองรับการ Search และ Pagination
|
||||||
|
*/
|
||||||
|
async findAllTags(query?: SearchTagDto) {
|
||||||
|
const qb = this.tagRepo.createQueryBuilder('tag');
|
||||||
|
|
||||||
|
if (query?.search) {
|
||||||
|
qb.where('tag.tag_name LIKE :search OR tag.description LIKE :search', {
|
||||||
|
search: `%${query.search}%`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
qb.orderBy('tag.tag_name', 'ASC');
|
||||||
|
|
||||||
|
// Pagination Logic
|
||||||
|
if (query?.page && query?.limit) {
|
||||||
|
const page = query.page;
|
||||||
|
const limit = query.limit;
|
||||||
|
qb.skip((page - 1) * limit).take(limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ถ้ามีการแบ่งหน้า ให้ส่งคืนทั้งข้อมูลและจำนวนทั้งหมด (count)
|
||||||
|
if (query?.page && query?.limit) {
|
||||||
|
const [items, total] = await qb.getManyAndCount();
|
||||||
|
return {
|
||||||
|
data: items,
|
||||||
|
meta: {
|
||||||
|
total,
|
||||||
|
page: query.page,
|
||||||
|
limit: query.limit,
|
||||||
|
totalPages: Math.ceil(total / query.limit),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return qb.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
async findOneTag(id: number) {
|
||||||
|
const tag = await this.tagRepo.findOne({ where: { id } });
|
||||||
|
if (!tag) {
|
||||||
|
throw new NotFoundException(`Tag with ID "${id}" not found`);
|
||||||
|
}
|
||||||
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
async createTag(dto: CreateTagDto) {
|
async createTag(dto: CreateTagDto) {
|
||||||
const tag = this.tagRepo.create(dto);
|
const tag = this.tagRepo.create(dto);
|
||||||
return this.tagRepo.save(tag);
|
return this.tagRepo.save(tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateTag(id: number, dto: UpdateTagDto) {
|
||||||
|
const tag = await this.findOneTag(id); // Reuse findOne for check
|
||||||
|
Object.assign(tag, dto);
|
||||||
|
return this.tagRepo.save(tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteTag(id: number) {
|
||||||
|
const tag = await this.findOneTag(id);
|
||||||
|
return this.tagRepo.remove(tag);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user