## Workflow ระดับ Project (correspondence_routing_steps, technical_doc_workflows): คือ "การเดินทาง" ของเอกสาร ระหว่างองค์กร (เช่น จากผู้รับเหมา -> ไปยังที่ปรึกษา -> ไปยังเจ้าของโครงการ) ## Workflow ระดับ Organization (Circulation): คือ "การแจกจ่าย" เอกสาร ภายในองค์กรของคุณเอง หลังจากที่คุณได้รับเอกสารนั้นมาแล้ว (เช่น เอกสารมาถึง Document Control แล้วต้องส่งต่อให้ใครบ้างในบริษัท) circulation_templates: ตารางหลักสำหรับเก็บชื่อแม่แบบ circulation_template_assignees: ตารางสำหรับเก็บ "รายชื่อผู้รับผิดชอบ" ที่ถูกกำหนดไว้ในแต่ละแม่แบบ Workflow การทำงานใน Frontend 1. หน้า Admin Panel: จะต้องมีเมนูใหม่สำหรับให้ Admin หรือผู้มีสิทธิ์ เข้าไป สร้าง/แก้ไข/ลบ แม่แบบใบเวียน (circulation_templates) สำหรับองค์กรของตนเองได้ 2. หน้าที่สร้างใบเวียน (Create Circulation Dialog): * ที่ด้านบนสุดของฟอร์ม จะมี Dropdown ใหม่ ปรากฏขึ้นมา เขียนว่า "ใช้แม่แบบ (Use Template)" * ใน Dropdown นี้ จะแสดงรายชื่อแม่แบบทั้งหมดที่องค์กรนั้นๆ สร้างไว้ * เมื่อผู้ใช้เลือกแม่แบบ: ** ระบบจะยิง API ไปดึงรายชื่อผู้รับผิดชอบจากตาราง circulation_template_assignees ** จากนั้น JavaScript จะทำการเติมข้อมูล (Auto-populate) ลงในช่อง "Main", "Action", และ "Information" ให้โดยอัตโนมัติ * ผู้ใช้ยังสามารถ แก้ไข/เพิ่มเติม/ลบ รายชื่อผู้รับผิดชอบได้ตามต้องการ ก่อนที่จะกดสร้างใบเวียนจริง การจัดการข้อมูล JSON จะเกิดขึ้นใน 3 ส่วนหลักๆ คือ Backend, Frontend, และ Database ครับ ## 1. การจัดการในฝั่ง Backend (NestJS) นี่คือส่วนที่ทำหน้าที่หลักในการสร้างและอ่านข้อมูล JSON อย่างเป็นระบบและปลอดภัย 1.1 การแก้ไข Entity เราจะแก้ไข Correspondence entity โดยเพิ่มคอลัมน์ details เข้าไป และลบ Entity ย่อยๆ ที่ไม่ใช้ออก src/correspondences/entities/correspondence.entity.ts @Entity('correspondences') export class Correspondence { // ... (คอลัมน์เดิมทั้งหมด: id, document_number, title, etc.) @Column({ type: 'json', // ◀️ กำหนดประเภทข้อมูลเป็น JSON nullable: true, comment: 'เก็บข้อมูลเฉพาะของเอกสารแต่ละประเภทในรูปแบบ JSON' }) details: any; // ◀️ ใช้ type 'any' หรือสร้าง Interface/Type ที่ซับซ้อนขึ้น } 1.2 การสร้าง DTOs สำหรับแต่ละประเภทเอกสาร เพื่อรักษาความถูกต้องของข้อมูล (Validation) เราจะสร้าง DTO แยกสำหรับเอกสารแต่ละประเภท ตัวอย่าง src/correspondences/dto/create-letter.dto.ts: TypeScript import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsString } from 'class-validator'; // DTO สำหรับข้อมูลใน details ของ Letter class LetterDetailsDto { @ApiProperty() @IsString() @IsNotEmpty() attention_to: string; @ApiProperty() @IsString() @IsNotEmpty() signatory_name: string; } // DTO หลักสำหรับสร้าง Letter export class CreateLetterDto { @ApiProperty({ description: "ข้อมูลพื้นฐานของเอกสาร" }) @ValidateNested() // ◀️ บอกให้ class-validator ตรวจสอบ object ข้างในด้วย @Type(() => CreateCorrespondenceDto) // ◀️ ใช้ DTO พื้นฐานร่วมกัน base_data: CreateCorrespondenceDto; @ApiProperty({ description: "ข้อมูลเฉพาะของ Letter" }) @ValidateNested() @Type(() => LetterDetailsDto) details: LetterDetailsDto; } 1.3 การสร้าง API Endpoint และ Logic ใน Service เราจะสร้าง Endpoint แยกสำหรับสร้างเอกสารแต่ละประเภทเพื่อความชัดเจน ใน CorrespondencesController: TypeScript @Post('letter') @ApiOperation({ summary: 'Create a new Letter' }) createLetter(@Body() createLetterDto: CreateLetterDto, @Req() req: Request) { const user = req.user as any; return this.correspondencesService.createTypedCorrespondence( createLetterDto.base_data, createLetterDto.details, user ); } ใน CorrespondencesService: TypeScript async createTypedCorrespondence(baseData: CreateCorrespondenceDto, details: any, user: User): Promise { // ... (โค้ดตรวจสอบสิทธิ์เหมือนเดิม) const newCorrespondence = this.correspondenceRepository.create({ ...baseData, // ข้อมูลพื้นฐาน (เลขที่เอกสาร, ชื่อเรื่อง, etc.) details: details, // ◀️ นำ object ของ details มาใส่ในคอลัมน์ JSON โดยตรง created_by_user_id: user.user_id, originator_org_id: user.org_id, status_id: 1, // 'Draft' }); return this.correspondenceRepository.save(newCorrespondence); } ## 2. การจัดการในฝั่ง Frontend (Next.js / React) Frontend จะทำหน้าที่แสดงฟอร์มที่ถูกต้องตามประเภทเอกสาร และส่งข้อมูลในรูปแบบที่ Backend ต้องการ 2.1 การแสดงฟอร์มแบบไดนามิก (Dynamic Forms) ในหน้า "Create Correspondence" เมื่อผู้ใช้เลือกประเภทเอกสารจาก Dropdown เราจะใช้ State เพื่อแสดงฟอร์มที่ถูกต้อง TypeScript const [docType, setDocType] = useState('LETTER'); // ... const renderDetailFields = () => { switch (docType) { case 'LETTER': return ( <> {/* ฟิลด์สำหรับ Attention To, Signatory */} ); case 'RFI': return ( <> {/* ฟิลด์สำหรับ Question, Required By Date */} ); default: return null; } } return (
{/* ฟิลด์พื้นฐาน (Document Number, Title) */} {/* Dropdown เลือกประเภทเอกสาร */} {renderDetailFields()} {/* ◀️ แสดงฟิลด์เฉพาะทางที่นี่ */}
); 2.2 การส่งข้อมูล เมื่อผู้ใช้กด Submit เราจะรวบรวมข้อมูลจากฟอร์มให้เป็นโครงสร้าง JSON ที่ Backend ต้องการ JavaScript const handleSubmit = () => { // รวบรวมข้อมูลพื้นฐาน const base_data = { document_number: '...', title: '...', // ... }; // รวบรวมข้อมูลเฉพาะทาง const details = { attention_to: '...', signatory_name: '...', }; // ส่งข้อมูลไปที่ API Endpoint ที่ถูกต้อง fetch('/api/correspondences/letter', { method: 'POST', body: JSON.stringify({ base_data, details }), }); } ## 3. การจัดการในระดับฐานข้อมูล (MariaDB) แม้ว่าเราจะไม่ค่อยได้ Query ข้อมูล JSON โดยตรงผ่าน SQL บ่อยนัก แต่ก็สามารถทำได้เมื่อจำเป็น (เช่น สำหรับการทำรายงานที่ซับซ้อน) ตัวอย่าง: ค้นหาเอกสาร Letter ทั้งหมดที่ส่งถึง "Mr. John Doe" SQL SELECT corr_id, document_number, details FROM correspondences WHERE type_id = (SELECT type_id FROM correspondence_types WHERE type_code = 'LETTER') -- กรองเฉพาะ Letter AND JSON_VALUE(details, '$.attention_to') = 'Mr. John Doe'; -- ◀️ ค้นหาค่าใน JSON การจัดการข้อมูลด้วยวิธีนี้จะทำให้ระบบของคุณมีความ