251124:1702 Ready to Phase 7

This commit is contained in:
admin
2025-11-24 17:03:36 +07:00
parent 4f45a69ed0
commit 553c2d13ad
2 changed files with 130 additions and 21 deletions

View File

@@ -1,9 +1,22 @@
// 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 { MasterService } from './master.service';
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 { RequirePermission } from '../../common/decorators/require-permission.decorator';
@@ -13,6 +26,10 @@ import { RequirePermission } from '../../common/decorators/require-permission.de
export class MasterController {
constructor(private readonly masterService: MasterService) {}
// =================================================================
// 📦 Dropdowns Endpoints (Read-Only for Frontend)
// =================================================================
@Get('correspondence-types')
@ApiOperation({ summary: 'Get all active correspondence types' })
getCorrespondenceTypes() {
@@ -49,16 +66,40 @@ export class MasterController {
return this.masterService.findAllCirculationStatuses();
}
// =================================================================
// 🏷️ Tag Management Endpoints
// =================================================================
@Get('tags')
@ApiOperation({ summary: 'Get all tags' })
getTags() {
return this.masterService.findAllTags();
@ApiOperation({ summary: 'Get all tags (supports search & pagination)' })
getTags(@Query() query: SearchTagDto) {
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')
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์จัดการ Tag
@ApiOperation({ summary: 'Create a new tag (Admin only)' })
@RequirePermission('master_data.tag.manage') // ต้องมีสิทธิ์ (Admin/Doc Control)
@ApiOperation({ summary: 'Create a new tag' })
createTag(@Body() dto: CreateTagDto) {
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);
}
}

View File

@@ -1,19 +1,22 @@
// File: src/modules/master/master.service.ts
import { Injectable } from '@nestjs/common';
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
// Import Entities จาก Module อื่นๆ (ตามโครงสร้างที่มีอยู่แล้ว)
// Import Entities
import { CorrespondenceType } from '../correspondence/entities/correspondence-type.entity';
import { CorrespondenceStatus } from '../correspondence/entities/correspondence-status.entity';
import { RfaType } from '../rfa/entities/rfa-type.entity';
import { RfaStatusCode } from '../rfa/entities/rfa-status-code.entity';
import { RfaApproveCode } from '../rfa/entities/rfa-approve-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 { UpdateTagDto } from './dto/update-tag.dto';
import { SearchTagDto } from './dto/search-tag.dto';
@Injectable()
export class MasterService {
@@ -40,58 +43,123 @@ export class MasterService {
private readonly tagRepo: Repository<Tag>,
) {}
// --- Correspondence ---
findAllCorrespondenceTypes() {
// =================================================================
// ✉️ Correspondence Master Data
// =================================================================
async findAllCorrespondenceTypes() {
return this.corrTypeRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
findAllCorrespondenceStatuses() {
async findAllCorrespondenceStatuses() {
return this.corrStatusRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
// --- RFA ---
findAllRfaTypes() {
// =================================================================
// 📐 RFA Master Data
// =================================================================
async findAllRfaTypes() {
return this.rfaTypeRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
findAllRfaStatuses() {
async findAllRfaStatuses() {
return this.rfaStatusRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
findAllRfaApproveCodes() {
async findAllRfaApproveCodes() {
return this.rfaApproveRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
// --- Circulation ---
findAllCirculationStatuses() {
// =================================================================
// 🔄 Circulation Master Data
// =================================================================
async findAllCirculationStatuses() {
return this.circulationStatusRepo.find({
where: { is_active: true },
order: { sort_order: 'ASC' },
});
}
// --- Tags ---
findAllTags() {
return this.tagRepo.find({ order: { tag_name: 'ASC' } });
// =================================================================
// 🏷️ Tag Management (CRUD)
// =================================================================
/**
* ค้นหา 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) {
const tag = this.tagRepo.create(dto);
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);
}
}