From 68d47f9cb20dcbc2e4d90b5d3f883b09bdbeb745 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 19 Nov 2025 01:04:53 +0700 Subject: [PATCH] 251119:0100 update requirements --- 01_lcbp3_v1_4_2.sql | 2417 +++++++++ Documnets/Project/0_Requirements_V1_4_2.md | 763 +++ Documnets/Project/1_FullStackJS_V1_4_2..md | 960 ++++ Documnets/Project/2_Backend_Plan_V1_4_2.md | 539 ++ Documnets/Project/3_Frontend_Plan_V1_4_2.md | 982 ++++ Documnets/Project/4_Data_Dictionary_V1_4_2.md | 2947 +++++++++++ .../01_lcbp3_v1_4_0.sql | 3756 ++++++------- .../01_lcbp3_v1_4_1.sql | 4710 +++++++++-------- ...Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md | 0 ...BP3-DMS_V1_4_1_Backend_Development_Plan.md | 0 ...MS_V1_4_1_Backend_Development_Plan_Grok.md | 0 .../LCBP3-DMS_V1_4_1_Data_Dictionary.md | 0 ...P3-DMS_V1_4_1_Frontend_Development_Plan.md | 0 .../Markdown}/LCBP3-DMS_V1_4_1_FullStackJS.md | 0 .../LCBP3-DMS_V1_4_1_Requirements.md | 0 ..._V1_4_2_Backend_Development_Plan (Patched) | 0 .../LCBP3-DMS_V1_4_2_FullStackJS (Patched) | 0 .../LCBP3-DMS_V1_4_2_Requirements (Patched) | 2 +- .../LCBP3-DMS_V1_4_2_Requirements..bak.md | 618 +++ ...BP3-DMS_V1_4_2_Backend_Development_Plan.md | 0 20 files changed, 13522 insertions(+), 4172 deletions(-) create mode 100644 01_lcbp3_v1_4_2.sql create mode 100644 Documnets/Project/0_Requirements_V1_4_2.md create mode 100644 Documnets/Project/1_FullStackJS_V1_4_2..md create mode 100644 Documnets/Project/2_Backend_Plan_V1_4_2.md create mode 100644 Documnets/Project/3_Frontend_Plan_V1_4_2.md create mode 100644 Documnets/Project/4_Data_Dictionary_V1_4_2.md rename 01_lcbp3_v1_4_0.sql => docs/01_lcbp3_v1_4_0.sql (98%) rename 01_lcbp3_v1_4_1.sql => docs/01_lcbp3_v1_4_1.sql (92%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS — Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_Backend_Development_Plan_Grok.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_Data_Dictionary.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_FullStackJS.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_1_Requirements.md (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_2_Backend_Development_Plan (Patched) (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_2_FullStackJS (Patched) (100%) rename {Documnets/Project => docs/Markdown}/LCBP3-DMS_V1_4_2_Requirements (Patched) (99%) create mode 100644 docs/Markdown/LCBP3-DMS_V1_4_2_Requirements..bak.md rename {Documnets/Project => docs/Markdown}/working LCBP3-DMS_V1_4_2_Backend_Development_Plan.md (100%) diff --git a/01_lcbp3_v1_4_2.sql b/01_lcbp3_v1_4_2.sql new file mode 100644 index 0000000..71d2a9e --- /dev/null +++ b/01_lcbp3_v1_4_2.sql @@ -0,0 +1,2417 @@ +-- ========================================================== +-- DMS v1.4.0 Document Management System Database +-- Deploy Script Schema +-- Server: Container Station on QNAPQNAP TS-473A +-- Database service: MariaDB 10.11 +-- database web ui: phpmyadmin 5-apache +-- database deelopment ui: DBeaver +-- backend sevice: NestJS +-- frontend sevice: next.js +-- reverse proxy: jc21/nginx-proxy-manager:latest +-- cron service: n8n +-- DMS v1.4.2 Improvements +-- Update: revise fron v1.4.1 Gemini) +-- ========================================================== +SET NAMES utf8mb4; +SET time_zone = '+07:00'; +-- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด +SET FOREIGN_KEY_CHECKS = 0; +DROP VIEW IF EXISTS v_document_statistics; +DROP VIEW IF EXISTS v_documents_with_attachments; +DROP VIEW IF EXISTS v_user_all_permissions; +DROP VIEW IF EXISTS v_audit_log_details; +DROP VIEW IF EXISTS v_user_tasks; +DROP VIEW IF EXISTS v_contract_parties_all; +DROP VIEW IF EXISTS v_current_rfas; +DROP VIEW IF EXISTS v_current_correspondences; +DROP PROCEDURE IF EXISTS sp_get_next_document_number; +-- 🗑️ DROP TABLE SCRIPT: LCBP3-DMS v1.4.2 +-- คำเตือน: ข้อมูลทั้งหมดจะหายไป กรุณา Backup ก่อนรันบน Production +SET FOREIGN_KEY_CHECKS = 0; +-- ============================================================ +-- ส่วนที่ 1: ตาราง System, Logs & Preferences (ตารางปลายทาง/ส่วนเสริม) +-- ============================================================ +DROP TABLE IF EXISTS backup_logs; +DROP TABLE IF EXISTS search_indices; +DROP TABLE IF EXISTS notifications; +DROP TABLE IF EXISTS audit_logs; +-- [NEW v1.4.2] ตารางการตั้งค่าส่วนตัวของผู้ใช้ (FK -> users) +DROP TABLE IF EXISTS user_preferencesว -- [NEW v1.4.2] ตารางเก็บ Schema สำหรับ Validate JSON (Stand-alone) +DROP TABLE IF EXISTS json_schemas; +-- ============================================================ +-- ส่วนที่ 2: ตาราง Junction (เชื่อมโยงข้อมูล M:N) +-- ============================================================ +DROP TABLE IF EXISTS correspondence_tags; +DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; +DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; +-- ============================================================ +-- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments) +-- ============================================================ +DROP TABLE IF EXISTS contract_drawing_attachments; +DROP TABLE IF EXISTS circulation_attachments; +DROP TABLE IF EXISTS shop_drawing_revision_attachments; +DROP TABLE IF EXISTS correspondence_attachments; +DROP TABLE IF EXISTS attachments; +-- ตารางหลักเก็บ path ไฟล์ +-- ============================================================ +-- ส่วนที่ 4: ตาราง Workflow & Routing (Process Logic) +-- ============================================================ +-- Circulation Workflow +DROP TABLE IF EXISTS circulation_routings; +DROP TABLE IF EXISTS circulation_template_assignees; +DROP TABLE IF EXISTS circulation_templates; +-- RFA Workflow +DROP TABLE IF EXISTS rfa_workflows; +DROP TABLE IF EXISTS rfa_workflow_template_steps; +DROP TABLE IF EXISTS rfa_workflow_templates; +-- Correspondence Workflow +DROP TABLE IF EXISTS correspondence_routings; +DROP TABLE IF EXISTS correspondence_routing_template_steps; +DROP TABLE IF EXISTS correspondence_status_transitions; +DROP TABLE IF EXISTS correspondence_routing_templates; +-- ============================================================ +-- ส่วนที่ 5: ตาราง Mapping สิทธิ์และโครงสร้าง (Access Control) +-- ============================================================ +DROP TABLE IF EXISTS role_permissions; +DROP TABLE IF EXISTS user_assignments; +DROP TABLE IF EXISTS contract_organizations; +DROP TABLE IF EXISTS project_organizations; +-- ============================================================ +-- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Revisions & Items) +-- ============================================================ +DROP TABLE IF EXISTS transmittal_items; +DROP TABLE IF EXISTS shop_drawing_revisions; +DROP TABLE IF EXISTS rfa_items; +DROP TABLE IF EXISTS rfa_revisions; +DROP TABLE IF EXISTS correspondence_references; +DROP TABLE IF EXISTS correspondence_recipients; +DROP TABLE IF EXISTS correspondence_revisions; +-- [Modified v1.4.2] มี Virtual Columns +-- ============================================================ +-- ส่วนที่ 7: ตารางเอกสารหลัก (Core Documents) +-- ============================================================ +DROP TABLE IF EXISTS circulations; +DROP TABLE IF EXISTS transmittals; +DROP TABLE IF EXISTS contract_drawings; +DROP TABLE IF EXISTS shop_drawings; +DROP TABLE IF EXISTS rfas; +DROP TABLE IF EXISTS correspondences; +-- ============================================================ +-- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Master Data) +-- ============================================================ +DROP TABLE IF EXISTS shop_drawing_sub_categories; +DROP TABLE IF EXISTS shop_drawing_main_categories; +DROP TABLE IF EXISTS contract_drawing_sub_cats; +DROP TABLE IF EXISTS contract_drawing_cats; +DROP TABLE IF EXISTS contract_drawing_volumes; +DROP TABLE IF EXISTS circulation_status_codes; +DROP TABLE IF EXISTS rfa_approve_codes; +DROP TABLE IF EXISTS rfa_status_codes; +DROP TABLE IF EXISTS rfa_types; +DROP TABLE IF EXISTS correspondence_status; +DROP TABLE IF EXISTS correspondence_types; +DROP TABLE IF EXISTS document_number_counters; +-- [Modified v1.4.2] มี version column +DROP TABLE IF EXISTS document_number_formats; +DROP TABLE IF EXISTS tags; +-- ============================================================ +-- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างรากฐาน (Root Tables) +-- ============================================================ +DROP TABLE IF EXISTS organization_roles; +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS permissions; +DROP TABLE IF EXISTS contracts; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS users; +-- Referenced by user_preferences, audit_logs, etc. +DROP TABLE IF EXISTS organizations; +-- Referenced by users, projects, etc. +-- ===================================================== +-- 1. 🏢 Core & Master Data (องค์กร, โครงการ, สัญญา) +-- ===================================================== +-- ตาราง Master เก็บประเภทบทบาทขององค์กร +CREATE TABLE organization_roles ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'ชื่อบทบาท (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทบทบาทขององค์กร'; +-- ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ +CREATE TABLE organizations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสองค์กร', + organization_name VARCHAR(255) NOT NULL COMMENT 'ชื่อองค์กร', + -- role_id INT COMMENT 'บทบาทขององค์กร', + is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ'; +-- Seed organization +INSERT INTO organizations (id, organization_code, organization_name) +VALUES (1, 'กทท.', 'การท่าเรือแห่งประเทศไทย'), + ( + 10, + 'สคฉ.3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3' + ), + ( + 11, + 'สคฉ.3-01', + 'ตรวจรับพัสดุ ที่ปรึกษาควบคุมงาน' + ), + (12, 'สคฉ.3-02', 'ตรวจรับพัสดุ งานทางทะเล'), + ( + 13, + 'สคฉ.3-03', + 'ตรวจรับพัสดุ อาคารและระบบสาธารณูปโภค' + ), + ( + 14, + 'สคฉ.3-04', + 'ตรวจรับพัสดุ ตรวจสอบผลกระทบสิ่งแวดล้อม' + ), + (15, 'สคฉ.3-05', 'ตรวจรับพัสดุ เยียวยาการประมง'), + ( + 16, + 'สคฉ.3-06', + 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 3' + ), + ( + 17, + 'สคฉ.3-07', + 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 4' + ), + ( + 18, + 'สคฉ.3-xx', + 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4' + ), + (21, 'TEAM', 'Designer Consulting Ltd.'), + (22, 'คคง.', 'Construction Supervision Ltd.'), + (41, 'ผรม.1', 'Contractor งานทางทะเล'), + (42, 'ผรม.2', 'Contractor อาคารและระบบ'), + (43, 'ผรม.3', 'Contractor #3 Ltd.'), + (44, 'ผรม.4', 'Contractor #4 Ltd.'), + (31, 'EN', 'Third Party Environment'), + (32, 'CAR', 'Third Party Fishery Care'); +-- ตาราง Master เก็บข้อมูลโครงการ +CREATE TABLE projects ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสโครงการ', + project_name VARCHAR(255) NOT NULL COMMENT 'ชื่อโครงการ', + -- parent_project_id INT COMMENT 'รหัสโครงการหลัก (ถ้ามี)', + -- contractor_organization_id INT COMMENT 'รหัสองค์กรผู้รับเหมา (ถ้ามี)', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, + -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลโครงการ'; +INSERT INTO projects (project_code, project_name) +VALUES ( + 'LCBP3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)' + ), + ( + 'LCBP3C1', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล' + ), + ( + 'LCBP3C2', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค' + ), + ( + 'LCBP3C3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง' + ), + ( + 'LCBP3C4', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง' + ); +-- ตาราง Master เก็บข้อมูลสัญญา +CREATE TABLE contracts ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL, + contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสัญญา', + contract_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสัญญา', + description TEXT COMMENT 'คำอธิบายสัญญา', + start_date DATE COMMENT 'วันที่เริ่มสัญญา', + end_date DATE COMMENT 'วันที่สิ้นสุดสัญญา', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลสัญญา'; +-- ใช้ Subquery เพื่อดึง project_id มาเชื่อมโยง ทำให้ไม่ต้องมานั่งจัดการ ID ด้วยตัวเอง +INSERT INTO contracts ( + contract_code, + contract_name, + project_id, + is_active + ) +VALUES ( + 'DSLCBP3', + 'งานจ้างที่ปรีกษาออกแบบ โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'PSLCBP3', + 'งานจ้างที่ปรีกษาควบคุมงาน โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'LCBP3-C1', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1' + ), + TRUE + ), + ( + 'LCBP3-C2', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2' + ), + TRUE + ), + ( + 'LCBP3-C3', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C3' + ), + TRUE + ), + ( + 'LCBP3-C4', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C4' + ), + TRUE + ), + ( + 'ENLCBP3', + 'งานจ้างเหมาตรวจสอบผลกระทบสิ่งแวดล้อมนะหว่างงานก่อสร้างโครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ); +-- ===================================================== +-- 2. 👥 Users & RBAC (ผู้ใช้, สิทธิ์, บทบาท) +-- ===================================================== +-- ตาราง Master เก็บข้อมูลผู้ใช้งาน (User) +CREATE TABLE users ( + user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + username VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อผู้ใช้งาน', + password_hash VARCHAR(255) NOT NULL COMMENT 'รหัสผ่าน (Hashed)', + first_name VARCHAR(50) COMMENT 'ชื่อจริง', + last_name VARCHAR(50) COMMENT 'นามสกุล', + email VARCHAR(100) NOT NULL UNIQUE COMMENT 'อีเมล', + line_id VARCHAR(100) COMMENT 'LINE ID', + primary_organization_id INT COMMENT 'สังกัดองค์กร', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + failed_attempts INT DEFAULT 0 COMMENT 'จำนวนครั้งที่ล็อกอินล้มเหลว', + locked_until DATETIME COMMENT 'ล็อกอินไม่ได้จนถึงเวลา', + last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)'; +-- Initial SUPER_ADMIN user +INSERT INTO users (username, password_hash, email, is_active) +VALUES ( + 'superadmin', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'superadmin@example.com', + 1 + ) ON DUPLICATE KEY +UPDATE email = +VALUES(email), + is_active = +VALUES(is_active); +-- Create editor01 user +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ( + 'editor01', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'editor01@example.com', + 1 + ); +-- Create viewer01 user (password hash placeholder, must change later) +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ( + 'viewer01', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'viewer01@example.com', + 1 + ); +-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ +CREATE TABLE roles ( + role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', + role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท', + scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, + -- ขอบเขตของบทบาท (จากข้อ 4.3) + description TEXT COMMENT 'คำอธิบายบทบาท', + is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = บทบาทของระบบ ลบไม่ได้)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ'; +-- ========================================================== +-- Seed Roles (บทบาทพื้นฐาน 5 บทบาท ตาม Req 4.3) +-- ========================================================== +-- 1. Superadmin (Global) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 1, + 'Superadmin', + 'Global', + 'ผู้ดูแลระบบสูงสุด: สามารถทำทุกอย่างในระบบ, จัดการองค์กร, และจัดการข้อมูลหลักระดับ Global' + ); +-- 2. Org Admin (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 2, + 'Org Admin', + 'Organization', + 'ผู้ดูแลองค์กร: จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, และดูรายงานขององค์กร' + ); +-- 3. Document Control (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 3, + 'Document Control', + 'Organization', + 'ควบคุมเอกสารขององค์กร: เพิ่ม/แก้ไข/ลบเอกสาร, และกำหนดสิทธิ์เอกสารภายในองค์กร' + ); +-- 4. Editor (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 4, + 'Editor', + 'Organization', + 'ผู้แก้ไขเอกสารขององค์กร: เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย' + ); +-- 5. Viewer (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 5, + 'Viewer', + 'Organization', + 'ผู้ดูเอกสารขององค์กร: ดูเอกสารที่มีสิทธิ์เข้าถึงเท่านั้น' + ); +-- 6. Project Manager (Project) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 6, + 'Project Manager', + 'Project', + 'ผู้จัดการโครงการ: จัดการสมาชิกในโครงการ, สร้าง/จัดการสัญญาในโครงการ, และดูรายงานโครงการ' + ); +-- 7. Contract Admin (Contract) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 7, + 'Contract Admin', + 'Contract', + 'ผู้ดูแลสัญญา: จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา, และอนุมัติเอกสารในสัญญา' + ); +-- ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ +CREATE TABLE permissions ( + permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัสสิทธิ์ (เช่น rfas.create, rfas.view)', + description TEXT COMMENT 'คำอธิบายสิทธิ์', + module VARCHAR(50) COMMENT 'โมดูลที่เกี่ยวข้อง', + scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'ระดับขอบเขตของสิทธิ์', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ'; +-- ===================================================== +-- 2. Seed Permissions (สิทธิ์การใช้งานทั้งหมด) +-- สิทธิ์ระดับระบบและการจัดการหลัก (System & Master Data) +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 1, + 'system.manage_all', + 'ทำทุกอย่างในระบบ (Superadmin Power)' + ), + -- การจัดการองค์กร + (2, 'organization.create', 'สร้างองค์กรใหม่'), + (3, 'organization.edit', 'แก้ไขข้อมูลองค์กร'), + (4, 'organization.delete', 'ลบองค์กร'), + (5, 'organization.view', 'ดูรายการองค์กร'), + -- การจัดการโครงการ + (6, 'project.create', 'สร้างโครงการใหม่'), + (7, 'project.edit', 'แก้ไขข้อมูลโครงการ'), + (8, 'project.delete', 'ลบโครงการ'), + (9, 'project.view', 'ดูรายการโครงการ'), + -- การจัดการบทบาทและสิทธิ์ (Roles & Permissions) + (10, 'role.create', 'สร้างบทบาท (Role) ใหม่'), + (11, 'role.edit', 'แก้ไขบทบาท (Role)'), + (12, 'role.delete', 'ลบบทบาท (Role)'), + ( + 13, + 'permission.assign', + 'มอบสิทธิ์ให้กับบทบาท (Role)' + ), + -- การจัดการข้อมูลหลัก (Master Data) + ( + 14, + 'master_data.document_type.manage', + 'จัดการประเภทเอกสาร (Document Types)' + ), + ( + 15, + 'master_data.document_status.manage', + 'จัดการสถานะเอกสาร (Document Statuses)' + ), + ( + 16, + 'master_data.drawing_category.manage', + 'จัดการหมวดหมู่แบบ (Drawing Categories)' + ), + (17, 'master_data.tag.manage', 'จัดการ Tags'), + -- การจัดการผู้ใช้งาน + (18, 'user.create', 'สร้างผู้ใช้งานใหม่'), + (19, 'user.edit', 'แก้ไขข้อมูลผู้ใช้งาน'), + (20, 'user.delete', 'ลบ/ปิดการใช้งานผู้ใช้'), + (21, 'user.view', 'ดูข้อมูลผู้ใช้งาน'), + ( + 22, + 'user.assign_organization', + 'มอบผู้ใช้งานให้กับองค์กร' + ); +-- ===================================================== +-- == 2. สิทธิ์การจัดการโครงการและสัญญา (Project & Contract) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 23, + 'project.manage_members', + 'จัดการสมาชิกในโครงการ (เชิญ/ถอดสมาชิก)' + ), + ( + 24, + 'project.create_contracts', + 'สร้างสัญญาในโครงการ' + ), + ( + 25, + 'project.manage_contracts', + 'จัดการสัญญาในโครงการ' + ), + ( + 26, + 'project.view_reports', + 'ดูรายงานระดับโครงการ' + ), + ( + 27, + 'contract.manage_members', + 'จัดการสมาชิกในสัญญา' + ), + (28, 'contract.view', 'ดูข้อมูลสัญญา'); +-- ===================================================== +-- == 3. สิทธิ์การจัดการเอกสาร (Document Management) == +-- ===================================================== +-- สิทธิ์ทั่วไปสำหรับเอกสารทุกประเภท +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 29, + 'document.create_draft', + 'สร้างเอกสารในสถานะฉบับร่าง (Draft)' + ), + (30, 'document.submit', 'ส่งเอกสาร (Submitted)'), + (31, 'document.view', 'ดูเอกสาร'), + (32, 'document.edit', 'แก้ไขเอกสาร (ทั่วไป)'), + ( + 33, + 'document.admin_edit', + 'แก้ไข/ถอน/ยกเลิกเอกสารที่ส่งแล้ว (Admin Power)' + ), + (34, 'document.delete', 'ลบเอกสาร'), + ( + 35, + 'document.attach', + 'จัดการไฟล์แนบ (อัปโหลด/ลบ)' + ), + -- สิทธิ์เฉพาะสำหรับ Correspondence + ( + 36, + 'correspondence.create', + 'สร้างเอกสารโต้ตอบ (Correspondence)' + ), + -- สิทธิ์เฉพาะสำหรับ Request for Approval (RFA) + (37, 'rfa.create', 'สร้างเอกสารขออนุมัติ (RFA)'), + ( + 38, + 'rfa.manage_shop_drawings', + 'จัดการข้อมูล Shop Drawing และ Contract Drawing ที่เกี่ยวข้อง' + ), + -- สิทธิ์เฉพาะสำหรับ Shop Drawing & Contract Drawing + ( + 39, + 'drawing.create', + 'สร้าง/แก้ไขข้อมูลแบบ (Shop/Contract Drawing)' + ), + -- สิทธิ์เฉพาะสำหรับ Transmittal + ( + 40, + 'transmittal.create', + 'สร้างเอกสารนำส่ง (Transmittal)' + ), + -- สิทธิ์เฉพาะสำหรับ Circulation Sheet (ใบเวียน) + ( + 41, + 'circulation.create', + 'สร้างใบเวียนเอกสาร (Circulation)' + ), + ( + 42, + 'circulation.respond', + 'ตอบกลับใบเวียน (Main/Action)' + ), + ( + 43, + 'circulation.acknowledge', + 'รับทราบใบเวียน (Information)' + ), + (44, 'circulation.close', 'ปิดใบเวียน'); +-- ===================================================== +-- == 4. สิทธิ์การจัดการ Workflow == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 45, + 'workflow.action_review', + 'ดำเนินการในขั้นตอนปัจจุบัน (เช่น ตรวจสอบแล้ว)' + ), + ( + 46, + 'workflow.force_proceed', + 'บังคับไปยังขั้นตอนถัดไป (Document Control Power)' + ), + ( + 47, + 'workflow.revert', + 'ย้อนกลับไปยังขั้นตอนก่อนหน้า (Document Control Power)' + ); +-- ===================================================== +-- == 5. สิทธิ์ด้านการค้นหาและรายงาน (Search & Reporting) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES (48, 'search.advanced', 'ใช้งานการค้นหาขั้นสูง'), + ( + 49, + 'report.generate', + 'สร้างรายงานสรุป (รายวัน/สัปดาห์/เดือน/ปี)' + ); +-- ตารางเชื่อมระหว่าง roles และ permissions (M:N) +CREATE TABLE role_permissions ( + role_id INT COMMENT 'ID ของบทบาท', + permission_id INT COMMENT 'ID ของสิทธิ์', + PRIMARY KEY (role_id, permission_id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง roles และ permissions (M:N)'; +-- ========================================================== +-- Seed Role-Permissions Mapping (จับคู่สิทธิ์เริ่มต้น) +-- ========================================================== +-- Seed data for the 'role_permissions' table +-- This table links roles to their specific permissions. +-- NOTE: This assumes the role_id and permission_id from the previous seed data files. +-- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. +-- ===================================================== +-- == 1. Superadmin (role_id = 1) - Gets ALL permissions == +-- ===================================================== +-- Superadmin can do everything. We can dynamically link all permissions to this role. +-- This is a robust way to ensure Superadmin always has full power. +INSERT INTO role_permissions (role_id, permission_id) +SELECT 1, + permission_id +FROM permissions; +-- ===================================================== +-- == 2. Org Admin (role_id = 2) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- จัดการผู้ใช้ในองค์กร + (2, 18), + -- user.create + (2, 19), + -- user.edit + (2, 20), + -- user.delete + (2, 21), + -- user.view + (2, 22), + -- user.assign_organization + -- จัดการองค์กร + (2, 3), + -- organization.edit + (2, 5), + -- organization.view + -- จัดการข้อมูลหลักที่อนุญาต (เฉพาะ Tags) + (2, 17), + -- master_data.tag.manage + -- ดูข้อมูลต่างๆ ในองค์กร + (2, 31), + -- document.view + (2, 9), + -- project.view + (2, 28), + -- contract.view + -- การค้นหาและรายงาน + (2, 48), + -- search.advanced + (2, 49); +-- report.generate +-- ===================================================== +-- == 3. Document Control (role_id = 3) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการเอกสารทั้งหมด + (3, 29), + -- document.create_draft + (3, 30), + -- document.submit + (3, 31), + -- document.view + (3, 32), + -- document.edit + (3, 33), + -- document.admin_edit + (3, 34), + -- document.delete + (3, 35), + -- document.attach + -- สิทธิ์สร้างเอกสารแต่ละประเภท + (3, 36), + -- correspondence.create + (3, 37), + -- rfa.create + (3, 39), + -- drawing.create + (3, 40), + -- transmittal.create + (3, 41), + -- circulation.create + -- สิทธิ์จัดการ Workflow + (3, 45), + -- workflow.action_review + (3, 46), + -- workflow.force_proceed + (3, 47), + -- workflow.revert + -- สิทธิ์จัดการ Circulation + (3, 42), + -- circulation.respond + (3, 43), + -- circulation.acknowledge + (3, 44), + -- circulation.close + -- สิทธิ์อื่นๆ ที่จำเป็น + (3, 38), + -- rfa.manage_shop_drawings + (3, 48), + -- search.advanced + (3, 49); +-- report.generate +-- ===================================================== +-- == 4. Editor (role_id = 4) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์แก้ไขเอกสาร (แต่ไม่ใช่สิทธิ์ Admin) + (4, 29), + -- document.create_draft + (4, 30), + -- document.submit + (4, 31), + -- document.view + (4, 32), + -- document.edit + (4, 35), + -- document.attach + -- สิทธิ์สร้างเอกสารแต่ละประเภท + (4, 36), + -- correspondence.create + (4, 37), + -- rfa.create + (4, 39), + -- drawing.create + (4, 40), + -- transmittal.create + (4, 41), + -- circulation.create + -- สิทธิ์อื่นๆ ที่จำเป็น + (4, 38), + -- rfa.manage_shop_drawings + (4, 48); +-- search.advanced +-- ===================================================== +-- == 5. Viewer (role_id = 5) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์ดูเท่านั้น + (5, 31), + -- document.view + (5, 48); +-- search.advanced +-- ===================================================== +-- == 6. Project Manager (role_id = 6) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการโครงการ + (6, 23), + -- project.manage_members + (6, 24), + -- project.create_contracts + (6, 25), + -- project.manage_contracts + (6, 26), + -- project.view_reports + (6, 9), + -- project.view + -- สิทธิ์จัดการข้อมูลหลักระดับโครงการ + (6, 16), + -- master_data.drawing_category.manage + -- สิทธิ์ดูข้อมูลในสัญญา + (6, 28), + -- contract.view + -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) + (6, 29), + -- document.create_draft + (6, 30), + -- document.submit + (6, 31), + -- document.view + (6, 32), + -- document.edit + (6, 35), + -- document.attach + (6, 36), + -- correspondence.create + (6, 37), + -- rfa.create + (6, 39), + -- drawing.create + (6, 40), + -- transmittal.create + (6, 41), + -- circulation.create + (6, 38), + -- rfa.manage_shop_drawings + (6, 48), + -- search.advanced + (6, 49); +-- report.generate +-- ===================================================== +-- == 7. Contract Admin (role_id = 7) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการสัญญา + (7, 27), + -- contract.manage_members + (7, 28), + -- contract.view + -- สิทธิ์ในการอนุมัติ (ส่วนหนึ่งของ Workflow) + (7, 45), + -- workflow.action_review + -- สิทธิ์จัดการข้อมูลเฉพาะสัญญา + (7, 38), + -- rfa.manage_shop_drawings + (7, 39), + -- drawing.create + -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) + (7, 29), + -- document.create_draft + (7, 30), + -- document.submit + (7, 31), + -- document.view + (7, 32), + -- document.edit + (7, 35), + -- document.attach + (7, 36), + -- correspondence.create + (7, 37), + -- rfa.create + (7, 40), + -- transmittal.create + (7, 41), + -- circulation.create + (7, 48); +-- search.advanced +-- ตารางเชื่อมผู้ใช้ (users) +CREATE TABLE user_assignments ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + role_id INT NOT NULL, + -- คอลัมน์สำหรับกำหนดขอบเขต (จะใช้เพียงอันเดียวต่อแถว) + organization_id INT NULL, + project_id INT NULL, + contract_id INT NULL, + assigned_by_user_id INT, + -- ผู้ที่มอบหมายบทบาทนี้ + assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id), + -- Constraint เพื่อให้แน่ใจว่ามีเพียงขอบเขตเดียวที่ถูกกำหนดในแต่ละแถว + CONSTRAINT chk_scope CHECK ( + ( + organization_id IS NOT NULL + AND project_id IS NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NOT NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NOT NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NULL + ) -- สำหรับ Global scope + ) +); +CREATE TABLE project_organizations ( + project_id INT NOT NULL, + organization_id INT NOT NULL, + PRIMARY KEY (project_id, organization_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); +CREATE TABLE contract_organizations ( + contract_id INT NOT NULL, + organization_id INT NOT NULL, + role_in_contract VARCHAR(100), + -- เช่น 'Owner', 'Designer', 'Consultant', 'Contractor' + PRIMARY KEY (contract_id, organization_id), + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); +-- ===================================================== +-- == 4. การเชื่อมโยงโครงการกับองค์กร (project_organizations) == +-- ===================================================== +-- โครงการหลัก (LCBP3) จะมีองค์กรหลักๆ เข้ามาเกี่ยวข้องทั้งหมด +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + id +FROM organizations +WHERE organization_code IN ( + 'กทท.', + 'สคฉ.3', + 'TEAM', + 'คคง.', + 'ผรม.1', + 'ผรม.2', + 'ผรม.3', + 'ผรม.4', + 'EN', + 'CAR' + ); +-- โครงการย่อย (LCBP3C1) จะมีเฉพาะองค์กรที่เกี่ยวข้อง +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1' + ), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-02', 'คคง.', 'ผรม.1'); +-- ทำเช่นเดียวกันสำหรับโครงการอื่นๆ (ตัวอย่าง) +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2' + ), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-03', 'คคง.', 'ผรม.2'); +-- ===================================================== +-- == 5. การเชื่อมโยงสัญญากับองค์กร (contract_organizations) == +-- ===================================================== +-- สัญญาที่ปรึกษาออกแบบ (DSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'TEAM' + ), + 'Designer' + ); +-- สัญญาที่ปรึกษาควบคุมงาน (PSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'คคง.' + ), + 'Consultant' + ); +-- สัญญางานก่อสร้าง ส่วนที่ 1 (LCBP3-C1) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'ผรม.1' + ), + 'Contractor' + ); +-- สัญญางานก่อสร้าง ส่วนที่ 2 (LCBP3-C2) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'ผรม.2' + ), + 'Contractor' + ); +-- สัญญาตรวจสอบสิ่งแวดล้อม (ENLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'EN' + ), + 'Consultant' + ); +-- ===================================================== +-- 3. ✉️ Correspondences (เอกสารหลัก, Revisions) +-- ===================================================== +-- ตาราง Master เก็บประเภทเอกสารโต้ตอบ +CREATE TABLE correspondence_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสประเภท (เช่น RFA, RFI)', + type_name VARCHAR(255) NOT NULL COMMENT 'ชื่อประเภท', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทเอกสารโต้ตอบ'; +INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) +VALUES ('RFA', 'Request for Approval', 1, 1), + ('RFI', 'Request for Information', 2, 1), + ('TRANSMITTAL', 'Transmittal', 3, 1), + ('EMAIL', 'Email', 4, 1), + ('INSTRUCTION', 'Instruction', 5, 1), + ('LETTER', 'Letter', 6, 1), + ('MEMO', 'Memorandum', 7, 1), + ('MOM', 'Minutes of Meeting', 8, 1), + ('NOTICE', 'Notice', 9, 1), + ( + 'OTHER', + 'Other', + 10, + 1 + ); +-- ตาราง Master เก็บสถานะของเอกสาร +CREATE TABLE correspondence_status ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสถานะหนังสือ (เช่น DRAFT, SUBOWN)', + status_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสถานะหนังสือ', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะของเอกสาร'; +INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) +VALUES ('DRAFT', 'Draft', 10, 1), + ('SUBOWN', 'Submitted to Owner', 21, 1), + ('SUBDSN', 'Submitted to Designer', 22, 1), + ('SUBCSC', 'Submitted to CSC', 23, 1), + ('SUBCON', 'Submitted to Contractor', 24, 1), + ('SUBOTH', 'Submitted to Others', 25, 1), + ('REPOWN', 'Reply by Owner', 31, 1), + ('REPDSN', 'Reply by Designer', 32, 1), + ('REPCSC', 'Reply by CSC', 33, 1), + ('REPCON', 'Reply by Contractor', 34, 1), + ('REPOTH', 'Reply by Others', 35, 1), + ('RSBOWN', 'Resubmited by Owner', 41, 1), + ('RSBDSN', 'Resubmited by Designer', 42, 1), + ('RSBCSC', 'Resubmited by CSC', 43, 1), + ('RSBCON', 'Resubmited by Contractor', 44, 1), + ('CLBOWN', 'Closed by Owner', 51, 1), + ('CLBDSN', 'Closed by Designer', 52, 1), + ('CLBCSC', 'Closed by CSC', 53, 1), + ('CLBCON', 'Closed by Contractor', 54, 1), + ('CCBOWN', 'Canceled by Owner', 91, 1), + ('CCBDSN', 'Canceled by Designer', 92, 1), + ('CCBCSC', 'Canceled by CSC', 93, 1), + ('CCBCON', 'Canceled by Contractor', 94, 1); +-- ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision +CREATE TABLE correspondences ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (นี่คือ "Master ID" ที่ใช้เชื่อมโยง)', + correspondence_number VARCHAR(100) NOT NULL COMMENT 'เลขที่เอกสาร (สร้างจาก DocumentNumberingModule)', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = ภายใน, 0 = ภายนอก)', + project_id INT NOT NULL COMMENT 'อยู่ในโครงการ', + originator_id INT COMMENT 'องค์กรผู้ส่ง', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision'; +-- ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N) +CREATE TABLE correspondence_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + correspondence_status_id INT NOT NULL COMMENT 'สถานะของ Revision นี้', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATETIME COMMENT 'วันที่ออกเอกสาร', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + due_date DATETIME COMMENT 'วันที่ครบกำหนด', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + details JSON COMMENT 'ข้อมูลเฉพาะ (เช่น RFI details)', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_master_revision_number (correspondence_id, revision_number), + UNIQUE KEY uq_master_current (correspondence_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N)'; +-- ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N) +CREATE TABLE correspondence_recipients ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + recipient_organization_id INT COMMENT 'ID องค์กรผู้รับ', + recipient_type ENUM('TO', 'CC') COMMENT 'ประเภทผู้รับ (TO หรือ CC)', + PRIMARY KEY ( + correspondence_id, + recipient_organization_id, + recipient_type + ), + FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N)'; +-- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ +CREATE TABLE tags ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อ Tag', + description TEXT COMMENT 'คำอธิบายแท็ก', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ'; +-- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) +CREATE TABLE correspondence_tags ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + tag_id INT COMMENT 'ID ของ Tag', + PRIMARY KEY (correspondence_id, tag_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; +-- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) +CREATE TABLE correspondence_references ( + src_correspondence_id INT COMMENT 'ID เอกสารต้นทาง', + tgt_correspondence_id INT COMMENT 'ID เอกสารเป้าหมาย', + PRIMARY KEY (src_correspondence_id, tgt_correspondence_id), + FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)'; +-- ===================================================== +-- 4. 📐 approval: RFA (เอกสารขออนุมัติ, Workflows) +-- ===================================================== +-- 3.1 routing Config for Templates +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Logic การเดินเอกสารที่ซับซ้อนกว่า Column ปกติ +CREATE TABLE correspondence_routing_templates ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของแม่แบบ', + template_name VARCHAR(255) NOT NULL COMMENT 'ชื่อแม่แบบ', + description TEXT COMMENT 'คำอธิบาย', + project_id INT NULL COMMENT 'ID โครงการ (ถ้าเป็นแม่แบบเฉพาะโครงการ)', + -- NULL = แม่แบบทั่วไป + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + is_active BOOLEAN DEFAULT TRUE, + workflow_config JSON NULL COMMENT 'Routing Logic Configuration', + UNIQUE KEY ux_routing_template_name_project (template_name, project_id), + CONSTRAINT fk_crt_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'แม่แบบสายงานการส่งต่อเอกสารขออนุมัติ'; +CREATE TABLE correspondence_status_transitions( + type_id INT NOT NULL COMMENT 'ID ของประเภทหนังสือ', + from_status_id INT NOT NULL COMMENT 'ID ของสถานะต้นทาง', + to_status_id INT NOT NULL COMMENT 'ID ของสถานะปลายทาง', + PRIMARY KEY (type_id, from_status_id, to_status_id), + CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(id), + CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_status(id), + CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_status(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสถานะที่อนุญาตให้เปลี่ยนแปลงได้ตามประเภทหนังสือ'; +-- 1.18.1 correspondence_routing_template_steps Table +CREATE TABLE correspondence_routing_template_steps ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + sequence INT NOT NULL COMMENT 'ลำดับขั้นตอน', + to_organization_id INT NOT NULL COMMENT 'ID องค์กรณ์ผู้รับในขั้นตอนนี้', + step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้', + expected_days INT NULL, + UNIQUE KEY ux_cor_template_sequence (template_id, sequence), + CONSTRAINT fk_cwts_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE CASCADE, + CONSTRAINT fk_cwts_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ขั้นตอนในแม่แบบ Workflow การส่งต่อเอกสาร'; +-- 1.19.1 correspondence_routings +-- 3.2 State Context for Running Workflows +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Snapshot ข้อมูล ณ ขณะนั้นเพื่อใช้ตัดสินใจใน Step ถัดไป +CREATE TABLE correspondence_routings ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร(FK -> correspondence_revisions)', + template_id INT NULL COMMENT 'ID ของแม่แบบที่ใช้ (ถ้ามี)', + -- สำหรับอ้างอิงถึงแม่แบบ + sequence INT NOT NULL COMMENT 'ลำดับของขั้นตอนการส่งต่อ', + from_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้ส่ง', + to_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้รับ', + step_purpose ENUM( + 'FOR_APPROVAL', + 'FOR_REVIEW', + 'FOR_INFORMATION', + 'FOR_ACTION' + ) NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ', + status ENUM( + 'SENT', + 'RECEIVED', + 'ACTIONED', + 'FORWARDED', + 'REPLIED' + ) NOT NULL DEFAULT 'SENT' COMMENT 'สถานะการดำเนินการของเอกสารในขั้นตอนนี้', + comments TEXT COMMENT 'หมายเหตุ หรือความคิดเห็นในการส่งต่อ', + due_date DATETIME NULL COMMENT 'วันที่ต้องตอบเอกสารในขั้นตอนนี้', + processed_by_user_id INT NULL COMMENT 'ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้', + processed_at TIMESTAMP NULL COMMENT 'เวลาที่ดำเนินการ', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่สร้างขั้นตอนนี้', + state_context JSON NULL COMMENT 'Snapshot of routing state context', + UNIQUE KEY ux_cor_routing_sequence (correspondence_id, sequence), + -- Foreign Keys + CONSTRAINT fk_crs_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + CONSTRAINT fk_crs_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE + SET NULL, + CONSTRAINT fk_crs_from_org FOREIGN KEY (from_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_to_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_processed_by_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางติดตาม Workflow การส่งต่อเอกสารทั่วไป'; +-- ตาราง Master สำหรับประเภท RFA +CREATE TABLE rfa_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภท RFA (เช่น DWG, DOC, MAT)', + type_name VARCHAR(100) NOT NULL COMMENT 'ชื่อประเภท RFA', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับประเภท RFA'; +INSERT INTO rfa_types (type_code, type_name, sort_order, is_active) +VALUES ('DWG', 'Shop Drawing', 10, 1), + ('DOC', 'Document', 20, 1), + ('SPC', 'Specification', 21, 1), + ('CAL', 'Calculation', 22, 1), + ('TRP', 'Test Report', 23, 1), + ('SRY', 'Survey Report', 24, 1), + ('QAQC', 'QA/QC Document', 25, 1), + ('MES', 'Method Statement', 30, 1), + ('MAT', 'Material', 40, 1), + ('ASB', 'As-Built', 50, 1), + ('OTH', 'Other', 99, 1); +-- ตาราง Master สำหรับสถานะ RFA +CREATE TABLE rfa_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะ RFA (เช่น DFT - Draft, FAP - For Approve)', + status_name VARCHAR(100) NOT NULL COMMENT 'ชื่อสถานะ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับสถานะ RFA'; +INSERT INTO rfa_status_codes ( + status_code, + status_name, + description, + sort_order + ) +VALUES ('DFT', 'Draft', 'ฉบับร่าง', 1), + ('FAP', 'For Approve', 'เพื่อขออนุมัติ', 11), + ('FRE', 'For Review', 'เพื่อตรวจสอบ', 12), + ('FCO', 'For Construction', 'เพื่อก่อสร้าง', 20), + ('ASB', 'AS-Built', 'แบบก่อสร้างจริง', 30), + ('OBS', 'Obsolete', 'ไม่ใช้งาน', 80), + ('CC', 'Canceled', 'ยกเลิก', 99); +-- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA +CREATE TABLE rfa_approve_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)', + approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับรหัสผลการอนุมัติ RFA'; +INSERT INTO rfa_approve_codes ( + approve_code, + approve_name, + sort_order, + is_active + ) +VALUES ('1A', 'Approved by Authority', 10, 1), + ('1C', 'Approved by CSC', 11, 1), + ('1N', 'Approved As Note', 12, 1), + ('1R', 'Approved with Remarks', 13, 1), + ('3C', 'Consultant Comments', 31, 1), + ('3R', 'Revise and Resubmit', 32, 1), + ('4X', 'Reject', 40, 1), + ('5N', 'No Further Action', 50, 1); +-- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions) +CREATE TABLE rfas ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (RFA Master ID)', + rfa_type_id INT NOT NULL COMMENT 'ประเภท RFA', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)'; +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N) +CREATE TABLE rfa_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID ของ Correspondence', + rfa_id INT NOT NULL COMMENT 'Master ID ของ RFA', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + rfa_status_code_id INT NOT NULL COMMENT 'สถานะ RFA', + rfa_approve_code_id INT COMMENT 'ผลการอนุมัติ', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATE COMMENT 'วันที่ส่งขออนุมัติ', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + approved_date DATE COMMENT 'วันที่อนุมัติ', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), + FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), + UNIQUE KEY uq_rr_current (rfa_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N)'; +-- ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N) +CREATE TABLE rfa_items ( + rfarev_correspondence_id INT COMMENT 'ID ของ RFA Revision', + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + PRIMARY KEY ( + rfarev_correspondence_id, + shop_drawing_revision_id + ), + FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N)'; +-- ตาราง Master เก็บแม่แบบสายอนุมัติ +-- 3.1 Workflow Config for Templates +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Logic การเดินเอกสารที่ซับซ้อนกว่า Column ปกติ +CREATE TABLE rfa_workflow_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + workflow_config JSON NULL COMMENT 'State Machine Configuration Rules' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายอนุมัติ'; +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE rfa_workflow_template_steps ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; +-- ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน +-- 3.2 State Context for Running Workflows +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Snapshot ข้อมูล ณ ขณะนั้นเพื่อใช้ตัดสินใจใน Step ถัดไป +CREATE TABLE rfa_workflows ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + rfa_revision_id INT NOT NULL COMMENT 'ID ของ RFA Revision', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + status ENUM( + 'PENDING', + 'IN_PROGRESS', + 'COMPLETED', + 'REJECTED' + ) COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + state_context JSON NULL COMMENT 'Snapshot of workflow state context', + FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน'; +-- ===================================================== +-- 5. 📐 Drawings (แบบ, หมวดหมู่) +-- ===================================================== +-- ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_volumes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + volume_code VARCHAR(50) NOT NULL COMMENT 'รหัสเล่ม', + volume_name VARCHAR(255) NOT NULL COMMENT 'ชื่อเล่ม', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_volume_project (project_id, volume_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา'; +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่หลัก', + cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_cat_project (project_id, cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา'; +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_sub_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + sub_cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่ย่อย', + sub_cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา'; +-- ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N) +CREATE TABLE contract_drawing_subcat_cat_maps ( + project_id INT COMMENT 'ID ของโครงการ', + sub_cat_id INT COMMENT 'ID ของหมวดหมู่ย่อย', + cat_id INT COMMENT 'ID ของหมวดหมู่หลัก', + PRIMARY KEY (project_id, sub_cat_id, cat_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, + FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N)'; +-- ตาราง Master เก็บข้อมูล "แบบคู่สัญญา" +CREATE TABLE contract_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + condwg_no VARCHAR(255) NOT NULL COMMENT 'เลขที่แบบสัญญา', + title VARCHAR(255) NOT NULL COMMENT 'ชื่อแบบสัญญา', + sub_cat_id INT COMMENT 'หมวดหมู่ย่อย', + volume_id INT COMMENT 'เล่ม', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, + FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, + UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบคู่สัญญา"'; +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_main_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่หลัก (เช่น ARCH, STR)', + main_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง'; +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_sub_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ย่อย (เช่น STR-COLUMN)', + sub_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง'; +-- ตาราง Master เก็บข้อมูล "แบบก่อสร้าง" +CREATE TABLE shop_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'เลขที่ Shop Drawing', + title VARCHAR(500) NOT NULL COMMENT 'ชื่อแบบ', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + sub_category_id INT NOT NULL COMMENT 'หมวดหมู่ย่อย', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id), + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id), + FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบก่อสร้าง"'; +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N) +CREATE TABLE shop_drawing_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + shop_drawing_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (เช่น 0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + revision_date DATE COMMENT 'วันที่ของ Revision', + description TEXT COMMENT 'คำอธิบายการแก้ไข', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, + UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N)'; +-- ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N) +CREATE TABLE shop_drawing_revision_contract_refs ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N)'; +-- ===================================================== +-- 6. 🔄 Circulations (ใบเวียนภายใน) +-- ===================================================== +-- ตาราง Master เก็บสถานะใบเวียน +CREATE TABLE circulation_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะการดำเนินงาน', + description VARCHAR(50) NOT NULL COMMENT 'คำอธิบายสถานะการดำเนินงาน', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะใบเวียน'; +INSERT INTO circulation_status_codes (code, description, sort_order) +VALUES ('OPEN', 'Open', 1), + ('IN_REVIEW', 'In Review', 2), + ('COMPLETED', 'ปCompleted', 3), + ('CANCELLED', 'Cancelled/Withdrawn', 9); +-- ตาราง "แม่" ของใบเวียนเอกสารภายใน +CREATE TABLE circulations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตารางใบเวียน', + correspondence_id INT UNIQUE COMMENT 'ID ของเอกสาร (จากตาราง correspondences)', + organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ที่เป็นเจ้าของใบเวียนนี้', + circulation_no VARCHAR(100) NOT NULL COMMENT 'เลขที่ใบเวียน', + circulation_subject VARCHAR(500) NOT NULL COMMENT 'เรื่องใบเวียน', + circulation_status_code VARCHAR(20) NOT NULL COMMENT 'รหัสสถานะใบเวียน', + created_by_user_id INT NOT NULL COMMENT 'ID ของผู้สร้างใบเวียน', + submitted_at TIMESTAMP NULL COMMENT 'วันที่ส่งใบเวียน', + closed_at TIMESTAMP NULL COMMENT 'วันที่ปิดใบเวียน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id), + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code), + FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของใบเวียนเอกสารภายใน'; +-- ตาราง Master เก็บแม่แบบสายงาน +CREATE TABLE circulation_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายงาน', + description TEXT COMMENT 'คำอธิบาย', + organization_id INT NOT NULL COMMENT 'องค์กรเจ้าของแม่แบบ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (organization_id) REFERENCES organizations(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายงาน'; +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE circulation_template_assignees ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; +-- ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow +CREATE TABLE circulation_routings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + circulation_id INT NOT NULL COMMENT 'ID ของใบเวียน', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + status ENUM( + 'PENDING', + 'IN_PROGRESS', + 'COMPLETED', + 'REJECTED' + ) COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow'; +-- ===================================================== +-- 7. 📤 Transmittals (เอกสารนำส่ง) +-- ===================================================== +-- ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences) +CREATE TABLE transmittals ( + correspondence_id INT PRIMARY KEY COMMENT 'ID ของเอกสาร', + purpose ENUM( + 'FOR_APPROVAL', + 'FOR_INFORMATION', + 'FOR_REVIEW', + 'OTHER' + ) COMMENT 'วัตถุประสงค์', + remarks TEXT COMMENT 'หมายเหตุ', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences)'; +-- ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N) +CREATE TABLE transmittal_items ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของรายการ', + transmittal_id INT NOT NULL COMMENT 'ID ของ Transmittal', + item_correspondence_id INT NOT NULL COMMENT 'ID ของเอกสารที่แนบไป', + quantity INT DEFAULT 1 COMMENT 'จำนวน', + remarks VARCHAR(255) COMMENT 'หมายเหตุสำหรับรายการนี้', + FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N)'; +-- ===================================================== +-- 8. 📎 File Management (ไฟล์แนบ) +-- ===================================================== +-- ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ +-- 2.2 Attachments - Two-Phase Storage & Security +-- รองรับ: Backend Plan T2.2, Req 3.9.1 +-- เหตุผล: จัดการไฟล์ขยะ (Orphan Files) และตรวจสอบความถูกต้องไฟล์ +CREATE TABLE attachments ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของไฟล์แนบ', + original_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ดั้งเดิมตอนอัปโหลด', + stored_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ที่เก็บจริงบน Server (ป้องกันชื่อซ้ำ)', + file_path VARCHAR(500) NOT NULL COMMENT 'Path ที่เก็บไฟล์ (บน QNAP /share/dms-data/)', + mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application/pdf)', + file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)', + is_temporary BOOLEAN DEFAULT TRUE COMMENT 'True=ยังไม่ Commit ลง DB จริง', + temp_id VARCHAR(100) NULL COMMENT 'ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1' uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด', + expires_at DATETIME NULL COMMENT 'เวลาหมดอายุของไฟล์ Temp' checksum VARCHAR(64) NULL COMMENT 'SHA-256 Checksum' FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ'; +-- ตารางเชื่อม correspondences กับ attachments (M:N) +CREATE TABLE correspondence_attachments ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (correspondence_id, attachment_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม correspondences กับ attachments (M:N)'; +-- ตารางเชื่อม circulations กับ attachments (M:N) +CREATE TABLE circulation_attachments ( + circulation_id INT COMMENT 'ID ของใบเวียน', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลักของใบเวียน)', + PRIMARY KEY (circulation_id, attachment_id), + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม circulations กับ attachments (M:N)'; +-- ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N) +CREATE TABLE shop_drawing_revision_attachments ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (shop_drawing_revision_id, attachment_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N)'; +-- ตารางเชื่อม contract_drawings กับ attachments (M:N) +CREATE TABLE contract_drawing_attachments ( + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (contract_drawing_id, attachment_id), + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม contract_drawings กับ attachments (M:N)'; +-- ===================================================== +-- 9. 🔢 Document Numbering (การสร้างเลขที่เอกสาร) +-- ===================================================== +-- ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร +CREATE TABLE document_number_formats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', + description TEXT COMMENT 'คำอธิบายรูปแบบนี้', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, + UNIQUE KEY uk_project_type (project_id, correspondence_type_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; +-- ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด +-- 2.1 Document Numbering - Optimistic Locking +-- รองรับ: Backend Plan T2.3, Req 3.10.5 +-- เหตุผล: ป้องกัน Race Condition เวลาขอเลขที่เอกสารพร้อมกัน +CREATE TABLE document_number_counters ( + project_id INT COMMENT 'โครงการ', + originator_organization_id INT COMMENT 'องค์กรผู้ส่ง', + correspondence_type_id INT COMMENT 'ประเภทเอกสาร', + current_year INT COMMENT 'ปี ค.ศ. ของตัวนับ', + last_number INT DEFAULT 0 COMMENT 'เลขที่ล่าสุดที่ใช้ไปแล้ว', + version INT DEFAULT 0 NOT NULL COMMENT 'Optimistic Lock Version', + PRIMARY KEY ( + project_id, + originator_organization_id, + correspondence_type_id, + current_year + ), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด'; +-- ===================================================== +-- 10. ⚙️ System & Logs (ระบบและ Log) +-- ===================================================== +-- 1.1 JSON Schemas Registry +-- รองรับ: Backend Plan T2.5.1, Req 6.11.1 +-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized +CREATE TABLE IF NOT EXISTS json_schemas ( + id INT AUTO_INCREMENT PRIMARY KEY, + schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1, CORR_GENERIC', + version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema', + schema_definition JSON NOT NULL COMMENT 'โครงสร้าง JSON Schema (Standard Format)', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_schema_code (schema_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +-- 1.2 User Preferences +-- รองรับ: Req 5.5, 6.8.3 +-- เหตุผล: แยกการตั้งค่า Notification และ UI ออกจากตาราง Users หลัก +CREATE TABLE IF NOT EXISTS user_preferences ( + user_id INT PRIMARY KEY, + notify_email BOOLEAN DEFAULT TRUE, + notify_line BOOLEAN DEFAULT TRUE, + digest_mode BOOLEAN DEFAULT FALSE COMMENT 'รับแจ้งเตือนแบบรวม (Digest) แทน Real-time', + ui_theme VARCHAR(20) DEFAULT 'light', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_user_prefs_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +-- ตารางเก็บบันทึกการกระทำของผู้ใช้ +-- 4.1 Audit Logs Enhancements +-- รองรับ: Req 6.1 +-- เหตุผล: รองรับ Distributed Tracing และระบุความรุนแรง +CREATE TABLE audit_logs ( + audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log', + request_id VARCHAR(100) NULL COMMENT 'Trace ID linking to app logs', + user_id INT COMMENT 'ผู้กระทำ', + action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)', + severity ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') DEFAULT 'INFO', + entity_type VARCHAR(50) COMMENT 'ตาราง/โมดูล (เช่น ''rfa'', ''correspondence'')', + entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ', + details_json JSON COMMENT 'ข้อมูลบริบท', + ip_address VARCHAR(45) COMMENT 'IP Address', + user_agent VARCHAR(255) COMMENT 'User Agent', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บบันทึกการกระทำของผู้ใช้'; +-- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System) +CREATE TABLE notifications ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน', + user_id INT NOT NULL COMMENT 'ID ผู้ใช้', + title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน', + message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน', + notification_type ENUM('EMAIL', 'LINE', 'SYSTEM') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)', + is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน', + entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''', + entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)'; +-- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search) +CREATE TABLE search_indices ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี', + entity_type VARCHAR(50) NOT NULL COMMENT 'ชนิดเอนทิตี (เช่น ''correspondence'', ''rfa'')', + entity_id INT NOT NULL COMMENT 'ID ของเอนทิตี', + content TEXT NOT NULL COMMENT 'เนื้อหาที่จะค้นหา', + indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง/อัปเดตัชนี' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)'; +-- ตารางสำหรับบันทึกประวัติการสำรองข้อมูล +CREATE TABLE backup_logs ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการสำรอง', + backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', + backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', + file_size BIGINT COMMENT 'ขนาดไฟล์', + status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', + started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', + completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', + error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับบันทึกประวัติการสำรองข้อมูล'; +-- 4.2 Virtual Columns for JSON Search (ตัวอย่างสำหรับ Correspondence) +-- รองรับ: Backend Plan T2.1, Req 3.11.3 +-- เหตุผล: เพิ่มความเร็วในการ Search/Sort ข้อมูลที่อยู่ใน JSON details +-- หมายเหตุ: ต้องมั่นใจว่า MariaDB เวอร์ชัน 10.11+ รองรับ Syntax นี้ +-- ตัวอย่าง: ดึง Project ID ที่อ้างอิงใน details ออกมาทำ Index +ALTER TABLE correspondence_revisions +ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId')) + ) VIRTUAL, + ADD INDEX idx_corr_rev_v_project (v_ref_project_id); +-- ตัวอย่าง: ดึง Document Type ย่อยจาก details +ALTER TABLE correspondence_revisions +ADD COLUMN v_doc_subtype VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))) VIRTUAL, + ADD INDEX idx_corr_rev_v_subtype (v_doc_subtype); +-- ทำแบบเดียวกันกับ RFA Revisions หากมีการเก็บ JSON details +ALTER TABLE rfa_revisions +ADD COLUMN details JSON NULL COMMENT 'RFA Specific Details' +AFTER description; +ALTER TABLE rfa_revisions +ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount')) + ) VIRTUAL; +-- ============================================================ +-- 5. PARTITIONING PREPARATION (Advance - Optional) +-- ============================================================ +-- หมายเหตุ: การทำ Partitioning บนตารางที่มีอยู่แล้ว (audit_logs, notifications) +-- มักจะต้อง Drop Primary Key เดิม แล้วสร้างใหม่โดยรวม Partition Key (created_at) เข้าไป +-- ขั้นตอนนี้ควรทำแยกต่างหากเมื่อระบบเริ่มมีข้อมูลเยอะ หรือทำใน Maintenance Window +-- +-- ตัวอย่าง SQL สำหรับ Audit Logs (Reference Only): +-- ALTER TABLE audit_logs DROP PRIMARY KEY, ADD PRIMARY KEY (audit_id, created_at); +-- ALTER TABLE audit_logs PARTITION BY RANGE (YEAR(created_at)) ( +-- PARTITION p2024 VALUES LESS THAN (2025), +-- PARTITION p2025 VALUES LESS THAN (2026), +-- PARTITION p_future VALUES LESS THAN MAXVALUE +-- ); +-- ===================================================== +-- CREATE INDEXES +-- ===================================================== +-- Indexes for document_number_formats +CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); +CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); +CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); +-- Indexes for document_number_counters +CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); +CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); +CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); +CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); +-- Indexes for tags +CREATE INDEX idx_tags_name ON tags(tag_name); +CREATE INDEX idx_tags_created_at ON tags(created_at); +-- Indexes for correspondence_tags +CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); +CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); +-- Indexes for audit_logs +CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); +CREATE INDEX idx_audit_logs_action ON audit_logs(action); +CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); +CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); +CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); +-- Indexes for notifications +CREATE INDEX idx_notifications_user ON notifications(user_id); +CREATE INDEX idx_notifications_type ON notifications(notification_type); +CREATE INDEX idx_notifications_read ON notifications(is_read); +CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); +CREATE INDEX idx_notifications_created_at ON notifications(created_at); +-- Indexes for search_indices +CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); +CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); +-- Indexes for backup_logs +CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); +CREATE INDEX idx_backup_logs_status ON backup_logs(status); +CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); +CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); +-- ===================================================== +-- Additional Composite Indexes for Performance +-- ===================================================== +-- Composite index for document_number_counters for faster lookups +CREATE INDEX idx_doc_counter_composite ON document_number_counters( + project_id, + originator_organization_id, + correspondence_type_id, + current_year +); +-- Composite index for notifications for user-specific queries +CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); +-- Composite index for audit_logs for reporting +CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); +-- Composite index for search_indices for entity-based queries +CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); +-- สร้าง Index สำหรับ Cleanup Job +CREATE INDEX idx_attachments_temp_cleanup ON attachments(is_temporary, expires_at); +CREATE INDEX idx_attachments_temp_id ON attachments(temp_id); +CREATE INDEX idx_audit_request_id ON audit_logs(request_id); +-- ===================================================== +-- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB +-- Generated from Data Dictionary +-- ===================================================== +-- ===================================================== +-- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์) +-- ===================================================== +-- Stored Procedure ดึงเลขที่เอกสารถัดไป +DELIMITER // CREATE PROCEDURE sp_get_next_document_number( + IN p_project_id INT, + IN p_originator_organization_id INT, + IN p_correspondence_type_id INT, + IN p_current_year INT, + OUT p_next_number INT +) BEGIN +DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN -- หากเกิดข้อผิดพลาด ให้ยกเลิก Transaction และส่ง Error กลับไป + ROLLBACK; +END; +START TRANSACTION; +-- ล็อกแถวเพื่อป้องกัน Race Condition +SELECT last_number INTO p_next_number +FROM document_number_counters +WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year FOR +UPDATE; +-- ถ้าไม่พบ record ให้สร้างใหม่ +IF p_next_number IS NULL THEN +SET p_next_number = 1; +INSERT INTO document_number_counters ( + project_id, + originator_organization_id, + correspondence_type_id, + current_year, + last_number + ) +VALUES ( + p_project_id, + p_originator_organization_id, + p_correspondence_type_id, + p_current_year, + p_next_number + ); +ELSE -- อัพเดทเลขที่ล่าสุด +SET p_next_number = p_next_number + 1; +UPDATE document_number_counters +SET last_number = p_next_number +WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year; +END IF; +COMMIT; +END // DELIMITER; +-- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA) +CREATE VIEW v_current_correspondences AS +SELECT c.id AS correspondence_id, + c.correspondence_number, + c.correspondence_type_id, + ct.type_code AS correspondence_type_code, + ct.type_name AS correspondence_type_name, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_code AS originator_code, + org.organization_name AS originator_name, + cr.id AS revision_id, + cr.revision_number, + cr.revision_label, + cr.title, + cr.document_date, + cr.issued_date, + cr.received_date, + cr.due_date, + cr.correspondence_status_id, + cs.status_code, + cs.status_name, + cr.created_by, + u.username AS created_by_username, + cr.created_at AS revision_created_at +FROM correspondences c + INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN organizations org ON c.originator_id = org.id + INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id + LEFT JOIN users u ON cr.created_by = u.user_id +WHERE cr.is_current = TRUE + AND c.correspondence_type_id NOT IN ( + SELECT id + FROM correspondence_types + WHERE type_code = 'RFA' + ) + AND c.deleted_at IS NULL; +-- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด +CREATE VIEW v_current_rfas AS +SELECT r.id AS rfa_id, + r.rfa_type_id, + rt.type_code AS rfa_type_code, + rt.type_name AS rfa_type_name, + rr.correspondence_id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_name AS originator_name, + rr.id AS revision_id, + rr.revision_number, + rr.revision_label, + rr.title, + rr.document_date, + rr.issued_date, + rr.received_date, + rr.approved_date, + rr.rfa_status_code_id, + rsc.status_code AS rfa_status_code, + rsc.status_name AS rfa_status_name, + rr.rfa_approve_code_id, + rac.approve_code AS rfa_approve_code, + rac.approve_name AS rfa_approve_name, + rr.created_by, + u.username AS created_by_username, + rr.created_at AS revision_created_at +FROM rfas r + INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id + INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id + INNER JOIN correspondences c ON rr.correspondence_id = c.id + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN organizations org ON c.originator_id = org.id + INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id + LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id + LEFT JOIN users u ON rr.created_by = u.user_id +WHERE rr.is_current = TRUE + AND r.deleted_at IS NULL + AND c.deleted_at IS NULL; +-- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization +CREATE VIEW v_contract_parties_all AS +SELECT c.id AS contract_id, + c.contract_code, + c.contract_name, + p.id AS project_id, + p.project_code, + p.project_name, + o.id AS organization_id, + o.organization_code, + o.organization_name, + co.role_in_contract +FROM contracts c + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN contract_organizations co ON c.id = co.contract_id + INNER JOIN organizations o ON co.organization_id = o.id +WHERE c.is_active = TRUE; +-- View แสดงรายการ "งานของฉัน" (My Tasks) ที่ยังไม่เสร็จ +CREATE VIEW v_user_tasks AS +SELECT cr.id AS routing_id, + c.id AS circulation_id, + c.circulation_no, + c.circulation_subject, + c.correspondence_id, + corr.correspondence_number, + corr.project_id, + p.project_code, + p.project_name, + cr.assigned_to AS user_id, + u.username, + u.first_name, + u.last_name, + cr.organization_id, + org.organization_name, + cr.step_number, + cr.status AS task_status, + cr.comments, + cr.completed_at, + cr.created_at AS assigned_at, + c.created_at AS circulation_created_at +FROM circulation_routings cr + INNER JOIN circulations c ON cr.circulation_id = c.id + INNER JOIN correspondences corr ON c.correspondence_id = corr.id + INNER JOIN projects p ON corr.project_id = p.id + INNER JOIN organizations org ON cr.organization_id = org.id + INNER JOIN users u ON cr.assigned_to = u.user_id +WHERE cr.status IN ('PENDING', 'IN_PROGRESS') + AND cr.assigned_to IS NOT NULL; +-- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ +CREATE VIEW v_audit_log_details AS +SELECT al.audit_id, + al.user_id, + u.username, + u.email, + u.first_name, + u.last_name, + al.action, + al.entity_type, + al.entity_id, + al.details_json, + al.ip_address, + al.user_agent, + al.created_at +FROM audit_logs al + LEFT JOIN users u ON al.user_id = u.user_id; +-- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน +CREATE VIEW v_user_all_permissions AS -- Global Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'GLOBAL' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Global scope +WHERE p.is_active = 1 + AND ua.organization_id IS NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Organization-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'ORGANIZATION' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Organization scope +WHERE p.is_active = 1 + AND ua.organization_id IS NOT NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Project-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + NULL AS contract_id, + 'PROJECT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Project scope +WHERE p.is_active = 1 + AND ua.project_id IS NOT NULL + AND ua.contract_id IS NULL +UNION ALL +-- Contract-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + ua.contract_id, + 'CONTRACT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Contract scope +WHERE p.is_active = 1 + AND ua.contract_id IS NOT NULL; +-- ===================================================== +-- Additional Useful Views +-- ===================================================== +-- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ +CREATE VIEW v_documents_with_attachments AS +SELECT 'CORRESPONDENCE' AS document_type, + c.id AS document_id, + c.correspondence_number AS document_number, + c.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM correspondences c + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +WHERE c.deleted_at IS NULL +GROUP BY c.id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CIRCULATION' AS document_type, + circ.id AS document_id, + circ.circulation_no AS document_number, + corr.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM circulations circ + INNER JOIN correspondences corr ON circ.correspondence_id = corr.id + INNER JOIN projects p ON corr.project_id = p.id + LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +GROUP BY circ.id, + circ.circulation_no, + corr.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'SHOP_DRAWING' AS document_type, + sdr.id AS document_id, + sd.drawing_number AS document_number, + sd.project_id, + p.project_code, + p.project_name, + COUNT(sdra.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM shop_drawing_revisions sdr + INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id + INNER JOIN projects p ON sd.project_id = p.id + LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id + LEFT JOIN attachments a ON sdra.attachment_id = a.id +WHERE sd.deleted_at IS NULL +GROUP BY sdr.id, + sd.drawing_number, + sd.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CONTRACT_DRAWING' AS document_type, + cd.id AS document_id, + cd.condwg_no AS document_number, + cd.project_id, + p.project_code, + p.project_name, + COUNT(cda.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM contract_drawings cd + INNER JOIN projects p ON cd.project_id = p.id + LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id + LEFT JOIN attachments a ON cda.attachment_id = a.id +WHERE cd.deleted_at IS NULL +GROUP BY cd.id, + cd.condwg_no, + cd.project_id, + p.project_code, + p.project_name; +-- View แสดงสถิติเอกสารตามประเภทและสถานะ +CREATE VIEW v_document_statistics AS +SELECT p.id AS project_id, + p.project_code, + p.project_name, + ct.id AS correspondence_type_id, + ct.type_code, + ct.type_name, + cs.id AS status_id, + cs.status_code, + cs.status_name, + COUNT(DISTINCT c.id) AS document_count, + COUNT(DISTINCT cr.id) AS revision_count +FROM projects p + CROSS JOIN correspondence_types ct + CROSS JOIN correspondence_status cs + LEFT JOIN correspondences c ON p.id = c.project_id + AND ct.id = c.correspondence_type_id + LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + AND cs.id = cr.correspondence_status_id + AND cr.is_current = TRUE +WHERE p.is_active = 1 + AND ct.is_active = 1 + AND cs.is_active = 1 +GROUP BY p.id, + p.project_code, + p.project_name, + ct.id, + ct.type_code, + ct.type_name, + cs.id, + cs.status_code, + cs.status_name; +-- ===================================================== +-- Indexes for View Performance Optimization +-- ===================================================== +-- Indexes for v_current_correspondences performance +CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); +CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); +CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); +-- Indexes for v_current_rfas performance +CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); +CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); +-- Indexes for v_user_tasks performance +CREATE INDEX idx_circulation_routings_status_assigned ON circulation_routings(status, assigned_to); +CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings(circulation_id, status); +-- Indexes for document statistics performance +CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); +CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); +SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/Documnets/Project/0_Requirements_V1_4_2.md b/Documnets/Project/0_Requirements_V1_4_2.md new file mode 100644 index 0000000..163b0d7 --- /dev/null +++ b/Documnets/Project/0_Requirements_V1_4_2.md @@ -0,0 +1,763 @@ +# 📝 **Documents Management System Version 1.4.2: Application Requirements Specification** + +**สถานะ:** FINAL +**วันที่:** 2025-11-19 +**อ้างอิงพื้นฐาน:** v1.4.1 และผลการ Review สถาปัตยกรรม +**Classification:** Internal Technical Documentation + +## 📌 **1. วัตถุประสงค์** + +สร้างเว็บแอปพลิเคชันสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System - DMS) แบบครบวงจร ที่เน้นความปลอดภัยสูงสุด ความถูกต้องของข้อมูล (Data Integrity) และรองรับการขยายตัวในอนาคต (Scalability) โดยแก้ไขปัญหา Race Condition และเพิ่มความเสถียรในการจัดการไฟล์และ Workflow + +- มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร +- ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล +- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ +- **เสริม:** ปรับปรุงความปลอดภัยของระบบด้วยมาตรการป้องกันที่ทันสมัย +- **เสริม:** เพิ่มความทนทานของระบบด้วยกลไก resilience patterns +- **เสริม:** สร้างระบบ monitoring และ observability ที่ครอบคลุม + +## 🛠️ **2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)** + +ใช้สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา + +**Domain:** `np-dms.work`, `www.np-dms.work` +**IP:** 159.192.126.103 +**Docker Network:** ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ `lcbp3` เพื่อให้สามารถสื่อสารกันได้ + +### **2.1 Infrastructure & Environment:** + +- **Server:** QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B) +- **Containerization:** Container Station (Docker & Docker Compose) ใช้ UI ของ Container Station เป็นหลัก ในการ configuration และการรัน docker command +- **Development Environment:** VS Code/Cursor on Windows 11 +- **Data Storage:** `/share/dms-data` บน QNAP +- **ข้อจำกัด:** ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น + +### **2.2 การจัดการ Configuration (ปรับปรุง):** + +- ใช้ `docker-compose.yml` สำหรับ environment variables ตามข้อจำกัดของ QNAP +- **Secrets Management (ใหม่):** + - ห้ามระบุ Sensitive Secrets (Password, Keys) ใน `docker-compose.yml` หลัก + - ต้องใช้ไฟล์ `docker-compose.override.yml` (ที่ถูก gitignore) สำหรับ Inject Environment Variables ที่เป็นความลับในแต่ละ Environment (Dev/Prod) + - ไฟล์ `docker-compose.yml` หลักให้ใส่ค่า Dummy หรือว่างไว้ +- **แต่ต้องมี mechanism สำหรับจัดการ sensitive secrets อย่างปลอดภัย** โดยใช้: + - Docker secrets (ถ้ารองรับ) + - External secret management (Hashicorp Vault) หรือ + - Encrypted environment variables +- Development environment ยังใช้ .env ได้ แต่ต้องไม่ commit เข้า version control +- ต้องมี configuration validation during application startup +- ต้องแยก configuration ตาม environment (development, staging, production) + +### **2.3 Core Services:** + +- **Code Hosting:** Gitea (Self-hosted on QNAP) + - Application name: git + - Service name: gitea + - Domain: `git.np-dms.work` + - หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน + +- **Backend / Data Platform:** NestJS + - Application name: lcbp3-backend + - Service name: backend + - Domain: `backend.np-dms.work` + - Framework: NestJS (Node.js, TypeScript, ESM) + - หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ + +- **Database:** MariaDB 10.11 + - Application name: lcbp3-db + - Service name: mariadb + - Domain: `db.np-dms.work` + - หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด + - Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล + +- **Database Management:** phpMyAdmin + - Application name: lcbp3-db + - Service: phpmyadmin:5-apache + - Service name: pma + - Domain: `pma.np-dms.work` + - หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI + +- **Frontend:** Next.js + - Application name: lcbp3-frontend + - Service name: frontend + - Domain: `lcbp3.np-dms.work` + - Framework: Next.js (App Router, React, TypeScript, ESM) + - Styling: Tailwind CSS + PostCSS + - Component Library: shadcn/ui + - หน้าที่: สร้างหน้าตาเว็บแอปพลิเคชันสำหรับให้ผู้ใช้งานเข้ามาดู Dashboard, จัดการเอกสาร, และติดตามงาน โดยจะสื่อสารกับ Backend ผ่าน API + +- **Workflow Automation:** n8n + - Application name: lcbp3-n8n + - Service: n8nio/n8n:latest + - Service name: n8n + - Domain: `n8n.np-dms.work` + - หน้าที่: จัดการ workflow ระหว่าง Backend และ Line + +- **Reverse Proxy:** Nginx Proxy Manager + - Application name: lcbp3-npm + - Service: Nginx Proxy Manager (nginx-proxy-manage: latest) + - Service name: npm + - Domain: `npm.np-dms.work` + - หน้าที่: เป็นด่านหน้าในการรับ-ส่งข้อมูล จัดการโดเมนทั้งหมด, ทำหน้าที่เป็น Proxy ชี้ไปยัง Service ที่ถูกต้อง, และจัดการ SSL Certificate (HTTPS) ให้อัตโนมัติ + +- **Search Engine:** Elasticsearch +- **Cache:** Redis + +### **2.4 Business Logic & Consistency (ปรับปรุง):** + +- **2.4.1 ตรรกะทางธุรกิจที่ซับซ้อนทั้งหมด** (เช่น การเปลี่ยนสถานะ Workflow [cite: 3.5.4, 3.6.5], การบังคับใช้สิทธิ์ [cite: 4.4], การตรวจสอบ Deadline [cite: 3.2.5]) **จะถูกจัดการในฝั่ง Backend (NestJS)** [cite: 2.3] เพื่อให้สามารถบำรุงรักษาและทดสอบได้ง่าย (Testability) + +- **2.4.2 Unified Workflow Engine (ใหม่):** รวม Logic การเดินเอกสารของ `CorrespondenceRouting` และ `RfaWorkflow` ให้ใช้ Core Engine เดียวกันเพื่อลดความซ้ำซ้อนและง่ายต่อการบำรุงรักษา + +- **2.4.3 Idempotency Keys (ใหม่):** API ที่สำคัญ (เช่น Submit Document, Approve) ต้องบังคับส่ง Header `Idempotency-Key` เพื่อป้องกันการทำรายการซ้ำจากการกดปุ่มรัวๆ หรือ Network Retry + +- **2.4.4 Optimistic Locking (ใหม่):** ใช้ Version Column ใน Database ควบคู่กับ Redis Lock สำหรับการสร้างเลขที่เอกสาร เพื่อเป็น Safety Net ชั้นสุดท้าย + +- **2.4.5** **จะไม่มีการใช้ SQL Triggers** เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก + +### **2.5 Data Migration และ Schema Versioning:** + +- ต้องมี database migration scripts สำหรับทุก schema change โดยใช้ TypeORM migrations +- ต้องรองรับ rollback ของ migration ได้ +- ต้องมี data seeding strategy สำหรับ environment ต่างๆ (development, staging, production) +- ต้องมี version compatibility between schema versions +- Migration scripts ต้องผ่านการทดสอบใน staging environment ก่อน production +- ต้องมี database backup ก่อนทำ migration ใน production + +### **2.6 กลยุทธ์ความทนทานและการจัดการข้อผิดพลาด (Resilience & Error Handling Strategy)** + +- 2.6.1 Circuit Breaker Pattern: ใช้สำหรับ external service calls (Email, LINE, Elasticsearch) +- 2.6.2 Retry Mechanism: ด้วย exponential backoff สำหรับ transient failures +- 2.6.3 Fallback Strategies: Graceful degradation เมื่อบริการภายนอกล้มเหลว +- 2.6.4 Error Handling: Error messages ต้องไม่เปิดเผยข้อมูล sensitive +- 2.6.5 Monitoring: Centralized error monitoring และ alerting system + +## **📦 3. ข้อกำหนดด้านฟังก์ชันการทำงาน (Functional Requirements)** + +### **3.1. การจัดการโครงสร้างโครงการและองค์กร** + +- 3.1.1. โครงการ (Projects): ระบบต้องสามารถจัดการเอกสารภายในหลายโครงการได้ (ปัจจุบันมี 4 โครงการ และจะเพิ่มขึ้นในอนาคต) +- 3.1.2. สัญญา (Contracts): ระบบต้องสามารถจัดการเอกสารภายในแต่ละสัญญาได้ ในแต่ละโครงการ มีได้หลายสัญญา หรืออย่างน้อย 1 สัญญา +- 3.1.3. องค์กร (Organizations): + - มีหลายองค์กรในโครงการ องค์กรณ์ที่เป็น Owner, Designer และ Consultant สามารถอยู่ในหลายโครงการและหลายสัญญาได้ + - Contractor จะถือ 1 สัญญา และอยู่ใน 1 โครงการเท่านั้น + +### **3.2. การจัดการเอกสารโต้ตอบ (Correspondence Management)** + +- 3.2.1. วัตถุประสงค์: เอกสารโต้ตอบ (correspondences) ระหว่างองกรณื-องกรณ์ ภายใน โครงการ (Projects) และระหว่าง องค์กร-องค์กร ภายนอก โครงการ (Projects), รองรับ To (ผู้รับหลัก) และ CC (ผู้รับสำเนา) หลายองค์กร +- 3.2.2. ประเภทเอกสาร: ระบบต้องรองรับเอกสารรูปแบบ ไฟล์ PDF หลายประเภท (Types) เช่น จดหมาย (Letter), อีเมล์ (Email), Request for Information (RFI), และสามารถเพิ่มประเภทใหม่ได้ในภายหลัง +- 3.2.3. การสร้างเอกสาร (Correspondence): + - ผู้ใช้ที่มีสิทธิ์ (เช่น Document Control) สามารถสร้างเอกสารรอไว้ในสถานะ ฉบับร่าง" (Draft) ได้ ซึ่งผู้ใช้งานต่างองค์กรจะมองไม่เห็น + - เมื่อกด "Submitted" แล้ว การแก้ไข, ถอนเอกสารกลับไปสถานะ Draft, หรือยกเลิก (Cancel) จะต้องทำโดยผู้ใช้ระดับ Admin ขึ้นไป พร้อมระบุเหตุผล +- 3.2.4. การอ้างอิงและจัดกลุ่ม: + - เอกสารสามารถอ้างถึง (Reference) เอกสารฉบับก่อนหน้าได้หลายฉบับ + - สามารถกำหนด Tag ได้หลาย Tag เพื่อจัดกลุ่มและใช้ในการค้นหาขั้นสูง +- 3.2.5. Correspondence Routing & Workflow + - 3.2.5.1 Routing Templates (แม่แบบการส่งต่อ) + - ผู้ดูแลระบบต้องสามารถสร้างแม่แบบการส่งต่อได้ + - แม่แบบสามารถเป็นแบบทั่วไป (ใช้ได้ทุกโครงการ) หรือเฉพาะโครงการ + - แต่ละแม่แบบประกอบด้วยลำดับขั้นตอนการส่งต่อ + - การส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Wouting ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) + - 3.2.5.2 Routing Steps (ขั้นตอนการส่งต่อ) แต่ละขั้นตอนในแม่แบบต้องกำหนด: + - **ลำดับขั้นตอน** (Sequence) + - **องค์กรผู้รับ** (To Organization) + - **วัตถุประสงค์** (Purpose): เพื่ออนุมัติ (FOR_APPROVAL), เพื่อตรวจสอบ (FOR_REVIEW), เพื่อทราบ (FOR_INFORMATION), เพื่อดำเนินการ (FOR_ACTION) + - **ระยะเวลาที่คาดหวัง** (Expected Duration) + - 3.2.5.3 Actual Routing Execution (การส่งต่อจริง) เมื่อสร้างเอกสารและเลือกใช้แม่แบบ ระบบต้อง: + - สร้างลำดับการส่งต่อตามแม่แบบ + - ติดตามสถานะของแต่ละขั้นตอน: ส่งแล้ว (SENT), กำลังดำเนินการ (IN_PROGRESS), ดำเนินการแล้ว (ACTIONED), ส่งต่อแล้ว (FORWARDED), ตอบกลับแล้ว (REPLIED) + - ระบุวันครบกำหนด (Due Date) สำหรับแต่ละขั้นตอน + - บันทึกผู้ดำเนินการและเวลาที่ดำเนินการ + - 3.2.5.4 Routing Flexibility (ความยืดหยุ่น) + - สามารถข้ามขั้นตอนได้ในกรณีพิเศษ (โดยผู้มีสิทธิ์) + - สามารถส่งกลับขั้นตอนก่อนหน้าได้ + - สามารถเพิ่มความคิดเห็นในแต่ละขั้นตอน + - แจ้งเตือนอัตโนมัติเมื่อถึงขั้นตอนใหม่หรือใกล้ครบกำหนด +- 3.2.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้ + - สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่เป็นผู้รับได้ + - มีระบบแจ้งเตือน ให้ผู้รับผิดชอบขององกรณ์ที่เป็น ผู้รับ/ผู้ส่ง ทราบ เมื่อมีเอกสารใหม่ หรือมีการเปลี่ยนสถานะ + +### **3.3. การจัดกาแบบคู่สัญญา (Contract Drawing)** + +- 3.3.1. วัตถุประสงค์: แบบคู่สัญญา (Contract Drawing) ใช้เพื่ออ้างอิงและใช้ในการตรวจสอบ +- 3.3.2. ประเภทเอกสาร: ไฟล์ PDF +- 3.3.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้ +- 3.3.4. การอ้างอิงและจัดกลุ่ม: ใช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Contract Drawing + +### **3.4. การจัดกาแบบก่อสร้าง (Shop Drawing)** + +- 3.4.1. วัตถุประสงค์: แบบก่อสร้าง (Shop Drawing) ใช้เในการตรวจสอบ โดยจัดส่งด้วย Request for Approval (RFA) +- 3.4.2. ประเภทเอกสาร: ไฟล์ PDF +- 3.4.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้ +- 3.4.4. การอ้างอิงและจัดกลุ่ม: ช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Shop Drawings + +### **3.5. การจัดการเอกสารขออนุมัติ (Request for Approval & Workflow)** + +- 3.5.1. วัตถุประสงค์: เอกสารขออนุมัติ (Request for Approval) ใช้ในการส่งเอกสารเพิอขออนุมัติ +- 3.5.2. ประเภทเอกสาร: Request for Approval (RFA) เป็นชนิดหนึ่งของ Correspondence ที่มีลักษณะเฉพาะที่ต้องได้รับการอนุมัติ มีประเภทดังนี้: + - Request for Drawing Approval (RFA_DWG) + - Request for Document Approval (RFA_DOC) + - Request for Method statement Approval (RFA_MES) + - Request for Material Approval (RFA_MAT) +- 3.5.2. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้ +- 3.5.4. การอ้างอิงและจัดกลุ่ม: การจัดการ Drawing (RFA_DWG): + - เอกสาร RFA_DWG จะประกอบไปด้วย Shop Drawing (shop_drawings) หลายแผ่น ซึ่งแต่ละแผ่นมี Revision ของตัวเอง + - Shop Drawing แต่ละ Revision สามารถอ้างอิงถึง Contract Drawing (Ccontract_drawings) หลายแผ่น หรือไม่อ้างถึงก็ได้ + - ระบบต้องมีส่วนสำหรับจัดการข้อมูล Master Data ของทั้ง Shop Drawing และ Contract Drawing แยกจากกัน +- 3.5.5. Workflow การอนุมัติ: ต้องรองรับกระบวนการอนุมัติที่ซับซ้อนและเป็นลำดับ เช่น + - ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Workflow ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) +- 3.5.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้ + - สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ได้ + - มีระบบแจ้งเตือน ให้ผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ทราบ เมื่อมี RFA ใหม่ หรือมีการเปลี่ยนสถานะ + +### **3.6.การจัดการเอกสารนำส่ง (Transmittals)** + +- 3.6.1. วัตถุประสงค์: เอกสารนำส่ง ใช้สำหรับ นำส่ง Request for Approval (RFAS) หลายฉบับ ไปยังองค์กรอื่น +- 3.6.2. ประเภทเอกสาร: ไฟล์ PDF +- 3.6.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้ +- 3.6.4. การอ้างอิงและจัดกลุ่ม: เอกสารนำส่ง เป็นส่วนหนึ่งใน Correspondence + +### **3.7. ใบเวียนเอกสาร (Circulation Sheet)** + +- 3.7.1. วัตถุประสงค์: การสื่อสาร เอกสาร (Correspondence) ทุกฉบับ จะมีใบเวียนเอกสารเพื่อควบคุมและมอบหมายงานภายในองค์กร (สามารถดูและแก้ไขได้เฉพาะคนในองค์กร) +- 3.7.2. ประเภทเอกสาร: ไฟล์ PDF +- 3.7.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ในองค์กรนั้น สามารถสร้างและแก้ไขได้ +- 3.7.4. การอ้างอิงและจัดกลุ่ม: การระบุผู้รับผิดชอบ: + - ผู้รับผิดชอบหลัก (Main): มีได้หลายคน + - ผู้ร่วมปฏิบัติงาน (Action): มีได้หลายคน + - ผู้ที่ต้องรับทราบ (Information): มีได้หลายคน +- 3.7.5. การติดตามงาน: + - สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบประเภท Main และ Action ได้ + - มีระบบแจ้งเตือนเมื่อมี Circulation ใหม่ และแจ้งเตือนล่วงหน้าก่อนถึงวันแล้วเสร็จ + - สามารถปิด Circulation ได้เมื่อดำเนินการตอบกลับไปยังองค์กรผู้ส่ง (Originator) แล้ว หรือ รับทราบแล้ว (For Information) + +### **3.8. ประวัติการแก้ไข (Revisions):** ระบบจะเก็บประวัติการสร้างและแก้ไข เอกสารทั้งหมด + +### **3.9. การจัดเก็บไฟล์ (File Handling - ปรับปรุงใหญ่)** + +- **3.9.1 Two-Phase Storage Strategy:** + 1. **Phase 1 (Upload):** ไฟล์ถูกอัปโหลดเข้าโฟลเดอร์ `temp/` และได้รับ `temp_id` + 2. **Phase 2 (Commit):** เมื่อ User กด Submit ฟอร์มสำเร็จ ระบบจะย้ายไฟล์จาก `temp/` ไปยัง `permanent/{YYYY}/{MM}/` และบันทึกลง Database ภายใน Transaction เดียวกัน + 3. **Cleanup:** มี Cron Job ลบไฟล์ใน `temp/` ที่ค้างเกิน 24 ชม. (Orphan Files) + +- **3.9.2 Security:** + - Virus Scan (ClamAV) ก่อนย้ายเข้า Permanent + - Whitelist File Types: PDF, DWG, DOCX, XLSX, ZIP + - Max Size: 50MB + - Access Control: ตรวจสอบสิทธิ์ผ่าน Junction Table ก่อนให้ Download Link + +- **3.9.3 ความปลอดภัยของการจัดเก็บไฟล์:** + - ต้องมีการ scan virus สำหรับไฟล์ที่อัปโหลดทั้งหมด โดยใช้ ClamAV หรือบริการ third-party + - จำกัดประเภทไฟล์ที่อนุญาต: PDF, DWG, DOCX, XLSX, ZIP (ต้องระบุรายการที่ชัดเจน) + - ขนาดไฟล์สูงสุด: 50MB ต่อไฟล์ + - ไฟล์ต้องถูกเก็บนอก web root และเข้าถึงได้ผ่าน authenticated endpoint เท่านั้น + - ต้องมี file integrity check (checksum) เพื่อป้องกันการแก้ไขไฟล์ + - Download links ต้องมี expiration time (default: 24 ชั่วโมง) + - ต้องบันทึก audit log ทุกครั้งที่มีการดาวน์โหลดไฟล์สำคัญ + +### **3.10. การจัดการเลขที่เอกสาร (Document Numbering - ปรับปรุง)** + +- 3.10.1. ระบบต้องสามารถสร้างเลขที่เอกสาร (เช่น correspondence_number) ได้โดยอัตโนมัติ +- 3.10.2. การนับเลข Running Number (SEQ) จะต้องนับแยกตาม Key ดังนี้: **โครงการ (Project)**, **องค์กรผู้ส่ง (Originator Organization)**, **ประเภทเอกสาร (Document Type)** และ **ปีปัจจุบัน (Year)** +- 3.10.3. ผู้ดูแลระบบ (Admin) ต้องสามารถกำหนด "รูปแบบ" (Format Template) ของเลขที่เอกสารได้ (เช่น {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}) โดยกำหนดแยกตามโครงการและประเภทเอกสาร +- 3.10.4. **กลไก:** ใช้ **Redis Distributed Lock** เป็นด่านแรก +- 3.10.5. **Safety Net:** เพิ่ม **Optimistic Locking** (ตรวจสอบ Version/Last Number ใน DB ขณะ Update) เพื่อป้องกันกรณี Redis ล่ม หรือ Race Condition หลุดรอด +- 3.10.6. ต้องมี retry mechanism และ fallback strategy เมื่อการ generate เลขที่เอกสารล้มเหลว + +### **3.11 การจัดการ JSON Details (JSON & Performance - ปรับปรุง)** + +- **3.11.1 วัตถุประสงค์** + - จัดเก็บข้อมูลแบบไดนามิกที่เฉพาะเจาะจงกับแต่ละประเภทของเอกสาร + - รองรับการขยายตัวของระบบโดยไม่ต้องเปลี่ยนแปลง database schema + - จัดการ metadata และข้อมูลประกอบสำหรับ correspondence, routing, และ workflows + +- **3.11.2 โครงสร้าง JSON Schema** + ระบบต้องมี predefined JSON schemas สำหรับประเภทเอกสารต่างๆ: + - **3.11.2.1 Correspondence Types** + - **GENERIC**: ข้อมูลพื้นฐานสำหรับเอกสารทั่วไป + - **RFI**: รายละเอียดคำถามและข้อมูลทางเทคนิค + - **RFA**: ข้อมูลการขออนุมัติแบบและวัสดุ + - **TRANSMITTAL**: รายการเอกสารที่ส่งต่อ + - **LETTER**: ข้อมูลจดหมายทางการ + - **EMAIL**: ข้อมูลอีเมล + - **3.11.2.2 Routing Types** + - **ROUTING_TEMPLATE**: กฎและเงื่อนไขการส่งต่อ + - **ROUTING_INSTANCE**: สถานะและประวัติการส่งต่อ + - **ROUTING_ACTION**: การดำเนินการในแต่ละขั้นตอน + - **3.11.2.3 Audit Types** + - **AUDIT_LOG**: ข้อมูลการตรวจสอบ + - **SECURITY_SCAN**: ผลการตรวจสอบความปลอดภัย + +- **3.11.3 Virtual Columns (ใหม่):** สำหรับ Field ใน JSON ที่ต้องใช้ในการค้นหา (Search) หรือจัดเรียง (Sort) บ่อยๆ **ต้องสร้าง Generated Column (Virtual Column)** ใน Database และทำ Index ไว้ เพื่อประสิทธิภาพสูงสุด + +- **3.11.4 Validation Rules** + - ต้องมี JSON schema validation สำหรับแต่ละประเภท + - ต้องรองรับ versioning ของ schema + - ต้องมี default values สำหรับ field ที่ไม่บังคับ + - ต้องตรวจสอบ data types และ format ให้ถูกต้อง + +- **3.11.5 Performance Requirements** + - JSON field ต้องมีขนาดไม่เกิน 50KB + - ต้องรองรับ indexing สำหรับ field ที่ใช้ค้นหาบ่อย + - ต้องมี compression สำหรับ JSON ขนาดใหญ่ + +- **3.11.6 Security Requirements** + - ต้อง sanitize JSON input เพื่อป้องกัน injection attacks + - ต้อง validate JSON structure ก่อนบันทึก + - ต้อง encrypt sensitive data ใน JSON fields + +## **🔐 4. ข้อกำหนดด้านสิทธิ์และการเข้าถึง (Access Control Requirements)** + +### **4.1. ภาพรวม:** ผู้ใช้และองค์กรสามารถดูและแก้ไขเอกสารได้ตามสิทธิ์ที่ได้รับ โดยระบบสิทธิ์จะเป็นแบบ Role-Based Access Control (RBAC) + +### **4.2. ลำดับชั้นของสิทธิ์ (Permission Hierarchy)** + +- Global: สิทธิ์สูงสุดของระบบ +- Organization: สิทธิ์ภายในองค์กร เป็นสิทธิ์พื้นฐานของผู้ใช้ +- Project: สิทธิ์เฉพาะในโครงการ จะถูกพิจารณาเมื่อผู้ใช้อยู่ในโครงการนั้น +- Contract: สิทธิ์เฉพาะในสัญญา จะถูกพิจารณาเมื่อผู้ใช้อยู่ในสัญญานั้น (สัญญาเป็นส่วนหนึ่งของโครงการ) + +กฎการบังคับใช้: เมื่อตรวจสอบสิทธิ์ ระบบจะพิจารณาสิทธิ์จากทุกระดับที่ผู้ใช้มี และใช้ สิทธิ์ที่มากที่สุด (Most Permissive) เป็นตัวตัดสิน + +ตัวอย่าง: ผู้ใช้ A เป็น Viewer ในองค์กร แต่ถูกมอบหมายเป็น Editor ในโครงการ X เมื่ออยู่ในโครงการ X ผู้ใช้ A จะมีสิทธิ์แก้ไขได้ + +### **4.3. การกำหนดบทบาท (Roles) และขอบเขต (Scope)** + +| บทบาท (Role) | ขอบเขต (Scope) | คำอธิบาย | สิทธิ์หลัก (Key Permissions) | +| :------------------- | :------------- | :---------------------- | :------------------------------------------------------------------------------------- | +| **Superadmin** | Global | ผู้ดูแลระบบสูงสุด | ทำทุกอย่างในระบบ, จัดการองค์กร, จัดการข้อมูลหลักระดับ Global | +| **Org Admin** | Organization | ผู้ดูแลองค์กร | จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, ดูรายงานขององค์กร | +| **Document Control** | Organization | ควบคุมเอกสารขององค์กร | เพิ่ม/แก้ไข/ลบเอกสาร, กำหนดสิทธิ์เอกสารภายในองค์กร | +| **Editor** | Organization | ผู้แก้ไขเอกสารขององค์กร | เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย | +| **Viewer** | Organization | ผู้ดูเอกสารขององค์กร | ดูเอกสารที่มีสิทธิ์เข้าถึง | +| **Project Manager** | Project | ผู้จัดการโครงการ | จัดการสมาชิกในโครงการ (เพิ่ม/ลบ/มอบบทบาท), สร้าง/จัดการสัญญาในโครงการ, ดูรายงานโครงการ | +| **Contract Admin** | Contract | ผู้ดูแลสัญญา | จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา (ถ้ามี), อนุมัติเอกสารในสัญญา | + +### **4.4. Token Management (ปรับปรุง)** + +- **Payload Optimization:** ใน JWT Access Token ให้เก็บเฉพาะ `userId` และ `scope` ปัจจุบันเท่านั้น +- **Permission Caching:** สิทธิ์ละเอียด (Permissions List) ให้เก็บใน **Redis** และดึงมาตรวจสอบเมื่อ Request เข้ามา เพื่อลดขนาด Token และเพิ่มความเร็ว + +### **4.5. กระบวนการเริ่มต้นใช้งาน (Onboarding Workflow) ที่สมบูรณ์** + +- **4.5.1. สร้างองค์กร (Organization)** + - **Superadmin** สร้างองค์กรใหม่ (เช่น บริษัท A) + - **Superadmin** แต่งตั้งผู้ใช้อย่างน้อย 1 คนให้เป็น **Org Admin** หรือ **Document Control** ของบริษัท A +- **4.5.2. เพิ่มผู้ใช้ในองค์กร** + - **Org Admin** ของบริษัท A เพิ่มผู้ใช้อื่นๆ (Editor, Viewer) เข้ามาในองค์กรของตน +- **4.5.3. มอบหมายผู้ใช้ให้กับโครงการ (Project)** + - **Project Manager** ของโครงการ X (ซึ่งอาจมาจากบริษัท A หรือบริษัทอื่น) ทำการ "เชิญ" หรือ "มอบหมาย" ผู้ใช้จากองค์กรต่างๆ ที่เกี่ยวข้องเข้ามาในโครงการ X + - ในขั้นตอนนี้ **Project Manager** จะกำหนด **บทบาทระดับโครงการ** (เช่น Project Member, หรืออาจไม่มีบทบาทพิเศษ ให้ใช้สิทธิ์จากระดับองค์กรไปก่อน) +- **4.5.4. เมอบหมายผู้ใช้ให้กับสัญญา (Contract)** + - **Contract Admin** ของสัญญา Y (ซึ่งเป็นส่วนหนึ่งของโครงการ X) ทำการเลือกผู้ใช้ที่อยู่ในโครงการ X แล้ว มอบหมายให้เข้ามาในสัญญา Y + - ในขั้นตอนนี้ **Contract Admin** จะกำหนด **บทบาทระดับสัญญา** (เช่น Contract Member) และสิทธิ์เฉพาะที่จำเป็น +- **4.5.5 Security Onboarding:** + - ต้องบังคับเปลี่ยน password ครั้งแรกสำหรับผู้ใช้ใหม่ + - ต้องมี security awareness training สำหรับผู้ใช้ที่มีสิทธิ์สูง + - ต้องมี process สำหรับการรีเซ็ต password ที่ปลอดภัย + - ต้องบันทึก audit log ทุกครั้งที่มีการเปลี่ยนแปลง permissions + +### **4.6. การจัดการข้อมูลหลัก (Master Data Management) ที่แบ่งตามระดับ** + +| ข้อมูลหลัก | ผู้มีสิทธิ์จัดการ | ระดับ | +| :---------------------------------- | :------------------------------ | :--------------------------------- | +| ประเภทเอกสาร (Correspondence, RFA) | **Superadmin** | Global | +| สถานะเอกสาร (Draft, Approved, etc.) | **Superadmin** | Global | +| หมวดหมู่แบบ (Shop Drawing) | **Project Manager** | Project (สร้างใหม่ได้ภายในโครงการ) | +| Tags | **Org Admin / Project Manager** | Organization / Project | +| บทบาทและสิทธิ์ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization | +| Document Numbering Formats | **Superadmin / Admin** | Global / Organization | + +## **👥 5. ข้อกำหนดด้านผู้ใช้งาน (User Interface & Experience)** + +### **5.1. Layout หลัก:** หน้าเว็บใช้รูปแบบ App Shell ที่ประกอบด้วย + +- Navbar (ส่วนบน): แสดงชื่อระบบ, เมนูผู้ใช้ (Profile), เมนูสำหรับ Document Control/เมนูสำหรับ Admin/Superadmin (จัดการผู้ใช้, จัดการสิทธิ์), และปุ่ม Login/Logout +- Sidebar (ด้านข้าง): เป็นเมนูหลักสำหรับเข้าถึงส่วนที่เกี่ยวข้องกับเอกสารทั้งหมด เช่น Dashboard, Correspondences, RFA, Drawings +- Main Content Area: พื้นที่สำหรับแสดงเนื้อหาหลักของหน้าที่เลือก + +### **5.2. หน้า Landing Page:** เป็นหน้าแรกที่แสดงข้อมูลบางส่วนของโครงการสำหรับผู้ใช้ที่ยังไม่ได้ล็อกอิน + +### **5.3. หน้า Dashboard:** เป็นหน้าแรกหลังจากล็อกอิน ประกอบด้วย + +- การ์ดสรุปภาพรวม (KPI Cards): แสดงข้อมูลสรุปที่สำคัญขององค์กร เช่น จำนวนเอกสาร, งานที่เกินกำหนด +- ตาราง "งานของฉัน" (My Tasks Table): แสดงรายการงานทั้งหมดจาก Circulation ที่ผู้ใช้ต้องดำเนินการ +- Security Metrics: แสดงจำนวน files scanned, security incidents, failed login attempts + +### **5.4. การติดตามสถานะ:** องค์กรสามารถติดตามสถานะเอกสารทั้งของตนเอง (Originator) และสถานะเอกสารที่ส่งมาถึงตนเอง (Recipient) + +### **5.5. การจัดการข้อมูลส่วนตัว (Profile Page):** ผู้ใช้สามารถจัดการข้อมูลส่วนตัวและเปลี่ยนรหัสผ่านของตนเองได้ + +### **5.6. การจัดการเอกสารทางเทคนิค (RFA & Workflow):** ผู้ใช้สามารถดู RFA ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว, ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป + +### **5.7. การจัดการใบเวียนเอกสาร (Circulation):** ผู้ใช้สามารถดู Circulation ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว,ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป + +### **5.8. การจัดการเอกสารนำส่ง (Transmittals):** ผู้ใช้สามารถดู Transmittals ในรูปแบบรายการทั้งหมดได้ในหน้าเดียว + +### **5.9. ข้อกำหนด UI/UX การแนบไฟล์ (File Attachment UX):** + +- ระบบต้องรองรับการอัปโหลดไฟล์หลายไฟล์พร้อมกัน (Multi-file upload) เช่น การลากและวาง (Drag-and-Drop) +- ในหน้าอัปโหลด (เช่น สร้าง RFA หรือ Correspondence) ผู้ใช้ต้องสามารถกำหนดได้ว่าไฟล์ใดเป็น "เอกสารหลัก" (Main Document เช่น PDF) และไฟล์ใดเป็น "เอกสารแนบประกอบ" (Supporting Attachments เช่น .dwg, .docx, .zip) +- **Security Feedback:** แสดง security warnings สำหรับ file types ที่เสี่ยงหรือ files ที่ fail virus scan +- **File Type Indicators:** แสดง file type icons และ security status + +### **5.10 Form & Interaction (ใหม่)** + +- **Dynamic Form Generator:** ใช้ Component กลางที่รับ JSON Schema แล้ว Render Form ออกมาอัตโนมัติ เพื่อลดความซ้ำซ้อนของโค้ดหน้าบ้าน และรองรับเอกสารประเภทใหม่ๆ ได้ทันที +- **Optimistic Updates:** การเปลี่ยนสถานะ (เช่น กด Approve, กด Read) ให้ UI เปลี่ยนสถานะทันทีให้ผู้ใช้เห็นก่อนรอ API Response (Rollback ถ้า Failed) + +### **5.11 Mobile Responsiveness (ใหม่)** + +- **Table Visualization:** บนหน้าจอมือถือ ตารางข้อมูลที่มีหลาย Column (เช่น Correspondence List) ต้องเปลี่ยนการแสดงผลเป็นแบบ **Card View** อัตโนมัติ +- **Navigation:** Sidebar ต้องเป็นแบบ Collapsible Drawer + +### **5.12 Resilience & Offline Support (ใหม่)** + +- **Auto-Save Draft:** ระบบต้องบันทึกข้อมูลฟอร์มที่กำลังกรอกลง **LocalStorage** อัตโนมัติ เพื่อป้องกันข้อมูลหายกรณีเน็ตหลุดหรือปิด Browser โดยไม่ได้ตั้งใจ +- **Graceful Degradation:** หาก Service รอง (เช่น Search, Notification) ล่ม ระบบหลัก (CRUD) ต้องยังทำงานต่อได้ + +## **🛡️ 6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)** + +### **6.1. การบันทึกการกระทำ (Audit Log):** ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง + +- **6.1.1 ขอบเขตการบันทึก Audit Log:** + - ทุกการสร้าง/แก้ไข/ลบ ข้อมูลสำคัญ (correspondences, RFAs, drawings, users, permissions) + - ทุกการเข้าถึงข้อมูล sensitive (user data, financial information) + - ทุกการเปลี่ยนสถานะ workflow (status transitions) + - ทุกการดาวน์โหลดไฟล์สำคัญ (contract documents, financial reports) + - ทุกการเปลี่ยนแปลง permission และ role assignment + - ทุกการล็อกอินที่สำเร็จและล้มเหลว + - ทุกการส่งคำขอ API ที่สำคัญ + +- **6.1.2 ข้อมูลที่ต้องบันทึกใน Audit Log:** + - ผู้ใช้งาน (user_id) + - การกระทำ (action) + - ชนิดของ entity (entity_type) + - ID ของ entity (entity_id) + - ข้อมูลก่อนการเปลี่ยนแปลง (old_values) - สำหรับ update operations + - ข้อมูลหลังการเปลี่ยนแปลง (new_values) - สำหรับ update operations + - IP address + - User agent + - Timestamp + - Request ID สำหรับ tracing + +### **6.2. Data Archiving & Partitioning (ใหม่)** + +- สำหรับตารางที่มีขนาดใหญ่และโตเร็ว (เช่น `audit_logs`, `notifications`, `correspondence_revisions`) ต้องออกแบบโดยรองรับ **Table Partitioning** (แบ่งตาม Range วันที่ หรือ List) เพื่อประสิทธิภาพในระยะยาว + +### **6.3. การค้นหา (Search):** ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag + +### **6.4. การทำรายงาน (Reporting):** สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้ + +### **6.5. ประสิทธิภาพ (Performance):** มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก + +- **6.5.1 ตัวชี้วัดประสิทธิภาพ:** + - **API Response Time:** < 200ms (90th percentile) สำหรับ operation ทั่วไป + - **Search Query Performance:** < 500ms สำหรับการค้นหาขั้นสูง + - **File Upload Performance:** < 30 seconds สำหรับไฟล์ขนาด 50MB + - **Concurrent Users:** รองรับผู้ใช้พร้อมกันอย่างน้อย 100 คน + - **Database Connection Pool:** ขนาดเหมาะสมกับ workload (default: min 5, max 20 connections) + - **Cache Hit Ratio:** > 80% สำหรับ cached data + - **Application Startup Time:** < 30 seconds + +- **6.5.2 Caching Strategy:** + - **Master Data Cache:** Roles, Permissions, Organizations, Project metadata (TTL: 1 hour) + - **User Session Cache:** User permissions และ profile data (TTL: 30 minutes) + - **Search Result Cache:** Frequently searched queries (TTL: 15 minutes) + - **File Metadata Cache:** Attachment metadata (TTL: 1 hour) + - **Document Cache:** Frequently accessed document metadata (TTL: 30 minutes) + - **ต้องมี cache invalidation strategy ที่ชัดเจน:** + - Invalidate on update/delete operations + - Time-based expiration + - Manual cache clearance สำหรับ admin operations + - ใช้ Redis เป็น distributed cache backend + - ต้องมี cache monitoring (hit/miss ratios) + +### **6.6. ความปลอดภัย (Security):** + +- มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force +- การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด + +- **6.6.1 Rate Limiting Strategy:** + - **Anonymous Endpoints:** 100 requests/hour ต่อ IP address + - **Authenticated Endpoints:** + - Viewer: 500 requests/hour + - Editor: 1000 requests/hour + - Document Control: 2000 requests/hour + - Admin/Superadmin: 5000 requests/hour + - **File Upload Endpoints:** 50 requests/hour ต่อ user + - **Search Endpoints:** 500 requests/hour ต่อ user + - **Authentication Endpoints:** 10 requests/minute ต่อ IP address + - **ต้องมี mechanism สำหรับยกเว้น rate limiting สำหรับ trusted services** + - ต้องบันทึก log เมื่อมีการ trigger rate limiting + +- **6.6.2 Error Handling และ Resilience:** + - ต้องมี circuit breaker pattern สำหรับ external service calls + - ต้องมี retry mechanism ด้วย exponential backoff + - ต้องมี graceful degradation เมื่อบริการภายนอกล้มเหลว + - Error messages ต้องไม่เปิดเผยข้อมูล sensitive + +- **6.6.3 Input Validation:** + - ต้องมี input validation ทั้งฝั่ง client และ server (defense in depth) + - ต้องป้องกัน OWASP Top 10 vulnerabilities: + - SQL Injection (ใช้ parameterized queries ผ่าน ORM) + - XSS (input sanitization และ output encoding) + - CSRF (CSRF tokens สำหรับ state-changing operations) + - ต้อง validate file uploads: + - File type (white-list approach) + - File size + - File content (magic number verification) + - ต้อง sanitize user inputs ก่อนแสดงผลใน UI + - ต้องใช้ content security policy (CSP) headers + - ต้องมี request size limits เพื่อป้องกัน DoS attacks + +- **6.6.4 Session และ Token Management:** + - **JWT token expiration:** 8 hours สำหรับ access token + - **Refresh token expiration:** 7 days + - **Refresh token mechanism:** ต้องรองรับ token rotation และ revocation + - **Token revocation on logout:** ต้องบันทึก revoked tokens จนกว่าจะ expire + - **Concurrent session management:** + - จำกัดจำนวน session พร้อมกันได้ (default: 5 devices) + - ต้องแจ้งเตือนเมื่อมี login จาก device/location ใหม่ + - **Device fingerprinting:** สำหรับ security และ audit purposes + - **Password policy:** + - ความยาวขั้นต่ำ: 8 characters + - ต้องมี uppercase, lowercase, number, special character + - ต้องเปลี่ยน password ทุก 90 วัน + - ต้องป้องกันการใช้ password ที่เคยใช้มาแล้ว 5 ครั้งล่าสุด + +### **6.7. การสำรองข้อมูลและการกู้คืน (Backup & Recovery):** + +- ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง +- ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้ + +- **6.7.1 ขั้นตอนการกู้คืน:** + - **Database Restoration Procedure:** + - สร้างจาก full backup ล่าสุด + - Apply transaction logs ถึง point-in-time ที่ต้องการ + - Verify data integrity post-restoration + - **File Storage Restoration Procedure:** + - Restore จาก QNAP snapshot หรือ backup + - Verify file integrity และ permissions + - **Application Redeployment Procedure:** + - Deploy จาก version ล่าสุดที่รู้ว่าทำงานได้ + - Verify application health + - **Data Integrity Verification Post-Recovery:** + - Run data consistency checks + - Verify critical business data + - **Recovery Time Objective (RTO):** < 4 ชั่วโมง + - **Recovery Point Objective (RPO):** < 1 ชั่วโมง + +### **6.8. กลยุทธ์การแจ้งเตือน (Notification Strategy - ปรับปรุง):** + +- **6.8.1 ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ** ดังนี้: + 1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา + 2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา + 3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ) + 4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5] + +- **6.8.2 Grouping/Digest (ใหม่):** กรณีมีการแจ้งเตือนประเภทเดียวกันจำนวนมากในช่วงเวลาสั้นๆ (เช่น Approve เอกสาร 10 ฉบับรวด) ระบบต้อง **รวม (Batch)** เป็น 1 Email/Line Notification เพื่อไม่ให้รบกวนผู้ใช้ (Spamming) + +- **6.8.3 Notification Delivery Guarantees:** + - **At-least-once delivery:** สำหรับ important notifications + - **Retry mechanism:** ด้วย exponential backoff (max 3 reties) + - **Dead letter queue:** สำหรับ notifications ที่ส่งไม่สำเร็จหลังจาก retries + - **Delivery status tracking:** ต้องบันทึกสถานะการส่ง notifications + - **Fallback channels:** ถ้า Email ล้มเหลว ให้ส่งผ่าน SYSTEM notification + - **Notification preferences:** ผู้ใช้ต้องสามารถกำหนด channel preferences ได้ + +### **6.9. Maintenance Mode (ใหม่)** + +- ระบบต้องมีกลไก **Maintenance Mode** ที่ Admin สามารถเปิดใช้งานได้ + - เมื่อเปิด: ผู้ใช้ทั่วไปจะเห็นหน้า "ปิดปรับปรุง" และไม่สามารถเรียก API ได้ (ยกเว้น Admin) + - ใช้สำหรับช่วง Deploy Version ใหม่ หรือ Database Migration + +### **6.10. Monitoring และ Observability** + +- **6.10.1 Application Monitoring:** + - **Health checks:** /health endpoint สำหรับ load balancer + - **Metrics collection:** Response times, error rates, throughput + - **Distributed tracing:** สำหรับ request tracing across services + - **Log aggregation:** Structured logging ด้วย JSON format + - **Alerting:** สำหรับ critical errors และ performance degradation +- **6.10.2 Business Metrics:** + - จำนวน documents created ต่อวัน + - Workflow completion rates + - User activity metrics + - System utilization rates + - Search query performance +- **6.10.3 Security Monitoring:** + - Failed login attempts + - Rate limiting triggers + - Virus scan results + - File download activities + - Permission changes + +### **6.11 JSON Processing & Validation** + +- **6.11.1 JSON Schema Management** + - ต้องมี centralized JSON schema registry + - ต้องรองรับ schema versioning และ migration + - ต้องมี schema validation during runtime +- **6.11.2 Performance Optimization** + - **Caching:** Cache parsed JSON structures + - **Compression:** ใช้ compression สำหรับ JSON ขนาดใหญ่ + - **Indexing:** Support JSON path indexing สำหรับ query +- **6.11.3 Error Handling** + - ต้องมี graceful degradation เมื่อ JSON validation ล้มเหลว + - ต้องมี default fallback values + - ต้องบันทึก error logs สำหรับ validation failures + +## **🧪 7. ข้อกำหนดด้านการทดสอบ (Testing Requirements)** + +### **7.1. Unit Testing:** + +- ต้องมี unit tests สำหรับ business logic ทั้งหมด +- Code coverage อย่างน้อย 70% สำหรับ backend services +- ต้องทดสอบ RBAC permission logic ทุกระดับ + +### **7.2. Integration Testing:** + +- ทดสอบการทำงานร่วมกันของ modules +- ทดสอบ database migrations และ data integrity +- ทดสอบ API endpoints ด้วย realistic data + +### **7.3. End-to-End Testing:** + +- ทดสอบ complete user workflows +- ทดสอบ document lifecycle จาก creation ถึง archival +- ทดสอบ cross-module integrations + +### **7.4. Security Testing:** + +- **Penetration Testing:** ทดสอบ OWASP Top 10 vulnerabilities +- **Security Audit:** Review code สำหรับ security flaws +- **Virus Scanning Test:** ทดสอบ file upload security +- **Rate Limiting Test:** ทดสอบ rate limiting functionality + +### **7.5. Performance Testing:** + +- **Load Testing:** ทดสอบด้วย realistic workloads +- **Stress Testing:** หา breaking points ของระบบ +- **Endurance Testing:** ทดสอบการทำงานต่อเนื่องเป็นเวลานาน + +### **7.6. Disaster Recovery Testing:** + +- ทดสอบ backup และ restoration procedures +- ทดสอบ failover mechanisms +- ทดสอบ data integrity หลังการ recovery + +### **7.7 Specific Scenario Testing (เพิ่ม)** + +- **Race Condition Test:** ทดสอบยิง Request ขอเลขที่เอกสารพร้อมกัน 100 Request +- **Transaction Test:** ทดสอบปิดเน็ตระหว่าง Upload ไฟล์ (ตรวจสอบว่าไม่มี Orphan File หรือ Broken Link) +- **Permission Test:** ทดสอบ CASL Integration ทั้งฝั่ง Backend และ Frontend ให้ตรงกัน + +## **8. ข้อกำหนดด้านการบำรุงรักษา (Maintenance Requirements)** + +### **8.1. Log Retention:** + +- Audit logs: 7 ปี +- Application logs: 1 ปี +- Performance metrics: 2 ปี + +### **8.2. Monitoring และ Alerting:** + +- ต้องมี proactive monitoring สำหรับ critical systems +- ต้องมี alerting สำหรับ security incidents +- ต้องมี performance degradation alerts + +### **8.3. Patch Management:** + +- ต้องมี process สำหรับ security patches +- ต้องทดสอบ patches ใน staging environment +- ต้องมี rollback plan สำหรับ failed updates + +### **8.4. Capacity Planning:** + +- ต้อง monitor resource utilization +- ต้องมี scaling strategy สำหรับ growth +- ต้องมี performance baselines และ trending + +## **9. ข้อกำหนดด้านการปฏิบัติตามกฎระเบียบ (Compliance Requirements)** + +### **9.1. Data Privacy:** + +- ต้องปฏิบัติตามกฎหมายคุ้มครองข้อมูลส่วนบุคคล +- ต้องมี data retention policies +- ต้องมี data deletion procedures + +### **9.2. Audit Compliance:** + +- ต้องรองรับ internal และ external audits +- ต้องมี comprehensive audit trails +- ต้องมี reporting capabilities สำหรับ compliance + +### **9.3. Security Standards:** + +- ต้องปฏิบัติตาม organizational security policies +- ต้องมี security incident response plan +- ต้องมี regular security assessments + +## **📋 สรุปการปรับปรุงจากเวอร์ชันก่อนหน้า** + +### **Security Enhancements:** + +1. **File Upload Security** - Virus scanning, file type validation, access controls +2. **Input Validation** - OWASP Top 10 protection, XSS/CSRF prevention +3. **Rate Limiting** - Comprehensive rate limiting strategy +4. **Secrets Management** - Secure handling of sensitive configuration + +### **Architecture Improvements:** + +1. **Document Numbering** - Changed from Stored Procedure to Application-level Locking with Optimistic Locking safety net +2. **Resilience Patterns** - Circuit breaker, retry mechanisms, fallback strategies +3. **Monitoring & Observability** - Health checks, metrics, distributed tracing +4. **Caching Strategy** - Comprehensive caching with proper invalidation +5. **Two-Phase File Storage** - Temp -> Permanent storage with transaction safety +6. **Unified Workflow Engine** - Consolidated routing logic for better maintainability + +### **Performance Targets:** + +1. **API Response Time** - < 200ms (90th percentile) +2. **Search Performance** - < 500ms +3. **File Upload** - < 30 seconds for 50MB files +4. **Cache Hit Ratio** - > 80% + +### **Operational Excellence:** + +1. **Disaster Recovery** - RTO < 4 hours, RPO < 1 hour +2. **Backup Procedures** - Comprehensive backup and restoration +3. **Security Testing** - Penetration testing and security audits +4. **Performance Testing** - Load testing with realistic workloads +5. **Maintenance Mode** - Graceful system maintenance capabilities + +### **User Experience Improvements:** + +1. **Dynamic Form Generator** - Reduced code duplication and better schema support +2. **Mobile Responsiveness** - Card view for tables on mobile devices +3. **Auto-Save Draft** - LocalStorage integration for form resilience +4. **Notification Digest** - Reduced notification spam + +### **Data Management:** + +1. **Virtual Columns** - Improved JSON field search performance +2. **Table Partitioning** - Support for large-scale data growth +3. **Idempotency Keys** - Prevention of duplicate transactions + +เอกสารนี้สะท้อนถึงความมุ่งมั่นในการสร้างระบบที่มีความปลอดภัย, มีความทนทาน, และมีประสิทธิภาพสูง พร้อมรองรับการเติบโตในอนาคตและความต้องการทางธุรกิจที่เปลี่ยนแปลงไป + +**หมายเหตุ:** Requirements นี้จะถูกทบทวนและปรับปรุงเป็นระยะตาม feedback จากทีมพัฒนาและความต้องการทางธุรกิจที่เปลี่ยนแปลงไป + +## **Document Control:** + +- **Document:** Application Requirements Specification DMS v1.4.2 +- **Version:** 1.4.2 +- **Date:** 2025-11-19 +- **Author:** System Architecture Team +- **Status:** FINAL +- **Classification:** Internal Technical Documentation +- **Approved By:** System Architect Team + +--- + +`End of Requirements Specification v1.4.2` diff --git a/Documnets/Project/1_FullStackJS_V1_4_2..md b/Documnets/Project/1_FullStackJS_V1_4_2..md new file mode 100644 index 0000000..4496cf9 --- /dev/null +++ b/Documnets/Project/1_FullStackJS_V1_4_2..md @@ -0,0 +1,960 @@ +# 📝 **Documents Management System Version 1.4.2: แนวทางการพัฒนา FullStackJS** + +**สถานะ:** FINAL GUIDELINE +**วันที่:** 2025-11-19 +**อ้างอิง:** Requirements Specification v1.4.2 +**ปรับปรุงโดย:** deepseek + +## 🧠 **1. ปรัชญาทั่วไป (General Philosophy)** + +แนวทางปฏิบัติที่ดีที่สุดแบบครบวงจรสำหรับการพัฒนา NestJS Backend, NextJS Frontend และ Tailwind-based UI/UX ในสภาพแวดล้อม TypeScript มุ่งเน้นที่ **"Data Integrity First"** (ความถูกต้องของข้อมูลต้องมาก่อน) ตามด้วย Security และ UX + +* **ความชัดเจน (clarity), ความง่ายในการบำรุงรักษา (maintainability), ความสอดคล้องกัน (consistency) และ การเข้าถึงได้ (accessibility)** ตลอดทั้งสแต็ก +* **Strict Typing:** ใช้ TypeScript อย่างเคร่งครัด ห้าม `any` +* **Consistency:** ใช้ภาษาอังกฤษใน Code / ภาษาไทยใน Comment +* **Resilience:** ระบบต้องทนทานต่อ Network Failure และ Race Condition + +## ⚙️ **2. แนวทางทั่วไปสำหรับ TypeScript** + +### **2.1 หลักการพื้นฐาน** + +* ใช้ **ภาษาอังกฤษ** สำหรับโค้ด +* ใช้ **ภาษาไทย** สำหรับ comment และเอกสารทั้งหมด +* กำหนดไทป์ (type) อย่างชัดเจนสำหรับตัวแปร, พารามิเตอร์ และค่าที่ส่งกลับ (return values) ทั้งหมด +* หลีกเลี่ยงการใช้ any; ให้สร้างไทป์ (types) หรืออินเทอร์เฟซ (interfaces) ที่กำหนดเอง +* ใช้ **JSDoc** สำหรับคลาส (classes) และเมธอด (methods) ที่เป็น public +* ส่งออก (Export) **สัญลักษณ์หลัก (main symbol) เพียงหนึ่งเดียว** ต่อไฟล์ +* หลีกเลี่ยงบรรทัดว่างภายในฟังก์ชัน +* ระบุ // File: path/filename ในบรรทัดแรกของทุกไฟล์ +* ระบุ // บันทึกการแก้ไข, หากมีการแก้ไขเพิ่มในอนาคต ให้เพิ่มบันทึก + +### **2.2 Configuration & Secrets Management** + +* **Production/Staging:** ห้ามใส่ Secrets (Password, Keys) ใน `docker-compose.yml` หลัก +* **Development:** ให้สร้างไฟล์ `docker-compose.override.yml` (เพิ่มใน `.gitignore`) เพื่อ Inject ตัวแปร Environment ที่เป็นความลับ +* **Validation:** ใช้ `joi` หรือ `zod` ในการ Validate Environment Variables ตอน Start App หากขาดตัวแปรสำคัญให้ Throw Error ทันที + +### **2.3 Idempotency (ความสามารถในการทำซ้ำได้)** + +* สำหรับการทำงานที่สำคัญ (Create Document, Approve, Transactional) **ต้อง** ออกแบบให้เป็น Idempotent +* Client **ต้อง** ส่ง Header `Idempotency-Key` (UUID) มากับ Request +* Server **ต้อง** ตรวจสอบว่า Key นี้เคยถูกประมวลผลสำเร็จไปแล้วหรือไม่ ถ้าใช่ ให้คืนค่าเดิมโดยไม่ทำซ้ำ + +### **2.4 ข้อตกลงในการตั้งชื่อ (Naming Conventions)** + +| Entity (สิ่งที่ตั้งชื่อ) | Convention (รูปแบบ) | Example (ตัวอย่าง) | +| :---- | :---- | :---- | +| Classes | PascalCase | UserService | +| Property | snake_case | user_id | +| Variables & Functions | camelCase | getUserInfo | +| Files & Folders | kebab-case | user-service.ts | +| Environment Variables | UPPERCASE | DATABASE_URL | +| Booleans | Verb + Noun | isActive, canDelete, hasPermission | + +ใช้คำเต็ม — ไม่ใช้อักษรย่อ — ยกเว้นคำมาตรฐาน (เช่น API, URL, req, res, err, ctx) + +### 🧩**2.5 ฟังก์ชัน (Functions)** + +* เขียนฟังก์ชันให้สั้น และทำ **หน้าที่เพียงอย่างเดียว** (single-purpose) (\< 20 บรรทัด) +* ใช้ **early returns** เพื่อลดการซ้อน (nesting) ของโค้ด +* ใช้ **map**, **filter**, **reduce** แทนการใช้ loops เมื่อเหมาะสม +* ควรใช้ **arrow functions** สำหรับตรรกะสั้นๆ, และใช้ **named functions** ในกรณีอื่น +* ใช้ **default parameters** แทนการตรวจสอบค่า null +* จัดกลุ่มพารามิเตอร์หลายตัวให้เป็นอ็อบเจกต์เดียว (RO-RO pattern) +* ส่งค่ากลับ (Return) เป็นอ็อบเจกต์ที่มีไทป์กำหนด (typed objects) ไม่ใช่ค่าพื้นฐาน (primitives) +* รักษาระดับของสิ่งที่เป็นนามธรรม (abstraction level) ให้เป็นระดับเดียวในแต่ละฟังก์ชัน + +### 🧱**2.6 การจัดการข้อมูล (Data Handling)** + +* ห่อหุ้มข้อมูล (Encapsulate) ในไทป์แบบผสม (composite types) +* ใช้ **immutability** (การไม่เปลี่ยนแปลงค่า) ด้วย readonly และ as const +* ทำการตรวจสอบความถูกต้องของข้อมูล (Validations) ในคลาสหรือ DTOs ไม่ใช่ภายในฟังก์ชันทางธุรกิจ +* ตรวจสอบความถูกต้องของข้อมูลโดยใช้ DTOs ที่มีไทป์กำหนดเสมอ + +### 🧰**2.7 คลาส (Classes)** + +* ปฏิบัติตามหลักการ **SOLID** +* ควรใช้ **composition มากกว่า inheritance** (Prefer composition over inheritance) +* กำหนด **interfaces** สำหรับสัญญา (contracts) +* ให้คลาสมุ่งเน้นการทำงานเฉพาะอย่างและมีขนาดเล็ก (\< 200 บรรทัด, \< 10 เมธอด, \< 10 properties) + +### 🚨**2.8 การจัดการข้อผิดพลาด (Error Handling)** + +* ใช้ Exceptions สำหรับข้อผิดพลาดที่ไม่คาดคิด +* ดักจับ (Catch) ข้อผิดพลาดเพื่อแก้ไขหรือเพิ่มบริบท (context) เท่านั้น; หากไม่เช่นนั้น ให้ใช้ global error handlers +* ระบุข้อความข้อผิดพลาด (error messages) ที่มีความหมายเสมอ + +### 🧪**2.9 การทดสอบ (ทั่วไป) (Testing (General))** + +* ใช้รูปแบบ **Arrange–Act–Assert** +* ใช้ชื่อตัวแปรในการทดสอบที่สื่อความหมาย (inputData, expectedOutput) +* เขียน **unit tests** สำหรับ public methods ทั้งหมด +* จำลอง (Mock) การพึ่งพาภายนอก (external dependencies) +* เพิ่ม **acceptance tests** ต่อโมดูลโดยใช้รูปแบบ Given–When-Then + +### **Testing Strategy โดยละเอียด** + +* **Test Pyramid Structure** + + /\ + / \ E2E Tests (10%) + /____\ Integration Tests (20%) + / \ Unit Tests (70%) +/________\ + +* **Testing Tools Stack** + +```typescript +// Backend Testing Stack +const backendTesting = { + unit: ['Jest', 'ts-jest', '@nestjs/testing'], + integration: ['Supertest', 'Testcontainers', 'Jest'], + e2e: ['Supertest', 'Jest', 'Database Seeds'], + security: ['Jest', 'Custom Security Test Helpers'], + performance: ['Jest', 'autocannon', 'artillery'] +}; + +// Frontend Testing Stack +const frontendTesting = { + unit: ['Vitest', 'React Testing Library'], + integration: ['React Testing Library', 'MSW'], + e2e: ['Playwright', 'Jest'], + visual: ['Playwright', 'Loki'] +}; +``` + +* **Test Data Management** + +```typescript +// Test Data Factories +interface TestDataFactory { + createUser(overrides?: Partial): User; + createCorrespondence(overrides?: Partial): Correspondence; + createRoutingTemplate(overrides?: Partial): RoutingTemplate; +} + +// Test Scenarios +const testScenarios = { + happyPath: 'Normal workflow execution', + edgeCases: 'Boundary conditions and limits', + errorConditions: 'Error handling and recovery', + security: 'Authentication and authorization', + performance: 'Load and stress conditions' +}; +``` + +## 🏗️ **3. แบ็กเอนด์ (NestJS) - Implementation Details** + +### **3.1 หลักการ** + +* **สถาปัตยกรรมแบบโมดูลาร์ (Modular architecture)**: + * หนึ่งโมดูลต่อหนึ่งโดเมน + * โครงสร้างแบบ Controller → Service → Repository (Model) +* API-First: มุ่งเน้นการสร้าง API ที่มีคุณภาพสูง มีเอกสารประกอบ (Swagger) ที่ชัดเจนสำหรับ Frontend Team +* DTOs ที่ตรวจสอบความถูกต้องด้วย **class-validator** +* ใช้ **MikroORM** (หรือ TypeORM/Prisma) สำหรับการคงอยู่ของข้อมูล (persistence) ซึ่งสอดคล้องกับสคีมา MariaDB +* ห่อหุ้มโค้ดที่ใช้ซ้ำได้ไว้ใน **common module** (@app/common): + * Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators + +### **3.2 Database & Data Modeling (MariaDB + TypeORM)** + +#### **3.2.1 Optimistic Locking & Versioning** + +เพื่อป้องกัน Race Condition ในการแก้ไขข้อมูลพร้อมกัน (โดยเฉพาะการรันเลขที่เอกสาร) ให้เพิ่ม Column `@VersionColumn()` ใน Entity ที่สำคัญ + +```typescript +@Entity() +export class DocumentCounter { + // ... fields + @Column() + last_number: number; + + @VersionColumn() // เพิ่ม Versioning + version: number; +} +``` + +#### **3.2.2 Virtual Columns for JSON Performance** + +เนื่องจากเราใช้ MariaDB 10.11 และมีการเก็บข้อมูล JSON (Details) ให้ใช้ **Generated Columns (Virtual)** สำหรับ Field ที่ต้อง Search/Sort บ่อยๆ และทำ Index บน Virtual Column นั้น + +```sql +-- ตัวอย่าง SQL Migration +ALTER TABLE correspondence_revisions +ADD COLUMN ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId'))) VIRTUAL; +CREATE INDEX idx_ref_project_id ON correspondence_revisions(ref_project_id); +``` + +#### **3.2.3 Partitioning Strategy** + +สำหรับตาราง `audit_logs` และ `notifications` ให้เตรียมออกแบบ Entity ให้รองรับ Partitioning (เช่น แยกตามปี) โดยใช้ Raw SQL Migration ในการสร้างตาราง + +### **3.3 File Storage Service (Two-Phase Storage)** + +ปรับปรุง Service จัดการไฟล์ให้รองรับ Transactional Integrity + +1. **Upload (Phase 1):** + * รับไฟล์ → Scan Virus (ClamAV) → Save ลงโฟลเดอร์ `temp/` + * Return `temp_id` และ Metadata กลับไปให้ Client +2. **Commit (Phase 2):** + * เมื่อ Business Logic (เช่น Create Correspondence) ทำงานสำเร็จ + * Service จะย้ายไฟล์จาก `temp/` ไปยัง `permanent/{YYYY}/{MM}/` + * Update path ใน Database + * ทั้งหมดนี้ต้องอยู่ภายใต้ Database Transaction เดียวกัน (ถ้า DB Fail, ไฟล์จะค้างที่ Temp และถูกลบโดย Cron Job) + +### **3.4 Document Numbering (Double-Lock Mechanism)** + +การออกเลขที่เอกสารต้องใช้กลไกความปลอดภัย 2 ชั้น: + +1. **Layer 1 (Redis Lock):** ใช้ `redlock` เพื่อ Block ไม่ให้ Process อื่นเข้ามายุ่งกับ Counter ของ Project/Type นั้นๆ ชั่วคราว +2. **Layer 2 (Optimistic Lock):** ตอน Update Database ให้เช็ค `version` ถ้า version เปลี่ยน (แสดงว่า Redis Lock หลุดหรือมีคนแทรก) ให้ Throw Error และ Retry ใหม่ + +### **3.5 Unified Workflow Engine** + +ห้ามแยก Logic ระหว่าง `CorrespondenceRouting` และ `RfaWorkflow` ออกจากกันเด็ดขาด ให้สร้าง `WorkflowEngineService` ที่เป็น Generic: + +* **Input:** `currentState`, `action`, `rules (Guard)` +* **Output:** `nextState`, `assignee` +* รองรับทั้ง Linear Flow (Routing) และ Complex Flow (RFA) ผ่าน Configuration + +### **3.6 ฟังก์ชันหลัก (Core Functionalities)** + +* Global **filters** สำหรับการจัดการ exception +* **Middlewares** สำหรับการจัดการ request +* **Guards** สำหรับการอนุญาต (permissions) และ RBAC +* **Interceptors** สำหรับการแปลงข้อมูล response และการบันทึก log + +### **3.7 ข้อจำกัดในการ Deploy (QNAP Container Station)** + +* **ห้ามใช้ไฟล์ .env** ในการตั้งค่า Environment Variables [cite: 2.1] +* การตั้งค่าทั้งหมด (เช่น Database connection string, JWT secret) **จะต้องถูกกำหนดผ่าน Environment Variable ใน docker-compose.yml โดยตรง** [cite: 6.5] ซึ่งจะจัดการผ่าน UI ของ QNAP Container Station [cite: 2.1] + +### **3.8 ข้อจำกัดด้านความปลอดภัย (Security Constraints):** + +* **File Upload Security:** ต้องมี virus scanning (ClamAV), file type validation (white-list), และ file size limits (50MB) +* **Input Validation:** ต้องป้องกัน OWASP Top 10 vulnerabilities (SQL Injection, XSS, CSRF) +* **Rate Limiting:** ต้อง implement rate limiting ตาม strategy ที่กำหนด +* **Secrets Management:** ต้องมี mechanism สำหรับจัดการ sensitive secrets อย่างปลอดภัย แม้จะใช้ docker-compose.yml + +### **3.9 โครงสร้างโมดูลตามโดเมน (Domain-Driven Module Structure)** + +เพื่อให้สอดคล้องกับสคีมา SQL (LCBP3-DMS) เราจะใช้โครงสร้างโมดูลแบบ **Domain-Driven (แบ่งตามขอบเขตธุรกิจ)** แทนการแบ่งตามฟังก์ชัน: + +#### 3.9.1 **CommonModule:** + +* เก็บ Services ที่ใช้ร่วมกัน เช่น DatabaseModule, FileStorageService (จัดการไฟล์ใน QNAP), AuditLogService, NotificationService +* จัดการ audit_logs +* NotificationService ต้องรองรับ Triggers ที่ระบุใน Requirement 6.7 [cite: 6.7] + +#### 3.9.2 **AuthModule:** + +* จัดการะการยืนยันตัวตน (JWT, Guards) +* **(สำคัญ)** ต้องรับผิดชอบการตรวจสอบสิทธิ์ **4 ระดับ** [cite: 4.2]: สิทธิ์ระดับระบบ (Global Role), สิทธิ์ระดับองกรณ์ (Organization Role), สิทธิ์ระดับโปรเจกต์ (Project Role), และ สิทธิ์ระดับสัญญา (Contract Role) +* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ: + * สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3] + * ให้ Superadmin สร้าง Organizations และกำหนด Org Admin ได้ [cite: 4.6] + * ให้ Superadmin/Admin จัดการ document_number_formats (รูปแบบเลขที่เอกสาร), document_number_counters (Running Number) [cite: 3.10] + +#### 3.9.3 **UserModule:** + +* จัดการ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles +* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ: + * สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3] + +#### 3.9.4 **ProjectModule:** + +* จัดการ projects, organizations, contracts, project_parties, contract_parties + +#### 3.9.5 **MasterModule:** + +* จัดการ master data (correspondence_types, rfa_types, rfa_status_codes, rfa_approve_codes, circulation_status_codes, correspondence_types, correspondence_status, tags) [cite: 4.5] + +#### 3.9.6 **CorrespondenceModule (โมดูลศูนย์กลาง):** + +* จัดการ correspondences, correspondence_revisions, correspondence_tags +* **(สำคัญ)** Service นี้ต้อง Inject DocumentNumberingService เพื่อขอเลขที่เอกสารใหม่ก่อนการสร้าง +* **(สำคัญ)** ตรรกะการสร้าง/อัปเดต Revision จะอยู่ใน Service นี้ +* จัดการ correspondence_attachments (ตารางเชื่อมไฟล์แนบ) +* รับผิดชอบ Routing **Correspondence Routing** (correspondence_routings, correspondence_routing_template_steps, correspondence_routing_templates, correspondence_status_transitions) สำหรับการส่งต่อเอกสารทั่วไประหว่างองค์กร + +#### 3.9.7 **RfaModule:** + +* จัดการ rfas, rfa_revisions, rfa_items +* รับผิดชอบเวิร์กโฟลว์ **"RFA Workflows"** (rfa_workflows, rfa_workflow_templates, rfa_workflow_template_steps, rfa_status_transitions) สำหรับการอนุมัติเอกสารทางเทคนิค + +#### 3.9.8 **DrawingModule:** + +* จัดการ shop_drawings, shop_drawing_revisions, contract_drawings, contract_drawing_volumes, contract_drawing_cats, contract_drawing_sub_cats, shop_drawing_main_categories, shop_drawing_sub_categories, contract_drawing_subcat_cat_maps, shop_drawing_revision_contract_refs +* จัดการ shop_drawing_revision_attachments และ contract_drawing_attachments(ตารางเชื่อมไฟล์แนบ) + +#### 3.9.9 **CirculationModule:** + +* จัดการ circulations, circulation_templates, circulation_assignees +* จัดการ circulation_attachments (ตารางเชื่อมไฟล์แนบ) +* รับผิดชอบเวิร์กโฟลว์ **"Circulations"** (circulation_status_transitions, circulation_template_assignees, circulation_assignees, circulation_recipients, circulation_actions, circulation_action_documents)สำหรับการเวียนเอกสาร **ภายในองค์กร** + +#### 3.9.10 **TransmittalModule:** + +* จัดการ transmittals และ transmittal_items + +#### 3.9.11 **SearchModule:** + +* ให้บริการค้นหาขั้นสูง (Advanced Search) [cite: 6.2] โดยใช้ **Elasticsearch** เพื่อรองรับการค้นหาแบบ Full-text จากชื่อเรื่อง, รายละเอียด, เลขที่เอกสาร, ประเภท, วันที่, และ Tags +* ระบบจะใช้ Elasticsearch Engine ในการจัดทำดัชนีเพื่อการค้นหาข้อมูลเชิงลึกจากเนื้อหาของเอกสาร โดยข้อมูลจะถูกส่งไปทำดัชนีจาก Backend (NestJS) ทุกครั้งที่มีการสร้างหรือแก้ไขเอกสาร + +#### 3.9.12 **DocumentNumberingModule:** + +* **สถานะ:** เป็น Module ภายใน (Internal Module) ไม่เปิด API สู่ภายนอก +* **หน้าที่:** ให้บริการ DocumentNumberingService ที่ Module อื่น (เช่น CorrespondenceModule) จะ Inject ไปใช้งาน +* **ตรรกะ:** รับผิดชอบการสร้างเลขที่เอกสารโดยใช้ **Redis distributed locking** แทน stored procedure +* **Features:** + * Application-level locking เพื่อป้องกัน race condition + * Retry mechanism ด้วย exponential backoff + * Fallback mechanism เมื่อการขอเลขล้มเหลว + * Audit log ทุกครั้งที่มีการ generate เลขที่เอกสารใหม่ + +#### 3.9.13 **CorrespondenceRoutingModule:** + +* **สถานะ:** โมดูลหลักสำหรับจัดการการส่งต่อเอกสาร +* **หน้าที่:** จัดการแม่แบบการส่งต่อและการส่งต่อจริง +* **Entities:** + * CorrespondenceRoutingTemplate + * CorrespondenceRoutingTemplateStep + * CorrespondenceRouting +* **Features:** + * สร้างและจัดการแม่แบบการส่งต่อ + * ดำเนินการส่งต่อเอกสารตามแม่แบบ + * ติดตามสถานะการส่งต่อ + * คำนวณวันครบกำหนดอัตโนมัติ + * ส่งการแจ้งเตือนเมื่อมีการส่งต่อใหม่ + +#### 3.9.14 **WorkflowEngineModule:** + +* **สถานะ:** Internal Module สำหรับจัดการ workflow logic +* **หน้าที่:** ประมวลผล state transitions และ business rules +* **Features:** + * State machine สำหรับสถานะเอกสาร + * Validation rules สำหรับการเปลี่ยนสถานะ + * Automatic status updates + * Deadline management และ escalation + +#### 3.9.15 **JsonSchemaModule:** + +* **สถานะ:** Internal Module สำหรับจัดการ JSON schemas +* **หน้าที่:** Validate, transform, และ manage JSON data structures +* **Features:** + * JSON schema validation ด้วย AJV + * Schema versioning และ migration + * Dynamic schema generation + * Data transformation และ sanitization + +#### 3.9.16 **DetailsService:** + +* **สถานะ:** Shared Service สำหรับจัดการ details fields +* **หน้าที่:** Centralized service สำหรับ JSON details operations +* **Methods:** + * validateDetails(type: string, data: any): ValidationResult + * transformDetails(input: any, targetVersion: string): any + * sanitizeDetails(data: any): any + * getDefaultDetails(type: string): any + +### **3.10 สถาปัตยกรรมระบบ (System Architecture)** + +โครงสร้างโมดูล (Module Structure) + +```bash +📁 src +├── 📄 app.module.ts +├── 📄 main.ts +├── 📁 common # @app/common (โมดูลส่วนกลาง) +│ ├── 📁 auth # AuthModule (JWT, Guards) +│ ├── 📁 config # Configuration +│ ├── 📁 decorators # Custom Decorators (เช่น @RequirePermission) +│ ├── 📁 entities # Shared Entities (User, Role, Permission) +│ ├── 📁 exceptions # Global Exception Filters +│ ├── 📁 file-storage # FileStorageService (Virus Scanning, Security) +│ ├── 📁 guards # Custom Guards (RBAC Guard, RateLimitGuard) +│ ├── 📁 interceptors # Interceptors (Audit Log, Transform, Performance) +│ ├── 📁 resilience # Circuit Breaker, Retry Patterns +│ └── 📁 services # Shared Services (NotificationService, CachingService) +├── 📁 modules +│ ├── 📁 user # UserModule (จัดการ Users, Roles, Permissions) +│ ├── 📁 project # ProjectModule (จัดการ Projects, Organizations, Contracts) +│ ├── 📁 correspondence # CorrespondenceModule (จัดการเอกสารโต้ตอบ) +│ ├── 📁 rfa # RfaModule (จัดการเอกสารขออนุมัติ) +│ ├── 📁 drawing # DrawingModule (จัดการแบบแปลน) +│ ├── 📁 circulation # CirculationModule (จัดการใบเวียน) +│ ├── 📁 transmittal # TransmittalModule (จัดการเอกสารนำส่ง) +│ ├── 📁 search # SearchModule (ค้นหาขั้นสูงด้วย Elasticsearch) +│ ├── 📁 monitoring # MonitoringModule (Metrics, Health Checks) +│ └── 📁 document-numbering # DocumentNumberingModule (Internal Module) +└── 📁 database # Database Migration & Seeding Scripts +``` + +### **3.11 กลยุทธ์ความทนทานและการจัดการข้อผิดพลาด (Resilience & Error Handling Strategy)** + +* **Circuit Breaker Pattern:** ใช้สำหรับ external service calls (Email, LINE, Elasticsearch) +* **Retry Mechanism:** ด้วย exponential backoff สำหรับ transient failures +* **Fallback Strategies:** Graceful degradation เมื่อบริการภายนอกล้มเหลว +* **Error Handling:** Error messages ต้องไม่เปิดเผยข้อมูล sensitive +* **Monitoring:** Centralized error monitoring และ alerting system + +### **3.12 FileStorageService (ปรับปรุงใหม่):** + +* **Virus Scanning:** Integrate ClamAV สำหรับ scan ไฟล์ที่อัปโหลดทั้งหมด +* **File Type Validation:** ใช้ white-list approach (PDF, DWG, DOCX, XLSX, ZIP) +* **File Size Limits:** 50MB ต่อไฟล์ +* **Security Measures:** + * เก็บไฟล์นอก web root + * Download ผ่าน authenticated endpoint เท่านั้น + * Download links มี expiration time (24 ชั่วโมง) + * File integrity checks (checksum) + * Access control checks ก่อนดาวน์โหลด + +### **3.13 เเทคโนโลยีที่ใช้ (Technology Stack)** + +| ส่วน | Library/Tool | หมายเหตุ | +|---|---|---| +| **Framework** | `@nestjs/core`, `@nestjs/common` | Core Framework | +| **Language** | `TypeScript` | ใช้ TypeScript ทั้งระบบ | +| **Database** | `MariaDB 10.11` | ฐานข้อมูลหลัก | +| **ORM** | `@nestjs/typeorm`, `typeorm` | 🗃️จัดการการเชื่อมต่อและ Query ฐานข้อมูล | +| **Validation** | `class-validator`, `class-transformer` | 📦ตรวจสอบและแปลงข้อมูลใน DTO | +| **Auth** | `@nestjs/jwt`, `@nestjs/passport`, `passport-jwt` | 🔐การยืนยันตัวตนด้วย JWT | +|**Authorization** | `casl` | 🔐จัดการสิทธิ์แบบ RBAC | +| **File Upload** | `multer` | 📁จัดการการอัปโหลดไฟล์ | +| **Search** | `@nestjs/elasticsearch` | 🔍สำหรับการค้นหาขั้นสูง | +| **Notification** | `nodemailer` | 📬ส่งอีเมลแจ้งเตือน | +| **Scheduling** | `@nestjs/schedule` | 📬สำหรับ Cron Jobs (เช่น แจ้งเตือน Deadline) | +| **Logging** | `winston` | 📊บันทึก Log ที่มีประสิทธิภาพ | +| **Testing** | `@nestjs/testing`, `jest`, `supertest` | 🧪ทดสอบ Unit, Integration และ E2E | +| **Documentation** | `@nestjs/swagger` | 🌐สร้าง API Documentation อัตโนมัติ | +| **Security** | `helmet`, `rate-limiter-flexible` | 🛡️เพิ่มความปลอดภัยให้ API | +| **Resilience** | `@nestjs/circuit-breaker` | 🔄 Circuit breaker pattern | +| **Caching** | `@nestjs/cache-manager`, `cache-manager-redis-store` | 💾 Distributed caching | +| **Security** | `helmet`, `csurf`, `rate-limiter-flexible` | 🛡️ Security enhancements | +| **Validation** | `class-validator`, `class-transformer` | ✅ Input validation | +| **Monitoring** | `@nestjs/monitoring`, `winston` | 📊 Application monitoring | +| **File Processing** | `clamscan` | 🦠 Virus scanning | +| **Cryptography** | `bcrypt`, `crypto` | 🔐 Password hashing และ checksums | +| **JSON Validation** | `ajv`, `ajv-formats` | 🎯 JSON schema validation | +| **JSON Processing** | `jsonpath`, `json-schema-ref-parser` | 🔧 JSON manipulation | +| **Data Transformation** | `class-transformer` | 🔄 Object transformation | +| **Compression** | `compression` | 📦 JSON compression | + +### **3.14 Security Testing:** + +* **Penetration Testing:** ทดสอบ OWASP Top 10 vulnerabilities +* **Security Audit:** Review code สำหรับ security flaws +* **Virus Scanning Test:** ทดสอบ file upload security +* **Rate Limiting Test:** ทดสอบ rate limiting functionality + +### **3.15 Performance Testing:** + +* **Load Testing:** ทดสอบด้วย realistic workloads +* **Stress Testing:** หา breaking points ของระบบ +* **Endurance Testing:** ทดสอบการทำงานต่อเนื่องเป็นเวลานาน + +### 🗄️**3.16 Backend State Management** + +Backend (NestJS) ควรเป็น **Stateless** (ไม่เก็บสถานะ) "State" ทั้งหมดจะถูกจัดเก็บใน MariaDB + +* **Request-Scoped State (สถานะภายใน Request เดียว):** + * **ปัญหา:** จะส่งต่อข้อมูล (เช่น User ที่ล็อกอิน) ระหว่าง Guard และ Service ใน Request เดียวกันได้อย่างไร? + * **วิธีแก้:** ใช้ **Request-Scoped Providers** ของ NestJS (เช่น AuthContextService) เพื่อเก็บข้อมูล User ปัจจุบันที่ได้จาก AuthGuard และให้ Service อื่น Inject ไปใช้ +* **Application-Scoped State (การ Caching):** + * **ปัญหา:** ข้อมูล Master (เช่น roles, permissions, organizations) ถูกเรียกใช้บ่อย + * **วิธีแก้:** ใช้ **Caching** (เช่น @nestjs/cache-manager) เพื่อ Caching ข้อมูลเหล่านี้ และลดภาระ Database + +### **3.17 Caching Strategy (ตามข้อ 6.4.2):** + +* **Master Data Cache:** Roles, Permissions, Organizations (TTL: 1 hour) +* **User Session Cache:** User permissions และ profile (TTL: 30 minutes) +* **Search Result Cache:** Frequently searched queries (TTL: 15 minutes) +* **File Metadata Cache:** Attachment metadata (TTL: 1 hour) +* **Cache Invalidation:** Clear cache on update/delete operations + +### **3.18 การไหลของข้อมูล (Data Flow)** + +#### **3.18.1 Main Flow:** + + 1. Request: ผ่าน Nginx Proxy Manager -> NestJS Controller + 2. **Rate Limiting:** RateLimitGuard ตรวจสอบ request limits + 3. **Input Validation:** Validation Pipe ตรวจสอบและ sanitize inputs + 4. Authentication: JWT Guard ตรวจสอบ Token และดึงข้อมูล User + 5. Authorization: RBAC Guard ตรวจสอบสิทธิ์ + 6. **Security Checks:** Virus scanning (สำหรับ file upload), XSS protection + 7. Business Logic: Service Layer ประมวลผลตรรกะทางธุรกิจ + 8. **Resilience:** Circuit breaker และ retry logic สำหรับ external calls + 9. Data Access: Repository Layer ติดต่อกับฐานข้อมูล + 10. **Caching:** Cache frequently accessed data + 11. **Audit Log:** บันทึกการกระทำสำคัญ + 12. Response: ส่งกลับไปยัง Frontend + +#### **3.18.2 Workflow Data Flow:** + + 1. User สร้างเอกสาร → เลือก routing template + 2. System สร้าง routing instances ตาม template + 3. สำหรับแต่ละ routing step: + - กำหนด due date (จาก expected_days) + - ส่ง notification ไปยังองค์กรผู้รับ + - อัพเดทสถานะเป็น SENT + 4. เมื่อองค์กรผู้รับดำเนินการ: + - อัพเดทสถานะเป็น ACTIONED/FORWARDED/REPLIED + - บันทึก processed_by และ processed_at + - ส่ง notification ไปยังขั้นตอนต่อไป (ถ้ามี) + 5. เมื่อครบทุกขั้นตอน → อัพเดทสถานะเอกสารเป็น COMPLETED + +#### **3.18.3 JSON Details Processing Flow:** + + 1. **Receive Request** → Get JSON data from client + 2. **Schema Validation** → Validate against predefined schema + 3. **Data Sanitization** → Sanitize and transform data + 4. **Version Check** → Handle schema version compatibility + 5. **Storage** → Store validated JSON in database + 6. **Retrieval** → Retrieve and transform on demand + +### 📊**3.19 Monitoring & Observability (ตามข้อ 6.8)** + +#### **Application Monitoring:** + +* **Health Checks:** `/health` endpoint สำหรับ load balancer +* **Metrics Collection:** Response times, error rates, throughput +* **Distributed Tracing:** สำหรับ request tracing across services +* **Log Aggregation:** Structured logging ด้วย JSON format +* **Alerting:** สำหรับ critical errors และ performance degradation + +#### **Business Metrics:** + +* จำนวน documents created ต่อวัน +* Workflow completion rates +* User activity metrics +* System utilization rates +* Search query performance + +#### **Performance Targets:** + +* API Response Time: < 200ms (90th percentile) +* Search Query Performance: < 500ms +* File Upload Performance: < 30 seconds สำหรับไฟล์ 50MB +* Cache Hit Ratio: > 80% + +## 🖥️ **4. ฟรอนต์เอนด์ (Next.js) - Implementation Details** + +**โปรไฟล์นักพัฒนา (Developer Profile:** วิศวกร TypeScript + React/NextJS ระดับ Senior +เชี่ยวชาญ TailwindCSS, Shadcn/UI, และ Radix สำหรับการพัฒนา UI + +### **4.1 State Management & Offline Support** + +#### **4.1.1 Auto-Save Drafts** + +ใช้ `zustand` ร่วมกับ middleware `persist` (ลง LocalStorage) สำหรับฟอร์มที่มีขนาดใหญ่ (RFA, Correspondence) เพื่อป้องกันข้อมูลหายเมื่อเน็ตหลุด + +```typescript +// lib/stores/draft-store.ts +export const useDraftStore = create( + persist( + (set) => ({ + drafts: {}, + saveDraft: (key, data) => set((state) => ({ drafts: { ...state.drafts, [key]: data } })), + clearDraft: (key) => set((state) => { + const newDrafts = { ...state.drafts }; + delete newDrafts[key]; + return { drafts: newDrafts }; + }), + }), + { name: 'form-drafts' } + ) +); +``` + +### **4.2 Dynamic Form Generator** + +เพื่อรองรับ JSON Schema หลากหลายรูปแบบ ให้สร้าง Component กลางที่รับ Schema แล้ว Gen Form ออกมา (ลดการแก้ Code บ่อยๆ) + +* **Libraries:** แนะนำ `react-jsonschema-form` หรือสร้าง Wrapper บน `react-hook-form` ที่ Recursively render field ตาม Type +* **Validation:** ใช้ `ajv` ที่ฝั่ง Client เพื่อ Validate JSON ก่อน Submit + +### **4.3 Mobile Responsiveness (Card View)** + +ตารางข้อมูล (`DataTable`) ต้องมีความฉลาดในการแสดงผล: + +* **Desktop:** แสดงเป็น Table ปกติ +* **Mobile:** แปลงเป็น **Card View** โดยอัตโนมัติ (ซ่อน Header, แสดง Label คู่ Value ในแต่ละ Card) + +```tsx +// components/ui/responsive-table.tsx +
+ {/* Desktop View */}
+
+
+ {data.map((item) => ( + + {/* Mobile View: Render cells as list items */} + + ))} +
+``` + +### **4.4 Optimistic Updates** + +ใช้ความสามารถของ **TanStack Query** (`onMutate`) เพื่ออัปเดต UI ทันที (เช่น เปลี่ยนสถานะจาก "รออ่าน" เป็น "อ่านแล้ว") แล้วค่อยส่ง Request ไป Server ถ้า Failed ค่อย Rollback + +### **4.5 แนวทางการพัฒนาโค้ด (Code Implementation Guidelines)** + +* ใช้ **early returns** เพื่อความชัดเจน +* ใช้คลาสของ **TailwindCSS** ในการกำหนดสไตล์เสมอ +* ควรใช้ class: syntax แบบมีเงื่อนไข (หรือ utility clsx) มากกว่าการใช้ ternary operators ใน class strings +* ใช้ **const arrow functions** สำหรับ components และ handlers +* Event handlers ให้ขึ้นต้นด้วย handle... (เช่น handleClick, handleSubmit) +* รวมแอตทริบิวต์สำหรับการเข้าถึง (accessibility) ด้วย: + tabIndex="0", aria-label, onKeyDown, ฯลฯ +* ตรวจสอบให้แน่ใจว่าโค้ดทั้งหมด **สมบูรณ์**, **ผ่านการทดสอบ**, และ **ไม่ซ้ำซ้อน (DRY)** +* ต้อง import โมดูลที่จำเป็นต้องใช้อย่างชัดเจนเสมอ + +### **4.6 UI/UX ด้วย React** + +* ใช้ **semantic HTML** +* ใช้คลาสของ **Tailwind** ที่รองรับ responsive (sm:, md:, lg:) +* รักษาลำดับชั้นของการมองเห็น (visual hierarchy) ด้วยการใช้ typography และ spacing +* ใช้ **Shadcn** components (Button, Input, Card, ฯลฯ) เพื่อ UI ที่สอดคล้องกัน +* ทำให้ components มีขนาดเล็กและมุ่งเน้นการทำงานเฉพาะอย่าง +* ใช้ utility classes สำหรับการจัดสไตล์อย่างรวดเร็ว (spacing, colors, text, ฯลฯ) +* ตรวจสอบให้แน่ใจว่าสอดคล้องกับ **ARIA** และใช้ semantic markup + +### **4.7 การตรวจสอบฟอร์มและข้อผิดพลาด (Form Validation & Errors)** + +* ใช้ไลบรารีฝั่ง client เช่น zod และ react-hook-form +* แสดงข้อผิดพลาดด้วย **alert components** หรือข้อความ inline +* ต้องมี labels, placeholders, และข้อความ feedback + +### **🧪4.8 Frontend Testing** + +เราจะใช้ **React Testing Library (RTL)** สำหรับการทดสอบ Component และ **Playwright** สำหรับ E2E: + +* **Unit Tests (การทดสอบหน่วยย่อย):** + * **เครื่องมือ:** Vitest + RTL + * **เป้าหมาย:** ทดสอบ Component ขนาดเล็ก (เช่น Buttons, Inputs) หรือ Utility functions +* **Integration Tests (การทดสอบการบูรณาการ):** + * **เครื่องมือ:** RTL + **Mock Service Worker (MSW)** + * **เป้าหมาย:** ทดสอบว่า Component หรือ Page ทำงานกับ API (ที่จำลองขึ้น) ได้ถูกต้อง + * **เทคนิค:** ใช้ MSW เพื่อจำลอง NestJS API และทดสอบว่า Component แสดงผลข้อมูลจำลองได้ถูกต้องหรือไม่ (เช่น ทดสอบหน้า Dashboard [cite: 5.3] ที่ดึงข้อมูลจาก v_user_tasks) +* **E2E (End-to-End) Tests:** + * **เครื่องมือ:** **Playwright** + * **เป้าหมาย:** ทดสอบ User Flow ทั้งระบบโดยอัตโนมัติ (เช่น ล็อกอิน -> สร้าง RFA -> ตรวจสอบ Workflow Visualization [cite: 5.6]) + +### **🗄️4.9 Frontend State Management** + +สำหรับ Next.js App Router เราจะแบ่ง State เป็น 4 ระดับ: + +1. **Local UI State (สถานะ UI ชั่วคราว):** + * **เครื่องมือ:** useState, useReducer + * **ใช้เมื่อ:** จัดการสถานะเล็กๆ ที่จบใน Component เดียว (เช่น Modal เปิด/ปิด, ค่าใน Input) +2. **Server State (สถานะข้อมูลจากเซิร์ฟเวอร์):** + * **เครื่องมือ:** **React Query (TanStack Query)** หรือ SWR + * **ใช้เมื่อ:** จัดการข้อมูลที่ดึงมาจาก NestJS API (เช่น รายการ correspondences, rfas, drawings) + * **ทำไม:** React Query เป็น "Cache" ที่จัดการ Caching, Re-fetching, และ Invalidation ให้โดยอัตโนมัติ +3. **Global Client State (สถานะส่วนกลางฝั่ง Client):** + * **เครื่องมือ:** **Zustand** (แนะนำ) หรือ Context API + * **ใช้เมื่อ:** จัดการข้อมูลที่ต้องใช้ร่วมกันทั่วทั้งแอป และ *ไม่ใช่* ข้อมูลจากเซิร์ฟเวอร์ (เช่น ข้อมูล User ที่ล็อกอิน, สิทธิ์ Permissions) +4. **Form State (สถานะของฟอร์ม):** + * **เครื่องมือ:** **React Hook Form** + **Zod** + * **ใช้เมื่อ:** จัดการฟอร์มที่ซับซ้อน (เช่น ฟอร์มสร้าง RFA, ฟอร์ม Circulation [cite: 3.7]) + +## 🔐 **5. Security & Access Control (Full Stack Integration)** + +### **5.1 CASL Integration (Shared Ability)** + +* **Backend:** ใช้ CASL กำหนด Permission Rule +* **Frontend:** ให้ดึง Rule (JSON) จาก Backend มา Load ใส่ `@casl/react` เพื่อให้ Logic การ Show/Hide ปุ่ม ตรงกัน 100% + +### **5.2 Maintenance Mode** + +เพิ่ม Middleware (ทั้ง NestJS และ Next.js) เพื่อตรวจสอบ Flag ใน Redis: + +* ถ้า `MAINTENANCE_MODE = true` +* **API:** Return `503 Service Unavailable` (ยกเว้น Admin IP) +* **Frontend:** Redirect ไปหน้า `/maintenance` + +### **5.3 Idempotency Client** + +สร้าง Axios Interceptor เพื่อ Generate `Idempotency-Key` สำหรับ POST/PUT/DELETE requests ทุกครั้ง + +```typescript +// lib/api/client.ts +import { v4 as uuidv4 } from 'uuid'; + +apiClient.interceptors.request.use((config) => { + if (['post', 'put', 'delete'].includes(config.method)) { + config.headers['Idempotency-Key'] = uuidv4(); + } + return config; +}); +``` + +### **5.4 RBAC และการควบคุมสิทธิ์ (RBAC & Permission Control)** + +ใช้ Decorators เพื่อบังคับใช้สิทธิ์การเข้าถึง โดยอ้างอิงสิทธิ์จากตาราง permissions + +```typescript +@RequirePermission('rfas.respond') // ต้องตรงกับ 'permission_code' +@Put(':id') +updateRFA(@Param('id') id: string) { + return this.rfaService.update(id); +} +``` + +#### **5.4.1 Roles (บทบาท)** + +* **Superadmin**: ไม่มีข้อจำกัดใดๆ [cite: 4.3] +* **Admin**: มีสิทธิ์เต็มที่ในองค์กร [cite: 4.3] +* **Document Control**: เพิ่ม/แก้ไข/ลบ เอกสารในองค์กร [cite: 4.3] +* **Editor**: สามารถ เพิ่ม/แก้ไข เอกสารที่กำหนด [cite: 4.3] +* **Viewer**: สามารถดู เอกสาร [cite: 4.3] + +#### **5.4.2 ตัวอย่าง Permissions (จากตาราง permissions)** + +* rfas.view, rfas.create, rfas.respond, rfas.delete +* drawings.view, drawings.upload, drawings.delete +* corr.view, corr.manage +* transmittals.manage +* cirs.manage +* project_parties.manage + +การจับคู่ระหว่าง roles และ permissions **เริ่มต้น** จะถูก seed ผ่านสคริปต์ (ดังที่เห็นในไฟล์ SQL)**อย่างไรก็ตาม AuthModule/UserModule ต้องมี API สำหรับ Admin เพื่อสร้าง Role ใหม่และกำหนดสิทธิ์ (Permissions) เพิ่มเติมได้ในภายหลัง** [cite: 4.3] + +## 📊 **6. Notification & Background Jobs** + +### **6.1 Digest Notification** + +ห้ามส่ง Email ทันทีที่เกิด Event ให้: + +1. Push Event ลง Queue (Redis/BullMQ) +2. มี Processor รอเวลา (เช่น 5 นาที) เพื่อ Group Events ที่คล้ายกัน (เช่น "คุณมีเอกสารรออนุมัติ 5 ฉบับ") +3. ส่ง Email เดียว (Digest) เพื่อลด Spam + +## 🔗 **7. แนวทางการบูรณาการ Full Stack (Full Stack Integration Guidelines)** + +| Aspect (แง่มุม) | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) | +| :---- | :---- | :---- | :---- | +| API | REST / GraphQL Controllers | API hooks ผ่าน fetch/axios/SWR | Components ที่รับข้อมูล | +| Validation (การตรวจสอบ) | class-validator DTOs | zod / react-hook-form | สถานะของฟอร์ม/input ใน Shadcn | +| Auth (การยืนยันตัวตน) | Guards, JWT | NextAuth / cookies | สถานะ UI ของ Auth (loading, signed in) | +| Errors (ข้อผิดพลาด) | Global filters | Toasts / modals | Alerts / ข้อความ feedback | +| Testing (การทดสอบ) | Jest (unit/e2e) | Vitest / Playwright | Visual regression | +| Styles (สไตล์) | Scoped modules (ถ้าจำเป็น) | Tailwind / Shadcn | Tailwind utilities | +| Accessibility (การเข้าถึง) | Guards + filters | ARIA attributes | Semantic HTML | + +## 🗂️ **8. ข้อตกลงเฉพาะสำหรับ DMS (LCBP3-DMS)** + +ส่วนนี้ขยายแนวทาง FullStackJS ทั่วไปสำหรับโปรเจกต์ **LCBP3-DMS** โดยมุ่งเน้นไปที่เวิร์กโฟลว์การอนุมัติเอกสาร (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation) + +### 🧾**8.1 มาตรฐาน AuditLog (AuditLog Standard)** + +บันทึกการดำเนินการ CRUD และการจับคู่ทั้งหมดลงในตาราง audit_logs + +| Field (ฟิลด์) | Type (จาก SQL) | Description (คำอธิบาย) | +| :---- | :---- | :---- | +| audit_id | BIGINT | Primary Key | +| user_id | INT | ผู้ใช้ที่ดำเนินการ (FK -> users) | +| action | VARCHAR(100) | rfa.create, correspondence.update, login.success | +| entity_type | VARCHAR(50) | ชื่อตาราง/โมดูล เช่น 'rfa', 'correspondence' | +| entity_id | VARCHAR(50) | Primary ID ของระเบียนที่ได้รับผลกระทบ | +| details_json | JSON | ข้อมูลบริบท (เช่น ฟิลด์ที่มีการเปลี่ยนแปลง) | +| ip_address | VARCHAR(45) | IP address ของผู้ดำเนินการ | +| user_agent | VARCHAR(255) | User Agent ของผู้ดำเนินการ | +| created_at | TIMESTAMP | Timestamp (UTC) | + +### 📂**8.2 การจัดการไฟล์ (File Handling)** + +#### **8.2.1 มาตรฐานการอัปโหลดไฟล์ (File Upload Standard)** + +* **Security-First Approach:** การอัปโหลดไฟล์ทั้งหมดจะถูกจัดการโดย FileStorageService ที่มี security measures ครบถ้วน +* ไฟล์จะถูกเชื่อมโยงไปยัง Entity ที่ถูกต้องผ่าน **ตารางเชื่อม (Junction Tables)** เท่านั้น: + * correspondence_attachments (เชื่อม Correspondence กับ Attachments) + * circulation_attachments (เชื่อม Circulation กับ Attachments) + * shop_drawing_revision_attachments (เชื่อม Shop Drawing Revision กับ Attachments) + * contract_drawing_attachments (เชื่อม Contract Drawing กับ Attachments) +* เส้นทางจัดเก็บไฟล์ (Upload path): อ้างอิงจาก Requirement 2.1 คือ /share/dms-data [cite: 2.1] โดย FileStorageService จะสร้างโฟลเดอร์ย่อยแบบรวมศูนย์ (เช่น /share/dms-data/uploads/{YYYY}/{MM}/[stored_filename]) +* ประเภทไฟล์ที่อนุญาต: pdf, dwg, docx, xlsx, zip (ผ่าน white-list validation) +* ขนาดสูงสุด: **50 MB** +* จัดเก็บนอก webroot +* ให้บริการไฟล์ผ่าน endpoint ที่ปลอดภัย /files/:attachment_id/download + +#### **8.2.2 Security Controls สำหรับ File Access:** + +การเข้าถึงไฟล์ไม่ใช่การเข้าถึงโดยตรง endpoint /files/:attachment_id/download จะต้อง: + +1. ค้นหาระเบียน attachment +2. ตรวจสอบว่า attachment_id นี้ เชื่อมโยงกับ Entity ใด (เช่น correspondence, circulation, shop_drawing_revision, contract_drawing) ผ่านตารางเชื่อม +3. ตรวจสอบว่าผู้ใช้มีสิทธิ์ (permission) ในการดู Entity ต้นทางนั้นๆ หรือไม่ +4. ตรวจสอบ download token expiration (24 ชั่วโมง) +5. บันทึก audit log การดาวน์โหลด + +### 🔟**8.3 การจัดการเลขที่เอกสาร (Document Numbering) [cite: 3.10]** + +* **เป้าหมาย:** สร้างเลขที่เอกสาร (เช่น correspondence_number) โดยอัตโนมัติ ตามรูปแบบที่กำหนด +* **ตรรกะการนับ:** การนับ Running number (SEQ) จะนับแยกตาม Key: **Project + Originator Organization + Document Type + Year** +* **ตาราง SQL:** + * document_number_formats: Admin ใช้กำหนด "รูปแบบ" (Template) ของเลขที่ (เช่น {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}) โดยกำหนดตาม **Project** และ **Document Type** [cite: 4.5] + * document_number_counters: ระบบใช้เก็บ "ตัวนับ" ล่าสุดของ Key (Project+Org+Type+Year) +* **การทำงาน (Backend):** + * DocumentNumberingModule จะให้บริการ DocumentNumberingService + * เมื่อ CorrespondenceModule ต้องการสร้างเอกสารใหม่, มันจะเรียก documentNumberingService.generateNextNumber(...) + * Service นี้จะใช้ **Redis distributed locking** แทน stored procedure ซึ่งจะจัดการ Database Transaction และ Row Locking ภายใน Application Layer เพื่อรับประกันการป้องกัน Race Condition + * มี retry mechanism และ fallback strategies + +### 📊**8.4 การรายงานและการส่งออก (Reporting & Exports)** + +#### **8.4.1 วิวสำหรับการรายงาน (Reporting Views) (จาก SQL)** + +การรายงานควรสร้างขึ้นจาก Views ที่กำหนดไว้ล่วงหน้าในฐานข้อมูลเป็นหลัก: + +* v_current_correspondences: สำหรับ revision ปัจจุบันทั้งหมดของเอกสารที่ไม่ใช่ RFA +* v_current_rfas: สำหรับ revision ปัจจุบันทั้งหมดของ RFA และข้อมูล master +* v_contract_parties_all: สำหรับการตรวจสอบความสัมพันธ์ของ project/contract/organization +* v_user_tasks: สำหรับ Dashboard "งานของฉัน" +* v_audit_log_details: สำหรับ Activity Feed + +Views เหล่านี้ทำหน้าที่เป็นแหล่งข้อมูลหลักสำหรับการรายงานฝั่งเซิร์ฟเวอร์และการส่งออกข้อมูล + +#### **8.4.2 กฎการส่งออก (Export Rules)** + +* Export formats: CSV, Excel, PDF. +* จัดเตรียมมุมมองสำหรับพิมพ์ (Print view). +* รวมลิงก์ไปยังต้นทาง (เช่น /rfas/:id). + +## 🧮 **9. ฟรอนต์เอนด์: รูปแบบ DataTable และฟอร์ม (Frontend: DataTable & Form Patterns)** + +### **9.1 DataTable (Server‑Side)** + +* Endpoint: /api/{module}?page=1&pageSize=20&sort=...&filter=... +* ต้องรองรับ: การแบ่งหน้า (pagination), การเรียงลำดับ (sorting), การค้นหา (search), การกรอง (filters) +* แสดง revision ล่าสุดแบบ inline เสมอ (สำหรับ RFA/Drawing) + +### **9.2 มาตรฐานฟอร์ม (Form Standards)** + +* ต้องมีการใช้งาน Dropdowns แบบขึ้นต่อกัน (Dependent dropdowns) (ตามที่สคีมารองรับ): + * Project → Contract Drawing Volumes + * Contract Drawing Category → Sub-Category + * RFA (ประเภท Shop Drawing) → Shop Drawing Revisions ที่เชื่อมโยงได้ +* **File Upload Security:** ต้องรองรับ **Multi-file upload (Drag-and-Drop)** [cite: 5.7] พร้อม virus scanning feedback +* **File Type Indicators:** UI ต้องอนุญาตให้ผู้ใช้กำหนดว่าไฟล์ใดเป็น **"เอกสารหลัก"** หรือ "เอกสารแนบประกอบ" [cite: 5.7] พร้อมแสดง file type icons +* **Security Feedback:** แสดง security warnings สำหรับ file types ที่เสี่ยงหรือ files ที่ fail virus scan +* ส่ง (Submit) ผ่าน API พร้อม feedback แบบ toast + +### **9.3 ข้อกำหนด Component เฉพาะ (Specific UI Requirements)** + +* **Dashboard - My Tasks:** ต้องพัฒนา Component ตาราง "งานของฉัน" (My Tasks)ซึ่งดึงข้อมูลงานที่ผู้ใช้ล็อกอินอยู่ต้องรับผิดชอบ (Main/Action) จาก v_user_tasks [cite: 5.3] +* **Workflow Visualization:** ต้องพัฒนา Component สำหรับแสดงผล Workflow (โดยเฉพาะ RFA)ที่แสดงขั้นตอนทั้งหมดเป็นลำดับ โดยขั้นตอนปัจจุบัน (active) เท่านั้นที่ดำเนินการได้ และขั้นตอนอื่นเป็น disabled [cite: 5.6] ต้องมีตรรกะสำหรับ Admin ในการ override หรือย้อนกลับขั้นตอนได้ [cite: 5.6] +* **Admin Panel:** ต้องมีหน้า UI สำหรับ Superadmin/Admin เพื่อจัดการข้อมูลหลัก (Master Data [cite: 4.5]), การเริ่มต้นใช้งาน (Onboarding [cite: 4.6]), และ **รูปแบบเลขที่เอกสาร (Numbering Formats [cite: 3.10])** +* **Security Dashboard:** แสดง security metrics และ audit logs สำหรับ administrators + +## 🧭 **10. แดชบอร์ดและฟีดกิจกรรม (Dashboard & Activity Feed)** + +### **10.1 การ์ดบนแดชบอร์ด (Dashboard Cards)** + +* แสดง Correspondences, RFAs, Circulations, Shop Drawing Revision ล่าสุด +* รวมสรุป KPI (เช่น "RFAs ที่รอการอนุมัติ", "Shop Drawing ที่รอการอนุมัติ") [cite: 5.3] +* รวมลิงก์ด่วนไปยังโมดูลต่างๆ +* **Security Metrics:** แสดงจำนวน files scanned, security incidents, failed login attempts + +### **10.2 ฟีดกิจกรรม (Activity Feed)** + +* แสดงรายการ v_audit_log_details ล่าสุด (10 รายการ) ที่เกี่ยวข้องกับผู้ใช้ +* รวม security-related activities (failed logins, permission changes) + +```typescript +// ตัวอย่าง API response +[ + { user: 'editor01', action: 'Updated RFA (LCBP3-RFA-001)', time: '2025-11-04T09:30Z' }, + { user: 'system', action: 'Virus scan completed - 0 threats found', time: '2025-11-04T09:25Z' } +] +``` + +## 🛡️ **11. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)** + +ส่วนนี้สรุปข้อกำหนด Non-Functional จาก requirements.md เพื่อให้ทีมพัฒนาทาน + +* **Audit Log [cite: 6.1]:** ทุกการกระทำที่สำคัญ (C/U/D) ต้องถูกบันทึกใน audit_logs +* **Performance [cite: 6.4]:** ต้องใช้ Caching สำหรับข้อมูลที่เรียกบ่อย และใช้ Pagination +* **Security [cite: 6.5]:** ต้องมี Rate Limiting และจัดการ Secret ผ่าน docker-compose.yml (ไม่ใช่ .env) +* **File Security [cite: 3.9.6]:** ต้องมี virus scanning, file type validation, access controls +* **Resilience [cite: 6.5.3]:** ต้องมี circuit breaker, retry mechanisms, graceful degradation +* **Backup & Recovery [cite: 6.6]:** ต้องมีแผนสำรองข้อมูลทั้ง Database (MariaDB) และ File Storage (/share/dms-data) อย่างน้อยวันละ 1 ครั้ง +* **Notification Strategy [cite: 6.7]:** ระบบแจ้งเตือน (Email/Line) ต้องถูก Trigger เมื่อมีเอกสารใหม่ส่งถึง, มีการมอบหมายงานใหม่ (Circulation), หรือ (ทางเลือก) เมื่องานเสร็จ/ใกล้ถึงกำหนด +* **Monitoring [cite: 6.8]:** ต้องมี health checks, metrics collection, alerting + +## ✅ **12. มาตรฐานที่นำไปใช้แล้ว (จาก SQL v1.4.0) (Implemented Standards (from SQL v1.4.0))** + +ส่วนนี้ยืนยันว่าแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้เป็นส่วนหนึ่งของการออกแบบฐานข้อมูลอยู่แล้ว และควรถูกนำไปใช้ประโยชน์ ไม่ใช่สร้างขึ้นใหม่ + +* ✅ **Soft Delete:** นำไปใช้แล้วผ่านคอลัมน์ deleted_at ในตารางสำคัญ (เช่น correspondences, rfas, project_parties) ตรรกะการดึงข้อมูลต้องกรอง deleted_at IS NULL +* ✅ **Database Indexes:** สคีมาได้มีการทำ index ไว้อย่างหนักหน่วงบน foreign keys และคอลัมน์ที่ใช้ค้นหาบ่อย (เช่น idx_rr_rfa, idx_cor_project, idx_cr_is_current) เพื่อประสิทธิภาพ +* ✅ **โครงสร้าง RBAC:** มีระบบ users, roles, permissions, user_roles, และ user_project_roles ที่ครอบคลุมอยู่แล้ว +* ✅ **Data Seeding:** ข้อมูล Master (roles, permissions, organization_roles, initial users, project parties) ถูกรวมอยู่ในสคริปต์สคีมาแล้ว +* ✅ **Application-level Locking:** ใช้ Redis distributed lock แทน stored procedure +* ✅ **File Security:** Virus scanning, file type validation, access control +* ✅ **Resilience Patterns:** Circuit breaker, retry, fallback mechanisms +* ✅ **Security Measures:** Input validation, rate limiting, security headers +* ✅ **Monitoring:** Health checks, metrics collection, distributed tracing + +## 🧩 **13. การปรับปรุงที่แนะนำ (สำหรับอนาคต) (Recommended Enhancements (Future))** + +* ✅ สร้าง Background job (โดยใช้ **n8n** เพื่อเชื่อมต่อกับ **Line** [cite: 2.7] และ/หรือใช้สำหรับการแจ้งเตือน RFA ที่ใกล้ถึงกำหนด due_date [cite: 6.7]) +* ✅ เพิ่ม job ล้างข้อมูลเป็นระยะสำหรับ attachments ที่ไม่ถูกเชื่อมโยงกับ Entity ใดๆ เลย (ไฟล์กำพร้า) +* 🔄 **AI-Powered Document Classification:** ใช้ machine learning สำหรับ automatic document categorization +* 🔄 **Advanced Analytics:** Predictive analytics สำหรับ workflow optimization +* 🔄 **Mobile App:** Native mobile application สำหรับ field workers +* 🔄 **Blockchain Integration:** สำหรับ document integrity verification ที่ต้องการความปลอดภัยสูงสุด + +## ✅ **14. Summary Checklist for Developers** + +ก่อนส่ง PR (Pull Request) นักพัฒนาต้องตรวจสอบหัวข้อต่อไปนี้: + +* [ ] **Security:** ไม่มี Secrets ใน Code, ใช้ `docker-compose.override.yml` แล้ว +* [ ] **Concurrency:** ใช้ Optimistic Lock ใน Entity ที่เสี่ยง Race Condition แล้ว +* [ ] **Idempotency:** API รองรับ Idempotency Key แล้ว +* [ ] **File Upload:** ใช้ Flow Two-Phase (Temp -> Perm) แล้ว +* [ ] **Mobile:** หน้าจอแสดงผลแบบ Card View บนมือถือได้ถูกต้อง +* [ ] **Performance:** สร้าง Index สำหรับ JSON Virtual Columns แล้ว (ถ้ามี) + +--- + +## 📋 **15. Summary of Key Changes from Previous Version** + +### **Security Enhancements:** + +1. **File Upload Security** - Virus scanning, file type validation, access controls +2. **Input Validation** - OWASP Top 10 protection, XSS/CSRF prevention +3. **Rate Limiting** - Comprehensive rate limiting strategy +4. **Secrets Management** - Secure handling of sensitive configuration + +### **Architecture Improvements:** + +1. **Document Numbering** - Changed from Stored Procedure to Application-level Locking +2. **Resilience Patterns** - Circuit breaker, retry mechanisms, fallback strategies +3. **Monitoring & Observability** - Health checks, metrics, distributed tracing +4. **Caching Strategy** - Comprehensive caching with proper invalidation + +### **Performance Targets :** + +1. **API Response Time** - < 200ms (90th percentile) +2. **Search Performance** - < 500ms +3. **File Upload** - < 30 seconds for 50MB files +4. **Cache Hit Ratio** - > 80% + +### **Operational Excellence:** + +1. **Disaster Recovery** - RTO < 4 hours, RPO < 1 hour +2. **Backup Procedures** - Comprehensive backup and restoration +3. **Security Testing** - Penetration testing and security audits +4. **Performance Testing** - Load testing with realistic workloads + +เอกสารนี้สะท้อนถึงความมุ่งมั่นในการสร้างระบบที่มีความปลอดภัย, มีความทนทาน, และมีประสิทธิภาพสูง พร้อมรองรับการเติบโตในอนาคตและความต้องการทางธุรกิจที่เปลี่ยนแปลงไป + +**หมายเหตุ:** แนวทางนี้จะถูกทบทวนและปรับปรุงเป็นระยะตาม feedback จากทีมพัฒนาและความต้องการทางธุรกิจที่เปลี่ยนแปลงไป + +--- + +`End of FullStackJS Guidelines v1.4.2` diff --git a/Documnets/Project/2_Backend_Plan_V1_4_2.md b/Documnets/Project/2_Backend_Plan_V1_4_2.md new file mode 100644 index 0000000..07b106a --- /dev/null +++ b/Documnets/Project/2_Backend_Plan_V1_4_2.md @@ -0,0 +1,539 @@ +# 📋 **แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.2 (ฉบับปรับปรุง)** + +**อ้างอิง:** Requirements v1.4.2 & FullStackJS Guidelines v1.4.2 +**จุดเน้นสำคัญ:** Concurrency Control, Data Integrity, Unified Workflow, Idempotency + +----- + +## 🎯 **ภาพรวมโครงการ** + +พัฒนา Backend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่มีความปลอดภัยสูง รองรับการทำงานพร้อมกัน (Concurrency) ได้อย่างถูกต้องแม่นยำ มีสถาปัตยกรรมที่ยืดหยุ่นต่อการขยายตัว และรองรับการจัดการเอกสารที่ซับซ้อน มีระบบ Workflow การอนุมัติ และการควบคุมสิทธิ์แบบ RBAC 4 ระดับ พร้อมมาตรการความปลอดภัยที่ทันสมัย + +----- + +## 📐 **สถาปัตยกรรมระบบ** + +### **Technology Stack (Updated)** + +- **Framework:** NestJS (TypeScript, ESM) +- **Database:** MariaDB 10.11 (ใช้ Virtual Columns) +- **ORM:** TypeORM (ใช้ Optimistic Locking) +- **Authentication:** JWT + Passport +- **Authorization:** CASL (RBAC 4-level) +- **File Upload:** Multer + Virus Scanning (ClamAV) + Two-Phase Storage +- **Search:** Elasticsearch +- **Notification:** Nodemailer + n8n (Line Integration) + BullMQ Queue +- **Caching/Locking:** Redis (Redlock) สำหรับ Distributed Locking +- **Queue:** BullMQ (Redis) สำหรับ Notification Batching และ Async Jobs +- **Resilience:** Circuit Breaker, Retry Patterns +- **Security:** Helmet, CSRF Protection, Rate Limiting, Idempotency +- **Monitoring:** Winston, Health Checks, Metrics +- **Scheduling:** @nestjs/schedule (Cron Jobs) +- **Documentation:** Swagger +- **Validation:** Zod / Class-validator + +### **โครงสร้างโมดูล (Domain-Driven)** + +```tree +src/ +├── common/ # Shared Module +│ ├── auth/ # JWT, Guards, RBAC +│ ├── config/ # Configuration Management +│ ├── decorators/ # @RequirePermission, @RateLimit +│ ├── entities/ # Base Entities +│ ├── exceptions/ # Global Filters +│ ├── file-storage/ # FileStorageService (Virus Scanning + Two-Phase) +│ ├── guards/ # RBAC Guard, RateLimitGuard +│ ├── interceptors/ # Audit, Transform, Performance, Idempotency +│ ├── resilience/ # Circuit Breaker, Retry Patterns +│ ├── security/ # Input Validation, XSS Protection +│ ├── idempotency/ # [New] Idempotency Logic +│ └── maintenance/ # [New] Maintenance Mode Guard +├── modules/ +│ ├── user/ # Users, Roles, Permissions +│ ├── project/ # Projects, Contracts, Organizations +│ ├── master/ # Master Data Management +│ ├── correspondence/ # Correspondence Management +│ ├── rfa/ # RFA & Workflows +│ ├── drawing/ # Shop/Contract Drawings +│ ├── circulation/ # Internal Circulation +│ ├── transmittal/ # Transmittals +│ ├── search/ # Elasticsearch +│ ├── monitoring/ # Metrics, Health Checks +│ ├── workflow-engine/ # [New] Unified Workflow Logic +│ ├── document-numbering/ # [Update] Double-Locking Logic +│ ├── notification/ # [Update] Queue & Digest +│ └── file-storage/ # [Update] Two-Phase Commit +└── database/ # Migrations & Seeds +``` + +----- + +## 🗓️ **แผนการพัฒนาแบบ Phase-Based** + +- *(Dependency Diagram ถูกละไว้เพื่อประหยัดพื้นที่ เนื่องจากมีการอ้างอิงจากแผนเดิม)* + +## **Phase 0: Infrastructure & Configuration (สัปดาห์ที่ 1)** + +**Milestone:** โครงสร้างพื้นฐานพร้อม รองรับ Secrets ที่ปลอดภัย และ Redis พร้อมใช้งาน + +### **Phase 0: Tasks** + +- **[ ] T0.1 Secure Configuration Setup** + + - [ ] ปรับปรุง `ConfigModule` ให้รองรับการอ่านค่าจาก Environment Variables + - [ ] สร้าง Template `docker-compose.override.yml.example` สำหรับ Dev + - [ ] Validate Config ด้วย Joi/Zod ตอน Start App (Throw error ถ้าขาด Secrets) + - [ ] **Security:** Setup network segmentation และ firewall rules + - [ ] **Deliverable:** Configuration Management พร้อมใช้งานอย่างปลอดภัย + - [ ] **Dependencies:** None (Task เริ่มต้น) + +- **[ ] T0.2 Redis & Queue Infrastructure** + + - [ ] Setup Redis Container + - [ ] Setup BullMQ Module ใน NestJS สำหรับจัดการ Background Jobs + - [ ] Setup Redis Client สำหรับ Distributed Lock (Redlock) + - [ ] **Security:** Setup Redis authentication และ encryption + - [ ] **Deliverable:** Redis และ Queue System พร้อมใช้งาน + - [ ] **Dependencies:** T0.1 + +- **[ ] T0.3 Setup Database Connection** + + - [ ] Import SQL Schema v1.4.2 เข้า MariaDB + - [ ] Run Seed Data (organizations, users, roles, permissions) + - [ ] Configure TypeORM ใน AppModule + - [ ] **Security:** Setup database connection encryption + - [ ] ทดสอบ Connection + - [ ] **Deliverable:** Database พร้อมใช้งาน, มี Seed Data + - [ ] **Dependencies:** T0.1 + +- **[ ] T0.4 Setup Git Repository** + + - [ ] สร้าง Repository ใน Gitea (git.np-dms.work) + - [ ] Setup .gitignore, README.md, SECURITY.md + - [ ] Commit Initial Project + - [ ] **Deliverable:** Code อยู่ใน Version Control + - [ ] **Dependencies:** T0.1, T0.2, T0.3 + +----- + +## **Phase 1: Core Foundation & Security (สัปดาห์ที่ 2-3)** + +**Milestone:** ระบบ Authentication, Authorization, Idempotency พื้นฐาน และ Security Baseline + +### **Phase 1: Tasks** + +- **[ ] T1.1 CommonModule - Base Infrastructure** + + - [ ] สร้าง Base Entity (id, created\_at, updated\_at, deleted\_at) + - [ ] สร้าง Global Exception Filter (ไม่เปิดเผย sensitive information) + - [ ] สร้าง Response Transform Interceptor + - [ ] สร้าง Audit Log Interceptor + - [ ] **[New] Idempotency Interceptor:** ตรวจสอบ Header `Idempotency-Key` และ Cache Response เดิมใน Redis + - [ ] **[New] Maintenance Mode Middleware:** ตรวจสอบ Flag ใน **Redis Key** เพื่อ Block API ระหว่างปรับปรุงระบบ **(Admin ใช้ Redis/Admin UI ในการ Toggle สถานะ)** + - [ ] สร้าง RequestContextService - สำหรับเก็บข้อมูลระหว่าง Request + - [ ] สร้าง ConfigService - Centralized configuration management + - [ ] สร้าง CryptoService - สำหรับ encryption/decryption + - [ ] **Security:** Implement input validation pipeline + - [ ] **Deliverable:** Common Services พร้อมใช้ รวมถึง Idempotency และ Maintenance Mode + - [ ] **Dependencies:** T0.2, T0.3 + +- **[ ] T1.2 AuthModule - JWT Authentication** + + - [ ] สร้าง Entity: User + - [ ] สร้าง AuthService: + - [ ] login(username, password) → JWT Token + - [ ] validateUser(username, password) → User | null + - [ ] Password Hashing (bcrypt) + salt + - [ ] สร้าง JWT Strategy (Passport) + - [ ] สร้าง JwtAuthGuard + - [ ] สร้าง Controllers: + - [ ] POST /auth/login → { access\_token, refresh\_token } + - [ ] POST /auth/register → Create User (Admin only) + - [ ] POST /auth/refresh → Refresh token + - [ ] POST /auth/logout → Revoke token + - [ ] GET /auth/profile (Protected) + - [ ] **Security:** Implement rate limiting สำหรับ authentication endpoints + - [ ] **Deliverable:** ล็อกอิน/ล็อกเอาต์ทำงานได้อย่างปลอดภัย + - [ ] **Dependencies:** T1.1, T0.3 + +- **[ ] T1.3 UserModule - User Management** + + - [ ] สร้าง Entities: User, Role, Permission, UserRole, UserAssignment, **UserPreference** + - [ ] สร้าง UserService CRUD (พร้อม soft delete) + - [ ] สร้าง RoleService CRUD + - [ ] สร้าง PermissionService (Read-Only, จาก Seed) + - [ ] สร้าง UserAssignmentService - สำหรับจัดการ user assignments ตาม scope + - [ ] สร้าง **UserPreferenceService** - สำหรับจัดการการตั้งค่า Notification และ UI + - [ ] สร้าง Controllers: + - [ ] GET /users → List Users (Paginated) + - [ ] GET /users/:id → User Detail + - [ ] POST /users → Create User (ต้องบังคับเปลี่ยน password ครั้งแรก) + - [ ] PUT /users/:id → Update User + - [ ] DELETE /users/:id → Soft Delete + - [ ] GET /roles → List Roles + - [ ] POST /roles → Create Role (Admin) + - [ ] PUT /roles/:id/permissions → Assign Permissions + - [ ] **Security:** Implement permission checks สำหรับ user management + - [ ] **Deliverable:** จัดการผู้ใช้, Role, และ Preferences ได้ + - [ ] **Dependencies:** T1.1, T1.2 + +- **[ ] T1.4 RBAC Guard - 4-Level Authorization** + + - [ ] สร้าง @RequirePermission() Decorator + - [ ] สร้าง RbacGuard ที่ตรวจสอบ 4 ระดับ: + - [ ] Global Permissions + - [ ] Organization Permissions + - [ ] Project Permissions + - [ ] Contract Permissions + - [ ] Permission Hierarchy Logic + - [ ] Integration กับ CASL + - [ ] **Security:** Implement audit logging สำหรับ permission checks + - [ ] **Deliverable:** ระบบสิทธิ์ทำงานได้ทั้ง 4 ระดับ + - [ ] **Dependencies:** T1.1, T1.3 + +- **[ ] T1.5 ProjectModule - Base Structures** + + - [ ] สร้าง Entities: + - [ ] Organization + - [ ] Project + - [ ] Contract + - [ ] ProjectOrganization (Junction) + - [ ] ContractOrganization (Junction) + - [ ] สร้าง Services & Controllers: + - [ ] GET /organizations → List + - [ ] POST /projects → Create (Superadmin) + - [ ] GET /projects/:id/contracts → List Contracts + - [ ] POST /projects/:id/contracts → Create Contract + - [ ] **Security:** Implement data isolation ระหว่าง organizations + - [ ] **Deliverable:** จัดการโครงสร้างโปรเจกต์ได้ + - [ ] **Dependencies:** T1.1, T1.2, T0.3 + +----- + +## **Phase 2: High-Integrity Data & File Management (สัปดาห์ที่ 4)** + +**Milestone:** Master Data, ระบบจัดการไฟล์แบบ Transactional, Document Numbering ที่ไม่มี Race Condition, JSON details system พร้อมใช้งาน + +### **Phase 2: Tasks** + +- **[ ] T2.1 Virtual Columns for JSON** + + - [ ] ออกแบบ Migration Script สำหรับตารางที่มี JSON Details + - [ ] เพิ่ม **Generated Columns (Virtual)** สำหรับฟิลด์ที่ใช้ Search บ่อยๆ (เช่น `project_id`, `type`) พร้อม Index + - [ ] **Security:** Implement admin-only access สำหรับ master data + - [ ] **Deliverable:** JSON Data Search Performance ดีขึ้น + - [ ] **Dependencies:** T0.3, T1.1, T1.5 + +- **[ ] T2.2 FileStorageService - Two-Phase Storage** + + - [ ] สร้าง Attachment Entity + - [ ] สร้าง FileStorageService: + - [ ] **Phase 1 (Upload):** API รับไฟล์ → Scan Virus → Save ลง `temp/` → Return `temp_id` + - [ ] **Phase 2 (Commit):** Method `commitFiles(tempIds[])` → ย้ายจาก `temp/` ไป `permanent/{YYYY}/{MM}/` → Update DB + - [ ] File type validation (white-list: PDF, DWG, DOCX, XLSX, PPTX, ZIP) + - [ ] File size check (max 50MB) + - [ ] Generate checksum (SHA-256) + - [ ] **Cleanup Job:** สร้าง Cron Job ลบไฟล์ใน `temp/` ที่ค้างเกิน 24 ชม. **โดยตรวจสอบจากคอลัมน์ `expires_at` ในตาราง `attachments`** + - [ ] สร้าง Controller: + - [ ] POST /files/upload → { temp\_id } (Protected) + - [ ] POST /files/commit → { attachment\_id, url } (Protected) + - [ ] GET /files/:id/download → File Stream (Protected + Expiration) + - [ ] **Security:** Access Control - ตรวจสอบสิทธิ์ผ่าน Junction Table + - [ ] **Deliverable:** อัปโหลด/ดาวน์โหลดไฟล์ได้อย่างปลอดภัย แบบ Transactional + - [ ] **Dependencies:** T1.1, T1.4 + +- **[ ] T2.3 DocumentNumberingModule - Double-Lock Mechanism** + + - [ ] สร้าง Entities: + - [ ] DocumentNumberFormat + - [ ] DocumentNumberCounter + - [ ] สร้าง DocumentNumberingService: + - [ ] generateNextNumber(projectId, orgId, typeId, year) → string + - [ ] ใช้ **Double-Lock Mechanism**: + 1. Acquire **Redis Lock** (Key: `doc_num:{project}:{type}`) + 2. Read DB & Calculate Next Number + 3. Update DB with **Optimistic Lock** Check (ใช้ `@VersionColumn()`) + 4. Release Redis Lock + 5. Retry on Failure ด้วย exponential backoff + - [ ] Fallback mechanism เมื่อการขอเลขล้มเหลว + - [ ] Format ตาม Template: {ORG\_CODE}-{TYPE\_CODE}-{YEAR\_SHORT}-{SEQ:4} + - **ไม่มี Controller** (Internal Service เท่านั้น) + - [ ] **Security:** Implement audit log ทุกครั้งที่มีการ generate เลขที่ + - [ ] **Deliverable:** Service สร้างเลขที่เอกสารได้ถูกต้องและปลอดภัย ไม่มี Race Condition + - [ ] **Dependencies:** T1.1, T0.3 + +- **[ ] T2.4 SecurityModule - Enhanced Security** + + - [ ] สร้าง Input Validation Service: + - [ ] XSS Prevention + - [ ] SQL Injection Prevention + - [ ] CSRF Protection + - [ ] สร้าง RateLimitGuard: + - [ ] Implement rate limiting ตาม strategy (anonymous: 100/hr, authenticated: 500-5000/hr) + - [ ] Different limits สำหรับ endpoints ต่างๆ + - [ ] สร้าง Security Headers Middleware + - [ ] **Security:** Implement content security policy (CSP) + - [ ] **Deliverable:** Security layers ทำงานได้ + - [ ] **Dependencies:** T1.1 + +- **[ ] T2.5 JSON Details & Schema Management** + + - [ ] T2.5.1 JsonSchemaModule - Schema Management: สร้าง Service สำหรับ Validate, get, register JSON schemas + - [ ] T2.5.2 DetailsService - Data Processing: สร้าง Service สำหรับ sanitize, transform, compress/decompress JSON + - [ ] T2.5.3 JSON Security & Validation: Implement security checks และ validation rules + - [ ] **Deliverable:** JSON schema system ทำงานได้ + - [ ] **Dependencies:** T1.1 + +----- + +## **Phase 3: Unified Workflow Engine (สัปดาห์ที่ 5-6)** + +**Milestone:** ระบบ Workflow กลางที่รองรับทั้ง Routing ปกติ และ RFA + +### **Phase 3: Tasks** + +- **[ ] T3.1 WorkflowEngineModule (New)** + + - [ ] ออกแบบ Generic Schema สำหรับ Workflow State Machine + - [ ] Implement Service: `initializeWorkflow()`, `processAction()`, `getNextStep()` + - [ ] รองรับ Logic การ "ข้ามขั้นตอน" และ "ส่งกลับ" ภายใน Engine เดียว + - [ ] **Security:** Implement audit logging สำหรับ workflow actions + - [ ] **Deliverable:** Unified Workflow Engine พร้อมใช้งาน + - [ ] **Dependencies:** T1.1 + +- **[ ] T3.2 CorrespondenceModule - Basic CRUD** + + - [ ] สร้าง Entities (Correspondence, Revision, Recipient, Tag, Reference, Attachment) + - [ ] สร้าง CorrespondenceService (Create with Document Numbering, Update with new Revision, Soft Delete) + - [ ] สร้าง Controllers (POST/GET/PUT/DELETE /correspondences) + - [ ] **Security:** Implement permission checks สำหรับ document access + - [ ] **Deliverable:** สร้าง/แก้ไข/ดูเอกสารได้ + - [ ] **Dependencies:** T1.1, T1.2, T1.3, T1.4, T1.5, T2.3, T2.2, T2.5 + +- **[ ] T3.3 CorrespondenceModule - Advanced Features** + + - [ ] Implement Status Transitions (DRAFT → SUBMITTED) + - [ ] Implement References (Link Documents) + - [ ] Implement Search (Basic) + - [ ] **Security:** Implement state transition validation + - [ ] **Deliverable:** Workflow พื้นฐานทำงานได้ + - [ ] **Dependencies:** T3.2 + +- **[ ] T3.4 Correspondence Integration with Workflow** + + - [ ] เชื่อมต่อ `CorrespondenceService` เข้ากับ `WorkflowEngineModule` + - [ ] ย้าย Logic การ Routing เดิมมาใช้ Engine ใหม่ + - [ ] สร้าง API endpoints สำหรับ Frontend (Templates, Pending Tasks, Bulk Action) + - [ ] **Security:** Implement permission checks สำหรับ workflow operations + - [ ] **Deliverable:** ระบบส่งต่อเอกสารทำงานได้สมบูรณ์ด้วย Unified Engine + - [ ] **Dependencies:** T3.1, T3.2 + +----- + +## **Phase 4: Drawing & Advanced Workflows (สัปดาห์ที่ 7-8)** + +**Milestone:** การจัดการแบบและ RFA โดยใช้ Unified Engine + +### **Phase 4: Tasks** + +- **[ ] T4.1 DrawingModule - Contract Drawings** + + - [ ] สร้าง Entities (ContractDrawing, Volume, Category, SubCategory, Attachment) + - [ ] สร้าง ContractDrawingService CRUD + - [ ] สร้าง Controllers (GET/POST /drawings/contract) + - [ ] **Security:** Implement access control สำหรับ contract drawings + - [ ] **Deliverable:** จัดการ Contract Drawings ได้ + - [ ] **Dependencies:** T1.1, T1.2, T1.4, T1.5, T2.2 + +- **[ ] T4.2 DrawingModule - Shop Drawings** + + - [ ] สร้าง Entities (ShopDrawing, Revision, Main/SubCategory, ContractRef, RevisionAttachment) + - [ ] สร้าง ShopDrawingService CRUD (รวมการสร้าง Revision) + - [ ] สร้าง Controllers (GET/POST /drawings/shop, /drawings/shop/:id/revisions) + - [ ] Link Shop Drawing Revision → Contract Drawings + - [ ] **Security:** Implement virus scanning สำหรับ drawing files + - [ ] **Deliverable:** จัดการ Shop Drawings และ Revisions ได้ + - [ ] **Dependencies:** T4.1 + +- **[ ] T5.1 RfaModule with Unified Workflow** + + - [ ] สร้าง Entities (Rfa, RfaRevision, RfaItem, RfaWorkflowTemplate/Step) + - [ ] สร้าง RfaService (Create RFA, Link Shop Drawings) + - [ ] Implement RFA Workflow โดยใช้ Configuration ของ `WorkflowEngineModule` + - [ ] สร้าง Controllers (POST/GET /rfas, POST /rfas/:id/workflow/...) + - [ ] **Resilience:** Implement circuit breaker สำหรับ notification services + - [ ] **Deliverable:** RFA Workflow ทำงานได้ด้วย Unified Engine + - [ ] **Dependencies:** T3.2, T4.2, T2.5, T6.2 + +----- + +## **Phase 5: Workflow Systems & Resilience (สัปดาห์ที่ 8-9)** + +**Milestone:** ระบบ Workflow ทั้งหมดพร้อม Resilience Patterns + +### **Phase 5: Tasks** + +- **[ ] T5.2 CirculationModule - Internal Routing** + + - [ ] สร้าง Entities (Circulation, Template, Routing, Attachment) + - [ ] สร้าง CirculationService (Create 1:1 with Correspondence, Assign User, Complete/Close Step) + - [ ] สร้าง Controllers (POST/GET /circulations, POST /circulations/:id/steps/...) + - [ ] **Resilience:** Implement retry mechanism สำหรับ assignment notifications + - [ ] **Deliverable:** ใบเวียนภายในองค์กรทำงานได้ + - [ ] **Dependencies:** T3.2, T2.5, T6.2 + +- **[ ] T5.3 TransmittalModule - Document Forwarding** + + - [ ] สร้าง Entities (Transmittal, TransmittalItem) + - [ ] สร้าง TransmittalService (Create Correspondence + Transmittal, Link Multiple Correspondences) + - [ ] สร้าง Controllers (POST/GET /transmittals) + - [ ] **Security:** Implement access control สำหรับ transmittal items + - [ ] **Deliverable:** สร้าง Transmittal ได้ + - [ ] **Dependencies:** T3.2 + +----- + +## **Phase 6: Notification & Resilience (สัปดาห์ที่ 9)** + +**Milestone:** ระบบแจ้งเตือนแบบ Digest และการจัดการข้อมูลขนาดใหญ่ + +### **Phase 6: Tasks** + +- **[ ] T6.1 SearchModule - Elasticsearch Integration** + + - [ ] Setup Elasticsearch Container + - [ ] สร้าง SearchService (index/update/delete documents, search) + - [ ] Index ทุก Document Type + - [ ] สร้าง Controllers (GET /search) + - [ ] **Resilience:** Implement circuit breaker สำหรับ Elasticsearch + - [ ] **Deliverable:** ค้นหาขั้นสูงทำงานได้ + - [ ] **Dependencies:** T3.2, T5.1, T4.2, T5.2, T5.3 + +- **[ ] T6.2 Notification Queue & Digest** + + - [ ] สร้าง NotificationService (sendEmail/Line/System) + - [ ] **Producer:** Push Event ลง BullMQ Queue + - [ ] **Consumer:** จัดกลุ่ม Notification (Digest Message) และส่งผ่าน Email/Line + - [ ] Integrate กับ Workflow Events (แจ้ง Recipients, Assignees, Deadline) + - [ ] สร้าง Controllers (GET /notifications, PUT /notifications/:id/read) + - [ ] **Resilience:** Implement retry mechanism ด้วย exponential backoff + - [ ] **Deliverable:** ระบบแจ้งเตือนทำงานได้แบบ Digest + - [ ] **Dependencies:** T1.1, T6.4 + +- **[ ] T6.3 MonitoringModule - Observability** + + - [ ] สร้าง Health Check Controller (GET /health) + - [ ] สร้าง Metrics Service (API response times, Error rates) + - [ ] สร้าง Performance Interceptor (Track request duration) + - [ ] สร้าง Logging Service (Structured logging) + - [ ] **Deliverable:** Monitoring system ทำงานได้ + - [ ] **Dependencies:** T1.1 + +- **[ ] T6.4 ResilienceModule - Circuit Breaker & Retry** + + - [ ] สร้าง Circuit Breaker Service (@CircuitBreaker() decorator) + - [ ] สร้าง Retry Service (@Retry() decorator) + - [ ] สร้าง Fallback Strategies + - [ ] Implement สำหรับ Email, LINE, Elasticsearch, Virus Scanning + - [ ] **Deliverable:** Resilience patterns ทำงานได้ + - [ ] **Dependencies:** T1.1 + +- **[ ] T6.5 Data Partitioning Strategy** + + - [ ] ออกแบบ Table Partitioning สำหรับ `audit_logs` และ `notifications` (แบ่งตาม Range: Year) + - [ ] เขียน Raw SQL Migration สำหรับสร้าง Partition Table + - [ ] **Deliverable:** Database Performance และ Scalability ดีขึ้น + - [ ] **Dependencies:** T0.3 + +----- + +## **Phase 7: Testing & Hardening (สัปดาห์ที่ 10-12)** + +**Milestone:** ทดสอบความทนทานต่อ Race Condition, Security, และ Performance + +### **Phase 7: Tasks** + +- **[ ] T7.1 Concurrency Testing** + + - [ ] เขียน Test Scenarios ยิง Request ขอเลขที่เอกสารพร้อมกัน 100 Request (ต้องไม่ซ้ำและไม่ข้าม) + - [ ] ทดสอบ Optimistic Lock ทำงานถูกต้องเมื่อ Redis ถูกปิด + - [ ] ทดสอบ File Upload พร้อมกันหลายไฟล์ + - [ ] **Deliverable:** ระบบทนทานต่อ Concurrency Issues + +- **[ ] T7.2 Transaction Integrity Testing** + + - [ ] ทดสอบ Upload ไฟล์แล้ว Kill Process ก่อน Commit + - [ ] ทดสอบ Two-Phase File Storage ทำงานถูกต้อง + - [ ] ทดสอบ Database Transaction Rollback Scenarios + - [ ] **Deliverable:** Data Integrity รับประกันได้ + +- **[ ] T7.3 Security & Idempotency Test** + + - [ ] ทดสอบ Replay Attack โดยใช้ `Idempotency-Key` ซ้ำ + - [ ] ทดสอบ Maintenance Mode Block API ได้จริง + - [ ] ทดสอบ RBAC 4-Level ทำงานถูกต้อง 100% + - [ ] **Deliverable:** Security และ Idempotency ทำงานได้ตาม设计要求 + +- **[ ] T7.4 Unit Testing (80% Coverage)** + +- **[ ] T7.5 Integration Testing** + +- **[ ] T7.6 E2E Testing** + +- **[ ] T7.7 Performance Testing** + + - [ ] Load Testing: 100 concurrent users + - [ ] **(สำคัญ)** การจูนและทดสอบ Load Test จะต้องทำในสภาพแวดล้อมที่จำลอง Spec ของ QNAP Server (TS-473A, AMD Ryzen V1500B) เพื่อให้ได้ค่า Response Time และ Connection Pool ที่เที่ยงตรง + - [ ] Stress Testing + - [ ] Endurance Testing + - [ ] **Deliverable:** Performance targets บรรลุ + +- **[ ] T7.8 Security Testing** + + - [ ] Penetration Testing (OWASP Top 10) + - [ ] Security Audit (Code review, Dependency scanning) + - [ ] File Upload Security Testing + - [ ] **Deliverable:** Security tests ผ่าน + +- **[ ] T7.9 Performance Optimization** + + - [ ] Implement Caching (Master Data, User Permissions, Search Results) + - [ ] Database Optimization (Review Indexes, Query Optimization, Pagination) + - [ ] **Deliverable:** Response Time \< 200ms (90th percentile) + +----- + +## **Phase 8: Documentation & Deployment (สัปดาห์ที่ 14)** + +**Milestone:** เอกสารและ Deploy สู่ Production พร้อม Security Hardening + +### **Phase 8: Tasks** + +- **[ ] T8.1 API Documentation (Swagger)** +- **[ ] T8.2 Technical Documentation** +- **[ ] T8.3 Security Hardening** +- **[ ] T8.4 Deployment Preparation (QNAP Setup, Nginx Proxy Manager)** +- **[ ] T8.5 Production Deployment** +- **[ ] T8.6 Handover to Frontend Team** + +----- + +## 📊 **สรุป Timeline** + +| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | +| :--- | :--- | :--- | :--- | +| Phase 0 | 1 สัปดาห์ | 4 | Infrastructure Ready + Security Base | +| Phase 1 | 2 สัปดาห์ | 5 | Auth & User Management + RBAC + Idempotency | +| Phase 2 | 1 สัปดาห์ | 5 | High-Integrity Data & File Management | +| Phase 3 | 2 สัปดาห์ | 4 | Unified Workflow Engine + Correspondence | +| Phase 4 | 2 สัปดาห์ | 3 | Drawing Management + RFA with Unified Workflow | +| Phase 5 | 2 สัปดาห์ | 2 | Workflow Systems + Resilience | +| Phase 6 | 1 สัปดาห์ | 5 | Notification & Resilience + Data Partitioning | +| Phase 7 | 3 สัปดาห์ | 9 | Testing & Hardening | +| Phase 8 | 1 สัปดาห์ | 6 | Documentation & Deploy | +| **รวม** | **15 สัปดาห์** | **39 Tasks** | **Production-Ready Backend v1.4.2** | + +----- + +`End of Backend Development Plan v1.4.2 (ฉบับปรับปรุง)` diff --git a/Documnets/Project/3_Frontend_Plan_V1_4_2.md b/Documnets/Project/3_Frontend_Plan_V1_4_2.md new file mode 100644 index 0000000..ff2e4ad --- /dev/null +++ b/Documnets/Project/3_Frontend_Plan_V1_4_2.md @@ -0,0 +1,982 @@ +# 📋 **แผนการพัฒนา Frontend (Next.js) - LCBP3-DMS v1.4.2** + +**อ้างอิง:** Requirements v1.4.2 & FullStackJS Guidelines v1.4.2 +**จุดเน้นสำคัญ:** Responsive Design, Dynamic Forms, Offline Support, Optimistic Updates + +## 🎯 **ภาพรวมโครงการ** + +พัฒนา Frontend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่มีความทันสมัย รองรับการทำงานบนอุปกรณ์ต่างๆ ได้อย่างสมบูรณ์ มีประสบการณ์ผู้ใช้ที่ราบรื่น และรองรับการทำงานแบบ Offline เบื้องต้น + +--- + +## 📐 **สถาปัตยกรรมระบบ** + +### **Technology Stack** + +- **Framework:** Next.js 14+ (App Router, React 18, TypeScript, ESM) +- **Styling:** Tailwind CSS + PostCSS +- **UI Components:** shadcn/ui + Radix UI Primitives +- **State Management:** + - **Server State:** TanStack Query (React Query) + - **Client State:** Zustand + - **Form State:** React Hook Form + Zod +- **API Client:** Axios (พร้อม Idempotency Interceptor) +- **Authentication:** NextAuth.js (รองรับ JWT) +- **File Upload:** Custom Hook + Drag & Drop +- **Testing:** + - **Unit/Integration:** Vitest + React Testing Library + - **E2E:** Playwright + - **Mocking:** MSW (Mock Service Worker) +- **Development:** + - **Package Manager:** pnpm + - **Linting:** ESLint + Prettier + - **Type Checking:** TypeScript Strict Mode + +### **โครงสร้างโปรเจกต์** + +```tree +app/ +├── (auth)/ +│ ├── login/ +│ └── register/ +├── (dashboard)/ +│ ├── layout.tsx +│ ├── page.tsx +│ └── components/ +├── admin/ +│ ├── users/ +│ ├── roles/ +│ └── numbering-formats/ +├── correspondences/ +│ ├── page.tsx +│ ├── [id]/ +│ └── new/ +├── rfas/ +│ ├── page.tsx +│ ├── [id]/ +│ └── new/ +├── drawings/ +├── circulations/ +├── transmittals/ +├── search/ +└── profile/ + +components/ +├── ui/ # shadcn/ui components +├── forms/ # Dynamic form components +├── tables/ # Responsive data tables +├── workflow/ # Workflow visualization +├── file-upload/ # File upload with security +├── notifications/ # Notification system +└── layout/ # App layout components + +lib/ +├── api/ # API clients & interceptors +├── auth/ # Authentication utilities +├── stores/ # Zustand stores +├── hooks/ # Custom React hooks +├── utils/ # Utility functions +├── constants/ # App constants +└── types/ # TypeScript type definitions + +styles/ +├── globals.css +└── components/ + +__tests__/ +├── unit/ +├── integration/ +└── e2e/ +``` + +--- + +## 🗓️ **แผนการพัฒนาแบบ Phase-Based** + +### **Dependency Diagram (ภาพรวม)** + +```mermaid +%% Phase 0: Foundation Setup +subgraph Phase0 [Phase 0: Foundation & Configuration] + F0_1[F0.1: Project Setup & Tooling] + F0_2[F0.2: Design System & UI Components] + F0_3[F0.3: API Client & Authentication] + F0_4[F0.4: State Management Setup] +end + +%% Phase 1: Core Layout & Navigation +subgraph Phase1 [Phase 1: Core Application Structure] + F1_1[F1.1: Main Layout & Navigation] + F1_2[F1.2: Authentication Pages] + F1_3[F1.3: Dashboard & Landing] + F1_4[F1.4: Responsive Design System] +end + +%% Phase 2: User Management & Profile +subgraph Phase2 [Phase 2: User Management & Security] + F2_1[F2.1: User Profile & Settings] + F2_2[F2.2: Admin Panel - User Management] + F2_3[F2.3: Admin Panel - Role Management] + F2_4[F2.4: Permission Integration] +end + +%% Phase 3: Project & Organization Management +subgraph Phase3 [Phase 3: Project Structure] + F3_1[F3.1: Project Management UI] + F3_2[F3.2: Organization Management] + F3_3[F3.3: Contract Management] +end + +%% Phase 4: Correspondence Management +subgraph Phase4 [Phase 4: Correspondence System] + F4_1[F4.1: Correspondence List & Search] + F4_2[F4.2: Correspondence Creation Form] + F4_3[F4.3: Correspondence Detail View] + F4_4[F4.4: File Upload Integration] +end + +%% Phase 5: Workflow & Routing System +subgraph Phase5 [Phase 5: Workflow Management] + F5_1[F5.1: Workflow Visualization Component] + F5_2[F5.2: Routing Template Management] + F5_3[F5.3: Workflow Step Actions] + F5_4[F5.4: Real-time Status Updates] +end + +%% Phase 6: Drawing Management +subgraph Phase6 [Phase 6: Drawing System] + F6_1[F6.1: Contract Drawings Management] + F6_2[F6.2: Shop Drawings Management] + F6_3[F6.3: Drawing Revision System] + F6_4[F6.4: Drawing References] +end + +%% Phase 7: RFA & Approval Workflows +subgraph Phase7 [Phase 7: RFA System] + F7_1[F7.1: RFA List & Dashboard] + F7_2[F7.2: RFA Creation with Dynamic Forms] + F7_3[F7.3: RFA Workflow Integration] + F7_4[F7.4: RFA Approval Interface] +end + +%% Phase 8: Circulation & Internal Routing +subgraph Phase8 [Phase 8: Internal Workflows] + F8_1[F8.1: Circulation Management] + F8_2[F8.2: Task Assignment Interface] + F8_3[F8.3: Internal Approval Flows] +end + +%% Phase 9: Advanced Features +subgraph Phase9 [Phase 9: Advanced Features] + F9_1[F9.1: Advanced Search Interface] + F9_2[F9.2: Notification System] + F9_3[F9.3: Reporting & Analytics] + F9_4[F9.4: Mobile Optimization] +end + +%% Phase 10: Testing & Optimization +subgraph Phase10 [Phase 10: Testing & Polish] + F10_1[F10.1: Comprehensive Testing] + F10_2[F10.2: Performance Optimization] + F10_3[F10.3: Security Hardening] + F10_4[F10.4: Documentation] +end + +%% Dependencies +F0_1 --> F0_2 +F0_1 --> F0_3 +F0_1 --> F0_4 + +F0_2 --> F1_1 +F0_3 --> F1_1 +F0_4 --> F1_1 +F1_1 --> F1_2 +F1_1 --> F1_3 +F1_1 --> F1_4 + +F1_1 --> F2_1 +F1_3 --> F2_1 +F0_3 --> F2_1 +F2_1 --> F2_2 +F2_2 --> F2_3 +F2_3 --> F2_4 + +F1_1 --> F3_1 +F2_4 --> F3_1 +F3_1 --> F3_2 +F3_2 --> F3_3 + +F1_1 --> F4_1 +F3_1 --> F4_1 +F4_1 --> F4_2 +F4_2 --> F4_3 +F4_2 --> F4_4 + +F4_1 --> F5_1 +F4_2 --> F5_2 +F4_3 --> F5_3 +F5_1 --> F5_4 + +F3_1 --> F6_1 +F4_4 --> F6_1 +F6_1 --> F6_2 +F6_2 --> F6_3 +F6_3 --> F6_4 + +F4_1 --> F7_1 +F5_1 --> F7_1 +F6_2 --> F7_1 +F7_1 --> F7_2 +F7_2 --> F7_3 +F7_3 --> F7_4 + +F4_1 --> F8_1 +F5_3 --> F8_1 +F8_1 --> F8_2 +F8_2 --> F8_3 + +F4_1 --> F9_1 +F7_1 --> F9_1 +F1_3 --> F9_2 +F5_4 --> F9_2 +F1_3 --> F9_3 +F1_4 --> F9_4 + +F1_1 --> F10_1 +F4_1 --> F10_1 +F7_1 --> F10_1 +F10_1 --> F10_2 +F10_2 --> F10_3 +F10_3 --> F10_4 +``` + +## **Phase 0: Foundation & Configuration (สัปดาห์ที่ 1)** + +**Milestone:** โครงสร้างพื้นฐานพร้อม รองรับ Development Workflow ที่มีประสิทธิภาพ + +### **Phase 0: Tasks** + +- **[ ] F0.1 Project Setup & Tooling** + - [ ] Initialize Next.js 14+ project with TypeScript + - [ ] Configure pnpm workspace + - [ ] Setup ESLint, Prettier, and pre-commit hooks + - [ ] Configure Tailwind CSS with PostCSS + - [ ] Setup shadcn/ui components + - [ ] Configure absolute imports and path aliases + - [ ] **Deliverable:** Development environment ready + - [ ] **Dependencies:** None + +- **[ ] F0.2 Design System & UI Components** + - [ ] Setup color palette and design tokens + - [ ] Create responsive design breakpoints + - [ ] Implement core shadcn/ui components: + - [ ] Button, Input, Label, Form + - [ ] Card, Table, Badge + - [ ] Dialog, Dropdown, Select + - [ ] Tabs, Accordion + - [ ] Create custom design system components: + - [ ] DataTable (responsive) + - [ ] FileUpload zone + - [ ] Workflow visualization + - [ ] **Deliverable:** Consistent UI component library + - [ ] **Dependencies:** F0.1 + +- **[ ] F0.3 API Client & Authentication** + - [ ] Setup Axios client with interceptors: + - [ ] Idempotency-Key header injection + - [ ] Authentication token management + - [ ] Error handling and retry logic + - [ ] Configure NextAuth.js for JWT authentication + - [ ] Create auth hooks (useAuth, usePermissions) + - [ ] Setup API route handlers for auth callbacks + - [ ] **Security:** Implement secure token storage + - [ ] **Deliverable:** Secure API communication layer + - [ ] **Dependencies:** F0.1 + +- **[ ] F0.4 State Management Setup** + - [ ] Configure TanStack Query for server state: + - [ ] Query client setup + - [ ] Default configurations + - [ ] Error boundaries + - [ ] Create Zustand stores: + - [ ] Auth store (user, permissions) + - [ ] UI store (theme, sidebar state) + - [ ] Draft store (form auto-save) + - [ ] Setup React Hook Form with Zod integration + - [ ] Create form validation schemas + - [ ] **Deliverable:** Robust state management system + - [ ] **Dependencies:** F0.1, F0.3 + +### **Phase 0: Testing - Foundation** + +#### **F0.T1 Component Test Suite** + +- [ ] **Unit Tests:** Core UI components (Button, Input, Card) +- [ ] **Integration Tests:** Form validation, API client interceptors +- [ ] **Visual Tests:** Component styling and responsive behavior + +#### **F0.T2 Authentication Test Suite** + +- [ ] **Unit Tests:** Auth hooks, token management +- [ ] **Integration Tests:** Login/logout flow, permission checks +- [ ] **Security Tests:** Token security, API authentication + +--- + +## **Phase 1: Core Application Structure (สัปดาห์ที่ 2)** + +**Milestone:** Layout หลักพร้อมใช้งาน การนำทางและ Authentication ทำงานได้ + +### **Phase 1: Tasks** + +- **[ ] F1.1 Main Layout & Navigation** + - [ ] Create App Shell layout: + - [ ] Navbar with user menu and notifications + - [ ] Collapsible sidebar with navigation + - [ ] Main content area with responsive design + - [ ] Implement navigation menu structure: + - [ ] Dashboard, Correspondences, RFAs, Drawings, etc. + - [ ] Dynamic menu based on user permissions + - [ ] Create breadcrumb navigation component + - [ ] Implement mobile-responsive sidebar (drawer) + - [ ] Maintenance Mode Integration: + - [ ] Implement a Global Middleware/Wrapper ที่ตรวจสอบสถานะ Maintenance Mode ผ่าน API/Service ก่อนการ Render หน้า หากสถานะเป็น true ให้ Redirect ผู้ใช้ (ยกเว้น Admin) ไปยังหน้า /maintenance ทันที เพื่อให้สอดคล้องกับ Logic ของ Backend. + - [ ] **Accessibility:** Ensure keyboard navigation and screen reader support + - [ ] **Deliverable:** Fully functional application layout + - [ ] **Dependencies:** F0.2, F0.3 + +- **[ ] F1.2 Authentication Pages** + - [ ] Create login page with form validation + - [ ] Implement forgot password flow + - [ ] Create registration page (admin-only) + - [ ] Setup protected route middleware + - [ ] Implement route-based permission checks + - [ ] **Security:** Rate limiting on auth attempts, secure password requirements + - [ ] **Deliverable:** Complete authentication flow + - [ ] **Dependencies:** F0.3, F1.1 + +- **[ ] F1.3 Dashboard & Landing** + - [ ] Create public landing page for non-authenticated users + - [ ] Implement main dashboard with: + - [ ] KPI cards (document counts, pending tasks) + - [ ] "My Tasks" table from v_user_tasks + - [ ] Recent activity feed + - [ ] Security metrics display + - [ ] Create dashboard widgets system + - [ ] Implement data fetching with TanStack Query + - [ ] **Performance:** Optimize dashboard data loading + - [ ] **Deliverable:** Functional dashboard with real data + - [ ] **Dependencies:** F0.4, F1.1 + +- **[ ] F1.4 Responsive Design System** + - [ ] Implement mobile-first responsive design + - [ ] Create card view components for mobile tables + - [ ] Setup touch-friendly interactions + - [ ] Optimize images and assets for mobile + - [ ] Test across multiple device sizes + - [ ] **UX:** Ensure seamless mobile experience + - [ ] **Deliverable:** Fully responsive application + - [ ] **Dependencies:** F0.2, F1.1 + +### **Phase 1: Testing - Core Structure** + +#### **F1.T1 Layout Test Suite** + +- [ ] **Unit Tests:** Navigation components, layout responsiveness +- [ ] **Integration Tests:** Route protection, permission-based navigation +- [ ] **E2E Tests:** Complete user navigation flow + +#### **F1.T2 Dashboard Test Suite** + +- [ ] **Unit Tests:** Dashboard components, data formatting +- [ ] **Integration Tests:** Data fetching and display, real-time updates +- [ ] **Performance Tests:** Dashboard loading performance + +--- + +## **Phase 2: User Management & Security (สัปดาห์ที่ 3)** + +**Milestone:** การจัดการผู้ใช้และสิทธิ์แบบสมบูรณ์ + +### **Phase 2: Tasks** + +- **[ ] F2.1 User Profile & Settings** + - [ ] Create user profile page: + - [ ] Personal information display/edit + - [ ] Password change functionality + - [ ] Notification preferences + - [ ] Implement profile picture upload + - [ ] Create user settings page + - [ ] **Security:** Secure password change with current password verification + - [ ] **Deliverable:** Complete user self-service management + - [ ] **Dependencies:** F1.1, F0.4 + +- **[ ] F2.2 Admin Panel - User Management** + - [ ] Create user list with search and filters + - [ ] Implement user creation form + - [ ] Create user edit interface + - [ ] Implement bulk user operations + - [ ] Add user activity tracking display + - [ ] **Security:** Admin-only access enforcement + - [ ] **Deliverable:** Comprehensive user management interface + - [ ] **Dependencies:** F1.1, F2.1 + +- **[ ] F2.3 Admin Panel - Role Management** + - [ ] Create role list and management interface + - [ ] Implement role creation and editing + - [ ] Create permission assignment interface + - [ ] Implement role-based access control visualization + - [ ] Add role usage statistics + - [ ] **Security:** Permission hierarchy enforcement + - [ ] **Deliverable:** Complete RBAC management system + - [ ] **Dependencies:** F2.2 + +- **[ ] F2.4 Permission Integration** + - [ ] Implement CASL ability integration + - [ ] Create permission-based UI components: + - [ ] ProtectedButton, ProtectedLink + - [ ] Conditional rendering based on permissions + - [ ] Setup real-time permission updates + - [ ] Implement permission debugging tools + - [ ] **Security:** Frontend-backend permission consistency + - [ ] **Deliverable:** Seamless permission enforcement throughout app + - [ ] **Dependencies:** F0.3, F2.3 + +### **Phase 2: Testing - User Management** + +#### **F2.T1 User Management Test Suite** + +- [ ] **Unit Tests:** User CRUD operations, form validation +- [ ] **Integration Tests:** User-role assignment, permission propagation +- [ ] **Security Tests:** Permission escalation attempts, admin access control + +#### **F2.T2 RBAC Test Suite** + +- [ ] **Unit Tests:** Permission checks, role validation +- [ ] **Integration Tests:** Multi-level permission enforcement, UI element protection +- [ ] **E2E Tests:** Complete role-based workflow testing + +--- + +## **Phase 3: Project Structure (สัปดาห์ที่ 4)** + +**Milestone:** การจัดการโครงสร้างโปรเจกต์และองค์กร + +### **Phase 3: Tasks** + +- **[ ] F3.1 Project Management UI** + - [ ] Create project list with search and filters + - [ ] Implement project creation and editing + - [ ] Create project detail view + - [ ] Implement project dashboard with statistics + - [ ] Add project member management + - [ ] **UX:** Intuitive project navigation and management + - [ ] **Deliverable:** Complete project management interface + - [ ] **Dependencies:** F1.1, F2.4 + +- **[ ] F3.2 Organization Management** + - [ ] Create organization list and management + - [ ] Implement organization creation and editing + - [ ] Create organization detail view + - [ ] Add organization user management + - [ ] Implement organization hierarchy visualization + - [ ] **Business Logic:** Proper organization-project relationships + - [ ] **Deliverable:** Comprehensive organization management + - [ ] **Dependencies:** F3.1 + +- **[ ] F3.3 Contract Management** + - [ ] Create contract list within projects + - [ ] Implement contract creation and editing + - [ ] Create contract detail view + - [ ] Add contract party management + - [ ] Implement contract document associations + - [ ] **Business Logic:** Contract-project-organization relationships + - [ ] **Deliverable:** Complete contract management system + - [ ] **Dependencies:** F3.1, F3.2 + +### **Phase 3: Testing - Project Structure** + +#### **F3.T1 Project Management Test Suite** + +- [ ] **Unit Tests:** Project CRUD operations, validation +- [ ] **Integration Tests:** Project-organization relationships, member management +- [ ] **Business Logic Tests:** Project hierarchy, access control + +--- + +## **Phase 4: Correspondence System (สัปดาห์ที่ 5-6)** + +**Milestone:** ระบบจัดการเอกสารโต้ตอบแบบสมบูรณ์ + +### **Phase 4: Tasks** + +- **[ ] F4.1 Correspondence List & Search** + - [ ] Create correspondence list with advanced filtering: + - [ ] Filter by type, status, project, organization + - [ ] Search by title, document number, content + - [ ] Date range filtering + - [ ] Implement responsive data table: + - [ ] Desktop: Full table view + - [ ] Mobile: Card view conversion + - [ ] Add bulk operations (export, status change) + - [ ] Implement real-time updates + - [ ] **Performance:** Virtual scrolling for large datasets + - [ ] **Deliverable:** High-performance correspondence listing + - [ ] **Dependencies:** F1.1, F3.1 + +- **[ ] F4.2 Correspondence Creation Form** + - [ ] Create dynamic form generator based on JSON schema + - [ ] Implement form with multiple sections: + - [ ] Basic information (type, title, recipients) + - [ ] Content and details (JSON schema-based) + - [ ] File attachments + - [ ] Routing template selection + - [ ] Add draft auto-save functionality + - [ ] Implement form validation with Zod + - [ ] **UX:** Intuitive form with progress indication + - [ ] **Deliverable:** Flexible correspondence creation interface + - [ ] **Dependencies:** F0.4, F4.1 + +- **[ ] F4.3 Correspondence Detail View** + - [ ] Create comprehensive detail page: + - [ ] Document header with metadata + - [ ] Content display based on type + - [ ] Revision history + - [ ] Related documents + - [ ] Workflow status visualization + - [ ] Implement document actions: + - [ ] Edit, withdraw, cancel (with permissions) + - [ ] Download, print + - [ ] Create related documents + - [ ] Add comments and activity timeline + - [ ] **UX:** Clean, readable document presentation + - [ ] **Deliverable:** Complete document detail experience + - [ ] **Dependencies:** F4.1, F4.2 + +- **[ ] F4.4 File Upload Integration** + - [ ] Create drag-and-drop file upload component + - [ ] Implement file type validation and preview + - [ ] Add virus scan status indication + - [ ] Create file management interface: + - [ ] Mark files as main/supporting documents + - [ ] Reorder and manage attachments + - [ ] File security status display + - [ ] Implement two-phase upload progress + - [ ] **Security:** File type restrictions, size limits, virus scan integration + - [ ] **Deliverable:** Secure and user-friendly file management + - [ ] **Dependencies:** F0.2, F4.2 + +### **Phase 4: Testing - Correspondence System** + +#### **F4.T1 Correspondence Test Suite** + +- [ ] **Unit Tests:** Form validation, file upload components +- [ ] **Integration Tests:** Complete document lifecycle, file attachment flow +- [ ] **E2E Tests:** End-to-end correspondence creation and management + +#### **F4.T2 File Upload Test Suite** + +- [ ] **Unit Tests:** File validation, type checking +- [ ] **Integration Tests:** Two-phase upload process, virus scan integration +- [ ] **Security Tests:** Malicious file upload attempts, security feedback + +--- + +## **Phase 5: Workflow Management (สัปดาห์ที่ 7)** + +**Milestone:** ระบบ Visualization และจัดการ Workflow + +### **Phase 5: Tasks** + +- **[ ] F5.1 Workflow Visualization Component** + - [ ] Create horizontal workflow progress visualization + - [ ] Implement step status indicators (pending, active, completed, skipped) + - [ ] Add due date and assignee information + - [ ] Create interactive workflow diagram + - [ ] Implement workflow history timeline + - [ ] **UX:** Clear visual representation of complex workflows + - [ ] **Deliverable:** Intuitive workflow visualization + - [ ] **Dependencies:** F4.3 + +- **[ ] F5.2 Routing Template Management** + - [ ] Create routing template list and editor + - [ ] Implement drag-and-drop step configuration + - [ ] Add step configuration (purpose, duration, assignee rules) + - [ ] Create template preview functionality + - [ ] Implement template versioning + - [ ] **Business Logic:** Proper step sequencing and validation + - [ ] **Deliverable:** Comprehensive routing template management + - [ ] **Dependencies:** F3.1, F4.2 + +- **[ ] F5.3 Workflow Step Actions** + - [ ] Create step action interface: + - [ ] Approve, reject, request changes + - [ ] Add comments and attachments + - [ ] Forward to other users + - [ ] Implement bulk step actions + - [ ] Add action confirmation with reason required + - [ ] Create step delegation functionality + - [ ] **UX:** Streamlined step completion process + - [ ] **Deliverable:** Efficient workflow step management + - [ ] **Dependencies:** F5.1 + +- **[ ] F5.4 Real-time Status Updates** + - [ ] Implement WebSocket connections for real-time updates + - [ ] Create status change notifications + - [ ] Add auto-refresh for workflow states + - [ ] Implement optimistic updates for better UX + - [ ] Create update history and audit trail + - [ ] **Performance:** Efficient real-time data synchronization + - [ ] **Deliverable:** Real-time workflow monitoring + - [ ] **Dependencies:** F5.1, F9.2 + +### **Phase 5: Testing - Workflow Management** + +#### **F5.T1 Workflow Test Suite** + +- [ ] **Unit Tests:** Workflow visualization, step status logic +- [ ] **Integration Tests:** Complete workflow execution, real-time updates +- [ ] **E2E Tests:** Multi-step workflow with different user roles + +--- + +## **Phase 6: Drawing System (สัปดาห์ที่ 8)** + +**Milestone:** ระบบจัดการแบบแปลนแบบสมบูรณ์ + +### **Phase 6: Tasks** + +- **[ ] F6.1 Contract Drawings Management** + - [ ] Create contract drawing list with categorization + - [ ] Implement drawing upload and metadata management + - [ ] Create drawing preview and viewer + - [ ] Add drawing version control + - [ ] Implement drawing search and filtering + - [ ] **UX:** Efficient drawing navigation and access + - [ ] **Deliverable:** Comprehensive contract drawing management + - [ ] **Dependencies:** F3.1, F4.4 + +- **[ ] F6.2 Shop Drawings Management** + - [ ] Create shop drawing list with revision tracking + - [ ] Implement shop drawing creation and revision system + - [ ] Create drawing comparison interface + - [ ] Add drawing approval status tracking + - [ ] Implement bulk drawing operations + - [ ] **Business Logic:** Proper revision control and approval workflows + - [ ] **Deliverable:** Complete shop drawing management system + - [ ] **Dependencies:** F6.1 + +- **[ ] F6.3 Drawing Revision System** + - [ ] Create revision history interface + - [ ] Implement revision comparison functionality + - [ ] Add revision notes and change tracking + - [ ] Create revision approval workflow + - [ ] Implement revision rollback capability + - [ ] **UX:** Clear visualization of changes between revisions + - [ ] **Deliverable:** Robust drawing revision control + - [ ] **Dependencies:** F6.2 + +- **[ ] F6.4 Drawing References** + - [ ] Create drawing reference management + - [ ] Implement cross-drawing references + - [ ] Add reference validation and integrity checks + - [ ] Create reference visualization + - [ ] Implement reference impact analysis + - [ ] **Business Logic:** Maintain reference integrity during changes + - [ ] **Deliverable:** Comprehensive drawing reference system + - [ ] **Dependencies:** F6.2, F6.3 + +### **Phase 6: Testing - Drawing System** + +#### **F6.T1 Drawing Management Test Suite** + +- [ ] **Unit Tests:** Drawing CRUD operations, revision logic +- [ ] **Integration Tests:** Drawing approval workflows, reference management +- [ ] **E2E Tests:** Complete drawing lifecycle with revisions + +--- + +## **Phase 7: RFA System (สัปดาห์ที่ 9-10)** + +**Milestone:** ระบบขออนุมัติแบบสมบูรณ์พร้อม Dynamic Forms + +### **Phase 7: Tasks** + +- **[ ] F7.1 RFA List & Dashboard** + - [ ] Create RFA dashboard with status overview + - [ ] Implement advanced RFA filtering and search + - [ ] Create RFA calendar view for deadlines + - [ ] Add RFA statistics and reporting + - [ ] Implement RFA bulk operations + - [ ] **UX:** Comprehensive RFA overview and management + - [ ] **Deliverable:** Complete RFA dashboard and listing + - [ ] **Dependencies:** F4.1, F5.1 + +- **[ ] F7.2 RFA Creation with Dynamic Forms** + - [ ] Create RFA type-specific form generator + - [ ] Implement dynamic form fields based on RFA type: + - [ ] RFA_DWG: Shop drawing selection + - [ ] RFA_DOC: Document specifications + - [ ] RFA_MES: Method statement details + - [ ] RFA_MAT: Material specifications + - [ ] Add form validation with JSON schema + - [ ] Implement form data persistence and recovery + - [ ] **UX:** Intuitive form experience for complex RFA types + - [ ] Dynamic Form & Schema Validation: สร้าง Component Dynamic Form Generator ที่: + - [ ] Fetch Schema: ดึงโครงสร้าง JSON Schema ที่ถูกต้องตาม rfa_type จาก Backend (ตาราง json_schemas ที่สร้างใหม่) ก่อนการ Render Form. + - [ ] Client-side Validation: Implement AJV (Another JSON Schema Validator) หรือไลบรารีที่เทียบเท่า เพื่อทำ Client-side Validation บนข้อมูล JSON ก่อนส่ง Submit. + - [ ] Implement dynamic form fields based on RFA type: RFA_DWG, RFA_DOC, RFA_MES, RFA_MAT. + - [ ] Add form data persistence and recovery. + - [ ] **Deliverable:** Flexible RFA creation system + - [ ] **Dependencies:** F4.2, F6.2 + +- **[ ] F7.3 RFA Workflow Integration** + - [ ] Integrate RFA with unified workflow engine + - [ ] Create RFA-specific workflow steps and actions + - [ ] Implement RFA approval interface + - [ ] Add RFA workflow history and tracking + - [ ] Create RFA workflow templates + - [ ] **Business Logic:** Proper RFA approval sequencing and validation + - [ ] **Deliverable:** Seamless RFA workflow integration + - [ ] **Dependencies:** F5.1, F7.2 + +- **[ ] F7.4 RFA Approval Interface** + - [ ] Create RFA review and approval interface + - [ ] Implement side-by-side document comparison + - [ ] Add approval comments and attachments + - [ ] Create conditional approval workflows + - [ ] Implement approval delegation and escalation + - [ ] **UX:** Efficient approval process for technical reviews + - [ ] **Deliverable:** Comprehensive RFA approval system + - [ ] **Dependencies:** F7.1, F7.3 + +### **Phase 7: Testing - RFA System** + +#### **F7.T1 RFA Test Suite** + +- [ ] **Unit Tests:** RFA form generation, validation logic +- [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration +- [ ] **E2E Tests:** Multi-type RFA creation and approval workflows + +--- + +## **Phase 8: Internal Workflows (สัปดาห์ที่ 11)** + +**Milestone:** ระบบใบเวียนและการจัดการงานภายใน + +### **Phase 8: Tasks** + +- **[ ] F8.1 Circulation Management** + - [ ] Create circulation list and management interface + - [ ] Implement circulation creation from correspondence + - [ ] Create circulation template management + - [ ] Add circulation status tracking + - [ ] Implement circulation search and filtering + - [ ] **Business Logic:** Proper circulation-correspondence relationships + - [ ] **Deliverable:** Comprehensive circulation management + - [ ] **Dependencies:** F4.1, F5.2 + +- **[ ] F8.2 Task Assignment Interface** + - [ ] Create task assignment interface with user selection + - [ ] Implement task priority and deadline setting + - [ ] Add task dependency management + - [ ] Create task progress tracking + - [ ] Implement task reassignment and delegation + - [ ] **UX:** Intuitive task management and assignment + - [ ] **Deliverable:** Efficient task assignment system + - [ ] **Dependencies:** F8.1 + +- **[ ] F8.3 Internal Approval Flows** + - [ ] Create internal approval workflow interface + - [ ] Implement multi-level approval processes + - [ ] Add approval chain visualization + - [ ] Create approval delegation system + - [ ] Implement approval deadline management + - [ ] **Business Logic:** Proper approval hierarchy and escalation + - [ ] **Deliverable:** Robust internal approval system + - [ ] **Dependencies:** F8.1, F8.2 + +### **Phase 8: Testing - Internal Workflows** + +#### **F8.T1 Circulation Test Suite** + +- [ ] **Unit Tests:** Circulation creation, task assignment logic +- [ ] **Integration Tests:** Complete circulation workflow, internal approvals +- [ ] **E2E Tests:** End-to-end circulation with task completion + +--- + +## **Phase 9: Advanced Features (สัปดาห์ที่ 12)** + +**Milestone:** ฟีเจอร์ขั้นสูงและการปรับปรุงประสบการณ์ผู้ใช้ + +### **Phase 9: Tasks** + +- **[ ] F9.1 Advanced Search Interface** + - [ ] Create unified search interface across all document types + - [ ] Implement faceted search with multiple filters + - [ ] Add search result highlighting and relevance scoring + - [ ] Create saved search and search templates + - [ ] Implement search result export functionality + - [ ] **Performance:** Efficient search with large datasets + - [ ] **Deliverable:** Powerful cross-document search system + - [ ] **Dependencies:** F4.1, F7.1 + +- **[ ] F9.2 Notification System** + - [ ] Create notification center with real-time updates + - [ ] Implement notification preferences management + - [ ] Add notification grouping and digest views + - [ ] Create actionable notifications with quick actions + - [ ] Implement notification read/unread status + - [ ] **UX:** Non-intrusive but effective notification delivery + - [ ] **Deliverable:** Comprehensive notification management + - [ ] **Dependencies:** F1.3, F5.4 + +- **[ ] F9.3 Reporting & Analytics** + - [ ] Create reporting dashboard with customizable widgets + - [ ] Implement data visualization components (charts, graphs) + - [ ] Add report scheduling and export + - [ ] Create ad-hoc reporting interface + - [ ] Implement performance metrics tracking + - [ ] **Business Logic:** Accurate data aggregation and reporting + - [ ] **Deliverable:** Powerful reporting and analytics system + - [ ] **Dependencies:** F1.3, F7.1 + +- **[ ] F9.4 Mobile Optimization** + - [ ] Implement touch-optimized interactions + - [ ] Create mobile-specific navigation patterns + - [ ] Add offline capability for critical functions + - [ ] Optimize images and assets for mobile networks + - [ ] Implement mobile-specific performance optimizations + - [ ] **UX:** Seamless mobile experience comparable to desktop + - [ ] **Deliverable:** Fully optimized mobile application + - [ ] **Dependencies:** F1.4 + +### **Phase 9: Testing - Advanced Features** + +#### **F9.T1 Advanced Features Test Suite** + +- [ ] **Unit Tests:** Search algorithms, notification logic +- [ ] **Integration Tests:** Cross-module search, real-time notifications +- [ ] **Performance Tests:** Search performance, mobile responsiveness + +--- + +## **Phase 10: Testing & Polish (สัปดาห์ที่ 13-14)** + +**Milestone:** แอปพลิเคชันที่ผ่านการทดสอบและปรับปรุงอย่างสมบูรณ์ + +### **Phase 10: Tasks** + +- **[ ] F10.1 Comprehensive Testing** + - [ ] Idempotency Testing: เพิ่มการทดสอบเฉพาะสำหรับ Axios Interceptor เพื่อจำลองการส่ง Request POST/PUT/DELETE ที่มี Idempotency-Key ซ้ำไปยัง Mock API (MSW) เพื่อยืนยันว่า Client-side ไม่ส่ง Key ซ้ำในการทำงานปกติ และไม่เกิด Side Effect จากการ Replay Attack. + - [ ] Write unit tests for all components and utilities + - [ ] Create integration tests for critical user flows + - [ ] Implement E2E tests for complete workflows + - [ ] Perform cross-browser compatibility testing + - [ ] Conduct accessibility testing (WCAG 2.1 AA) + - [ ] **Quality:** 80%+ test coverage, all critical paths tested + - [ ] **Deliverable:** Fully tested application + - [ ] **Dependencies:** All previous phases + +- **[ ] F10.2 Performance Optimization** + - [ ] Implement code splitting and lazy loading + - [ ] Optimize bundle size and asset delivery + - [ ] Add performance monitoring and metrics + - [ ] Implement caching strategies for static assets + - [ ] Optimize API call patterns and reduce over-fetching + - [ ] **Performance:** Core Web Vitals targets met + - [ ] **Deliverable:** High-performance application + - [ ] **Dependencies:** F10.1 + +- **[ ] F10.3 Security Hardening** + - [ ] Conduct security audit and penetration testing + - [ ] Implement Content Security Policy (CSP) + - [ ] Add security headers and protections + - [ ] Conduct dependency vulnerability scanning + - [ ] Implement secure coding practices review + - [ ] **Security:** No critical security vulnerabilities + - [ ] **Deliverable:** Security-hardened application + - [ ] **Dependencies:** F10.1 + +- **[ ] F10.4 Documentation** + - [ ] Create user documentation and guides + - [ ] Write technical documentation for developers + - [ ] Create API integration documentation + - [ ] Add inline code documentation + - [ ] Create deployment and maintenance guides + - [ ] **Quality:** Comprehensive and up-to-date documentation + - [ ] **Deliverable:** Complete documentation suite + - [ ] **Dependencies:** F10.1 + +### **Phase 10: Testing - Final Validation** + +#### **F10.T1 Final Test Suite** + +- [ ] **Performance Tests:** Load testing, stress testing +- [ ] **Security Tests:** Final security audit, vulnerability assessment +- [ ] **User Acceptance Tests:** Real user testing, feedback incorporation +- [ ] **Compatibility Tests:** Cross-browser, cross-device testing + +--- + +## 📊 **สรุป Timeline** + +| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | +| ------- | ------------ | -------- | ------------------------------------ | +| Phase 0 | 1 สัปดาห์ | 4 | Foundation & Tooling Ready | +| Phase 1 | 1 สัปดาห์ | 4 | Core Application Structure | +| Phase 2 | 1 สัปดาห์ | 4 | User Management & Security | +| Phase 3 | 1 สัปดาห์ | 3 | Project Structure Management | +| Phase 4 | 2 สัปดาห์ | 4 | Correspondence System | +| Phase 5 | 1 สัปดาห์ | 4 | Workflow Management | +| Phase 6 | 1 สัปดาห์ | 4 | Drawing System | +| Phase 7 | 2 สัปดาห์ | 4 | RFA System (Dynamic Forms) | +| Phase 8 | 1 สัปดาห์ | 3 | Internal Workflows | +| Phase 9 | 1 สัปดาห์ | 4 | Advanced Features | +| Phase 10| 2 สัปดาห์ | 4 | Testing & Polish (Idempotency Test) | +| **รวม** | **14 สัปดาห์** | **39 Tasks** | **Production-Ready Frontend v1.4.2** | + +--- + +## 🎯 **Critical Success Factors** + +1. **User Experience First:** ทุกฟีเจอร์ต้องออกแบบเพื่อประสบการณ์ผู้ใช้ที่ดี +2. **Responsive Design:** รองรับการใช้งานบนอุปกรณ์ทุกรูปแบบ +3. **Performance:** Core Web Vitals ต้องอยู่ในเกณฑ์ที่ดี +4. **Accessibility:** ต้องเป็นไปตามมาตรฐาน WCAG 2.1 AA +5. **Security:** ป้องกัน XSS, CSRF และความเสี่ยงด้านความปลอดภัยอื่นๆ +6. **Offline Support:** รองรับการทำงานแบบ Offline เบื้องต้น +7. **Real-time Updates:** การอัปเดตสถานะแบบ Real-time +8. **Testing Coverage:** ครอบคลุมการทดสอบทุก Critical Path +9. **Documentation:** เอกสารครบถ้วนสำหรับผู้ใช้และนักพัฒนา + +--- + +## 📋 **Quality Assurance Checklist** + +### **ก่อน Production Deployment** + +- [ ] **Performance:** Core Web Vitals ผ่านเกณฑ์ +- [ ] **Accessibility:** WCAG 2.1 AA compliant +- [ ] **Security:** Security audit ผ่าน +- [ ] **Testing:** Test coverage ≥ 80% +- [ ] **Browser Compatibility:** ทำงานได้บนเบราว์เซอร์หลัก +- [ ] **Mobile Responsive:** ใช้งานได้ดีบนมือถือ +- [ ] **Documentation:** เอกสารครบถ้วน +- [ ] **User Acceptance:** ได้รับการยอมรับจากผู้ใช้ + +--- + +## 🚀 **ขั้นตอนถัดไป** + +1. **Approve แผนนี้** → ปรับแต่งตาม Feedback +2. **Coordinate กับ Backend Team** → Sync API Specifications +3. **เริ่มพัฒนา Phase 0** → Setup Foundation +4. **Regular Sync** → ประสานงานกับ Backend ทุกสัปดาห์ +5. **User Testing** → ทดสอบกับผู้ใช้จริงระหว่างพัฒนา +6. **Deploy to Production** → Week 15 (พร้อม Backend) + +--- + +`End of Frontend Development Plan v1.4.2 (ฉบับปรับปรุง)` diff --git a/Documnets/Project/4_Data_Dictionary_V1_4_2.md b/Documnets/Project/4_Data_Dictionary_V1_4_2.md new file mode 100644 index 0000000..8aed404 --- /dev/null +++ b/Documnets/Project/4_Data_Dictionary_V1_4_2.md @@ -0,0 +1,2947 @@ +# **ตารางฐานข้อมูล (Data Dictionary) - LCBP3-DMS (V1.4.2)** + +เอกสารนี้สรุปโครงสร้างตาราง, Foreign Keys (FK), และ Constraints ที่สำคัญทั้งหมดในฐานข้อมูล LCBP3-DMS (v1.4.0) เพื่อใช้เป็นเอกสารอ้างอิงสำหรับทีมพัฒนา Backend (NestJS) และ Frontend (Next.js) โดยอิงจาก Requirements และ SQL Script ล่าสุด [GLM-4.6] + +--- + +## **1. 🏢 Core & Master Data Tables (องค์กร, โครงการ, สัญญา)** + +### 1.1 organization_roles + +**Purpose**: Master table for organization role types in the system + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | ---------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for organization role | +| role_name | VARCHAR(20) | NOT NULL, UNIQUE | Role name (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY) | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (role_name) + +**Business Rules**: + +- Predefined system roles for organization types +- Cannot be deleted if referenced by organizations + +--- + +### 1.2 organizations + +**Purpose**: Master table storing all organizations involved in the system + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | ----------------------------------- | ---------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for organization | +| organization_code | VARCHAR(20) | NOT NULL, UNIQUE | Organization code (e.g., 'กทท.', 'TEAM') | +| organization_name | VARCHAR(255) | NOT NULL | Full organization name | +| is_active | BOOLEAN | DEFAULT TRUE | Active status (1=active, 0=inactive) | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (organization_code) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: users, project_organizations, contract_organizations, correspondences, circulations + +**Seed Data**: Pre-populated with 15 organizations including: + +- Port Authority of Thailand (กทท.) +- Project supervision consultants (สค©.3-xx) +- Design consultants (TEAM) +- Construction supervisors (คคง.) +- Contractors (ผรม.1-4) +- Third parties (EN, CAR) + +--- + +### 1.3 projects + +**Purpose**: Master table for all projects in the system + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | --------------------------- | ----------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for project | +| project_code | VARCHAR(50) | NOT NULL, UNIQUE | Project code (e.g., 'LCBP3') | +| project_name | VARCHAR(255) | NOT NULL | Full project name | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (project_code) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: contracts, correspondences, document_number_formats, drawings + +**Seed Data**: 5 projects for Laem Chabang Port Phase 3 (LCBP3) including main project and 4 sub-contracts + +--- + +### 1.4 contracts + +**Purpose**: Master table for contracts within projects + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | ----------------------------------- | ------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for contract | +| project_id | INT | NOT NULL, FK | Reference to projects table | +| contract_code | VARCHAR(50) | NOT NULL, UNIQUE | Contract code | +| contract_name | VARCHAR(255) | NOT NULL | Full contract name | +| description | TEXT | NULL | Contract description | +| start_date | DATE | NULL | Contract start date | +| end_date | DATE | NULL | Contract end date | +| is_active | BOOLEAN | DEFAULT TRUE | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (contract_code) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- INDEX (project_id, is_active) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_organizations, user_assignments + +**Seed Data**: 7 contracts including design, supervision, construction, and environmental monitoring + +--- + +## **2. 👥 Users & RBAC Tables (ผู้ใช้, สิทธิ์, บทบาท)** + +### 2.1 users + +**Purpose**: Master table storing all system users + +| Column Name | Data Type | Constraints | Description | +| ----------------------- | ------------ | ----------------------------------- | -------------------------------- | +| user_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for user | +| username | VARCHAR(50) | NOT NULL, UNIQUE | Login username | +| password_hash | VARCHAR(255) | NOT NULL | Hashed password (bcrypt) | +| first_name | VARCHAR(50) | NULL | User's first name | +| last_name | VARCHAR(50) | NULL | User's last name | +| email | VARCHAR(100) | NOT NULL, UNIQUE | Email address | +| line_id | VARCHAR(100) | NULL | LINE messenger ID | +| primary_organization_id | INT | NULL, FK | Primary organization affiliation | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| failed_attempts | INT | DEFAULT 0 | Failed login attempts counter | +| locked_until | DATETIME | NULL | Account lock expiration time | +| last_login_at | TIMESTAMP | NULL | Last successful login timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (user_id) +- UNIQUE (username) +- UNIQUE (email) +- FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +- INDEX (is_active) +- INDEX (email) + +**Relationships**: + +- Parent: organizations (primary_organization_id) +- Referenced by: user_assignments, audit_logs, notifications, circulation_routings + +**Security Features**: + +- Password stored as bcrypt hash +- Account locking after failed attempts +- Last login tracking + +**Seed Data**: 3 initial users (superadmin, editor01, viewer01) + +--- + +### 2.2 roles + +**Purpose**: Master table defining system roles with scope levels + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ---------------------------------------------------- | +| role_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for role | +| role_name | VARCHAR(100) | NOT NULL | Role name (e.g., 'Superadmin', 'Document Control') | +| scope | ENUM | NOT NULL | Scope level: Global, Organization, Project, Contract | +| description | TEXT | NULL | Role description | +| is_system | BOOLEAN | DEFAULT FALSE | System role flag (cannot be deleted) | + +**Indexes**: + +- PRIMARY KEY (role_id) +- INDEX (scope) + +**Relationships**: + +- Referenced by: role_permissions, user_assignments + +**Seed Data**: 7 predefined roles + +1. Superadmin (Global) - Full system access +2. Org Admin (Organization) - Organization management +3. Document Control (Organization) - Document lifecycle management +4. Editor (Organization) - Document creation and editing +5. Viewer (Organization) - Read-only access +6. Project Manager (Project) - Project-level management +7. Contract Admin (Contract) - Contract-specific administration + +--- + +### 2.3 permissions + +**Purpose**: Master table defining all system permissions + +| Column Name | Data Type | Constraints | Description | +| --------------- | ------------ | --------------------------- | ------------------------------------------------------ | +| permission_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for permission | +| permission_name | VARCHAR(100) | NOT NULL, UNIQUE | Permission code (e.g., 'rfas.create', 'document.view') | +| description | TEXT | NULL | Permission description | +| module | VARCHAR(50) | NULL | Related module name | +| scope_level | ENUM | NULL | Scope: GLOBAL, ORG, PROJECT | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (permission_id) +- UNIQUE (permission_name) +- INDEX (module) +- INDEX (scope_level) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: role_permissions + +**Permission Categories**: + +1. **System Management** (1): system.manage_all +2. **Organization Management** (2-5): create, edit, delete, view +3. **Project Management** (6-9, 23-26): create, edit, delete, view, manage members/contracts/reports +4. **Role & Permission Management** (10-13): create, edit, delete roles, assign permissions +5. **Master Data Management** (14-17): document types, statuses, categories, tags +6. **User Management** (18-22): create, edit, delete, view, assign organization +7. **Contract Management** (27-28): manage members, view +8. **Document Management** (29-44): CRUD operations, workflows, circulation +9. **Search & Reporting** (48-49): advanced search, generate reports + +**Total Permissions**: 49 + +--- + +### 2.4 role_permissions + +**Purpose**: Junction table mapping roles to permissions (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------- | --------- | --------------- | ------------------------------ | +| role_id | INT | PRIMARY KEY, FK | Reference to roles table | +| permission_id | INT | PRIMARY KEY, FK | Reference to permissions table | + +**Indexes**: + +- PRIMARY KEY (role_id, permission_id) +- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE +- FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE +- INDEX (permission_id) + +**Relationships**: + +- Parent: roles, permissions + +**Seed Data**: Complete permission mappings for all 7 roles + +- Superadmin: All 49 permissions +- Org Admin: 15 permissions (user/org/tag management, view access) +- Document Control: 26 permissions (full document lifecycle) +- Editor: 12 permissions (document CRUD without admin powers) +- Viewer: 2 permissions (view and search only) +- Project Manager: 23 permissions (project management + document CRUD) +- Contract Admin: 15 permissions (contract management + document CRUD) + +--- + +### 2.5 user_assignments + +**Purpose**: Junction table assigning users to roles with scope context + +| Column Name | Data Type | Constraints | Description | +| ------------------- | --------- | --------------------------- | ---------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| user_id | INT | NOT NULL, FK | Reference to users table | +| role_id | INT | NOT NULL, FK | Reference to roles table | +| organization_id | INT | NULL, FK | Organization scope (if applicable) | +| project_id | INT | NULL, FK | Project scope (if applicable) | +| contract_id | INT | NULL, FK | Contract scope (if applicable) | +| assigned_by_user_id | INT | NULL, FK | User who made the assignment | +| assigned_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Assignment timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id) +- INDEX (user_id, role_id) +- INDEX (organization_id) +- INDEX (project_id) +- INDEX (contract_id) + +**Constraints**: + +- CHECK: Only one scope field (organization_id, project_id, contract_id) can be NOT NULL, or all NULL for Global scope + +**Relationships**: + +- Parent: users, roles, organizations, projects, contracts + +**Business Rules**: + +- User can have multiple role assignments with different scopes +- Scope inheritance: Contract → Project → Organization → Global +- Global scope: all scope fields are NULL + +--- + +### 2.6 project_organizations + +**Purpose**: Junction table linking projects to participating organizations (M:N) + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | --------------- | -------------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects table | +| organization_id | INT | PRIMARY KEY, FK | Reference to organizations table | + +**Indexes**: + +- PRIMARY KEY (project_id, organization_id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- INDEX (organization_id) + +**Relationships**: + +- Parent: projects, organizations + +**Seed Data**: Pre-populated with project-organization relationships + +--- + +### 2.7 contract_organizations + +**Purpose**: Junction table linking contracts to participating organizations with roles (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | ------------ | --------------- | ------------------------------------------------------------------------- | +| contract_id | INT | PRIMARY KEY, FK | Reference to contracts table | +| organization_id | INT | PRIMARY KEY, FK | Reference to organizations table | +| role_in_contract | VARCHAR(100) | NULL | Organization's role in contract (Owner, Designer, Consultant, Contractor) | + +**Indexes**: + +- PRIMARY KEY (contract_id, organization_id) +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- INDEX (organization_id) +- INDEX (role_in_contract) + +**Relationships**: + +- Parent: contracts, organizations + +**Seed Data**: Pre-populated with contract-organization-role relationships + +--- + +## **3. ✉️ Correspondences Tables (เอกสารหลัก, Revisions, Workflows)** + +### 3.1 correspondence_types + +**Purpose**: Master table for correspondence document types + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | --------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| type_code | VARCHAR(50) | NOT NULL, UNIQUE | Type code (e.g., 'RFA', 'RFI', 'TRANSMITTAL') | +| type_name | VARCHAR(255) | NOT NULL | Full type name | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (type_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: correspondences, document_number_formats, document_number_counters + +**Seed Data**: 10 correspondence types including RFA, RFI, TRANSMITTAL, EMAIL, INSTRUCTION, LETTER, MEMO, MOM, NOTICE, OTHER + +--- + +### 3.2 correspondence_status + +**Purpose**: Master table for correspondence status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| status_code | VARCHAR(50) | NOT NULL, UNIQUE | Status code (e.g., 'DRAFT', 'SUBOWN') | +| status_name | VARCHAR(255) | NOT NULL | Full status name | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (status_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: correspondence_revisions + +**Seed Data**: 23 status codes covering draft, submission, reply, resubmission, closure, and cancellation states by different parties (Owner, Designer, CSC, Contractor) + +--- + +### 3.3 correspondences + +**Purpose**: Master table for correspondence documents (non-revisioned data) + +| Column Name | Data Type | Constraints | Description | +| ------------------------- | ------------ | --------------------------- | ------------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master correspondence ID | +| correspondence_number | VARCHAR(100) | NOT NULL | Document number (from numbering system) | +| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types | +| is_internal_communication | TINYINT(1) | DEFAULT 0 | Internal (1) or external (0) communication | +| project_id | INT | NOT NULL, FK | Reference to projects table | +| originator_id | INT | NULL, FK | Originating organization | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| created_by | INT | NULL, FK | User who created the record | +| deleted_at | DATETIME | NULL | Soft delete timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- UNIQUE KEY (project_id, correspondence_number) +- INDEX (correspondence_type_id) +- INDEX (originator_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: correspondence_types, projects, organizations, users +- Children: correspondence_revisions, correspondence_recipients, correspondence_tags, correspondence_references, correspondence_attachments, circulations, transmittals + +**Business Rules**: + +- One correspondence can have multiple revisions +- Correspondence number must be unique within a project +- Soft delete preserves history + +--- + +### 3.4 correspondence_revisions + +**Purpose**: Child table storing revision history of correspondences (1:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | ------------ | --------------------------- | ------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| correspondence_id | INT | NOT NULL, FK | Master correspondence ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) | +| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag | +| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision | +| title | VARCHAR(255) | NOT NULL | Document title | +| document_date | DATE | NULL | Document date | +| issued_date | DATETIME | NULL | Issue date | +| received_date | DATETIME | NULL | Received date | +| due_date | DATETIME | NULL | Due date for response | +| description | TEXT | NULL | Revision description | +| details | JSON | NULL | Type-specific details (e.g., RFI questions) | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | +| created_by | INT | NULL, FK | User who created revision | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL +- UNIQUE KEY (correspondence_id, revision_number) +- UNIQUE KEY (correspondence_id, is_current) - Only one current revision per correspondence +- INDEX (correspondence_status_id) +- INDEX (is_current) +- INDEX (document_date) +- INDEX (issued_date) + +**Relationships**: + +- Parent: correspondences, correspondence_status, users + +**Business Rules**: + +- Only one revision can be marked as current (is_current=TRUE) per correspondence +- Revision numbers are sequential starting from 0 +- JSON details field allows type-specific data storage + +--- + +### 3.5 correspondence_recipients + +**Purpose**: Junction table for correspondence recipients (TO/CC) (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------- | ---------------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| recipient_organization_id | INT | PRIMARY KEY, FK | Recipient organization | +| recipient_type | ENUM('TO', 'CC') | PRIMARY KEY | Recipient type | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type) +- FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT +- INDEX (recipient_organization_id) +- INDEX (recipient_type) + +**Relationships**: + +- Parent: correspondences, organizations + +**Business Rules**: + +- One organization can be both TO and CC recipient +- Cascade delete when correspondence is deleted + +--- + +### 3.6 tags + +**Purpose**: Master table for document tagging system + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique tag ID | +| tag_name | VARCHAR(100) | NOT NULL, UNIQUE | Tag name | +| description | TEXT | NULL | Tag description | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (tag_name) +- INDEX (tag_name) - For autocomplete + +**Relationships**: + +- Referenced by: correspondence_tags + +--- + +### 3.7 correspondence_tags + +**Purpose**: Junction table linking correspondences to tags (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| tag_id | INT | PRIMARY KEY, FK | Reference to tags | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, tag_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE +- INDEX (tag_id) + +**Relationships**: + +- Parent: correspondences, tags + +--- + +### 3.8 correspondence_references + +**Purpose**: Junction table for cross-referencing correspondences (M:N) + +| Column Name | Data Type | Constraints | Description | +| --------------------- | --------- | --------------- | ------------------------------------- | +| src_correspondence_id | INT | PRIMARY KEY, FK | Source correspondence ID | +| tgt_correspondence_id | INT | PRIMARY KEY, FK | Target (referenced) correspondence ID | + +**Indexes**: + +- PRIMARY KEY (src_correspondence_id, tgt_correspondence_id) +- FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- INDEX (tgt_correspondence_id) + +**Relationships**: + +- Parent: correspondences (both sides) + +**Business Rules**: + +- Allows tracing document relationships +- Bi-directional references should be created as separate records + +### 3.9 ตาราง correspondence_routing_templates + +**Purpose**: เก็บข้อมูลแม่แบบ (Template) ของสายงานการส่งต่อเอกสารเพื่อขออนุมัติ ทำให้ไม่ต้องกำหนดขั้นตอนซ้ำทุกครั้ง สามารถสร้างเป็นแม่แบบทั่วไป หรือเฉพาะสำหรับโครงการใดโครงการหนึ่งได้ + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ | +| template_name | VARCHAR(255) | NOT NULL | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ" | +| description | TEXT | NULL | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้ | +| project_id | INT | NULL | ID ของโครงการที่แม่แบบนี้สังกัดอยู่ (ถ้ามี) **ค่า NULL หมายถึง** เป็น "แม่แบบทั่วไป" ที่สามารถใช้กับทุกโครงการได้ | +| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ | +| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด | +| is_active | BOOLEAN | DEFAULT TRUE | สถานะใช้งาน | + +**Indexes**: + +- ux_routing_template_name_project (template_name, project_id): ดัชนีแบบ UNIQUE ป้องกันการมีชื่อแม่แบบซ้ำกันในแต่ละโครงการ หรือในส่วนของแม่แบบทั่วไป (ที่ project_id เป็น NULL) + +**ความสัมพันธ์ Foreign Key:** + +- fk_crt_project: อ้างอิงไปยัง projects(id) +- ON DELETE CASCAD`: ถ้าโครงการ (projects) ถูกลบ แม่แบบที่เกี่ยวข้องกับโครงการนั้นๆ จะถูกลบไปด้วยทั้งหมด + +--- + +### 3.10 ตาราง correspondence_routing_template_steps + +**Purpose**: เก็บรายละเอียดของแต่ละขั้นตอน (Steps) ภายในแม่แบบสายงาน (correspondence_routing_templates) กำหนดว่าจะส่งไปที่องค์กรไหน ลำดับเป็นเท่าไร และเพื่อวัตถุประสงค์อะไร + +| Column Name | Data Type | Constraints | Description | +| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน | +| template_id | INT | NOT NULL | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่ | +| sequence | INT | NOT NULL | ลำดับของขั้นตอน (1, 2, 3, ...) | +| to_organization_id | INT | NOT NULL | ID ขององค์กรที่เป็นผู้รับในขั้นตอนนี้ | +| step_purpose | ENUM | NOT NULL,DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้ **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ] | +| expected_days | INT | NULL | วันที่คาดหวัง | + +**Indexes**: + +- ux_cor_template_sequence (template_id, sequence): ดัชนีแบบ UNIQUE ป้องกันการมีลำดับขั้นตอนซ้ำกันภายในแม่แบบเดียวกัน + +**ความสัมพันธ์ Foreign Key:** + +- fk_cwts_template: อ้างอิงไปยัง correspondence_routing_templates(id) +- ON DELETE CASCADE: ถ้าแม่แบบถูกลบ ขั้นตอนทั้งหมดภายในแม่แบบนั้นจะถูกลบไปด้วย +- fk_cwts_org: อ้างอิงไปยัง organizations(id) +- ON DELETE CASCADE: ถ้าองค์กรถูกลบ ขั้นตอนที่ชี้ไปยังองค์กรนั้นจะถูกลบ + +--- + +### 3.11 ตาราง correspondence_routings + +**Purpose**: เป็นตารางที่เก็บข้อมูลการส่งต่อเอกสารจริง (Instance/Run-time) ติดตามประวัติการเคลื่อนย้ายของแต่ละเอกสาร ว่าผ่านใครมาบ้าง อยู่ที่ใคร และสถานะปัจจุบันคืออะไร + +| Column Name | Data Type | Constraints | Description | +| -------------------- | --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของรายการส่งต่อ | +| correspondence_id | INT | NOT NUL | ID ของเอกสาร (FK ไปยัง correspondence_revisions) | +| template_id | INT | NULL | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง) | +| sequence | INT | NOT NULL | ลำดับของขั้นตอนการส่งต่อจริง | +| from_organization_id | INT | NOT NULL | ID ขององค์กรผู้ส่ง | +| to_organization_id | INT | NOT NULL | ID ขององค์กรผู้รับ | +| step_purpose | ENUM | NOT NULL, DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้จริง **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ, FOR_ACTION: เพื่อดำเนินการ] | +| status | ENUM | NOT NULL, DEFAULT SENT | [ACTIONED: ดำเนินการแล้ว, FORWARDED: ส่งต่อแล้ว, REPLIE: ตอบกลับแล้ว] | +| comments | TEXT | NULL | หมายเหตุหรือความคิดเห็นในการส่งต่อ | +| due_date | DATETIME | NULL | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้ | +| processed_by_user_id | INT | NULL | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ | +| processed_at | TIMESTAMP | NULL | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น | +| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้ | + +**Indexes**: + +- ux_cor_routing_sequence (correspondence_id, sequence): ดัชนีแบบ UNIQUE ทำให้มั่นใจได้ว่าแต่ละเอกสารจะมีขั้นตอนการส่งต่อตามลำดับได้เพียงชุดเดียว + +**ความสัมพันธ์ Foreign Key:** + +- fk_crs_correspondence: อ้างอิงไปยัง correspondence_revisions(correspondence_id) +- ON DELETE CASCADE`: ถ้าเอกสาร (revision) ถูกลบ ประวัติการส่งต่อทั้งหมดจะถูกลบไปด้วย +- fk_crs_template: อ้างอิงไปยัง correspondence_routing_templates(id) +- ON DELETE SET NULL: ถ้าแม่แบบถูกลบ ค่า template_id ในตารางนี้จะถูกเปลี่ยนเป็น NULL เพื่อรักษาประวัติการส่งต่อไว้ +- fk_crs_from_org: อ้างอิงไปยัง organizations(id) +- ON DELETE CASCADE: ถ้าองค์กรผู้ส่งถูกลบ รายการส่งต่อนี้จะถูกลบ +- fk_crs_to_org: อ้างอิงไปยัง organizations(id) +- ON DELETE CASCADE: ถ้าองค์กรผู้รับถูกลบ รายการส่งต่อนี้จะถูกลบ +- fk_crs_processed_by_user: อ้างอิงไปยัง users(user_id) +- ON DELETE SET NULL: ถ้าผู้ใช้ถูกลบ ค่า processed_by_user_id จะถูกเปลี่ยนเป็น NULL เพื่อรักษาประวัติการดำเนินการไว้ + +--- + +### 3.12 ตาราง correspondence_status_transitions + +**Purpose**: ตารางนี้ใช้กำหนดกฎ (State Machine) ว่าสถานะใดสามารถเปลี่ยนไปเป็นสถานะใดได้บ้าง โดยขึ้นอยู่กับประเภทของหนังสือ เพื่อควบคุมการไหลของสถานะให้ถูกต้องตามข้อบังคับ + +| Column Name | Data Type | Constraints | Description | +| -------------- | --------- | ----------- | ----------------------------------------------- | +| type_id | INT | PRIMARY KEY | ID ของประเภทหนังสือ (เช่น หนังสือภายใน, หนังสือภายนอก) | +| from_status_id | INT | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง) | +| to_status_id | INT | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ) | + +**คีย์หลัก (Primary Key):** + +- (type_id, from_status_id, to_status_id)`: คีย์หลักแบบประกอบ ทำให้แน่ใจว่าแต่ละประเภทหนังสือ การเปลี่ยนจากสถานะหนึ่งไปอีกสถานะหนึ่งจะซ้ำกันไม่ได้ + +**ความสัมพันธ์ Foreign Key:** + +- fk_cst_type : อ้างอิงไปยัง correspondence_types(id) +- fk_cst_from : อ้างอิงไปยัง correspondence_status(id) +- fk_cst_to : อ้างอิงไปยัง correspondence_status(id) +- ไม่มีการกำหนด ON DELETE จึงเป็นค่าเริ่มต้น RESTRICT หมายความว่า จะลบประเภทหนังสือหรือสถานะไม่ได้ ถ้ายังมีการใช้งานอยู่ในตารางนี้ + +--- + +## **4. 📐 approval: RFA Tables (เอกสารขออนุมัติ, Workflows)** + +### 4.1 rfa_types + +**Purpose**: Master table for RFA (Request for Approval) types + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| type_code | VARCHAR(20) | NOT NULL, UNIQUE | Type code (DWG, DOC, MAT, etc.) | +| type_name | VARCHAR(100) | NOT NULL | Full type name | +| description | TEXT | NULL | Type description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (type_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfas + +**Seed Data**: 11 RFA types including Shop Drawing (DWG), Document (DOC), Specification (SPC), Calculation (CAL), Test Report (TRP), Survey Report (SRY), QA/QC Document, Method Statement (MES), Material (MAT), As-Built (ASB), Other (OTH) + +--- + +### 4.2 rfa_status_codes + +**Purpose**: Master table for RFA status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | --------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| status_code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (DFT, FAP, FRE, etc.) | +| status_name | VARCHAR(100) | NOT NULL | Full status name | +| description | TEXT | NULL | Status description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (status_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfa_revisions + +**Seed Data**: 7 status codes + +- DFT: Draft +- FAP: For Approve +- FRE: For Review +- FCO: For Construction +- ASB: AS-Built +- OBS: Obsolete +- CC: Canceled + +--- + +### 4.3 rfa_approve_codes + +**Purpose**: Master table for RFA approval result codes + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | --------------------------- | -------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| approve_code | VARCHAR(20) | NOT NULL, UNIQUE | Approval code (1A, 1C, 3R, etc.) | +| approve_name | VARCHAR(100) | NOT NULL | Full approval name | +| description | TEXT | NULL | Code description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (approve_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfa_revisions + +**Seed Data**: 8 approval codes + +- 1A: Approved by Authority +- 1C: Approved by CSC +- 1N: Approved As Note +- 1R: Approved with Remarks +- 3C: Consultant Comments +- 3R: Revise and Resubmit +- 4X: Reject +- 5N: No Further Action + +--- + +### 4.4 rfas + +**Purpose**: Master table for RFA documents (non-revisioned data) + +| Column Name | Data Type | Constraints | Description | +| ----------- | --------- | --------------------------- | --------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master RFA ID | +| rfa_type_id | INT | NOT NULL, FK | Reference to rfa_types | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| created_by | INT | NULL, FK | User who created the record | +| deleted_at | DATETIME | NULL | Soft delete timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id) +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- INDEX (rfa_type_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: rfa_types, users +- Children: rfa_revisions + +**Business Rules**: + +- One RFA can have multiple revisions +- Soft delete preserves history + +--- + +### 4.5 rfa_revisions + +**Purpose**: Child table storing revision history of RFAs (1:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------- | ------------ | --------------------------- | ---------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) | +| rfa_id | INT | NOT NULL, FK | Master RFA ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) | +| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag | +| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status | +| rfa_approve_code_id | INT | NULL, FK | Approval result code | +| title | VARCHAR(255) | NOT NULL | RFA title | +| document_date | DATE | NULL | Document date | +| issued_date | DATE | NULL | Issue date for approval | +| received_date | DATETIME | NULL | Received date | +| approved_date | DATE | NULL | Approval date | +| description | TEXT | NULL | Revision description | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | +| created_by | INT | NULL, FK | User who created revision | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE +- FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id) +- FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL +- UNIQUE KEY (rfa_id, revision_number) +- UNIQUE KEY (rfa_id, is_current) +- INDEX (rfa_status_code_id) +- INDEX (rfa_approve_code_id) +- INDEX (is_current) + +**Relationships**: + +- Parent: correspondences, rfas, rfa_status_codes, rfa_approve_codes, users +- Children: rfa_items, rfa_workflows + +**Business Rules**: + +- RFA is a specialized type of correspondence +- Only one revision can be current per RFA +- Links to shop drawings through rfa_items + +--- + +### 4.6 rfa_items + +**Purpose**: Junction table linking RFA revisions to shop drawing revisions (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ------------------------------ | +| rfarev_correspondence_id | INT | PRIMARY KEY, FK | RFA revision correspondence ID | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Shop drawing revision ID | + +**Indexes**: + +- PRIMARY KEY (rfarev_correspondence_id, shop_drawing_revision_id) +- FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- INDEX (shop_drawing_revision_id) + +**Relationships**: + +- Parent: rfa_revisions, shop_drawing_revisions + +**Business Rules**: + +- Used primarily for RFA type = 'DWG' (Shop Drawing) +- One RFA can contain multiple shop drawings +- One shop drawing can be referenced by multiple RFAs + +--- + +### 4.7 rfa_workflow_templates + +**Purpose**: Master table for RFA approval workflow templates + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID | +| template_name | VARCHAR(100) | NOT NULL | Template name | +| description | TEXT | NULL | Template description | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- INDEX (is_active) +- INDEX (template_name) + +**Relationships**: + +- Children: rfa_workflow_template_steps + +**Business Rules**: + +- Defines reusable approval workflows for RFAs +- Can be assigned to specific RFA types or organizations + +--- + +### 4.8 rfa_workflow_template_steps + +**Purpose**: Child table defining steps in workflow templates + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | --------------------------- | ----------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique step ID | +| template_id | INT | NOT NULL, FK | Reference to workflow template | +| step_number | INT | NOT NULL | Step sequence order | +| organization_id | INT | NOT NULL, FK | Organization responsible for step | +| role_id | INT | NULL, FK | Required role for this step | +| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE | +| duration_days | INT | NULL | Expected duration in days | +| is_optional | BOOLEAN | DEFAULT FALSE | Optional step flag | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (role_id) REFERENCES roles(role_id) +- INDEX (template_id, step_number) +- INDEX (organization_id) + +**Relationships**: + +- Parent: rfa_workflow_templates, organizations, roles + +**Business Rules**: + +- Steps are executed in step_number order +- Optional steps can be skipped +- Duration used for deadline calculation + +--- + +### 4.9 rfa_workflows + +**Purpose**: Transaction log table tracking actual RFA approval workflow execution + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | ----------------------------------- | ------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID | +| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision | +| step_number | INT | NOT NULL | Current step number | +| organization_id | INT | NOT NULL, FK | Organization responsible | +| assigned_to | INT | NULL, FK | Assigned user ID | +| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE | +| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED | +| comments | TEXT | NULL | Comments/remarks | +| completed_at | DATETIME | NULL | Completion timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (assigned_to) REFERENCES users(user_id) +- INDEX (rfa_revision_id, step_number) +- INDEX (assigned_to, status) +- INDEX (status) + +**Relationships**: + +- Parent: rfa_revisions, organizations, users + +**Business Rules**: + +- Records actual workflow execution history +- Tracks who did what and when +- Multiple records per RFA revision (one per step) +- Status changes tracked via updated_at + +--- + +## **5. 📐 Drawings Tables (แบบ, หมวดหมู่)** + +### 5.1 contract_drawing_volumes + +**Purpose**: Master table for contract drawing volume classification + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique volume ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| volume_code | VARCHAR(50) | NOT NULL | Volume code | +| volume_name | VARCHAR(255) | NOT NULL | Volume name | +| description | TEXT | NULL | Volume description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, volume_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawings + +**Business Rules**: + +- Volume codes must be unique within a project +- Used for organizing large sets of contract drawings + +--- + +### 5.2 contract_drawing_cats + +**Purpose**: Master table for contract drawing main categories + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| cat_code | VARCHAR(50) | NOT NULL | Category code | +| cat_name | VARCHAR(255) | NOT NULL | Category name | +| description | TEXT | NULL | Category description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, cat_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawing_subcat_cat_maps + +**Business Rules**: + +- Category codes must be unique within a project +- Hierarchical relationship with sub-categories via mapping table + +--- + +### 5.3 contract_drawing_sub_cats + +**Purpose**: Master table for contract drawing sub-categories + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| sub_cat_code | VARCHAR(50) | NOT NULL | Sub-category code | +| sub_cat_name | VARCHAR(255) | NOT NULL | Sub-category name | +| description | TEXT | NULL | Sub-category description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, sub_cat_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawings, contract_drawing_subcat_cat_maps + +**Business Rules**: + +- Sub-category codes must be unique within a project +- Can be mapped to multiple main categories via mapping table + +--- + +### 5.4 contract_drawing_subcat_cat_maps + +**Purpose**: Junction table mapping sub-categories to main categories (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------- | --------- | --------------- | -------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects | +| sub_cat_id | INT | PRIMARY KEY, FK | Reference to sub-category | +| cat_id | INT | PRIMARY KEY, FK | Reference to main category | + +**Indexes**: + +- PRIMARY KEY (project_id, sub_cat_id, cat_id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE +- FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +- INDEX (sub_cat_id) +- INDEX (cat_id) + +**Relationships**: + +- Parent: projects, contract_drawing_sub_cats, contract_drawing_cats + +**Business Rules**: + +- Allows flexible categorization +- One sub-category can belong to multiple main categories +- All three fields required for uniqueness + +--- + +### 5.5 contract_drawings + +**Purpose**: Master table for contract drawings (from contract specifications) + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| condwg_no | VARCHAR(255) | NOT NULL | Contract drawing number | +| title | VARCHAR(255) | NOT NULL | Drawing title | +| sub_cat_id | INT | NULL, FK | Reference to sub-category | +| volume_id | INT | NULL, FK | Reference to volume | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| deleted_at | DATETIME | NULL | Soft delete timestamp | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT +- FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT +- FOREIGN KEY (updated_by) REFERENCES users(user_id) +- UNIQUE KEY (project_id, condwg_no) +- INDEX (sub_cat_id) +- INDEX (volume_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: projects, contract_drawing_sub_cats, contract_drawing_volumes, users +- Referenced by: shop_drawing_revision_contract_refs, contract_drawing_attachments + +**Business Rules**: + +- Drawing numbers must be unique within a project +- Represents baseline/contract drawings +- Referenced by shop drawings for compliance tracking +- Soft delete preserves history + +--- + +### 5.6 shop_drawing_main_categories + +**Purpose**: Master table for shop drawing main categories (discipline-level) + +| Column Name | Data Type | Constraints | Description | +| ------------------ | ------------ | ----------------------------------- | ------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID | +| main_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Category code (ARCH, STR, MEP, etc.) | +| main_category_name | VARCHAR(255) | NOT NULL | Category name | +| description | TEXT | NULL | Category description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (main_category_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: shop_drawing_sub_categories, shop_drawings + +**Business Rules**: + +- Global categories (not project-specific) +- Typically represents engineering disciplines + +--- + +### 5.7 shop_drawing_sub_categories + +**Purpose**: Master table for shop drawing sub-categories (component-level) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | ----------------------------------- | ----------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID | +| sub_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Sub-category code (STR-COLUMN, ARCH-DOOR, etc.) | +| sub_category_name | VARCHAR(255) | NOT NULL | Sub-category name | +| main_category_id | INT | NOT NULL, FK | Reference to main category | +| description | TEXT | NULL | Sub-category description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (sub_category_code) +- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +- INDEX (main_category_id) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Parent: shop_drawing_main_categories +- Referenced by: shop_drawings + +**Business Rules**: + +- Global sub-categories (not project-specific) +- Hierarchical under main categories +- Represents specific drawing types or components + +--- + +### 5.8 shop_drawings + +**Purpose**: Master table for shop drawings (contractor-submitted) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | ------------ | ----------------------------------- | -------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | Shop drawing number | +| title | VARCHAR(500) | NOT NULL | Drawing title | +| main_category_id | INT | NOT NULL, FK | Reference to main category | +| sub_category_id | INT | NOT NULL, FK | Reference to sub-category | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| deleted_at | DATETIME | NULL | Soft delete timestamp | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (drawing_number) +- FOREIGN KEY (project_id) REFERENCES projects(id) +- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +- FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) +- FOREIGN KEY (updated_by) REFERENCES users(user_id) +- INDEX (project_id) +- INDEX (main_category_id) +- INDEX (sub_category_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users +- Children: shop_drawing_revisions + +**Business Rules**: + +- Drawing numbers are globally unique across all projects +- Represents contractor shop drawings +- Can have multiple revisions +- Soft delete preserves history + +--- + +### 5.9 shop_drawing_revisions + +**Purpose**: Child table storing revision history of shop drawings (1:N) + +| Column Name | Data Type | Constraints | Description | +| --------------- | ----------- | --------------------------- | ------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| shop_drawing_id | INT | NOT NULL, FK | Master shop drawing ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) | +| revision_date | DATE | NULL | Revision date | +| description | TEXT | NULL | Revision description/changes | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE +- UNIQUE KEY (shop_drawing_id, revision_number) +- INDEX (revision_date) + +**Relationships**: + +- Parent: shop_drawings +- Referenced by: rfa_items, shop_drawing_revision_contract_refs, shop_drawing_revision_attachments + +**Business Rules**: + +- Revision numbers are sequential starting from 0 +- Each revision can reference multiple contract drawings +- Each revision can have multiple file attachments +- Linked to RFAs for approval tracking + +--- + +### 5.10 shop_drawing_revision_contract_refs + +**Purpose**: Junction table linking shop drawing revisions to referenced contract drawings (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ---------------------------------- | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision | +| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing | + +**Indexes**: + +- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id) +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +- INDEX (contract_drawing_id) + +**Relationships**: + +- Parent: shop_drawing_revisions, contract_drawings + +**Business Rules**: + +- Tracks which contract drawings each shop drawing revision is based on +- Ensures compliance with contract specifications +- One shop drawing revision can reference multiple contract drawings + +--- + +## **6. 🔄 Circulations Tables (ใบเวียนภายใน)** + +### 6.1 circulation_status_codes + +**Purpose**: Master table for circulation workflow status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | --------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique status ID | +| code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (OPEN, IN_REVIEW, COMPLETED, CANCELLED) | +| description | VARCHAR(50) | NOT NULL | Status description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: circulations + +**Seed Data**: 4 status codes + +- OPEN: Initial status when created +- IN_REVIEW: Under review by recipients +- COMPLETED: All recipients have responded +- CANCELLED: Withdrawn/cancelled + +--- + +### 6.2 circulations + +**Purpose**: Master table for internal circulation sheets (document routing) + +| Column Name | Data Type | Constraints | Description | +| ----------------------- | ------------ | ----------------------------------- | ----------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique circulation ID | +| correspondence_id | INT | UNIQUE, FK | Link to correspondence (1:1 relationship) | +| organization_id | INT | NOT NULL, FK | Organization that owns this circulation | +| circulation_no | VARCHAR(100) | NOT NULL | Circulation sheet number | +| circulation_subject | VARCHAR(500) | NOT NULL | Subject/title | +| circulation_status_code | VARCHAR(20) | NOT NULL, FK | Current status code | +| created_by_user_id | INT | NOT NULL, FK | User who created circulation | +| submitted_at | TIMESTAMP | NULL | Submission timestamp | +| closed_at | TIMESTAMP | NULL | Closure timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (correspondence_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code) +- FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) +- INDEX (organization_id) +- INDEX (circulation_status_code) +- INDEX (created_by_user_id) + +**Relationships**: + +- Parent: correspondences, organizations, circulation_status_codes, users +- Children: circulation_routings, circulation_attachments + +**Business Rules**: + +- Internal document routing within organization +- One-to-one relationship with correspondences +- Tracks document review/approval workflow +- Status progression: OPEN → IN_REVIEW → COMPLETED/CANCELLED + +--- + +### 6.3 circulation_templates + +**Purpose**: Master table for circulation workflow templates + +| Column Name | Data Type | Constraints | Description | +| --------------- | ------------ | ----------------------------------- | --------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID | +| template_name | VARCHAR(100) | NOT NULL | Template name | +| description | TEXT | NULL | Template description | +| organization_id | INT | NOT NULL, FK | Template owner organization | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- INDEX (organization_id) +- INDEX (is_active) + +**Relationships**: + +- Parent: organizations +- Children: circulation_template_assignees + +**Business Rules**: + +- Organization-specific templates +- Defines reusable routing workflows + +--- + +### 6.4 circulation_template_assignees + +**Purpose**: Child table defining steps in circulation templates + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | --------------------------- | --------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique step ID | +| template_id | INT | NOT NULL, FK | Reference to template | +| step_number | INT | NOT NULL | Step sequence order | +| organization_id | INT | NOT NULL, FK | Organization responsible for step | +| role_id | INT | NULL, FK | Required role for this step | +| duration_days | INT | NULL | Expected duration in days | +| is_optional | BOOLEAN | DEFAULT FALSE | Optional step flag | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (role_id) REFERENCES roles(role_id) +- INDEX (template_id, step_number) +- INDEX (organization_id) + +**Relationships**: + +- Parent: circulation_templates, organizations, roles + +**Business Rules**: + +- Steps executed in step_number order +- Optional steps can be skipped +- Duration used for deadline calculation + +--- + +### 6.5 circulation_routings + +**Purpose**: Transaction log table tracking actual circulation routing execution + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | ----------------------------------- | ------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique routing log ID | +| circulation_id | INT | NOT NULL, FK | Reference to circulation | +| step_number | INT | NOT NULL | Current step number | +| organization_id | INT | NOT NULL, FK | Organization responsible | +| assigned_to | INT | NULL, FK | Assigned user ID | +| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED | +| comments | TEXT | NULL | Comments/remarks | +| completed_at | DATETIME | NULL | Completion timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (assigned_to) REFERENCES users(user_id) +- INDEX (circulation_id, step_number) +- INDEX (assigned_to, status) +- INDEX (status) + +**Relationships**: + +- Parent: circulations, organizations, users + +**Business Rules**: + +- Records actual routing history +- Multiple records per circulation (one per step) +- Tracks who reviewed/approved and when +- Used in v_user_tasks view for pending items + +--- + +## **7. 📤 Transmittals Tables (เอกสารนำส่ง)** + +### 7.1 transmittals + +**Purpose**: Child table for transmittal-specific data (1:1 with correspondences) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | --------------------------------------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences (1:1) | +| purpose | ENUM | NULL | Purpose: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW, OTHER | +| remarks | TEXT | NULL | Additional remarks | + +**Indexes**: + +- PRIMARY KEY (correspondence_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- INDEX (purpose) + +**Relationships**: + +- Parent: correspondences +- Children: transmittal_items + +**Business Rules**: + +- One-to-one relationship with correspondences +- Transmittal is a correspondence type for forwarding documents +- Contains metadata about the transmission + +--- + +### 7.2 transmittal_items + +**Purpose**: Junction table listing documents included in transmittal (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------------- | ------------ | --------------------------- | --------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique item ID | +| transmittal_id | INT | NOT NULL, FK | Reference to transmittal | +| item_correspondence_id | INT | NOT NULL, FK | Reference to document being transmitted | +| quantity | INT | DEFAULT 1 | Number of copies | +| remarks | VARCHAR(255) | NULL | Item-specific remarks | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- UNIQUE KEY (transmittal_id, item_correspondence_id) +- INDEX (item_correspondence_id) + +**Relationships**: + +- Parent: transmittals, correspondences + +**Business Rules**: + +- One transmittal can contain multiple documents +- Tracks quantity of physical copies (if applicable) +- Links to any type of correspondence document + +--- + +## **8. 📎 File Management Tables (ไฟล์แนบ)** + +### 8.1 attachments + +**Purpose**: Central repository for all file attachments in the system + +| Column Name | Data Type | Constraints | Description | +| ------------------- | ------------ | --------------------------- | --------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID | +| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload | +| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename | +| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) | +| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) | +| file_size | INT | NOT NULL | File size in bytes | +| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +- INDEX (stored_filename) +- INDEX (mime_type) +- INDEX (uploaded_by_user_id) +- INDEX (created_at) + +**Relationships**: + +- Parent: users +- Referenced by: correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments, contract_drawing_attachments + +**Business Rules**: + +- Central storage prevents file duplication +- Stored filename prevents naming conflicts +- File path points to QNAP NAS storage +- Original filename preserved for download +- One file record can be linked to multiple documents + +--- + +### 8.2 correspondence_attachments + +**Purpose**: Junction table linking correspondences to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, attachment_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: correspondences, attachments + +**Business Rules**: + +- One correspondence can have multiple attachments +- One attachment can be linked to multiple correspondences +- is_main_document identifies primary file (typically PDF) + +--- + +### 8.3 circulation_attachments + +**Purpose**: Junction table linking circulations to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | --------- | --------------- | -------------------------- | +| circulation_id | INT | PRIMARY KEY, FK | Reference to circulations | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (circulation_id, attachment_id) +- FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: circulations, attachments + +--- + +### 8.4 shop_drawing_revision_attachments + +**Purpose**: Junction table linking shop drawing revisions to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ---------------------------------- | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (shop_drawing_revision_id, attachment_id) +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (file_type) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: shop_drawing_revisions, attachments + +**Business Rules**: + +- file_type categorizes drawing file formats +- Typically includes PDF for viewing and DWG for editing +- SOURCE may include native CAD files + +--- + +### 8.5 contract_drawing_attachments + +**Purpose**: Junction table linking contract drawings to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------- | --------- | --------------- | ---------------------------------- | +| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (contract_drawing_id, attachment_id) +- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (file_type) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: contract_drawings, attachments + +--- + +## **9. 🔢 Document Numbering Tables (การสร้างเลขที่เอกสาร)** + +### 9.1 document_number_formats + +**Purpose**: Master table defining document numbering templates per project and type + +| Column Name | Data Type | Constraints | Description | +| ---------------------- | ------------ | ----------------------------------- | -------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique format ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence types | +| format_template | VARCHAR(255) | NOT NULL | Template string (e.g., '{ORG_CODE}-{TYPE_CODE}-{SEQ:4}') | +| description | TEXT | NULL | Format description | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, correspondence_type_id) +- INDEX (project_id) +- INDEX (correspondence_type_id) + +**Relationships**: + +- Parent: projects, correspondence_types + +**Business Rules**: + +- One format template per project per correspondence type combination +- Template placeholders: {ORG_CODE}, {TYPE_CODE}, {YEAR}, {SEQ:n} where n is zero-padding length +- Used by document numbering module to generate unique document numbers + +--- + +### 9.2 document_number_counters + +**Purpose**: Transaction table maintaining running sequence numbers for document numbering + +| Column Name | Data Type | Constraints | Description | +| -------------------------- | --------- | --------------- | --------------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects | +| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization | +| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types | +| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) | +| last_number | INT | DEFAULT 0 | Last assigned sequence number | + +**Indexes**: + +- PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, current_year) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +- INDEX (project_id) +- INDEX (originator_organization_id) +- INDEX (correspondence_type_id) +- INDEX (current_year) + +**Relationships**: + +- Parent: projects, organizations, correspondence_types + +**Business Rules**: + +- Composite primary key ensures unique counters per project/organization/type/year +- Counter resets each year +- Thread-safe increments handled by stored procedure sp_get_next_document_number +- last_number tracks highest assigned number +- Used with document_number_formats to generate complete document numbers + +--- + +## **10. ⚙️ System & Logs Tables (ระบบและ Log)** + +### 10.1 audit_logs + +**Purpose**: Comprehensive audit trail for all significant system actions + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | --------------------------- | ------------------------------------------------------ | +| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID | +| user_id | INT | NULL, FK | User who performed action | +| action | VARCHAR(100) | NOT NULL | Action code (e.g., 'rfa.create', 'login.success') | +| entity_type | VARCHAR(50) | NULL | Entity/module affected (e.g., 'rfa', 'correspondence') | +| entity_id | VARCHAR(50) | NULL | Primary ID of affected record | +| details_json | JSON | NULL | Additional context/details in JSON format | +| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) | +| user_agent | VARCHAR(255) | NULL | Browser user agent string | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Action timestamp | + +**Indexes**: + +- PRIMARY KEY (audit_id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL +- INDEX (user_id) +- INDEX (action) +- INDEX (entity_type, entity_id) +- INDEX (created_at) +- INDEX (ip_address) + +**Relationships**: + +- Parent: users + +**Business Rules**: + +- Immutable records (no updates/deletes) +- Captures all CRUD operations on sensitive data +- Includes authentication events (login, logout, failed attempts) +- JSON details field for flexible data storage +- Retention policy: typically 7 years for compliance +- Used for security audits, compliance reporting, and troubleshooting + +**Common Actions**: + +- Authentication: login.success, login.failed, logout +- Documents: correspondence.create, correspondence.update, rfa.submit +- Users: user.create, user.deactivate, role.assign +- Workflow: rfa.approve, rfa.reject, circulation.complete + +--- + +### 10.2 notifications + +**Purpose**: User notification queue for email, LINE, and in-system alerts + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | --------------------------- | ------------------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique notification ID | +| user_id | INT | NOT NULL, FK | Recipient user ID | +| title | VARCHAR(255) | NOT NULL | Notification title/subject | +| message | TEXT | NOT NULL | Notification message body | +| notification_type | ENUM | NOT NULL | Type: EMAIL, LINE, SYSTEM | +| is_read | BOOLEAN | DEFAULT FALSE | Read status flag | +| entity_type | VARCHAR(50) | NULL | Related entity type (e.g., 'rfa', 'circulation') | +| entity_id | INT | NULL | Related entity ID | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Notification creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +- INDEX (user_id) +- INDEX (notification_type) +- INDEX (is_read) +- INDEX (entity_type, entity_id) +- INDEX (created_at) +- COMPOSITE INDEX (user_id, is_read, created_at) - For user notification listing + +**Relationships**: + +- Parent: users + +**Business Rules**: + +- EMAIL: Sent via SMTP to user's email address +- LINE: Sent via LINE Notify API to user's LINE ID +- SYSTEM: In-app notifications displayed in user interface +- Same notification can trigger multiple types (EMAIL + SYSTEM) +- entity_type/entity_id allow deep-linking to related records +- is_read flag only applicable for SYSTEM notifications +- Auto-cleanup: delete read notifications older than 30 days + +**Common Notification Triggers**: + +- Task assignment (circulation routing, RFA workflow) +- Document status changes (submitted, approved, rejected) +- Approaching deadlines +- System announcements +- Mention in comments + +--- + +### 10.3 search_indices + +**Purpose**: Full-text search index for document content + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | ------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique index ID | +| entity_type | VARCHAR(50) | NOT NULL | Entity type (e.g., 'correspondence', 'rfa') | +| entity_id | INT | NOT NULL | Entity primary ID | +| content | TEXT | NOT NULL | Searchable text content | +| indexed_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last indexing timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- INDEX (entity_type, entity_id) +- INDEX (indexed_at) + +**Business Rules**: + +- Automatically populated/updated when documents change +- Content includes: title, description, document number, metadata +- May include OCR text from PDF attachments (future enhancement) +- Used by advanced search functionality +- Periodic re-indexing to catch missed updates +- Supports Boolean operators, phrase searching + +**Search Features**: + +- Natural language queries +- Wildcard support (\*, ?) +- Boolean operators (AND, OR, NOT) +- Phrase matching with quotes +- Result ranking by relevance + +--- + +### 10.4 backup_logs + +**Purpose**: Log table tracking database and file backup operations + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | --------------------------- | ---------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique backup log ID | +| backup_type | ENUM | NOT NULL | Type: DATABASE, FILES, FULL | +| backup_path | VARCHAR(500) | NOT NULL | Path to backup file/directory | +| file_size | BIGINT | NULL | Backup file size in bytes | +| status | ENUM | NOT NULL | Status: STARTED, COMPLETED, FAILED | +| started_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Backup start timestamp | +| completed_at | TIMESTAMP | NULL | Backup completion timestamp | +| error_message | TEXT | NULL | Error details if failed | + +**Indexes**: + +- PRIMARY KEY (id) +- INDEX (backup_type) +- INDEX (status) +- INDEX (started_at) +- INDEX (completed_at) + +**Business Rules**: + +- DATABASE: MariaDB dump of database schema and data +- FILES: Backup of attachment files from QNAP storage +- FULL: Complete system backup (database + files) +- Triggered by n8n cron jobs +- Backup retention policy defined in backup strategy +- Failed backups trigger alert notifications +- completed_at - started_at = backup duration + +**Monitoring**: + +- Alert if no successful backup in 24 hours +- Track backup size trends over time +- Verify backup integrity with test restores + +--- + +## **11. 📊 Views & Procedures (วิว และ โปรซีเดอร์)** + +### 11.1 v_current_correspondences + +**Purpose**: View showing current revision of all non-RFA correspondences + +**Columns**: + +- correspondence_id, correspondence_number, correspondence_type_id/code/name +- project_id, project_code, project_iname +- organization_id, organization_code, organization_name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, due_date +- correspondence_status_id, correspondence_status_code, correspondence_status_name +- created_by, created_by_username, revision_created_at + +**Filters**: + +- is_current = TRUE (only latest revision) +- correspondence_type NOT IN ('RFA') (excludes RFAs) +- deleted_at IS NULL (excludes soft-deleted records) + +**Business Rules**: + +- Provides flattened view of current correspondence state +- Joins correspondence_revisions with is_current flag +- Used by dashboard, document listing screens +- Excludes RFAs (they have separate view) + +--- + +### 11.2 v_current_rfas + +**Purpose**: View showing current revision of all RFA documents + +**Columns**: + +- rfa_id, rfa_type_id, rfa_type_code, rfa_type_name +- correspondence_id, correspondence_number +- project_id, project_code, project_iname +- organization_id, organization_name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, approved_date +- rfa_status_code_id, rfa_status_code, rfa_status_name +- rfa_approve_code_id, rfa_approve_code, rfa_approve_code_name +- created_by, created_by_username, revision_created_at + +**Filters**: + +- is_current = TRUE +- deleted_at IS NULL (both rfas and correspondences) + +**Business Rules**: + +- Specialized view for RFA documents +- Includes RFA-specific fields (approval codes, approved date) +- Joins across rfas → rfa_revisions → correspondences +- Used by RFA management screens + +--- + +### 11.3 v_contract_parties_all + +**Purpose**: View showing all organization relationships across contracts and projects + +**Columns**: + +- contract_id, contract_code, contract_name +- project_id, project_code, project_iname +- organization_id, organization_code, organization_name +- role_in_contract + +**Business Rules**: + +- Joins contracts → projects → contract_organizations → organizations +- Shows only active contracts (is_active = TRUE) +- Used for permission checks and document routing +- Supports multi-organization projects/contracts + +--- + +### 11.4 v_user_tasks + +**Purpose**: View showing pending tasks assigned to users (action items) + +**Columns**: + +- routing_id, circulation_id, circulation_no, circulation_subject +- correspondence_id, correspondence_number +- project_id, project_code, project_name +- user_id, username, first_name, last_name +- organization_id, organization_name +- step_number, task_status, comments +- completed_at, assigned_at, circulation_created_at + +**Filters**: + +- status IN ('PENDING', 'IN_PROGRESS') +- assigned_to IS NOT NULL + +**Business Rules**: + +- Shows circulation routings requiring user action +- Used for "My Tasks" / "Inbox" functionality +- Excludes completed/cancelled tasks +- Ordered by creation date (oldest first) + +--- + +### 11.5 v_audit_log_details + +**Purpose**: View enriching audit logs with user information + +**Columns**: + +- audit_id, user_id, username, email, first_name, last_name +- action, entity_type, entity_id +- details_json, ip_address, user_agent +- created_at + +**Business Rules**: + +- Joins audit_logs with users table +- Used for audit trail reports +- Includes user details even if user later deleted (LEFT JOIN) + +--- + +### 11.6 v_user_all_permissions + +**Purpose**: View showing all effective permissions for users across all scopes + +**Columns**: + +- user_id, role_id, role_name +- permission_id, permission_name +- module, scope_level +- organization_id, project_id, contract_id +- permission_scope (GLOBAL, ORGANIZATION, PROJECT, CONTRACT) + +**Business Rules**: + +- UNION of permissions from Global, Organization, Project, and Contract scopes +- Used for authorization checks +- Considers role-permission mappings at all levels +- Only shows active permissions (is_active = 1) +- One row per user-permission-scope combination + +**Usage Example**: + +```sql +SELECT permission_name +FROM v_user_all_permissions +WHERE user_id = ? + AND project_id = ? + AND permission_name = 'document.edit'; +``` + +--- + +### 11.7 v_documents_with_attachments + +**Purpose**: View showing all documents and their attachment counts + +**Columns**: + +- document_type (CORRESPONDENCE, CIRCULATION, SHOP_DRAWING, CONTRACT_DRAWING) +- document_id, document_number +- project_id, project_code, project_name +- attachment_count, latest_attachment_date + +**Business Rules**: + +- UNION of all document types with attachments +- Used for document listing with file indicators +- Helps identify documents missing attachments +- Aggregates count per document + +--- + +### 11.8 v_document_statistics + +**Purpose**: View providing aggregated document statistics by project, type, and status + +**Columns**: + +- project_id, project_code, project_name +- correspondence_type_id, correspondence_type_code, correspondence_type_name +- status_id, status_code, status_name +- document_count, revision_count + +**Business Rules**: + +- CROSS JOIN creates all possible combinations +- LEFT JOIN shows zeros for combinations with no documents +- Only includes active projects, types, and statuses +- Used for dashboard charts and reports +- Groups by project → type → status + +--- + +### 11.9 sp_get_next_document_number + +**Purpose**: Stored procedure to safely generate next sequential document number + +**Parameters**: + +- IN p_project_id INT +- IN p_originator_organization_id INT +- IN p_correspondence_type_id INT +- IN p_current_year INT +- OUT p_next_number INT + +**Logic**: + +1. Start transaction +2. SELECT last_number FOR UPDATE (locks row) +3. If record doesn't exist, INSERT with last_number = 1 +4. Else UPDATE last_number = last_number + 1 +5. Return new number via OUT parameter +6. COMMIT transaction + +**Business Rules**: + +- Thread-safe counter increment +- FOR UPDATE lock prevents race conditions +- Handles first-time counter initialization +- Rolls back on any error +- Must be called within document creation transaction +- Used in conjunction with document_number_formats template + +**Usage Example**: + +```sql +CALL sp_get_next_document_number( + 1, -- project_id + 10, -- organization_id + 1, -- type_id (RFA) + 2568, -- Buddhist year + @next_number +); +-- @next_number now contains next sequence +``` + +--- + +## Database Indexes Summary + +### Performance Optimization Indexes + +**Primary Indexes** (automatic with PRIMARY KEY): + +- All tables have PRIMARY KEY with AUTO_INCREMENT + +**Foreign Key Indexes** (automatic with FOREIGN KEY): + +- All FK relationships automatically indexed + +**Additional Performance Indexes**: + +1. **Correspondence Tables**: + + - `idx_correspondences_type_project` on (correspondence_type_id, project_id) + - `idx_corr_revisions_current_status` on (is_current, correspondence_status_id) + - `idx_corr_revisions_correspondence_current` on (correspondence_id, is_current) + - `idx_correspondences_project_type` on (project_id, correspondence_type_id) + +2. **RFA Tables**: + + - `idx_rfa_revisions_current_status` on (is_current, rfa_status_code_id) + - `idx_rfa_revisions_rfa_current` on (rfa_id, is_current) + +3. **Circulation Tables**: + + - `idx_circulation_routings_status_assigned` on (status, assigned_to) + - `idx_circulation_routings_circulation_status` on (circulation_id, status) + +4. **Document Numbering**: + + - `idx_doc_counter_composite` on (project_id, originator_organization_id, correspondence_type_id, current_year) + +5. **Audit & Notifications**: + + - `idx_audit_logs_reporting` on (created_at, entity_type, action) + - `idx_notifications_user_unread` on (user_id, is_read, created_at) + +--- + +## Data Integrity Constraints + +### Foreign Key Constraints + +**Cascade Delete**: + +- Parent-child relationships where child should be deleted with parent +- Examples: correspondence_revisions, shop_drawing_revisions, project/contract relationships + +**Restrict Delete**: + +- Prevents deletion if references exist +- Examples: correspondence_types, rfa_status_codes, organizations (when referenced) + +**Set NULL**: + +- Preserves record but removes reference +- Examples: originator_id, created_by, updated_by + +### Unique Constraints + +1. **Globally Unique**: + + - usernames, emails + - shop_drawing.drawing_number + +2. **Unique Within Scope**: + + - (project_id, correspondence_number) + - (project_id, condwg_no) + - (correspondence_id, revision_number) + - (rfa_id, revision_number) + +3. **Composite Unique**: + - (correspondence_id, is_current) - ensures only one current revision + - (project_id, correspondence_type_id) - in document_number_formats + +### Check Constraints + +1. **user_assignments.chk_scope**: + - Ensures only one scope field (organization_id, project_id, contract_id) is NOT NULL + - OR all are NULL for Global scope + +### Business Rule Constraints + +1. **Soft Delete Pattern**: + + - deleted_at timestamp instead of hard delete + - Preserves audit trail and relationships + - Applied to: correspondences, rfas, shop_drawings, contract_drawings + +2. **Current Revision Pattern**: + + - is_current flag with UNIQUE constraint + - Ensures only one current revision per document + +3. **Sequential Numbering**: + - revision_number starts at 0, increments by 1 + - Enforced by application logic + stored procedure + +--- + +## Security & Permissions Model + +### Access Control Hierarchy + +```tree +Global Scope (Superadmin) + └── Organization Scope (Org Admin, Document Control) + └── Project Scope (Project Manager) + └── Contract Scope (Contract Admin) +``` + +### Permission Inheritance + +- Users can have multiple role assignments at different scopes +- More specific scopes inherit access from broader scopes +- Permission checks evaluate all applicable scopes +- View v_user_all_permissions aggregates effective permissions + +### Role-Based Access Control (RBAC) + +**7 Predefined Roles**: + +1. Superadmin (Global) - Full system access +2. Org Admin (Organization) - Organization management +3. Document Control (Organization) - Document lifecycle + admin powers +4. Editor (Organization) - Document CRUD +5. Viewer (Organization) - Read-only +6. Project Manager (Project) - Project + document management +7. Contract Admin (Contract) - Contract-specific management + +### Permission Categories (49 total) + +1. **System Management** (1): Full system control +2. **Organization Management** (4): CRUD on organizations +3. **Project Management** (8): CRUD + member/contract management +4. **Role & Permission Management** (4): RBAC administration +5. **Master Data Management** (4): Document types, categories, tags +6. **User Management** (5): User CRUD + organization assignment +7. **Contract Management** (2): Contract administration +8. **Document Management** (16): Full document lifecycle +9. **Workflow Management** (3): Approval workflow control +10. **Search & Reporting** (2): Advanced search, report generation + +--- + +## Data Migration & Seeding + +### Pre-populated Master Data + +1. **organization_roles**: Not used in current implementation +2. **organizations**: 15 organizations (Owner, Consultants, Contractors, Third parties) +3. **projects**: 5 projects (LCBP3 + 4 sub-contracts) +4. **contracts**: 7 contracts +5. **users**: 3 initial users (superadmin, editor01, viewer01) +6. **roles**: 7 predefined roles +7. **permissions**: 49 system permissions +8. **role_permissions**: Complete permission mappings +9. **project_organizations**: Project-organization relationships +10. **contract_organizations**: Contract-organization-role relationships +11. **correspondence_types**: 10 types +12. **correspondence_status**: 23 status codes +13. **rfa_types**: 11 types +14. **rfa_status_codes**: 7 statuses +15. **rfa_approve_codes**: 8 approval codes +16. **circulation_status_codes**: 4 statuses + +### Initial Passwords + +All seed users have password: `password123` + +- Hashed as: `$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq` +- **Must be changed on first login in production** + +--- + +## Backup & Recovery Strategy + +### Database Backup + +**Strategy**: + +- Daily full database backups +- Hourly incremental backups (transaction logs) +- Retention: 30 days online, 7 years archived + +**Backup Method**: + +- MariaDB mysqldump for logical backups +- MariaDB Backup (Mariabackup) for physical backups +- Automated via n8n cron workflows + +### File Backup + +**Strategy**: + +- Daily backup of /share/dms-data/ directory +- QNAP snapshot every 4 hours +- Offsite replication to secondary NAS + +**File Organization**: + +```tree +/share/dms-data/ + ├── attachments/ + │ ├── 2025/ + │ │ ├── 01/ + │ │ │ └── {uuid}-{filename} + │ │ └── 02/ + │ └── 2024/ + └── temp/ +``` + +### Recovery Procedures + +1. **Point-in-Time Recovery**: Using transaction logs +2. **Full Restore**: From latest full backup +3. **Selective Restore**: Individual tables or records +4. **File Recovery**: From QNAP snapshots or backup + +--- + +## Performance Optimization + +### Query Optimization + +1. **Use Indexed Columns**: WHERE, JOIN, ORDER BY clauses +2. **Avoid SELECT**: Specify needed columns +3. **Use Views**: For complex, frequently-used queries +4. **Limit Result Sets**: Use LIMIT and pagination +5. **Analyze Slow Queries**: Enable slow query log + +### Index Maintenance + +```sql +-- Check index usage +SELECT * FROM information_schema.STATISTICS +WHERE TABLE_SCHEMA = 'lcbp3'; + +-- Optimize tables +OPTIMIZE TABLE correspondences; + +-- Analyze tables +ANALYZE TABLE correspondences; +``` + +### Connection Pooling + +- Backend uses connection pool (NestJS TypeORM) +- Min pool size: 5 +- Max pool size: 20 +- Idle timeout: 10 minutes + +--- + +## Data Validation Rules + +### Required Fields Validation + +**At Application Level**: + +- Email format validation +- Date range validation (start_date < end_date) +- File size limits (5MB per attachment) +- File type restrictions (PDF, DWG, DOC, XLS, images) + +**At Database Level**: + +- NOT NULL constraints +- UNIQUE constraints +- Foreign key constraints +- Check constraints (user_assignments scope) + +### Business Logic Validation + +1. **Document Workflow**: + + - Cannot edit submitted documents (unless Document Control) + - Cannot skip workflow steps (unless forced) + - Must provide approval comments + +2. **User Management**: + + - Cannot delete users with active assignments + - Cannot deactivate own account + - Must have valid organization for non-Global roles + +3. **File Management**: + - Original filename preserved for audit + - Unique stored filename prevents conflicts + - File path must exist and be accessible + +--- + +## Change Log & Versioning + +### Database Version: v1.4.0 + +**Changes from v1.3.0**: + +- Added comprehensive RBAC system (roles, permissions, user_assignments) +- Refactored organization-project-contract relationships +- Added junction tables for M:N relationships +- Implemented soft delete pattern +- Added full-text search support +- Enhanced audit logging with JSON details +- Added circulation workflow templates +- Improved document numbering with stored procedure +- Added comprehensive views for common queries +- Optimized indexes for performance + +**Migration Path**: + +- v1.3.0 → v1.4.0: Run migration script (not provided in this excerpt) +- Backup database before migration +- Test migration on staging environment first + +--- + +## Technical Specifications + +### Database Configuration + +**MariaDB Server**: + +- Version: 10.11 +- Character Set: utf8mb4 +- Collation: utf8mb4_general_ci +- Time Zone: +07:00 (Bangkok/Asia) +- SQL Mode: STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION + +**Connection Settings**: + +- Host: Container on QNAP TS-473A +- Port: 3306 (default) +- Max Connections: 100 +- Max Packet Size: 64MB + +### Table Engine + +**InnoDB Features Used**: + +- ACID compliance +- Foreign key constraints +- Row-level locking +- Crash recovery +- Transaction support + +### Storage Requirements + +**Estimated Initial Size**: + +- Database: ~50 MB (with seed data) +- Indexes: ~20 MB +- Total: ~70 MB + +**Growth Estimates**: + +- 1,000 documents/month: +100 MB/month (database) +- 10 attachments/document @ 2MB avg: +20 GB/month (files) +- Plan for 1TB+ storage within first year + +--- + +## Application Integration + +### Backend (NestJS) + +**TypeORM Entities**: + +- One entity class per table +- Decorators for columns, relationships +- DTOs for data transfer +- Repositories for data access + +**Key Modules**: + +- AuthModule: Authentication, authorization +- DocumentsModule: Correspondence, RFA management +- DrawingsModule: Shop drawing, contract drawing +- WorkflowModule: Circulation, approval workflows +- FilesModule: Attachment management +- UsersModule: User, role, permission management + +### Frontend (Next.js) + +**Key Features**: + +- Document listing/search +- Document creation wizards +- Workflow approval interface +- File upload/download +- User management console +- Dashboard analytics + +### Integration Points + +1. **Document Numbering**: + + - Call sp_get_next_document_number + - Format with template from document_number_formats + - Store in correspondences.correspondence_number + +2. **File Upload**: + + - Upload to QNAP /share/dms-data/ + - Create attachment record + - Link via junction table + +3. **Workflow Execution**: + + - Check rfa_workflow_templates + - Create rfa_workflows records + - Update status as steps complete + - Send notifications + +4. **Permission Checks**: + - Query v_user_all_permissions + - Cache results per session + - Re-check on sensitive operations + +--- + +## Monitoring & Maintenance + +### Health Checks + +```sql +-- Database size +SELECT + table_schema, + SUM(data_length + index_length) / 1024 / 1024 AS size_mb +FROM information_schema.TABLES +WHERE table_schema = 'dms_db' +GROUP BY table_schema; + +-- Table sizes +SELECT + table_name, + (data_length + index_length) / 1024 / 1024 AS size_mb, + table_rows +FROM information_schema.TABLES +WHERE table_schema = 'dms_db' +ORDER BY (data_length + index_length) DESC; + +-- Active connections +SHOW PROCESSLIST; + +-- Lock wait statistics +SELECT * FROM information_schema.INNODB_LOCK_WAITS; +``` + +### Scheduled Maintenance + +**Daily**: + +- Full database backup +- Check backup log for failures +- Monitor disk space + +**Weekly**: + +- OPTIMIZE tables +- ANALYZE tables +- Review slow query log +- Check for deadlocks + +**Monthly**: + +- Review audit logs +- Clean up old notifications (30+ days) +- Archive old audit logs (7+ years) +- Verify backup integrity (test restore) + +**Quarterly**: + +- Review and optimize indexes +- Update database statistics +- Capacity planning review + +--- + +## Glossary + +**Terms**: + +- **Correspondence**: Any formal document exchanged between parties +- **RFA**: Request for Approval - formal submittal for review/approval +- **Circulation**: Internal document routing workflow +- **Transmittal**: Cover sheet for forwarding multiple documents +- **Shop Drawing**: Detailed construction drawing from contractor +- **Contract Drawing**: Baseline drawing from contract specifications +- **Revision**: Version of a document +- **Originator**: Organization that creates/sends a document +- **Recipient**: Organization that receives a document (TO or CC) +- **Scope**: Level of permission application (Global, Organization, Project, Contract) + +**Acronyms**: + +- **DMS**: Document Management System +- **LCBP3**: Laem Chabang Port Phase 3 +- **RBAC**: Role-Based Access Control +- **RFA**: Request for Approval +- **RFI**: Request for Information +- **CSC**: Construction Supervision Consultant +- **TO**: Primary recipient (action required) +- **CC**: Carbon copy (for information) +- **QNAP**: Network Attached Storage device manufacturer + +--- + +**Document Control**: + +- Document: Data Dictionary - DMS v1.4.0 +- Version: 1.0 +- Date: 2025-01-XX +- Author: System Architecture Team +- Status: FINAL +- Classification: Internal Technical Documentation + +--- + +`End of Data Dictionary v1.4.2 (ฉบับปรับปรุง)` + +จากการวิเคราะห์เอกสาร **LCBP3-DMS_V1_4_1_Data_Dictionary.md** เปรียบเทียบกับ **Requirements v1.4.2** และ **Backend Plan v1.4.2** พบว่ามีตารางที่ต้อง **ปรับแก้ (Modify)** และ **เพิ่มใหม่ (Add)** เพื่อให้รองรับฟีเจอร์สำคัญ เช่น Optimistic Locking, Two-Phase File Storage, JSON Schema Management และ Unified Workflow ดังนี้ครับ + +--- + +## **1. ตารางที่ต้องปรับแก้ (Modified Tables)** + +### **1.1 ตาราง `document_number_counters` (ระบบเลขที่เอกสาร)** + +**เหตุผล:** รองรับ **Double-Lock Mechanism** และ **Optimistic Locking** เพื่อป้องกัน Race Condition 100% ตาม Backend Plan T2.3 และ Requirements 3.10.5 + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **version** | **ADD** | `INT DEFAULT 0` | ใช้สำหรับ Optimistic Locking (ตรวจสอบค่าก่อน Update) | +| last_number | MODIFY | `INT` | ต้องรองรับการทำงานร่วมกับ `@VersionColumn()` ของ TypeORM | + +--- + +### **1.2 ตาราง `attachments` (ระบบจัดการไฟล์)** + +**เหตุผล:** รองรับ **Two-Phase Storage Strategy** (Temp -> Permanent) เพื่อจัดการไฟล์ขยะ (Orphan Files) และความปลอดภัย ตาม Backend Plan T2.2 และ Requirements 3.9.1 + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **is_temporary** | **ADD** | `BOOLEAN DEFAULT TRUE` | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) | +| **temp_id** | **ADD** | `VARCHAR(100)` | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) | +| **expires_at** | **ADD** | `DATETIME` | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) | +| **checksum** | **ADD** | `VARCHAR(64)` | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] | + +--- + +### **1.3 ตาราง `correspondence_revisions` (และตารางที่มี JSON Details)** + +**เหตุผล:** เพิ่มประสิทธิภาพการค้นหาภายใน JSON Field ด้วย **Virtual Columns** ตาม Backend Plan T2.1 และ Requirements 3.11.3 + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **v_ref_project_id** | **ADD** | `INT GENERATED ALWAYS AS (...) VIRTUAL` | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index | +| **v_ref_type** | **ADD** | `VARCHAR(50) GENERATED ALWAYS AS (...) VIRTUAL` | Virtual Column ดึง Type จาก JSON details | +| **INDEX** | **ADD** | - | เพิ่ม Index บน Virtual Columns เหล่านี้ | + +--- + +### **1.4 ตาราง `audit_logs` (ระบบตรวจสอบ)** + +**เหตุผล:** รองรับการ Tracing และ Partitioning ตาม Requirements 6.1 และ 6.2 + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **request_id** | **ADD** | `VARCHAR(100)` | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs | +| **severity** | **ADD** | `ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL')` | ระดับความรุนแรงของเหตุการณ์ | +| **PARTITIONING** | **ALTER** | - | ปรับตารางให้รองรับ Partition By Range (Year) [Backend Plan T6.5] | + +--- + +### **1.5 ตาราง `rfa_workflow_templates` และ `correspondence_routing_templates`** + +**เหตุผล:** รองรับ **Unified Workflow Engine** ที่ยืดหยุ่นขึ้น ตาม Backend Plan T3.1 + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **workflow_config** | **ADD** | `JSON` | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ | + +--- + +### **1.6 ตาราง `rfa_workflows` และ `correspondence_routings`** + +**เหตุผล:** เก็บ Context ของ State Machine สำหรับ Unified Workflow Engine + +| Column Name | Change Type | Data Type | Description | +| :--- | :--- | :--- | :--- | +| **state_context** | **ADD** | `JSON` | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) | + +--- + +## **2. ตารางที่ต้องเพิ่มใหม่ (New Tables)** + +### **2.1 ตาราง `json_schemas` (New)** + +**เหตุผล:** รองรับ **Centralized JSON Schema Registry** เพื่อ Validate ข้อมูล JSON Details ของเอกสารแต่ละประเภท ตาม Requirements 6.11.1 และ Backend Plan T2.5.1 + +| Column Name | Data Type | Constraints | Description | +| :--- | :--- | :--- | :--- | +| **id** | `INT` | PK, AI | Unique Identifier | +| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`) | +| **version** | `INT` | NOT NULL | เวอร์ชันของ Schema | +| **schema_definition** | `JSON` | NOT NULL | โครงสร้าง JSON Schema (Standard JSON Schema format) | +| **is_active** | `BOOLEAN` | DEFAULT TRUE | สถานะการใช้งาน | +| **created_at** | `TIMESTAMP` | | วันที่สร้าง | + +--- + +### **2.2 ตาราง `user_preferences` (New)** + +**เหตุผล:** แยกข้อมูลการตั้งค่าส่วนตัว (เช่น Notification Settings) ออกจากตาราง Users เพื่อความยืดหยุ่น ตาม Requirements 5.5 และ 6.8.3 + +| Column Name | Data Type | Constraints | Description | +| :--- | :--- | :--- | :--- | +| **user_id** | `INT` | PK, FK | อ้างอิงตาราง users | +| **notify_email** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทางอีเมล | +| **notify_line** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทาง LINE | +| **digest_mode** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนแบบรวม (Digest) แทน Real-time | +| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark) | +| **updated_at** | `TIMESTAMP` | | วันที่แก้ไขล่าสุด | + +--- + +## **สรุปภาพรวมการเปลี่ยนแปลง (Schema Evolution)** + +1. **Security & Integrity:** เพิ่ม `version` (Optimistic Lock), `checksum`, `is_temporary` (Secure File Upload). +2. **Performance:** เพิ่ม `Virtual Columns` และ `Partitioning` บน Audit Logs. +3. **Flexibility:** เพิ่มตาราง `json_schemas` และคอลัมน์ `JSON` สำหรับ Workflow Engine เพื่อลดการผูกติดกับ Logic แบบ Hard-code ใน DB. + +แนะนำให้ทีม Backend (NestJS) สร้าง **Migration Scripts** (TypeORM Migrations) ตามรายการข้างต้น เพื่ออัปเกรดจาก v1.4.1 เป็น v1.4.2 ครับ diff --git a/01_lcbp3_v1_4_0.sql b/docs/01_lcbp3_v1_4_0.sql similarity index 98% rename from 01_lcbp3_v1_4_0.sql rename to docs/01_lcbp3_v1_4_0.sql index 796f130..48e7802 100644 --- a/01_lcbp3_v1_4_0.sql +++ b/docs/01_lcbp3_v1_4_0.sql @@ -1,1878 +1,1878 @@ --- ========================================================== --- DMS v1.4.0 Document Management System Database --- Deploy Script Schema --- Server: Container Station on QNAPQNAP TS-473A --- Database service: MariaDB 10.11 --- database web ui: phpmyadmin 5-apache --- database deelopment ui: DBeaver --- backend sevice: NestJS --- frontend sevice: next.js --- reverse proxy: jc21/nginx-proxy-manager:latest --- cron service: n8n --- DMS v1.4.0 Improvements --- Update: first revise fron v1.3.0 (GLM-4.6 & Gemini) --- ========================================================== - -SET NAMES utf8mb4; -SET time_zone = '+07:00'; --- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด -SET FOREIGN_KEY_CHECKS=0; - -DROP VIEW IF EXISTS v_document_statistics; -DROP VIEW IF EXISTS v_documents_with_attachments; -DROP VIEW IF EXISTS v_user_all_permissions; -DROP VIEW IF EXISTS v_audit_log_details; -DROP VIEW IF EXISTS v_user_tasks; -DROP VIEW IF EXISTS v_contract_parties_all; -DROP VIEW IF EXISTS v_current_rfas; -DROP VIEW IF EXISTS v_current_correspondences; - -DROP PROCEDURE IF EXISTS sp_get_next_document_number; --- 🗑️ Drop all tables in reverse dependency order --- ส่วนที่ 1: ตาราง System & Utility (ไม่ค่อยมีการอ้างอิง หรือเป็นตารางสุดท้ายในสายงาน) -DROP TABLE IF EXISTS backup_logs; -DROP TABLE IF EXISTS search_indices; -DROP TABLE IF EXISTS notifications; -DROP TABLE IF EXISTS audit_logs; - --- ส่วนที่ 2: ตารางที่เชื่อมโยงกับเอกสารและข้อมูลหลัก (Junction Tables) -DROP TABLE IF EXISTS correspondence_tags; -DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; -DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; - --- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments & Their Junctions) -DROP TABLE IF EXISTS contract_drawing_attachments; -DROP TABLE IF EXISTS circulation_attachments; -DROP TABLE IF EXISTS shop_drawing_revision_attachments; -DROP TABLE IF EXISTS correspondence_attachments; -DROP TABLE IF EXISTS attachments; - --- ส่วนที่ 4: ตารางเกี่ยวกับ Workflow และ Template -DROP TABLE IF EXISTS circulation_routings; -DROP TABLE IF EXISTS circulation_template_assignees; -DROP TABLE IF EXISTS circulation_templates; -DROP TABLE IF EXISTS rfa_workflows; -DROP TABLE IF EXISTS rfa_workflow_template_steps; -DROP TABLE IF EXISTS rfa_workflow_templates; --- add -DROP TABLE IF EXISTS correspondence_routings; -DROP TABLE IF EXISTS correspondence_routing_template_steps; -DROP TABLE IF EXISTS correspondence_status_transitions; -DROP TABLE IF EXISTS correspondence_routing_templates; - --- ส่วนที่ 5: ตาราง Mapping หลัก (Main Mapping Tables) -DROP TABLE IF EXISTS role_permissions; -DROP TABLE IF EXISTS user_assignments; -DROP TABLE IF EXISTS contract_organizations; -DROP TABLE IF EXISTS project_organizations; - --- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Detail/Item Tables) -DROP TABLE IF EXISTS transmittal_items; -DROP TABLE IF EXISTS shop_drawing_revisions; -DROP TABLE IF EXISTS rfa_items; -DROP TABLE IF EXISTS rfa_revisions; -DROP TABLE IF EXISTS correspondence_references; -DROP TABLE IF EXISTS correspondence_recipients; -DROP TABLE IF EXISTS correspondence_revisions; - --- ส่วนที่ 7: ตารางเอกสารหลัก (Core Document Tables) -DROP TABLE IF EXISTS circulations; -DROP TABLE IF EXISTS transmittals; -DROP TABLE IF EXISTS contract_drawings; -DROP TABLE IF EXISTS shop_drawings; -DROP TABLE IF EXISTS rfas; -DROP TABLE IF EXISTS correspondences; - --- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Categories & Master Data) -DROP TABLE IF EXISTS shop_drawing_sub_categories; -DROP TABLE IF EXISTS shop_drawing_main_categories; -DROP TABLE IF EXISTS contract_drawing_sub_cats; -DROP TABLE IF EXISTS contract_drawing_cats; -DROP TABLE IF EXISTS contract_drawing_volumes; -DROP TABLE IF EXISTS circulation_status_codes; -DROP TABLE IF EXISTS rfa_approve_codes; -DROP TABLE IF EXISTS rfa_status_codes; -DROP TABLE IF EXISTS rfa_types; -DROP TABLE IF EXISTS correspondence_status; -DROP TABLE IF EXISTS correspondence_types; -DROP TABLE IF EXISTS document_number_counters; -DROP TABLE IF EXISTS document_number_formats; -DROP TABLE IF EXISTS tags; - --- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างหลัก (Users, Roles & Core Structure) -DROP TABLE IF EXISTS organization_roles; -DROP TABLE IF EXISTS roles; -DROP TABLE IF EXISTS permissions; -DROP TABLE IF EXISTS contracts; -DROP TABLE IF EXISTS projects; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS organizations; - --- ===================================================== --- 1. 🏢 Core & Master Data (องค์กร, โครงการ, สัญญา) --- ===================================================== - --- ตาราง Master เก็บประเภทบทบาทขององค์กร -CREATE TABLE organization_roles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'ชื่อบทบาท (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บประเภทบทบาทขององค์กร'; - --- ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ -CREATE TABLE organizations ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสองค์กร', - organization_name VARCHAR(255) NOT NULL COMMENT 'ชื่อองค์กร', - -- role_id INT COMMENT 'บทบาทขององค์กร', - is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' - -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ'; --- Seed organization -INSERT INTO organizations (id, organization_code, organization_name) VALUES -(1, 'กทท.', 'การท่าเรือแห่งประเทศไทย'), -(10, 'สคฉ.3', 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3'), -(11, 'สคฉ.3-01', 'ตรวจรับพัสดุ ที่ปรึกษาควบคุมงาน'), -(12, 'สคฉ.3-02', 'ตรวจรับพัสดุ งานทางทะเล'), -(13, 'สคฉ.3-03', 'ตรวจรับพัสดุ อาคารและระบบสาธารณูปโภค'), -(14, 'สคฉ.3-04', 'ตรวจรับพัสดุ ตรวจสอบผลกระทบสิ่งแวดล้อม'), -(15, 'สคฉ.3-05', 'ตรวจรับพัสดุ เยียวยาการประมง'), -(16, 'สคฉ.3-06', 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 3'), -(17, 'สคฉ.3-07', 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 4'), -(18, 'สคฉ.3-xx', 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4'), -(21, 'TEAM', 'Designer Consulting Ltd.'), -(22, 'คคง.', 'Construction Supervision Ltd.'), -(41, 'ผรม.1', 'Contractor งานทางทะเล'), -(42, 'ผรม.2', 'Contractor อาคารและระบบ'), -(43, 'ผรม.3', 'Contractor #3 Ltd.'), -(44, 'ผรม.4', 'Contractor #4 Ltd.'), -(31, 'EN', 'Third Party Environment'), -(32, 'CAR', 'Third Party Fishery Care'); - --- ตาราง Master เก็บข้อมูลโครงการ -CREATE TABLE projects ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสโครงการ', - project_name VARCHAR(255) NOT NULL COMMENT 'ชื่อโครงการ', - -- parent_project_id INT COMMENT 'รหัสโครงการหลัก (ถ้ามี)', - -- contractor_organization_id INT COMMENT 'รหัสองค์กรผู้รับเหมา (ถ้ามี)', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' - -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, - -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลโครงการ'; -INSERT INTO projects (project_code, project_name) VALUES - ('LCBP3','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)'), - ('LCBP3C1','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล'), - ('LCBP3C2','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค'), - ('LCBP3C3','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง'), - ('LCBP3C4','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง'); - --- ตาราง Master เก็บข้อมูลสัญญา -CREATE TABLE contracts ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL, - contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสัญญา', - contract_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสัญญา', - description TEXT COMMENT 'คำอธิบายสัญญา', - start_date DATE COMMENT 'วันที่เริ่มสัญญา', - end_date DATE COMMENT 'วันที่สิ้นสุดสัญญา', - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลสัญญา'; --- ใช้ Subquery เพื่อดึง project_id มาเชื่อมโยง ทำให้ไม่ต้องมานั่งจัดการ ID ด้วยตัวเอง -INSERT INTO contracts (contract_code, contract_name, project_id, is_active) VALUES -('DSLCBP3', 'งานจ้างที่ปรีกษาออกแบบ โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE), -('PSLCBP3', 'งานจ้างที่ปรีกษาควบคุมงาน โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE), -('LCBP3-C1', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล', (SELECT id FROM projects WHERE project_code = 'LCBP3C1'), TRUE), -('LCBP3-C2', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค', (SELECT id FROM projects WHERE project_code = 'LCBP3C2'), TRUE), -('LCBP3-C3', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง', (SELECT id FROM projects WHERE project_code = 'LCBP3C3'), TRUE), -('LCBP3-C4', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง', (SELECT id FROM projects WHERE project_code = 'LCBP3C4'), TRUE), -('ENLCBP3', 'งานจ้างเหมาตรวจสอบผลกระทบสิ่งแวดล้อมนะหว่างงานก่อสร้างโครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE); --- ===================================================== --- 2. 👥 Users & RBAC (ผู้ใช้, สิทธิ์, บทบาท) --- ===================================================== - --- ตาราง Master เก็บข้อมูลผู้ใช้งาน (User) -CREATE TABLE users ( - user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - username VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อผู้ใช้งาน', - password_hash VARCHAR(255) NOT NULL COMMENT 'รหัสผ่าน (Hashed)', - first_name VARCHAR(50) COMMENT 'ชื่อจริง', - last_name VARCHAR(50) COMMENT 'นามสกุล', - email VARCHAR(100) NOT NULL UNIQUE COMMENT 'อีเมล', - line_id VARCHAR(100) COMMENT 'LINE ID', - primary_organization_id INT COMMENT 'สังกัดองค์กร', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - failed_attempts INT DEFAULT 0 COMMENT 'จำนวนครั้งที่ล็อกอินล้มเหลว', - locked_until DATETIME COMMENT 'ล็อกอินไม่ได้จนถึงเวลา', - last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)'; --- Initial SUPER_ADMIN user -INSERT INTO users (username, password_hash, email, is_active) -VALUES ('superadmin', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'superadmin@example.com', 1) -ON DUPLICATE KEY UPDATE email=VALUES(email), is_active=VALUES(is_active); - --- Create editor01 user -INSERT IGNORE INTO users (username, password_hash, email, is_active) -VALUES ('editor01', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'editor01@example.com', 1); - --- Create viewer01 user (password hash placeholder, must change later) -INSERT IGNORE INTO users (username, password_hash, email, is_active) -VALUES ('viewer01', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'viewer01@example.com', 1); - --- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ -CREATE TABLE roles ( - role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', - role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท', - scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, -- ขอบเขตของบทบาท (จากข้อ 4.3) - description TEXT COMMENT 'คำอธิบายบทบาท', - is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = บทบาทของระบบ ลบไม่ได้)' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ'; --- ========================================================== --- Seed Roles (บทบาทพื้นฐาน 5 บทบาท ตาม Req 4.3) --- ========================================================== --- 1. Superadmin (Global) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(1, 'Superadmin', 'Global', 'ผู้ดูแลระบบสูงสุด: สามารถทำทุกอย่างในระบบ, จัดการองค์กร, และจัดการข้อมูลหลักระดับ Global'); - --- 2. Org Admin (Organization) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(2, 'Org Admin', 'Organization', 'ผู้ดูแลองค์กร: จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, และดูรายงานขององค์กร'); - --- 3. Document Control (Organization) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(3, 'Document Control', 'Organization', 'ควบคุมเอกสารขององค์กร: เพิ่ม/แก้ไข/ลบเอกสาร, และกำหนดสิทธิ์เอกสารภายในองค์กร'); - --- 4. Editor (Organization) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(4, 'Editor', 'Organization', 'ผู้แก้ไขเอกสารขององค์กร: เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย'); - --- 5. Viewer (Organization) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(5, 'Viewer', 'Organization', 'ผู้ดูเอกสารขององค์กร: ดูเอกสารที่มีสิทธิ์เข้าถึงเท่านั้น'); - --- 6. Project Manager (Project) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(6, 'Project Manager', 'Project', 'ผู้จัดการโครงการ: จัดการสมาชิกในโครงการ, สร้าง/จัดการสัญญาในโครงการ, และดูรายงานโครงการ'); - --- 7. Contract Admin (Contract) -INSERT INTO roles (role_id, role_name, scope, description) VALUES -(7, 'Contract Admin', 'Contract', 'ผู้ดูแลสัญญา: จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา, และอนุมัติเอกสารในสัญญา'); - --- ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ -CREATE TABLE permissions ( - permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัสสิทธิ์ (เช่น rfas.create, rfas.view)', - description TEXT COMMENT 'คำอธิบายสิทธิ์', - module VARCHAR(50) COMMENT 'โมดูลที่เกี่ยวข้อง', - scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'ระดับขอบเขตของสิทธิ์', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ'; --- ===================================================== --- 2. Seed Permissions (สิทธิ์การใช้งานทั้งหมด) --- สิทธิ์ระดับระบบและการจัดการหลัก (System & Master Data) --- ===================================================== - -INSERT INTO permissions (permission_id, permission_name, description) VALUES -(1, 'system.manage_all', 'ทำทุกอย่างในระบบ (Superadmin Power)'), - --- การจัดการองค์กร -(2, 'organization.create', 'สร้างองค์กรใหม่'), -(3, 'organization.edit', 'แก้ไขข้อมูลองค์กร'), -(4, 'organization.delete', 'ลบองค์กร'), -(5, 'organization.view', 'ดูรายการองค์กร'), - --- การจัดการโครงการ -(6, 'project.create', 'สร้างโครงการใหม่'), -(7, 'project.edit', 'แก้ไขข้อมูลโครงการ'), -(8, 'project.delete', 'ลบโครงการ'), -(9, 'project.view', 'ดูรายการโครงการ'), - --- การจัดการบทบาทและสิทธิ์ (Roles & Permissions) -(10, 'role.create', 'สร้างบทบาท (Role) ใหม่'), -(11, 'role.edit', 'แก้ไขบทบาท (Role)'), -(12, 'role.delete', 'ลบบทบาท (Role)'), -(13, 'permission.assign', 'มอบสิทธิ์ให้กับบทบาท (Role)'), - --- การจัดการข้อมูลหลัก (Master Data) -(14, 'master_data.document_type.manage', 'จัดการประเภทเอกสาร (Document Types)'), -(15, 'master_data.document_status.manage', 'จัดการสถานะเอกสาร (Document Statuses)'), -(16, 'master_data.drawing_category.manage', 'จัดการหมวดหมู่แบบ (Drawing Categories)'), -(17, 'master_data.tag.manage', 'จัดการ Tags'), - --- การจัดการผู้ใช้งาน -(18, 'user.create', 'สร้างผู้ใช้งานใหม่'), -(19, 'user.edit', 'แก้ไขข้อมูลผู้ใช้งาน'), -(20, 'user.delete', 'ลบ/ปิดการใช้งานผู้ใช้'), -(21, 'user.view', 'ดูข้อมูลผู้ใช้งาน'), -(22, 'user.assign_organization', 'มอบผู้ใช้งานให้กับองค์กร'); - - --- ===================================================== --- == 2. สิทธิ์การจัดการโครงการและสัญญา (Project & Contract) == --- ===================================================== - -INSERT INTO permissions (permission_id, permission_name, description) VALUES -(23, 'project.manage_members', 'จัดการสมาชิกในโครงการ (เชิญ/ถอดสมาชิก)'), -(24, 'project.create_contracts', 'สร้างสัญญาในโครงการ'), -(25, 'project.manage_contracts', 'จัดการสัญญาในโครงการ'), -(26, 'project.view_reports', 'ดูรายงานระดับโครงการ'), - -(27, 'contract.manage_members', 'จัดการสมาชิกในสัญญา'), -(28, 'contract.view', 'ดูข้อมูลสัญญา'); - - --- ===================================================== --- == 3. สิทธิ์การจัดการเอกสาร (Document Management) == --- ===================================================== - --- สิทธิ์ทั่วไปสำหรับเอกสารทุกประเภท -INSERT INTO permissions (permission_id, permission_name, description) VALUES -(29, 'document.create_draft', 'สร้างเอกสารในสถานะฉบับร่าง (Draft)'), -(30, 'document.submit', 'ส่งเอกสาร (Submitted)'), -(31, 'document.view', 'ดูเอกสาร'), -(32, 'document.edit', 'แก้ไขเอกสาร (ทั่วไป)'), -(33, 'document.admin_edit', 'แก้ไข/ถอน/ยกเลิกเอกสารที่ส่งแล้ว (Admin Power)'), -(34, 'document.delete', 'ลบเอกสาร'), -(35, 'document.attach', 'จัดการไฟล์แนบ (อัปโหลด/ลบ)'), - --- สิทธิ์เฉพาะสำหรับ Correspondence -(36, 'correspondence.create', 'สร้างเอกสารโต้ตอบ (Correspondence)'), - --- สิทธิ์เฉพาะสำหรับ Request for Approval (RFA) -(37, 'rfa.create', 'สร้างเอกสารขออนุมัติ (RFA)'), -(38, 'rfa.manage_shop_drawings', 'จัดการข้อมูล Shop Drawing และ Contract Drawing ที่เกี่ยวข้อง'), - --- สิทธิ์เฉพาะสำหรับ Shop Drawing & Contract Drawing -(39, 'drawing.create', 'สร้าง/แก้ไขข้อมูลแบบ (Shop/Contract Drawing)'), - --- สิทธิ์เฉพาะสำหรับ Transmittal -(40, 'transmittal.create', 'สร้างเอกสารนำส่ง (Transmittal)'), - --- สิทธิ์เฉพาะสำหรับ Circulation Sheet (ใบเวียน) -(41, 'circulation.create', 'สร้างใบเวียนเอกสาร (Circulation)'), -(42, 'circulation.respond', 'ตอบกลับใบเวียน (Main/Action)'), -(43, 'circulation.acknowledge', 'รับทราบใบเวียน (Information)'), -(44, 'circulation.close', 'ปิดใบเวียน'); - - --- ===================================================== --- == 4. สิทธิ์การจัดการ Workflow == --- ===================================================== - -INSERT INTO permissions (permission_id, permission_name, description) VALUES -(45, 'workflow.action_review', 'ดำเนินการในขั้นตอนปัจจุบัน (เช่น ตรวจสอบแล้ว)'), -(46, 'workflow.force_proceed', 'บังคับไปยังขั้นตอนถัดไป (Document Control Power)'), -(47, 'workflow.revert', 'ย้อนกลับไปยังขั้นตอนก่อนหน้า (Document Control Power)'); - - --- ===================================================== --- == 5. สิทธิ์ด้านการค้นหาและรายงาน (Search & Reporting) == --- ===================================================== - -INSERT INTO permissions (permission_id, permission_name, description) VALUES -(48, 'search.advanced', 'ใช้งานการค้นหาขั้นสูง'), -(49, 'report.generate', 'สร้างรายงานสรุป (รายวัน/สัปดาห์/เดือน/ปี)'); - --- ตารางเชื่อมระหว่าง roles และ permissions (M:N) -CREATE TABLE role_permissions ( - role_id INT COMMENT 'ID ของบทบาท', - permission_id INT COMMENT 'ID ของสิทธิ์', - PRIMARY KEY (role_id, permission_id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, - FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง roles และ permissions (M:N)'; --- ========================================================== --- Seed Role-Permissions Mapping (จับคู่สิทธิ์เริ่มต้น) --- ========================================================== --- Seed data for the 'role_permissions' table --- This table links roles to their specific permissions. - --- NOTE: This assumes the role_id and permission_id from the previous seed data files. --- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. - --- ===================================================== --- == 1. Superadmin (role_id = 1) - Gets ALL permissions == --- ===================================================== - --- Superadmin can do everything. We can dynamically link all permissions to this role. --- This is a robust way to ensure Superadmin always has full power. -INSERT INTO role_permissions (role_id, permission_id) -SELECT 1, permission_id FROM permissions; - --- ===================================================== --- == 2. Org Admin (role_id = 2) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- จัดการผู้ใช้ในองค์กร -(2, 18), -- user.create -(2, 19), -- user.edit -(2, 20), -- user.delete -(2, 21), -- user.view -(2, 22), -- user.assign_organization --- จัดการองค์กร -(2, 3), -- organization.edit -(2, 5), -- organization.view --- จัดการข้อมูลหลักที่อนุญาต (เฉพาะ Tags) -(2, 17), -- master_data.tag.manage --- ดูข้อมูลต่างๆ ในองค์กร -(2, 31), -- document.view -(2, 9), -- project.view -(2, 28), -- contract.view --- การค้นหาและรายงาน -(2, 48), -- search.advanced -(2, 49); -- report.generate - --- ===================================================== --- == 3. Document Control (role_id = 3) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- สิทธิ์จัดการเอกสารทั้งหมด -(3, 29), -- document.create_draft -(3, 30), -- document.submit -(3, 31), -- document.view -(3, 32), -- document.edit -(3, 33), -- document.admin_edit -(3, 34), -- document.delete -(3, 35), -- document.attach --- สิทธิ์สร้างเอกสารแต่ละประเภท -(3, 36), -- correspondence.create -(3, 37), -- rfa.create -(3, 39), -- drawing.create -(3, 40), -- transmittal.create -(3, 41), -- circulation.create --- สิทธิ์จัดการ Workflow -(3, 45), -- workflow.action_review -(3, 46), -- workflow.force_proceed -(3, 47), -- workflow.revert --- สิทธิ์จัดการ Circulation -(3, 42), -- circulation.respond -(3, 43), -- circulation.acknowledge -(3, 44), -- circulation.close --- สิทธิ์อื่นๆ ที่จำเป็น -(3, 38), -- rfa.manage_shop_drawings -(3, 48), -- search.advanced -(3, 49); -- report.generate - --- ===================================================== --- == 4. Editor (role_id = 4) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- สิทธิ์แก้ไขเอกสาร (แต่ไม่ใช่สิทธิ์ Admin) -(4, 29), -- document.create_draft -(4, 30), -- document.submit -(4, 31), -- document.view -(4, 32), -- document.edit -(4, 35), -- document.attach --- สิทธิ์สร้างเอกสารแต่ละประเภท -(4, 36), -- correspondence.create -(4, 37), -- rfa.create -(4, 39), -- drawing.create -(4, 40), -- transmittal.create -(4, 41), -- circulation.create --- สิทธิ์อื่นๆ ที่จำเป็น -(4, 38), -- rfa.manage_shop_drawings -(4, 48); -- search.advanced - --- ===================================================== --- == 5. Viewer (role_id = 5) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- สิทธิ์ดูเท่านั้น -(5, 31), -- document.view -(5, 48); -- search.advanced - --- ===================================================== --- == 6. Project Manager (role_id = 6) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- สิทธิ์จัดการโครงการ -(6, 23), -- project.manage_members -(6, 24), -- project.create_contracts -(6, 25), -- project.manage_contracts -(6, 26), -- project.view_reports -(6, 9), -- project.view --- สิทธิ์จัดการข้อมูลหลักระดับโครงการ -(6, 16), -- master_data.drawing_category.manage --- สิทธิ์ดูข้อมูลในสัญญา -(6, 28), -- contract.view --- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) -(6, 29), -- document.create_draft -(6, 30), -- document.submit -(6, 31), -- document.view -(6, 32), -- document.edit -(6, 35), -- document.attach -(6, 36), -- correspondence.create -(6, 37), -- rfa.create -(6, 39), -- drawing.create -(6, 40), -- transmittal.create -(6, 41), -- circulation.create -(6, 38), -- rfa.manage_shop_drawings -(6, 48), -- search.advanced -(6, 49); -- report.generate - --- ===================================================== --- == 7. Contract Admin (role_id = 7) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) VALUES --- สิทธิ์จัดการสัญญา -(7, 27), -- contract.manage_members -(7, 28), -- contract.view --- สิทธิ์ในการอนุมัติ (ส่วนหนึ่งของ Workflow) -(7, 45), -- workflow.action_review --- สิทธิ์จัดการข้อมูลเฉพาะสัญญา -(7, 38), -- rfa.manage_shop_drawings -(7, 39), -- drawing.create --- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) -(7, 29), -- document.create_draft -(7, 30), -- document.submit -(7, 31), -- document.view -(7, 32), -- document.edit -(7, 35), -- document.attach -(7, 36), -- correspondence.create -(7, 37), -- rfa.create -(7, 40), -- transmittal.create -(7, 41), -- circulation.create -(7, 48); -- search.advanced - --- ตารางเชื่อมผู้ใช้ (users) -CREATE TABLE user_assignments ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL, - role_id INT NOT NULL, - - -- คอลัมน์สำหรับกำหนดขอบเขต (จะใช้เพียงอันเดียวต่อแถว) - organization_id INT NULL, - project_id INT NULL, - contract_id INT NULL, - - assigned_by_user_id INT, -- ผู้ที่มอบหมายบทบาทนี้ - assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, - FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id), - - -- Constraint เพื่อให้แน่ใจว่ามีเพียงขอบเขตเดียวที่ถูกกำหนดในแต่ละแถว - CONSTRAINT chk_scope CHECK ( - (organization_id IS NOT NULL AND project_id IS NULL AND contract_id IS NULL) OR - (organization_id IS NULL AND project_id IS NOT NULL AND contract_id IS NULL) OR - (organization_id IS NULL AND project_id IS NULL AND contract_id IS NOT NULL) OR - (organization_id IS NULL AND project_id IS NULL AND contract_id IS NULL) -- สำหรับ Global scope - ) -); - -CREATE TABLE project_organizations ( - project_id INT NOT NULL, - organization_id INT NOT NULL, - PRIMARY KEY (project_id, organization_id), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE -); - -CREATE TABLE contract_organizations ( - contract_id INT NOT NULL, - organization_id INT NOT NULL, - role_in_contract VARCHAR(100), -- เช่น 'Owner', 'Designer', 'Consultant', 'Contractor' - PRIMARY KEY (contract_id, organization_id), - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE -); - --- ===================================================== --- == 4. การเชื่อมโยงโครงการกับองค์กร (project_organizations) == --- ===================================================== - --- โครงการหลัก (LCBP3) จะมีองค์กรหลักๆ เข้ามาเกี่ยวข้องทั้งหมด -INSERT INTO project_organizations (project_id, organization_id) -SELECT - (SELECT id FROM projects WHERE project_code = 'LCBP3'), - id -FROM organizations -WHERE organization_code IN ('กทท.', 'สคฉ.3', 'TEAM', 'คคง.', 'ผรม.1', 'ผรม.2', 'ผรม.3', 'ผรม.4', 'EN', 'CAR'); - --- โครงการย่อย (LCBP3C1) จะมีเฉพาะองค์กรที่เกี่ยวข้อง -INSERT INTO project_organizations (project_id, organization_id) -SELECT - (SELECT id FROM projects WHERE project_code = 'LCBP3C1'), - id -FROM organizations -WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-02', 'คคง.', 'ผรม.1'); - --- ทำเช่นเดียวกันสำหรับโครงการอื่นๆ (ตัวอย่าง) -INSERT INTO project_organizations (project_id, organization_id) -SELECT - (SELECT id FROM projects WHERE project_code = 'LCBP3C2'), - id -FROM organizations -WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-03', 'คคง.', 'ผรม.2'); - - --- ===================================================== --- == 5. การเชื่อมโยงสัญญากับองค์กร (contract_organizations) == --- ===================================================== - --- สัญญาที่ปรึกษาออกแบบ (DSLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES - ((SELECT id FROM contracts WHERE contract_code = 'DSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), - ((SELECT id FROM contracts WHERE contract_code = 'DSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'TEAM'), 'Designer'); - --- สัญญาที่ปรึกษาควบคุมงาน (PSLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES - ((SELECT id FROM contracts WHERE contract_code = 'PSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), - ((SELECT id FROM contracts WHERE contract_code = 'PSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'คคง.'), 'Consultant'); - --- สัญญางานก่อสร้าง ส่วนที่ 1 (LCBP3-C1) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES - ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C1'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), - ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C1'), (SELECT id FROM organizations WHERE organization_code = 'ผรม.1'), 'Contractor'); - --- สัญญางานก่อสร้าง ส่วนที่ 2 (LCBP3-C2) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES - ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C2'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), - ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C2'), (SELECT id FROM organizations WHERE organization_code = 'ผรม.2'), 'Contractor'); - --- สัญญาตรวจสอบสิ่งแวดล้อม (ENLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES - ((SELECT id FROM contracts WHERE contract_code = 'ENLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), - ((SELECT id FROM contracts WHERE contract_code = 'ENLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'EN'), 'Consultant'); - --- ===================================================== --- 3. ✉️ Correspondences (เอกสารหลัก, Revisions) --- ===================================================== - --- ตาราง Master เก็บประเภทเอกสารโต้ตอบ -CREATE TABLE correspondence_types ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสประเภท (เช่น RFA, RFI)', - type_name VARCHAR(255) NOT NULL COMMENT 'ชื่อประเภท', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บประเภทเอกสารโต้ตอบ'; -INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) VALUES - ('RFA', 'Request for Approval', 1,1), - ('RFI', 'Request for Information', 2,1), - ('TRANSMITTAL', 'Transmittal', 3,1), - ('EMAIL', 'Email', 4,1), - ('INSTRUCTION', 'Instruction', 5,1), - ('LETTER', 'Letter', 6,1), - ('MEMO', 'Memorandum', 7,1), - ('MOM', 'Minutes of Meeting', 8,1), - ('NOTICE', 'Notice', 9,1), - ('OTHER', 'Other', 10,1); - --- ตาราง Master เก็บสถานะของเอกสาร -CREATE TABLE correspondence_status ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสถานะหนังสือ (เช่น DRAFT, SUBOWN)', - status_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสถานะหนังสือ', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บสถานะของเอกสาร'; -INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) VALUES - ('DRAFT', 'Draft', 10,1), - ('SUBOWN', 'Submitted to Owner', 21,1), - ('SUBDSN', 'Submitted to Designer', 22,1), - ('SUBCSC', 'Submitted to CSC', 23,1), - ('SUBCON', 'Submitted to Contractor', 24,1), - ('SUBOTH', 'Submitted to Others', 25,1), - ('REPOWN', 'Reply by Owner', 31,1), - ('REPDSN', 'Reply by Designer', 32,1), - ('REPCSC', 'Reply by CSC', 33,1), - ('REPCON', 'Reply by Contractor', 34,1), - ('REPOTH', 'Reply by Others', 35,1), - ('RSBOWN', 'Resubmited by Owner', 41,1), - ('RSBDSN', 'Resubmited by Designer', 42,1), - ('RSBCSC', 'Resubmited by CSC', 43,1), - ('RSBCON', 'Resubmited by Contractor', 44,1), - ('CLBOWN', 'Closed by Owner', 51,1), - ('CLBDSN', 'Closed by Designer', 52,1), - ('CLBCSC', 'Closed by CSC', 53,1), - ('CLBCON', 'Closed by Contractor', 54,1), - ('CCBOWN', 'Canceled by Owner', 91,1), - ('CCBDSN', 'Canceled by Designer', 92,1), - ('CCBCSC', 'Canceled by CSC', 93,1), - ('CCBCON', 'Canceled by Contractor', 94,1); - --- ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision -CREATE TABLE correspondences ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (นี่คือ "Master ID" ที่ใช้เชื่อมโยง)', - correspondence_number VARCHAR(100) NOT NULL COMMENT 'เลขที่เอกสาร (สร้างจาก DocumentNumberingModule)', - correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', - is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = ภายใน, 0 = ภายนอก)', - project_id INT NOT NULL COMMENT 'อยู่ในโครงการ', - originator_id INT COMMENT 'องค์กรผู้ส่ง', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - created_by INT COMMENT 'ผู้สร้าง', - deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, - UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision'; - --- ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N) -CREATE TABLE correspondence_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - correspondence_id INT NOT NULL COMMENT 'Master ID', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', - correspondence_status_id INT NOT NULL COMMENT 'สถานะของ Revision นี้', - title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', - document_date DATE COMMENT 'วันที่ในเอกสาร', - issued_date DATETIME COMMENT 'วันที่ออกเอกสาร', - received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', - due_date DATETIME COMMENT 'วันที่ครบกำหนด', - description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', - details JSON COMMENT 'ข้อมูลเฉพาะ (เช่น RFI details)', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', - created_by INT COMMENT 'ผู้สร้าง', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, - UNIQUE KEY uq_master_revision_number (correspondence_id, revision_number), - UNIQUE KEY uq_master_current (correspondence_id, is_current) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N)'; - --- ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N) -CREATE TABLE correspondence_recipients ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - recipient_organization_id INT COMMENT 'ID องค์กรผู้รับ', - recipient_type ENUM('TO', 'CC') COMMENT 'ประเภทผู้รับ (TO หรือ CC)', - PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type), - FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N)'; - --- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ -CREATE TABLE tags ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อ Tag', - description TEXT COMMENT 'คำอธิบายแท็ก', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ'; - --- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) -CREATE TABLE correspondence_tags ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - tag_id INT COMMENT 'ID ของ Tag', - PRIMARY KEY (correspondence_id, tag_id), - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; - --- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) -CREATE TABLE correspondence_references ( - src_correspondence_id INT COMMENT 'ID เอกสารต้นทาง', - tgt_correspondence_id INT COMMENT 'ID เอกสารเป้าหมาย', - PRIMARY KEY (src_correspondence_id, tgt_correspondence_id), - FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)'; - --- ===================================================== --- 4. 📐 approval: RFA (เอกสารขออนุมัติ, Workflows) --- ===================================================== -CREATE TABLE correspondence_routing_templates ( - id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของแม่แบบ', - template_name VARCHAR(255) NOT NULL COMMENT 'ชื่อแม่แบบ', - description TEXT COMMENT 'คำอธิบาย', - project_id INT NULL COMMENT 'ID โครงการ (ถ้าเป็นแม่แบบเฉพาะโครงการ)', -- NULL = แม่แบบทั่วไป - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - UNIQUE KEY ux_routing_template_name_project (template_name, project_id), - CONSTRAINT fk_crt_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='แม่แบบสายงานการส่งต่อเอกสารขออนุมัติ'; - -CREATE TABLE correspondence_status_transitions( - type_id INT NOT NULL COMMENT 'ID ของประเภทหนังสือ', - from_status_id INT NOT NULL COMMENT 'ID ของสถานะต้นทาง', - to_status_id INT NOT NULL COMMENT 'ID ของสถานะปลายทาง', - PRIMARY KEY (type_id, from_status_id, to_status_id), - CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(id), - CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_status(id), - CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_status(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสถานะที่อนุญาตให้เปลี่ยนแปลงได้ตามประเภทหนังสือ'; - --- 1.18.1 correspondence_routing_template_steps Table -CREATE TABLE correspondence_routing_template_steps ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - sequence INT NOT NULL COMMENT 'ลำดับขั้นตอน', - to_organization_id INT NOT NULL COMMENT 'ID องค์กรณ์ผู้รับในขั้นตอนนี้', - step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้', - - UNIQUE KEY ux_cor_template_sequence (template_id, sequence), - CONSTRAINT fk_cwts_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE CASCADE, - CONSTRAINT fk_cwts_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ขั้นตอนในแม่แบบ Workflow การส่งต่อเอกสาร'; - --- 1.19.1 correspondence_routing_steps Table -CREATE TABLE correspondence_routings ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', - correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร(FK -> correspondence_revisions)', - template_id INT NULL COMMENT 'ID ของแม่แบบที่ใช้ (ถ้ามี)', -- สำหรับอ้างอิงถึงแม่แบบ - sequence INT NOT NULL COMMENT 'ลำดับของขั้นตอนการส่งต่อ', - from_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้ส่ง', - to_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้รับ', - step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION', 'FOR_ACTION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ', - status ENUM('SENT', 'RECEIVED', 'ACTIONED', 'FORWARDED', 'REPLIED') NOT NULL DEFAULT 'SENT' COMMENT 'สถานะการดำเนินการของเอกสารในขั้นตอนนี้', - comments TEXT COMMENT 'หมายเหตุ หรือความคิดเห็นในการส่งต่อ', - due_date DATETIME NULL COMMENT 'วันที่ต้องตอบเอกสารในขั้นตอนนี้', - processed_by_user_id INT NULL COMMENT 'ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้', - processed_at TIMESTAMP NULL COMMENT 'เวลาที่ดำเนินการ', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่สร้างขั้นตอนนี้', - - UNIQUE KEY ux_cor_routing_sequence (correspondence_id, sequence), - -- Foreign Keys - CONSTRAINT fk_crs_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, - CONSTRAINT fk_crs_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE SET NULL, - CONSTRAINT fk_crs_from_org FOREIGN KEY (from_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - CONSTRAINT fk_crs_to_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - CONSTRAINT fk_crs_processed_by_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางติดตาม Workflow การส่งต่อเอกสารทั่วไป'; - --- ตาราง Master สำหรับประเภท RFA -CREATE TABLE rfa_types ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - type_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภท RFA (เช่น DWG, DOC, MAT)', - type_name VARCHAR(100) NOT NULL COMMENT 'ชื่อประเภท RFA', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับประเภท RFA'; -INSERT INTO rfa_types (type_code, type_name, sort_order, is_active) VALUES - ('DWG', 'Shop Drawing', 10, 1), - ('DOC', 'Document', 20, 1), - ('SPC', 'Specification', 21, 1), - ('CAL', 'Calculation', 22, 1), - ('TRP', 'Test Report', 23, 1), - ('SRY', 'Survey Report', 24, 1), - ('QAQC', 'QA/QC Document', 25, 1), - ('MES', 'Method Statement', 30, 1), - ('MAT', 'Material', 40, 1), - ('ASB', 'As-Built', 50, 1), - ('OTH', 'Other', 99, 1); - --- ตาราง Master สำหรับสถานะ RFA -CREATE TABLE rfa_status_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะ RFA (เช่น DFT - Draft, FAP - For Approve)', - status_name VARCHAR(100) NOT NULL COMMENT 'ชื่อสถานะ', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับสถานะ RFA'; -INSERT INTO rfa_status_codes (status_code, status_name, description, sort_order) VALUES -('DFT', 'Draft', 'ฉบับร่าง', 1), -('FAP', 'For Approve', 'เพื่อขออนุมัติ', 11), -('FRE', 'For Review', 'เพื่อตรวจสอบ', 12), -('FCO', 'For Construction', 'เพื่อก่อสร้าง', 20), -('ASB', 'AS-Built', 'แบบก่อสร้างจริง', 30), -('OBS', 'Obsolete', 'ไม่ใช้งาน', 80), -('CC', 'Canceled', 'ยกเลิก', 99); - --- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA -CREATE TABLE rfa_approve_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)', - approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับรหัสผลการอนุมัติ RFA'; -INSERT INTO rfa_approve_codes (approve_code, approve_name, sort_order, is_active) VALUES -('1A', 'Approved by Authority', 10, 1), -('1C', 'Approved by CSC', 11, 1), -('1N', 'Approved As Note', 12, 1), -('1R', 'Approved with Remarks', 13, 1), -('3C', 'Consultant Comments', 31, 1), -('3R', 'Revise and Resubmit', 32, 1), -('4X', 'Reject', 40, 1), -('5N', 'No Further Action', 50, 1); - --- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions) -CREATE TABLE rfas ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (RFA Master ID)', - rfa_type_id INT NOT NULL COMMENT 'ประเภท RFA', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - created_by INT COMMENT 'ผู้สร้าง', - deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', - FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)'; - --- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N) -CREATE TABLE rfa_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - correspondence_id INT NOT NULL COMMENT 'Master ID ของ Correspondence', - rfa_id INT NOT NULL COMMENT 'Master ID ของ RFA', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', - rfa_status_code_id INT NOT NULL COMMENT 'สถานะ RFA', - rfa_approve_code_id INT COMMENT 'ผลการอนุมัติ', - title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', - document_date DATE COMMENT 'วันที่ในเอกสาร', - issued_date DATE COMMENT 'วันที่ส่งขออนุมัติ', - received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', - approved_date DATE COMMENT 'วันที่อนุมัติ', - description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', - created_by INT COMMENT 'ผู้สร้าง', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, - FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), - FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, - UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), - UNIQUE KEY uq_rr_current (rfa_id, is_current) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N)'; - --- ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N) -CREATE TABLE rfa_items ( - rfarev_correspondence_id INT COMMENT 'ID ของ RFA Revision', - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - PRIMARY KEY (rfarev_correspondence_id, shop_drawing_revision_id), - FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N)'; - --- ตาราง Master เก็บแม่แบบสายอนุมัติ -CREATE TABLE rfa_workflow_templates ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายอนุมัติ', - description TEXT COMMENT 'คำอธิบาย', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บแม่แบบสายอนุมัติ'; - --- ตารางลูก เก็บขั้นตอนในแม่แบบ -CREATE TABLE rfa_workflow_template_steps ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - role_id INT COMMENT 'บทบาทที่รับผิดชอบ', - action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', - duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', - is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', - FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางลูก เก็บขั้นตอนในแม่แบบ'; - --- ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน -CREATE TABLE rfa_workflows ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - rfa_revision_id INT NOT NULL COMMENT 'ID ของ RFA Revision', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', - action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', - status ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'REJECTED') COMMENT 'สถานะ', - comments TEXT COMMENT 'ความคิดเห็น', - completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (assigned_to) REFERENCES users(user_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน'; - --- ===================================================== --- 5. 📐 Drawings (แบบ, หมวดหมู่) --- ===================================================== - --- ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_volumes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - volume_code VARCHAR(50) NOT NULL COMMENT 'รหัสเล่ม', - volume_name VARCHAR(255) NOT NULL COMMENT 'ชื่อเล่ม', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_volume_project (project_id, volume_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา'; - --- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_cats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่หลัก', - cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_cat_project (project_id, cat_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา'; - --- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_sub_cats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - sub_cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่ย่อย', - sub_cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา'; - --- ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N) -CREATE TABLE contract_drawing_subcat_cat_maps ( - project_id INT COMMENT 'ID ของโครงการ', - sub_cat_id INT COMMENT 'ID ของหมวดหมู่ย่อย', - cat_id INT COMMENT 'ID ของหมวดหมู่หลัก', - PRIMARY KEY (project_id, sub_cat_id, cat_id), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, - FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N)'; - --- ตาราง Master เก็บข้อมูล "แบบคู่สัญญา" -CREATE TABLE contract_drawings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - condwg_no VARCHAR(255) NOT NULL COMMENT 'เลขที่แบบสัญญา', - title VARCHAR(255) NOT NULL COMMENT 'ชื่อแบบสัญญา', - sub_cat_id INT COMMENT 'หมวดหมู่ย่อย', - volume_id INT COMMENT 'เล่ม', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - deleted_at DATETIME NULL COMMENT 'วันที่ลบ', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, - FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, - UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูล "แบบคู่สัญญา"'; - --- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง -CREATE TABLE shop_drawing_main_categories ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่หลัก (เช่น ARCH, STR)', - main_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง'; - --- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง -CREATE TABLE shop_drawing_sub_categories ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ย่อย (เช่น STR-COLUMN)', - sub_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', - main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง'; - --- ตาราง Master เก็บข้อมูล "แบบก่อสร้าง" -CREATE TABLE shop_drawings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'เลขที่ Shop Drawing', - title VARCHAR(500) NOT NULL COMMENT 'ชื่อแบบ', - main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', - sub_category_id INT NOT NULL COMMENT 'หมวดหมู่ย่อย', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - deleted_at DATETIME NULL COMMENT 'วันที่ลบ', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id), - FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id), - FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูล "แบบก่อสร้าง"'; - --- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N) -CREATE TABLE shop_drawing_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - shop_drawing_id INT NOT NULL COMMENT 'Master ID', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (เช่น 0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - revision_date DATE COMMENT 'วันที่ของ Revision', - description TEXT COMMENT 'คำอธิบายการแก้ไข', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, - UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N)'; - --- ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N) -CREATE TABLE shop_drawing_revision_contract_refs ( - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', - PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id), - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N)'; - --- ===================================================== --- 6. 🔄 Circulations (ใบเวียนภายใน) --- ===================================================== - --- ตาราง Master เก็บสถานะใบเวียน -CREATE TABLE circulation_status_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะการดำเนินงาน', - description VARCHAR(50) NOT NULL COMMENT 'คำอธิบายสถานะการดำเนินงาน', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บสถานะใบเวียน'; -INSERT INTO circulation_status_codes (code, description, sort_order) VALUES -('OPEN', 'Open', 1), -('IN_REVIEW', 'In Review', 2), -('COMPLETED', 'ปCompleted', 3), -('CANCELLED', 'Cancelled/Withdrawn', 9); - --- ตาราง "แม่" ของใบเวียนเอกสารภายใน -CREATE TABLE circulations ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตารางใบเวียน', - correspondence_id INT UNIQUE COMMENT 'ID ของเอกสาร (จากตาราง correspondences)', - organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ที่เป็นเจ้าของใบเวียนนี้', - circulation_no VARCHAR(100) NOT NULL COMMENT 'เลขที่ใบเวียน', - circulation_subject VARCHAR(500) NOT NULL COMMENT 'เรื่องใบเวียน', - circulation_status_code VARCHAR(20) NOT NULL COMMENT 'รหัสสถานะใบเวียน', - created_by_user_id INT NOT NULL COMMENT 'ID ของผู้สร้างใบเวียน', - submitted_at TIMESTAMP NULL COMMENT 'วันที่ส่งใบเวียน', - closed_at TIMESTAMP NULL COMMENT 'วันที่ปิดใบเวียน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id), - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code), - FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของใบเวียนเอกสารภายใน'; - --- ตาราง Master เก็บแม่แบบสายงาน -CREATE TABLE circulation_templates ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายงาน', - description TEXT COMMENT 'คำอธิบาย', - organization_id INT NOT NULL COMMENT 'องค์กรเจ้าของแม่แบบ', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (organization_id) REFERENCES organizations(id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บแม่แบบสายงาน'; - --- ตารางลูก เก็บขั้นตอนในแม่แบบ -CREATE TABLE circulation_template_assignees ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - role_id INT COMMENT 'บทบาทที่รับผิดชอบ', - duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', - is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', - FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางลูก เก็บขั้นตอนในแม่แบบ'; - --- ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow -CREATE TABLE circulation_routings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - circulation_id INT NOT NULL COMMENT 'ID ของใบเวียน', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', - status ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'REJECTED') COMMENT 'สถานะ', - comments TEXT COMMENT 'ความคิดเห็น', - completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (assigned_to) REFERENCES users(user_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow'; - --- ===================================================== --- 7. 📤 Transmittals (เอกสารนำส่ง) --- ===================================================== - --- ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences) -CREATE TABLE transmittals ( - correspondence_id INT PRIMARY KEY COMMENT 'ID ของเอกสาร', - purpose ENUM('FOR_APPROVAL', 'FOR_INFORMATION', 'FOR_REVIEW', 'OTHER') COMMENT 'วัตถุประสงค์', - remarks TEXT COMMENT 'หมายเหตุ', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences)'; - --- ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N) -CREATE TABLE transmittal_items ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของรายการ', - transmittal_id INT NOT NULL COMMENT 'ID ของ Transmittal', - item_correspondence_id INT NOT NULL COMMENT 'ID ของเอกสารที่แนบไป', - quantity INT DEFAULT 1 COMMENT 'จำนวน', - remarks VARCHAR(255) COMMENT 'หมายเหตุสำหรับรายการนี้', - FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N)'; - --- ===================================================== --- 8. 📎 File Management (ไฟล์แนบ) --- ===================================================== - --- ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ -CREATE TABLE attachments ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของไฟล์แนบ', - original_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ดั้งเดิมตอนอัปโหลด', - stored_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ที่เก็บจริงบน Server (ป้องกันชื่อซ้ำ)', - file_path VARCHAR(500) NOT NULL COMMENT 'Path ที่เก็บไฟล์ (บน QNAP /share/dms-data/)', - mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application/pdf)', - file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)', - uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด', - FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ'; - --- ตารางเชื่อม correspondences กับ attachments (M:N) -CREATE TABLE correspondence_attachments ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (correspondence_id, attachment_id), - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม correspondences กับ attachments (M:N)'; - --- ตารางเชื่อม circulations กับ attachments (M:N) -CREATE TABLE circulation_attachments ( - circulation_id INT COMMENT 'ID ของใบเวียน', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลักของใบเวียน)', - PRIMARY KEY (circulation_id, attachment_id), - FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม circulations กับ attachments (M:N)'; - --- ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N) -CREATE TABLE shop_drawing_revision_attachments ( - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (shop_drawing_revision_id, attachment_id), - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N)'; - --- ตารางเชื่อม contract_drawings กับ attachments (M:N) -CREATE TABLE contract_drawing_attachments ( - contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (contract_drawing_id, attachment_id), - FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม contract_drawings กับ attachments (M:N)'; - --- ===================================================== --- 9. 🔢 Document Numbering (การสร้างเลขที่เอกสาร) --- ===================================================== - --- ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร -CREATE TABLE document_number_formats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', - format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', - description TEXT COMMENT 'คำอธิบายรูปแบบนี้', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, - UNIQUE KEY uk_project_type (project_id, correspondence_type_id) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; - --- ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด -CREATE TABLE document_number_counters ( - project_id INT COMMENT 'โครงการ', - originator_organization_id INT COMMENT 'องค์กรผู้ส่ง', - correspondence_type_id INT COMMENT 'ประเภทเอกสาร', - current_year INT COMMENT 'ปี ค.ศ. ของตัวนับ', - last_number INT DEFAULT 0 COMMENT 'เลขที่ล่าสุดที่ใช้ไปแล้ว', - PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, current_year), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด'; - --- ===================================================== --- 10. ⚙️ System & Logs (ระบบและ Log) --- ===================================================== --- ตารางเก็บบันทึกการกระทำของผู้ใช้ -CREATE TABLE audit_logs ( - audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log', - user_id INT COMMENT 'ผู้กระทำ', - action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)', - entity_type VARCHAR(50) COMMENT 'ตาราง/โมดูล (เช่น ''rfa'', ''correspondence'')', - entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ', - details_json JSON COMMENT 'ข้อมูลบริบท', - ip_address VARCHAR(45) COMMENT 'IP Address', - user_agent VARCHAR(255) COMMENT 'User Agent', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ', - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเก็บบันทึกการกระทำของผู้ใช้'; - --- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System) -CREATE TABLE notifications ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน', - user_id INT NOT NULL COMMENT 'ID ผู้ใช้', - title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน', - message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน', - notification_type ENUM('EMAIL', 'LINE', 'SYSTEM') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)', - is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน', - entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''', - entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)'; - --- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search) -CREATE TABLE search_indices ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี', - entity_type VARCHAR(50) NOT NULL COMMENT 'ชนิดเอนทิตี (เช่น ''correspondence'', ''rfa'')', - entity_id INT NOT NULL COMMENT 'ID ของเอนทิตี', - content TEXT NOT NULL COMMENT 'เนื้อหาที่จะค้นหา', - indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง/อัปเดตัชนี' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)'; - --- ตารางสำหรับบันทึกประวัติการสำรองข้อมูล -CREATE TABLE backup_logs ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการสำรอง', - backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', - backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', - file_size BIGINT COMMENT 'ขนาดไฟล์', - status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', - started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', - completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', - error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับบันทึกประวัติการสำรองข้อมูล'; - --- ===================================================== --- CREATE INDEXES --- ===================================================== - --- Indexes for document_number_formats -CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); -CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); -CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); - --- Indexes for document_number_counters -CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); -CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); -CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); -CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); - --- Indexes for tags -CREATE INDEX idx_tags_name ON tags(tag_name); -CREATE INDEX idx_tags_created_at ON tags(created_at); - --- Indexes for correspondence_tags -CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); -CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); - --- Indexes for audit_logs -CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); -CREATE INDEX idx_audit_logs_action ON audit_logs(action); -CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); -CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); -CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); - --- Indexes for notifications -CREATE INDEX idx_notifications_user ON notifications(user_id); -CREATE INDEX idx_notifications_type ON notifications(notification_type); -CREATE INDEX idx_notifications_read ON notifications(is_read); -CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); -CREATE INDEX idx_notifications_created_at ON notifications(created_at); - --- Indexes for search_indices -CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); -CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); -CREATE FULLTEXT INDEX idx_search_indices_content ON search_indices(content); - --- Indexes for backup_logs -CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); -CREATE INDEX idx_backup_logs_status ON backup_logs(status); -CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); -CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); - --- ===================================================== --- Additional Composite Indexes for Performance --- ===================================================== - --- Composite index for document_number_counters for faster lookups -CREATE INDEX idx_doc_counter_composite ON document_number_counters(project_id, originator_organization_id, correspondence_type_id, current_year); - --- Composite index for notifications for user-specific queries -CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); - --- Composite index for audit_logs for reporting -CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); - --- Composite index for search_indices for entity-based queries -CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); - --- ===================================================== --- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB --- Generated from Data Dictionary --- ===================================================== - --- ===================================================== --- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์) --- ===================================================== - --- Stored Procedure ดึงเลขที่เอกสารถัดไป -DELIMITER // - -CREATE PROCEDURE sp_get_next_document_number( - IN p_project_id INT, - IN p_originator_organization_id INT, - IN p_correspondence_type_id INT, - IN p_current_year INT, - OUT p_next_number INT -) -BEGIN - DECLARE EXIT HANDLER FOR SQLEXCEPTION - BEGIN - -- หากเกิดข้อผิดพลาด ให้ยกเลิก Transaction และส่ง Error กลับไป - ROLLBACK; - END; - - START TRANSACTION; - - -- ล็อกแถวเพื่อป้องกัน Race Condition - SELECT last_number INTO p_next_number - FROM document_number_counters - WHERE project_id = p_project_id - AND originator_organization_id = p_originator_organization_id - AND correspondence_type_id = p_correspondence_type_id - AND current_year = p_current_year - FOR UPDATE; - - -- ถ้าไม่พบ record ให้สร้างใหม่ - IF p_next_number IS NULL THEN - SET p_next_number = 1; - INSERT INTO document_number_counters - (project_id, originator_organization_id, correspondence_type_id, current_year, last_number) - VALUES (p_project_id, p_originator_organization_id, p_correspondence_type_id, p_current_year, p_next_number); - ELSE - -- อัพเดทเลขที่ล่าสุด - SET p_next_number = p_next_number + 1; - UPDATE document_number_counters - SET last_number = p_next_number - WHERE project_id = p_project_id - AND originator_organization_id = p_originator_organization_id - AND correspondence_type_id = p_correspondence_type_id - AND current_year = p_current_year; - END IF; - - COMMIT; -END // - -DELIMITER ; - --- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA) -CREATE VIEW v_current_correspondences AS -SELECT - c.id AS correspondence_id, - c.correspondence_number, - c.correspondence_type_id, - ct.type_code AS correspondence_type_code, - ct.type_name AS correspondence_type_name, - c.project_id, - p.project_code, - p.project_name, - c.originator_id, - org.organization_code AS originator_code, - org.organization_name AS originator_name, - cr.id AS revision_id, - cr.revision_number, - cr.revision_label, - cr.title, - cr.document_date, - cr.issued_date, - cr.received_date, - cr.due_date, - cr.correspondence_status_id, - cs.status_code, - cs.status_name, - cr.created_by, - u.username AS created_by_username, - cr.created_at AS revision_created_at -FROM correspondences c -INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id -INNER JOIN projects p ON c.project_id = p.id -LEFT JOIN organizations org ON c.originator_id = org.id -INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id -INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id -LEFT JOIN users u ON cr.created_by = u.user_id -WHERE cr.is_current = TRUE - AND c.correspondence_type_id NOT IN (SELECT id FROM correspondence_types WHERE type_code = 'RFA') - AND c.deleted_at IS NULL; - --- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด -CREATE VIEW v_current_rfas AS -SELECT - r.id AS rfa_id, - r.rfa_type_id, - rt.type_code AS rfa_type_code, - rt.type_name AS rfa_type_name, - rr.correspondence_id, - c.correspondence_number, - c.project_id, - p.project_code, - p.project_name, - c.originator_id, - org.organization_name AS originator_name, - rr.id AS revision_id, - rr.revision_number, - rr.revision_label, - rr.title, - rr.document_date, - rr.issued_date, - rr.received_date, - rr.approved_date, - rr.rfa_status_code_id, - rsc.status_code AS rfa_status_code, - rsc.status_name AS rfa_status_name, - rr.rfa_approve_code_id, - rac.approve_code AS rfa_approve_code, - rac.approve_name AS rfa_approve_name, - rr.created_by, - u.username AS created_by_username, - rr.created_at AS revision_created_at -FROM rfas r -INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id -INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id -INNER JOIN correspondences c ON rr.correspondence_id = c.id -INNER JOIN projects p ON c.project_id = p.id -INNER JOIN organizations org ON c.originator_id = org.id -INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id -LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id -LEFT JOIN users u ON rr.created_by = u.user_id -WHERE rr.is_current = TRUE - AND r.deleted_at IS NULL - AND c.deleted_at IS NULL; - --- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization -CREATE VIEW v_contract_parties_all AS -SELECT - c.id AS contract_id, - c.contract_code, - c.contract_name, - p.id AS project_id, - p.project_code, - p.project_name, - o.id AS organization_id, - o.organization_code, - o.organization_name, - co.role_in_contract -FROM contracts c -INNER JOIN projects p ON c.project_id = p.id -INNER JOIN contract_organizations co ON c.id = co.contract_id -INNER JOIN organizations o ON co.organization_id = o.id -WHERE c.is_active = TRUE; - --- View แสดงรายการ "งานของฉัน" (My Tasks) ที่ยังไม่เสร็จ -CREATE VIEW v_user_tasks AS -SELECT - cr.id AS routing_id, - c.id AS circulation_id, - c.circulation_no, - c.circulation_subject, - c.correspondence_id, - corr.correspondence_number, - corr.project_id, - p.project_code, - p.project_name, - cr.assigned_to AS user_id, - u.username, - u.first_name, - u.last_name, - cr.organization_id, - org.organization_name, - cr.step_number, - cr.status AS task_status, - cr.comments, - cr.completed_at, - cr.created_at AS assigned_at, - c.created_at AS circulation_created_at -FROM circulation_routings cr -INNER JOIN circulations c ON cr.circulation_id = c.id -INNER JOIN correspondences corr ON c.correspondence_id = corr.id -INNER JOIN projects p ON corr.project_id = p.id -INNER JOIN organizations org ON cr.organization_id = org.id -INNER JOIN users u ON cr.assigned_to = u.user_id -WHERE cr.status IN ('PENDING', 'IN_PROGRESS') - AND cr.assigned_to IS NOT NULL; - --- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ -CREATE VIEW v_audit_log_details AS -SELECT - al.audit_id, - al.user_id, - u.username, - u.email, - u.first_name, - u.last_name, - al.action, - al.entity_type, - al.entity_id, - al.details_json, - al.ip_address, - al.user_agent, - al.created_at -FROM audit_logs al -LEFT JOIN users u ON al.user_id = u.user_id; - --- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน -CREATE VIEW v_user_all_permissions AS --- Global Permissions -SELECT - ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - NULL AS project_id, - NULL AS contract_id, - 'GLOBAL' AS permission_scope -FROM user_assignments ua -INNER JOIN roles r ON ua.role_id = r.role_id -INNER JOIN role_permissions rp ON ua.role_id = rp.role_id -INNER JOIN permissions p ON rp.permission_id = p.permission_id --- Global scope -WHERE p.is_active = 1 AND ua.organization_id IS NULL AND ua.project_id IS NULL AND ua.contract_id IS NULL - -UNION ALL - --- Organization-specific Permissions -SELECT - ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - NULL AS project_id, - NULL AS contract_id, - 'ORGANIZATION' AS permission_scope -FROM user_assignments ua -INNER JOIN roles r ON ua.role_id = r.role_id -INNER JOIN role_permissions rp ON ua.role_id = rp.role_id -INNER JOIN permissions p ON rp.permission_id = p.permission_id --- Organization scope -WHERE p.is_active = 1 AND ua.organization_id IS NOT NULL AND ua.project_id IS NULL AND ua.contract_id IS NULL -UNION ALL - --- Project-specific Permissions -SELECT - ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - ua.project_id, - NULL AS contract_id, - 'PROJECT' AS permission_scope -FROM user_assignments ua -INNER JOIN roles r ON ua.role_id = r.role_id -INNER JOIN role_permissions rp ON ua.role_id = rp.role_id -INNER JOIN permissions p ON rp.permission_id = p.permission_id --- Project scope - -WHERE p.is_active = 1 AND ua.project_id IS NOT NULL AND ua.contract_id IS NULL -UNION ALL - --- Contract-specific Permissions -SELECT - ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - ua.project_id, - ua.contract_id, - 'CONTRACT' AS permission_scope -FROM user_assignments ua -INNER JOIN roles r ON ua.role_id = r.role_id -INNER JOIN role_permissions rp ON ua.role_id = rp.role_id -INNER JOIN permissions p ON rp.permission_id = p.permission_id --- Contract scope -WHERE p.is_active = 1 AND ua.contract_id IS NOT NULL; - --- ===================================================== --- Additional Useful Views --- ===================================================== - --- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ -CREATE VIEW v_documents_with_attachments AS -SELECT - 'CORRESPONDENCE' AS document_type, - c.id AS document_id, - c.correspondence_number AS document_number, - c.project_id, - p.project_code, - p.project_name, - COUNT(ca.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM correspondences c -INNER JOIN projects p ON c.project_id = p.id -LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id -LEFT JOIN attachments a ON ca.attachment_id = a.id -WHERE c.deleted_at IS NULL -GROUP BY c.id, c.correspondence_number, c.project_id, p.project_code, p.project_name - -UNION ALL - -SELECT - 'CIRCULATION' AS document_type, - circ.id AS document_id, - circ.circulation_no AS document_number, - corr.project_id, - p.project_code, - p.project_name, - COUNT(ca.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM circulations circ -INNER JOIN correspondences corr ON circ.correspondence_id = corr.id -INNER JOIN projects p ON corr.project_id = p.id -LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id -LEFT JOIN attachments a ON ca.attachment_id = a.id -GROUP BY circ.id, circ.circulation_no, corr.project_id, p.project_code, p.project_name - -UNION ALL - -SELECT - 'SHOP_DRAWING' AS document_type, - sdr.id AS document_id, - sd.drawing_number AS document_number, - sd.project_id, - p.project_code, - p.project_name, - COUNT(sdra.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM shop_drawing_revisions sdr -INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id -INNER JOIN projects p ON sd.project_id = p.id -LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id -LEFT JOIN attachments a ON sdra.attachment_id = a.id -WHERE sd.deleted_at IS NULL -GROUP BY sdr.id, sd.drawing_number, sd.project_id, p.project_code, p.project_name - -UNION ALL - -SELECT - 'CONTRACT_DRAWING' AS document_type, - cd.id AS document_id, - cd.condwg_no AS document_number, - cd.project_id, - p.project_code, - p.project_name, - COUNT(cda.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM contract_drawings cd -INNER JOIN projects p ON cd.project_id = p.id -LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id -LEFT JOIN attachments a ON cda.attachment_id = a.id -WHERE cd.deleted_at IS NULL -GROUP BY cd.id, cd.condwg_no, cd.project_id, p.project_code, p.project_name; - --- View แสดงสถิติเอกสารตามประเภทและสถานะ -CREATE VIEW v_document_statistics AS -SELECT - p.id AS project_id, - p.project_code, - p.project_name, - ct.id AS correspondence_type_id, - ct.type_code, - ct.type_name, - cs.id AS status_id, - cs.status_code, - cs.status_name, - COUNT(DISTINCT c.id) AS document_count, - COUNT(DISTINCT cr.id) AS revision_count -FROM projects p -CROSS JOIN correspondence_types ct -CROSS JOIN correspondence_status cs -LEFT JOIN correspondences c ON p.id = c.project_id AND ct.id = c.correspondence_type_id -LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id AND cs.id = cr.correspondence_status_id AND cr.is_current = TRUE -WHERE p.is_active = 1 - AND ct.is_active = 1 - AND cs.is_active = 1 -GROUP BY p.id, p.project_code, p.project_name, ct.id, ct.type_code, ct.type_name, cs.id, cs.status_code, cs.status_name; - --- ===================================================== --- Indexes for View Performance Optimization --- ===================================================== - --- Indexes for v_current_correspondences performance -CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); -CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); -CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); - --- Indexes for v_current_rfas performance -CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); -CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); - --- Indexes for v_user_tasks performance -CREATE INDEX idx_circulation_routings_status_assigned ON circulation_routings(status, assigned_to); -CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings(circulation_id, status); - --- Indexes for document statistics performance -CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); -CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); - -SET FOREIGN_KEY_CHECKS=1; +-- ========================================================== +-- DMS v1.4.0 Document Management System Database +-- Deploy Script Schema +-- Server: Container Station on QNAPQNAP TS-473A +-- Database service: MariaDB 10.11 +-- database web ui: phpmyadmin 5-apache +-- database deelopment ui: DBeaver +-- backend sevice: NestJS +-- frontend sevice: next.js +-- reverse proxy: jc21/nginx-proxy-manager:latest +-- cron service: n8n +-- DMS v1.4.0 Improvements +-- Update: first revise fron v1.3.0 (GLM-4.6 & Gemini) +-- ========================================================== + +SET NAMES utf8mb4; +SET time_zone = '+07:00'; +-- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด +SET FOREIGN_KEY_CHECKS=0; + +DROP VIEW IF EXISTS v_document_statistics; +DROP VIEW IF EXISTS v_documents_with_attachments; +DROP VIEW IF EXISTS v_user_all_permissions; +DROP VIEW IF EXISTS v_audit_log_details; +DROP VIEW IF EXISTS v_user_tasks; +DROP VIEW IF EXISTS v_contract_parties_all; +DROP VIEW IF EXISTS v_current_rfas; +DROP VIEW IF EXISTS v_current_correspondences; + +DROP PROCEDURE IF EXISTS sp_get_next_document_number; +-- 🗑️ Drop all tables in reverse dependency order +-- ส่วนที่ 1: ตาราง System & Utility (ไม่ค่อยมีการอ้างอิง หรือเป็นตารางสุดท้ายในสายงาน) +DROP TABLE IF EXISTS backup_logs; +DROP TABLE IF EXISTS search_indices; +DROP TABLE IF EXISTS notifications; +DROP TABLE IF EXISTS audit_logs; + +-- ส่วนที่ 2: ตารางที่เชื่อมโยงกับเอกสารและข้อมูลหลัก (Junction Tables) +DROP TABLE IF EXISTS correspondence_tags; +DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; +DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; + +-- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments & Their Junctions) +DROP TABLE IF EXISTS contract_drawing_attachments; +DROP TABLE IF EXISTS circulation_attachments; +DROP TABLE IF EXISTS shop_drawing_revision_attachments; +DROP TABLE IF EXISTS correspondence_attachments; +DROP TABLE IF EXISTS attachments; + +-- ส่วนที่ 4: ตารางเกี่ยวกับ Workflow และ Template +DROP TABLE IF EXISTS circulation_routings; +DROP TABLE IF EXISTS circulation_template_assignees; +DROP TABLE IF EXISTS circulation_templates; +DROP TABLE IF EXISTS rfa_workflows; +DROP TABLE IF EXISTS rfa_workflow_template_steps; +DROP TABLE IF EXISTS rfa_workflow_templates; +-- add +DROP TABLE IF EXISTS correspondence_routings; +DROP TABLE IF EXISTS correspondence_routing_template_steps; +DROP TABLE IF EXISTS correspondence_status_transitions; +DROP TABLE IF EXISTS correspondence_routing_templates; + +-- ส่วนที่ 5: ตาราง Mapping หลัก (Main Mapping Tables) +DROP TABLE IF EXISTS role_permissions; +DROP TABLE IF EXISTS user_assignments; +DROP TABLE IF EXISTS contract_organizations; +DROP TABLE IF EXISTS project_organizations; + +-- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Detail/Item Tables) +DROP TABLE IF EXISTS transmittal_items; +DROP TABLE IF EXISTS shop_drawing_revisions; +DROP TABLE IF EXISTS rfa_items; +DROP TABLE IF EXISTS rfa_revisions; +DROP TABLE IF EXISTS correspondence_references; +DROP TABLE IF EXISTS correspondence_recipients; +DROP TABLE IF EXISTS correspondence_revisions; + +-- ส่วนที่ 7: ตารางเอกสารหลัก (Core Document Tables) +DROP TABLE IF EXISTS circulations; +DROP TABLE IF EXISTS transmittals; +DROP TABLE IF EXISTS contract_drawings; +DROP TABLE IF EXISTS shop_drawings; +DROP TABLE IF EXISTS rfas; +DROP TABLE IF EXISTS correspondences; + +-- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Categories & Master Data) +DROP TABLE IF EXISTS shop_drawing_sub_categories; +DROP TABLE IF EXISTS shop_drawing_main_categories; +DROP TABLE IF EXISTS contract_drawing_sub_cats; +DROP TABLE IF EXISTS contract_drawing_cats; +DROP TABLE IF EXISTS contract_drawing_volumes; +DROP TABLE IF EXISTS circulation_status_codes; +DROP TABLE IF EXISTS rfa_approve_codes; +DROP TABLE IF EXISTS rfa_status_codes; +DROP TABLE IF EXISTS rfa_types; +DROP TABLE IF EXISTS correspondence_status; +DROP TABLE IF EXISTS correspondence_types; +DROP TABLE IF EXISTS document_number_counters; +DROP TABLE IF EXISTS document_number_formats; +DROP TABLE IF EXISTS tags; + +-- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างหลัก (Users, Roles & Core Structure) +DROP TABLE IF EXISTS organization_roles; +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS permissions; +DROP TABLE IF EXISTS contracts; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS users; +DROP TABLE IF EXISTS organizations; + +-- ===================================================== +-- 1. 🏢 Core & Master Data (องค์กร, โครงการ, สัญญา) +-- ===================================================== + +-- ตาราง Master เก็บประเภทบทบาทขององค์กร +CREATE TABLE organization_roles ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'ชื่อบทบาท (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บประเภทบทบาทขององค์กร'; + +-- ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ +CREATE TABLE organizations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสองค์กร', + organization_name VARCHAR(255) NOT NULL COMMENT 'ชื่อองค์กร', + -- role_id INT COMMENT 'บทบาทขององค์กร', + is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' + -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ'; +-- Seed organization +INSERT INTO organizations (id, organization_code, organization_name) VALUES +(1, 'กทท.', 'การท่าเรือแห่งประเทศไทย'), +(10, 'สคฉ.3', 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3'), +(11, 'สคฉ.3-01', 'ตรวจรับพัสดุ ที่ปรึกษาควบคุมงาน'), +(12, 'สคฉ.3-02', 'ตรวจรับพัสดุ งานทางทะเล'), +(13, 'สคฉ.3-03', 'ตรวจรับพัสดุ อาคารและระบบสาธารณูปโภค'), +(14, 'สคฉ.3-04', 'ตรวจรับพัสดุ ตรวจสอบผลกระทบสิ่งแวดล้อม'), +(15, 'สคฉ.3-05', 'ตรวจรับพัสดุ เยียวยาการประมง'), +(16, 'สคฉ.3-06', 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 3'), +(17, 'สคฉ.3-07', 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 4'), +(18, 'สคฉ.3-xx', 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4'), +(21, 'TEAM', 'Designer Consulting Ltd.'), +(22, 'คคง.', 'Construction Supervision Ltd.'), +(41, 'ผรม.1', 'Contractor งานทางทะเล'), +(42, 'ผรม.2', 'Contractor อาคารและระบบ'), +(43, 'ผรม.3', 'Contractor #3 Ltd.'), +(44, 'ผรม.4', 'Contractor #4 Ltd.'), +(31, 'EN', 'Third Party Environment'), +(32, 'CAR', 'Third Party Fishery Care'); + +-- ตาราง Master เก็บข้อมูลโครงการ +CREATE TABLE projects ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสโครงการ', + project_name VARCHAR(255) NOT NULL COMMENT 'ชื่อโครงการ', + -- parent_project_id INT COMMENT 'รหัสโครงการหลัก (ถ้ามี)', + -- contractor_organization_id INT COMMENT 'รหัสองค์กรผู้รับเหมา (ถ้ามี)', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' + -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, + -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลโครงการ'; +INSERT INTO projects (project_code, project_name) VALUES + ('LCBP3','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)'), + ('LCBP3C1','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล'), + ('LCBP3C2','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค'), + ('LCBP3C3','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง'), + ('LCBP3C4','โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง'); + +-- ตาราง Master เก็บข้อมูลสัญญา +CREATE TABLE contracts ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL, + contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสัญญา', + contract_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสัญญา', + description TEXT COMMENT 'คำอธิบายสัญญา', + start_date DATE COMMENT 'วันที่เริ่มสัญญา', + end_date DATE COMMENT 'วันที่สิ้นสุดสัญญา', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลสัญญา'; +-- ใช้ Subquery เพื่อดึง project_id มาเชื่อมโยง ทำให้ไม่ต้องมานั่งจัดการ ID ด้วยตัวเอง +INSERT INTO contracts (contract_code, contract_name, project_id, is_active) VALUES +('DSLCBP3', 'งานจ้างที่ปรีกษาออกแบบ โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE), +('PSLCBP3', 'งานจ้างที่ปรีกษาควบคุมงาน โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE), +('LCBP3-C1', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล', (SELECT id FROM projects WHERE project_code = 'LCBP3C1'), TRUE), +('LCBP3-C2', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค', (SELECT id FROM projects WHERE project_code = 'LCBP3C2'), TRUE), +('LCBP3-C3', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง', (SELECT id FROM projects WHERE project_code = 'LCBP3C3'), TRUE), +('LCBP3-C4', 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง', (SELECT id FROM projects WHERE project_code = 'LCBP3C4'), TRUE), +('ENLCBP3', 'งานจ้างเหมาตรวจสอบผลกระทบสิ่งแวดล้อมนะหว่างงานก่อสร้างโครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', (SELECT id FROM projects WHERE project_code = 'LCBP3'), TRUE); +-- ===================================================== +-- 2. 👥 Users & RBAC (ผู้ใช้, สิทธิ์, บทบาท) +-- ===================================================== + +-- ตาราง Master เก็บข้อมูลผู้ใช้งาน (User) +CREATE TABLE users ( + user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + username VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อผู้ใช้งาน', + password_hash VARCHAR(255) NOT NULL COMMENT 'รหัสผ่าน (Hashed)', + first_name VARCHAR(50) COMMENT 'ชื่อจริง', + last_name VARCHAR(50) COMMENT 'นามสกุล', + email VARCHAR(100) NOT NULL UNIQUE COMMENT 'อีเมล', + line_id VARCHAR(100) COMMENT 'LINE ID', + primary_organization_id INT COMMENT 'สังกัดองค์กร', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + failed_attempts INT DEFAULT 0 COMMENT 'จำนวนครั้งที่ล็อกอินล้มเหลว', + locked_until DATETIME COMMENT 'ล็อกอินไม่ได้จนถึงเวลา', + last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)'; +-- Initial SUPER_ADMIN user +INSERT INTO users (username, password_hash, email, is_active) +VALUES ('superadmin', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'superadmin@example.com', 1) +ON DUPLICATE KEY UPDATE email=VALUES(email), is_active=VALUES(is_active); + +-- Create editor01 user +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ('editor01', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'editor01@example.com', 1); + +-- Create viewer01 user (password hash placeholder, must change later) +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ('viewer01', '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', 'viewer01@example.com', 1); + +-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ +CREATE TABLE roles ( + role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', + role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท', + scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, -- ขอบเขตของบทบาท (จากข้อ 4.3) + description TEXT COMMENT 'คำอธิบายบทบาท', + is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = บทบาทของระบบ ลบไม่ได้)' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ'; +-- ========================================================== +-- Seed Roles (บทบาทพื้นฐาน 5 บทบาท ตาม Req 4.3) +-- ========================================================== +-- 1. Superadmin (Global) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(1, 'Superadmin', 'Global', 'ผู้ดูแลระบบสูงสุด: สามารถทำทุกอย่างในระบบ, จัดการองค์กร, และจัดการข้อมูลหลักระดับ Global'); + +-- 2. Org Admin (Organization) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(2, 'Org Admin', 'Organization', 'ผู้ดูแลองค์กร: จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, และดูรายงานขององค์กร'); + +-- 3. Document Control (Organization) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(3, 'Document Control', 'Organization', 'ควบคุมเอกสารขององค์กร: เพิ่ม/แก้ไข/ลบเอกสาร, และกำหนดสิทธิ์เอกสารภายในองค์กร'); + +-- 4. Editor (Organization) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(4, 'Editor', 'Organization', 'ผู้แก้ไขเอกสารขององค์กร: เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย'); + +-- 5. Viewer (Organization) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(5, 'Viewer', 'Organization', 'ผู้ดูเอกสารขององค์กร: ดูเอกสารที่มีสิทธิ์เข้าถึงเท่านั้น'); + +-- 6. Project Manager (Project) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(6, 'Project Manager', 'Project', 'ผู้จัดการโครงการ: จัดการสมาชิกในโครงการ, สร้าง/จัดการสัญญาในโครงการ, และดูรายงานโครงการ'); + +-- 7. Contract Admin (Contract) +INSERT INTO roles (role_id, role_name, scope, description) VALUES +(7, 'Contract Admin', 'Contract', 'ผู้ดูแลสัญญา: จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา, และอนุมัติเอกสารในสัญญา'); + +-- ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ +CREATE TABLE permissions ( + permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัสสิทธิ์ (เช่น rfas.create, rfas.view)', + description TEXT COMMENT 'คำอธิบายสิทธิ์', + module VARCHAR(50) COMMENT 'โมดูลที่เกี่ยวข้อง', + scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'ระดับขอบเขตของสิทธิ์', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ'; +-- ===================================================== +-- 2. Seed Permissions (สิทธิ์การใช้งานทั้งหมด) +-- สิทธิ์ระดับระบบและการจัดการหลัก (System & Master Data) +-- ===================================================== + +INSERT INTO permissions (permission_id, permission_name, description) VALUES +(1, 'system.manage_all', 'ทำทุกอย่างในระบบ (Superadmin Power)'), + +-- การจัดการองค์กร +(2, 'organization.create', 'สร้างองค์กรใหม่'), +(3, 'organization.edit', 'แก้ไขข้อมูลองค์กร'), +(4, 'organization.delete', 'ลบองค์กร'), +(5, 'organization.view', 'ดูรายการองค์กร'), + +-- การจัดการโครงการ +(6, 'project.create', 'สร้างโครงการใหม่'), +(7, 'project.edit', 'แก้ไขข้อมูลโครงการ'), +(8, 'project.delete', 'ลบโครงการ'), +(9, 'project.view', 'ดูรายการโครงการ'), + +-- การจัดการบทบาทและสิทธิ์ (Roles & Permissions) +(10, 'role.create', 'สร้างบทบาท (Role) ใหม่'), +(11, 'role.edit', 'แก้ไขบทบาท (Role)'), +(12, 'role.delete', 'ลบบทบาท (Role)'), +(13, 'permission.assign', 'มอบสิทธิ์ให้กับบทบาท (Role)'), + +-- การจัดการข้อมูลหลัก (Master Data) +(14, 'master_data.document_type.manage', 'จัดการประเภทเอกสาร (Document Types)'), +(15, 'master_data.document_status.manage', 'จัดการสถานะเอกสาร (Document Statuses)'), +(16, 'master_data.drawing_category.manage', 'จัดการหมวดหมู่แบบ (Drawing Categories)'), +(17, 'master_data.tag.manage', 'จัดการ Tags'), + +-- การจัดการผู้ใช้งาน +(18, 'user.create', 'สร้างผู้ใช้งานใหม่'), +(19, 'user.edit', 'แก้ไขข้อมูลผู้ใช้งาน'), +(20, 'user.delete', 'ลบ/ปิดการใช้งานผู้ใช้'), +(21, 'user.view', 'ดูข้อมูลผู้ใช้งาน'), +(22, 'user.assign_organization', 'มอบผู้ใช้งานให้กับองค์กร'); + + +-- ===================================================== +-- == 2. สิทธิ์การจัดการโครงการและสัญญา (Project & Contract) == +-- ===================================================== + +INSERT INTO permissions (permission_id, permission_name, description) VALUES +(23, 'project.manage_members', 'จัดการสมาชิกในโครงการ (เชิญ/ถอดสมาชิก)'), +(24, 'project.create_contracts', 'สร้างสัญญาในโครงการ'), +(25, 'project.manage_contracts', 'จัดการสัญญาในโครงการ'), +(26, 'project.view_reports', 'ดูรายงานระดับโครงการ'), + +(27, 'contract.manage_members', 'จัดการสมาชิกในสัญญา'), +(28, 'contract.view', 'ดูข้อมูลสัญญา'); + + +-- ===================================================== +-- == 3. สิทธิ์การจัดการเอกสาร (Document Management) == +-- ===================================================== + +-- สิทธิ์ทั่วไปสำหรับเอกสารทุกประเภท +INSERT INTO permissions (permission_id, permission_name, description) VALUES +(29, 'document.create_draft', 'สร้างเอกสารในสถานะฉบับร่าง (Draft)'), +(30, 'document.submit', 'ส่งเอกสาร (Submitted)'), +(31, 'document.view', 'ดูเอกสาร'), +(32, 'document.edit', 'แก้ไขเอกสาร (ทั่วไป)'), +(33, 'document.admin_edit', 'แก้ไข/ถอน/ยกเลิกเอกสารที่ส่งแล้ว (Admin Power)'), +(34, 'document.delete', 'ลบเอกสาร'), +(35, 'document.attach', 'จัดการไฟล์แนบ (อัปโหลด/ลบ)'), + +-- สิทธิ์เฉพาะสำหรับ Correspondence +(36, 'correspondence.create', 'สร้างเอกสารโต้ตอบ (Correspondence)'), + +-- สิทธิ์เฉพาะสำหรับ Request for Approval (RFA) +(37, 'rfa.create', 'สร้างเอกสารขออนุมัติ (RFA)'), +(38, 'rfa.manage_shop_drawings', 'จัดการข้อมูล Shop Drawing และ Contract Drawing ที่เกี่ยวข้อง'), + +-- สิทธิ์เฉพาะสำหรับ Shop Drawing & Contract Drawing +(39, 'drawing.create', 'สร้าง/แก้ไขข้อมูลแบบ (Shop/Contract Drawing)'), + +-- สิทธิ์เฉพาะสำหรับ Transmittal +(40, 'transmittal.create', 'สร้างเอกสารนำส่ง (Transmittal)'), + +-- สิทธิ์เฉพาะสำหรับ Circulation Sheet (ใบเวียน) +(41, 'circulation.create', 'สร้างใบเวียนเอกสาร (Circulation)'), +(42, 'circulation.respond', 'ตอบกลับใบเวียน (Main/Action)'), +(43, 'circulation.acknowledge', 'รับทราบใบเวียน (Information)'), +(44, 'circulation.close', 'ปิดใบเวียน'); + + +-- ===================================================== +-- == 4. สิทธิ์การจัดการ Workflow == +-- ===================================================== + +INSERT INTO permissions (permission_id, permission_name, description) VALUES +(45, 'workflow.action_review', 'ดำเนินการในขั้นตอนปัจจุบัน (เช่น ตรวจสอบแล้ว)'), +(46, 'workflow.force_proceed', 'บังคับไปยังขั้นตอนถัดไป (Document Control Power)'), +(47, 'workflow.revert', 'ย้อนกลับไปยังขั้นตอนก่อนหน้า (Document Control Power)'); + + +-- ===================================================== +-- == 5. สิทธิ์ด้านการค้นหาและรายงาน (Search & Reporting) == +-- ===================================================== + +INSERT INTO permissions (permission_id, permission_name, description) VALUES +(48, 'search.advanced', 'ใช้งานการค้นหาขั้นสูง'), +(49, 'report.generate', 'สร้างรายงานสรุป (รายวัน/สัปดาห์/เดือน/ปี)'); + +-- ตารางเชื่อมระหว่าง roles และ permissions (M:N) +CREATE TABLE role_permissions ( + role_id INT COMMENT 'ID ของบทบาท', + permission_id INT COMMENT 'ID ของสิทธิ์', + PRIMARY KEY (role_id, permission_id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง roles และ permissions (M:N)'; +-- ========================================================== +-- Seed Role-Permissions Mapping (จับคู่สิทธิ์เริ่มต้น) +-- ========================================================== +-- Seed data for the 'role_permissions' table +-- This table links roles to their specific permissions. + +-- NOTE: This assumes the role_id and permission_id from the previous seed data files. +-- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. + +-- ===================================================== +-- == 1. Superadmin (role_id = 1) - Gets ALL permissions == +-- ===================================================== + +-- Superadmin can do everything. We can dynamically link all permissions to this role. +-- This is a robust way to ensure Superadmin always has full power. +INSERT INTO role_permissions (role_id, permission_id) +SELECT 1, permission_id FROM permissions; + +-- ===================================================== +-- == 2. Org Admin (role_id = 2) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- จัดการผู้ใช้ในองค์กร +(2, 18), -- user.create +(2, 19), -- user.edit +(2, 20), -- user.delete +(2, 21), -- user.view +(2, 22), -- user.assign_organization +-- จัดการองค์กร +(2, 3), -- organization.edit +(2, 5), -- organization.view +-- จัดการข้อมูลหลักที่อนุญาต (เฉพาะ Tags) +(2, 17), -- master_data.tag.manage +-- ดูข้อมูลต่างๆ ในองค์กร +(2, 31), -- document.view +(2, 9), -- project.view +(2, 28), -- contract.view +-- การค้นหาและรายงาน +(2, 48), -- search.advanced +(2, 49); -- report.generate + +-- ===================================================== +-- == 3. Document Control (role_id = 3) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- สิทธิ์จัดการเอกสารทั้งหมด +(3, 29), -- document.create_draft +(3, 30), -- document.submit +(3, 31), -- document.view +(3, 32), -- document.edit +(3, 33), -- document.admin_edit +(3, 34), -- document.delete +(3, 35), -- document.attach +-- สิทธิ์สร้างเอกสารแต่ละประเภท +(3, 36), -- correspondence.create +(3, 37), -- rfa.create +(3, 39), -- drawing.create +(3, 40), -- transmittal.create +(3, 41), -- circulation.create +-- สิทธิ์จัดการ Workflow +(3, 45), -- workflow.action_review +(3, 46), -- workflow.force_proceed +(3, 47), -- workflow.revert +-- สิทธิ์จัดการ Circulation +(3, 42), -- circulation.respond +(3, 43), -- circulation.acknowledge +(3, 44), -- circulation.close +-- สิทธิ์อื่นๆ ที่จำเป็น +(3, 38), -- rfa.manage_shop_drawings +(3, 48), -- search.advanced +(3, 49); -- report.generate + +-- ===================================================== +-- == 4. Editor (role_id = 4) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- สิทธิ์แก้ไขเอกสาร (แต่ไม่ใช่สิทธิ์ Admin) +(4, 29), -- document.create_draft +(4, 30), -- document.submit +(4, 31), -- document.view +(4, 32), -- document.edit +(4, 35), -- document.attach +-- สิทธิ์สร้างเอกสารแต่ละประเภท +(4, 36), -- correspondence.create +(4, 37), -- rfa.create +(4, 39), -- drawing.create +(4, 40), -- transmittal.create +(4, 41), -- circulation.create +-- สิทธิ์อื่นๆ ที่จำเป็น +(4, 38), -- rfa.manage_shop_drawings +(4, 48); -- search.advanced + +-- ===================================================== +-- == 5. Viewer (role_id = 5) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- สิทธิ์ดูเท่านั้น +(5, 31), -- document.view +(5, 48); -- search.advanced + +-- ===================================================== +-- == 6. Project Manager (role_id = 6) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- สิทธิ์จัดการโครงการ +(6, 23), -- project.manage_members +(6, 24), -- project.create_contracts +(6, 25), -- project.manage_contracts +(6, 26), -- project.view_reports +(6, 9), -- project.view +-- สิทธิ์จัดการข้อมูลหลักระดับโครงการ +(6, 16), -- master_data.drawing_category.manage +-- สิทธิ์ดูข้อมูลในสัญญา +(6, 28), -- contract.view +-- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) +(6, 29), -- document.create_draft +(6, 30), -- document.submit +(6, 31), -- document.view +(6, 32), -- document.edit +(6, 35), -- document.attach +(6, 36), -- correspondence.create +(6, 37), -- rfa.create +(6, 39), -- drawing.create +(6, 40), -- transmittal.create +(6, 41), -- circulation.create +(6, 38), -- rfa.manage_shop_drawings +(6, 48), -- search.advanced +(6, 49); -- report.generate + +-- ===================================================== +-- == 7. Contract Admin (role_id = 7) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) VALUES +-- สิทธิ์จัดการสัญญา +(7, 27), -- contract.manage_members +(7, 28), -- contract.view +-- สิทธิ์ในการอนุมัติ (ส่วนหนึ่งของ Workflow) +(7, 45), -- workflow.action_review +-- สิทธิ์จัดการข้อมูลเฉพาะสัญญา +(7, 38), -- rfa.manage_shop_drawings +(7, 39), -- drawing.create +-- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) +(7, 29), -- document.create_draft +(7, 30), -- document.submit +(7, 31), -- document.view +(7, 32), -- document.edit +(7, 35), -- document.attach +(7, 36), -- correspondence.create +(7, 37), -- rfa.create +(7, 40), -- transmittal.create +(7, 41), -- circulation.create +(7, 48); -- search.advanced + +-- ตารางเชื่อมผู้ใช้ (users) +CREATE TABLE user_assignments ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + role_id INT NOT NULL, + + -- คอลัมน์สำหรับกำหนดขอบเขต (จะใช้เพียงอันเดียวต่อแถว) + organization_id INT NULL, + project_id INT NULL, + contract_id INT NULL, + + assigned_by_user_id INT, -- ผู้ที่มอบหมายบทบาทนี้ + assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id), + + -- Constraint เพื่อให้แน่ใจว่ามีเพียงขอบเขตเดียวที่ถูกกำหนดในแต่ละแถว + CONSTRAINT chk_scope CHECK ( + (organization_id IS NOT NULL AND project_id IS NULL AND contract_id IS NULL) OR + (organization_id IS NULL AND project_id IS NOT NULL AND contract_id IS NULL) OR + (organization_id IS NULL AND project_id IS NULL AND contract_id IS NOT NULL) OR + (organization_id IS NULL AND project_id IS NULL AND contract_id IS NULL) -- สำหรับ Global scope + ) +); + +CREATE TABLE project_organizations ( + project_id INT NOT NULL, + organization_id INT NOT NULL, + PRIMARY KEY (project_id, organization_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); + +CREATE TABLE contract_organizations ( + contract_id INT NOT NULL, + organization_id INT NOT NULL, + role_in_contract VARCHAR(100), -- เช่น 'Owner', 'Designer', 'Consultant', 'Contractor' + PRIMARY KEY (contract_id, organization_id), + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); + +-- ===================================================== +-- == 4. การเชื่อมโยงโครงการกับองค์กร (project_organizations) == +-- ===================================================== + +-- โครงการหลัก (LCBP3) จะมีองค์กรหลักๆ เข้ามาเกี่ยวข้องทั้งหมด +INSERT INTO project_organizations (project_id, organization_id) +SELECT + (SELECT id FROM projects WHERE project_code = 'LCBP3'), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'TEAM', 'คคง.', 'ผรม.1', 'ผรม.2', 'ผรม.3', 'ผรม.4', 'EN', 'CAR'); + +-- โครงการย่อย (LCBP3C1) จะมีเฉพาะองค์กรที่เกี่ยวข้อง +INSERT INTO project_organizations (project_id, organization_id) +SELECT + (SELECT id FROM projects WHERE project_code = 'LCBP3C1'), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-02', 'คคง.', 'ผรม.1'); + +-- ทำเช่นเดียวกันสำหรับโครงการอื่นๆ (ตัวอย่าง) +INSERT INTO project_organizations (project_id, organization_id) +SELECT + (SELECT id FROM projects WHERE project_code = 'LCBP3C2'), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-03', 'คคง.', 'ผรม.2'); + + +-- ===================================================== +-- == 5. การเชื่อมโยงสัญญากับองค์กร (contract_organizations) == +-- ===================================================== + +-- สัญญาที่ปรึกษาออกแบบ (DSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES + ((SELECT id FROM contracts WHERE contract_code = 'DSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), + ((SELECT id FROM contracts WHERE contract_code = 'DSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'TEAM'), 'Designer'); + +-- สัญญาที่ปรึกษาควบคุมงาน (PSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES + ((SELECT id FROM contracts WHERE contract_code = 'PSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), + ((SELECT id FROM contracts WHERE contract_code = 'PSLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'คคง.'), 'Consultant'); + +-- สัญญางานก่อสร้าง ส่วนที่ 1 (LCBP3-C1) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES + ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C1'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), + ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C1'), (SELECT id FROM organizations WHERE organization_code = 'ผรม.1'), 'Contractor'); + +-- สัญญางานก่อสร้าง ส่วนที่ 2 (LCBP3-C2) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES + ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C2'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), + ((SELECT id FROM contracts WHERE contract_code = 'LCBP3-C2'), (SELECT id FROM organizations WHERE organization_code = 'ผรม.2'), 'Contractor'); + +-- สัญญาตรวจสอบสิ่งแวดล้อม (ENLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES + ((SELECT id FROM contracts WHERE contract_code = 'ENLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'กทท.'), 'Owner'), + ((SELECT id FROM contracts WHERE contract_code = 'ENLCBP3'), (SELECT id FROM organizations WHERE organization_code = 'EN'), 'Consultant'); + +-- ===================================================== +-- 3. ✉️ Correspondences (เอกสารหลัก, Revisions) +-- ===================================================== + +-- ตาราง Master เก็บประเภทเอกสารโต้ตอบ +CREATE TABLE correspondence_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสประเภท (เช่น RFA, RFI)', + type_name VARCHAR(255) NOT NULL COMMENT 'ชื่อประเภท', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บประเภทเอกสารโต้ตอบ'; +INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) VALUES + ('RFA', 'Request for Approval', 1,1), + ('RFI', 'Request for Information', 2,1), + ('TRANSMITTAL', 'Transmittal', 3,1), + ('EMAIL', 'Email', 4,1), + ('INSTRUCTION', 'Instruction', 5,1), + ('LETTER', 'Letter', 6,1), + ('MEMO', 'Memorandum', 7,1), + ('MOM', 'Minutes of Meeting', 8,1), + ('NOTICE', 'Notice', 9,1), + ('OTHER', 'Other', 10,1); + +-- ตาราง Master เก็บสถานะของเอกสาร +CREATE TABLE correspondence_status ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสถานะหนังสือ (เช่น DRAFT, SUBOWN)', + status_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสถานะหนังสือ', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บสถานะของเอกสาร'; +INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) VALUES + ('DRAFT', 'Draft', 10,1), + ('SUBOWN', 'Submitted to Owner', 21,1), + ('SUBDSN', 'Submitted to Designer', 22,1), + ('SUBCSC', 'Submitted to CSC', 23,1), + ('SUBCON', 'Submitted to Contractor', 24,1), + ('SUBOTH', 'Submitted to Others', 25,1), + ('REPOWN', 'Reply by Owner', 31,1), + ('REPDSN', 'Reply by Designer', 32,1), + ('REPCSC', 'Reply by CSC', 33,1), + ('REPCON', 'Reply by Contractor', 34,1), + ('REPOTH', 'Reply by Others', 35,1), + ('RSBOWN', 'Resubmited by Owner', 41,1), + ('RSBDSN', 'Resubmited by Designer', 42,1), + ('RSBCSC', 'Resubmited by CSC', 43,1), + ('RSBCON', 'Resubmited by Contractor', 44,1), + ('CLBOWN', 'Closed by Owner', 51,1), + ('CLBDSN', 'Closed by Designer', 52,1), + ('CLBCSC', 'Closed by CSC', 53,1), + ('CLBCON', 'Closed by Contractor', 54,1), + ('CCBOWN', 'Canceled by Owner', 91,1), + ('CCBDSN', 'Canceled by Designer', 92,1), + ('CCBCSC', 'Canceled by CSC', 93,1), + ('CCBCON', 'Canceled by Contractor', 94,1); + +-- ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision +CREATE TABLE correspondences ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (นี่คือ "Master ID" ที่ใช้เชื่อมโยง)', + correspondence_number VARCHAR(100) NOT NULL COMMENT 'เลขที่เอกสาร (สร้างจาก DocumentNumberingModule)', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = ภายใน, 0 = ภายนอก)', + project_id INT NOT NULL COMMENT 'อยู่ในโครงการ', + originator_id INT COMMENT 'องค์กรผู้ส่ง', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, + UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision'; + +-- ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N) +CREATE TABLE correspondence_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + correspondence_status_id INT NOT NULL COMMENT 'สถานะของ Revision นี้', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATETIME COMMENT 'วันที่ออกเอกสาร', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + due_date DATETIME COMMENT 'วันที่ครบกำหนด', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + details JSON COMMENT 'ข้อมูลเฉพาะ (เช่น RFI details)', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, + UNIQUE KEY uq_master_revision_number (correspondence_id, revision_number), + UNIQUE KEY uq_master_current (correspondence_id, is_current) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N)'; + +-- ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N) +CREATE TABLE correspondence_recipients ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + recipient_organization_id INT COMMENT 'ID องค์กรผู้รับ', + recipient_type ENUM('TO', 'CC') COMMENT 'ประเภทผู้รับ (TO หรือ CC)', + PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type), + FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N)'; + +-- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ +CREATE TABLE tags ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อ Tag', + description TEXT COMMENT 'คำอธิบายแท็ก', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ'; + +-- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) +CREATE TABLE correspondence_tags ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + tag_id INT COMMENT 'ID ของ Tag', + PRIMARY KEY (correspondence_id, tag_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; + +-- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) +CREATE TABLE correspondence_references ( + src_correspondence_id INT COMMENT 'ID เอกสารต้นทาง', + tgt_correspondence_id INT COMMENT 'ID เอกสารเป้าหมาย', + PRIMARY KEY (src_correspondence_id, tgt_correspondence_id), + FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)'; + +-- ===================================================== +-- 4. 📐 approval: RFA (เอกสารขออนุมัติ, Workflows) +-- ===================================================== +CREATE TABLE correspondence_routing_templates ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของแม่แบบ', + template_name VARCHAR(255) NOT NULL COMMENT 'ชื่อแม่แบบ', + description TEXT COMMENT 'คำอธิบาย', + project_id INT NULL COMMENT 'ID โครงการ (ถ้าเป็นแม่แบบเฉพาะโครงการ)', -- NULL = แม่แบบทั่วไป + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + UNIQUE KEY ux_routing_template_name_project (template_name, project_id), + CONSTRAINT fk_crt_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='แม่แบบสายงานการส่งต่อเอกสารขออนุมัติ'; + +CREATE TABLE correspondence_status_transitions( + type_id INT NOT NULL COMMENT 'ID ของประเภทหนังสือ', + from_status_id INT NOT NULL COMMENT 'ID ของสถานะต้นทาง', + to_status_id INT NOT NULL COMMENT 'ID ของสถานะปลายทาง', + PRIMARY KEY (type_id, from_status_id, to_status_id), + CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(id), + CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_status(id), + CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_status(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสถานะที่อนุญาตให้เปลี่ยนแปลงได้ตามประเภทหนังสือ'; + +-- 1.18.1 correspondence_routing_template_steps Table +CREATE TABLE correspondence_routing_template_steps ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + sequence INT NOT NULL COMMENT 'ลำดับขั้นตอน', + to_organization_id INT NOT NULL COMMENT 'ID องค์กรณ์ผู้รับในขั้นตอนนี้', + step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้', + + UNIQUE KEY ux_cor_template_sequence (template_id, sequence), + CONSTRAINT fk_cwts_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE CASCADE, + CONSTRAINT fk_cwts_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ขั้นตอนในแม่แบบ Workflow การส่งต่อเอกสาร'; + +-- 1.19.1 correspondence_routing_steps Table +CREATE TABLE correspondence_routings ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร(FK -> correspondence_revisions)', + template_id INT NULL COMMENT 'ID ของแม่แบบที่ใช้ (ถ้ามี)', -- สำหรับอ้างอิงถึงแม่แบบ + sequence INT NOT NULL COMMENT 'ลำดับของขั้นตอนการส่งต่อ', + from_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้ส่ง', + to_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้รับ', + step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION', 'FOR_ACTION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ', + status ENUM('SENT', 'RECEIVED', 'ACTIONED', 'FORWARDED', 'REPLIED') NOT NULL DEFAULT 'SENT' COMMENT 'สถานะการดำเนินการของเอกสารในขั้นตอนนี้', + comments TEXT COMMENT 'หมายเหตุ หรือความคิดเห็นในการส่งต่อ', + due_date DATETIME NULL COMMENT 'วันที่ต้องตอบเอกสารในขั้นตอนนี้', + processed_by_user_id INT NULL COMMENT 'ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้', + processed_at TIMESTAMP NULL COMMENT 'เวลาที่ดำเนินการ', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่สร้างขั้นตอนนี้', + + UNIQUE KEY ux_cor_routing_sequence (correspondence_id, sequence), + -- Foreign Keys + CONSTRAINT fk_crs_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + CONSTRAINT fk_crs_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE SET NULL, + CONSTRAINT fk_crs_from_org FOREIGN KEY (from_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_to_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_processed_by_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางติดตาม Workflow การส่งต่อเอกสารทั่วไป'; + +-- ตาราง Master สำหรับประเภท RFA +CREATE TABLE rfa_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภท RFA (เช่น DWG, DOC, MAT)', + type_name VARCHAR(100) NOT NULL COMMENT 'ชื่อประเภท RFA', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับประเภท RFA'; +INSERT INTO rfa_types (type_code, type_name, sort_order, is_active) VALUES + ('DWG', 'Shop Drawing', 10, 1), + ('DOC', 'Document', 20, 1), + ('SPC', 'Specification', 21, 1), + ('CAL', 'Calculation', 22, 1), + ('TRP', 'Test Report', 23, 1), + ('SRY', 'Survey Report', 24, 1), + ('QAQC', 'QA/QC Document', 25, 1), + ('MES', 'Method Statement', 30, 1), + ('MAT', 'Material', 40, 1), + ('ASB', 'As-Built', 50, 1), + ('OTH', 'Other', 99, 1); + +-- ตาราง Master สำหรับสถานะ RFA +CREATE TABLE rfa_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะ RFA (เช่น DFT - Draft, FAP - For Approve)', + status_name VARCHAR(100) NOT NULL COMMENT 'ชื่อสถานะ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับสถานะ RFA'; +INSERT INTO rfa_status_codes (status_code, status_name, description, sort_order) VALUES +('DFT', 'Draft', 'ฉบับร่าง', 1), +('FAP', 'For Approve', 'เพื่อขออนุมัติ', 11), +('FRE', 'For Review', 'เพื่อตรวจสอบ', 12), +('FCO', 'For Construction', 'เพื่อก่อสร้าง', 20), +('ASB', 'AS-Built', 'แบบก่อสร้างจริง', 30), +('OBS', 'Obsolete', 'ไม่ใช้งาน', 80), +('CC', 'Canceled', 'ยกเลิก', 99); + +-- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA +CREATE TABLE rfa_approve_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)', + approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับรหัสผลการอนุมัติ RFA'; +INSERT INTO rfa_approve_codes (approve_code, approve_name, sort_order, is_active) VALUES +('1A', 'Approved by Authority', 10, 1), +('1C', 'Approved by CSC', 11, 1), +('1N', 'Approved As Note', 12, 1), +('1R', 'Approved with Remarks', 13, 1), +('3C', 'Consultant Comments', 31, 1), +('3R', 'Revise and Resubmit', 32, 1), +('4X', 'Reject', 40, 1), +('5N', 'No Further Action', 50, 1); + +-- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions) +CREATE TABLE rfas ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (RFA Master ID)', + rfa_type_id INT NOT NULL COMMENT 'ประเภท RFA', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)'; + +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N) +CREATE TABLE rfa_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID ของ Correspondence', + rfa_id INT NOT NULL COMMENT 'Master ID ของ RFA', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + rfa_status_code_id INT NOT NULL COMMENT 'สถานะ RFA', + rfa_approve_code_id INT COMMENT 'ผลการอนุมัติ', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATE COMMENT 'วันที่ส่งขออนุมัติ', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + approved_date DATE COMMENT 'วันที่อนุมัติ', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), + FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL, + UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), + UNIQUE KEY uq_rr_current (rfa_id, is_current) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N)'; + +-- ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N) +CREATE TABLE rfa_items ( + rfarev_correspondence_id INT COMMENT 'ID ของ RFA Revision', + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + PRIMARY KEY (rfarev_correspondence_id, shop_drawing_revision_id), + FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N)'; + +-- ตาราง Master เก็บแม่แบบสายอนุมัติ +CREATE TABLE rfa_workflow_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บแม่แบบสายอนุมัติ'; + +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE rfa_workflow_template_steps ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางลูก เก็บขั้นตอนในแม่แบบ'; + +-- ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน +CREATE TABLE rfa_workflows ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + rfa_revision_id INT NOT NULL COMMENT 'ID ของ RFA Revision', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + status ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'REJECTED') COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน'; + +-- ===================================================== +-- 5. 📐 Drawings (แบบ, หมวดหมู่) +-- ===================================================== + +-- ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_volumes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + volume_code VARCHAR(50) NOT NULL COMMENT 'รหัสเล่ม', + volume_name VARCHAR(255) NOT NULL COMMENT 'ชื่อเล่ม', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_volume_project (project_id, volume_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา'; + +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่หลัก', + cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_cat_project (project_id, cat_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา'; + +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_sub_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + sub_cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่ย่อย', + sub_cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา'; + +-- ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N) +CREATE TABLE contract_drawing_subcat_cat_maps ( + project_id INT COMMENT 'ID ของโครงการ', + sub_cat_id INT COMMENT 'ID ของหมวดหมู่ย่อย', + cat_id INT COMMENT 'ID ของหมวดหมู่หลัก', + PRIMARY KEY (project_id, sub_cat_id, cat_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, + FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N)'; + +-- ตาราง Master เก็บข้อมูล "แบบคู่สัญญา" +CREATE TABLE contract_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + condwg_no VARCHAR(255) NOT NULL COMMENT 'เลขที่แบบสัญญา', + title VARCHAR(255) NOT NULL COMMENT 'ชื่อแบบสัญญา', + sub_cat_id INT COMMENT 'หมวดหมู่ย่อย', + volume_id INT COMMENT 'เล่ม', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, + FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, + UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูล "แบบคู่สัญญา"'; + +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_main_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่หลัก (เช่น ARCH, STR)', + main_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง'; + +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_sub_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ย่อย (เช่น STR-COLUMN)', + sub_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง'; + +-- ตาราง Master เก็บข้อมูล "แบบก่อสร้าง" +CREATE TABLE shop_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'เลขที่ Shop Drawing', + title VARCHAR(500) NOT NULL COMMENT 'ชื่อแบบ', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + sub_category_id INT NOT NULL COMMENT 'หมวดหมู่ย่อย', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id), + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id), + FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บข้อมูล "แบบก่อสร้าง"'; + +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N) +CREATE TABLE shop_drawing_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + shop_drawing_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (เช่น 0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + revision_date DATE COMMENT 'วันที่ของ Revision', + description TEXT COMMENT 'คำอธิบายการแก้ไข', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, + UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N)'; + +-- ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N) +CREATE TABLE shop_drawing_revision_contract_refs ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N)'; + +-- ===================================================== +-- 6. 🔄 Circulations (ใบเวียนภายใน) +-- ===================================================== + +-- ตาราง Master เก็บสถานะใบเวียน +CREATE TABLE circulation_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะการดำเนินงาน', + description VARCHAR(50) NOT NULL COMMENT 'คำอธิบายสถานะการดำเนินงาน', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บสถานะใบเวียน'; +INSERT INTO circulation_status_codes (code, description, sort_order) VALUES +('OPEN', 'Open', 1), +('IN_REVIEW', 'In Review', 2), +('COMPLETED', 'ปCompleted', 3), +('CANCELLED', 'Cancelled/Withdrawn', 9); + +-- ตาราง "แม่" ของใบเวียนเอกสารภายใน +CREATE TABLE circulations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตารางใบเวียน', + correspondence_id INT UNIQUE COMMENT 'ID ของเอกสาร (จากตาราง correspondences)', + organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ที่เป็นเจ้าของใบเวียนนี้', + circulation_no VARCHAR(100) NOT NULL COMMENT 'เลขที่ใบเวียน', + circulation_subject VARCHAR(500) NOT NULL COMMENT 'เรื่องใบเวียน', + circulation_status_code VARCHAR(20) NOT NULL COMMENT 'รหัสสถานะใบเวียน', + created_by_user_id INT NOT NULL COMMENT 'ID ของผู้สร้างใบเวียน', + submitted_at TIMESTAMP NULL COMMENT 'วันที่ส่งใบเวียน', + closed_at TIMESTAMP NULL COMMENT 'วันที่ปิดใบเวียน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id), + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code), + FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "แม่" ของใบเวียนเอกสารภายใน'; + +-- ตาราง Master เก็บแม่แบบสายงาน +CREATE TABLE circulation_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายงาน', + description TEXT COMMENT 'คำอธิบาย', + organization_id INT NOT NULL COMMENT 'องค์กรเจ้าของแม่แบบ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (organization_id) REFERENCES organizations(id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บแม่แบบสายงาน'; + +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE circulation_template_assignees ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางลูก เก็บขั้นตอนในแม่แบบ'; + +-- ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow +CREATE TABLE circulation_routings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + circulation_id INT NOT NULL COMMENT 'ID ของใบเวียน', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + status ENUM('PENDING', 'IN_PROGRESS', 'COMPLETED', 'REJECTED') COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow'; + +-- ===================================================== +-- 7. 📤 Transmittals (เอกสารนำส่ง) +-- ===================================================== + +-- ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences) +CREATE TABLE transmittals ( + correspondence_id INT PRIMARY KEY COMMENT 'ID ของเอกสาร', + purpose ENUM('FOR_APPROVAL', 'FOR_INFORMATION', 'FOR_REVIEW', 'OTHER') COMMENT 'วัตถุประสงค์', + remarks TEXT COMMENT 'หมายเหตุ', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences)'; + +-- ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N) +CREATE TABLE transmittal_items ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของรายการ', + transmittal_id INT NOT NULL COMMENT 'ID ของ Transmittal', + item_correspondence_id INT NOT NULL COMMENT 'ID ของเอกสารที่แนบไป', + quantity INT DEFAULT 1 COMMENT 'จำนวน', + remarks VARCHAR(255) COMMENT 'หมายเหตุสำหรับรายการนี้', + FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N)'; + +-- ===================================================== +-- 8. 📎 File Management (ไฟล์แนบ) +-- ===================================================== + +-- ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ +CREATE TABLE attachments ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของไฟล์แนบ', + original_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ดั้งเดิมตอนอัปโหลด', + stored_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ที่เก็บจริงบน Server (ป้องกันชื่อซ้ำ)', + file_path VARCHAR(500) NOT NULL COMMENT 'Path ที่เก็บไฟล์ (บน QNAP /share/dms-data/)', + mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application/pdf)', + file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)', + uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด', + FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ'; + +-- ตารางเชื่อม correspondences กับ attachments (M:N) +CREATE TABLE correspondence_attachments ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (correspondence_id, attachment_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม correspondences กับ attachments (M:N)'; + +-- ตารางเชื่อม circulations กับ attachments (M:N) +CREATE TABLE circulation_attachments ( + circulation_id INT COMMENT 'ID ของใบเวียน', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลักของใบเวียน)', + PRIMARY KEY (circulation_id, attachment_id), + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม circulations กับ attachments (M:N)'; + +-- ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N) +CREATE TABLE shop_drawing_revision_attachments ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (shop_drawing_revision_id, attachment_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N)'; + +-- ตารางเชื่อม contract_drawings กับ attachments (M:N) +CREATE TABLE contract_drawing_attachments ( + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (contract_drawing_id, attachment_id), + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเชื่อม contract_drawings กับ attachments (M:N)'; + +-- ===================================================== +-- 9. 🔢 Document Numbering (การสร้างเลขที่เอกสาร) +-- ===================================================== + +-- ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร +CREATE TABLE document_number_formats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', + description TEXT COMMENT 'คำอธิบายรูปแบบนี้', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, + UNIQUE KEY uk_project_type (project_id, correspondence_type_id) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; + +-- ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด +CREATE TABLE document_number_counters ( + project_id INT COMMENT 'โครงการ', + originator_organization_id INT COMMENT 'องค์กรผู้ส่ง', + correspondence_type_id INT COMMENT 'ประเภทเอกสาร', + current_year INT COMMENT 'ปี ค.ศ. ของตัวนับ', + last_number INT DEFAULT 0 COMMENT 'เลขที่ล่าสุดที่ใช้ไปแล้ว', + PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, current_year), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด'; + +-- ===================================================== +-- 10. ⚙️ System & Logs (ระบบและ Log) +-- ===================================================== +-- ตารางเก็บบันทึกการกระทำของผู้ใช้ +CREATE TABLE audit_logs ( + audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log', + user_id INT COMMENT 'ผู้กระทำ', + action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)', + entity_type VARCHAR(50) COMMENT 'ตาราง/โมดูล (เช่น ''rfa'', ''correspondence'')', + entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ', + details_json JSON COMMENT 'ข้อมูลบริบท', + ip_address VARCHAR(45) COMMENT 'IP Address', + user_agent VARCHAR(255) COMMENT 'User Agent', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางเก็บบันทึกการกระทำของผู้ใช้'; + +-- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System) +CREATE TABLE notifications ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน', + user_id INT NOT NULL COMMENT 'ID ผู้ใช้', + title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน', + message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน', + notification_type ENUM('EMAIL', 'LINE', 'SYSTEM') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)', + is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน', + entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''', + entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)'; + +-- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search) +CREATE TABLE search_indices ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี', + entity_type VARCHAR(50) NOT NULL COMMENT 'ชนิดเอนทิตี (เช่น ''correspondence'', ''rfa'')', + entity_id INT NOT NULL COMMENT 'ID ของเอนทิตี', + content TEXT NOT NULL COMMENT 'เนื้อหาที่จะค้นหา', + indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง/อัปเดตัชนี' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)'; + +-- ตารางสำหรับบันทึกประวัติการสำรองข้อมูล +CREATE TABLE backup_logs ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการสำรอง', + backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', + backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', + file_size BIGINT COMMENT 'ขนาดไฟล์', + status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', + started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', + completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', + error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='ตารางสำหรับบันทึกประวัติการสำรองข้อมูล'; + +-- ===================================================== +-- CREATE INDEXES +-- ===================================================== + +-- Indexes for document_number_formats +CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); +CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); +CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); + +-- Indexes for document_number_counters +CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); +CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); +CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); +CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); + +-- Indexes for tags +CREATE INDEX idx_tags_name ON tags(tag_name); +CREATE INDEX idx_tags_created_at ON tags(created_at); + +-- Indexes for correspondence_tags +CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); +CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); + +-- Indexes for audit_logs +CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); +CREATE INDEX idx_audit_logs_action ON audit_logs(action); +CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); +CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); +CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); + +-- Indexes for notifications +CREATE INDEX idx_notifications_user ON notifications(user_id); +CREATE INDEX idx_notifications_type ON notifications(notification_type); +CREATE INDEX idx_notifications_read ON notifications(is_read); +CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); +CREATE INDEX idx_notifications_created_at ON notifications(created_at); + +-- Indexes for search_indices +CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); +CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); +CREATE FULLTEXT INDEX idx_search_indices_content ON search_indices(content); + +-- Indexes for backup_logs +CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); +CREATE INDEX idx_backup_logs_status ON backup_logs(status); +CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); +CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); + +-- ===================================================== +-- Additional Composite Indexes for Performance +-- ===================================================== + +-- Composite index for document_number_counters for faster lookups +CREATE INDEX idx_doc_counter_composite ON document_number_counters(project_id, originator_organization_id, correspondence_type_id, current_year); + +-- Composite index for notifications for user-specific queries +CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); + +-- Composite index for audit_logs for reporting +CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); + +-- Composite index for search_indices for entity-based queries +CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); + +-- ===================================================== +-- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB +-- Generated from Data Dictionary +-- ===================================================== + +-- ===================================================== +-- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์) +-- ===================================================== + +-- Stored Procedure ดึงเลขที่เอกสารถัดไป +DELIMITER // + +CREATE PROCEDURE sp_get_next_document_number( + IN p_project_id INT, + IN p_originator_organization_id INT, + IN p_correspondence_type_id INT, + IN p_current_year INT, + OUT p_next_number INT +) +BEGIN + DECLARE EXIT HANDLER FOR SQLEXCEPTION + BEGIN + -- หากเกิดข้อผิดพลาด ให้ยกเลิก Transaction และส่ง Error กลับไป + ROLLBACK; + END; + + START TRANSACTION; + + -- ล็อกแถวเพื่อป้องกัน Race Condition + SELECT last_number INTO p_next_number + FROM document_number_counters + WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year + FOR UPDATE; + + -- ถ้าไม่พบ record ให้สร้างใหม่ + IF p_next_number IS NULL THEN + SET p_next_number = 1; + INSERT INTO document_number_counters + (project_id, originator_organization_id, correspondence_type_id, current_year, last_number) + VALUES (p_project_id, p_originator_organization_id, p_correspondence_type_id, p_current_year, p_next_number); + ELSE + -- อัพเดทเลขที่ล่าสุด + SET p_next_number = p_next_number + 1; + UPDATE document_number_counters + SET last_number = p_next_number + WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year; + END IF; + + COMMIT; +END // + +DELIMITER ; + +-- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA) +CREATE VIEW v_current_correspondences AS +SELECT + c.id AS correspondence_id, + c.correspondence_number, + c.correspondence_type_id, + ct.type_code AS correspondence_type_code, + ct.type_name AS correspondence_type_name, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_code AS originator_code, + org.organization_name AS originator_name, + cr.id AS revision_id, + cr.revision_number, + cr.revision_label, + cr.title, + cr.document_date, + cr.issued_date, + cr.received_date, + cr.due_date, + cr.correspondence_status_id, + cs.status_code, + cs.status_name, + cr.created_by, + u.username AS created_by_username, + cr.created_at AS revision_created_at +FROM correspondences c +INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id +INNER JOIN projects p ON c.project_id = p.id +LEFT JOIN organizations org ON c.originator_id = org.id +INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id +INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id +LEFT JOIN users u ON cr.created_by = u.user_id +WHERE cr.is_current = TRUE + AND c.correspondence_type_id NOT IN (SELECT id FROM correspondence_types WHERE type_code = 'RFA') + AND c.deleted_at IS NULL; + +-- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด +CREATE VIEW v_current_rfas AS +SELECT + r.id AS rfa_id, + r.rfa_type_id, + rt.type_code AS rfa_type_code, + rt.type_name AS rfa_type_name, + rr.correspondence_id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_name AS originator_name, + rr.id AS revision_id, + rr.revision_number, + rr.revision_label, + rr.title, + rr.document_date, + rr.issued_date, + rr.received_date, + rr.approved_date, + rr.rfa_status_code_id, + rsc.status_code AS rfa_status_code, + rsc.status_name AS rfa_status_name, + rr.rfa_approve_code_id, + rac.approve_code AS rfa_approve_code, + rac.approve_name AS rfa_approve_name, + rr.created_by, + u.username AS created_by_username, + rr.created_at AS revision_created_at +FROM rfas r +INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id +INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id +INNER JOIN correspondences c ON rr.correspondence_id = c.id +INNER JOIN projects p ON c.project_id = p.id +INNER JOIN organizations org ON c.originator_id = org.id +INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id +LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id +LEFT JOIN users u ON rr.created_by = u.user_id +WHERE rr.is_current = TRUE + AND r.deleted_at IS NULL + AND c.deleted_at IS NULL; + +-- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization +CREATE VIEW v_contract_parties_all AS +SELECT + c.id AS contract_id, + c.contract_code, + c.contract_name, + p.id AS project_id, + p.project_code, + p.project_name, + o.id AS organization_id, + o.organization_code, + o.organization_name, + co.role_in_contract +FROM contracts c +INNER JOIN projects p ON c.project_id = p.id +INNER JOIN contract_organizations co ON c.id = co.contract_id +INNER JOIN organizations o ON co.organization_id = o.id +WHERE c.is_active = TRUE; + +-- View แสดงรายการ "งานของฉัน" (My Tasks) ที่ยังไม่เสร็จ +CREATE VIEW v_user_tasks AS +SELECT + cr.id AS routing_id, + c.id AS circulation_id, + c.circulation_no, + c.circulation_subject, + c.correspondence_id, + corr.correspondence_number, + corr.project_id, + p.project_code, + p.project_name, + cr.assigned_to AS user_id, + u.username, + u.first_name, + u.last_name, + cr.organization_id, + org.organization_name, + cr.step_number, + cr.status AS task_status, + cr.comments, + cr.completed_at, + cr.created_at AS assigned_at, + c.created_at AS circulation_created_at +FROM circulation_routings cr +INNER JOIN circulations c ON cr.circulation_id = c.id +INNER JOIN correspondences corr ON c.correspondence_id = corr.id +INNER JOIN projects p ON corr.project_id = p.id +INNER JOIN organizations org ON cr.organization_id = org.id +INNER JOIN users u ON cr.assigned_to = u.user_id +WHERE cr.status IN ('PENDING', 'IN_PROGRESS') + AND cr.assigned_to IS NOT NULL; + +-- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ +CREATE VIEW v_audit_log_details AS +SELECT + al.audit_id, + al.user_id, + u.username, + u.email, + u.first_name, + u.last_name, + al.action, + al.entity_type, + al.entity_id, + al.details_json, + al.ip_address, + al.user_agent, + al.created_at +FROM audit_logs al +LEFT JOIN users u ON al.user_id = u.user_id; + +-- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน +CREATE VIEW v_user_all_permissions AS +-- Global Permissions +SELECT + ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'GLOBAL' AS permission_scope +FROM user_assignments ua +INNER JOIN roles r ON ua.role_id = r.role_id +INNER JOIN role_permissions rp ON ua.role_id = rp.role_id +INNER JOIN permissions p ON rp.permission_id = p.permission_id +-- Global scope +WHERE p.is_active = 1 AND ua.organization_id IS NULL AND ua.project_id IS NULL AND ua.contract_id IS NULL + +UNION ALL + +-- Organization-specific Permissions +SELECT + ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'ORGANIZATION' AS permission_scope +FROM user_assignments ua +INNER JOIN roles r ON ua.role_id = r.role_id +INNER JOIN role_permissions rp ON ua.role_id = rp.role_id +INNER JOIN permissions p ON rp.permission_id = p.permission_id +-- Organization scope +WHERE p.is_active = 1 AND ua.organization_id IS NOT NULL AND ua.project_id IS NULL AND ua.contract_id IS NULL +UNION ALL + +-- Project-specific Permissions +SELECT + ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + NULL AS contract_id, + 'PROJECT' AS permission_scope +FROM user_assignments ua +INNER JOIN roles r ON ua.role_id = r.role_id +INNER JOIN role_permissions rp ON ua.role_id = rp.role_id +INNER JOIN permissions p ON rp.permission_id = p.permission_id +-- Project scope + +WHERE p.is_active = 1 AND ua.project_id IS NOT NULL AND ua.contract_id IS NULL +UNION ALL + +-- Contract-specific Permissions +SELECT + ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + ua.contract_id, + 'CONTRACT' AS permission_scope +FROM user_assignments ua +INNER JOIN roles r ON ua.role_id = r.role_id +INNER JOIN role_permissions rp ON ua.role_id = rp.role_id +INNER JOIN permissions p ON rp.permission_id = p.permission_id +-- Contract scope +WHERE p.is_active = 1 AND ua.contract_id IS NOT NULL; + +-- ===================================================== +-- Additional Useful Views +-- ===================================================== + +-- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ +CREATE VIEW v_documents_with_attachments AS +SELECT + 'CORRESPONDENCE' AS document_type, + c.id AS document_id, + c.correspondence_number AS document_number, + c.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM correspondences c +INNER JOIN projects p ON c.project_id = p.id +LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id +LEFT JOIN attachments a ON ca.attachment_id = a.id +WHERE c.deleted_at IS NULL +GROUP BY c.id, c.correspondence_number, c.project_id, p.project_code, p.project_name + +UNION ALL + +SELECT + 'CIRCULATION' AS document_type, + circ.id AS document_id, + circ.circulation_no AS document_number, + corr.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM circulations circ +INNER JOIN correspondences corr ON circ.correspondence_id = corr.id +INNER JOIN projects p ON corr.project_id = p.id +LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id +LEFT JOIN attachments a ON ca.attachment_id = a.id +GROUP BY circ.id, circ.circulation_no, corr.project_id, p.project_code, p.project_name + +UNION ALL + +SELECT + 'SHOP_DRAWING' AS document_type, + sdr.id AS document_id, + sd.drawing_number AS document_number, + sd.project_id, + p.project_code, + p.project_name, + COUNT(sdra.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM shop_drawing_revisions sdr +INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id +INNER JOIN projects p ON sd.project_id = p.id +LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id +LEFT JOIN attachments a ON sdra.attachment_id = a.id +WHERE sd.deleted_at IS NULL +GROUP BY sdr.id, sd.drawing_number, sd.project_id, p.project_code, p.project_name + +UNION ALL + +SELECT + 'CONTRACT_DRAWING' AS document_type, + cd.id AS document_id, + cd.condwg_no AS document_number, + cd.project_id, + p.project_code, + p.project_name, + COUNT(cda.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM contract_drawings cd +INNER JOIN projects p ON cd.project_id = p.id +LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id +LEFT JOIN attachments a ON cda.attachment_id = a.id +WHERE cd.deleted_at IS NULL +GROUP BY cd.id, cd.condwg_no, cd.project_id, p.project_code, p.project_name; + +-- View แสดงสถิติเอกสารตามประเภทและสถานะ +CREATE VIEW v_document_statistics AS +SELECT + p.id AS project_id, + p.project_code, + p.project_name, + ct.id AS correspondence_type_id, + ct.type_code, + ct.type_name, + cs.id AS status_id, + cs.status_code, + cs.status_name, + COUNT(DISTINCT c.id) AS document_count, + COUNT(DISTINCT cr.id) AS revision_count +FROM projects p +CROSS JOIN correspondence_types ct +CROSS JOIN correspondence_status cs +LEFT JOIN correspondences c ON p.id = c.project_id AND ct.id = c.correspondence_type_id +LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id AND cs.id = cr.correspondence_status_id AND cr.is_current = TRUE +WHERE p.is_active = 1 + AND ct.is_active = 1 + AND cs.is_active = 1 +GROUP BY p.id, p.project_code, p.project_name, ct.id, ct.type_code, ct.type_name, cs.id, cs.status_code, cs.status_name; + +-- ===================================================== +-- Indexes for View Performance Optimization +-- ===================================================== + +-- Indexes for v_current_correspondences performance +CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); +CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); +CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); + +-- Indexes for v_current_rfas performance +CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); +CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); + +-- Indexes for v_user_tasks performance +CREATE INDEX idx_circulation_routings_status_assigned ON circulation_routings(status, assigned_to); +CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings(circulation_id, status); + +-- Indexes for document statistics performance +CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); +CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); + +SET FOREIGN_KEY_CHECKS=1; diff --git a/01_lcbp3_v1_4_1.sql b/docs/01_lcbp3_v1_4_1.sql similarity index 92% rename from 01_lcbp3_v1_4_1.sql rename to docs/01_lcbp3_v1_4_1.sql index 1897531..d59eaaa 100644 --- a/01_lcbp3_v1_4_1.sql +++ b/docs/01_lcbp3_v1_4_1.sql @@ -1,2294 +1,2418 @@ --- ========================================================== --- DMS v1.4.0 Document Management System Database --- Deploy Script Schema --- Server: Container Station on QNAPQNAP TS-473A --- Database service: MariaDB 10.11 --- database web ui: phpmyadmin 5-apache --- database deelopment ui: DBeaver --- backend sevice: NestJS --- frontend sevice: next.js --- reverse proxy: jc21/nginx-proxy-manager:latest --- cron service: n8n --- DMS v1.4.1 Improvements --- Update: first revise fron v1.3.0 (GLM-4.6 & Gemini) --- Update: revise fron v1.4.0 (deepseek) --- ========================================================== -SET NAMES utf8mb4; -SET time_zone = '+07:00'; --- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด -SET FOREIGN_KEY_CHECKS = 0; -DROP VIEW IF EXISTS v_document_statistics; -DROP VIEW IF EXISTS v_documents_with_attachments; -DROP VIEW IF EXISTS v_user_all_permissions; -DROP VIEW IF EXISTS v_audit_log_details; -DROP VIEW IF EXISTS v_user_tasks; -DROP VIEW IF EXISTS v_contract_parties_all; -DROP VIEW IF EXISTS v_current_rfas; -DROP VIEW IF EXISTS v_current_correspondences; -DROP PROCEDURE IF EXISTS sp_get_next_document_number; --- 🗑️ Drop all tables in reverse dependency order --- ส่วนที่ 1: ตาราง System & Utility (ไม่ค่อยมีการอ้างอิง หรือเป็นตารางสุดท้ายในสายงาน) -DROP TABLE IF EXISTS backup_logs; -DROP TABLE IF EXISTS search_indices; -DROP TABLE IF EXISTS notifications; -DROP TABLE IF EXISTS audit_logs; --- ส่วนที่ 2: ตารางที่เชื่อมโยงกับเอกสารและข้อมูลหลัก (Junction Tables) -DROP TABLE IF EXISTS correspondence_tags; -DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; -DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; --- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments & Their Junctions) -DROP TABLE IF EXISTS contract_drawing_attachments; -DROP TABLE IF EXISTS circulation_attachments; -DROP TABLE IF EXISTS shop_drawing_revision_attachments; -DROP TABLE IF EXISTS correspondence_attachments; -DROP TABLE IF EXISTS attachments; --- ส่วนที่ 4: ตารางเกี่ยวกับ Workflow และ Template -DROP TABLE IF EXISTS circulation_routings; -DROP TABLE IF EXISTS circulation_template_assignees; -DROP TABLE IF EXISTS circulation_templates; -DROP TABLE IF EXISTS rfa_workflows; -DROP TABLE IF EXISTS rfa_workflow_template_steps; -DROP TABLE IF EXISTS rfa_workflow_templates; --- add -DROP TABLE IF EXISTS correspondence_routings; -DROP TABLE IF EXISTS correspondence_routing_template_steps; -DROP TABLE IF EXISTS correspondence_status_transitions; -DROP TABLE IF EXISTS correspondence_routing_templates; --- ส่วนที่ 5: ตาราง Mapping หลัก (Main Mapping Tables) -DROP TABLE IF EXISTS role_permissions; -DROP TABLE IF EXISTS user_assignments; -DROP TABLE IF EXISTS contract_organizations; -DROP TABLE IF EXISTS project_organizations; --- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Detail/Item Tables) -DROP TABLE IF EXISTS transmittal_items; -DROP TABLE IF EXISTS shop_drawing_revisions; -DROP TABLE IF EXISTS rfa_items; -DROP TABLE IF EXISTS rfa_revisions; -DROP TABLE IF EXISTS correspondence_references; -DROP TABLE IF EXISTS correspondence_recipients; -DROP TABLE IF EXISTS correspondence_revisions; --- ส่วนที่ 7: ตารางเอกสารหลัก (Core Document Tables) -DROP TABLE IF EXISTS circulations; -DROP TABLE IF EXISTS transmittals; -DROP TABLE IF EXISTS contract_drawings; -DROP TABLE IF EXISTS shop_drawings; -DROP TABLE IF EXISTS rfas; -DROP TABLE IF EXISTS correspondences; --- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Categories & Master Data) -DROP TABLE IF EXISTS shop_drawing_sub_categories; -DROP TABLE IF EXISTS shop_drawing_main_categories; -DROP TABLE IF EXISTS contract_drawing_sub_cats; -DROP TABLE IF EXISTS contract_drawing_cats; -DROP TABLE IF EXISTS contract_drawing_volumes; -DROP TABLE IF EXISTS circulation_status_codes; -DROP TABLE IF EXISTS rfa_approve_codes; -DROP TABLE IF EXISTS rfa_status_codes; -DROP TABLE IF EXISTS rfa_types; -DROP TABLE IF EXISTS correspondence_status; -DROP TABLE IF EXISTS correspondence_types; -DROP TABLE IF EXISTS document_number_counters; -DROP TABLE IF EXISTS document_number_formats; -DROP TABLE IF EXISTS tags; --- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างหลัก (Users, Roles & Core Structure) -DROP TABLE IF EXISTS organization_roles; -DROP TABLE IF EXISTS roles; -DROP TABLE IF EXISTS permissions; -DROP TABLE IF EXISTS contracts; -DROP TABLE IF EXISTS projects; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS organizations; --- ===================================================== --- 1. 🏢 Core & Master Data (องค์กร, โครงการ, สัญญา) --- ===================================================== --- ตาราง Master เก็บประเภทบทบาทขององค์กร -CREATE TABLE organization_roles ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'ชื่อบทบาท (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทบทบาทขององค์กร'; --- ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ -CREATE TABLE organizations ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสองค์กร', - organization_name VARCHAR(255) NOT NULL COMMENT 'ชื่อองค์กร', - -- role_id INT COMMENT 'บทบาทขององค์กร', - is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ'; --- Seed organization -INSERT INTO organizations (id, organization_code, organization_name) -VALUES (1, 'กทท.', 'การท่าเรือแห่งประเทศไทย'), - ( - 10, - 'สคฉ.3', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3' - ), - ( - 11, - 'สคฉ.3-01', - 'ตรวจรับพัสดุ ที่ปรึกษาควบคุมงาน' - ), - (12, 'สคฉ.3-02', 'ตรวจรับพัสดุ งานทางทะเล'), - ( - 13, - 'สคฉ.3-03', - 'ตรวจรับพัสดุ อาคารและระบบสาธารณูปโภค' - ), - ( - 14, - 'สคฉ.3-04', - 'ตรวจรับพัสดุ ตรวจสอบผลกระทบสิ่งแวดล้อม' - ), - (15, 'สคฉ.3-05', 'ตรวจรับพัสดุ เยียวยาการประมง'), - ( - 16, - 'สคฉ.3-06', - 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 3' - ), - ( - 17, - 'สคฉ.3-07', - 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 4' - ), - ( - 18, - 'สคฉ.3-xx', - 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4' - ), - (21, 'TEAM', 'Designer Consulting Ltd.'), - (22, 'คคง.', 'Construction Supervision Ltd.'), - (41, 'ผรม.1', 'Contractor งานทางทะเล'), - (42, 'ผรม.2', 'Contractor อาคารและระบบ'), - (43, 'ผรม.3', 'Contractor #3 Ltd.'), - (44, 'ผรม.4', 'Contractor #4 Ltd.'), - (31, 'EN', 'Third Party Environment'), - (32, 'CAR', 'Third Party Fishery Care'); --- ตาราง Master เก็บข้อมูลโครงการ -CREATE TABLE projects ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสโครงการ', - project_name VARCHAR(255) NOT NULL COMMENT 'ชื่อโครงการ', - -- parent_project_id INT COMMENT 'รหัสโครงการหลัก (ถ้ามี)', - -- contractor_organization_id INT COMMENT 'รหัสองค์กรผู้รับเหมา (ถ้ามี)', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, - -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลโครงการ'; -INSERT INTO projects (project_code, project_name) -VALUES ( - 'LCBP3', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)' - ), - ( - 'LCBP3C1', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล' - ), - ( - 'LCBP3C2', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค' - ), - ( - 'LCBP3C3', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง' - ), - ( - 'LCBP3C4', - 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง' - ); --- ตาราง Master เก็บข้อมูลสัญญา -CREATE TABLE contracts ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL, - contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสัญญา', - contract_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสัญญา', - description TEXT COMMENT 'คำอธิบายสัญญา', - start_date DATE COMMENT 'วันที่เริ่มสัญญา', - end_date DATE COMMENT 'วันที่สิ้นสุดสัญญา', - is_active BOOLEAN DEFAULT TRUE, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลสัญญา'; --- ใช้ Subquery เพื่อดึง project_id มาเชื่อมโยง ทำให้ไม่ต้องมานั่งจัดการ ID ด้วยตัวเอง -INSERT INTO contracts ( - contract_code, - contract_name, - project_id, - is_active - ) -VALUES ( - 'DSLCBP3', - 'งานจ้างที่ปรีกษาออกแบบ โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3' - ), - TRUE - ), - ( - 'PSLCBP3', - 'งานจ้างที่ปรีกษาควบคุมงาน โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3' - ), - TRUE - ), - ( - 'LCBP3-C1', - 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C1' - ), - TRUE - ), - ( - 'LCBP3-C2', - 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C2' - ), - TRUE - ), - ( - 'LCBP3-C3', - 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C3' - ), - TRUE - ), - ( - 'LCBP3-C4', - 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C4' - ), - TRUE - ), - ( - 'ENLCBP3', - 'งานจ้างเหมาตรวจสอบผลกระทบสิ่งแวดล้อมนะหว่างงานก่อสร้างโครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', - ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3' - ), - TRUE - ); --- ===================================================== --- 2. 👥 Users & RBAC (ผู้ใช้, สิทธิ์, บทบาท) --- ===================================================== --- ตาราง Master เก็บข้อมูลผู้ใช้งาน (User) -CREATE TABLE users ( - user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - username VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อผู้ใช้งาน', - password_hash VARCHAR(255) NOT NULL COMMENT 'รหัสผ่าน (Hashed)', - first_name VARCHAR(50) COMMENT 'ชื่อจริง', - last_name VARCHAR(50) COMMENT 'นามสกุล', - email VARCHAR(100) NOT NULL UNIQUE COMMENT 'อีเมล', - line_id VARCHAR(100) COMMENT 'LINE ID', - primary_organization_id INT COMMENT 'สังกัดองค์กร', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - failed_attempts INT DEFAULT 0 COMMENT 'จำนวนครั้งที่ล็อกอินล้มเหลว', - locked_until DATETIME COMMENT 'ล็อกอินไม่ได้จนถึงเวลา', - last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE - SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)'; --- Initial SUPER_ADMIN user -INSERT INTO users (username, password_hash, email, is_active) -VALUES ( - 'superadmin', - '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', - 'superadmin@example.com', - 1 - ) ON DUPLICATE KEY -UPDATE email = -VALUES(email), - is_active = -VALUES(is_active); --- Create editor01 user -INSERT IGNORE INTO users (username, password_hash, email, is_active) -VALUES ( - 'editor01', - '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', - 'editor01@example.com', - 1 - ); --- Create viewer01 user (password hash placeholder, must change later) -INSERT IGNORE INTO users (username, password_hash, email, is_active) -VALUES ( - 'viewer01', - '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', - 'viewer01@example.com', - 1 - ); --- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ -CREATE TABLE roles ( - role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', - role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท', - scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, - -- ขอบเขตของบทบาท (จากข้อ 4.3) - description TEXT COMMENT 'คำอธิบายบทบาท', - is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = บทบาทของระบบ ลบไม่ได้)' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ'; --- ========================================================== --- Seed Roles (บทบาทพื้นฐาน 5 บทบาท ตาม Req 4.3) --- ========================================================== --- 1. Superadmin (Global) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 1, - 'Superadmin', - 'Global', - 'ผู้ดูแลระบบสูงสุด: สามารถทำทุกอย่างในระบบ, จัดการองค์กร, และจัดการข้อมูลหลักระดับ Global' - ); --- 2. Org Admin (Organization) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 2, - 'Org Admin', - 'Organization', - 'ผู้ดูแลองค์กร: จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, และดูรายงานขององค์กร' - ); --- 3. Document Control (Organization) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 3, - 'Document Control', - 'Organization', - 'ควบคุมเอกสารขององค์กร: เพิ่ม/แก้ไข/ลบเอกสาร, และกำหนดสิทธิ์เอกสารภายในองค์กร' - ); --- 4. Editor (Organization) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 4, - 'Editor', - 'Organization', - 'ผู้แก้ไขเอกสารขององค์กร: เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย' - ); --- 5. Viewer (Organization) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 5, - 'Viewer', - 'Organization', - 'ผู้ดูเอกสารขององค์กร: ดูเอกสารที่มีสิทธิ์เข้าถึงเท่านั้น' - ); --- 6. Project Manager (Project) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 6, - 'Project Manager', - 'Project', - 'ผู้จัดการโครงการ: จัดการสมาชิกในโครงการ, สร้าง/จัดการสัญญาในโครงการ, และดูรายงานโครงการ' - ); --- 7. Contract Admin (Contract) -INSERT INTO roles (role_id, role_name, scope, description) -VALUES ( - 7, - 'Contract Admin', - 'Contract', - 'ผู้ดูแลสัญญา: จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา, และอนุมัติเอกสารในสัญญา' - ); --- ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ -CREATE TABLE permissions ( - permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัสสิทธิ์ (เช่น rfas.create, rfas.view)', - description TEXT COMMENT 'คำอธิบายสิทธิ์', - module VARCHAR(50) COMMENT 'โมดูลที่เกี่ยวข้อง', - scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'ระดับขอบเขตของสิทธิ์', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ'; --- ===================================================== --- 2. Seed Permissions (สิทธิ์การใช้งานทั้งหมด) --- สิทธิ์ระดับระบบและการจัดการหลัก (System & Master Data) --- ===================================================== -INSERT INTO permissions (permission_id, permission_name, description) -VALUES ( - 1, - 'system.manage_all', - 'ทำทุกอย่างในระบบ (Superadmin Power)' - ), - -- การจัดการองค์กร - (2, 'organization.create', 'สร้างองค์กรใหม่'), - (3, 'organization.edit', 'แก้ไขข้อมูลองค์กร'), - (4, 'organization.delete', 'ลบองค์กร'), - (5, 'organization.view', 'ดูรายการองค์กร'), - -- การจัดการโครงการ - (6, 'project.create', 'สร้างโครงการใหม่'), - (7, 'project.edit', 'แก้ไขข้อมูลโครงการ'), - (8, 'project.delete', 'ลบโครงการ'), - (9, 'project.view', 'ดูรายการโครงการ'), - -- การจัดการบทบาทและสิทธิ์ (Roles & Permissions) - (10, 'role.create', 'สร้างบทบาท (Role) ใหม่'), - (11, 'role.edit', 'แก้ไขบทบาท (Role)'), - (12, 'role.delete', 'ลบบทบาท (Role)'), - ( - 13, - 'permission.assign', - 'มอบสิทธิ์ให้กับบทบาท (Role)' - ), - -- การจัดการข้อมูลหลัก (Master Data) - ( - 14, - 'master_data.document_type.manage', - 'จัดการประเภทเอกสาร (Document Types)' - ), - ( - 15, - 'master_data.document_status.manage', - 'จัดการสถานะเอกสาร (Document Statuses)' - ), - ( - 16, - 'master_data.drawing_category.manage', - 'จัดการหมวดหมู่แบบ (Drawing Categories)' - ), - (17, 'master_data.tag.manage', 'จัดการ Tags'), - -- การจัดการผู้ใช้งาน - (18, 'user.create', 'สร้างผู้ใช้งานใหม่'), - (19, 'user.edit', 'แก้ไขข้อมูลผู้ใช้งาน'), - (20, 'user.delete', 'ลบ/ปิดการใช้งานผู้ใช้'), - (21, 'user.view', 'ดูข้อมูลผู้ใช้งาน'), - ( - 22, - 'user.assign_organization', - 'มอบผู้ใช้งานให้กับองค์กร' - ); --- ===================================================== --- == 2. สิทธิ์การจัดการโครงการและสัญญา (Project & Contract) == --- ===================================================== -INSERT INTO permissions (permission_id, permission_name, description) -VALUES ( - 23, - 'project.manage_members', - 'จัดการสมาชิกในโครงการ (เชิญ/ถอดสมาชิก)' - ), - ( - 24, - 'project.create_contracts', - 'สร้างสัญญาในโครงการ' - ), - ( - 25, - 'project.manage_contracts', - 'จัดการสัญญาในโครงการ' - ), - ( - 26, - 'project.view_reports', - 'ดูรายงานระดับโครงการ' - ), - ( - 27, - 'contract.manage_members', - 'จัดการสมาชิกในสัญญา' - ), - (28, 'contract.view', 'ดูข้อมูลสัญญา'); --- ===================================================== --- == 3. สิทธิ์การจัดการเอกสาร (Document Management) == --- ===================================================== --- สิทธิ์ทั่วไปสำหรับเอกสารทุกประเภท -INSERT INTO permissions (permission_id, permission_name, description) -VALUES ( - 29, - 'document.create_draft', - 'สร้างเอกสารในสถานะฉบับร่าง (Draft)' - ), - (30, 'document.submit', 'ส่งเอกสาร (Submitted)'), - (31, 'document.view', 'ดูเอกสาร'), - (32, 'document.edit', 'แก้ไขเอกสาร (ทั่วไป)'), - ( - 33, - 'document.admin_edit', - 'แก้ไข/ถอน/ยกเลิกเอกสารที่ส่งแล้ว (Admin Power)' - ), - (34, 'document.delete', 'ลบเอกสาร'), - ( - 35, - 'document.attach', - 'จัดการไฟล์แนบ (อัปโหลด/ลบ)' - ), - -- สิทธิ์เฉพาะสำหรับ Correspondence - ( - 36, - 'correspondence.create', - 'สร้างเอกสารโต้ตอบ (Correspondence)' - ), - -- สิทธิ์เฉพาะสำหรับ Request for Approval (RFA) - (37, 'rfa.create', 'สร้างเอกสารขออนุมัติ (RFA)'), - ( - 38, - 'rfa.manage_shop_drawings', - 'จัดการข้อมูล Shop Drawing และ Contract Drawing ที่เกี่ยวข้อง' - ), - -- สิทธิ์เฉพาะสำหรับ Shop Drawing & Contract Drawing - ( - 39, - 'drawing.create', - 'สร้าง/แก้ไขข้อมูลแบบ (Shop/Contract Drawing)' - ), - -- สิทธิ์เฉพาะสำหรับ Transmittal - ( - 40, - 'transmittal.create', - 'สร้างเอกสารนำส่ง (Transmittal)' - ), - -- สิทธิ์เฉพาะสำหรับ Circulation Sheet (ใบเวียน) - ( - 41, - 'circulation.create', - 'สร้างใบเวียนเอกสาร (Circulation)' - ), - ( - 42, - 'circulation.respond', - 'ตอบกลับใบเวียน (Main/Action)' - ), - ( - 43, - 'circulation.acknowledge', - 'รับทราบใบเวียน (Information)' - ), - (44, 'circulation.close', 'ปิดใบเวียน'); --- ===================================================== --- == 4. สิทธิ์การจัดการ Workflow == --- ===================================================== -INSERT INTO permissions (permission_id, permission_name, description) -VALUES ( - 45, - 'workflow.action_review', - 'ดำเนินการในขั้นตอนปัจจุบัน (เช่น ตรวจสอบแล้ว)' - ), - ( - 46, - 'workflow.force_proceed', - 'บังคับไปยังขั้นตอนถัดไป (Document Control Power)' - ), - ( - 47, - 'workflow.revert', - 'ย้อนกลับไปยังขั้นตอนก่อนหน้า (Document Control Power)' - ); --- ===================================================== --- == 5. สิทธิ์ด้านการค้นหาและรายงาน (Search & Reporting) == --- ===================================================== -INSERT INTO permissions (permission_id, permission_name, description) -VALUES (48, 'search.advanced', 'ใช้งานการค้นหาขั้นสูง'), - ( - 49, - 'report.generate', - 'สร้างรายงานสรุป (รายวัน/สัปดาห์/เดือน/ปี)' - ); --- ตารางเชื่อมระหว่าง roles และ permissions (M:N) -CREATE TABLE role_permissions ( - role_id INT COMMENT 'ID ของบทบาท', - permission_id INT COMMENT 'ID ของสิทธิ์', - PRIMARY KEY (role_id, permission_id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, - FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง roles และ permissions (M:N)'; --- ========================================================== --- Seed Role-Permissions Mapping (จับคู่สิทธิ์เริ่มต้น) --- ========================================================== --- Seed data for the 'role_permissions' table --- This table links roles to their specific permissions. --- NOTE: This assumes the role_id and permission_id from the previous seed data files. --- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. --- ===================================================== --- == 1. Superadmin (role_id = 1) - Gets ALL permissions == --- ===================================================== --- Superadmin can do everything. We can dynamically link all permissions to this role. --- This is a robust way to ensure Superadmin always has full power. -INSERT INTO role_permissions (role_id, permission_id) -SELECT 1, - permission_id -FROM permissions; --- ===================================================== --- == 2. Org Admin (role_id = 2) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- จัดการผู้ใช้ในองค์กร - (2, 18), - -- user.create - (2, 19), - -- user.edit - (2, 20), - -- user.delete - (2, 21), - -- user.view - (2, 22), - -- user.assign_organization - -- จัดการองค์กร - (2, 3), - -- organization.edit - (2, 5), - -- organization.view - -- จัดการข้อมูลหลักที่อนุญาต (เฉพาะ Tags) - (2, 17), - -- master_data.tag.manage - -- ดูข้อมูลต่างๆ ในองค์กร - (2, 31), - -- document.view - (2, 9), - -- project.view - (2, 28), - -- contract.view - -- การค้นหาและรายงาน - (2, 48), - -- search.advanced - (2, 49); --- report.generate --- ===================================================== --- == 3. Document Control (role_id = 3) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- สิทธิ์จัดการเอกสารทั้งหมด - (3, 29), - -- document.create_draft - (3, 30), - -- document.submit - (3, 31), - -- document.view - (3, 32), - -- document.edit - (3, 33), - -- document.admin_edit - (3, 34), - -- document.delete - (3, 35), - -- document.attach - -- สิทธิ์สร้างเอกสารแต่ละประเภท - (3, 36), - -- correspondence.create - (3, 37), - -- rfa.create - (3, 39), - -- drawing.create - (3, 40), - -- transmittal.create - (3, 41), - -- circulation.create - -- สิทธิ์จัดการ Workflow - (3, 45), - -- workflow.action_review - (3, 46), - -- workflow.force_proceed - (3, 47), - -- workflow.revert - -- สิทธิ์จัดการ Circulation - (3, 42), - -- circulation.respond - (3, 43), - -- circulation.acknowledge - (3, 44), - -- circulation.close - -- สิทธิ์อื่นๆ ที่จำเป็น - (3, 38), - -- rfa.manage_shop_drawings - (3, 48), - -- search.advanced - (3, 49); --- report.generate --- ===================================================== --- == 4. Editor (role_id = 4) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- สิทธิ์แก้ไขเอกสาร (แต่ไม่ใช่สิทธิ์ Admin) - (4, 29), - -- document.create_draft - (4, 30), - -- document.submit - (4, 31), - -- document.view - (4, 32), - -- document.edit - (4, 35), - -- document.attach - -- สิทธิ์สร้างเอกสารแต่ละประเภท - (4, 36), - -- correspondence.create - (4, 37), - -- rfa.create - (4, 39), - -- drawing.create - (4, 40), - -- transmittal.create - (4, 41), - -- circulation.create - -- สิทธิ์อื่นๆ ที่จำเป็น - (4, 38), - -- rfa.manage_shop_drawings - (4, 48); --- search.advanced --- ===================================================== --- == 5. Viewer (role_id = 5) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- สิทธิ์ดูเท่านั้น - (5, 31), - -- document.view - (5, 48); --- search.advanced --- ===================================================== --- == 6. Project Manager (role_id = 6) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- สิทธิ์จัดการโครงการ - (6, 23), - -- project.manage_members - (6, 24), - -- project.create_contracts - (6, 25), - -- project.manage_contracts - (6, 26), - -- project.view_reports - (6, 9), - -- project.view - -- สิทธิ์จัดการข้อมูลหลักระดับโครงการ - (6, 16), - -- master_data.drawing_category.manage - -- สิทธิ์ดูข้อมูลในสัญญา - (6, 28), - -- contract.view - -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) - (6, 29), - -- document.create_draft - (6, 30), - -- document.submit - (6, 31), - -- document.view - (6, 32), - -- document.edit - (6, 35), - -- document.attach - (6, 36), - -- correspondence.create - (6, 37), - -- rfa.create - (6, 39), - -- drawing.create - (6, 40), - -- transmittal.create - (6, 41), - -- circulation.create - (6, 38), - -- rfa.manage_shop_drawings - (6, 48), - -- search.advanced - (6, 49); --- report.generate --- ===================================================== --- == 7. Contract Admin (role_id = 7) == --- ===================================================== -INSERT INTO role_permissions (role_id, permission_id) -VALUES -- สิทธิ์จัดการสัญญา - (7, 27), - -- contract.manage_members - (7, 28), - -- contract.view - -- สิทธิ์ในการอนุมัติ (ส่วนหนึ่งของ Workflow) - (7, 45), - -- workflow.action_review - -- สิทธิ์จัดการข้อมูลเฉพาะสัญญา - (7, 38), - -- rfa.manage_shop_drawings - (7, 39), - -- drawing.create - -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) - (7, 29), - -- document.create_draft - (7, 30), - -- document.submit - (7, 31), - -- document.view - (7, 32), - -- document.edit - (7, 35), - -- document.attach - (7, 36), - -- correspondence.create - (7, 37), - -- rfa.create - (7, 40), - -- transmittal.create - (7, 41), - -- circulation.create - (7, 48); --- search.advanced --- ตารางเชื่อมผู้ใช้ (users) -CREATE TABLE user_assignments ( - id INT AUTO_INCREMENT PRIMARY KEY, - user_id INT NOT NULL, - role_id INT NOT NULL, - -- คอลัมน์สำหรับกำหนดขอบเขต (จะใช้เพียงอันเดียวต่อแถว) - organization_id INT NULL, - project_id INT NULL, - contract_id INT NULL, - assigned_by_user_id INT, - -- ผู้ที่มอบหมายบทบาทนี้ - assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, - FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id), - -- Constraint เพื่อให้แน่ใจว่ามีเพียงขอบเขตเดียวที่ถูกกำหนดในแต่ละแถว - CONSTRAINT chk_scope CHECK ( - ( - organization_id IS NOT NULL - AND project_id IS NULL - AND contract_id IS NULL - ) - OR ( - organization_id IS NULL - AND project_id IS NOT NULL - AND contract_id IS NULL - ) - OR ( - organization_id IS NULL - AND project_id IS NULL - AND contract_id IS NOT NULL - ) - OR ( - organization_id IS NULL - AND project_id IS NULL - AND contract_id IS NULL - ) -- สำหรับ Global scope - ) -); -CREATE TABLE project_organizations ( - project_id INT NOT NULL, - organization_id INT NOT NULL, - PRIMARY KEY (project_id, organization_id), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE -); -CREATE TABLE contract_organizations ( - contract_id INT NOT NULL, - organization_id INT NOT NULL, - role_in_contract VARCHAR(100), - -- เช่น 'Owner', 'Designer', 'Consultant', 'Contractor' - PRIMARY KEY (contract_id, organization_id), - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE -); --- ===================================================== --- == 4. การเชื่อมโยงโครงการกับองค์กร (project_organizations) == --- ===================================================== --- โครงการหลัก (LCBP3) จะมีองค์กรหลักๆ เข้ามาเกี่ยวข้องทั้งหมด -INSERT INTO project_organizations (project_id, organization_id) -SELECT ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3' - ), - id -FROM organizations -WHERE organization_code IN ( - 'กทท.', - 'สคฉ.3', - 'TEAM', - 'คคง.', - 'ผรม.1', - 'ผรม.2', - 'ผรม.3', - 'ผรม.4', - 'EN', - 'CAR' - ); --- โครงการย่อย (LCBP3C1) จะมีเฉพาะองค์กรที่เกี่ยวข้อง -INSERT INTO project_organizations (project_id, organization_id) -SELECT ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C1' - ), - id -FROM organizations -WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-02', 'คคง.', 'ผรม.1'); --- ทำเช่นเดียวกันสำหรับโครงการอื่นๆ (ตัวอย่าง) -INSERT INTO project_organizations (project_id, organization_id) -SELECT ( - SELECT id - FROM projects - WHERE project_code = 'LCBP3C2' - ), - id -FROM organizations -WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-03', 'คคง.', 'ผรม.2'); --- ===================================================== --- == 5. การเชื่อมโยงสัญญากับองค์กร (contract_organizations) == --- ===================================================== --- สัญญาที่ปรึกษาออกแบบ (DSLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'DSLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'กทท.' - ), - 'Owner' - ), - ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'DSLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'TEAM' - ), - 'Designer' - ); --- สัญญาที่ปรึกษาควบคุมงาน (PSLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'PSLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'กทท.' - ), - 'Owner' - ), - ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'PSLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'คคง.' - ), - 'Consultant' - ); --- สัญญางานก่อสร้าง ส่วนที่ 1 (LCBP3-C1) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'LCBP3-C1' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'กทท.' - ), - 'Owner' - ), - ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'LCBP3-C1' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'ผรม.1' - ), - 'Contractor' - ); --- สัญญางานก่อสร้าง ส่วนที่ 2 (LCBP3-C2) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'LCBP3-C2' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'กทท.' - ), - 'Owner' - ), - ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'LCBP3-C2' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'ผรม.2' - ), - 'Contractor' - ); --- สัญญาตรวจสอบสิ่งแวดล้อม (ENLCBP3) -INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) -VALUES ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'ENLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'กทท.' - ), - 'Owner' - ), - ( - ( - SELECT id - FROM contracts - WHERE contract_code = 'ENLCBP3' - ), - ( - SELECT id - FROM organizations - WHERE organization_code = 'EN' - ), - 'Consultant' - ); --- ===================================================== --- 3. ✉️ Correspondences (เอกสารหลัก, Revisions) --- ===================================================== --- ตาราง Master เก็บประเภทเอกสารโต้ตอบ -CREATE TABLE correspondence_types ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสประเภท (เช่น RFA, RFI)', - type_name VARCHAR(255) NOT NULL COMMENT 'ชื่อประเภท', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทเอกสารโต้ตอบ'; -INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) -VALUES ('RFA', 'Request for Approval', 1, 1), - ('RFI', 'Request for Information', 2, 1), - ('TRANSMITTAL', 'Transmittal', 3, 1), - ('EMAIL', 'Email', 4, 1), - ('INSTRUCTION', 'Instruction', 5, 1), - ('LETTER', 'Letter', 6, 1), - ('MEMO', 'Memorandum', 7, 1), - ('MOM', 'Minutes of Meeting', 8, 1), - ('NOTICE', 'Notice', 9, 1), - ( - 'OTHER', - 'Other', - 10, - 1 - ); --- ตาราง Master เก็บสถานะของเอกสาร -CREATE TABLE correspondence_status ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสถานะหนังสือ (เช่น DRAFT, SUBOWN)', - status_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสถานะหนังสือ', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะของเอกสาร'; -INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) -VALUES ('DRAFT', 'Draft', 10, 1), - ('SUBOWN', 'Submitted to Owner', 21, 1), - ('SUBDSN', 'Submitted to Designer', 22, 1), - ('SUBCSC', 'Submitted to CSC', 23, 1), - ('SUBCON', 'Submitted to Contractor', 24, 1), - ('SUBOTH', 'Submitted to Others', 25, 1), - ('REPOWN', 'Reply by Owner', 31, 1), - ('REPDSN', 'Reply by Designer', 32, 1), - ('REPCSC', 'Reply by CSC', 33, 1), - ('REPCON', 'Reply by Contractor', 34, 1), - ('REPOTH', 'Reply by Others', 35, 1), - ('RSBOWN', 'Resubmited by Owner', 41, 1), - ('RSBDSN', 'Resubmited by Designer', 42, 1), - ('RSBCSC', 'Resubmited by CSC', 43, 1), - ('RSBCON', 'Resubmited by Contractor', 44, 1), - ('CLBOWN', 'Closed by Owner', 51, 1), - ('CLBDSN', 'Closed by Designer', 52, 1), - ('CLBCSC', 'Closed by CSC', 53, 1), - ('CLBCON', 'Closed by Contractor', 54, 1), - ('CCBOWN', 'Canceled by Owner', 91, 1), - ('CCBDSN', 'Canceled by Designer', 92, 1), - ('CCBCSC', 'Canceled by CSC', 93, 1), - ('CCBCON', 'Canceled by Contractor', 94, 1); --- ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision -CREATE TABLE correspondences ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (นี่คือ "Master ID" ที่ใช้เชื่อมโยง)', - correspondence_number VARCHAR(100) NOT NULL COMMENT 'เลขที่เอกสาร (สร้างจาก DocumentNumberingModule)', - correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', - is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = ภายใน, 0 = ภายนอก)', - project_id INT NOT NULL COMMENT 'อยู่ในโครงการ', - originator_id INT COMMENT 'องค์กรผู้ส่ง', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - created_by INT COMMENT 'ผู้สร้าง', - deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE - SET NULL, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE - SET NULL, - UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision'; --- ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N) -CREATE TABLE correspondence_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - correspondence_id INT NOT NULL COMMENT 'Master ID', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', - correspondence_status_id INT NOT NULL COMMENT 'สถานะของ Revision นี้', - title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', - document_date DATE COMMENT 'วันที่ในเอกสาร', - issued_date DATETIME COMMENT 'วันที่ออกเอกสาร', - received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', - due_date DATETIME COMMENT 'วันที่ครบกำหนด', - description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', - details JSON COMMENT 'ข้อมูลเฉพาะ (เช่น RFI details)', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', - created_by INT COMMENT 'ผู้สร้าง', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE - SET NULL, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE - SET NULL, - UNIQUE KEY uq_master_revision_number (correspondence_id, revision_number), - UNIQUE KEY uq_master_current (correspondence_id, is_current) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N)'; --- ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N) -CREATE TABLE correspondence_recipients ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - recipient_organization_id INT COMMENT 'ID องค์กรผู้รับ', - recipient_type ENUM('TO', 'CC') COMMENT 'ประเภทผู้รับ (TO หรือ CC)', - PRIMARY KEY ( - correspondence_id, - recipient_organization_id, - recipient_type - ), - FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N)'; --- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ -CREATE TABLE tags ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อ Tag', - description TEXT COMMENT 'คำอธิบายแท็ก', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ'; --- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) -CREATE TABLE correspondence_tags ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - tag_id INT COMMENT 'ID ของ Tag', - PRIMARY KEY (correspondence_id, tag_id), - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; --- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) -CREATE TABLE correspondence_references ( - src_correspondence_id INT COMMENT 'ID เอกสารต้นทาง', - tgt_correspondence_id INT COMMENT 'ID เอกสารเป้าหมาย', - PRIMARY KEY (src_correspondence_id, tgt_correspondence_id), - FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)'; --- ===================================================== --- 4. 📐 approval: RFA (เอกสารขออนุมัติ, Workflows) --- ===================================================== -CREATE TABLE correspondence_routing_templates ( - id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของแม่แบบ', - template_name VARCHAR(255) NOT NULL COMMENT 'ชื่อแม่แบบ', - description TEXT COMMENT 'คำอธิบาย', - project_id INT NULL COMMENT 'ID โครงการ (ถ้าเป็นแม่แบบเฉพาะโครงการ)', - -- NULL = แม่แบบทั่วไป - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - is_active BOOLEAN DEFAULT TRUE, - UNIQUE KEY ux_routing_template_name_project (template_name, project_id), - CONSTRAINT fk_crt_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'แม่แบบสายงานการส่งต่อเอกสารขออนุมัติ'; -CREATE TABLE correspondence_status_transitions( - type_id INT NOT NULL COMMENT 'ID ของประเภทหนังสือ', - from_status_id INT NOT NULL COMMENT 'ID ของสถานะต้นทาง', - to_status_id INT NOT NULL COMMENT 'ID ของสถานะปลายทาง', - PRIMARY KEY (type_id, from_status_id, to_status_id), - CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(id), - CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_status(id), - CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_status(id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสถานะที่อนุญาตให้เปลี่ยนแปลงได้ตามประเภทหนังสือ'; --- 1.18.1 correspondence_routing_template_steps Table -CREATE TABLE correspondence_routing_template_steps ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - sequence INT NOT NULL COMMENT 'ลำดับขั้นตอน', - to_organization_id INT NOT NULL COMMENT 'ID องค์กรณ์ผู้รับในขั้นตอนนี้', - step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้', - expected_days INT NULL, - UNIQUE KEY ux_cor_template_sequence (template_id, sequence), - CONSTRAINT fk_cwts_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE CASCADE, - CONSTRAINT fk_cwts_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ขั้นตอนในแม่แบบ Workflow การส่งต่อเอกสาร'; --- 1.19.1 correspondence_routing_steps Table -CREATE TABLE correspondence_routings ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', - correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร(FK -> correspondence_revisions)', - template_id INT NULL COMMENT 'ID ของแม่แบบที่ใช้ (ถ้ามี)', - -- สำหรับอ้างอิงถึงแม่แบบ - sequence INT NOT NULL COMMENT 'ลำดับของขั้นตอนการส่งต่อ', - from_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้ส่ง', - to_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้รับ', - step_purpose ENUM( - 'FOR_APPROVAL', - 'FOR_REVIEW', - 'FOR_INFORMATION', - 'FOR_ACTION' - ) NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ', - status ENUM( - 'SENT', - 'RECEIVED', - 'ACTIONED', - 'FORWARDED', - 'REPLIED' - ) NOT NULL DEFAULT 'SENT' COMMENT 'สถานะการดำเนินการของเอกสารในขั้นตอนนี้', - comments TEXT COMMENT 'หมายเหตุ หรือความคิดเห็นในการส่งต่อ', - due_date DATETIME NULL COMMENT 'วันที่ต้องตอบเอกสารในขั้นตอนนี้', - processed_by_user_id INT NULL COMMENT 'ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้', - processed_at TIMESTAMP NULL COMMENT 'เวลาที่ดำเนินการ', - created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่สร้างขั้นตอนนี้', - UNIQUE KEY ux_cor_routing_sequence (correspondence_id, sequence), - -- Foreign Keys - CONSTRAINT fk_crs_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, - CONSTRAINT fk_crs_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE - SET NULL, - CONSTRAINT fk_crs_from_org FOREIGN KEY (from_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - CONSTRAINT fk_crs_to_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - CONSTRAINT fk_crs_processed_by_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id) ON DELETE - SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางติดตาม Workflow การส่งต่อเอกสารทั่วไป'; --- ตาราง Master สำหรับประเภท RFA -CREATE TABLE rfa_types ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - type_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภท RFA (เช่น DWG, DOC, MAT)', - type_name VARCHAR(100) NOT NULL COMMENT 'ชื่อประเภท RFA', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับประเภท RFA'; -INSERT INTO rfa_types (type_code, type_name, sort_order, is_active) -VALUES ('DWG', 'Shop Drawing', 10, 1), - ('DOC', 'Document', 20, 1), - ('SPC', 'Specification', 21, 1), - ('CAL', 'Calculation', 22, 1), - ('TRP', 'Test Report', 23, 1), - ('SRY', 'Survey Report', 24, 1), - ('QAQC', 'QA/QC Document', 25, 1), - ('MES', 'Method Statement', 30, 1), - ('MAT', 'Material', 40, 1), - ('ASB', 'As-Built', 50, 1), - ('OTH', 'Other', 99, 1); --- ตาราง Master สำหรับสถานะ RFA -CREATE TABLE rfa_status_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะ RFA (เช่น DFT - Draft, FAP - For Approve)', - status_name VARCHAR(100) NOT NULL COMMENT 'ชื่อสถานะ', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับสถานะ RFA'; -INSERT INTO rfa_status_codes ( - status_code, - status_name, - description, - sort_order - ) -VALUES ('DFT', 'Draft', 'ฉบับร่าง', 1), - ('FAP', 'For Approve', 'เพื่อขออนุมัติ', 11), - ('FRE', 'For Review', 'เพื่อตรวจสอบ', 12), - ('FCO', 'For Construction', 'เพื่อก่อสร้าง', 20), - ('ASB', 'AS-Built', 'แบบก่อสร้างจริง', 30), - ('OBS', 'Obsolete', 'ไม่ใช้งาน', 80), - ('CC', 'Canceled', 'ยกเลิก', 99); --- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA -CREATE TABLE rfa_approve_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)', - approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับรหัสผลการอนุมัติ RFA'; -INSERT INTO rfa_approve_codes ( - approve_code, - approve_name, - sort_order, - is_active - ) -VALUES ('1A', 'Approved by Authority', 10, 1), - ('1C', 'Approved by CSC', 11, 1), - ('1N', 'Approved As Note', 12, 1), - ('1R', 'Approved with Remarks', 13, 1), - ('3C', 'Consultant Comments', 31, 1), - ('3R', 'Revise and Resubmit', 32, 1), - ('4X', 'Reject', 40, 1), - ('5N', 'No Further Action', 50, 1); --- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions) -CREATE TABLE rfas ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (RFA Master ID)', - rfa_type_id INT NOT NULL COMMENT 'ประเภท RFA', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - created_by INT COMMENT 'ผู้สร้าง', - deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', - FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE - SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)'; --- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N) -CREATE TABLE rfa_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - correspondence_id INT NOT NULL COMMENT 'Master ID ของ Correspondence', - rfa_id INT NOT NULL COMMENT 'Master ID ของ RFA', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', - rfa_status_code_id INT NOT NULL COMMENT 'สถานะ RFA', - rfa_approve_code_id INT COMMENT 'ผลการอนุมัติ', - title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', - document_date DATE COMMENT 'วันที่ในเอกสาร', - issued_date DATE COMMENT 'วันที่ส่งขออนุมัติ', - received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', - approved_date DATE COMMENT 'วันที่อนุมัติ', - description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', - created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', - created_by INT COMMENT 'ผู้สร้าง', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, - FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), - FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE - SET NULL, - FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE - SET NULL, - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE - SET NULL, - UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), - UNIQUE KEY uq_rr_current (rfa_id, is_current) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N)'; --- ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N) -CREATE TABLE rfa_items ( - rfarev_correspondence_id INT COMMENT 'ID ของ RFA Revision', - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - PRIMARY KEY ( - rfarev_correspondence_id, - shop_drawing_revision_id - ), - FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N)'; --- ตาราง Master เก็บแม่แบบสายอนุมัติ -CREATE TABLE rfa_workflow_templates ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายอนุมัติ', - description TEXT COMMENT 'คำอธิบาย', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายอนุมัติ'; --- ตารางลูก เก็บขั้นตอนในแม่แบบ -CREATE TABLE rfa_workflow_template_steps ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - role_id INT COMMENT 'บทบาทที่รับผิดชอบ', - action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', - duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', - is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', - FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; --- ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน -CREATE TABLE rfa_workflows ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - rfa_revision_id INT NOT NULL COMMENT 'ID ของ RFA Revision', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', - action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', - status ENUM( - 'PENDING', - 'IN_PROGRESS', - 'COMPLETED', - 'REJECTED' - ) COMMENT 'สถานะ', - comments TEXT COMMENT 'ความคิดเห็น', - completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (assigned_to) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน'; --- ===================================================== --- 5. 📐 Drawings (แบบ, หมวดหมู่) --- ===================================================== --- ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_volumes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - volume_code VARCHAR(50) NOT NULL COMMENT 'รหัสเล่ม', - volume_name VARCHAR(255) NOT NULL COMMENT 'ชื่อเล่ม', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_volume_project (project_id, volume_code) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา'; --- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_cats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่หลัก', - cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_cat_project (project_id, cat_code) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา'; --- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา -CREATE TABLE contract_drawing_sub_cats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - sub_cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่ย่อย', - sub_cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา'; --- ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N) -CREATE TABLE contract_drawing_subcat_cat_maps ( - project_id INT COMMENT 'ID ของโครงการ', - sub_cat_id INT COMMENT 'ID ของหมวดหมู่ย่อย', - cat_id INT COMMENT 'ID ของหมวดหมู่หลัก', - PRIMARY KEY (project_id, sub_cat_id, cat_id), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, - FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N)'; --- ตาราง Master เก็บข้อมูล "แบบคู่สัญญา" -CREATE TABLE contract_drawings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - condwg_no VARCHAR(255) NOT NULL COMMENT 'เลขที่แบบสัญญา', - title VARCHAR(255) NOT NULL COMMENT 'ชื่อแบบสัญญา', - sub_cat_id INT COMMENT 'หมวดหมู่ย่อย', - volume_id INT COMMENT 'เล่ม', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - deleted_at DATETIME NULL COMMENT 'วันที่ลบ', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, - FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, - UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบคู่สัญญา"'; --- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง -CREATE TABLE shop_drawing_main_categories ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่หลัก (เช่น ARCH, STR)', - main_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง'; --- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง -CREATE TABLE shop_drawing_sub_categories ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ย่อย (เช่น STR-COLUMN)', - sub_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', - main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', - description TEXT COMMENT 'คำอธิบาย', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง'; --- ตาราง Master เก็บข้อมูล "แบบก่อสร้าง" -CREATE TABLE shop_drawings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'เลขที่ Shop Drawing', - title VARCHAR(500) NOT NULL COMMENT 'ชื่อแบบ', - main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', - sub_category_id INT NOT NULL COMMENT 'หมวดหมู่ย่อย', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - deleted_at DATETIME NULL COMMENT 'วันที่ลบ', - updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id), - FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id), - FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบก่อสร้าง"'; --- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N) -CREATE TABLE shop_drawing_revisions ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', - shop_drawing_id INT NOT NULL COMMENT 'Master ID', - revision_number INT NOT NULL COMMENT 'หมายเลข Revision (เช่น 0, 1, 2...)', - revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', - revision_date DATE COMMENT 'วันที่ของ Revision', - description TEXT COMMENT 'คำอธิบายการแก้ไข', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, - UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N)'; --- ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N) -CREATE TABLE shop_drawing_revision_contract_refs ( - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', - PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id), - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N)'; --- ===================================================== --- 6. 🔄 Circulations (ใบเวียนภายใน) --- ===================================================== --- ตาราง Master เก็บสถานะใบเวียน -CREATE TABLE circulation_status_codes ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะการดำเนินงาน', - description VARCHAR(50) NOT NULL COMMENT 'คำอธิบายสถานะการดำเนินงาน', - sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะใบเวียน'; -INSERT INTO circulation_status_codes (code, description, sort_order) -VALUES ('OPEN', 'Open', 1), - ('IN_REVIEW', 'In Review', 2), - ('COMPLETED', 'ปCompleted', 3), - ('CANCELLED', 'Cancelled/Withdrawn', 9); --- ตาราง "แม่" ของใบเวียนเอกสารภายใน -CREATE TABLE circulations ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตารางใบเวียน', - correspondence_id INT UNIQUE COMMENT 'ID ของเอกสาร (จากตาราง correspondences)', - organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ที่เป็นเจ้าของใบเวียนนี้', - circulation_no VARCHAR(100) NOT NULL COMMENT 'เลขที่ใบเวียน', - circulation_subject VARCHAR(500) NOT NULL COMMENT 'เรื่องใบเวียน', - circulation_status_code VARCHAR(20) NOT NULL COMMENT 'รหัสสถานะใบเวียน', - created_by_user_id INT NOT NULL COMMENT 'ID ของผู้สร้างใบเวียน', - submitted_at TIMESTAMP NULL COMMENT 'วันที่ส่งใบเวียน', - closed_at TIMESTAMP NULL COMMENT 'วันที่ปิดใบเวียน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id), - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code), - FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของใบเวียนเอกสารภายใน'; --- ตาราง Master เก็บแม่แบบสายงาน -CREATE TABLE circulation_templates ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายงาน', - description TEXT COMMENT 'คำอธิบาย', - organization_id INT NOT NULL COMMENT 'องค์กรเจ้าของแม่แบบ', - is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (organization_id) REFERENCES organizations(id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายงาน'; --- ตารางลูก เก็บขั้นตอนในแม่แบบ -CREATE TABLE circulation_template_assignees ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - role_id INT COMMENT 'บทบาทที่รับผิดชอบ', - duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', - is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', - FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (role_id) REFERENCES roles(role_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; --- ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow -CREATE TABLE circulation_routings ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - circulation_id INT NOT NULL COMMENT 'ID ของใบเวียน', - step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', - organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', - assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', - status ENUM( - 'PENDING', - 'IN_PROGRESS', - 'COMPLETED', - 'REJECTED' - ) COMMENT 'สถานะ', - comments TEXT COMMENT 'ความคิดเห็น', - completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, - FOREIGN KEY (organization_id) REFERENCES organizations(id), - FOREIGN KEY (assigned_to) REFERENCES users(user_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow'; --- ===================================================== --- 7. 📤 Transmittals (เอกสารนำส่ง) --- ===================================================== --- ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences) -CREATE TABLE transmittals ( - correspondence_id INT PRIMARY KEY COMMENT 'ID ของเอกสาร', - purpose ENUM( - 'FOR_APPROVAL', - 'FOR_INFORMATION', - 'FOR_REVIEW', - 'OTHER' - ) COMMENT 'วัตถุประสงค์', - remarks TEXT COMMENT 'หมายเหตุ', - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences)'; --- ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N) -CREATE TABLE transmittal_items ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของรายการ', - transmittal_id INT NOT NULL COMMENT 'ID ของ Transmittal', - item_correspondence_id INT NOT NULL COMMENT 'ID ของเอกสารที่แนบไป', - quantity INT DEFAULT 1 COMMENT 'จำนวน', - remarks VARCHAR(255) COMMENT 'หมายเหตุสำหรับรายการนี้', - FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, - FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N)'; --- ===================================================== --- 8. 📎 File Management (ไฟล์แนบ) --- ===================================================== --- ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ -CREATE TABLE attachments ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของไฟล์แนบ', - original_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ดั้งเดิมตอนอัปโหลด', - stored_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ที่เก็บจริงบน Server (ป้องกันชื่อซ้ำ)', - file_path VARCHAR(500) NOT NULL COMMENT 'Path ที่เก็บไฟล์ (บน QNAP /share/dms-data/)', - mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application/pdf)', - file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)', - uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด', - FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ'; --- ตารางเชื่อม correspondences กับ attachments (M:N) -CREATE TABLE correspondence_attachments ( - correspondence_id INT COMMENT 'ID ของเอกสาร', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (correspondence_id, attachment_id), - FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม correspondences กับ attachments (M:N)'; --- ตารางเชื่อม circulations กับ attachments (M:N) -CREATE TABLE circulation_attachments ( - circulation_id INT COMMENT 'ID ของใบเวียน', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลักของใบเวียน)', - PRIMARY KEY (circulation_id, attachment_id), - FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม circulations กับ attachments (M:N)'; --- ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N) -CREATE TABLE shop_drawing_revision_attachments ( - shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (shop_drawing_revision_id, attachment_id), - FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N)'; --- ตารางเชื่อม contract_drawings กับ attachments (M:N) -CREATE TABLE contract_drawing_attachments ( - contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', - attachment_id INT COMMENT 'ID ของไฟล์แนบ', - file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', - is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', - PRIMARY KEY (contract_drawing_id, attachment_id), - FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE, - FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม contract_drawings กับ attachments (M:N)'; --- ===================================================== --- 9. 🔢 Document Numbering (การสร้างเลขที่เอกสาร) --- ===================================================== --- ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร -CREATE TABLE document_number_formats ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', - project_id INT NOT NULL COMMENT 'โครงการ', - correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', - format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', - description TEXT COMMENT 'คำอธิบายรูปแบบนี้', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, - UNIQUE KEY uk_project_type (project_id, correspondence_type_id) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; --- ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด -CREATE TABLE document_number_counters ( - project_id INT COMMENT 'โครงการ', - originator_organization_id INT COMMENT 'องค์กรผู้ส่ง', - correspondence_type_id INT COMMENT 'ประเภทเอกสาร', - current_year INT COMMENT 'ปี ค.ศ. ของตัวนับ', - last_number INT DEFAULT 0 COMMENT 'เลขที่ล่าสุดที่ใช้ไปแล้ว', - PRIMARY KEY ( - project_id, - originator_organization_id, - correspondence_type_id, - current_year - ), - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, - FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, - FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด'; --- ===================================================== --- 10. ⚙️ System & Logs (ระบบและ Log) --- ===================================================== --- ตารางเก็บบันทึกการกระทำของผู้ใช้ -CREATE TABLE audit_logs ( - audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log', - user_id INT COMMENT 'ผู้กระทำ', - action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)', - entity_type VARCHAR(50) COMMENT 'ตาราง/โมดูล (เช่น ''rfa'', ''correspondence'')', - entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ', - details_json JSON COMMENT 'ข้อมูลบริบท', - ip_address VARCHAR(45) COMMENT 'IP Address', - user_agent VARCHAR(255) COMMENT 'User Agent', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ', - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE - SET NULL -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บบันทึกการกระทำของผู้ใช้'; --- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System) -CREATE TABLE notifications ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน', - user_id INT NOT NULL COMMENT 'ID ผู้ใช้', - title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน', - message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน', - notification_type ENUM('EMAIL', 'LINE', 'SYSTEM') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)', - is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน', - entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''', - entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)'; --- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search) -CREATE TABLE search_indices ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี', - entity_type VARCHAR(50) NOT NULL COMMENT 'ชนิดเอนทิตี (เช่น ''correspondence'', ''rfa'')', - entity_id INT NOT NULL COMMENT 'ID ของเอนทิตี', - content TEXT NOT NULL COMMENT 'เนื้อหาที่จะค้นหา', - indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง/อัปเดตัชนี' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)'; --- ตารางสำหรับบันทึกประวัติการสำรองข้อมูล -CREATE TABLE backup_logs ( - id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการสำรอง', - backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', - backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', - file_size BIGINT COMMENT 'ขนาดไฟล์', - status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', - started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', - completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', - error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับบันทึกประวัติการสำรองข้อมูล'; --- ===================================================== --- CREATE INDEXES --- ===================================================== --- Indexes for document_number_formats -CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); -CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); -CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); --- Indexes for document_number_counters -CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); -CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); -CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); -CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); --- Indexes for tags -CREATE INDEX idx_tags_name ON tags(tag_name); -CREATE INDEX idx_tags_created_at ON tags(created_at); --- Indexes for correspondence_tags -CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); -CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); --- Indexes for audit_logs -CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); -CREATE INDEX idx_audit_logs_action ON audit_logs(action); -CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); -CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); -CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); --- Indexes for notifications -CREATE INDEX idx_notifications_user ON notifications(user_id); -CREATE INDEX idx_notifications_type ON notifications(notification_type); -CREATE INDEX idx_notifications_read ON notifications(is_read); -CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); -CREATE INDEX idx_notifications_created_at ON notifications(created_at); --- Indexes for search_indices -CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); -CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); --- Indexes for backup_logs -CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); -CREATE INDEX idx_backup_logs_status ON backup_logs(status); -CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); -CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); --- ===================================================== --- Additional Composite Indexes for Performance --- ===================================================== --- Composite index for document_number_counters for faster lookups -CREATE INDEX idx_doc_counter_composite ON document_number_counters( - project_id, - originator_organization_id, - correspondence_type_id, - current_year -); --- Composite index for notifications for user-specific queries -CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); --- Composite index for audit_logs for reporting -CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); --- Composite index for search_indices for entity-based queries -CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); --- ===================================================== --- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB --- Generated from Data Dictionary --- ===================================================== --- ===================================================== --- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์) --- ===================================================== --- Stored Procedure ดึงเลขที่เอกสารถัดไป -DELIMITER // CREATE PROCEDURE sp_get_next_document_number( - IN p_project_id INT, - IN p_originator_organization_id INT, - IN p_correspondence_type_id INT, - IN p_current_year INT, - OUT p_next_number INT -) BEGIN -DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN -- หากเกิดข้อผิดพลาด ให้ยกเลิก Transaction และส่ง Error กลับไป - ROLLBACK; -END; -START TRANSACTION; --- ล็อกแถวเพื่อป้องกัน Race Condition -SELECT last_number INTO p_next_number -FROM document_number_counters -WHERE project_id = p_project_id - AND originator_organization_id = p_originator_organization_id - AND correspondence_type_id = p_correspondence_type_id - AND current_year = p_current_year FOR -UPDATE; --- ถ้าไม่พบ record ให้สร้างใหม่ -IF p_next_number IS NULL THEN -SET p_next_number = 1; -INSERT INTO document_number_counters ( - project_id, - originator_organization_id, - correspondence_type_id, - current_year, - last_number - ) -VALUES ( - p_project_id, - p_originator_organization_id, - p_correspondence_type_id, - p_current_year, - p_next_number - ); -ELSE -- อัพเดทเลขที่ล่าสุด -SET p_next_number = p_next_number + 1; -UPDATE document_number_counters -SET last_number = p_next_number -WHERE project_id = p_project_id - AND originator_organization_id = p_originator_organization_id - AND correspondence_type_id = p_correspondence_type_id - AND current_year = p_current_year; -END IF; -COMMIT; -END // DELIMITER; --- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA) -CREATE VIEW v_current_correspondences AS -SELECT c.id AS correspondence_id, - c.correspondence_number, - c.correspondence_type_id, - ct.type_code AS correspondence_type_code, - ct.type_name AS correspondence_type_name, - c.project_id, - p.project_code, - p.project_name, - c.originator_id, - org.organization_code AS originator_code, - org.organization_name AS originator_name, - cr.id AS revision_id, - cr.revision_number, - cr.revision_label, - cr.title, - cr.document_date, - cr.issued_date, - cr.received_date, - cr.due_date, - cr.correspondence_status_id, - cs.status_code, - cs.status_name, - cr.created_by, - u.username AS created_by_username, - cr.created_at AS revision_created_at -FROM correspondences c - INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id - INNER JOIN projects p ON c.project_id = p.id - LEFT JOIN organizations org ON c.originator_id = org.id - INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id - INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id - LEFT JOIN users u ON cr.created_by = u.user_id -WHERE cr.is_current = TRUE - AND c.correspondence_type_id NOT IN ( - SELECT id - FROM correspondence_types - WHERE type_code = 'RFA' - ) - AND c.deleted_at IS NULL; --- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด -CREATE VIEW v_current_rfas AS -SELECT r.id AS rfa_id, - r.rfa_type_id, - rt.type_code AS rfa_type_code, - rt.type_name AS rfa_type_name, - rr.correspondence_id, - c.correspondence_number, - c.project_id, - p.project_code, - p.project_name, - c.originator_id, - org.organization_name AS originator_name, - rr.id AS revision_id, - rr.revision_number, - rr.revision_label, - rr.title, - rr.document_date, - rr.issued_date, - rr.received_date, - rr.approved_date, - rr.rfa_status_code_id, - rsc.status_code AS rfa_status_code, - rsc.status_name AS rfa_status_name, - rr.rfa_approve_code_id, - rac.approve_code AS rfa_approve_code, - rac.approve_name AS rfa_approve_name, - rr.created_by, - u.username AS created_by_username, - rr.created_at AS revision_created_at -FROM rfas r - INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id - INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id - INNER JOIN correspondences c ON rr.correspondence_id = c.id - INNER JOIN projects p ON c.project_id = p.id - INNER JOIN organizations org ON c.originator_id = org.id - INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id - LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id - LEFT JOIN users u ON rr.created_by = u.user_id -WHERE rr.is_current = TRUE - AND r.deleted_at IS NULL - AND c.deleted_at IS NULL; --- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization -CREATE VIEW v_contract_parties_all AS -SELECT c.id AS contract_id, - c.contract_code, - c.contract_name, - p.id AS project_id, - p.project_code, - p.project_name, - o.id AS organization_id, - o.organization_code, - o.organization_name, - co.role_in_contract -FROM contracts c - INNER JOIN projects p ON c.project_id = p.id - INNER JOIN contract_organizations co ON c.id = co.contract_id - INNER JOIN organizations o ON co.organization_id = o.id -WHERE c.is_active = TRUE; --- View แสดงรายการ "งานของฉัน" (My Tasks) ที่ยังไม่เสร็จ -CREATE VIEW v_user_tasks AS -SELECT cr.id AS routing_id, - c.id AS circulation_id, - c.circulation_no, - c.circulation_subject, - c.correspondence_id, - corr.correspondence_number, - corr.project_id, - p.project_code, - p.project_name, - cr.assigned_to AS user_id, - u.username, - u.first_name, - u.last_name, - cr.organization_id, - org.organization_name, - cr.step_number, - cr.status AS task_status, - cr.comments, - cr.completed_at, - cr.created_at AS assigned_at, - c.created_at AS circulation_created_at -FROM circulation_routings cr - INNER JOIN circulations c ON cr.circulation_id = c.id - INNER JOIN correspondences corr ON c.correspondence_id = corr.id - INNER JOIN projects p ON corr.project_id = p.id - INNER JOIN organizations org ON cr.organization_id = org.id - INNER JOIN users u ON cr.assigned_to = u.user_id -WHERE cr.status IN ('PENDING', 'IN_PROGRESS') - AND cr.assigned_to IS NOT NULL; --- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ -CREATE VIEW v_audit_log_details AS -SELECT al.audit_id, - al.user_id, - u.username, - u.email, - u.first_name, - u.last_name, - al.action, - al.entity_type, - al.entity_id, - al.details_json, - al.ip_address, - al.user_agent, - al.created_at -FROM audit_logs al - LEFT JOIN users u ON al.user_id = u.user_id; --- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน -CREATE VIEW v_user_all_permissions AS -- Global Permissions -SELECT ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - NULL AS project_id, - NULL AS contract_id, - 'GLOBAL' AS permission_scope -FROM user_assignments ua - INNER JOIN roles r ON ua.role_id = r.role_id - INNER JOIN role_permissions rp ON ua.role_id = rp.role_id - INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Global scope -WHERE p.is_active = 1 - AND ua.organization_id IS NULL - AND ua.project_id IS NULL - AND ua.contract_id IS NULL -UNION ALL --- Organization-specific Permissions -SELECT ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - NULL AS project_id, - NULL AS contract_id, - 'ORGANIZATION' AS permission_scope -FROM user_assignments ua - INNER JOIN roles r ON ua.role_id = r.role_id - INNER JOIN role_permissions rp ON ua.role_id = rp.role_id - INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Organization scope -WHERE p.is_active = 1 - AND ua.organization_id IS NOT NULL - AND ua.project_id IS NULL - AND ua.contract_id IS NULL -UNION ALL --- Project-specific Permissions -SELECT ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - ua.project_id, - NULL AS contract_id, - 'PROJECT' AS permission_scope -FROM user_assignments ua - INNER JOIN roles r ON ua.role_id = r.role_id - INNER JOIN role_permissions rp ON ua.role_id = rp.role_id - INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Project scope -WHERE p.is_active = 1 - AND ua.project_id IS NOT NULL - AND ua.contract_id IS NULL -UNION ALL --- Contract-specific Permissions -SELECT ua.user_id, - ua.role_id, - r.role_name, - rp.permission_id, - p.permission_name, - p.module, - p.scope_level, - ua.organization_id, - ua.project_id, - ua.contract_id, - 'CONTRACT' AS permission_scope -FROM user_assignments ua - INNER JOIN roles r ON ua.role_id = r.role_id - INNER JOIN role_permissions rp ON ua.role_id = rp.role_id - INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Contract scope -WHERE p.is_active = 1 - AND ua.contract_id IS NOT NULL; --- ===================================================== --- Additional Useful Views --- ===================================================== --- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ -CREATE VIEW v_documents_with_attachments AS -SELECT 'CORRESPONDENCE' AS document_type, - c.id AS document_id, - c.correspondence_number AS document_number, - c.project_id, - p.project_code, - p.project_name, - COUNT(ca.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM correspondences c - INNER JOIN projects p ON c.project_id = p.id - LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id - LEFT JOIN attachments a ON ca.attachment_id = a.id -WHERE c.deleted_at IS NULL -GROUP BY c.id, - c.correspondence_number, - c.project_id, - p.project_code, - p.project_name -UNION ALL -SELECT 'CIRCULATION' AS document_type, - circ.id AS document_id, - circ.circulation_no AS document_number, - corr.project_id, - p.project_code, - p.project_name, - COUNT(ca.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM circulations circ - INNER JOIN correspondences corr ON circ.correspondence_id = corr.id - INNER JOIN projects p ON corr.project_id = p.id - LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id - LEFT JOIN attachments a ON ca.attachment_id = a.id -GROUP BY circ.id, - circ.circulation_no, - corr.project_id, - p.project_code, - p.project_name -UNION ALL -SELECT 'SHOP_DRAWING' AS document_type, - sdr.id AS document_id, - sd.drawing_number AS document_number, - sd.project_id, - p.project_code, - p.project_name, - COUNT(sdra.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM shop_drawing_revisions sdr - INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id - INNER JOIN projects p ON sd.project_id = p.id - LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id - LEFT JOIN attachments a ON sdra.attachment_id = a.id -WHERE sd.deleted_at IS NULL -GROUP BY sdr.id, - sd.drawing_number, - sd.project_id, - p.project_code, - p.project_name -UNION ALL -SELECT 'CONTRACT_DRAWING' AS document_type, - cd.id AS document_id, - cd.condwg_no AS document_number, - cd.project_id, - p.project_code, - p.project_name, - COUNT(cda.attachment_id) AS attachment_count, - MAX(a.created_at) AS latest_attachment_date -FROM contract_drawings cd - INNER JOIN projects p ON cd.project_id = p.id - LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id - LEFT JOIN attachments a ON cda.attachment_id = a.id -WHERE cd.deleted_at IS NULL -GROUP BY cd.id, - cd.condwg_no, - cd.project_id, - p.project_code, - p.project_name; --- View แสดงสถิติเอกสารตามประเภทและสถานะ -CREATE VIEW v_document_statistics AS -SELECT p.id AS project_id, - p.project_code, - p.project_name, - ct.id AS correspondence_type_id, - ct.type_code, - ct.type_name, - cs.id AS status_id, - cs.status_code, - cs.status_name, - COUNT(DISTINCT c.id) AS document_count, - COUNT(DISTINCT cr.id) AS revision_count -FROM projects p - CROSS JOIN correspondence_types ct - CROSS JOIN correspondence_status cs - LEFT JOIN correspondences c ON p.id = c.project_id - AND ct.id = c.correspondence_type_id - LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id - AND cs.id = cr.correspondence_status_id - AND cr.is_current = TRUE -WHERE p.is_active = 1 - AND ct.is_active = 1 - AND cs.is_active = 1 -GROUP BY p.id, - p.project_code, - p.project_name, - ct.id, - ct.type_code, - ct.type_name, - cs.id, - cs.status_code, - cs.status_name; --- ===================================================== --- Indexes for View Performance Optimization --- ===================================================== --- Indexes for v_current_correspondences performance -CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); -CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); -CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); --- Indexes for v_current_rfas performance -CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); -CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); --- Indexes for v_user_tasks performance -CREATE INDEX idx_circulation_routings_status_assigned ON circulation_routings(status, assigned_to); -CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings(circulation_id, status); --- Indexes for document statistics performance -CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); -CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); +-- ========================================================== +-- DMS v1.4.0 Document Management System Database +-- Deploy Script Schema +-- Server: Container Station on QNAPQNAP TS-473A +-- Database service: MariaDB 10.11 +-- database web ui: phpmyadmin 5-apache +-- database deelopment ui: DBeaver +-- backend sevice: NestJS +-- frontend sevice: next.js +-- reverse proxy: jc21/nginx-proxy-manager:latest +-- cron service: n8n +-- DMS v1.4.1 Improvements +-- Update: first revise fron v1.3.0 (GLM-4.6 & Gemini) +-- Update: revise fron v1.4.0 (deepseek) +-- ========================================================== +SET NAMES utf8mb4; +SET time_zone = '+07:00'; +-- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด +SET FOREIGN_KEY_CHECKS = 0; +DROP VIEW IF EXISTS v_document_statistics; +DROP VIEW IF EXISTS v_documents_with_attachments; +DROP VIEW IF EXISTS v_user_all_permissions; +DROP VIEW IF EXISTS v_audit_log_details; +DROP VIEW IF EXISTS v_user_tasks; +DROP VIEW IF EXISTS v_contract_parties_all; +DROP VIEW IF EXISTS v_current_rfas; +DROP VIEW IF EXISTS v_current_correspondences; +DROP PROCEDURE IF EXISTS sp_get_next_document_number; +-- 🗑️ DROP TABLE SCRIPT: LCBP3-DMS v1.4.2 +-- คำเตือน: ข้อมูลทั้งหมดจะหายไป กรุณา Backup ก่อนรันบน Production +SET FOREIGN_KEY_CHECKS = 0; +-- ============================================================ +-- ส่วนที่ 1: ตาราง System, Logs & Preferences (ตารางปลายทาง/ส่วนเสริม) +-- ============================================================ +DROP TABLE IF EXISTS backup_logs; +DROP TABLE IF EXISTS search_indices; +DROP TABLE IF EXISTS notifications; +DROP TABLE IF EXISTS audit_logs; +-- [NEW v1.4.2] ตารางการตั้งค่าส่วนตัวของผู้ใช้ (FK -> users) +DROP TABLE IF EXISTS user_preferencesว -- [NEW v1.4.2] ตารางเก็บ Schema สำหรับ Validate JSON (Stand-alone) +DROP TABLE IF EXISTS json_schemas; +-- ============================================================ +-- ส่วนที่ 2: ตาราง Junction (เชื่อมโยงข้อมูล M:N) +-- ============================================================ +DROP TABLE IF EXISTS correspondence_tags; +DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; +DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; +-- ============================================================ +-- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments) +-- ============================================================ +DROP TABLE IF EXISTS contract_drawing_attachments; +DROP TABLE IF EXISTS circulation_attachments; +DROP TABLE IF EXISTS shop_drawing_revision_attachments; +DROP TABLE IF EXISTS correspondence_attachments; +DROP TABLE IF EXISTS attachments; +-- ตารางหลักเก็บ path ไฟล์ +-- ============================================================ +-- ส่วนที่ 4: ตาราง Workflow & Routing (Process Logic) +-- ============================================================ +-- Circulation Workflow +DROP TABLE IF EXISTS circulation_routings; +DROP TABLE IF EXISTS circulation_template_assignees; +DROP TABLE IF EXISTS circulation_templates; +-- RFA Workflow +DROP TABLE IF EXISTS rfa_workflows; +DROP TABLE IF EXISTS rfa_workflow_template_steps; +DROP TABLE IF EXISTS rfa_workflow_templates; +-- Correspondence Workflow +DROP TABLE IF EXISTS correspondence_routings; +DROP TABLE IF EXISTS correspondence_routing_template_steps; +DROP TABLE IF EXISTS correspondence_status_transitions; +DROP TABLE IF EXISTS correspondence_routing_templates; +-- ============================================================ +-- ส่วนที่ 5: ตาราง Mapping สิทธิ์และโครงสร้าง (Access Control) +-- ============================================================ +DROP TABLE IF EXISTS role_permissions; +DROP TABLE IF EXISTS user_assignments; +DROP TABLE IF EXISTS contract_organizations; +DROP TABLE IF EXISTS project_organizations; +-- ============================================================ +-- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Revisions & Items) +-- ============================================================ +DROP TABLE IF EXISTS transmittal_items; +DROP TABLE IF EXISTS shop_drawing_revisions; +DROP TABLE IF EXISTS rfa_items; +DROP TABLE IF EXISTS rfa_revisions; +DROP TABLE IF EXISTS correspondence_references; +DROP TABLE IF EXISTS correspondence_recipients; +DROP TABLE IF EXISTS correspondence_revisions; +-- [Modified v1.4.2] มี Virtual Columns +-- ============================================================ +-- ส่วนที่ 7: ตารางเอกสารหลัก (Core Documents) +-- ============================================================ +DROP TABLE IF EXISTS circulations; +DROP TABLE IF EXISTS transmittals; +DROP TABLE IF EXISTS contract_drawings; +DROP TABLE IF EXISTS shop_drawings; +DROP TABLE IF EXISTS rfas; +DROP TABLE IF EXISTS correspondences; +-- ============================================================ +-- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Master Data) +-- ============================================================ +DROP TABLE IF EXISTS shop_drawing_sub_categories; +DROP TABLE IF EXISTS shop_drawing_main_categories; +DROP TABLE IF EXISTS contract_drawing_sub_cats; +DROP TABLE IF EXISTS contract_drawing_cats; +DROP TABLE IF EXISTS contract_drawing_volumes; +DROP TABLE IF EXISTS circulation_status_codes; +DROP TABLE IF EXISTS rfa_approve_codes; +DROP TABLE IF EXISTS rfa_status_codes; +DROP TABLE IF EXISTS rfa_types; +DROP TABLE IF EXISTS correspondence_status; +DROP TABLE IF EXISTS correspondence_types; +DROP TABLE IF EXISTS document_number_counters; +-- [Modified v1.4.2] มี version column +DROP TABLE IF EXISTS document_number_formats; +DROP TABLE IF EXISTS tags; +-- ============================================================ +-- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างรากฐาน (Root Tables) +-- ============================================================ +DROP TABLE IF EXISTS organization_roles; +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS permissions; +DROP TABLE IF EXISTS contracts; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS users; +-- Referenced by user_preferences, audit_logs, etc. +DROP TABLE IF EXISTS organizations; +-- Referenced by users, projects, etc. +-- ===================================================== +-- 1. 🏢 Core & Master Data (องค์กร, โครงการ, สัญญา) +-- ===================================================== +-- ตาราง Master เก็บประเภทบทบาทขององค์กร +CREATE TABLE organization_roles ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'ชื่อบทบาท (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทบทบาทขององค์กร'; +-- ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ +CREATE TABLE organizations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสองค์กร', + organization_name VARCHAR(255) NOT NULL COMMENT 'ชื่อองค์กร', + -- role_id INT COMMENT 'บทบาทขององค์กร', + is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลองค์กรทั้งหมดที่เกี่ยวข้องในระบบ'; +-- Seed organization +INSERT INTO organizations (id, organization_code, organization_name) +VALUES (1, 'กทท.', 'การท่าเรือแห่งประเทศไทย'), + ( + 10, + 'สคฉ.3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3' + ), + ( + 11, + 'สคฉ.3-01', + 'ตรวจรับพัสดุ ที่ปรึกษาควบคุมงาน' + ), + (12, 'สคฉ.3-02', 'ตรวจรับพัสดุ งานทางทะเล'), + ( + 13, + 'สคฉ.3-03', + 'ตรวจรับพัสดุ อาคารและระบบสาธารณูปโภค' + ), + ( + 14, + 'สคฉ.3-04', + 'ตรวจรับพัสดุ ตรวจสอบผลกระทบสิ่งแวดล้อม' + ), + (15, 'สคฉ.3-05', 'ตรวจรับพัสดุ เยียวยาการประมง'), + ( + 16, + 'สคฉ.3-06', + 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 3' + ), + ( + 17, + 'สคฉ.3-07', + 'ตรวจรับพัสดุ งานก่อสร้าง ส่วนที่ 4' + ), + ( + 18, + 'สคฉ.3-xx', + 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4' + ), + (21, 'TEAM', 'Designer Consulting Ltd.'), + (22, 'คคง.', 'Construction Supervision Ltd.'), + (41, 'ผรม.1', 'Contractor งานทางทะเล'), + (42, 'ผรม.2', 'Contractor อาคารและระบบ'), + (43, 'ผรม.3', 'Contractor #3 Ltd.'), + (44, 'ผรม.4', 'Contractor #4 Ltd.'), + (31, 'EN', 'Third Party Environment'), + (32, 'CAR', 'Third Party Fishery Care'); +-- ตาราง Master เก็บข้อมูลโครงการ +CREATE TABLE projects ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสโครงการ', + project_name VARCHAR(255) NOT NULL COMMENT 'ชื่อโครงการ', + -- parent_project_id INT COMMENT 'รหัสโครงการหลัก (ถ้ามี)', + -- contractor_organization_id INT COMMENT 'รหัสองค์กรผู้รับเหมา (ถ้ามี)', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, + -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลโครงการ'; +INSERT INTO projects (project_code, project_name) +VALUES ( + 'LCBP3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)' + ), + ( + 'LCBP3C1', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล' + ), + ( + 'LCBP3C2', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค' + ), + ( + 'LCBP3C3', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง' + ), + ( + 'LCBP3C4', + 'โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง' + ); +-- ตาราง Master เก็บข้อมูลสัญญา +CREATE TABLE contracts ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL, + contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสัญญา', + contract_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสัญญา', + description TEXT COMMENT 'คำอธิบายสัญญา', + start_date DATE COMMENT 'วันที่เริ่มสัญญา', + end_date DATE COMMENT 'วันที่สิ้นสุดสัญญา', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลสัญญา'; +-- ใช้ Subquery เพื่อดึง project_id มาเชื่อมโยง ทำให้ไม่ต้องมานั่งจัดการ ID ด้วยตัวเอง +INSERT INTO contracts ( + contract_code, + contract_name, + project_id, + is_active + ) +VALUES ( + 'DSLCBP3', + 'งานจ้างที่ปรีกษาออกแบบ โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'PSLCBP3', + 'งานจ้างที่ปรีกษาควบคุมงาน โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'LCBP3-C1', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1) งานก่อสร้างงานทางทะเล', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1' + ), + TRUE + ), + ( + 'LCBP3-C2', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 2) งานก่อสร้างอาคาร ท่าเทียบเรือ ระบบถนน และระบบสาธารณูปโภค', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2' + ), + TRUE + ), + ( + 'LCBP3-C3', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 3) งานก่อสร้าง', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C3' + ), + TRUE + ), + ( + 'LCBP3-C4', + 'งานก่อสร้าง โครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 4) งานก่อสร้าง', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C4' + ), + TRUE + ), + ( + 'ENLCBP3', + 'งานจ้างเหมาตรวจสอบผลกระทบสิ่งแวดล้อมนะหว่างงานก่อสร้างโครงการพัฒนาท่าเรือแหลมฉบัง ระยะที่ 3 (ส่วนที่ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ); +-- ===================================================== +-- 2. 👥 Users & RBAC (ผู้ใช้, สิทธิ์, บทบาท) +-- ===================================================== +-- ตาราง Master เก็บข้อมูลผู้ใช้งาน (User) +CREATE TABLE users ( + user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + username VARCHAR(50) NOT NULL UNIQUE COMMENT 'ชื่อผู้ใช้งาน', + password_hash VARCHAR(255) NOT NULL COMMENT 'รหัสผ่าน (Hashed)', + first_name VARCHAR(50) COMMENT 'ชื่อจริง', + last_name VARCHAR(50) COMMENT 'นามสกุล', + email VARCHAR(100) NOT NULL UNIQUE COMMENT 'อีเมล', + line_id VARCHAR(100) COMMENT 'LINE ID', + primary_organization_id INT COMMENT 'สังกัดองค์กร', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + failed_attempts INT DEFAULT 0 COMMENT 'จำนวนครั้งที่ล็อกอินล้มเหลว', + locked_until DATETIME COMMENT 'ล็อกอินไม่ได้จนถึงเวลา', + last_login_at TIMESTAMP NULL COMMENT 'วันที่และเวลาที่ล็อกอินล่าสุด', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูลผู้ใช้งาน (User)'; +-- Initial SUPER_ADMIN user +INSERT INTO users (username, password_hash, email, is_active) +VALUES ( + 'superadmin', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'superadmin@example.com', + 1 + ) ON DUPLICATE KEY +UPDATE email = +VALUES(email), + is_active = +VALUES(is_active); +-- Create editor01 user +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ( + 'editor01', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'editor01@example.com', + 1 + ); +-- Create viewer01 user (password hash placeholder, must change later) +INSERT IGNORE INTO users (username, password_hash, email, is_active) +VALUES ( + 'viewer01', + '$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq', + 'viewer01@example.com', + 1 + ); +-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ +CREATE TABLE roles ( + role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', + role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท', + scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, + -- ขอบเขตของบทบาท (จากข้อ 4.3) + description TEXT COMMENT 'คำอธิบายบทบาท', + is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = บทบาทของระบบ ลบไม่ได้)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ'; +-- ========================================================== +-- Seed Roles (บทบาทพื้นฐาน 5 บทบาท ตาม Req 4.3) +-- ========================================================== +-- 1. Superadmin (Global) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 1, + 'Superadmin', + 'Global', + 'ผู้ดูแลระบบสูงสุด: สามารถทำทุกอย่างในระบบ, จัดการองค์กร, และจัดการข้อมูลหลักระดับ Global' + ); +-- 2. Org Admin (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 2, + 'Org Admin', + 'Organization', + 'ผู้ดูแลองค์กร: จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, และดูรายงานขององค์กร' + ); +-- 3. Document Control (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 3, + 'Document Control', + 'Organization', + 'ควบคุมเอกสารขององค์กร: เพิ่ม/แก้ไข/ลบเอกสาร, และกำหนดสิทธิ์เอกสารภายในองค์กร' + ); +-- 4. Editor (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 4, + 'Editor', + 'Organization', + 'ผู้แก้ไขเอกสารขององค์กร: เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย' + ); +-- 5. Viewer (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 5, + 'Viewer', + 'Organization', + 'ผู้ดูเอกสารขององค์กร: ดูเอกสารที่มีสิทธิ์เข้าถึงเท่านั้น' + ); +-- 6. Project Manager (Project) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 6, + 'Project Manager', + 'Project', + 'ผู้จัดการโครงการ: จัดการสมาชิกในโครงการ, สร้าง/จัดการสัญญาในโครงการ, และดูรายงานโครงการ' + ); +-- 7. Contract Admin (Contract) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 7, + 'Contract Admin', + 'Contract', + 'ผู้ดูแลสัญญา: จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา, และอนุมัติเอกสารในสัญญา' + ); +-- ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ +CREATE TABLE permissions ( + permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัสสิทธิ์ (เช่น rfas.create, rfas.view)', + description TEXT COMMENT 'คำอธิบายสิทธิ์', + module VARCHAR(50) COMMENT 'โมดูลที่เกี่ยวข้อง', + scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'ระดับขอบเขตของสิทธิ์', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "สิทธิ์" (Permission) หรือ "การกระทำ" ทั้งหมดในระบบ'; +-- ===================================================== +-- 2. Seed Permissions (สิทธิ์การใช้งานทั้งหมด) +-- สิทธิ์ระดับระบบและการจัดการหลัก (System & Master Data) +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 1, + 'system.manage_all', + 'ทำทุกอย่างในระบบ (Superadmin Power)' + ), + -- การจัดการองค์กร + (2, 'organization.create', 'สร้างองค์กรใหม่'), + (3, 'organization.edit', 'แก้ไขข้อมูลองค์กร'), + (4, 'organization.delete', 'ลบองค์กร'), + (5, 'organization.view', 'ดูรายการองค์กร'), + -- การจัดการโครงการ + (6, 'project.create', 'สร้างโครงการใหม่'), + (7, 'project.edit', 'แก้ไขข้อมูลโครงการ'), + (8, 'project.delete', 'ลบโครงการ'), + (9, 'project.view', 'ดูรายการโครงการ'), + -- การจัดการบทบาทและสิทธิ์ (Roles & Permissions) + (10, 'role.create', 'สร้างบทบาท (Role) ใหม่'), + (11, 'role.edit', 'แก้ไขบทบาท (Role)'), + (12, 'role.delete', 'ลบบทบาท (Role)'), + ( + 13, + 'permission.assign', + 'มอบสิทธิ์ให้กับบทบาท (Role)' + ), + -- การจัดการข้อมูลหลัก (Master Data) + ( + 14, + 'master_data.document_type.manage', + 'จัดการประเภทเอกสาร (Document Types)' + ), + ( + 15, + 'master_data.document_status.manage', + 'จัดการสถานะเอกสาร (Document Statuses)' + ), + ( + 16, + 'master_data.drawing_category.manage', + 'จัดการหมวดหมู่แบบ (Drawing Categories)' + ), + (17, 'master_data.tag.manage', 'จัดการ Tags'), + -- การจัดการผู้ใช้งาน + (18, 'user.create', 'สร้างผู้ใช้งานใหม่'), + (19, 'user.edit', 'แก้ไขข้อมูลผู้ใช้งาน'), + (20, 'user.delete', 'ลบ/ปิดการใช้งานผู้ใช้'), + (21, 'user.view', 'ดูข้อมูลผู้ใช้งาน'), + ( + 22, + 'user.assign_organization', + 'มอบผู้ใช้งานให้กับองค์กร' + ); +-- ===================================================== +-- == 2. สิทธิ์การจัดการโครงการและสัญญา (Project & Contract) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 23, + 'project.manage_members', + 'จัดการสมาชิกในโครงการ (เชิญ/ถอดสมาชิก)' + ), + ( + 24, + 'project.create_contracts', + 'สร้างสัญญาในโครงการ' + ), + ( + 25, + 'project.manage_contracts', + 'จัดการสัญญาในโครงการ' + ), + ( + 26, + 'project.view_reports', + 'ดูรายงานระดับโครงการ' + ), + ( + 27, + 'contract.manage_members', + 'จัดการสมาชิกในสัญญา' + ), + (28, 'contract.view', 'ดูข้อมูลสัญญา'); +-- ===================================================== +-- == 3. สิทธิ์การจัดการเอกสาร (Document Management) == +-- ===================================================== +-- สิทธิ์ทั่วไปสำหรับเอกสารทุกประเภท +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 29, + 'document.create_draft', + 'สร้างเอกสารในสถานะฉบับร่าง (Draft)' + ), + (30, 'document.submit', 'ส่งเอกสาร (Submitted)'), + (31, 'document.view', 'ดูเอกสาร'), + (32, 'document.edit', 'แก้ไขเอกสาร (ทั่วไป)'), + ( + 33, + 'document.admin_edit', + 'แก้ไข/ถอน/ยกเลิกเอกสารที่ส่งแล้ว (Admin Power)' + ), + (34, 'document.delete', 'ลบเอกสาร'), + ( + 35, + 'document.attach', + 'จัดการไฟล์แนบ (อัปโหลด/ลบ)' + ), + -- สิทธิ์เฉพาะสำหรับ Correspondence + ( + 36, + 'correspondence.create', + 'สร้างเอกสารโต้ตอบ (Correspondence)' + ), + -- สิทธิ์เฉพาะสำหรับ Request for Approval (RFA) + (37, 'rfa.create', 'สร้างเอกสารขออนุมัติ (RFA)'), + ( + 38, + 'rfa.manage_shop_drawings', + 'จัดการข้อมูล Shop Drawing และ Contract Drawing ที่เกี่ยวข้อง' + ), + -- สิทธิ์เฉพาะสำหรับ Shop Drawing & Contract Drawing + ( + 39, + 'drawing.create', + 'สร้าง/แก้ไขข้อมูลแบบ (Shop/Contract Drawing)' + ), + -- สิทธิ์เฉพาะสำหรับ Transmittal + ( + 40, + 'transmittal.create', + 'สร้างเอกสารนำส่ง (Transmittal)' + ), + -- สิทธิ์เฉพาะสำหรับ Circulation Sheet (ใบเวียน) + ( + 41, + 'circulation.create', + 'สร้างใบเวียนเอกสาร (Circulation)' + ), + ( + 42, + 'circulation.respond', + 'ตอบกลับใบเวียน (Main/Action)' + ), + ( + 43, + 'circulation.acknowledge', + 'รับทราบใบเวียน (Information)' + ), + (44, 'circulation.close', 'ปิดใบเวียน'); +-- ===================================================== +-- == 4. สิทธิ์การจัดการ Workflow == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 45, + 'workflow.action_review', + 'ดำเนินการในขั้นตอนปัจจุบัน (เช่น ตรวจสอบแล้ว)' + ), + ( + 46, + 'workflow.force_proceed', + 'บังคับไปยังขั้นตอนถัดไป (Document Control Power)' + ), + ( + 47, + 'workflow.revert', + 'ย้อนกลับไปยังขั้นตอนก่อนหน้า (Document Control Power)' + ); +-- ===================================================== +-- == 5. สิทธิ์ด้านการค้นหาและรายงาน (Search & Reporting) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES (48, 'search.advanced', 'ใช้งานการค้นหาขั้นสูง'), + ( + 49, + 'report.generate', + 'สร้างรายงานสรุป (รายวัน/สัปดาห์/เดือน/ปี)' + ); +-- ตารางเชื่อมระหว่าง roles และ permissions (M:N) +CREATE TABLE role_permissions ( + role_id INT COMMENT 'ID ของบทบาท', + permission_id INT COMMENT 'ID ของสิทธิ์', + PRIMARY KEY (role_id, permission_id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง roles และ permissions (M:N)'; +-- ========================================================== +-- Seed Role-Permissions Mapping (จับคู่สิทธิ์เริ่มต้น) +-- ========================================================== +-- Seed data for the 'role_permissions' table +-- This table links roles to their specific permissions. +-- NOTE: This assumes the role_id and permission_id from the previous seed data files. +-- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. +-- ===================================================== +-- == 1. Superadmin (role_id = 1) - Gets ALL permissions == +-- ===================================================== +-- Superadmin can do everything. We can dynamically link all permissions to this role. +-- This is a robust way to ensure Superadmin always has full power. +INSERT INTO role_permissions (role_id, permission_id) +SELECT 1, + permission_id +FROM permissions; +-- ===================================================== +-- == 2. Org Admin (role_id = 2) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- จัดการผู้ใช้ในองค์กร + (2, 18), + -- user.create + (2, 19), + -- user.edit + (2, 20), + -- user.delete + (2, 21), + -- user.view + (2, 22), + -- user.assign_organization + -- จัดการองค์กร + (2, 3), + -- organization.edit + (2, 5), + -- organization.view + -- จัดการข้อมูลหลักที่อนุญาต (เฉพาะ Tags) + (2, 17), + -- master_data.tag.manage + -- ดูข้อมูลต่างๆ ในองค์กร + (2, 31), + -- document.view + (2, 9), + -- project.view + (2, 28), + -- contract.view + -- การค้นหาและรายงาน + (2, 48), + -- search.advanced + (2, 49); +-- report.generate +-- ===================================================== +-- == 3. Document Control (role_id = 3) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการเอกสารทั้งหมด + (3, 29), + -- document.create_draft + (3, 30), + -- document.submit + (3, 31), + -- document.view + (3, 32), + -- document.edit + (3, 33), + -- document.admin_edit + (3, 34), + -- document.delete + (3, 35), + -- document.attach + -- สิทธิ์สร้างเอกสารแต่ละประเภท + (3, 36), + -- correspondence.create + (3, 37), + -- rfa.create + (3, 39), + -- drawing.create + (3, 40), + -- transmittal.create + (3, 41), + -- circulation.create + -- สิทธิ์จัดการ Workflow + (3, 45), + -- workflow.action_review + (3, 46), + -- workflow.force_proceed + (3, 47), + -- workflow.revert + -- สิทธิ์จัดการ Circulation + (3, 42), + -- circulation.respond + (3, 43), + -- circulation.acknowledge + (3, 44), + -- circulation.close + -- สิทธิ์อื่นๆ ที่จำเป็น + (3, 38), + -- rfa.manage_shop_drawings + (3, 48), + -- search.advanced + (3, 49); +-- report.generate +-- ===================================================== +-- == 4. Editor (role_id = 4) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์แก้ไขเอกสาร (แต่ไม่ใช่สิทธิ์ Admin) + (4, 29), + -- document.create_draft + (4, 30), + -- document.submit + (4, 31), + -- document.view + (4, 32), + -- document.edit + (4, 35), + -- document.attach + -- สิทธิ์สร้างเอกสารแต่ละประเภท + (4, 36), + -- correspondence.create + (4, 37), + -- rfa.create + (4, 39), + -- drawing.create + (4, 40), + -- transmittal.create + (4, 41), + -- circulation.create + -- สิทธิ์อื่นๆ ที่จำเป็น + (4, 38), + -- rfa.manage_shop_drawings + (4, 48); +-- search.advanced +-- ===================================================== +-- == 5. Viewer (role_id = 5) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์ดูเท่านั้น + (5, 31), + -- document.view + (5, 48); +-- search.advanced +-- ===================================================== +-- == 6. Project Manager (role_id = 6) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการโครงการ + (6, 23), + -- project.manage_members + (6, 24), + -- project.create_contracts + (6, 25), + -- project.manage_contracts + (6, 26), + -- project.view_reports + (6, 9), + -- project.view + -- สิทธิ์จัดการข้อมูลหลักระดับโครงการ + (6, 16), + -- master_data.drawing_category.manage + -- สิทธิ์ดูข้อมูลในสัญญา + (6, 28), + -- contract.view + -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) + (6, 29), + -- document.create_draft + (6, 30), + -- document.submit + (6, 31), + -- document.view + (6, 32), + -- document.edit + (6, 35), + -- document.attach + (6, 36), + -- correspondence.create + (6, 37), + -- rfa.create + (6, 39), + -- drawing.create + (6, 40), + -- transmittal.create + (6, 41), + -- circulation.create + (6, 38), + -- rfa.manage_shop_drawings + (6, 48), + -- search.advanced + (6, 49); +-- report.generate +-- ===================================================== +-- == 7. Contract Admin (role_id = 7) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- สิทธิ์จัดการสัญญา + (7, 27), + -- contract.manage_members + (7, 28), + -- contract.view + -- สิทธิ์ในการอนุมัติ (ส่วนหนึ่งของ Workflow) + (7, 45), + -- workflow.action_review + -- สิทธิ์จัดการข้อมูลเฉพาะสัญญา + (7, 38), + -- rfa.manage_shop_drawings + (7, 39), + -- drawing.create + -- สิทธิ์ในการจัดการเอกสาร (ระดับ Editor) + (7, 29), + -- document.create_draft + (7, 30), + -- document.submit + (7, 31), + -- document.view + (7, 32), + -- document.edit + (7, 35), + -- document.attach + (7, 36), + -- correspondence.create + (7, 37), + -- rfa.create + (7, 40), + -- transmittal.create + (7, 41), + -- circulation.create + (7, 48); +-- search.advanced +-- ตารางเชื่อมผู้ใช้ (users) +CREATE TABLE user_assignments ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + role_id INT NOT NULL, + -- คอลัมน์สำหรับกำหนดขอบเขต (จะใช้เพียงอันเดียวต่อแถว) + organization_id INT NULL, + project_id INT NULL, + contract_id INT NULL, + assigned_by_user_id INT, + -- ผู้ที่มอบหมายบทบาทนี้ + assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE, + FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id), + -- Constraint เพื่อให้แน่ใจว่ามีเพียงขอบเขตเดียวที่ถูกกำหนดในแต่ละแถว + CONSTRAINT chk_scope CHECK ( + ( + organization_id IS NOT NULL + AND project_id IS NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NOT NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NOT NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NULL + ) -- สำหรับ Global scope + ) +); +CREATE TABLE project_organizations ( + project_id INT NOT NULL, + organization_id INT NOT NULL, + PRIMARY KEY (project_id, organization_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); +CREATE TABLE contract_organizations ( + contract_id INT NOT NULL, + organization_id INT NOT NULL, + role_in_contract VARCHAR(100), + -- เช่น 'Owner', 'Designer', 'Consultant', 'Contractor' + PRIMARY KEY (contract_id, organization_id), + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +); +-- ===================================================== +-- == 4. การเชื่อมโยงโครงการกับองค์กร (project_organizations) == +-- ===================================================== +-- โครงการหลัก (LCBP3) จะมีองค์กรหลักๆ เข้ามาเกี่ยวข้องทั้งหมด +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + id +FROM organizations +WHERE organization_code IN ( + 'กทท.', + 'สคฉ.3', + 'TEAM', + 'คคง.', + 'ผรม.1', + 'ผรม.2', + 'ผรม.3', + 'ผรม.4', + 'EN', + 'CAR' + ); +-- โครงการย่อย (LCBP3C1) จะมีเฉพาะองค์กรที่เกี่ยวข้อง +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1' + ), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-02', 'คคง.', 'ผรม.1'); +-- ทำเช่นเดียวกันสำหรับโครงการอื่นๆ (ตัวอย่าง) +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2' + ), + id +FROM organizations +WHERE organization_code IN ('กทท.', 'สคฉ.3', 'สคฉ.3-03', 'คคง.', 'ผรม.2'); +-- ===================================================== +-- == 5. การเชื่อมโยงสัญญากับองค์กร (contract_organizations) == +-- ===================================================== +-- สัญญาที่ปรึกษาออกแบบ (DSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'TEAM' + ), + 'Designer' + ); +-- สัญญาที่ปรึกษาควบคุมงาน (PSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'คคง.' + ), + 'Consultant' + ); +-- สัญญางานก่อสร้าง ส่วนที่ 1 (LCBP3-C1) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'ผรม.1' + ), + 'Contractor' + ); +-- สัญญางานก่อสร้าง ส่วนที่ 2 (LCBP3-C2) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'ผรม.2' + ), + 'Contractor' + ); +-- สัญญาตรวจสอบสิ่งแวดล้อม (ENLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'กทท.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'EN' + ), + 'Consultant' + ); +-- ===================================================== +-- 3. ✉️ Correspondences (เอกสารหลัก, Revisions) +-- ===================================================== +-- ตาราง Master เก็บประเภทเอกสารโต้ตอบ +CREATE TABLE correspondence_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสประเภท (เช่น RFA, RFI)', + type_name VARCHAR(255) NOT NULL COMMENT 'ชื่อประเภท', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บประเภทเอกสารโต้ตอบ'; +INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) +VALUES ('RFA', 'Request for Approval', 1, 1), + ('RFI', 'Request for Information', 2, 1), + ('TRANSMITTAL', 'Transmittal', 3, 1), + ('EMAIL', 'Email', 4, 1), + ('INSTRUCTION', 'Instruction', 5, 1), + ('LETTER', 'Letter', 6, 1), + ('MEMO', 'Memorandum', 7, 1), + ('MOM', 'Minutes of Meeting', 8, 1), + ('NOTICE', 'Notice', 9, 1), + ( + 'OTHER', + 'Other', + 10, + 1 + ); +-- ตาราง Master เก็บสถานะของเอกสาร +CREATE TABLE correspondence_status ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสสถานะหนังสือ (เช่น DRAFT, SUBOWN)', + status_name VARCHAR(255) NOT NULL COMMENT 'ชื่อสถานะหนังสือ', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะของเอกสาร'; +INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) +VALUES ('DRAFT', 'Draft', 10, 1), + ('SUBOWN', 'Submitted to Owner', 21, 1), + ('SUBDSN', 'Submitted to Designer', 22, 1), + ('SUBCSC', 'Submitted to CSC', 23, 1), + ('SUBCON', 'Submitted to Contractor', 24, 1), + ('SUBOTH', 'Submitted to Others', 25, 1), + ('REPOWN', 'Reply by Owner', 31, 1), + ('REPDSN', 'Reply by Designer', 32, 1), + ('REPCSC', 'Reply by CSC', 33, 1), + ('REPCON', 'Reply by Contractor', 34, 1), + ('REPOTH', 'Reply by Others', 35, 1), + ('RSBOWN', 'Resubmited by Owner', 41, 1), + ('RSBDSN', 'Resubmited by Designer', 42, 1), + ('RSBCSC', 'Resubmited by CSC', 43, 1), + ('RSBCON', 'Resubmited by Contractor', 44, 1), + ('CLBOWN', 'Closed by Owner', 51, 1), + ('CLBDSN', 'Closed by Designer', 52, 1), + ('CLBCSC', 'Closed by CSC', 53, 1), + ('CLBCON', 'Closed by Contractor', 54, 1), + ('CCBOWN', 'Canceled by Owner', 91, 1), + ('CCBDSN', 'Canceled by Designer', 92, 1), + ('CCBCSC', 'Canceled by CSC', 93, 1), + ('CCBCON', 'Canceled by Contractor', 94, 1); +-- ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision +CREATE TABLE correspondences ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (นี่คือ "Master ID" ที่ใช้เชื่อมโยง)', + correspondence_number VARCHAR(100) NOT NULL COMMENT 'เลขที่เอกสาร (สร้างจาก DocumentNumberingModule)', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = ภายใน, 0 = ภายนอก)', + project_id INT NOT NULL COMMENT 'อยู่ในโครงการ', + originator_id INT COMMENT 'องค์กรผู้ส่ง', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของเอกสารโต้ตอบ เก็บข้อมูลที่ไม่เปลี่ยนตาม Revision'; +-- ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N) +CREATE TABLE correspondence_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + correspondence_status_id INT NOT NULL COMMENT 'สถานะของ Revision นี้', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATETIME COMMENT 'วันที่ออกเอกสาร', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + due_date DATETIME COMMENT 'วันที่ครบกำหนด', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + details JSON COMMENT 'ข้อมูลเฉพาะ (เช่น RFI details)', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_master_revision_number (correspondence_id, revision_number), + UNIQUE KEY uq_master_current (correspondence_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติการแก้ไข (Revisions) ของ correspondences (1:N)'; +-- ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N) +CREATE TABLE correspondence_recipients ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + recipient_organization_id INT COMMENT 'ID องค์กรผู้รับ', + recipient_type ENUM('TO', 'CC') COMMENT 'ประเภทผู้รับ (TO หรือ CC)', + PRIMARY KEY ( + correspondence_id, + recipient_organization_id, + recipient_type + ), + FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมผู้รับ (TO/CC) สำหรับเอกสารแต่ละฉบับ (M:N)'; +-- ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ +CREATE TABLE tags ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'ชื่อ Tag', + description TEXT COMMENT 'คำอธิบายแท็ก', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ Tags ทั้งหมดที่ใช้ในระบบ'; +-- ตารางเชื่อมระหว่าง correspondences และ tags (M:N) +CREATE TABLE correspondence_tags ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + tag_id INT COMMENT 'ID ของ Tag', + PRIMARY KEY (correspondence_id, tag_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง correspondences และ tags (M:N)'; +-- ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N) +CREATE TABLE correspondence_references ( + src_correspondence_id INT COMMENT 'ID เอกสารต้นทาง', + tgt_correspondence_id INT COMMENT 'ID เอกสารเป้าหมาย', + PRIMARY KEY (src_correspondence_id, tgt_correspondence_id), + FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมการอ้างอิงระหว่างเอกสาร (M:N)'; +-- ===================================================== +-- 4. 📐 approval: RFA (เอกสารขออนุมัติ, Workflows) +-- ===================================================== +-- 3.1 routing Config for Templates +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Logic การเดินเอกสารที่ซับซ้อนกว่า Column ปกติ +CREATE TABLE correspondence_routing_templates ( + id INT NOT NULL PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของแม่แบบ', + template_name VARCHAR(255) NOT NULL COMMENT 'ชื่อแม่แบบ', + description TEXT COMMENT 'คำอธิบาย', + project_id INT NULL COMMENT 'ID โครงการ (ถ้าเป็นแม่แบบเฉพาะโครงการ)', + -- NULL = แม่แบบทั่วไป + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + is_active BOOLEAN DEFAULT TRUE, + workflow_config JSON NULL COMMENT 'Routing Logic Configuration', + UNIQUE KEY ux_routing_template_name_project (template_name, project_id), + CONSTRAINT fk_crt_project FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'แม่แบบสายงานการส่งต่อเอกสารขออนุมัติ'; +CREATE TABLE correspondence_status_transitions( + type_id INT NOT NULL COMMENT 'ID ของประเภทหนังสือ', + from_status_id INT NOT NULL COMMENT 'ID ของสถานะต้นทาง', + to_status_id INT NOT NULL COMMENT 'ID ของสถานะปลายทาง', + PRIMARY KEY (type_id, from_status_id, to_status_id), + CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(id), + CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_status(id), + CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_status(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสถานะที่อนุญาตให้เปลี่ยนแปลงได้ตามประเภทหนังสือ'; +-- 1.18.1 correspondence_routing_template_steps Table +CREATE TABLE correspondence_routing_template_steps ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + sequence INT NOT NULL COMMENT 'ลำดับขั้นตอน', + to_organization_id INT NOT NULL COMMENT 'ID องค์กรณ์ผู้รับในขั้นตอนนี้', + step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION') NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้', + expected_days INT NULL, + UNIQUE KEY ux_cor_template_sequence (template_id, sequence), + CONSTRAINT fk_cwts_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE CASCADE, + CONSTRAINT fk_cwts_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ขั้นตอนในแม่แบบ Workflow การส่งต่อเอกสาร'; +-- 1.19.1 correspondence_routings +-- 3.2 State Context for Running Workflows +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Snapshot ข้อมูล ณ ขณะนั้นเพื่อใช้ตัดสินใจใน Step ถัดไป +CREATE TABLE correspondence_routings ( + id INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'ID ของขั้นตอน', + correspondence_id INT NOT NULL COMMENT 'ID ของเอกสาร(FK -> correspondence_revisions)', + template_id INT NULL COMMENT 'ID ของแม่แบบที่ใช้ (ถ้ามี)', + -- สำหรับอ้างอิงถึงแม่แบบ + sequence INT NOT NULL COMMENT 'ลำดับของขั้นตอนการส่งต่อ', + from_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้ส่ง', + to_organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ผู้รับ', + step_purpose ENUM( + 'FOR_APPROVAL', + 'FOR_REVIEW', + 'FOR_INFORMATION', + 'FOR_ACTION' + ) NOT NULL DEFAULT 'FOR_REVIEW' COMMENT 'วัตถุประสงค์ของขั้นตอนนี้ เช่น เพื่ออนุมัติ, เพื่อตรวจสอบ, หรือเพื่อรับทราบ', + status ENUM( + 'SENT', + 'RECEIVED', + 'ACTIONED', + 'FORWARDED', + 'REPLIED' + ) NOT NULL DEFAULT 'SENT' COMMENT 'สถานะการดำเนินการของเอกสารในขั้นตอนนี้', + comments TEXT COMMENT 'หมายเหตุ หรือความคิดเห็นในการส่งต่อ', + due_date DATETIME NULL COMMENT 'วันที่ต้องตอบเอกสารในขั้นตอนนี้', + processed_by_user_id INT NULL COMMENT 'ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้', + processed_at TIMESTAMP NULL COMMENT 'เวลาที่ดำเนินการ', + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่สร้างขั้นตอนนี้', + state_context JSON NULL COMMENT 'Snapshot of routing state context', + UNIQUE KEY ux_cor_routing_sequence (correspondence_id, sequence), + -- Foreign Keys + CONSTRAINT fk_crs_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE, + CONSTRAINT fk_crs_template FOREIGN KEY (template_id) REFERENCES correspondence_routing_templates(id) ON DELETE + SET NULL, + CONSTRAINT fk_crs_from_org FOREIGN KEY (from_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_to_org FOREIGN KEY (to_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + CONSTRAINT fk_crs_processed_by_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางติดตาม Workflow การส่งต่อเอกสารทั่วไป'; +-- ตาราง Master สำหรับประเภท RFA +CREATE TABLE rfa_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + type_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสประเภท RFA (เช่น DWG, DOC, MAT)', + type_name VARCHAR(100) NOT NULL COMMENT 'ชื่อประเภท RFA', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับประเภท RFA'; +INSERT INTO rfa_types (type_code, type_name, sort_order, is_active) +VALUES ('DWG', 'Shop Drawing', 10, 1), + ('DOC', 'Document', 20, 1), + ('SPC', 'Specification', 21, 1), + ('CAL', 'Calculation', 22, 1), + ('TRP', 'Test Report', 23, 1), + ('SRY', 'Survey Report', 24, 1), + ('QAQC', 'QA/QC Document', 25, 1), + ('MES', 'Method Statement', 30, 1), + ('MAT', 'Material', 40, 1), + ('ASB', 'As-Built', 50, 1), + ('OTH', 'Other', 99, 1); +-- ตาราง Master สำหรับสถานะ RFA +CREATE TABLE rfa_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะ RFA (เช่น DFT - Draft, FAP - For Approve)', + status_name VARCHAR(100) NOT NULL COMMENT 'ชื่อสถานะ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับสถานะ RFA'; +INSERT INTO rfa_status_codes ( + status_code, + status_name, + description, + sort_order + ) +VALUES ('DFT', 'Draft', 'ฉบับร่าง', 1), + ('FAP', 'For Approve', 'เพื่อขออนุมัติ', 11), + ('FRE', 'For Review', 'เพื่อตรวจสอบ', 12), + ('FCO', 'For Construction', 'เพื่อก่อสร้าง', 20), + ('ASB', 'AS-Built', 'แบบก่อสร้างจริง', 30), + ('OBS', 'Obsolete', 'ไม่ใช้งาน', 80), + ('CC', 'Canceled', 'ยกเลิก', 99); +-- ตาราง Master สำหรับรหัสผลการอนุมัติ RFA +CREATE TABLE rfa_approve_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสผลการอนุมัติ (เช่น 1A - Approved, 3R - Revise and Resubmit)', + approve_name VARCHAR(100) NOT NULL COMMENT 'ชื่อผลการอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับรหัสผลการอนุมัติ RFA'; +INSERT INTO rfa_approve_codes ( + approve_code, + approve_name, + sort_order, + is_active + ) +VALUES ('1A', 'Approved by Authority', 10, 1), + ('1C', 'Approved by CSC', 11, 1), + ('1N', 'Approved As Note', 12, 1), + ('1R', 'Approved with Remarks', 13, 1), + ('3C', 'Consultant Comments', 31, 1), + ('3R', 'Revise and Resubmit', 32, 1), + ('4X', 'Reject', 40, 1), + ('5N', 'No Further Action', 50, 1); +-- ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions) +CREATE TABLE rfas ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง (RFA Master ID)', + rfa_type_id INT NOT NULL COMMENT 'ประเภท RFA', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + created_by INT COMMENT 'ผู้สร้าง', + deleted_at DATETIME NULL COMMENT 'สำหรับ Soft Delete', + FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของ RFA (มีความสัมพันธ์ 1:N กับ rfa_revisions)'; +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N) +CREATE TABLE rfa_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID ของ Correspondence', + rfa_id INT NOT NULL COMMENT 'Master ID ของ RFA', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision ปัจจุบัน)', + rfa_status_code_id INT NOT NULL COMMENT 'สถานะ RFA', + rfa_approve_code_id INT COMMENT 'ผลการอนุมัติ', + title VARCHAR(255) NOT NULL COMMENT 'เรื่อง', + document_date DATE COMMENT 'วันที่ในเอกสาร', + issued_date DATE COMMENT 'วันที่ส่งขออนุมัติ', + received_date DATETIME COMMENT 'วันที่ลงรับเอกสาร', + approved_date DATE COMMENT 'วันที่อนุมัติ', + description TEXT COMMENT 'คำอธิบายการแก้ไขใน Revision นี้', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้างเอกสาร', + created_by INT COMMENT 'ผู้สร้าง', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), + FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), + UNIQUE KEY uq_rr_current (rfa_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ rfas (1:N)'; +-- ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N) +CREATE TABLE rfa_items ( + rfarev_correspondence_id INT COMMENT 'ID ของ RFA Revision', + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + PRIMARY KEY ( + rfarev_correspondence_id, + shop_drawing_revision_id + ), + FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง rfa_revisions (ที่เป็นประเภท DWG) กับ shop_drawing_revisions (M:N)'; +-- ตาราง Master เก็บแม่แบบสายอนุมัติ +-- 3.1 Workflow Config for Templates +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Logic การเดินเอกสารที่ซับซ้อนกว่า Column ปกติ +CREATE TABLE rfa_workflow_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายอนุมัติ', + description TEXT COMMENT 'คำอธิบาย', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + workflow_config JSON NULL COMMENT 'State Machine Configuration Rules' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายอนุมัติ'; +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE rfa_workflow_template_steps ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; +-- ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน +-- 3.2 State Context for Running Workflows +-- รองรับ: Backend Plan T3.1 +-- เหตุผล: เก็บ Snapshot ข้อมูล ณ ขณะนั้นเพื่อใช้ตัดสินใจใน Step ถัดไป +CREATE TABLE rfa_workflows ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + rfa_revision_id INT NOT NULL COMMENT 'ID ของ RFA Revision', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + action_type ENUM('REVIEW', 'APPROVE', 'ACKNOWLEDGE') COMMENT 'ประเภทการกระทำ', + status ENUM( + 'PENDING', + 'IN_PROGRESS', + 'COMPLETED', + 'REJECTED' + ) COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + state_context JSON NULL COMMENT 'Snapshot of workflow state context', + FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การอนุมัติของ RFA จริงตามสายงาน'; +-- ===================================================== +-- 5. 📐 Drawings (แบบ, หมวดหมู่) +-- ===================================================== +-- ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_volumes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + volume_code VARCHAR(50) NOT NULL COMMENT 'รหัสเล่ม', + volume_name VARCHAR(255) NOT NULL COMMENT 'ชื่อเล่ม', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_volume_project (project_id, volume_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "เล่ม" ของแบบคู่สัญญา'; +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่หลัก', + cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_cat_project (project_id, cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบคู่สัญญา'; +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา +CREATE TABLE contract_drawing_sub_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + sub_cat_code VARCHAR(50) NOT NULL COMMENT 'รหัสหมวดหมู่ย่อย', + sub_cat_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบคู่สัญญา'; +-- ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N) +CREATE TABLE contract_drawing_subcat_cat_maps ( + project_id INT COMMENT 'ID ของโครงการ', + sub_cat_id INT COMMENT 'ID ของหมวดหมู่ย่อย', + cat_id INT COMMENT 'ID ของหมวดหมู่หลัก', + PRIMARY KEY (project_id, sub_cat_id, cat_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, + FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง หมวดหมู่หลัก-ย่อย (M:N)'; +-- ตาราง Master เก็บข้อมูล "แบบคู่สัญญา" +CREATE TABLE contract_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + condwg_no VARCHAR(255) NOT NULL COMMENT 'เลขที่แบบสัญญา', + title VARCHAR(255) NOT NULL COMMENT 'ชื่อแบบสัญญา', + sub_cat_id INT COMMENT 'หมวดหมู่ย่อย', + volume_id INT COMMENT 'เล่ม', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, + FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, + UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบคู่สัญญา"'; +-- ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_main_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่หลัก (เช่น ARCH, STR)', + main_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่หลัก" ของแบบก่อสร้าง'; +-- ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง +CREATE TABLE shop_drawing_sub_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสหมวดหมู่ย่อย (เช่น STR-COLUMN)', + sub_category_name VARCHAR(255) NOT NULL COMMENT 'ชื่อหมวดหมู่ย่อย', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + description TEXT COMMENT 'คำอธิบาย', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master สำหรับ "หมวดหมู่ย่อย" ของแบบก่อสร้าง'; +-- ตาราง Master เก็บข้อมูล "แบบก่อสร้าง" +CREATE TABLE shop_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'เลขที่ Shop Drawing', + title VARCHAR(500) NOT NULL COMMENT 'ชื่อแบบ', + main_category_id INT NOT NULL COMMENT 'หมวดหมู่หลัก', + sub_category_id INT NOT NULL COMMENT 'หมวดหมู่ย่อย', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + deleted_at DATETIME NULL COMMENT 'วันที่ลบ', + updated_by INT COMMENT 'ผู้แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id), + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id), + FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บข้อมูล "แบบก่อสร้าง"'; +-- ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N) +CREATE TABLE shop_drawing_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Revision', + shop_drawing_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'หมายเลข Revision (เช่น 0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision ที่แสดง (เช่น A, B, 1.1)', + revision_date DATE COMMENT 'วันที่ของ Revision', + description TEXT COMMENT 'คำอธิบายการแก้ไข', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, + UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "ลูก" เก็บประวัติ (Revisions) ของ shop_drawings (1:N)'; +-- ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N) +CREATE TABLE shop_drawing_revision_contract_refs ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง shop_drawing_revisions กับ contract_drawings (M:N)'; +-- ===================================================== +-- 6. 🔄 Circulations (ใบเวียนภายใน) +-- ===================================================== +-- ตาราง Master เก็บสถานะใบเวียน +CREATE TABLE circulation_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + code VARCHAR(20) NOT NULL UNIQUE COMMENT 'รหัสสถานะการดำเนินงาน', + description VARCHAR(50) NOT NULL COMMENT 'คำอธิบายสถานะการดำเนินงาน', + sort_order INT DEFAULT 0 COMMENT 'ลำดับการแสดงผล', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บสถานะใบเวียน'; +INSERT INTO circulation_status_codes (code, description, sort_order) +VALUES ('OPEN', 'Open', 1), + ('IN_REVIEW', 'In Review', 2), + ('COMPLETED', 'ปCompleted', 3), + ('CANCELLED', 'Cancelled/Withdrawn', 9); +-- ตาราง "แม่" ของใบเวียนเอกสารภายใน +CREATE TABLE circulations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตารางใบเวียน', + correspondence_id INT UNIQUE COMMENT 'ID ของเอกสาร (จากตาราง correspondences)', + organization_id INT NOT NULL COMMENT 'ID ขององค์กรณ์ที่เป็นเจ้าของใบเวียนนี้', + circulation_no VARCHAR(100) NOT NULL COMMENT 'เลขที่ใบเวียน', + circulation_subject VARCHAR(500) NOT NULL COMMENT 'เรื่องใบเวียน', + circulation_status_code VARCHAR(20) NOT NULL COMMENT 'รหัสสถานะใบเวียน', + created_by_user_id INT NOT NULL COMMENT 'ID ของผู้สร้างใบเวียน', + submitted_at TIMESTAMP NULL COMMENT 'วันที่ส่งใบเวียน', + closed_at TIMESTAMP NULL COMMENT 'วันที่ปิดใบเวียน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id), + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code), + FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "แม่" ของใบเวียนเอกสารภายใน'; +-- ตาราง Master เก็บแม่แบบสายงาน +CREATE TABLE circulation_templates ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_name VARCHAR(100) NOT NULL COMMENT 'ชื่อแม่แบบสายงาน', + description TEXT COMMENT 'คำอธิบาย', + organization_id INT NOT NULL COMMENT 'องค์กรเจ้าของแม่แบบ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'สถานะการใช้งาน', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (organization_id) REFERENCES organizations(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บแม่แบบสายงาน'; +-- ตารางลูก เก็บขั้นตอนในแม่แบบ +CREATE TABLE circulation_template_assignees ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + template_id INT NOT NULL COMMENT 'ID ของแม่แบบ', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + role_id INT COMMENT 'บทบาทที่รับผิดชอบ', + duration_days INT COMMENT 'ระยะเวลาที่กำหนด (วัน)', + is_optional BOOLEAN DEFAULT FALSE COMMENT 'เป็นขั้นตอนเลือกหรือไม่', + FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (role_id) REFERENCES roles(role_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางลูก เก็บขั้นตอนในแม่แบบ'; +-- ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow +CREATE TABLE circulation_routings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + circulation_id INT NOT NULL COMMENT 'ID ของใบเวียน', + step_number INT NOT NULL COMMENT 'ลำดับขั้นตอน', + organization_id INT NOT NULL COMMENT 'องค์กรที่รับผิดชอบ', + assigned_to INT COMMENT 'ผู้ใช้ที่ได้รับมอบหมาย', + status ENUM( + 'PENDING', + 'IN_PROGRESS', + 'COMPLETED', + 'REJECTED' + ) COMMENT 'สถานะ', + comments TEXT COMMENT 'ความคิดเห็น', + completed_at DATETIME COMMENT 'วันที่เสร็จสิ้น', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (organization_id) REFERENCES organizations(id), + FOREIGN KEY (assigned_to) REFERENCES users(user_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางประวัติ (Log) การส่งต่อของเอกสารจริงตาม Workflow'; +-- ===================================================== +-- 7. 📤 Transmittals (เอกสารนำส่ง) +-- ===================================================== +-- ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences) +CREATE TABLE transmittals ( + correspondence_id INT PRIMARY KEY COMMENT 'ID ของเอกสาร', + purpose ENUM( + 'FOR_APPROVAL', + 'FOR_INFORMATION', + 'FOR_REVIEW', + 'OTHER' + ) COMMENT 'วัตถุประสงค์', + remarks TEXT COMMENT 'หมายเหตุ', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางข้อมูลเฉพาะของเอกสารนำส่ง (เป็นตารางลูก 1:1 ของ correspondences)'; +-- ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N) +CREATE TABLE transmittal_items ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของรายการ', + transmittal_id INT NOT NULL COMMENT 'ID ของ Transmittal', + item_correspondence_id INT NOT NULL COMMENT 'ID ของเอกสารที่แนบไป', + quantity INT DEFAULT 1 COMMENT 'จำนวน', + remarks VARCHAR(255) COMMENT 'หมายเหตุสำหรับรายการนี้', + FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อมระหว่าง transmittals และเอกสารที่นำส่ง (M:N)'; +-- ===================================================== +-- 8. 📎 File Management (ไฟล์แนบ) +-- ===================================================== +-- ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ +-- 2.2 Attachments - Two-Phase Storage & Security +-- รองรับ: Backend Plan T2.2, Req 3.9.1 +-- เหตุผล: จัดการไฟล์ขยะ (Orphan Files) และตรวจสอบความถูกต้องไฟล์ +CREATE TABLE attachments ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของไฟล์แนบ', + original_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ดั้งเดิมตอนอัปโหลด', + stored_filename VARCHAR(255) NOT NULL COMMENT 'ชื่อไฟล์ที่เก็บจริงบน Server (ป้องกันชื่อซ้ำ)', + file_path VARCHAR(500) NOT NULL COMMENT 'Path ที่เก็บไฟล์ (บน QNAP /share/dms-data/)', + mime_type VARCHAR(100) NOT NULL COMMENT 'ประเภทไฟล์ (เช่น application/pdf)', + file_size INT NOT NULL COMMENT 'ขนาดไฟล์ (bytes)', + is_temporary BOOLEAN DEFAULT TRUE COMMENT 'True=ยังไม่ Commit ลง DB จริง', + temp_id VARCHAR(100) NULL COMMENT 'ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1' uploaded_by_user_id INT NOT NULL COMMENT 'ผู้อัปโหลดไฟล์', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่อัปโหลด', + expires_at DATETIME NULL COMMENT 'เวลาหมดอายุของไฟล์ Temp' checksum VARCHAR(64) NULL COMMENT 'SHA-256 Checksum' FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ'; +-- ตารางเชื่อม correspondences กับ attachments (M:N) +CREATE TABLE correspondence_attachments ( + correspondence_id INT COMMENT 'ID ของเอกสาร', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (correspondence_id, attachment_id), + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม correspondences กับ attachments (M:N)'; +-- ตารางเชื่อม circulations กับ attachments (M:N) +CREATE TABLE circulation_attachments ( + circulation_id INT COMMENT 'ID ของใบเวียน', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลักของใบเวียน)', + PRIMARY KEY (circulation_id, attachment_id), + FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม circulations กับ attachments (M:N)'; +-- ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N) +CREATE TABLE shop_drawing_revision_attachments ( + shop_drawing_revision_id INT COMMENT 'ID ของ Shop Drawing Revision', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (shop_drawing_revision_id, attachment_id), + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม shop_drawing_revisions กับ attachments (M:N)'; +-- ตารางเชื่อม contract_drawings กับ attachments (M:N) +CREATE TABLE contract_drawing_attachments ( + contract_drawing_id INT COMMENT 'ID ของ Contract Drawing', + attachment_id INT COMMENT 'ID ของไฟล์แนบ', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') COMMENT 'ประเภทไฟล์', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = ไฟล์หลัก)', + PRIMARY KEY (contract_drawing_id, attachment_id), + FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE, + FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเชื่อม contract_drawings กับ attachments (M:N)'; +-- ===================================================== +-- 9. 🔢 Document Numbering (การสร้างเลขที่เอกสาร) +-- ===================================================== +-- ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร +CREATE TABLE document_number_formats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง', + project_id INT NOT NULL COMMENT 'โครงการ', + correspondence_type_id INT NOT NULL COMMENT 'ประเภทเอกสาร', + format_template VARCHAR(255) NOT NULL COMMENT 'รูปแบบ Template (เช่น {ORG_CODE}-{TYPE_CODE}-{SEQ:4})', + description TEXT COMMENT 'คำอธิบายรูปแบบนี้', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'วันที่แก้ไขล่าสุด', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, + UNIQUE KEY uk_project_type (project_id, correspondence_type_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง Master เก็บ "รูปแบบ" Template ของเลขที่เอกสาร'; +-- ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด +-- 2.1 Document Numbering - Optimistic Locking +-- รองรับ: Backend Plan T2.3, Req 3.10.5 +-- เหตุผล: ป้องกัน Race Condition เวลาขอเลขที่เอกสารพร้อมกัน +CREATE TABLE document_number_counters ( + project_id INT COMMENT 'โครงการ', + originator_organization_id INT COMMENT 'องค์กรผู้ส่ง', + correspondence_type_id INT COMMENT 'ประเภทเอกสาร', + current_year INT COMMENT 'ปี ค.ศ. ของตัวนับ', + last_number INT DEFAULT 0 COMMENT 'เลขที่ล่าสุดที่ใช้ไปแล้ว', + version INT DEFAULT 0 NOT NULL COMMENT 'Optimistic Lock Version', + PRIMARY KEY ( + project_id, + originator_organization_id, + correspondence_type_id, + current_year + ), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บ "ตัวนับ" (Running Number) ล่าสุด'; +-- ===================================================== +-- 10. ⚙️ System & Logs (ระบบและ Log) +-- ===================================================== +-- 1.1 JSON Schemas Registry +-- รองรับ: Backend Plan T2.5.1, Req 6.11.1 +-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized +CREATE TABLE IF NOT EXISTS json_schemas ( + id INT AUTO_INCREMENT PRIMARY KEY, + schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1, CORR_GENERIC', + version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema', + schema_definition JSON NOT NULL COMMENT 'โครงสร้าง JSON Schema (Standard Format)', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + INDEX idx_schema_code (schema_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +-- 1.2 User Preferences +-- รองรับ: Req 5.5, 6.8.3 +-- เหตุผล: แยกการตั้งค่า Notification และ UI ออกจากตาราง Users หลัก +CREATE TABLE IF NOT EXISTS user_preferences ( + user_id INT PRIMARY KEY, + notify_email BOOLEAN DEFAULT TRUE, + notify_line BOOLEAN DEFAULT TRUE, + digest_mode BOOLEAN DEFAULT FALSE COMMENT 'รับแจ้งเตือนแบบรวม (Digest) แทน Real-time', + ui_theme VARCHAR(20) DEFAULT 'light', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_user_prefs_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +-- ตารางเก็บบันทึกการกระทำของผู้ใช้ +-- 4.1 Audit Logs Enhancements +-- รองรับ: Req 6.1 +-- เหตุผล: รองรับ Distributed Tracing และระบุความรุนแรง +CREATE TABLE audit_logs ( + audit_id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของ Log', + request_id VARCHAR(100) NULL COMMENT 'Trace ID linking to app logs', + user_id INT COMMENT 'ผู้กระทำ', + action VARCHAR(100) NOT NULL COMMENT 'การกระทำ (เช่น rfa.create, correspondence.update, login.success)', + severity ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') DEFAULT 'INFO', + entity_type VARCHAR(50) COMMENT 'ตาราง/โมดูล (เช่น ''rfa'', ''correspondence'')', + entity_id VARCHAR(50) COMMENT 'Primary ID ของระเบียนที่ได้รับผลกระทำ', + details_json JSON COMMENT 'ข้อมูลบริบท', + ip_address VARCHAR(45) COMMENT 'IP Address', + user_agent VARCHAR(255) COMMENT 'User Agent', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาที่กระทำ', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางเก็บบันทึกการกระทำของผู้ใช้'; +-- ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System) +CREATE TABLE notifications ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการแจ้งเตือน', + user_id INT NOT NULL COMMENT 'ID ผู้ใช้', + title VARCHAR(255) NOT NULL COMMENT 'หัวข้อการแจ้งเตือน', + message TEXT NOT NULL COMMENT 'รายละเอียดการแจ้งเตือน', + notification_type ENUM('EMAIL', 'LINE', 'SYSTEM') NOT NULL COMMENT 'ประเภท (EMAIL, LINE, SYSTEM)', + is_read BOOLEAN DEFAULT FALSE COMMENT 'สถานะการอ่าน', + entity_type VARCHAR(50) COMMENT 'เช่น ''rfa'', ''circulation''', + entity_id INT COMMENT 'ID ของเอนทิตีที่เกี่ยวข้อง', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง', + FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการการแจ้งเตือน (Email/Line/System)'; +-- ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search) +CREATE TABLE search_indices ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของดัชนี', + entity_type VARCHAR(50) NOT NULL COMMENT 'ชนิดเอนทิตี (เช่น ''correspondence'', ''rfa'')', + entity_id INT NOT NULL COMMENT 'ID ของเอนทิตี', + content TEXT NOT NULL COMMENT 'เนื้อหาที่จะค้นหา', + indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง/อัปเดตัชนี' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับจัดการดัชนีการค้นหาขั้นสูง (Full-text Search)'; +-- ตารางสำหรับบันทึกประวัติการสำรองข้อมูล +CREATE TABLE backup_logs ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของการสำรอง', + backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'ประเภท (DATABASE, FILES, FULL)', + backup_path VARCHAR(500) NOT NULL COMMENT 'ตำแหน่งไฟล์สำรอง', + file_size BIGINT COMMENT 'ขนาดไฟล์', + status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'สถานะ', + started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'เวลาเริ่มต้น', + completed_at TIMESTAMP NULL COMMENT 'เวลาเสร็จสิ้น', + error_message TEXT COMMENT 'ข้อความผิดพลาด (ถ้ามี)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตารางสำหรับบันทึกประวัติการสำรองข้อมูล'; +-- 4.2 Virtual Columns for JSON Search (ตัวอย่างสำหรับ Correspondence) +-- รองรับ: Backend Plan T2.1, Req 3.11.3 +-- เหตุผล: เพิ่มความเร็วในการ Search/Sort ข้อมูลที่อยู่ใน JSON details +-- หมายเหตุ: ต้องมั่นใจว่า MariaDB เวอร์ชัน 10.11+ รองรับ Syntax นี้ +-- ตัวอย่าง: ดึง Project ID ที่อ้างอิงใน details ออกมาทำ Index +ALTER TABLE correspondence_revisions +ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId')) + ) VIRTUAL, + ADD INDEX idx_corr_rev_v_project (v_ref_project_id); +-- ตัวอย่าง: ดึง Document Type ย่อยจาก details +ALTER TABLE correspondence_revisions +ADD COLUMN v_doc_subtype VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))) VIRTUAL, + ADD INDEX idx_corr_rev_v_subtype (v_doc_subtype); +-- ทำแบบเดียวกันกับ RFA Revisions หากมีการเก็บ JSON details +ALTER TABLE rfa_revisions +ADD COLUMN details JSON NULL COMMENT 'RFA Specific Details' +AFTER description; +ALTER TABLE rfa_revisions +ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount')) + ) VIRTUAL; +-- ============================================================ +-- 5. PARTITIONING PREPARATION (Advance - Optional) +-- ============================================================ +-- หมายเหตุ: การทำ Partitioning บนตารางที่มีอยู่แล้ว (audit_logs, notifications) +-- มักจะต้อง Drop Primary Key เดิม แล้วสร้างใหม่โดยรวม Partition Key (created_at) เข้าไป +-- ขั้นตอนนี้ควรทำแยกต่างหากเมื่อระบบเริ่มมีข้อมูลเยอะ หรือทำใน Maintenance Window +-- +-- ตัวอย่าง SQL สำหรับ Audit Logs (Reference Only): +-- ALTER TABLE audit_logs DROP PRIMARY KEY, ADD PRIMARY KEY (audit_id, created_at); +-- ALTER TABLE audit_logs PARTITION BY RANGE (YEAR(created_at)) ( +-- PARTITION p2024 VALUES LESS THAN (2025), +-- PARTITION p2025 VALUES LESS THAN (2026), +-- PARTITION p_future VALUES LESS THAN MAXVALUE +-- ); +-- ===================================================== +-- CREATE INDEXES +-- ===================================================== +-- Indexes for document_number_formats +CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); +CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); +CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); +-- Indexes for document_number_counters +CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); +CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); +CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); +CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); +-- Indexes for tags +CREATE INDEX idx_tags_name ON tags(tag_name); +CREATE INDEX idx_tags_created_at ON tags(created_at); +-- Indexes for correspondence_tags +CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); +CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); +-- Indexes for audit_logs +CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); +CREATE INDEX idx_audit_logs_action ON audit_logs(action); +CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); +CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); +CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); +-- Indexes for notifications +CREATE INDEX idx_notifications_user ON notifications(user_id); +CREATE INDEX idx_notifications_type ON notifications(notification_type); +CREATE INDEX idx_notifications_read ON notifications(is_read); +CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); +CREATE INDEX idx_notifications_created_at ON notifications(created_at); +-- Indexes for search_indices +CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); +CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); +-- Indexes for backup_logs +CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); +CREATE INDEX idx_backup_logs_status ON backup_logs(status); +CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); +CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); +-- ===================================================== +-- Additional Composite Indexes for Performance +-- ===================================================== +-- Composite index for document_number_counters for faster lookups +CREATE INDEX idx_doc_counter_composite ON document_number_counters( + project_id, + originator_organization_id, + correspondence_type_id, + current_year +); +-- Composite index for notifications for user-specific queries +CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); +-- Composite index for audit_logs for reporting +CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); +-- Composite index for search_indices for entity-based queries +CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); +-- สร้าง Index สำหรับ Cleanup Job +CREATE INDEX idx_attachments_temp_cleanup ON attachments(is_temporary, expires_at); +CREATE INDEX idx_attachments_temp_id ON attachments(temp_id); +CREATE INDEX idx_audit_request_id ON audit_logs(request_id); +-- ===================================================== +-- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB +-- Generated from Data Dictionary +-- ===================================================== +-- ===================================================== +-- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์) +-- ===================================================== +-- Stored Procedure ดึงเลขที่เอกสารถัดไป +DELIMITER // CREATE PROCEDURE sp_get_next_document_number( + IN p_project_id INT, + IN p_originator_organization_id INT, + IN p_correspondence_type_id INT, + IN p_current_year INT, + OUT p_next_number INT +) BEGIN +DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN -- หากเกิดข้อผิดพลาด ให้ยกเลิก Transaction และส่ง Error กลับไป + ROLLBACK; +END; +START TRANSACTION; +-- ล็อกแถวเพื่อป้องกัน Race Condition +SELECT last_number INTO p_next_number +FROM document_number_counters +WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year FOR +UPDATE; +-- ถ้าไม่พบ record ให้สร้างใหม่ +IF p_next_number IS NULL THEN +SET p_next_number = 1; +INSERT INTO document_number_counters ( + project_id, + originator_organization_id, + correspondence_type_id, + current_year, + last_number + ) +VALUES ( + p_project_id, + p_originator_organization_id, + p_correspondence_type_id, + p_current_year, + p_next_number + ); +ELSE -- อัพเดทเลขที่ล่าสุด +SET p_next_number = p_next_number + 1; +UPDATE document_number_counters +SET last_number = p_next_number +WHERE project_id = p_project_id + AND originator_organization_id = p_originator_organization_id + AND correspondence_type_id = p_correspondence_type_id + AND current_year = p_current_year; +END IF; +COMMIT; +END // DELIMITER; +-- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA) +CREATE VIEW v_current_correspondences AS +SELECT c.id AS correspondence_id, + c.correspondence_number, + c.correspondence_type_id, + ct.type_code AS correspondence_type_code, + ct.type_name AS correspondence_type_name, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_code AS originator_code, + org.organization_name AS originator_name, + cr.id AS revision_id, + cr.revision_number, + cr.revision_label, + cr.title, + cr.document_date, + cr.issued_date, + cr.received_date, + cr.due_date, + cr.correspondence_status_id, + cs.status_code, + cs.status_name, + cr.created_by, + u.username AS created_by_username, + cr.created_at AS revision_created_at +FROM correspondences c + INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN organizations org ON c.originator_id = org.id + INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id + LEFT JOIN users u ON cr.created_by = u.user_id +WHERE cr.is_current = TRUE + AND c.correspondence_type_id NOT IN ( + SELECT id + FROM correspondence_types + WHERE type_code = 'RFA' + ) + AND c.deleted_at IS NULL; +-- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด +CREATE VIEW v_current_rfas AS +SELECT r.id AS rfa_id, + r.rfa_type_id, + rt.type_code AS rfa_type_code, + rt.type_name AS rfa_type_name, + rr.correspondence_id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_name AS originator_name, + rr.id AS revision_id, + rr.revision_number, + rr.revision_label, + rr.title, + rr.document_date, + rr.issued_date, + rr.received_date, + rr.approved_date, + rr.rfa_status_code_id, + rsc.status_code AS rfa_status_code, + rsc.status_name AS rfa_status_name, + rr.rfa_approve_code_id, + rac.approve_code AS rfa_approve_code, + rac.approve_name AS rfa_approve_name, + rr.created_by, + u.username AS created_by_username, + rr.created_at AS revision_created_at +FROM rfas r + INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id + INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id + INNER JOIN correspondences c ON rr.correspondence_id = c.id + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN organizations org ON c.originator_id = org.id + INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id + LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id + LEFT JOIN users u ON rr.created_by = u.user_id +WHERE rr.is_current = TRUE + AND r.deleted_at IS NULL + AND c.deleted_at IS NULL; +-- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization +CREATE VIEW v_contract_parties_all AS +SELECT c.id AS contract_id, + c.contract_code, + c.contract_name, + p.id AS project_id, + p.project_code, + p.project_name, + o.id AS organization_id, + o.organization_code, + o.organization_name, + co.role_in_contract +FROM contracts c + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN contract_organizations co ON c.id = co.contract_id + INNER JOIN organizations o ON co.organization_id = o.id +WHERE c.is_active = TRUE; +-- View แสดงรายการ "งานของฉัน" (My Tasks) ที่ยังไม่เสร็จ +CREATE VIEW v_user_tasks AS +SELECT cr.id AS routing_id, + c.id AS circulation_id, + c.circulation_no, + c.circulation_subject, + c.correspondence_id, + corr.correspondence_number, + corr.project_id, + p.project_code, + p.project_name, + cr.assigned_to AS user_id, + u.username, + u.first_name, + u.last_name, + cr.organization_id, + org.organization_name, + cr.step_number, + cr.status AS task_status, + cr.comments, + cr.completed_at, + cr.created_at AS assigned_at, + c.created_at AS circulation_created_at +FROM circulation_routings cr + INNER JOIN circulations c ON cr.circulation_id = c.id + INNER JOIN correspondences corr ON c.correspondence_id = corr.id + INNER JOIN projects p ON corr.project_id = p.id + INNER JOIN organizations org ON cr.organization_id = org.id + INNER JOIN users u ON cr.assigned_to = u.user_id +WHERE cr.status IN ('PENDING', 'IN_PROGRESS') + AND cr.assigned_to IS NOT NULL; +-- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ +CREATE VIEW v_audit_log_details AS +SELECT al.audit_id, + al.user_id, + u.username, + u.email, + u.first_name, + u.last_name, + al.action, + al.entity_type, + al.entity_id, + al.details_json, + al.ip_address, + al.user_agent, + al.created_at +FROM audit_logs al + LEFT JOIN users u ON al.user_id = u.user_id; +-- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน +CREATE VIEW v_user_all_permissions AS -- Global Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'GLOBAL' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Global scope +WHERE p.is_active = 1 + AND ua.organization_id IS NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Organization-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'ORGANIZATION' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Organization scope +WHERE p.is_active = 1 + AND ua.organization_id IS NOT NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Project-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + NULL AS contract_id, + 'PROJECT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Project scope +WHERE p.is_active = 1 + AND ua.project_id IS NOT NULL + AND ua.contract_id IS NULL +UNION ALL +-- Contract-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + ua.contract_id, + 'CONTRACT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Contract scope +WHERE p.is_active = 1 + AND ua.contract_id IS NOT NULL; +-- ===================================================== +-- Additional Useful Views +-- ===================================================== +-- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ +CREATE VIEW v_documents_with_attachments AS +SELECT 'CORRESPONDENCE' AS document_type, + c.id AS document_id, + c.correspondence_number AS document_number, + c.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM correspondences c + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +WHERE c.deleted_at IS NULL +GROUP BY c.id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CIRCULATION' AS document_type, + circ.id AS document_id, + circ.circulation_no AS document_number, + corr.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM circulations circ + INNER JOIN correspondences corr ON circ.correspondence_id = corr.id + INNER JOIN projects p ON corr.project_id = p.id + LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +GROUP BY circ.id, + circ.circulation_no, + corr.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'SHOP_DRAWING' AS document_type, + sdr.id AS document_id, + sd.drawing_number AS document_number, + sd.project_id, + p.project_code, + p.project_name, + COUNT(sdra.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM shop_drawing_revisions sdr + INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id + INNER JOIN projects p ON sd.project_id = p.id + LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id + LEFT JOIN attachments a ON sdra.attachment_id = a.id +WHERE sd.deleted_at IS NULL +GROUP BY sdr.id, + sd.drawing_number, + sd.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CONTRACT_DRAWING' AS document_type, + cd.id AS document_id, + cd.condwg_no AS document_number, + cd.project_id, + p.project_code, + p.project_name, + COUNT(cda.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM contract_drawings cd + INNER JOIN projects p ON cd.project_id = p.id + LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id + LEFT JOIN attachments a ON cda.attachment_id = a.id +WHERE cd.deleted_at IS NULL +GROUP BY cd.id, + cd.condwg_no, + cd.project_id, + p.project_code, + p.project_name; +-- View แสดงสถิติเอกสารตามประเภทและสถานะ +CREATE VIEW v_document_statistics AS +SELECT p.id AS project_id, + p.project_code, + p.project_name, + ct.id AS correspondence_type_id, + ct.type_code, + ct.type_name, + cs.id AS status_id, + cs.status_code, + cs.status_name, + COUNT(DISTINCT c.id) AS document_count, + COUNT(DISTINCT cr.id) AS revision_count +FROM projects p + CROSS JOIN correspondence_types ct + CROSS JOIN correspondence_status cs + LEFT JOIN correspondences c ON p.id = c.project_id + AND ct.id = c.correspondence_type_id + LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + AND cs.id = cr.correspondence_status_id + AND cr.is_current = TRUE +WHERE p.is_active = 1 + AND ct.is_active = 1 + AND cs.is_active = 1 +GROUP BY p.id, + p.project_code, + p.project_name, + ct.id, + ct.type_code, + ct.type_name, + cs.id, + cs.status_code, + cs.status_name; +-- ===================================================== +-- Indexes for View Performance Optimization +-- ===================================================== +-- Indexes for v_current_correspondences performance +CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); +CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); +CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); +-- Indexes for v_current_rfas performance +CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); +CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); +-- Indexes for v_user_tasks performance +CREATE INDEX idx_circulation_routings_status_assigned ON circulation_routings(status, assigned_to); +CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings(circulation_id, status); +-- Indexes for document statistics performance +CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); +CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); SET FOREIGN_KEY_CHECKS = 1; \ No newline at end of file diff --git a/Documnets/Project/LCBP3-DMS — Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md b/docs/Markdown/LCBP3-DMS — Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md similarity index 100% rename from Documnets/Project/LCBP3-DMS — Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md rename to docs/Markdown/LCBP3-DMS — Task Breakdown สำหรับ Phase 2A–2C (v1.4.2).md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md b/docs/Markdown/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_Backend_Development_Plan_Grok.md b/docs/Markdown/LCBP3-DMS_V1_4_1_Backend_Development_Plan_Grok.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_Backend_Development_Plan_Grok.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_Backend_Development_Plan_Grok.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_Data_Dictionary.md b/docs/Markdown/LCBP3-DMS_V1_4_1_Data_Dictionary.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_Data_Dictionary.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_Data_Dictionary.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md b/docs/Markdown/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_FullStackJS.md b/docs/Markdown/LCBP3-DMS_V1_4_1_FullStackJS.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_FullStackJS.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_FullStackJS.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_1_Requirements.md b/docs/Markdown/LCBP3-DMS_V1_4_1_Requirements.md similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_1_Requirements.md rename to docs/Markdown/LCBP3-DMS_V1_4_1_Requirements.md diff --git a/Documnets/Project/LCBP3-DMS_V1_4_2_Backend_Development_Plan (Patched) b/docs/Markdown/LCBP3-DMS_V1_4_2_Backend_Development_Plan (Patched) similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_2_Backend_Development_Plan (Patched) rename to docs/Markdown/LCBP3-DMS_V1_4_2_Backend_Development_Plan (Patched) diff --git a/Documnets/Project/LCBP3-DMS_V1_4_2_FullStackJS (Patched) b/docs/Markdown/LCBP3-DMS_V1_4_2_FullStackJS (Patched) similarity index 100% rename from Documnets/Project/LCBP3-DMS_V1_4_2_FullStackJS (Patched) rename to docs/Markdown/LCBP3-DMS_V1_4_2_FullStackJS (Patched) diff --git a/Documnets/Project/LCBP3-DMS_V1_4_2_Requirements (Patched) b/docs/Markdown/LCBP3-DMS_V1_4_2_Requirements (Patched) similarity index 99% rename from Documnets/Project/LCBP3-DMS_V1_4_2_Requirements (Patched) rename to docs/Markdown/LCBP3-DMS_V1_4_2_Requirements (Patched) index 636b345..0786e2b 100644 --- a/Documnets/Project/LCBP3-DMS_V1_4_2_Requirements (Patched) +++ b/docs/Markdown/LCBP3-DMS_V1_4_2_Requirements (Patched) @@ -1,4 +1,4 @@ -# LCBP3-DMS_V1_4_2_Requirements (Patched) +# LCBP3-DMS_V1_4_1_Requirements (Patched) > เอกสารนี้เป็นเวอร์ชันปรับปรุงจาก LCBP3-DMS_V1_4_1_Requirements.md > มีการระบุจุดแก้ไขด้วย (เพิ่ม v1.4.2) / (ปรับแก้ v1.4.2) / (ลบ v1.4.2) / (ย้าย v1.4.2) diff --git a/docs/Markdown/LCBP3-DMS_V1_4_2_Requirements..bak.md b/docs/Markdown/LCBP3-DMS_V1_4_2_Requirements..bak.md new file mode 100644 index 0000000..efa8f87 --- /dev/null +++ b/docs/Markdown/LCBP3-DMS_V1_4_2_Requirements..bak.md @@ -0,0 +1,618 @@ +# 📝 **LCBP3-DMS Documents Management System Version 1.4.2: Application Requirements Specification (by DeepSeek)** + +* **ปรับปรุงตามข้อเสนอแนะจาก FullStackJS Guidelines และแผนการพัฒนา** + +## 📌 **1. วัตถุประสงค์** + +สร้างเว็บแอปพลิเคชั่นสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่สามารถจัดการและควบคุม การสื่อสารด้วยเอกสารที่ซับซ้อน อย่างมีประสิทธิภาพ + +* มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร +* ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล +* เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ +* **เสริม:** ปรับปรุงความปลอดภัยของระบบด้วยมาตรการป้องกันที่ทันสมัย +* **เสริม:** เพิ่มความทนทานของระบบด้วยกลไก resilience patterns +* **เสริม:** สร้างระบบ monitoring และ observability ที่ครอบคลุม + +## 🛠️ **2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)** + +### **2.1 Infrastructure & Environment:** + +* **Server:** QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B) +* **Containerization:** Container Station (Docker & Docker Compose) +* **Domain:** np-dms.work, +* **IP:** 159.192.126.103 +* **Docker Network:** ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 +* **Data Storage:** /share/dms-data บน QNAP +* **ข้อจำกัด:** ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น + +### **2.2 Technology Stack:** + +* Backend: + * framework: NestJS (TypeScript, ESM) + * database: MariaDB 10.11 + * orm: TypeORM + * auth: JWT + Passport + CASL + * fileProcessing: Multer + ClamAV + * search: Elasticsearch + * caching: Redis + * resilience: Circuit Breaker, Retry Patterns + +* frontend: + * framework: Next.js 14 (App Router, React, TypeScript, ESM) + * styling: Tailwind CSS + PostCSS + * components: shadcn/ui + Radix UI + * stateManagement: Zustand + TanStack Query + * forms: React Hook Form + Zod + +* infrastructure: + * reverseProxy: Nginx Proxy Manager + * containerization: Docker + Docker Compose + * monitoring: Winston + Health Checks + * workflow: n8n + +### **2.3 Performance Targets:** + +```typescript +const PERFORMANCE_TARGETS = { + api: { + responseTime: '< 200ms (90th percentile)', + searchPerformance: '< 500ms', + concurrentUsers: '100 users', + errorRate: '< 1%' + }, + frontend: { + firstContentfulPaint: '< 1.5s', + largestContentfulPaint: '< 2.5s', + bundleSize: '< 500KB (gzipped)' + }, + database: { + queryTime: '< 100ms (p95)', + connectionPool: '20-50 connections' + }, + files: { + uploadTime: '< 30s (50MB files)', + downloadTime: '< 5s (50MB files)', + virusScanTime: '< 10s' + } +}; +``` + +## 📦 **3. ข้อกำหนดด้านฟังก์ชันการทำงาน (Functional Requirements)** + +### **3.1 Simplified JSON Structure:** + +```typescript +// Simplified JSON Details Structure +interface BaseDetails { + version: string; + type: string; + created_at: string; + updated_at?: string; +} + +interface CorrespondenceDetails extends BaseDetails { + subject: string; + description?: string; + priority: 'LOW' | 'NORMAL' | 'HIGH' | 'URGENT'; + confidentiality: 'PUBLIC' | 'INTERNAL' | 'CONFIDENTIAL'; + references?: Array<{ + correspondence_id: number; + description: string; + }>; +} + +interface RFIDetails extends BaseDetails { + questions: Array<{ + question_text: string; + response_required: boolean; + deadline?: string; + }>; + category?: 'TECHNICAL' | 'ADMINISTRATIVE'; + urgency?: 'LOW' | 'NORMAL' | 'HIGH'; +} +``` + +### **3.2 Enhanced Document Management:** + +* **3.2.1** ระบบต้องรองรับการจัดการเอกสารแบบ Real-time Collaboration +* **3.2.2** ต้องมีระบบ Version Control ที่ชัดเจนสำหรับทุกเอกสาร +* **3.2.3** รองรับการค้นหา Full-text Search ผ่าน Elasticsearch +* **3.2.4** ระบบต้องรองรับ Bulk Operations สำหรับการจัดการเอกสารจำนวนมาก + +### **3.3 Advanced Workflow Management:** + +* **3.3.1** รองรับ Conditional Workflow Routing ตาม business rules +* **3.3.2** ระบบต้องมี Escalation Mechanisms สำหรับงานที่เลยกำหนด +* **3.3.3** รองรับ Parallel Workflow Steps เมื่อเหมาะสม +* **3.3.4** ต้องมีระบบ Notification Preferences สำหรับผู้ใช้ + +## 🔐 **4. ข้อกำหนดด้านสิทธิ์และการเข้าถึง (Access Control Requirements)** + +### **4.1 Enhanced RBAC System:** + +```typescript +const PERMISSION_HIERARCHY = { + levels: ['GLOBAL', 'ORGANIZATION', 'PROJECT', 'CONTRACT'], + evaluation: 'MOST_PERMISSIVE', + features: { + dynamicRoles: 'Admin สามารถสร้างบทบาทใหม่ได้', + permissionTemplates: 'ใช้ template สำหรับบทบาทมาตรฐาน', + timeBoundPermissions: 'สิทธิ์ชั่วคราวตามระยะเวลา' + } +}; +``` + +### **4.2 Advanced Security Controls:** + +* **4.2.1** ต้องมี Session Management ที่ปลอดภัย +* **4.2.2** รองรับ Multi-factor Authentication (MFA) +* **4.2.3** ต้องมีระบบ Audit Trail ที่ครอบคลุม +* **4.2.4** รองรับ Security Policy Enforcement + +## 👥 **5. ข้อกำหนดด้านผู้ใช้งาน (User Interface & Experience)** + +### **5.1 Component Architecture:** + +``` +📁 Frontend Structure: +├── 📁 app/ # Next.js App Router +├── 📁 components/ +│ ├── 📁 ui/ # Shadcn/ui components +│ ├── 📁 forms/ # Form components +│ ├── 📁 workflows/ # Workflow components +│ ├── 📁 data-display/ # Data display components +│ └── 📁 layouts/ # Layout components +├── 📁 hooks/ # Custom hooks +├── 📁 stores/ # Zustand stores +├── 📁 lib/ # Utilities and config +└── 📁 types/ # TypeScript definitions +``` + +### **5.2 State Management Strategy:** + +```typescript +const STATE_MANAGEMENT = { + serverState: { + tool: 'TanStack Query', + useCases: ['API data', 'Search results', 'User profiles'] + }, + clientState: { + tool: 'Zustand', + useCases: ['UI state', 'Form state', 'User preferences'] + }, + formState: { + tool: 'React Hook Form + Zod', + useCases: ['All forms', 'Validation', 'Form wizard'] + } +}; +``` + +## 🚀 **6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)** + +### **6.1 Enhanced Performance Requirements:** + +```typescript +const PERFORMANCE_REQUIREMENTS = { + scalability: { + concurrentUsers: '100+ users', + documentStorage: '10,000+ documents', + fileStorage: '1TB+ capacity' + }, + reliability: { + uptime: '99.9%', + backupRecovery: '4-hour RTO, 1-hour RPO', + errorHandling: 'Graceful degradation' + }, + security: { + authentication: 'JWT with refresh tokens', + authorization: 'RBAC with 4-level hierarchy', + dataProtection: 'Encryption at rest and in transit' + } +}; +``` + +### **6.2 Advanced Monitoring & Observability:** + +```typescript +const MONITORING_REQUIREMENTS = { + applicationMetrics: [ + 'api_response_times', + 'error_rates', + 'user_activity', + 'workflow_completion_rates' + ], + businessMetrics: [ + 'documents_created_daily', + 'average_approval_time', + 'sla_compliance_rates', + 'user_satisfaction_scores' + ], + securityMetrics: [ + 'failed_login_attempts', + 'file_scan_results', + 'permission_changes', + 'security_incidents' + ] +}; +``` + +### **6.3 Enhanced Security Requirements:** + +* **6.3.1** ต้องมี Comprehensive Input Validation +* **6.3.2** ต้องป้องกัน OWASP Top 10 vulnerabilities +* **6.3.3** ต้องมี Rate Limiting ที่ configuraable +* **6.3.4** ต้องมี Security Headers และ CSP + +### **6.4 Database Optimization Requirements:** + +```typescript +const DATABASE_REQUIREMENTS = { + performance: { + queryOptimization: 'All queries under 100ms', + indexingStrategy: 'Composite indexes for common queries', + connectionPooling: '20-50 connections' + }, + maintenance: { + backup: 'Daily full + hourly incremental', + cleanup: 'Automated archive of old records', + monitoring: 'Slow query logging and alerting' + } +}; +``` + +## 🧪 **7. ข้อกำหนดด้านการทดสอบ (Testing Requirements)** + +### **7.1 Comprehensive Testing Strategy:** + +```typescript +const TESTING_STRATEGY = { + unitTesting: { + coverage: '80% minimum', + focus: 'Business logic and utilities', + tools: ['Jest', 'React Testing Library'] + }, + integrationTesting: { + coverage: 'Critical user journeys', + focus: 'API endpoints and database operations', + tools: ['Supertest', 'Testcontainers'] + }, + e2eTesting: { + coverage: 'Core business workflows', + focus: 'User registration to document approval', + tools: ['Playwright', 'Jest'] + }, + performanceTesting: { + coverage: 'Critical paths under load', + focus: 'API response times and concurrent users', + tools: ['k6', 'Artillery'] + }, + securityTesting: { + coverage: 'OWASP Top 10 vulnerabilities', + focus: 'Authentication, authorization, input validation', + tools: ['OWASP ZAP', 'Snyk'] + } +}; +``` + +### **7.2 Quality Gates:** + +```typescript +const QUALITY_GATES = { + preCommit: [ + 'ESLint with no errors', + 'Prettier formatting', + 'TypeScript compilation', + 'Unit tests passing' + ], + preMerge: [ + 'All tests passing', + 'Code review completed', + 'Security scan clean', + 'Performance benchmarks met' + ], + preDeploy: [ + 'Integration tests passing', + 'E2E tests passing', + 'Load tests successful', + 'Security audit completed' + ] +}; +``` + +## 🔄 **8. ข้อกำหนดด้านการบำรุงรักษา (Maintenance Requirements)** + +### **8.1 Operational Excellence:** + +```typescript +const OPERATIONAL_REQUIREMENTS = { + monitoring: { + healthChecks: '/health, /ready, /live endpoints', + alerting: 'Real-time alerts for critical issues', + logging: 'Structured JSON logs with request IDs' + }, + backup: { + frequency: 'Daily full + hourly incremental', + retention: '30 days for backups, 7 years for audit logs', + verification: 'Automated backup validation' + }, + updates: { + securityPatches: 'Applied within 24 hours of release', + minorUpdates: 'Monthly maintenance windows', + majorUpdates: 'Quarterly with thorough testing' + } +}; +``` + +### **8.2 Disaster Recovery:** + +* **8.2.1** Recovery Time Objective (RTO): < 4 ชั่วโมง +* **8.2.2** Recovery Point Objective (RPO): < 1 ชั่วโมง +* **8.2.3** ต้องมี Automated Recovery Procedures +* **8.2.4** ต้องมี Regular Disaster Recovery Testing + +## 👥 **9. ข้อกำหนดด้านการพัฒนา (Development Requirements)** + +### **9.1 Development Workflow:** + +```typescript +const DEVELOPMENT_WORKFLOW = { + environmentSetup: { + time: '30 minutes maximum', + tools: ['Docker', 'Node.js 18+', 'VS Code'], + commands: ['npm run setup', 'npm run dev', 'npm run test'] + }, + gitWorkflow: { + branching: 'Feature branches with PR reviews', + commitConventions: 'Conventional commits', + codeReview: '2 reviewers minimum' + }, + collaboration: { + communication: 'Daily standups, weekly planning', + documentation: 'Auto-generated API docs, ADRs', + knowledgeSharing: 'Pair programming, tech talks' + } +}; +``` + +### **9.2 Code Quality Standards:** + +```typescript +const CODE_QUALITY_STANDARDS = { + backend: { + language: 'TypeScript with strict mode', + style: 'NestJS style guide with ESLint', + testing: '80% coverage, Arrange-Act-Assert pattern' + }, + frontend: { + language: 'TypeScript with strict mode', + style: 'Next.js style guide with Prettier', + testing: '70% coverage, React Testing Library' + }, + database: { + naming: 'Consistent snake_case convention', + indexing: 'Strategic indexes for performance', + migrations: 'TypeORM migrations with rollback' + } +}; +``` + +## 📊 **10. ข้อกำหนดด้านการรายงานและวิเคราะห์ (Reporting & Analytics Requirements)** + +### **10.1 Business Intelligence:** + +* **10.1.1** ต้องมี Real-time Dashboard สำหรับ Key Metrics +* **10.1.2** รองรับ Custom Reports และ Exports +* **10.1.3** ต้องมี Predictive Analytics สำหรับ Workflow Optimization +* **10.1.4** รองรับ Data Visualization ที่หลากหลาย + +### **10.2 Advanced Analytics:** + +```typescript +const ANALYTICS_REQUIREMENTS = { + performanceMetrics: [ + 'document_processing_times', + 'workflow_bottlenecks', + 'user_engagement_metrics', + 'system_utilization_rates' + ], + businessMetrics: [ + 'sla_compliance_rates', + 'document_approval_rates', + 'user_satisfaction_scores', + 'cost_savings_analytics' + ], + predictiveAnalytics: [ + 'workflow_completion_predictions', + 'resource_utilization_forecasts', + 'capacity_planning_insights' + ] +}; +``` + +## 🔧 **11. ข้อกำหนดด้านการปรับปรุงระบบ (System Enhancement Requirements)** + +### **11.1 Scalability & Extensibility:** + +* **11.1.1** ระบบต้องรองรับ Horizontal Scaling +* **11.1.2** ต้องมี Clean Architecture สำหรับการขยาย功能 +* **11.1.3** รองรับ Plugin Architecture สำหรับฟีเจอร์เพิ่มเติม +* **11.1.4** ต้องมี API Versioning Strategy + +### **11.2 Integration Capabilities:** + +```typescript +const INTEGRATION_REQUIREMENTS = { + externalSystems: [ + 'LINE Messaging API', + 'Email Services (SMTP)', + 'External Storage Systems', + 'Third-party Authentication' + ], + apiStandards: { + rest: 'JSON API standards', + webhooks: 'Event-driven notifications', + webSockets: 'Real-time updates', + graphql: 'Optional for complex queries' + } +}; +``` + +## 🛡️ **12. ข้อกำหนดด้านความปลอดภัยขั้นสูง (Advanced Security Requirements)** + +### **12.1 Comprehensive Security Framework:** + +```typescript +const SECURITY_FRAMEWORK = { + authentication: { + primary: 'JWT with refresh tokens', + secondary: 'Multi-factor authentication ready', + session: 'Secure session management' + }, + authorization: { + model: 'RBAC with 4-level hierarchy', + enforcement: 'Attribute-based access control', + auditing: 'Comprehensive permission logging' + }, + dataProtection: { + encryption: 'At rest and in transit', + masking: 'Sensitive data masking', + retention: 'Automated data lifecycle management' + } +}; +``` + +### **12.2 Security Monitoring:** + +* **12.2.1** ต้องมี Real-time Threat Detection +* **12.2.2** รองรับ Security Incident Response +* **12.2.3** ต้องมี Vulnerability Management Program +* **12.2.4** รองรับ Compliance Auditing + +## 📈 **13. ข้อกำหนดด้านประสิทธิภาพขั้นสูง (Advanced Performance Requirements)** + +### **13.1 Optimization Targets:** + +```typescript +const ADVANCED_PERFORMANCE_TARGETS = { + database: { + queryOptimization: 'All complex queries under 50ms', + connectionManagement: 'Intelligent connection pooling', + cachingStrategy: 'Multi-level caching architecture' + }, + application: { + memoryManagement: 'Efficient garbage collection', + cpuUtilization: 'Optimal resource usage', + responseTimes: 'Progressive performance improvements' + }, + frontend: { + loadingOptimization: 'Lazy loading and code splitting', + renderingPerformance: 'Optimized virtual DOM', + assetDelivery: 'CDN and compression strategies' + } +}; +``` + +### **13.2 Load Handling:** + +* **13.2.1** ต้องรองรับ Peak Loads ได้ 3x Normal Capacity +* **13.2.2** ต้องมี Auto-scaling Capabilities +* **13.2.3** รองรับ Graceful Degradation +* **13.2.4** ต้องมี Comprehensive Load Testing + +## 🔄 **14. ข้อกำหนดด้านการอัปเกรดและความเข้ากันได้ (Upgrade & Compatibility Requirements)** + +### **14.1 Version Management:** + +```typescript +const VERSION_MANAGEMENT = { + apiVersioning: { + strategy: 'URL versioning with backward compatibility', + deprecation: '6-month deprecation notice', + migration: 'Automated migration tools' + }, + databaseMigrations: { + strategy: 'TypeORM migrations with rollback capability', + testing: 'Comprehensive migration testing', + automation: 'CI/CD integrated migration pipelines' + } +}; +``` + +### **14.2 Compatibility Requirements:** + +* **14.2.1** ต้องรองรับ Browser ที่ทันสมัย (Latest 2 versions) +* **14.2.2** รองรับ Mobile Responsive Design +* **14.2.3** ต้องมี Accessibility Compliance (WCAG 2.1 AA) +* **14.2.4** รองรับ Internationalization (i18n) + +--- + +## 📋 **สรุปการปรับปรุงจากเวอร์ชันก่อนหน้า** + +### **Security Enhancements:** + +1. **Advanced RBAC** - 4-level permission hierarchy with dynamic roles +2. **Comprehensive Encryption** - Data protection at rest and in transit +3. **Security Monitoring** - Real-time threat detection and incident response +4. **Input Validation** - Advanced OWASP Top 10 protection + +### **Performance Improvements:** + +1. **Optimized JSON Structure** - Simplified and efficient data handling +2. **Advanced Caching** - Multi-level caching strategy +3. **Database Optimization** - Comprehensive query optimization +4. **Frontend Performance** - Enhanced loading and rendering + +### **Architecture Enhancements:** + +1. **Microservices Ready** - Clean architecture for future scalability +2. **API-First Design** - Comprehensive API versioning strategy +3. **Component Architecture** - Structured frontend development +4. **State Management** - Optimized client and server state handling + +### **Operational Excellence:** + +1. **Comprehensive Monitoring** - Application, business, and security metrics +2. **Disaster Recovery** - Automated recovery with clear RTO/RPO +3. **Quality Assurance** - Multi-level testing strategy with quality gates +4. **Development Workflow** - Efficient team collaboration standards + +### **Business Intelligence:** + +1. **Advanced Analytics** - Predictive analytics and business insights +2. **Real-time Reporting** - Comprehensive dashboard and reporting +3. **Custom Exports** - Flexible data export capabilities +4. **Performance Metrics** - Business and technical performance tracking + +## 🎯 **Critical Success Factors** + +1. **Security First** - ทุก Feature ต้องพิจารณาด้านความปลอดภัยเป็นหลัก +2. **Performance Excellence** - ตอบสนองตาม Performance Targets ที่กำหนด +3. **User Experience** - Interface ที่ใช้งานง่ายและมีประสิทธิภาพ +4. **Scalability** - ออกแบบรองรับการขยายตัวในอนาคต +5. **Maintainability** - โค้ดที่สะอาดและบำรุงรักษาง่าย +6. **Compliance** - เป็นไปตามมาตรฐานและกฎระเบียบที่เกี่ยวข้อง + +## 📊 **Implementation Metrics** + +| หมวดหมู่ | เป้าหมาย | วิธีการวัดผล | +| ------------------- | ----------------------------- | -------------------------- | +| **Performance** | API Response < 200ms | 90th percentile monitoring | +| **Security** | Zero Critical Vulnerabilities | Regular security scans | +| **Quality** | 80% Test Coverage | Automated testing reports | +| **Usability** | User Satisfaction > 4.5/5 | User feedback surveys | +| **Reliability** | 99.9% Uptime | System monitoring | +| **Maintainability** | < 5% Code Duplication | Static code analysis | + +--- + +**Document Control:** + +* Document: Application Requirements Specification DMS v1.4.2 +* Version: 1.4.2 +* Date: 2025-11-16 +* Author: System Architecture Team +* Status: FINAL +* Classification: Internal Technical Documentation + +_End of Requirements Specification_ diff --git a/Documnets/Project/working LCBP3-DMS_V1_4_2_Backend_Development_Plan.md b/docs/Markdown/working LCBP3-DMS_V1_4_2_Backend_Development_Plan.md similarity index 100% rename from Documnets/Project/working LCBP3-DMS_V1_4_2_Backend_Development_Plan.md rename to docs/Markdown/working LCBP3-DMS_V1_4_2_Backend_Development_Plan.md