From 94b63dc359a2634f1c38a898506a4e45b9cbeaac Mon Sep 17 00:00:00 2001 From: admin Date: Mon, 17 Nov 2025 22:09:01 +0700 Subject: [PATCH] 251117:2200 rebase .md --- 01_lcbp3_v1_4_0.sql | 3756 +++++------ 01_lcbp3_v1_4_1.sql | 2295 +++++++ docs/.gitignore | 328 +- docs/1.bak | 752 +-- docs/DMS README.bak | 1554 ++--- docs/DMS v1.3.0 - Interactive Report.html | 1446 ++--- docs/Docker compose all.yaml | 386 +- ...BP3-DMS_V1_4_0_Backend_Development_Plan.md | 1310 ++-- docs/LCBP3-DMS_V1_4_0_Data_Dictionary.md | 5670 ++++++++--------- ...P3-DMS_V1_4_0_Frontend_Development_Plan.md | 1666 ++--- docs/LCBP3-DMS_V1_4_0_FullStackJS.md | 960 +-- docs/LCBP3-DMS_V1_4_0_requirements.md | 490 +- ...BP3-DMS_V1_4_1_Backend_Development_Plan.md | 1052 +++ docs/LCBP3-DMS_V1_4_1_Data_Dictionary.md | 2837 +++++++++ ...P3-DMS_V1_4_1_Frontend_Development_Plan.md | 2491 ++++++++ docs/LCBP3-DMS_V1_4_1_FullStackJS.md | 1049 +++ docs/LCBP3-DMS_V1_4_1_requirements.md | 635 ++ docs/LCBP3C2.ovpn | 90 +- docs/Markdown/FullStackJS_Guidelines.md | 384 +- docs/Markdown/FullStackJS_Guidelines01.md | 728 +-- .../LCBP3-DMS Requirements Specification.bak | 272 +- ...P3-DMS V1_1_0_application _requirements.md | 394 +- docs/Markdown/LCBP3-DMS V1_1_1_FullStackJS.md | 910 +-- ...P3-DMS V1_1_1_application _requirements.md | 400 +- .../LCBP3-DMS_V1_1_0_application _database.md | 108 +- .../LCBP3-DMS_V1_2_0_Data_Dictionary.ิbak | 1362 ++-- .../LCBP3-DMS_V1_2_1_Data_Dictionary.md | 1552 ++--- docs/Markdown/LCBP3-DMS_V1_2_1_FullStackJS.md | 916 +-- ...P3-DMS_V1_2_1_application _requirements.md | 418 +- .../LCBP3-DMS_V1_3_0_Data_Dictionary.md | 1548 ++--- docs/Markdown/LCBP3-DMS_V1_3_0_FullStackJS.md | 956 +-- .../Markdown/LCBP3-DMS_V1_3_0_Test_Plan_TH.md | 158 +- .../LCBP3-DMS_V1_3_0_backend_dev_plan.md | 234 +- .../LCBP3-DMS_V1_3_0_frontend_dev_plan.md | 338 +- .../Markdown/LCBP3-DMS_V1_3_0_requirements.md | 420 +- .../LCBP3-DMS_V1_4_0_Data_Dictionary.bak.md | 1800 +++--- docs/Markdown/icon.md | 218 +- docs/NestJS01.bak | 268 +- docs/NextJS01.bak | 64 +- docs/SQL/01_dms_v1_0_0.bak.sql | 2494 ++++---- docs/SQL/01_dms_v1_0_0.sql | 3128 ++++----- docs/SQL/01_dms_v1_0_0_patch.sql | 80 +- docs/SQL/01_dms_v1_0_1.sql | 3072 ++++----- docs/SQL/01_lcbp3_v1_1_0.sql | 3204 +++++----- docs/SQL/01_lcbp3_v1_1_1.sql | 3318 +++++----- docs/SQL/01_lcbp3_v1_2_0.sql | 3752 +++++------ docs/SQL/01_lcbp3_v1_3_0.sql | 3738 +++++------ docs/SQL/01_lcbp3_v1_3_0.txt | 3738 +++++------ docs/SQL/01_lcbp3_v1_4_0.sql | 3844 +++++------ docs/SQL/01_lcbp3_v1_4_0.txt | 3844 +++++------ docs/SQL/Cluad.sql | 768 +-- docs/SQL/seed01.sql | 428 +- docs/SQL/seed02.sql | 630 +- docs/SQL/temp.sql | 284 +- docs/SQL/triggers.sql | 92 +- docs/backend_setup.bak | 340 +- docs/features.bak | 34 +- docs/frontend.md | 186 +- docs/ux_ui.md | 82 +- docs/workflow.bak | 384 +- prompt.md | 300 +- 61 files changed, 45157 insertions(+), 34798 deletions(-) create mode 100644 01_lcbp3_v1_4_1.sql create mode 100644 docs/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md create mode 100644 docs/LCBP3-DMS_V1_4_1_Data_Dictionary.md create mode 100644 docs/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md create mode 100644 docs/LCBP3-DMS_V1_4_1_FullStackJS.md create mode 100644 docs/LCBP3-DMS_V1_4_1_requirements.md diff --git a/01_lcbp3_v1_4_0.sql b/01_lcbp3_v1_4_0.sql index 48e7802..796f130 100644 --- a/01_lcbp3_v1_4_0.sql +++ b/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/01_lcbp3_v1_4_1.sql new file mode 100644 index 0000000..3193fda --- /dev/null +++ b/01_lcbp3_v1_4_1.sql @@ -0,0 +1,2295 @@ +-- ========================================================== +-- 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); +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; \ No newline at end of file diff --git a/docs/.gitignore b/docs/.gitignore index 30b9c7c..6279386 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -1,164 +1,164 @@ -# ---> Node -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -lerna-debug.log* -.pnpm-debug.log* - -# Diagnostic reports (https://nodejs.org/api/report.html) -report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json - -# Runtime data -pids -*.pid -*.seed -*.pid.lock - -# Directory for instrumented libs generated by jscoverage/JSCover -lib-cov - -# Coverage directory used by tools like istanbul -coverage -*.lcov - -# nyc test coverage -.nyc_output - -# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) -.grunt - -# Bower dependency directory (https://bower.io/) -bower_components - -# node-waf configuration -.lock-wscript - -# Compiled binary addons (https://nodejs.org/api/addons.html) -build/Release - -# Dependency directories -node_modules/ -jspm_packages/ - -# Snowpack dependency directory (https://snowpack.dev/) -web_modules/ - -# TypeScript cache -*.tsbuildinfo - -# Optional npm cache directory -.npm - -# Optional eslint cache -.eslintcache - -# Optional stylelint cache -.stylelintcache - -# Microbundle cache -.rpt2_cache/ -.rts2_cache_cjs/ -.rts2_cache_es/ -.rts2_cache_umd/ - -# Optional REPL history -.node_repl_history - -# Output of 'npm pack' -*.tgz - -# Yarn Integrity file -.yarn-integrity - -# dotenv environment variable files -.env -.env.development.local -.env.test.local -.env.production.local -.env.local - -# parcel-bundler cache (https://parceljs.org/) -.cache -.parcel-cache - -# Next.js build output -.next -out - -# Nuxt.js build / generate output -.nuxt -dist - -# Gatsby files -.cache/ -# Comment in the public line in if your project uses Gatsby and not Next.js -# https://nextjs.org/blog/next-9-1#public-directory-support -# public - -# vuepress build output -.vuepress/dist - -# vuepress v2.x temp and cache directory -.temp -.cache - -# vitepress build output -**/.vitepress/dist - -# vitepress cache directory -**/.vitepress/cache - -# Docusaurus cache and generated files -.docusaurus - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Stores VSCode versions used for testing VSCode extensions -.vscode-test - -# yarn v2 -.yarn/cache -.yarn/unplugged -.yarn/build-state.yml -.yarn/install-state.gz -.pnp.* - -# ---> Windows -# Windows thumbnail cache files -Thumbs.db -Thumbs.db:encryptable -ehthumbs.db -ehthumbs_vista.db - -# Dump file -*.stackdump - -# Folder config file -[Dd]esktop.ini - -# Recycle Bin used on file shares -$RECYCLE.BIN/ - -# Windows Installer files -*.cab -*.msi -*.msix -*.msm -*.msp - -# Windows shortcuts -*.lnk - +# ---> Node +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# vitepress build output +**/.vitepress/dist + +# vitepress cache directory +**/.vitepress/cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* + +# ---> Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + diff --git a/docs/1.bak b/docs/1.bak index e2393d2..6cef2da 100644 --- a/docs/1.bak +++ b/docs/1.bak @@ -1,377 +1,377 @@ -ผมได้ตรวจสอบไฟล์ `FullStackJS_Guidelines01.md` และ `01_lcbp3_v1_1_0.sql` ของคุณแล้ว และได้อัปเดตไฟล์ Guidelines (Markdown) ให้สอดคล้องกับโครงสร้างฐานข้อมูล (SQL) ที่คุณกำหนดไว้อย่างสมบูรณ์ครับ - -การเปลี่ยนแปลงที่สำคัญคือ: - -1. **การจัดการ UI (UI Conflict):** แก้ไขความขัดแย้งในไฟล์เดิมที่ระบุทั้ง **Tailwind/Shadcn** และ **Bootstrap** ผมได้รวมศูนย์การพัฒนา UI ไปที่ **Tailwind/Shadcn** ตามที่ระบุไว้ในโปรไฟล์นักพัฒนา (Frontend Developer Profile) และลบส่วนของ Bootstrap ที่ซ้ำซ้อนออกไป -2. **DMS-Specific Conventions:** อัปเดตส่วนนี้ทั้งหมดเพื่อให้ตรงกับสคีม SQL ของคุณ - * เปลี่ยนชื่อโปรเจกต์อ้างอิงเป็น **LCBP3-DMS** - * ปรับปรุงแผนผัง **Domain Modules** ให้สะท้อนโครงสร้างจริง (เช่น `correspondences` เป็นศูนย์กลาง, เพิ่ม `circulations`) - * อัปเดตตาราง **AuditLog** ให้ตรงกับฟิลด์ใน SQL (เช่น เพิ่ม `entity_type`, `details_json`, `ip_address`) - * อัปเดตตัวอย่าง **RBAC Permissions** ให้ตรงกับสิทธิ์ที่มีในตาราง `permissions` ของคุณ - * ชี้แจง - เรื่อง **File Handling** ว่าตาราง `attachments` จะเชื่อมโยงผ่าน `correspondence_id` - * อัปเดตส่วน **Reporting** เพื่ออ้างอิงถึง `VIEWS` (เช่น `v_current_rfas`) ที่คุณได้สร้างไว้ใน SQL - * ย้ายส่วน **"Recommended Enhancements"** (เช่น soft delete, indexes) ที่มีอยู่แล้วใน SQL ไปยังส่วนใหม่ชื่อ **"Implemented Standards"** เพื่อสะท้อนว่างานส่วนนี้เสร็จแล้ว - -นี่คือไฟล์ `FullStackJS_Guidelines01.md` ฉบับที่อัปเดตแล้วครับ: - ------ - -# FullStackJS Development Guidelines - -## 🧠 General Philosophy - -Unified best practices for **NestJS Backend**, **NextJS Frontend**, and **Tailwind-based UI/UX** development in TypeScript environments. -Focus on **clarity**, **maintainability**, **consistency**, and **accessibility** across the entire stack. - ------ - -## ⚙️ TypeScript General Guidelines - -### Basic Principles - - - Use **English** for all code and documentation. - - Explicitly type all variables, parameters, and return values. - - Avoid `any`; create custom types or interfaces. - - Use **JSDoc** for public classes and methods. - - Export only **one main symbol** per file. - - Avoid blank lines within functions. - -### Naming Conventions - -| Entity | Convention | Example | -|:--|:--|:--| -| Classes | PascalCase | `UserService` | -| Variables & Functions | camelCase | `getUserInfo` | -| Files & Folders | kebab-case | `user-service.ts` | -| Environment Variables | UPPERCASE | `DATABASE_URL` | -| Booleans | Verb + Noun | `isActive`, `canDelete`, `hasPermission` | - -Use full words — no abbreviations — except for standard ones (`API`, `URL`, `req`, `res`, `err`, `ctx`). - ------ - -## 🧩 Functions - - - Write short, single-purpose functions (\<20 lines). - - Use **early returns** to reduce nesting. - - Use **map**, **filter**, **reduce** instead of loops when suitable. - - Prefer **arrow functions** for short logic, **named functions** otherwise. - - Use **default parameters** over null checks. - - Group multiple parameters into a single object (RO-RO pattern). - - Return typed objects, not primitives. - - Maintain a single abstraction level per function. - ------ - -## 🧱 Data Handling - - - Encapsulate data in composite types. - - Use **immutability** with `readonly` and `as const`. - - Perform validations in classes or DTOs, not within business functions. - - Always validate data using typed DTOs. - ------ - -## 🧰 Classes - - - Follow **SOLID** principles. - - Prefer **composition over inheritance**. - - Define **interfaces** for contracts. - - Keep classes focused and small (\<200 lines, \<10 methods, \<10 properties). - ------ - -## 🚨 Error Handling - - - Use exceptions for unexpected errors. - - Catch only to fix or add context; otherwise, use global error handlers. - - Always provide meaningful error messages. - ------ - -## 🧪 Testing (General) - - - Use the **Arrange–Act–Assert** pattern. - - Use descriptive test variable names (`inputData`, `expectedOutput`). - - Write **unit tests** for all public methods. - - Mock external dependencies. - - Add **acceptance tests** per module using Given–When–Then. - ------ - -# 🏗️ Backend (NestJS) - -### Principles - - - **Modular architecture**: - - One module per domain. - - Controller → Service → Repository (Model) structure. - - DTOs validated with **class-validator**. - - Use **MikroORM** (or TypeORM/Prisma) for persistence, aligning with the MariaDB schema. - - Encapsulate reusable code in a **common module** (`@app/common`): - - Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators. - -### Core Functionalities - - - Global **filters** for exception handling. - - **Middlewares** for request handling. - - **Guards** for permissions and RBAC. - - **Interceptors** for response transformation and logging. - -### Testing - - - Use **Jest** for testing. - - Test each controller and service. - - Add `admin/test` endpoint as a smoke test. - ------ - -# 🖥️ Frontend (NextJS / React / UI) - -### Developer Profile - -Senior-level TypeScript + React/NextJS engineer. -Expert in **TailwindCSS**, **Shadcn/UI**, and **Radix** for UI development. - -### Code Implementation Guidelines - - - Use **early returns** for clarity. - - Always style with **TailwindCSS** classes. - - Prefer `class:` conditional syntax (or `clsx` utility) over ternary operators in class strings. - - Use **const arrow functions** for components and handlers. - - Event handlers start with `handle...` (e.g., `handleClick`, `handleSubmit`). - - Include accessibility attributes: - `tabIndex="0"`, `aria-label`, `onKeyDown`, etc. - - Ensure all code is **complete**, **tested**, and **DRY**. - - Always import required modules explicitly. - -### UI/UX with React - - - Use **semantic HTML**. - - Apply **responsive Tailwind** classes (`sm:`, `md:`, `lg:`). - - Maintain visual hierarchy with typography and spacing. - - Use **Shadcn** components (Button, Input, Card, etc.) for consistent UI. - - Keep components small and focused. - - Use utility classes for quick styling (spacing, colors, text, etc.). - - Ensure **ARIA compliance** and semantic markup. - -### Form Validation & Errors - - - Use client-side libraries like `zod` and `react-hook-form`. - - Show errors with **alert components** or inline messages. - - Include labels, placeholders, and feedback messages. - ------ - -# 🔗 Full Stack Integration Guidelines - -| Aspect | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) | -|:--|:--|:--|:--| -| API | REST / GraphQL Controllers | API hooks via fetch/axios/SWR | Components consuming data | -| Validation | `class-validator` DTOs | `zod` / `react-hook-form` | Shadcn form/input states | -| Auth | Guards, JWT | NextAuth / cookies | Auth UI states (loading, signed in) | -| Errors | Global filters | Toasts / modals | Alerts / feedback text | -| Testing | Jest (unit/e2e) | Vitest / Playwright | Visual regression | -| Styles | Scoped modules (if needed) | Tailwind / Shadcn | Tailwind utilities | -| Accessibility | Guards + filters | ARIA attributes | Semantic HTML | - ------ - -# 🗂️ DMS-Specific Conventions (LCBP3-DMS) - -This section extends the general FullStackJS guidelines for the **LCBP3-DMS** project, focusing on document approval workflows (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation). - -## 🧱 Backend Domain Modules - -Use a modular domain structure reflecting the SQL schema. `correspondences` should act as the central hub. - -``` -src/ - ├─ modules/ - │ ├─ correspondences/ (Core: Master documents, Revisions, Attachments) - │ ├─ rfas/ (RFA logic, Revisions, Workflows, Items) - │ ├─ drawings/ (ShopDrawings, ContractDrawings, Categories) - │ ├─ circulations/ (Internal circulation, Templates, Assignees) - │ ├─ transmittals/ (Transmittal logic, Items) - │ ├─ projects-contracts/ (Projects, Contracts, Organizations, Parties) - │ ├─ users-auth/ (Users, Roles, Permissions, Auth) - │ ├─ audit-log/ - │ └─ common/ -``` - -### Naming Convention - -| Entity | Example (from SQL) | -|:--|:--| -| Table | `correspondences`, `rfa_revisions`, `contract_parties` | -| Column | `correspondence_id`, `created_by`, `is_current` | -| DTO | `CreateRfaDto`, `UpdateCorrespondenceDto` | -| Controller | `rfas.controller.ts` | -| Service | `correspondences.service.ts` | - ------ - -## 🧩 RBAC & Permission Control - -Use decorators to enforce access rights, referencing permissions from the `permissions` table. - -```ts -@RequirePermission('rfas.respond') // Must match a 'permission_code' -@Put(':id') -updateRFA(@Param('id') id: string) { - return this.rfaService.update(id); -} -``` - -### Roles - - - **SUPER\_ADMIN**: Full system access (from `roles` table). - - **ADMIN**: Organization-level access. - - **EDITOR**: Module-specific write access. - - **VIEWER**: Read-only access. - -### Example Permissions (from `permissions` table) - - - `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` - -Seed mapping between roles and permissions via seeder scripts (as seen in SQL file). - ------ - -## 🧾 AuditLog Standard - -Log all CRUD and mapping operations to the `audit_logs` table. - -| Field | Type (from SQL) | Description | -|:--|:--|:--| -| `audit_id` | `BIGINT` | Primary Key | -| `user_id` | `INT` | User performing the action (FK -\> users) | -| `action` | `VARCHAR(100)` | `rfa.create`, `correspondence.update`, `login.success` | -| `entity_type`| `VARCHAR(50)` | Table/module name, e.g., 'rfa', 'correspondence' | -| `entity_id` | `VARCHAR(50)` | Primary ID of the affected record | -| `details_json`| `JSON` | Contextual data (e.g., changed fields) | -| `ip_address` | `VARCHAR(45)` | Actor's IP address | -| `user_agent` | `VARCHAR(255)`| Actor's User Agent | -| `created_at` | `TIMESTAMP` | UTC timestamp | - -Example implementation: - -```ts -await this.auditLogService.log({ - userId: user.id, - action: 'rfa.update_status', - entityType: 'rfa_revisions', - entityId: rfaRevision.id, - detailsJson: { from: 'DFT', to: 'FAP' }, - ipAddress: req.ip, -}); -``` - ------ - -## 📂 File Handling - -### File Upload Standard - - - Centralize all uploads via the `attachments` table. - - Upload path (convention): `/storage/{year}/{month}/` - - Stored filename: Use `stored_filename` (e.g., UUID or hash) to prevent conflicts. `original_filename` is for display. - - Allowed types: `pdf, dwg, docx, xlsx, zip` - - Max size: **50 MB** - - Store outside webroot. - - Serve via secure endpoint `/files/:attachment_id/download`. - -### Access Control - -File access is not direct. The `/files/:attachment_id/download` endpoint must: - -1. Find the `attachment` record. -2. Get its `correspondence_id`. -3. Verify the user has permission to view that specific `correspondence` (or its related RFA, Transmittal, etc.). - ------ - -## 📊 Reporting & Exports - -### Reporting Views (from SQL) - -Reports should be built primarily from the pre-defined database Views: - - - `v_current_correspondences`: For all current non-RFA document revisions. - - `v_current_rfas`: For all current RFA revisions and their master data. - - `v_contract_parties_all`: For auditing project/contract/organization relationships. - -These views serve as the primary source for server-side reporting and data exports. - -### Export Rules - - - Export formats: CSV, Excel, PDF. - - Provide print view. - - Include source entity link (e.g., `/rfas/:id`). - ------ - -## 🧮 Frontend: DataTable & Form Patterns - -### DataTable (Server‑Side) - - - Endpoint: `/api/{module}?page=1&pageSize=20&sort=...&filter=...` - - Must support: pagination, sorting, search, filters. - - Always display latest revision inline (for RFA/Drawing). - -### Form Standards - - - Dependent dropdowns must be implemented (as supported by schema): - - Project → Contract Drawing Volumes - - Contract Drawing Category → Sub-Category - - RFA (Shop Drawing type) → Linkable Shop Drawing Revisions - - File upload: preview + validation (via `attachments` logic). - - Submit via API with toast feedback. - ------ - -## 🧭 Dashboard & Activity Feed - -### Dashboard Cards - - - Show latest Correspondences, RFAs, Circulations. - - Include KPI summaries (e.g., "RFAs Pending Approval"). - - Include quick links to modules. - -### Activity Feed - - - Display recent `audit_logs` actions (10 latest) relevant to the user. - - - -```ts -// Example API response -[ - { user: 'editor01', action: 'Updated RFA (LCBP3C1-RFA-001)', time: '2025-11-04T09:30Z' } -] -``` - ------ - -## ✅ Implemented Standards (from SQL v1.1.0) - -This section confirms that the following best practices are already part of the database design and should be leveraged, not re-implemented. - - - ✅ **Soft Delete:** Implemented via `deleted_at` columns on key tables (e.g., `correspondences`, `rfas`, `project_parties`). Logic must filter for `deleted_at IS NULL`. - - ✅ **Database Indexes:** The schema is heavily indexed on foreign keys and common query columns (e.g., `idx_rr_rfa`, `idx_cor_project`, `idx_cr_is_current`) for performance. - - ✅ **RBAC Structure:** A comprehensive `users`, `roles`, `permissions`, `user_roles`, and `user_project_roles` system is in place. - - ✅ **Data Seeding:** Master data (roles, permissions, organization\_roles, initial users, project parties) is included in the schema script. - -## 🧩 Recommended Enhancements (Future) - - - ✅ Implement Fulltext search on fields like `correspondence_revisions.title` or `details`. - - ✅ Create a background job (using **n8n** as noted in SQL comments) for RFA deadline reminders based on `due_date`. - - ✅ Add a periodic cleanup job for `attachments` that are not linked to any `correspondence_id` (orphaned files). - +ผมได้ตรวจสอบไฟล์ `FullStackJS_Guidelines01.md` และ `01_lcbp3_v1_1_0.sql` ของคุณแล้ว และได้อัปเดตไฟล์ Guidelines (Markdown) ให้สอดคล้องกับโครงสร้างฐานข้อมูล (SQL) ที่คุณกำหนดไว้อย่างสมบูรณ์ครับ + +การเปลี่ยนแปลงที่สำคัญคือ: + +1. **การจัดการ UI (UI Conflict):** แก้ไขความขัดแย้งในไฟล์เดิมที่ระบุทั้ง **Tailwind/Shadcn** และ **Bootstrap** ผมได้รวมศูนย์การพัฒนา UI ไปที่ **Tailwind/Shadcn** ตามที่ระบุไว้ในโปรไฟล์นักพัฒนา (Frontend Developer Profile) และลบส่วนของ Bootstrap ที่ซ้ำซ้อนออกไป +2. **DMS-Specific Conventions:** อัปเดตส่วนนี้ทั้งหมดเพื่อให้ตรงกับสคีม SQL ของคุณ + * เปลี่ยนชื่อโปรเจกต์อ้างอิงเป็น **LCBP3-DMS** + * ปรับปรุงแผนผัง **Domain Modules** ให้สะท้อนโครงสร้างจริง (เช่น `correspondences` เป็นศูนย์กลาง, เพิ่ม `circulations`) + * อัปเดตตาราง **AuditLog** ให้ตรงกับฟิลด์ใน SQL (เช่น เพิ่ม `entity_type`, `details_json`, `ip_address`) + * อัปเดตตัวอย่าง **RBAC Permissions** ให้ตรงกับสิทธิ์ที่มีในตาราง `permissions` ของคุณ + * ชี้แจง + เรื่อง **File Handling** ว่าตาราง `attachments` จะเชื่อมโยงผ่าน `correspondence_id` + * อัปเดตส่วน **Reporting** เพื่ออ้างอิงถึง `VIEWS` (เช่น `v_current_rfas`) ที่คุณได้สร้างไว้ใน SQL + * ย้ายส่วน **"Recommended Enhancements"** (เช่น soft delete, indexes) ที่มีอยู่แล้วใน SQL ไปยังส่วนใหม่ชื่อ **"Implemented Standards"** เพื่อสะท้อนว่างานส่วนนี้เสร็จแล้ว + +นี่คือไฟล์ `FullStackJS_Guidelines01.md` ฉบับที่อัปเดตแล้วครับ: + +----- + +# FullStackJS Development Guidelines + +## 🧠 General Philosophy + +Unified best practices for **NestJS Backend**, **NextJS Frontend**, and **Tailwind-based UI/UX** development in TypeScript environments. +Focus on **clarity**, **maintainability**, **consistency**, and **accessibility** across the entire stack. + +----- + +## ⚙️ TypeScript General Guidelines + +### Basic Principles + + - Use **English** for all code and documentation. + - Explicitly type all variables, parameters, and return values. + - Avoid `any`; create custom types or interfaces. + - Use **JSDoc** for public classes and methods. + - Export only **one main symbol** per file. + - Avoid blank lines within functions. + +### Naming Conventions + +| Entity | Convention | Example | +|:--|:--|:--| +| Classes | PascalCase | `UserService` | +| Variables & Functions | camelCase | `getUserInfo` | +| Files & Folders | kebab-case | `user-service.ts` | +| Environment Variables | UPPERCASE | `DATABASE_URL` | +| Booleans | Verb + Noun | `isActive`, `canDelete`, `hasPermission` | + +Use full words — no abbreviations — except for standard ones (`API`, `URL`, `req`, `res`, `err`, `ctx`). + +----- + +## 🧩 Functions + + - Write short, single-purpose functions (\<20 lines). + - Use **early returns** to reduce nesting. + - Use **map**, **filter**, **reduce** instead of loops when suitable. + - Prefer **arrow functions** for short logic, **named functions** otherwise. + - Use **default parameters** over null checks. + - Group multiple parameters into a single object (RO-RO pattern). + - Return typed objects, not primitives. + - Maintain a single abstraction level per function. + +----- + +## 🧱 Data Handling + + - Encapsulate data in composite types. + - Use **immutability** with `readonly` and `as const`. + - Perform validations in classes or DTOs, not within business functions. + - Always validate data using typed DTOs. + +----- + +## 🧰 Classes + + - Follow **SOLID** principles. + - Prefer **composition over inheritance**. + - Define **interfaces** for contracts. + - Keep classes focused and small (\<200 lines, \<10 methods, \<10 properties). + +----- + +## 🚨 Error Handling + + - Use exceptions for unexpected errors. + - Catch only to fix or add context; otherwise, use global error handlers. + - Always provide meaningful error messages. + +----- + +## 🧪 Testing (General) + + - Use the **Arrange–Act–Assert** pattern. + - Use descriptive test variable names (`inputData`, `expectedOutput`). + - Write **unit tests** for all public methods. + - Mock external dependencies. + - Add **acceptance tests** per module using Given–When–Then. + +----- + +# 🏗️ Backend (NestJS) + +### Principles + + - **Modular architecture**: + - One module per domain. + - Controller → Service → Repository (Model) structure. + - DTOs validated with **class-validator**. + - Use **MikroORM** (or TypeORM/Prisma) for persistence, aligning with the MariaDB schema. + - Encapsulate reusable code in a **common module** (`@app/common`): + - Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators. + +### Core Functionalities + + - Global **filters** for exception handling. + - **Middlewares** for request handling. + - **Guards** for permissions and RBAC. + - **Interceptors** for response transformation and logging. + +### Testing + + - Use **Jest** for testing. + - Test each controller and service. + - Add `admin/test` endpoint as a smoke test. + +----- + +# 🖥️ Frontend (NextJS / React / UI) + +### Developer Profile + +Senior-level TypeScript + React/NextJS engineer. +Expert in **TailwindCSS**, **Shadcn/UI**, and **Radix** for UI development. + +### Code Implementation Guidelines + + - Use **early returns** for clarity. + - Always style with **TailwindCSS** classes. + - Prefer `class:` conditional syntax (or `clsx` utility) over ternary operators in class strings. + - Use **const arrow functions** for components and handlers. + - Event handlers start with `handle...` (e.g., `handleClick`, `handleSubmit`). + - Include accessibility attributes: + `tabIndex="0"`, `aria-label`, `onKeyDown`, etc. + - Ensure all code is **complete**, **tested**, and **DRY**. + - Always import required modules explicitly. + +### UI/UX with React + + - Use **semantic HTML**. + - Apply **responsive Tailwind** classes (`sm:`, `md:`, `lg:`). + - Maintain visual hierarchy with typography and spacing. + - Use **Shadcn** components (Button, Input, Card, etc.) for consistent UI. + - Keep components small and focused. + - Use utility classes for quick styling (spacing, colors, text, etc.). + - Ensure **ARIA compliance** and semantic markup. + +### Form Validation & Errors + + - Use client-side libraries like `zod` and `react-hook-form`. + - Show errors with **alert components** or inline messages. + - Include labels, placeholders, and feedback messages. + +----- + +# 🔗 Full Stack Integration Guidelines + +| Aspect | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) | +|:--|:--|:--|:--| +| API | REST / GraphQL Controllers | API hooks via fetch/axios/SWR | Components consuming data | +| Validation | `class-validator` DTOs | `zod` / `react-hook-form` | Shadcn form/input states | +| Auth | Guards, JWT | NextAuth / cookies | Auth UI states (loading, signed in) | +| Errors | Global filters | Toasts / modals | Alerts / feedback text | +| Testing | Jest (unit/e2e) | Vitest / Playwright | Visual regression | +| Styles | Scoped modules (if needed) | Tailwind / Shadcn | Tailwind utilities | +| Accessibility | Guards + filters | ARIA attributes | Semantic HTML | + +----- + +# 🗂️ DMS-Specific Conventions (LCBP3-DMS) + +This section extends the general FullStackJS guidelines for the **LCBP3-DMS** project, focusing on document approval workflows (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation). + +## 🧱 Backend Domain Modules + +Use a modular domain structure reflecting the SQL schema. `correspondences` should act as the central hub. + +``` +src/ + ├─ modules/ + │ ├─ correspondences/ (Core: Master documents, Revisions, Attachments) + │ ├─ rfas/ (RFA logic, Revisions, Workflows, Items) + │ ├─ drawings/ (ShopDrawings, ContractDrawings, Categories) + │ ├─ circulations/ (Internal circulation, Templates, Assignees) + │ ├─ transmittals/ (Transmittal logic, Items) + │ ├─ projects-contracts/ (Projects, Contracts, Organizations, Parties) + │ ├─ users-auth/ (Users, Roles, Permissions, Auth) + │ ├─ audit-log/ + │ └─ common/ +``` + +### Naming Convention + +| Entity | Example (from SQL) | +|:--|:--| +| Table | `correspondences`, `rfa_revisions`, `contract_parties` | +| Column | `correspondence_id`, `created_by`, `is_current` | +| DTO | `CreateRfaDto`, `UpdateCorrespondenceDto` | +| Controller | `rfas.controller.ts` | +| Service | `correspondences.service.ts` | + +----- + +## 🧩 RBAC & Permission Control + +Use decorators to enforce access rights, referencing permissions from the `permissions` table. + +```ts +@RequirePermission('rfas.respond') // Must match a 'permission_code' +@Put(':id') +updateRFA(@Param('id') id: string) { + return this.rfaService.update(id); +} +``` + +### Roles + + - **SUPER\_ADMIN**: Full system access (from `roles` table). + - **ADMIN**: Organization-level access. + - **EDITOR**: Module-specific write access. + - **VIEWER**: Read-only access. + +### Example Permissions (from `permissions` table) + + - `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` + +Seed mapping between roles and permissions via seeder scripts (as seen in SQL file). + +----- + +## 🧾 AuditLog Standard + +Log all CRUD and mapping operations to the `audit_logs` table. + +| Field | Type (from SQL) | Description | +|:--|:--|:--| +| `audit_id` | `BIGINT` | Primary Key | +| `user_id` | `INT` | User performing the action (FK -\> users) | +| `action` | `VARCHAR(100)` | `rfa.create`, `correspondence.update`, `login.success` | +| `entity_type`| `VARCHAR(50)` | Table/module name, e.g., 'rfa', 'correspondence' | +| `entity_id` | `VARCHAR(50)` | Primary ID of the affected record | +| `details_json`| `JSON` | Contextual data (e.g., changed fields) | +| `ip_address` | `VARCHAR(45)` | Actor's IP address | +| `user_agent` | `VARCHAR(255)`| Actor's User Agent | +| `created_at` | `TIMESTAMP` | UTC timestamp | + +Example implementation: + +```ts +await this.auditLogService.log({ + userId: user.id, + action: 'rfa.update_status', + entityType: 'rfa_revisions', + entityId: rfaRevision.id, + detailsJson: { from: 'DFT', to: 'FAP' }, + ipAddress: req.ip, +}); +``` + +----- + +## 📂 File Handling + +### File Upload Standard + + - Centralize all uploads via the `attachments` table. + - Upload path (convention): `/storage/{year}/{month}/` + - Stored filename: Use `stored_filename` (e.g., UUID or hash) to prevent conflicts. `original_filename` is for display. + - Allowed types: `pdf, dwg, docx, xlsx, zip` + - Max size: **50 MB** + - Store outside webroot. + - Serve via secure endpoint `/files/:attachment_id/download`. + +### Access Control + +File access is not direct. The `/files/:attachment_id/download` endpoint must: + +1. Find the `attachment` record. +2. Get its `correspondence_id`. +3. Verify the user has permission to view that specific `correspondence` (or its related RFA, Transmittal, etc.). + +----- + +## 📊 Reporting & Exports + +### Reporting Views (from SQL) + +Reports should be built primarily from the pre-defined database Views: + + - `v_current_correspondences`: For all current non-RFA document revisions. + - `v_current_rfas`: For all current RFA revisions and their master data. + - `v_contract_parties_all`: For auditing project/contract/organization relationships. + +These views serve as the primary source for server-side reporting and data exports. + +### Export Rules + + - Export formats: CSV, Excel, PDF. + - Provide print view. + - Include source entity link (e.g., `/rfas/:id`). + +----- + +## 🧮 Frontend: DataTable & Form Patterns + +### DataTable (Server‑Side) + + - Endpoint: `/api/{module}?page=1&pageSize=20&sort=...&filter=...` + - Must support: pagination, sorting, search, filters. + - Always display latest revision inline (for RFA/Drawing). + +### Form Standards + + - Dependent dropdowns must be implemented (as supported by schema): + - Project → Contract Drawing Volumes + - Contract Drawing Category → Sub-Category + - RFA (Shop Drawing type) → Linkable Shop Drawing Revisions + - File upload: preview + validation (via `attachments` logic). + - Submit via API with toast feedback. + +----- + +## 🧭 Dashboard & Activity Feed + +### Dashboard Cards + + - Show latest Correspondences, RFAs, Circulations. + - Include KPI summaries (e.g., "RFAs Pending Approval"). + - Include quick links to modules. + +### Activity Feed + + - Display recent `audit_logs` actions (10 latest) relevant to the user. + + + +```ts +// Example API response +[ + { user: 'editor01', action: 'Updated RFA (LCBP3C1-RFA-001)', time: '2025-11-04T09:30Z' } +] +``` + +----- + +## ✅ Implemented Standards (from SQL v1.1.0) + +This section confirms that the following best practices are already part of the database design and should be leveraged, not re-implemented. + + - ✅ **Soft Delete:** Implemented via `deleted_at` columns on key tables (e.g., `correspondences`, `rfas`, `project_parties`). Logic must filter for `deleted_at IS NULL`. + - ✅ **Database Indexes:** The schema is heavily indexed on foreign keys and common query columns (e.g., `idx_rr_rfa`, `idx_cor_project`, `idx_cr_is_current`) for performance. + - ✅ **RBAC Structure:** A comprehensive `users`, `roles`, `permissions`, `user_roles`, and `user_project_roles` system is in place. + - ✅ **Data Seeding:** Master data (roles, permissions, organization\_roles, initial users, project parties) is included in the schema script. + +## 🧩 Recommended Enhancements (Future) + + - ✅ Implement Fulltext search on fields like `correspondence_revisions.title` or `details`. + - ✅ Create a background job (using **n8n** as noted in SQL comments) for RFA deadline reminders based on `due_date`. + - ✅ Add a periodic cleanup job for `attachments` that are not linked to any `correspondence_id` (orphaned files). + ----- \ No newline at end of file diff --git a/docs/DMS README.bak b/docs/DMS README.bak index 4e83e9d..616a9da 100644 --- a/docs/DMS README.bak +++ b/docs/DMS README.bak @@ -1,777 +1,777 @@ -# 📝 0. Project Title: Document Management System (DMS) Web Application for Laem Chabang Port Development Project, Phase 3 - -## 0. Project - -### 📌 0.1 Project Overview / Description - -- ระบบ Document Management System (DMS) เป็นเว็บแอปพลิเคชันที่ออกแบบมาเพื่อจัดการเอกสารภายในโครงการอย่างมีประสิทธิภาพ -- โดยมีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร -- ระบบนี้จะช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล -- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ - -### 🎯 0.2 Objectives - -- พัฒนาระบบที่สามารถจัดการเอกสารได้อย่างเป็นระบบ -- ลดความซ้ำซ้อนในการจัดเก็บเอกสาร -- เพิ่มความปลอดภัยในการเข้าถึงและจัดการเอกสาร -- รองรับการทำงานร่วมกันแบบออนไลน์ - -### 📦 0.3 Scope of Work - -ระบบจะครอบคลุมฟีเจอร์หลักดังนี้: - -- การลงทะเบียนและเข้าสู่ระบบ ของผู้ใช้งาน -- การอัปโหลดและจัดเก็บเอกสารในรูปแบบต่าง ๆ (PDF, DOCX, XLSX ฯลฯ) -- การจัดหมวดหมู่และแท็กเอกสาร -- การค้นหาเอกสารด้วยคำสำคัญหรือฟิลเตอร์ -- การกำหนดสิทธิ์การเข้าถึงเอกสาร (เช่น อ่านอย่างเดียว, แก้ไข, ลบ) -- การบันทึกประวัติการใช้งานเอกสาร (Audit Trail) -- การมอบหมายงานให้กับผู้เกี่ยวข้อง และแจ้งเตือนเมื่อมีการมอบหมายงาน -- การแจ้งเตือนเมื่อถึงกำหนดวันที่ต้องส่งเอกสารต่อให้ ผู้เกี่ยวข้องอื่นๆ -- การแจ้งเตือนเมื่อมีการเปลี่ยนแปลงเอกสาร - -### 👥 0.4 Target Users - -- พนักงานภายใน ขององค์กร -- พนักงานควบคุมเอกสาร (Document Control)/ ผู้ดูแลระบบขององค์กร (admin) -- ผู้จัดการฝ่ายเอกสาร ขององค์กร -- ผู้จัดการโครงการ ขององค์กร -- คณะกรรมการ ของโครงการ -- ผู้ดูแลระบบ IT ของโครงการ (superadmin) - -### 📈 0.5 Expected Outcomes - -- ลดเวลาในการค้นหาเอกสารลงอย่างน้อย 50% -- ลดเวลาในการจัดทำรายงานเอกสาร ประจำวัน, ประจำสัปดาห์, ประจำเดือน, ประจำปี และ รายงานเอกสารทั้งโครงการ -- ลดการใช้เอกสารกระดาษในองค์กร -- เพิ่มความปลอดภัยในการจัดเก็บข้อมูล -- รองรับการทำงานแบบ Remote Work - -### 📘 0.6 Requirements Use Cases - -#### 📘 Use Case: Upload Document - -Actor: พนักงานควบคุมเอกสาร (Document Control) -Description: พนักงานควบคุมเอกสารสามารถอัปโหลดเอกสารเข้าสู่ระบบเพื่อจัดเก็บและใช้งานในภายหลัง -Preconditions: พนักงานควบคุมเอกสารต้องเข้าสู่ระบบก่อน -Main Flow: - -พนักงานควบคุมเอกสารเลือกเมนู “อัปโหลดเอกสาร” -เลือกไฟล์จากเครื่องคอมพิวเตอร์ -กรอกข้อมูลประกอบ เช่น ชื่อเอกสาร หมวดหมู่ แท็ก -กดปุ่ม “อัปโหลด” -ระบบบันทึกเอกสารและแสดงผลการอัปโหลดสำเร็จ - -Postconditions: เอกสารถูกจัดเก็บในระบบและสามารถค้นหาได้ - -#### 📘 Use Case: Assign Users to Document - -Actor: พนักงานควบคุมเอกสาร (Document Control) -Description: พนักงานควบคุมเอกสารสามารถ มอบหมายงานให้กับ Users -Preconditions: พนักงานควบคุมเอกสารต้องเข้าสู่ระบบก่อน, เอกสารต้องอัปโหลดเรียบร้อยแล้ว -Main Flow: - -พนักงานควบคุมเอกสารเลือกเมนู “มอบหมายงาน” -เลือกเอกสารในระบบ -เลือก Users กำหนดวันสิ้นสุดงาน -กดปุ่ม “มอบหมายงาน” -ระบบบันทึกเอกสารและแสดงผลการมอบหมายงานสำเร็จ - -Postconditions: งานที่มอยหมาย จัดเก็บในระบบและสามารถค้นหาได้ - -#### 📘 Use Case: Search Document - -Actor: ผู้ใช้งานทั่วไป -Description: ผู้ใช้งานสามารถค้นหาเอกสารจากระบบด้วยคำสำคัญหรือฟิลเตอร์ -Preconditions: ผู้ใช้งานต้องเข้าสู่ระบบ -Main Flow: - -ผู้ใช้งานกรอกคำค้นหรือเลือกฟิลเตอร์ (หมวดหมู่, วันที่, ผู้สร้าง, ผู้ได้รับมอบหมายงาน, สถานะ, title, subject) -กดปุ่ม “ค้นหา” -ระบบแสดงรายการเอกสารที่ตรงกับเงื่อนไข - -Postconditions: ผู้ใช้งานสามารถเปิดดูหรือดาวน์โหลดเอกสารที่ค้นพบได้ - -#### 📘 Use Case: Manage Access - -Actor: ผู้ดูแลระบบโครงการ (superadmin) / ผู้ดูแลระบบขององค์กร (admin) -Description: ผู้ดูแลระบบสามารถกำหนดสิทธิ์การเข้าถึงเอกสารให้กับผู้ใช้งาน -Preconditions: ผู้ดูแลระบบต้องเข้าสู่ระบบ -Main Flow: - -ผู้ดูแลระบบเลือกเอกสาร -กด “จัดการสิทธิ์” -เลือกผู้ใช้งานและกำหนดสิทธิ์ (อ่าน, แก้ไข, ลบ) -กด “บันทึก” - -Postconditions: สิทธิ์การเข้าถึงเอกสารถูกปรับตามที่กำหนด - -#### 📘 Use Case: View Document History - -Actor: ผู้ใช้งานทั่วไป / ผู้ดูแลระบบ -Description: ผู้ใช้งานสามารถดูประวัติการใช้งานเอกสาร เช่น การแก้ไข การดาวน์โหลด -Preconditions: ผู้ใช้งานต้องมีสิทธิ์เข้าถึงเอกสาร -Main Flow: - -ผู้ใช้งานเปิดเอกสาร -เลือก “ดูประวัติ” -ระบบแสดงรายการกิจกรรมที่เกี่ยวข้องกับเอกสาร - -Postconditions: ผู้ใช้งานสามารถตรวจสอบการเปลี่ยนแปลงย้อนหลังได้ - -### 🔄 0.7 Workflow อัตโนมัติในระบบ DMS - -✅ ประโยชน์ของ Workflow อัตโนมัติใน DMS - -- ลดภาระงานซ้ำ ๆ ของผู้ใช้งาน -- เพิ่มความปลอดภัยและการควบคุมเอกสาร -- เพิ่มความเร็วในการดำเนินงาน -- ลดข้อผิดพลาดจากการทำงานด้วยมือ - -#### 🧩 Workflow: 1. Document treat Workflow - -กรณี: เมื่อมีการอัปโหลดเอกสารต้องได้รับการมอบหมายงานให้กับ พนักงานภายในองค์กรณ์ -ขั้นตอนอัตโนมัติ: - -1. ผู้ใช้งานอัปโหลดเอกสารและเลือก “มอบหมายงาน” -2. ระบบส่งแจ้งเตือนไปยังผู้ได้รับมอบหมายงาน -3. ผู้อนุมัติสามารถตรวจสอบและกด “ตรวจสอบแล้ว” -4. ระบบบันทึกสถานะเอกสารและ ส่งต่อ ไปยัง องกรณือื่น ตามลำดับ เมื่อได้ผลและจัดทำเอกสารตอบแล้ว จึงแจ้งผลกลับไปยังผู้ส่ง - -#### 📥 Workflow: 2. Auto Tagging & Categorization - -กรณี: เอกสารที่อัปโหลดมีชื่อหรือเนื้อหาที่ตรงกับหมวดหมู่ที่กำหนดไว้ -ขั้นตอนอัตโนมัติ: - -เมื่ออัปโหลดเอกสาร ระบบวิเคราะห์ชื่อไฟล์หรือเนื้อหา -ระบบกำหนดหมวดหมู่และแท็กให้โดยอัตโนมัติ เช่น “ใบเสนอราคา” → หมวด “การเงิน” -ผู้ใช้งานสามารถแก้ไขได้หากต้องการ - -#### 🔐 Workflow: 3. Access Control Workflow - -กรณี: เอกสารที่มีความลับสูงต้องจำกัดการเข้าถึง -ขั้นตอนอัตโนมัติ: - -เมื่ออัปโหลดเอกสารที่มีคำว่า “ลับ” หรือ “Confidential” -ระบบกำหนดสิทธิ์เริ่มต้นให้เฉพาะผู้ใช้งานระดับผู้จัดการขึ้นไป -ระบบแจ้งเตือนผู้ดูแลระบบให้ตรวจสอบสิทธิ์เพิ่มเติม - -#### 📤 Workflow: 4. Expiry & Archiving Workflow - -กรณี: เอกสารที่มีอายุการใช้งาน เช่น สัญญา หรือใบอนุญาต -ขั้นตอนอัตโนมัติ: - -เมื่ออัปโหลดเอกสาร ผู้ใช้งานระบุวันหมดอายุ -ระบบแจ้งเตือนก่อนหมดอายุล่วงหน้า เช่น 30 วัน -เมื่อถึงวันหมดอายุ ระบบย้ายเอกสารไปยังหมวด “Archive” โดยอัตโนมัติ - -#### 📊 Workflow: 5. Audit Trail & Notification Workflow - -กรณี: มีการแก้ไขหรือดาวน์โหลดเอกสารสำคัญ -ขั้นตอนอัตโนมัติ: - -ทุกการกระทำกับเอกสาร (เปิด, แก้ไข, ลบ) จะถูกบันทึกใน Audit Log -หากเอกสารถูกแก้ไขโดยผู้ใช้งานที่ไม่ใช่เจ้าของ ระบบแจ้งเตือนเจ้าของเอกสารทันที - -## 🛠️ 1. DMS Architecture Deep Dive (Backend + Frontend) - -### 1.1 Executive Summary - -- Reverse proxy (Nginx/NPM) เผยแพร่ Frontend (Next.js) และ Backend (Node.js/Express) ผ่าน HTTPS (HSTS) -- Backend เชื่อม MariaDB 10.11 (ข้อมูลหลัก DMS) และแยก n8n + Postgres 16 สำหรับ workflow -- RBAC/ABAC ถูกบังคับใช้งานใน middleware + มีชุด SQL (tables → triggers → procedures → views → seed) -- ไฟล์จริง (PDF/DWG) เก็บนอก webroot ที่ /share/dms‑data พร้อมมาตรฐานการตั้งชื่อ+โฟลเดอร์ -- Dev/Prod แยกชัดเจนผ่าน Docker multi‑stage + docker‑compose + โฟลเดอร์ persist logs/config/certs - -### 1.2 Runtime Topology & Trust Boundaries - -```text -Internet Clients (Browser) - │ HTTPS 443 (HSTS) [QNAP mgmt = 8443] - ▼ -┌─────────────────────────────────────────────────────┐ -│ Reverse Proxy Layer │ -│ ├─ Nginx (Alpine) or Nginx Proxy Manager (NPM) │ -│ ├─ TLS (LE cert; SAN multi‑subdomain) │ -│ └─ Routes: │ -│ • /, /_next/* → Frontend (Next.js :3000) │ -│ • /api/* → Backend (Express :3001) │ -│ • /pma/* → phpMyAdmin │ -│ • /n8n/* → n8n (Workflows) │ -└─────────────────────────────────────────────────────┘ - │ │ - │ └──────────┐ - ▼ │ - Frontend (Next.js) │ - │ Cookie-based Auth (HttpOnly) │ - ▼ ▼ - Backend (Node/Express ESM) ─────────► MariaDB 10.11 - │ │ - └────────────────────────────────────┘ - Project data (.pdf/.dwg) @ /share/dms-data - - n8n (workflows) ──► Postgres 16 (separate DB for automations) -``` - -==Trust Boundaries== - -- Public zone: Internet ↔ Reverse proxy -- App zone: Reverse proxy ↔ FE/BE containers (internal Docker network) -- # Data zone: Backend ↔ Databases (MariaDB, Postgres) + /share/dms-data - -### 1.3 Frontend: Next.js (ESM) / React.js - -#### 1.3.1 Stack & Key libs - -- Next.js (App Router), React, ESM -- Tailwind CSS, PostCSS, shadcn/ui (components.json) -- Fetch API (credentials include) → Cookie Auth (HttpOnly) - -#### 1.3.2 Directory Layout - -```text -/frontend/ -├─ app/ -│ ├─ login/ -│ ├─ dashboard/ -│ ├─ users/ -│ ├─ correspondences/ -│ ├─ health/ -│ └─ layout.tsx / page.tsx (ตาม App Router) -├─ public/ -├─ Dockerfile (multi-stage: dev/prod) -├─ package.json -├─ next.config.js -└─ ... -``` - -#### 1.3.3 Routing & Layouts - -- Public /login, /health -- Protected: /dashboard, /users, /correspondences, ... (client-side guard) -- เก็บ middleware.ts (ของเดิม) เพื่อหลีกเลี่ยง regression; ใช้ client‑guard + server action อย่างระมัดระวัง - -#### 1.3.4 Auth Flow (Cookie-based) - -1. ผู้ใช้ submit form /login → POST /api/auth/login (Backend) -2. Backend set HttpOnly cookie (JWT) + SameSite=Lax/Strict, Secure -3. หน้า protected เรียก GET /api/auth/me เพื่อตรวจสอบสถานะ -4. หาก 401 → redirect → /login - -**CORS/Fetch**: เเปิด credentials: 'include' ทุกครั้ง, ตั้ง NEXT_PUBLIC_API_BASE เป็น origin ของ backend ผ่าน proxy (เช่น https://lcbp3.np-dms.work) - -#### 1.3.5 UI/UX - -- Sea‑blue palette, sidebar พับได้, card‑based KPI -- ตารางข้อมูลเตรียมรองรับ server‑side DataTables\*\* -- shadcn/ui: Button, Card, Badge, Tabs, Dropdown, Tooltip, Switch, etc. - -#### 1.3.6 Config & ENV - -- NEXT_PUBLIC_API_BAS (ex: https://lcbp3.np-dms.work) -- Build output แยก dev/prod; ระวัง EACCES บน QNAP → ใช้ user node + ปรับสิทธิ์โวลุ่ม .next/\* - -#### 1.3.7 Error Handling & Observability (FE) - -- Global error boundary (app router) + toast/alert patterns -- Network layer: แยก handler สำหรับ 401/403/500 + retry/backoff ที่จำเป็น -- Metrics (optional): web‑vitals, UX timing (เก็บฝั่ง n8n หรือ simple logging) - ---- - -### 1.4 Backend Architecture (Node.js ESM / Express) - -#### 1.4.1 Stack & Structure - -- Node 20.x, ESM modules, Express\*\* -- mysql2/promise, jsonwebtoken, cookie-parser, cors, helmet, winston/morgan - -```text -/backend/ -├─ src/ -│ ├─ index.js # bootstrap server, CORS, cookies, health -│ ├─ routes/ -│ │ ├─ auth.js # /api/auth/* (login, me, logout) -│ │ ├─ users.js # /api/users/* -│ │ ├─ correspondences.js # /api/correspondences/* -│ │ ├─ drawings.js # /api/drawings/* -│ │ ├─ rfas.js # /api/rfas/* -│ │ └─ transmittals.js # /api/transmittals/* -│ ├─ middleware/ -│ │ ├─ authGuard.js # verify JWT from cookie -│ │ ├─ requirePermission.js# RBAC/ABAC enforcement -│ │ ├─ errorHandler.js -│ │ └─ requestLogger.js -│ ├─ db/ -│ │ ├─ pool.js # createPool, sane defaults -│ │ └─ models/ # query builders (User, Drawing, ...) -│ ├─ utils/ -│ │ ├─ hash.js (bcrypt/argon2) -│ │ ├─ jwt.js -│ │ ├─ pagination.js -│ │ └─ responses.js -│ └─ config/ -│ └─ index.js # env, constants -├─ Dockerfile -└─ package.json -``` - -#### 1.4.2 Request Lifecycle - -1. helmet + cors (allow specific origin; credentials true) -2. cookie-parser, json limit (e.g., 2MB) -3. requestLogger → trace + response time -4. Route handler → authGuard (protected) → requirePermission (per‑route) → Controller -5. Error bubbles → errorHandler (JSON shape, status map) - -#### 1.4.3 Auth & RBAC/ABAC - -- JWT ใน HttpOnly cookie; Claims: sub (user_id), roles, exp -- authGuard: ตรวจ token → แนบ req.user -- requirePermission: เช็ค permission ตามเส้นทาง/วิธี; แผนขยาย ABAC (เช่น project scope, owner, doc state) -- Roles/Permissions ถูก seed ใน SQL; มี view เมทริกซ์ เพื่อ debug (เช่น v_role_permission_matrix) - -\*\*ตัวอย่าง pseudo requirePermission(permission) - -```js -export const requirePermission = (perm) => async (req, res, next) => { - if (!req.user) return res.status(401).json({ error: "Unauthenticated" }); - const ok = await checkPermission(req.user.user_id, perm, req.context); - if (!ok) return res.status(403).json({ error: "Forbidden" }); - return next(); -}; -``` - -#### 1.4.4 Database Access & Pooling - -- createPool({ connectionLimit: 10~25, queueLimit: 0, waitForConnections: true }) -- ใช้ parameterized queries เสมอ; ปรับ sql_mode ที่จำเป็นใน my.cnf - -#### 1.4.5 File Storage & Secure Download - -- Root: /share/dms‑data -- โครงโฟลเดอร์: {module}/{yyyy}/{mm}/{entityId}/ + ชื่อไฟล์ตามมาตรฐาน (เช่น DRW-code-REV-rev.pdf) -- Endpoint download: ตรวจสิทธิ์ (RBAC/ABAC) → res.sendFile()/stream; ป้องกัน path traversal -- MIME allowlist + size limit + virus scan (optional; ภายหลัง) - -#### 1.4.6 Health & Readiness - -- GET /api/health → { ok: true } -- (optional) /api/ready ตรวจ DB ping + disk space (dms‑data) - -#### 1.4.7 Config & ENV (BE) - -- DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME -- JWT_SECRET, COOKIE_NAME, COOKIE_SAMESITE, COOKIE_SECURE -- CORS_ORIGIN, LOG_LEVEL, APP_BASE_URL -- FILE_ROOT=/share/dms-data - -#### 1.4.8 Logging - -- Access log (morgan) + App log (winston) → /share/Container/dms/logs/backend/ -- รูปแบบ JSON (timestamp, level, msg, reqId) + daily rotation (logrotate/container‑side) - -### 1.5 Database (MariaDB 10.11) - -#### 1.5.1 Schema Overview (ย่อ) - -- RBAC core: users, roles, permissions, user_roles, role_permissions -- Domain: drawings, contracts, correspondences, rfas, transmittals, organizations, projects, ... -- Audit: audit_logs (แผนขยาย), deleted_at (soft delete, แผนงาน) - -```text -[users]────[roles]────[permissions] - │ - └── activities/audit_logs (future expansion) - -[drawings]────[contracts] -[rfas]────[drawings] -[correspondences] (internal/external flag) -``` - -#### 1.5.2 Init SQL Pipeline - -1. 01\_\*\_deploy_table_rbac.sql — สร้างตารางหลักทั้งหมด + RBAC -2. 02\_\*\_triggers.sql — บังคับ data rules, auto‑audit fields -3. 03\_\*\_procedures_handlers.sql — upsert/bulk handlers (เช่น sp_bulk_import_contract_dwg) -4. 04\_\*\_views.sql — รายงาน/เมทริกซ์สิทธิ์ (v_role_permission_matrix, etc.) -5. 05\_\*\_seed_data.sql — ค่าพื้นฐาน domain (project, categories, statuses) -6. 06\_\*\_seed_users.sql — บัญชีเริ่มต้น (superadmin, editors, viewers) -7. 07\_\*\_seed_contract_dwg.sql — ข้อมูลตัวอย่างแบบสัญญา - -#### 1.5.3 Indexing & Performance - -- Composite indexes ตามคอลัมน์ filter/sort (เช่น (project_id, updated_at DESC)) -- Full‑text index (optional) สำหรับ advanced search -- Query plan review (EXPLAIN) + เพิ่ม covering index ตามรายงาน - -#### 1.5.4 MySQL/MariaDB Config (my.cnf — แนวทาง) - -```conf -[mysqld] -innodb_buffer_pool_size = 4G # ปรับตาม RAM/QNAP -innodb_log_file_size = 512M -innodb_flush_log_at_trx_commit = 1 -max_connections = 200 -sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION -character-set-server = utf8mb4 -collation-server = utf8mb4_unicode_ci -``` - -> ปรับค่าให้เหมาะกับ workload จริง + เฝ้าดู IO/CPU ของ QNAP - -#### 1.5.5 Backup/Restore - -- Logical backup: mysqldump --routines --triggers --single-transaction -- Physical (snapshot QNAP) + schedule ผ่าน n8n/cron -- เก็บสำเนา off‑NAS (encrypted) - -### 1.6 Reverse Proxy & TLS - -#### 1.6.1 Nginx (Alpine) — ตัวอย่าง server block - -> สำคัญ: บนสภาพแวดล้อมนี้ ให้ใช้คนละบรรทัด: -> listen 443 ssl; -> http2 on; -> หลีกเลี่ยง listen 443 ssl http2; - -```conf -server { - listen 80; - server_name lcbp3.np-dms.work; - return 301 https://$host$request_uri; -} - -server { - listen 443 ssl; - http2 on; - server_name lcbp3.np-dms.work; - - ssl_certificate /etc/nginx/certs/fullchain.pem; - ssl_certificate_key /etc/nginx/certs/privkey.pem; - add_header Strict-Transport-Security "max-age=63072000; preload" always; - - # Frontend - location / { - proxy_pass http://frontend:3000; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # Next.js static - location /_next/ { - proxy_pass http://frontend:3000; - } - - # Backend API - location /api/ { - proxy_http_version 1.1; - proxy_set_header Connection ""; - proxy_pass http://backend:3001; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-Proto $scheme; - } - - # phpMyAdmin (sub-path) - location /pma/ { - proxy_pass http://phpmyadmin:80/; - } - - # n8n - location /n8n/ { - proxy_pass http://n8n:5678/; - } -} -``` - -#### 1.6.2 Nginx Proxy Manager (NPM) — Tips - -- ระวังอย่าใส่ proxy_http_version ซ้ำซ้อน (duplicate directive) ใน Advanced -- ถ้าต้องแก้ไฟล์ด้านใน NPM → ระวังไฟล์ใน /data/nginx/proxy_host/\*.conf -- จัดการ certificate / SAN หลาย sub‑domain ใน UI แต่ mainten ดีเรื่อง symlink/renew - -#### 1.6.3 TLS & Certificates - -- Let’s Encrypt (HTTP‑01 webroot/standalone) + HSTS -- QNAP mgmt เปลี่ยนเป็น 8443 → พอร์ต 443 public ว่างสำหรับ Nginx/NPM - -### 1.7 Docker Compose Topology - -#### 1.7.1 Services (สรุป) - -- frontend (Next.js) :3000 -- backend (Express) :3001 -- mariadb (10.11) :3306 (internal) -- phpmyadmin :80 (internal) -- nginx or npm :80/443 (published) -- n8n :5678 (internal) -- postgres_n8n (16-alpine) -- pgadmin4 - -#### 1.7.2 Volumes & Paths - -```text -/share/Container/dms/ -├─ mariadb/data -├─ mariadb/init/*.sql -├─ backend/ (code) -├─ frontend/ (code) -├─ phpmyadmin/{sessions,tmp,config.user.inc.php} -├─ nginx/{nginx.conf,dms.conf,certs/} -├─ n8n, n8n-postgres, n8n-cache -└─ logs/{backend,frontend,nginx,pgadmin,phpmyadmin,postgres_n8n} -/share/dms-data (pdf/dwg storage) -``` - -#### 1.7.3 Healthchecks (suggested) - -- backend: - - ```sh - curl http://localhost:3001/api/health - ``` - -- frontend: curl /health (simple JSON) -- mariadb: mysqladmin ping with credentials -- nginx: nginx -t at startup - -#### 1.7.4 Security Hardening - -- รัน container ด้วย user non‑root (user: node สำหรับ FE/BE) -- จำกัด capabilities; read‑only FS (ยกเว้นโวลุ่มจำเป็น) -- เฉพาะ backend เมานต์ /share/dms-data - -### 1.8 Observability, Ops, and Troubleshooting - -#### 1.8.1 Logs - -- Frontend → /logs/frontend/\* -- Backend → /logs/backend/\* (app/access/error) -- Nginx/NPM → /logs/nginx/\* -- MariaDB → default datadir log + slow query (เปิดใน my.cnf หากต้องการ) - -#### 1.8.2 Common Issues & Playbooks - -- 401 Unauthenticated: ตรวจ authGuard → JWT cookie มี/หมดอายุ → เวลา server/FE sync → CORS credentials: true -- EACCES Next.js: สิทธิ์ .next/\* + run as`node, โวลุ่ม map ถูก user:group -- NPM duplicate directive: ลบซ้ำ proxy_http_version ใน Advanced / ตรวจ proxy_host/\*.conf -- LE cert path/symlink: ตรวจ /etc/letsencrypt/live/npm-\* symlink ชี้ถูก -- DB field not found: ตรวจ schema vs code (migration/init SQL) → sync ให้ตรง - -#### 1.8.3 Performance Guides - -- Backend: keep‑alive, gzip/deflate at proxy, pool 10–25, paginate, avoid N+1 -- Frontend: prefetch critical routes, cache static, image optimization -- DB: เพิ่ม index จุด filter, analyze query (EXPLAIN), ปรับ buffer pool - -### 1.9 Security & Compliance - -- HTTPS only + HSTS (preload) -- CORS: allow list เฉพาะ FE origin; Access-Control-Allow-Credentials: true -- Cookie: HttpOnly, Secure, SameSite=Lax/Strict -- Input Validation: celebrate/zod (optional) + sanitize -- Rate limiting: per IP/route (optional) -- AuditLog: วางแผนเพิ่ม ครอบคลุม CRUD + mapping (actor, action, entity, before/after) -- Backups: DB + /share/dms-data + config (encrypted off‑NAS) - -### 1.10 Backlog → Architecture Mapping - -1. RBAC Enforcement ครบ → เติม requirePermission ทุก route + test matrix ผ่าน view -2. AuditLog ครบ CRUD/Mapping → trigger + table audit_logs + BE hook -3. Upload/Download จริงของ Drawing Revisions → BE endpoints + virus scan (optional) -4. Dashboard KPI → BE summary endpoints + FE cards/charts -5. Server‑side DataTables → paging/sort/filter + indexesรองรับ -6. รายงาน Export CSV/Excel/PDF → BE export endpoints + FE buttons -7. Soft delete (deleted_at) → BE filter default scope + restore endpoint -8. Validation เข้ม → celebrate/zod schema + consistent error shape -9. Indexing/Perf → slow query log + EXPLAIN review -10. Job/Cron Deadline Alerts → n8n schedule + SMTP - -### 1.11 Port & ENV Matrix (Quick Ref) - -| Component | Ports | Key ENV | -| Nginx/NPM | 80/443 (public) | SSL paths, HSTS | -| Frontend | 3000 (internal) | NEXT*PUBLIC_API_BASE | -| Backend | 3001 (internal) | DB*\*, JWT*SECRET, CORS_ORIGIN, FILE_ROOT | -| MariaDB | 3306 (internal) | MY_CNF, credentials | -| n8n | 5678 (internal) | N8N*, webhook URL under /n8n/ | -| Postgres | 5432 (internal) | n8n DB | - -QNAP mgmt: 8443 (already moved) - -### 1.12 Sample Snippets - -#### 1.12.1 Backend CORS (credentials) - -```js -app.use( - cors({ - origin: ["https://lcbp3.np-dms.work"], - credentials: true, - }) -); -``` - -#### 1.12.2 Secure Download (guarded) - -```js -router.get( - "/files/:module/:id/:filename", - authGuard, - requirePermission("file.read"), - async (req, res) => { - const { module, id, filename } = req.params; - // 1) ABAC: verify user can access this module/entity - const ok = await canReadFile(req.user.user_id, module, id); - if (!ok) return res.status(403).json({ error: "Forbidden" }); - - const abs = path.join(FILE_ROOT, module, id, filename); - if (!abs.startsWith(FILE_ROOT)) - return res.status(400).json({ error: "Bad path" }); - return res.sendFile(abs); - } -); -``` - -#### 1.12.3 Healthcheck - -```js -router.get("/health", (req, res) => res.json({ ok: true })); -``` - -### 13 Deployment Workflow (Suggested) - -1. Git (Gitea) branch strategy feature/\* → PR → main -2. Build images (dev/prod) via Dockerfile multi‑stage; pin Node/MariaDB versions -3. docker compose up -d --build จาก /share/Container/dms -4. Validate: /health, /api/health, login roundtrip -5. Monitor logs + baseline perf; run SQL smoke tests (views/triggers/procs) - -### 14 Appendix - -- Naming conventions: snake_case DB, camelCase JS -- Timezones: store UTC in DB; display in app TZ (+07:00) -- Character set: UTF‑8 (utf8mb4_unicode_ci) -- Large file policy: size limit (e.g., 50–200MB), allowlist extensions -- Retention: archive strategy for old revisions (optional) - -## บทบาท: คุณคือ Programmer และ Document Engineer ที่เชี่ยวชาญ - -1. การพัฒนาเว็บแอป (Web Application Development) -2. Configuration of Container Station on QNAP -3. Database: mariadb:10.11 -4. Database management: phpmyadmin:5-apache -5. Backend: node:.js (ESM) -6. Frontend: next.js, react -7. Workflow automation: n8n: -8. Workflow database: postgres:16-alpine -9. Workflow database management: pgadmin4 -10. Reverse proxy: nginx:1.27-alpine -11. linux on QNAP -12. การจัดการฐานข้อมูล (Database Management) -13. การวิเคราะห์ฐานข้อมูล (Database Analysis) -14. การจัดการฐานข้อมูลเชิงสัมพันธ์ (Relational Databases) -15. ภาษา SQL -16. RBAC - -## 2. ระบบที่ใช้ - -## Server - -- ใช้ Container Station เป็น SERVER บน QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B 4 cores 8 threads) **เปลี่ยน port 443 ของ QNAP เป็น 8443 แล้ว** - -## การพัฒนาโครงการ - -- ด้วย Visual Studio Code บน Windows 11 -- ใช้ ๊ UI ของ Container Station เป็นหลัก - -## โครงสร้างโฟลเดอร์ (บน QNAP) - -/share/Container/dms/ -├─ docker-compose.yml # Create โดย UI Container Station -├─ mariadb/ -│ ├─ data/ # ข้อมูลจริงของ MariaDB -│ ├─ init/ # ข้อมูลเริ่มต้นของ MariaDB -│ │ ├─ 01_dms_data_v5_1_deploy_table_rbac.sql # Create all data table & RBAC table here! -│ │ ├─ 02_dms_data_v5_1_triggers.sql # Create all triggers here! -│ │ ├─ 03_dms_data_v5_1_procedures_handlers.sql # Create all procedures here! -│ │ ├─ 04_dms_data_v5_1_views.sql # Create all views here! -│ │ ├─ 05 dms_data_v5_1_seeก_data.sql # Seed nescesary data here! -│ │ ├─ 06_dms_data_v5_1_seed_users.sql # Seed users data here! -│ │ └─ 07_dms_data_v5_1_seed_contract_dwg.sql # Seed contract drawing data here! -│ └─ my.cnf -├─ backend/ -│ ├─ app/ -│ ├─ src/ -│ │ ├─ db/ -│ │ │ └─models/ -│ │ ├─ middleware/ -│ │ ├─ routes/ -│ │ ├─ utils/ -│ │ └─ index.js -│ ├─ Dockerfile -│ ├─ package.json -│ └─ package-lock.json # ไม่มี -├─ frontend/ -│ ├─ app/ -│ │ ├─ correspondences/ -│ │ ├─ dashboard/ -│ │ ├─ health/ -│ │ ├─ login/ -│ │ └─ users/ -│ ├─ public/ -│ ├─ Dockerfile -│ ├─ package.json -│ ├─ package-lock.json # ไม่มี -│ ├─ next.config.js -│ └─ page.jsx -├─ phpmyadmin/ -│ ├─ sessions/ # โฟลเดอร์เซสชันถาวรของ phpMyAdmin -│ ├─ tmp/ -│ ├─ config.user.inc.php -│ └─ zzz-custom.ini -├─ nginx/ -│ ├─ certs/ -│ ├─ nginx.conf -│ └─ dms.conf -├─ n8n/ -├─ n8n-cache/ -├─ n8n-postgres/ -└─ logs/ -├─ backend/ -├─ frontend/ -├─ nginx/ -├─ pgadmin/ -├─ phpmyadmin/ -└─ postgres_n8n/ -/share/dms-data # เก็บข้อมมูล .pdf, .dwg แยกตาม correspondences, documents - -# งานที่ต้องการ: - -- ไม่ใช้ .env เด็ดขาด Container Station ไม่รองรับ และ docker-compose.yml ได้ทดสอบ รันบน Container station มาแล้ว -- Code ของ backend ทั้งหมด -- การทดสอบระบบ backend ทุกส่วน ให้พร้อม สำหรับ frontend - -# กรณี 2: มี Git อยู่แล้ว (มี main อยู่) - -2.1 อัปเดต main ให้ตรงล่าสุดก่อนแตกบร้านช์ - -cd /share/Container/dms -git checkout main -git pull --ff-only # ถ้าเชื่อม remote อยู่ -git tag -f stable-$(date +%F) # tag จุดเสถียรปัจจุบัน - -2.2 แตก branch งาน Dashboard -git checkout -b feature/dashboard-update-$(date +%y%m%d) -git checkout -b feature/dashboard-update-251004 - -2.3 ทำงาน/คอมมิตตามปกติ - -# แก้ไฟล์ frontend/app/dashboard/\* และที่เกี่ยวข้อง - -git add frontend/app/dashboard -git commit -m "feat(dashboard): เพิ่มส่วนจัดการ user" -git push -u origin feature/dashboard-update-251004 +# 📝 0. Project Title: Document Management System (DMS) Web Application for Laem Chabang Port Development Project, Phase 3 + +## 0. Project + +### 📌 0.1 Project Overview / Description + +- ระบบ Document Management System (DMS) เป็นเว็บแอปพลิเคชันที่ออกแบบมาเพื่อจัดการเอกสารภายในโครงการอย่างมีประสิทธิภาพ +- โดยมีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร +- ระบบนี้จะช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล +- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ + +### 🎯 0.2 Objectives + +- พัฒนาระบบที่สามารถจัดการเอกสารได้อย่างเป็นระบบ +- ลดความซ้ำซ้อนในการจัดเก็บเอกสาร +- เพิ่มความปลอดภัยในการเข้าถึงและจัดการเอกสาร +- รองรับการทำงานร่วมกันแบบออนไลน์ + +### 📦 0.3 Scope of Work + +ระบบจะครอบคลุมฟีเจอร์หลักดังนี้: + +- การลงทะเบียนและเข้าสู่ระบบ ของผู้ใช้งาน +- การอัปโหลดและจัดเก็บเอกสารในรูปแบบต่าง ๆ (PDF, DOCX, XLSX ฯลฯ) +- การจัดหมวดหมู่และแท็กเอกสาร +- การค้นหาเอกสารด้วยคำสำคัญหรือฟิลเตอร์ +- การกำหนดสิทธิ์การเข้าถึงเอกสาร (เช่น อ่านอย่างเดียว, แก้ไข, ลบ) +- การบันทึกประวัติการใช้งานเอกสาร (Audit Trail) +- การมอบหมายงานให้กับผู้เกี่ยวข้อง และแจ้งเตือนเมื่อมีการมอบหมายงาน +- การแจ้งเตือนเมื่อถึงกำหนดวันที่ต้องส่งเอกสารต่อให้ ผู้เกี่ยวข้องอื่นๆ +- การแจ้งเตือนเมื่อมีการเปลี่ยนแปลงเอกสาร + +### 👥 0.4 Target Users + +- พนักงานภายใน ขององค์กร +- พนักงานควบคุมเอกสาร (Document Control)/ ผู้ดูแลระบบขององค์กร (admin) +- ผู้จัดการฝ่ายเอกสาร ขององค์กร +- ผู้จัดการโครงการ ขององค์กร +- คณะกรรมการ ของโครงการ +- ผู้ดูแลระบบ IT ของโครงการ (superadmin) + +### 📈 0.5 Expected Outcomes + +- ลดเวลาในการค้นหาเอกสารลงอย่างน้อย 50% +- ลดเวลาในการจัดทำรายงานเอกสาร ประจำวัน, ประจำสัปดาห์, ประจำเดือน, ประจำปี และ รายงานเอกสารทั้งโครงการ +- ลดการใช้เอกสารกระดาษในองค์กร +- เพิ่มความปลอดภัยในการจัดเก็บข้อมูล +- รองรับการทำงานแบบ Remote Work + +### 📘 0.6 Requirements Use Cases + +#### 📘 Use Case: Upload Document + +Actor: พนักงานควบคุมเอกสาร (Document Control) +Description: พนักงานควบคุมเอกสารสามารถอัปโหลดเอกสารเข้าสู่ระบบเพื่อจัดเก็บและใช้งานในภายหลัง +Preconditions: พนักงานควบคุมเอกสารต้องเข้าสู่ระบบก่อน +Main Flow: + +พนักงานควบคุมเอกสารเลือกเมนู “อัปโหลดเอกสาร” +เลือกไฟล์จากเครื่องคอมพิวเตอร์ +กรอกข้อมูลประกอบ เช่น ชื่อเอกสาร หมวดหมู่ แท็ก +กดปุ่ม “อัปโหลด” +ระบบบันทึกเอกสารและแสดงผลการอัปโหลดสำเร็จ + +Postconditions: เอกสารถูกจัดเก็บในระบบและสามารถค้นหาได้ + +#### 📘 Use Case: Assign Users to Document + +Actor: พนักงานควบคุมเอกสาร (Document Control) +Description: พนักงานควบคุมเอกสารสามารถ มอบหมายงานให้กับ Users +Preconditions: พนักงานควบคุมเอกสารต้องเข้าสู่ระบบก่อน, เอกสารต้องอัปโหลดเรียบร้อยแล้ว +Main Flow: + +พนักงานควบคุมเอกสารเลือกเมนู “มอบหมายงาน” +เลือกเอกสารในระบบ +เลือก Users กำหนดวันสิ้นสุดงาน +กดปุ่ม “มอบหมายงาน” +ระบบบันทึกเอกสารและแสดงผลการมอบหมายงานสำเร็จ + +Postconditions: งานที่มอยหมาย จัดเก็บในระบบและสามารถค้นหาได้ + +#### 📘 Use Case: Search Document + +Actor: ผู้ใช้งานทั่วไป +Description: ผู้ใช้งานสามารถค้นหาเอกสารจากระบบด้วยคำสำคัญหรือฟิลเตอร์ +Preconditions: ผู้ใช้งานต้องเข้าสู่ระบบ +Main Flow: + +ผู้ใช้งานกรอกคำค้นหรือเลือกฟิลเตอร์ (หมวดหมู่, วันที่, ผู้สร้าง, ผู้ได้รับมอบหมายงาน, สถานะ, title, subject) +กดปุ่ม “ค้นหา” +ระบบแสดงรายการเอกสารที่ตรงกับเงื่อนไข + +Postconditions: ผู้ใช้งานสามารถเปิดดูหรือดาวน์โหลดเอกสารที่ค้นพบได้ + +#### 📘 Use Case: Manage Access + +Actor: ผู้ดูแลระบบโครงการ (superadmin) / ผู้ดูแลระบบขององค์กร (admin) +Description: ผู้ดูแลระบบสามารถกำหนดสิทธิ์การเข้าถึงเอกสารให้กับผู้ใช้งาน +Preconditions: ผู้ดูแลระบบต้องเข้าสู่ระบบ +Main Flow: + +ผู้ดูแลระบบเลือกเอกสาร +กด “จัดการสิทธิ์” +เลือกผู้ใช้งานและกำหนดสิทธิ์ (อ่าน, แก้ไข, ลบ) +กด “บันทึก” + +Postconditions: สิทธิ์การเข้าถึงเอกสารถูกปรับตามที่กำหนด + +#### 📘 Use Case: View Document History + +Actor: ผู้ใช้งานทั่วไป / ผู้ดูแลระบบ +Description: ผู้ใช้งานสามารถดูประวัติการใช้งานเอกสาร เช่น การแก้ไข การดาวน์โหลด +Preconditions: ผู้ใช้งานต้องมีสิทธิ์เข้าถึงเอกสาร +Main Flow: + +ผู้ใช้งานเปิดเอกสาร +เลือก “ดูประวัติ” +ระบบแสดงรายการกิจกรรมที่เกี่ยวข้องกับเอกสาร + +Postconditions: ผู้ใช้งานสามารถตรวจสอบการเปลี่ยนแปลงย้อนหลังได้ + +### 🔄 0.7 Workflow อัตโนมัติในระบบ DMS + +✅ ประโยชน์ของ Workflow อัตโนมัติใน DMS + +- ลดภาระงานซ้ำ ๆ ของผู้ใช้งาน +- เพิ่มความปลอดภัยและการควบคุมเอกสาร +- เพิ่มความเร็วในการดำเนินงาน +- ลดข้อผิดพลาดจากการทำงานด้วยมือ + +#### 🧩 Workflow: 1. Document treat Workflow + +กรณี: เมื่อมีการอัปโหลดเอกสารต้องได้รับการมอบหมายงานให้กับ พนักงานภายในองค์กรณ์ +ขั้นตอนอัตโนมัติ: + +1. ผู้ใช้งานอัปโหลดเอกสารและเลือก “มอบหมายงาน” +2. ระบบส่งแจ้งเตือนไปยังผู้ได้รับมอบหมายงาน +3. ผู้อนุมัติสามารถตรวจสอบและกด “ตรวจสอบแล้ว” +4. ระบบบันทึกสถานะเอกสารและ ส่งต่อ ไปยัง องกรณือื่น ตามลำดับ เมื่อได้ผลและจัดทำเอกสารตอบแล้ว จึงแจ้งผลกลับไปยังผู้ส่ง + +#### 📥 Workflow: 2. Auto Tagging & Categorization + +กรณี: เอกสารที่อัปโหลดมีชื่อหรือเนื้อหาที่ตรงกับหมวดหมู่ที่กำหนดไว้ +ขั้นตอนอัตโนมัติ: + +เมื่ออัปโหลดเอกสาร ระบบวิเคราะห์ชื่อไฟล์หรือเนื้อหา +ระบบกำหนดหมวดหมู่และแท็กให้โดยอัตโนมัติ เช่น “ใบเสนอราคา” → หมวด “การเงิน” +ผู้ใช้งานสามารถแก้ไขได้หากต้องการ + +#### 🔐 Workflow: 3. Access Control Workflow + +กรณี: เอกสารที่มีความลับสูงต้องจำกัดการเข้าถึง +ขั้นตอนอัตโนมัติ: + +เมื่ออัปโหลดเอกสารที่มีคำว่า “ลับ” หรือ “Confidential” +ระบบกำหนดสิทธิ์เริ่มต้นให้เฉพาะผู้ใช้งานระดับผู้จัดการขึ้นไป +ระบบแจ้งเตือนผู้ดูแลระบบให้ตรวจสอบสิทธิ์เพิ่มเติม + +#### 📤 Workflow: 4. Expiry & Archiving Workflow + +กรณี: เอกสารที่มีอายุการใช้งาน เช่น สัญญา หรือใบอนุญาต +ขั้นตอนอัตโนมัติ: + +เมื่ออัปโหลดเอกสาร ผู้ใช้งานระบุวันหมดอายุ +ระบบแจ้งเตือนก่อนหมดอายุล่วงหน้า เช่น 30 วัน +เมื่อถึงวันหมดอายุ ระบบย้ายเอกสารไปยังหมวด “Archive” โดยอัตโนมัติ + +#### 📊 Workflow: 5. Audit Trail & Notification Workflow + +กรณี: มีการแก้ไขหรือดาวน์โหลดเอกสารสำคัญ +ขั้นตอนอัตโนมัติ: + +ทุกการกระทำกับเอกสาร (เปิด, แก้ไข, ลบ) จะถูกบันทึกใน Audit Log +หากเอกสารถูกแก้ไขโดยผู้ใช้งานที่ไม่ใช่เจ้าของ ระบบแจ้งเตือนเจ้าของเอกสารทันที + +## 🛠️ 1. DMS Architecture Deep Dive (Backend + Frontend) + +### 1.1 Executive Summary + +- Reverse proxy (Nginx/NPM) เผยแพร่ Frontend (Next.js) และ Backend (Node.js/Express) ผ่าน HTTPS (HSTS) +- Backend เชื่อม MariaDB 10.11 (ข้อมูลหลัก DMS) และแยก n8n + Postgres 16 สำหรับ workflow +- RBAC/ABAC ถูกบังคับใช้งานใน middleware + มีชุด SQL (tables → triggers → procedures → views → seed) +- ไฟล์จริง (PDF/DWG) เก็บนอก webroot ที่ /share/dms‑data พร้อมมาตรฐานการตั้งชื่อ+โฟลเดอร์ +- Dev/Prod แยกชัดเจนผ่าน Docker multi‑stage + docker‑compose + โฟลเดอร์ persist logs/config/certs + +### 1.2 Runtime Topology & Trust Boundaries + +```text +Internet Clients (Browser) + │ HTTPS 443 (HSTS) [QNAP mgmt = 8443] + ▼ +┌─────────────────────────────────────────────────────┐ +│ Reverse Proxy Layer │ +│ ├─ Nginx (Alpine) or Nginx Proxy Manager (NPM) │ +│ ├─ TLS (LE cert; SAN multi‑subdomain) │ +│ └─ Routes: │ +│ • /, /_next/* → Frontend (Next.js :3000) │ +│ • /api/* → Backend (Express :3001) │ +│ • /pma/* → phpMyAdmin │ +│ • /n8n/* → n8n (Workflows) │ +└─────────────────────────────────────────────────────┘ + │ │ + │ └──────────┐ + ▼ │ + Frontend (Next.js) │ + │ Cookie-based Auth (HttpOnly) │ + ▼ ▼ + Backend (Node/Express ESM) ─────────► MariaDB 10.11 + │ │ + └────────────────────────────────────┘ + Project data (.pdf/.dwg) @ /share/dms-data + + n8n (workflows) ──► Postgres 16 (separate DB for automations) +``` + +==Trust Boundaries== + +- Public zone: Internet ↔ Reverse proxy +- App zone: Reverse proxy ↔ FE/BE containers (internal Docker network) +- # Data zone: Backend ↔ Databases (MariaDB, Postgres) + /share/dms-data + +### 1.3 Frontend: Next.js (ESM) / React.js + +#### 1.3.1 Stack & Key libs + +- Next.js (App Router), React, ESM +- Tailwind CSS, PostCSS, shadcn/ui (components.json) +- Fetch API (credentials include) → Cookie Auth (HttpOnly) + +#### 1.3.2 Directory Layout + +```text +/frontend/ +├─ app/ +│ ├─ login/ +│ ├─ dashboard/ +│ ├─ users/ +│ ├─ correspondences/ +│ ├─ health/ +│ └─ layout.tsx / page.tsx (ตาม App Router) +├─ public/ +├─ Dockerfile (multi-stage: dev/prod) +├─ package.json +├─ next.config.js +└─ ... +``` + +#### 1.3.3 Routing & Layouts + +- Public /login, /health +- Protected: /dashboard, /users, /correspondences, ... (client-side guard) +- เก็บ middleware.ts (ของเดิม) เพื่อหลีกเลี่ยง regression; ใช้ client‑guard + server action อย่างระมัดระวัง + +#### 1.3.4 Auth Flow (Cookie-based) + +1. ผู้ใช้ submit form /login → POST /api/auth/login (Backend) +2. Backend set HttpOnly cookie (JWT) + SameSite=Lax/Strict, Secure +3. หน้า protected เรียก GET /api/auth/me เพื่อตรวจสอบสถานะ +4. หาก 401 → redirect → /login + +**CORS/Fetch**: เเปิด credentials: 'include' ทุกครั้ง, ตั้ง NEXT_PUBLIC_API_BASE เป็น origin ของ backend ผ่าน proxy (เช่น https://lcbp3.np-dms.work) + +#### 1.3.5 UI/UX + +- Sea‑blue palette, sidebar พับได้, card‑based KPI +- ตารางข้อมูลเตรียมรองรับ server‑side DataTables\*\* +- shadcn/ui: Button, Card, Badge, Tabs, Dropdown, Tooltip, Switch, etc. + +#### 1.3.6 Config & ENV + +- NEXT_PUBLIC_API_BAS (ex: https://lcbp3.np-dms.work) +- Build output แยก dev/prod; ระวัง EACCES บน QNAP → ใช้ user node + ปรับสิทธิ์โวลุ่ม .next/\* + +#### 1.3.7 Error Handling & Observability (FE) + +- Global error boundary (app router) + toast/alert patterns +- Network layer: แยก handler สำหรับ 401/403/500 + retry/backoff ที่จำเป็น +- Metrics (optional): web‑vitals, UX timing (เก็บฝั่ง n8n หรือ simple logging) + +--- + +### 1.4 Backend Architecture (Node.js ESM / Express) + +#### 1.4.1 Stack & Structure + +- Node 20.x, ESM modules, Express\*\* +- mysql2/promise, jsonwebtoken, cookie-parser, cors, helmet, winston/morgan + +```text +/backend/ +├─ src/ +│ ├─ index.js # bootstrap server, CORS, cookies, health +│ ├─ routes/ +│ │ ├─ auth.js # /api/auth/* (login, me, logout) +│ │ ├─ users.js # /api/users/* +│ │ ├─ correspondences.js # /api/correspondences/* +│ │ ├─ drawings.js # /api/drawings/* +│ │ ├─ rfas.js # /api/rfas/* +│ │ └─ transmittals.js # /api/transmittals/* +│ ├─ middleware/ +│ │ ├─ authGuard.js # verify JWT from cookie +│ │ ├─ requirePermission.js# RBAC/ABAC enforcement +│ │ ├─ errorHandler.js +│ │ └─ requestLogger.js +│ ├─ db/ +│ │ ├─ pool.js # createPool, sane defaults +│ │ └─ models/ # query builders (User, Drawing, ...) +│ ├─ utils/ +│ │ ├─ hash.js (bcrypt/argon2) +│ │ ├─ jwt.js +│ │ ├─ pagination.js +│ │ └─ responses.js +│ └─ config/ +│ └─ index.js # env, constants +├─ Dockerfile +└─ package.json +``` + +#### 1.4.2 Request Lifecycle + +1. helmet + cors (allow specific origin; credentials true) +2. cookie-parser, json limit (e.g., 2MB) +3. requestLogger → trace + response time +4. Route handler → authGuard (protected) → requirePermission (per‑route) → Controller +5. Error bubbles → errorHandler (JSON shape, status map) + +#### 1.4.3 Auth & RBAC/ABAC + +- JWT ใน HttpOnly cookie; Claims: sub (user_id), roles, exp +- authGuard: ตรวจ token → แนบ req.user +- requirePermission: เช็ค permission ตามเส้นทาง/วิธี; แผนขยาย ABAC (เช่น project scope, owner, doc state) +- Roles/Permissions ถูก seed ใน SQL; มี view เมทริกซ์ เพื่อ debug (เช่น v_role_permission_matrix) + +\*\*ตัวอย่าง pseudo requirePermission(permission) + +```js +export const requirePermission = (perm) => async (req, res, next) => { + if (!req.user) return res.status(401).json({ error: "Unauthenticated" }); + const ok = await checkPermission(req.user.user_id, perm, req.context); + if (!ok) return res.status(403).json({ error: "Forbidden" }); + return next(); +}; +``` + +#### 1.4.4 Database Access & Pooling + +- createPool({ connectionLimit: 10~25, queueLimit: 0, waitForConnections: true }) +- ใช้ parameterized queries เสมอ; ปรับ sql_mode ที่จำเป็นใน my.cnf + +#### 1.4.5 File Storage & Secure Download + +- Root: /share/dms‑data +- โครงโฟลเดอร์: {module}/{yyyy}/{mm}/{entityId}/ + ชื่อไฟล์ตามมาตรฐาน (เช่น DRW-code-REV-rev.pdf) +- Endpoint download: ตรวจสิทธิ์ (RBAC/ABAC) → res.sendFile()/stream; ป้องกัน path traversal +- MIME allowlist + size limit + virus scan (optional; ภายหลัง) + +#### 1.4.6 Health & Readiness + +- GET /api/health → { ok: true } +- (optional) /api/ready ตรวจ DB ping + disk space (dms‑data) + +#### 1.4.7 Config & ENV (BE) + +- DB_HOST, DB_PORT, DB_USER, DB_PASS, DB_NAME +- JWT_SECRET, COOKIE_NAME, COOKIE_SAMESITE, COOKIE_SECURE +- CORS_ORIGIN, LOG_LEVEL, APP_BASE_URL +- FILE_ROOT=/share/dms-data + +#### 1.4.8 Logging + +- Access log (morgan) + App log (winston) → /share/Container/dms/logs/backend/ +- รูปแบบ JSON (timestamp, level, msg, reqId) + daily rotation (logrotate/container‑side) + +### 1.5 Database (MariaDB 10.11) + +#### 1.5.1 Schema Overview (ย่อ) + +- RBAC core: users, roles, permissions, user_roles, role_permissions +- Domain: drawings, contracts, correspondences, rfas, transmittals, organizations, projects, ... +- Audit: audit_logs (แผนขยาย), deleted_at (soft delete, แผนงาน) + +```text +[users]────[roles]────[permissions] + │ + └── activities/audit_logs (future expansion) + +[drawings]────[contracts] +[rfas]────[drawings] +[correspondences] (internal/external flag) +``` + +#### 1.5.2 Init SQL Pipeline + +1. 01\_\*\_deploy_table_rbac.sql — สร้างตารางหลักทั้งหมด + RBAC +2. 02\_\*\_triggers.sql — บังคับ data rules, auto‑audit fields +3. 03\_\*\_procedures_handlers.sql — upsert/bulk handlers (เช่น sp_bulk_import_contract_dwg) +4. 04\_\*\_views.sql — รายงาน/เมทริกซ์สิทธิ์ (v_role_permission_matrix, etc.) +5. 05\_\*\_seed_data.sql — ค่าพื้นฐาน domain (project, categories, statuses) +6. 06\_\*\_seed_users.sql — บัญชีเริ่มต้น (superadmin, editors, viewers) +7. 07\_\*\_seed_contract_dwg.sql — ข้อมูลตัวอย่างแบบสัญญา + +#### 1.5.3 Indexing & Performance + +- Composite indexes ตามคอลัมน์ filter/sort (เช่น (project_id, updated_at DESC)) +- Full‑text index (optional) สำหรับ advanced search +- Query plan review (EXPLAIN) + เพิ่ม covering index ตามรายงาน + +#### 1.5.4 MySQL/MariaDB Config (my.cnf — แนวทาง) + +```conf +[mysqld] +innodb_buffer_pool_size = 4G # ปรับตาม RAM/QNAP +innodb_log_file_size = 512M +innodb_flush_log_at_trx_commit = 1 +max_connections = 200 +sql_mode = STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION +character-set-server = utf8mb4 +collation-server = utf8mb4_unicode_ci +``` + +> ปรับค่าให้เหมาะกับ workload จริง + เฝ้าดู IO/CPU ของ QNAP + +#### 1.5.5 Backup/Restore + +- Logical backup: mysqldump --routines --triggers --single-transaction +- Physical (snapshot QNAP) + schedule ผ่าน n8n/cron +- เก็บสำเนา off‑NAS (encrypted) + +### 1.6 Reverse Proxy & TLS + +#### 1.6.1 Nginx (Alpine) — ตัวอย่าง server block + +> สำคัญ: บนสภาพแวดล้อมนี้ ให้ใช้คนละบรรทัด: +> listen 443 ssl; +> http2 on; +> หลีกเลี่ยง listen 443 ssl http2; + +```conf +server { + listen 80; + server_name lcbp3.np-dms.work; + return 301 https://$host$request_uri; +} + +server { + listen 443 ssl; + http2 on; + server_name lcbp3.np-dms.work; + + ssl_certificate /etc/nginx/certs/fullchain.pem; + ssl_certificate_key /etc/nginx/certs/privkey.pem; + add_header Strict-Transport-Security "max-age=63072000; preload" always; + + # Frontend + location / { + proxy_pass http://frontend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # Next.js static + location /_next/ { + proxy_pass http://frontend:3000; + } + + # Backend API + location /api/ { + proxy_http_version 1.1; + proxy_set_header Connection ""; + proxy_pass http://backend:3001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Proto $scheme; + } + + # phpMyAdmin (sub-path) + location /pma/ { + proxy_pass http://phpmyadmin:80/; + } + + # n8n + location /n8n/ { + proxy_pass http://n8n:5678/; + } +} +``` + +#### 1.6.2 Nginx Proxy Manager (NPM) — Tips + +- ระวังอย่าใส่ proxy_http_version ซ้ำซ้อน (duplicate directive) ใน Advanced +- ถ้าต้องแก้ไฟล์ด้านใน NPM → ระวังไฟล์ใน /data/nginx/proxy_host/\*.conf +- จัดการ certificate / SAN หลาย sub‑domain ใน UI แต่ mainten ดีเรื่อง symlink/renew + +#### 1.6.3 TLS & Certificates + +- Let’s Encrypt (HTTP‑01 webroot/standalone) + HSTS +- QNAP mgmt เปลี่ยนเป็น 8443 → พอร์ต 443 public ว่างสำหรับ Nginx/NPM + +### 1.7 Docker Compose Topology + +#### 1.7.1 Services (สรุป) + +- frontend (Next.js) :3000 +- backend (Express) :3001 +- mariadb (10.11) :3306 (internal) +- phpmyadmin :80 (internal) +- nginx or npm :80/443 (published) +- n8n :5678 (internal) +- postgres_n8n (16-alpine) +- pgadmin4 + +#### 1.7.2 Volumes & Paths + +```text +/share/Container/dms/ +├─ mariadb/data +├─ mariadb/init/*.sql +├─ backend/ (code) +├─ frontend/ (code) +├─ phpmyadmin/{sessions,tmp,config.user.inc.php} +├─ nginx/{nginx.conf,dms.conf,certs/} +├─ n8n, n8n-postgres, n8n-cache +└─ logs/{backend,frontend,nginx,pgadmin,phpmyadmin,postgres_n8n} +/share/dms-data (pdf/dwg storage) +``` + +#### 1.7.3 Healthchecks (suggested) + +- backend: + + ```sh + curl http://localhost:3001/api/health + ``` + +- frontend: curl /health (simple JSON) +- mariadb: mysqladmin ping with credentials +- nginx: nginx -t at startup + +#### 1.7.4 Security Hardening + +- รัน container ด้วย user non‑root (user: node สำหรับ FE/BE) +- จำกัด capabilities; read‑only FS (ยกเว้นโวลุ่มจำเป็น) +- เฉพาะ backend เมานต์ /share/dms-data + +### 1.8 Observability, Ops, and Troubleshooting + +#### 1.8.1 Logs + +- Frontend → /logs/frontend/\* +- Backend → /logs/backend/\* (app/access/error) +- Nginx/NPM → /logs/nginx/\* +- MariaDB → default datadir log + slow query (เปิดใน my.cnf หากต้องการ) + +#### 1.8.2 Common Issues & Playbooks + +- 401 Unauthenticated: ตรวจ authGuard → JWT cookie มี/หมดอายุ → เวลา server/FE sync → CORS credentials: true +- EACCES Next.js: สิทธิ์ .next/\* + run as`node, โวลุ่ม map ถูก user:group +- NPM duplicate directive: ลบซ้ำ proxy_http_version ใน Advanced / ตรวจ proxy_host/\*.conf +- LE cert path/symlink: ตรวจ /etc/letsencrypt/live/npm-\* symlink ชี้ถูก +- DB field not found: ตรวจ schema vs code (migration/init SQL) → sync ให้ตรง + +#### 1.8.3 Performance Guides + +- Backend: keep‑alive, gzip/deflate at proxy, pool 10–25, paginate, avoid N+1 +- Frontend: prefetch critical routes, cache static, image optimization +- DB: เพิ่ม index จุด filter, analyze query (EXPLAIN), ปรับ buffer pool + +### 1.9 Security & Compliance + +- HTTPS only + HSTS (preload) +- CORS: allow list เฉพาะ FE origin; Access-Control-Allow-Credentials: true +- Cookie: HttpOnly, Secure, SameSite=Lax/Strict +- Input Validation: celebrate/zod (optional) + sanitize +- Rate limiting: per IP/route (optional) +- AuditLog: วางแผนเพิ่ม ครอบคลุม CRUD + mapping (actor, action, entity, before/after) +- Backups: DB + /share/dms-data + config (encrypted off‑NAS) + +### 1.10 Backlog → Architecture Mapping + +1. RBAC Enforcement ครบ → เติม requirePermission ทุก route + test matrix ผ่าน view +2. AuditLog ครบ CRUD/Mapping → trigger + table audit_logs + BE hook +3. Upload/Download จริงของ Drawing Revisions → BE endpoints + virus scan (optional) +4. Dashboard KPI → BE summary endpoints + FE cards/charts +5. Server‑side DataTables → paging/sort/filter + indexesรองรับ +6. รายงาน Export CSV/Excel/PDF → BE export endpoints + FE buttons +7. Soft delete (deleted_at) → BE filter default scope + restore endpoint +8. Validation เข้ม → celebrate/zod schema + consistent error shape +9. Indexing/Perf → slow query log + EXPLAIN review +10. Job/Cron Deadline Alerts → n8n schedule + SMTP + +### 1.11 Port & ENV Matrix (Quick Ref) + +| Component | Ports | Key ENV | +| Nginx/NPM | 80/443 (public) | SSL paths, HSTS | +| Frontend | 3000 (internal) | NEXT*PUBLIC_API_BASE | +| Backend | 3001 (internal) | DB*\*, JWT*SECRET, CORS_ORIGIN, FILE_ROOT | +| MariaDB | 3306 (internal) | MY_CNF, credentials | +| n8n | 5678 (internal) | N8N*, webhook URL under /n8n/ | +| Postgres | 5432 (internal) | n8n DB | + +QNAP mgmt: 8443 (already moved) + +### 1.12 Sample Snippets + +#### 1.12.1 Backend CORS (credentials) + +```js +app.use( + cors({ + origin: ["https://lcbp3.np-dms.work"], + credentials: true, + }) +); +``` + +#### 1.12.2 Secure Download (guarded) + +```js +router.get( + "/files/:module/:id/:filename", + authGuard, + requirePermission("file.read"), + async (req, res) => { + const { module, id, filename } = req.params; + // 1) ABAC: verify user can access this module/entity + const ok = await canReadFile(req.user.user_id, module, id); + if (!ok) return res.status(403).json({ error: "Forbidden" }); + + const abs = path.join(FILE_ROOT, module, id, filename); + if (!abs.startsWith(FILE_ROOT)) + return res.status(400).json({ error: "Bad path" }); + return res.sendFile(abs); + } +); +``` + +#### 1.12.3 Healthcheck + +```js +router.get("/health", (req, res) => res.json({ ok: true })); +``` + +### 13 Deployment Workflow (Suggested) + +1. Git (Gitea) branch strategy feature/\* → PR → main +2. Build images (dev/prod) via Dockerfile multi‑stage; pin Node/MariaDB versions +3. docker compose up -d --build จาก /share/Container/dms +4. Validate: /health, /api/health, login roundtrip +5. Monitor logs + baseline perf; run SQL smoke tests (views/triggers/procs) + +### 14 Appendix + +- Naming conventions: snake_case DB, camelCase JS +- Timezones: store UTC in DB; display in app TZ (+07:00) +- Character set: UTF‑8 (utf8mb4_unicode_ci) +- Large file policy: size limit (e.g., 50–200MB), allowlist extensions +- Retention: archive strategy for old revisions (optional) + +## บทบาท: คุณคือ Programmer และ Document Engineer ที่เชี่ยวชาญ + +1. การพัฒนาเว็บแอป (Web Application Development) +2. Configuration of Container Station on QNAP +3. Database: mariadb:10.11 +4. Database management: phpmyadmin:5-apache +5. Backend: node:.js (ESM) +6. Frontend: next.js, react +7. Workflow automation: n8n: +8. Workflow database: postgres:16-alpine +9. Workflow database management: pgadmin4 +10. Reverse proxy: nginx:1.27-alpine +11. linux on QNAP +12. การจัดการฐานข้อมูล (Database Management) +13. การวิเคราะห์ฐานข้อมูล (Database Analysis) +14. การจัดการฐานข้อมูลเชิงสัมพันธ์ (Relational Databases) +15. ภาษา SQL +16. RBAC + +## 2. ระบบที่ใช้ + +## Server + +- ใช้ Container Station เป็น SERVER บน QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B 4 cores 8 threads) **เปลี่ยน port 443 ของ QNAP เป็น 8443 แล้ว** + +## การพัฒนาโครงการ + +- ด้วย Visual Studio Code บน Windows 11 +- ใช้ ๊ UI ของ Container Station เป็นหลัก + +## โครงสร้างโฟลเดอร์ (บน QNAP) + +/share/Container/dms/ +├─ docker-compose.yml # Create โดย UI Container Station +├─ mariadb/ +│ ├─ data/ # ข้อมูลจริงของ MariaDB +│ ├─ init/ # ข้อมูลเริ่มต้นของ MariaDB +│ │ ├─ 01_dms_data_v5_1_deploy_table_rbac.sql # Create all data table & RBAC table here! +│ │ ├─ 02_dms_data_v5_1_triggers.sql # Create all triggers here! +│ │ ├─ 03_dms_data_v5_1_procedures_handlers.sql # Create all procedures here! +│ │ ├─ 04_dms_data_v5_1_views.sql # Create all views here! +│ │ ├─ 05 dms_data_v5_1_seeก_data.sql # Seed nescesary data here! +│ │ ├─ 06_dms_data_v5_1_seed_users.sql # Seed users data here! +│ │ └─ 07_dms_data_v5_1_seed_contract_dwg.sql # Seed contract drawing data here! +│ └─ my.cnf +├─ backend/ +│ ├─ app/ +│ ├─ src/ +│ │ ├─ db/ +│ │ │ └─models/ +│ │ ├─ middleware/ +│ │ ├─ routes/ +│ │ ├─ utils/ +│ │ └─ index.js +│ ├─ Dockerfile +│ ├─ package.json +│ └─ package-lock.json # ไม่มี +├─ frontend/ +│ ├─ app/ +│ │ ├─ correspondences/ +│ │ ├─ dashboard/ +│ │ ├─ health/ +│ │ ├─ login/ +│ │ └─ users/ +│ ├─ public/ +│ ├─ Dockerfile +│ ├─ package.json +│ ├─ package-lock.json # ไม่มี +│ ├─ next.config.js +│ └─ page.jsx +├─ phpmyadmin/ +│ ├─ sessions/ # โฟลเดอร์เซสชันถาวรของ phpMyAdmin +│ ├─ tmp/ +│ ├─ config.user.inc.php +│ └─ zzz-custom.ini +├─ nginx/ +│ ├─ certs/ +│ ├─ nginx.conf +│ └─ dms.conf +├─ n8n/ +├─ n8n-cache/ +├─ n8n-postgres/ +└─ logs/ +├─ backend/ +├─ frontend/ +├─ nginx/ +├─ pgadmin/ +├─ phpmyadmin/ +└─ postgres_n8n/ +/share/dms-data # เก็บข้อมมูล .pdf, .dwg แยกตาม correspondences, documents + +# งานที่ต้องการ: + +- ไม่ใช้ .env เด็ดขาด Container Station ไม่รองรับ และ docker-compose.yml ได้ทดสอบ รันบน Container station มาแล้ว +- Code ของ backend ทั้งหมด +- การทดสอบระบบ backend ทุกส่วน ให้พร้อม สำหรับ frontend + +# กรณี 2: มี Git อยู่แล้ว (มี main อยู่) + +2.1 อัปเดต main ให้ตรงล่าสุดก่อนแตกบร้านช์ + +cd /share/Container/dms +git checkout main +git pull --ff-only # ถ้าเชื่อม remote อยู่ +git tag -f stable-$(date +%F) # tag จุดเสถียรปัจจุบัน + +2.2 แตก branch งาน Dashboard +git checkout -b feature/dashboard-update-$(date +%y%m%d) +git checkout -b feature/dashboard-update-251004 + +2.3 ทำงาน/คอมมิตตามปกติ + +# แก้ไฟล์ frontend/app/dashboard/\* และที่เกี่ยวข้อง + +git add frontend/app/dashboard +git commit -m "feat(dashboard): เพิ่มส่วนจัดการ user" +git push -u origin feature/dashboard-update-251004 diff --git a/docs/DMS v1.3.0 - Interactive Report.html b/docs/DMS v1.3.0 - Interactive Report.html index f9323e8..b67df3d 100644 --- a/docs/DMS v1.3.0 - Interactive Report.html +++ b/docs/DMS v1.3.0 - Interactive Report.html @@ -1,724 +1,724 @@ - - - - - - DMS v1.3.0 - รายงานสรุปสถาปัตยกรรมระบบ - - - - - - - - - - - - - - -
- -
- - -
- - -
-

ภาพรวมระบบ (Overview)

-

- นี่คือรายงานสรุปเชิงโต้ตอบสำหรับ **ระบบจัดการเอกสาร (DMS) v1.3.0** แอปพลิเคชันนี้ถูกออกแบบมาเพื่อวิเคราะห์ข้อกำหนดของระบบ, สถาปัตยกรรม, และโครงสร้างข้อมูล เพื่อให้ทีมพัฒนาและผู้มีส่วนได้ส่วนเสียสามารถทำความเข้าใจภาพรวมของโปรเจกต์ที่ซับซ้อนนี้ได้อย่างรวดเร็ว -

-
-
-
6+
-
โมดูลเอกสารหลัก
-
-
-
44+
-
ตารางข้อมูล (Tables)
-
-
-
5+
-
วิวข้อมูล (Views)
-
-
-
7
-
Services (Docker)
-
-
-
- - -
-

สถาปัตยกรรมและเทคโนโลยี (System Architecture)

-

- ระบบ DMS v1.3.0 ถูกออกแบบบนสถาปัตยกรรม Headless/API-First ที่ทันสมัย โดยทำงานทั้งหมดบน QNAP Server ผ่าน Container Station (Docker) เพื่อให้ง่ายต่อการจัดการและบำรุงรักษา -

- -
-

แผนผังการเชื่อมต่อ (Docker Container Stack)

-
- -
-
Nginx Proxy Manager
-

np-dms.work (HTTPS)

- - -
-
Network: 'lcbp3'
- -
- -
-
-

Next.js (Frontend)

-

ให้บริการ UI (React)

-
-
- -
-
-

NestJS (Backend)

-

จัดการ API & Business Logic

-
-
- -
-
-

N8N (Automation)

-

จัดการ Workflow & Line Notify

-
-
-
- - -
-
-
-
- - -
-
-

MariaDB (Database)

-

เก็บข้อมูลทั้งหมด (SQL)

-
-
-

Elasticsearch (Search)

-

ให้บริการค้นหาขั้นสูง

-
-
- -
-
-
-
-
- - -
-

โมดูลเอกสารหลัก (Document Modules)

-

- ระบบ DMS แบ่งการจัดการเอกสารออกเป็นโมดูลย่อยที่ชัดเจน แต่ละโมดูลมี Workflow และตารางข้อมูลเฉพาะของตนเอง แต่ทั้งหมดเชื่อมโยงกับโครงสร้างเอกสารกลาง (Correspondence) -

-
- -
-
-

1. Correspondence (เอกสารโต้ตอบ)

-

โมดูลหลักสำหรับเอกสารเข้า-ออกทั่วไป (จดหมาย, เมโม) เป็นตาราง "แม่" สำหรับเอกสารประเภทอื่นเกือบทั้งหมด

- ตารางข้อมูลหลัก: -
    -
  • `correspondences` (Master)
  • -
  • `correspondence_revisions` (Child)
  • -
  • `correspondence_recipients`
  • -
-
-
- -
-
-

2. RFA (ขออนุมัติ)

-

โมดูลสำหรับเอกสารขออนุมัติ (Request for Approval) เช่น ขออนุมัติแบบ, วัสดุ, หรือเอกสาร ที่มี Workflow ชัดเจน (Draft, Submit, Review, Approved)

- ตารางข้อมูลหลัก: -
    -
  • `rfas` (Master)
  • -
  • `rfa_revisions` (Child)
  • -
  • `rfa_status_codes`
  • -
  • `rfa_items` (เชื่อม Shop Drawing)
  • -
-
-
- -
-
-

3. Drawing (แบบแปลน)

-

จัดการแบบแปลน 2 ประเภทหลัก: แบบตามสัญญา (Contract Drawing) และแบบสำหรับก่อสร้าง (Shop Drawing) ซึ่ง Shop Drawing จะถูกอ้างอิงใน RFA

- ตารางข้อมูลหลัก: -
    -
  • `contract_drawings`
  • -
  • `shop_drawings` (Master)
  • -
  • `shop_drawing_revisions` (Child)
  • -
  • `shop_drawing_revision_contract_refs`
  • - -
-
-
- -
-
-

4. Transmittal (เอกสารนำส่ง)

-

เอกสารนำส่ง (คล้ายใบปะหน้า) ที่ใช้สำหรับรวบรวมเอกสาร RFA หลายๆ ฉบับ เพื่อส่งให้ผู้รับในคราวเดียว

- ตารางข้อมูลหลัก: -
    -
  • `transmittals` (1:1 with Correspondence)
  • -
  • `transmittal_items` (เชื่อม RFA)
  • -
-
-
- -
-
-

5. Circulation (ใบเวียนภายใน)

-

ระบบใบเวียนภายในองค์กร (Internal) ใช้สำหรับส่งเอกสาร (ที่อ้างอิงจาก Correspondence) เพื่อให้ทีมตรวจสอบ, รับทราบ, หรือดำเนินการ

- ตารางข้อมูลหลัก: -
    -
  • `circulations` (Master)
  • -
  • `circulation_assignees` (Tasks)
  • -
  • `circulation_actions` (Logs)
  • -
-
-
- -
-
-

6. สัดส่วนตารางข้อมูล

-

แผนภูมิแสดงสัดส่วนตารางข้อมูลที่เกี่ยวข้องในแต่ละโมดูลหลัก (ไม่รวมตาราง Core และ RBAC)

-
- -
-
-
- -
-
- - -
-

ฟีเจอร์หลัก (Core Features)

-

- นอกเหนือจากโมดูลเอกสาร ระบบยังมีฟีเจอร์สนับสนุนที่สำคัญ ซึ่งทำงานข้ามโมดูลต่างๆ เพื่อให้ระบบทำงานได้อย่างสมบูรณ์ -

-
- -
-

การจัดการสิทธิ์ (RBAC)

-

- ระบบใช้ Role-Based Access Control (RBAC) ที่ละเอียดมาก ผู้ใช้ (Users) จะได้รับบทบาท (Roles) ซึ่งผูกกับสิทธิ์ (Permissions) - แผนภูมินี้แสดงตัวอย่างจำนวนสิทธิ์ (Permissions) ที่แต่ละบทบาทพื้นฐานอาจมี เพื่อให้เห็นภาพความซับซ้อนในการเข้าถึง -

-
- -
-
- -
-

การสร้างเลขที่เอกสาร (Numbering)

-

- หนึ่งในส่วนที่สำคัญที่สุด คือการสร้างเลขที่เอกสาร (Running Number) เพื่อป้องกันเลขที่ซ้ำ (Race Condition) ระบบใช้ Stored Procedure (`sp_get_next_document_number`) ใน MariaDB ซึ่งใช้คำสั่ง `SELECT ... FOR UPDATE` เพื่อ "ล็อก" แถวของตัวนับก่อนที่จะอัปเดตค่า -

-
-SELECT last_number
-INTO v_last_number
-FROM document_number_counters
-WHERE project_id = p_project_id 
-  AND ...
-FOR UPDATE;
-
-IF v_last_number IS NULL THEN
-  INSERT INTO ... (last_number) VALUES (1);
-  SET p_next_number = 1;
-ELSE
-  SET p_next_number = v_last_number + 1;
-  UPDATE document_number_counters
-  SET last_number = p_next_number
-  WHERE ...;
-END IF;
-
- -
-

การค้นหา (Advanced Search)

-

- (Req 6.2) ระบบใช้ **Elasticsearch** เป็น Service แยกต่างหากสำหรับให้บริการค้นหาขั้นสูง - NestJS (Backend) จะทำการ Index ข้อมูลจาก Views (`v_current_correspondences`, `v_current_rfas` ฯลฯ) ไปยัง Elasticsearch - ทำให้ผู้ใช้สามารถค้นหาแบบ Full-text ข้ามโมดูลทั้งหมด (Correspondence, RFA, Drawing) ได้อย่างรวดเร็ว -

-
- -
-

การแจ้งเตือน (Notifications)

-

- (Req 6.7) ระบบใช้ **N8N (Automation)** เป็นศูนย์กลางการแจ้งเตือน - เมื่อมีเหตุการณ์สำคัญ (เช่น มีการสร้างใบเวียนใหม่) NestJS (Backend) จะยิง Webhook ไปที่ N8N - จากนั้น N8N จะทำหน้าที่ส่งการแจ้งเตือนไปยัง **Line Notify** หรือ Email ตาม Workflow ที่ตั้งค่าไว้ -

-
-
-
- - -
-

สำรวจโครงสร้างข้อมูล (Data Explorer)

-

- ระบบ DMS มีตารางข้อมูลมากกว่า 44 ตารางเพื่อรองรับฟีเจอร์ที่ซับซ้อน เลือกตารางจากรายการด้านล่างเพื่อดูโครงสร้าง, ประเภทข้อมูล, และความสัมพันธ์ (Foreign Keys) -

- -
-
- - -
- -
-

-

- -
- - - - - - - - - - - -
Column (Field)TypeDescription / Relation
-
-
-
-
- -
- -
-

Interactive Report generated for DMS v1.3.0

-
- - - + + + + + + DMS v1.3.0 - รายงานสรุปสถาปัตยกรรมระบบ + + + + + + + + + + + + + + +
+ +
+ + +
+ + +
+

ภาพรวมระบบ (Overview)

+

+ นี่คือรายงานสรุปเชิงโต้ตอบสำหรับ **ระบบจัดการเอกสาร (DMS) v1.3.0** แอปพลิเคชันนี้ถูกออกแบบมาเพื่อวิเคราะห์ข้อกำหนดของระบบ, สถาปัตยกรรม, และโครงสร้างข้อมูล เพื่อให้ทีมพัฒนาและผู้มีส่วนได้ส่วนเสียสามารถทำความเข้าใจภาพรวมของโปรเจกต์ที่ซับซ้อนนี้ได้อย่างรวดเร็ว +

+
+
+
6+
+
โมดูลเอกสารหลัก
+
+
+
44+
+
ตารางข้อมูล (Tables)
+
+
+
5+
+
วิวข้อมูล (Views)
+
+
+
7
+
Services (Docker)
+
+
+
+ + +
+

สถาปัตยกรรมและเทคโนโลยี (System Architecture)

+

+ ระบบ DMS v1.3.0 ถูกออกแบบบนสถาปัตยกรรม Headless/API-First ที่ทันสมัย โดยทำงานทั้งหมดบน QNAP Server ผ่าน Container Station (Docker) เพื่อให้ง่ายต่อการจัดการและบำรุงรักษา +

+ +
+

แผนผังการเชื่อมต่อ (Docker Container Stack)

+
+ +
+
Nginx Proxy Manager
+

np-dms.work (HTTPS)

+ + +
+
Network: 'lcbp3'
+ +
+ +
+
+

Next.js (Frontend)

+

ให้บริการ UI (React)

+
+
+ +
+
+

NestJS (Backend)

+

จัดการ API & Business Logic

+
+
+ +
+
+

N8N (Automation)

+

จัดการ Workflow & Line Notify

+
+
+
+ + +
+
+
+
+ + +
+
+

MariaDB (Database)

+

เก็บข้อมูลทั้งหมด (SQL)

+
+
+

Elasticsearch (Search)

+

ให้บริการค้นหาขั้นสูง

+
+
+ +
+
+
+
+
+ + +
+

โมดูลเอกสารหลัก (Document Modules)

+

+ ระบบ DMS แบ่งการจัดการเอกสารออกเป็นโมดูลย่อยที่ชัดเจน แต่ละโมดูลมี Workflow และตารางข้อมูลเฉพาะของตนเอง แต่ทั้งหมดเชื่อมโยงกับโครงสร้างเอกสารกลาง (Correspondence) +

+
+ +
+
+

1. Correspondence (เอกสารโต้ตอบ)

+

โมดูลหลักสำหรับเอกสารเข้า-ออกทั่วไป (จดหมาย, เมโม) เป็นตาราง "แม่" สำหรับเอกสารประเภทอื่นเกือบทั้งหมด

+ ตารางข้อมูลหลัก: +
    +
  • `correspondences` (Master)
  • +
  • `correspondence_revisions` (Child)
  • +
  • `correspondence_recipients`
  • +
+
+
+ +
+
+

2. RFA (ขออนุมัติ)

+

โมดูลสำหรับเอกสารขออนุมัติ (Request for Approval) เช่น ขออนุมัติแบบ, วัสดุ, หรือเอกสาร ที่มี Workflow ชัดเจน (Draft, Submit, Review, Approved)

+ ตารางข้อมูลหลัก: +
    +
  • `rfas` (Master)
  • +
  • `rfa_revisions` (Child)
  • +
  • `rfa_status_codes`
  • +
  • `rfa_items` (เชื่อม Shop Drawing)
  • +
+
+
+ +
+
+

3. Drawing (แบบแปลน)

+

จัดการแบบแปลน 2 ประเภทหลัก: แบบตามสัญญา (Contract Drawing) และแบบสำหรับก่อสร้าง (Shop Drawing) ซึ่ง Shop Drawing จะถูกอ้างอิงใน RFA

+ ตารางข้อมูลหลัก: +
    +
  • `contract_drawings`
  • +
  • `shop_drawings` (Master)
  • +
  • `shop_drawing_revisions` (Child)
  • +
  • `shop_drawing_revision_contract_refs`
  • + +
+
+
+ +
+
+

4. Transmittal (เอกสารนำส่ง)

+

เอกสารนำส่ง (คล้ายใบปะหน้า) ที่ใช้สำหรับรวบรวมเอกสาร RFA หลายๆ ฉบับ เพื่อส่งให้ผู้รับในคราวเดียว

+ ตารางข้อมูลหลัก: +
    +
  • `transmittals` (1:1 with Correspondence)
  • +
  • `transmittal_items` (เชื่อม RFA)
  • +
+
+
+ +
+
+

5. Circulation (ใบเวียนภายใน)

+

ระบบใบเวียนภายในองค์กร (Internal) ใช้สำหรับส่งเอกสาร (ที่อ้างอิงจาก Correspondence) เพื่อให้ทีมตรวจสอบ, รับทราบ, หรือดำเนินการ

+ ตารางข้อมูลหลัก: +
    +
  • `circulations` (Master)
  • +
  • `circulation_assignees` (Tasks)
  • +
  • `circulation_actions` (Logs)
  • +
+
+
+ +
+
+

6. สัดส่วนตารางข้อมูล

+

แผนภูมิแสดงสัดส่วนตารางข้อมูลที่เกี่ยวข้องในแต่ละโมดูลหลัก (ไม่รวมตาราง Core และ RBAC)

+
+ +
+
+
+ +
+
+ + +
+

ฟีเจอร์หลัก (Core Features)

+

+ นอกเหนือจากโมดูลเอกสาร ระบบยังมีฟีเจอร์สนับสนุนที่สำคัญ ซึ่งทำงานข้ามโมดูลต่างๆ เพื่อให้ระบบทำงานได้อย่างสมบูรณ์ +

+
+ +
+

การจัดการสิทธิ์ (RBAC)

+

+ ระบบใช้ Role-Based Access Control (RBAC) ที่ละเอียดมาก ผู้ใช้ (Users) จะได้รับบทบาท (Roles) ซึ่งผูกกับสิทธิ์ (Permissions) + แผนภูมินี้แสดงตัวอย่างจำนวนสิทธิ์ (Permissions) ที่แต่ละบทบาทพื้นฐานอาจมี เพื่อให้เห็นภาพความซับซ้อนในการเข้าถึง +

+
+ +
+
+ +
+

การสร้างเลขที่เอกสาร (Numbering)

+

+ หนึ่งในส่วนที่สำคัญที่สุด คือการสร้างเลขที่เอกสาร (Running Number) เพื่อป้องกันเลขที่ซ้ำ (Race Condition) ระบบใช้ Stored Procedure (`sp_get_next_document_number`) ใน MariaDB ซึ่งใช้คำสั่ง `SELECT ... FOR UPDATE` เพื่อ "ล็อก" แถวของตัวนับก่อนที่จะอัปเดตค่า +

+
+SELECT last_number
+INTO v_last_number
+FROM document_number_counters
+WHERE project_id = p_project_id 
+  AND ...
+FOR UPDATE;
+
+IF v_last_number IS NULL THEN
+  INSERT INTO ... (last_number) VALUES (1);
+  SET p_next_number = 1;
+ELSE
+  SET p_next_number = v_last_number + 1;
+  UPDATE document_number_counters
+  SET last_number = p_next_number
+  WHERE ...;
+END IF;
+
+ +
+

การค้นหา (Advanced Search)

+

+ (Req 6.2) ระบบใช้ **Elasticsearch** เป็น Service แยกต่างหากสำหรับให้บริการค้นหาขั้นสูง + NestJS (Backend) จะทำการ Index ข้อมูลจาก Views (`v_current_correspondences`, `v_current_rfas` ฯลฯ) ไปยัง Elasticsearch + ทำให้ผู้ใช้สามารถค้นหาแบบ Full-text ข้ามโมดูลทั้งหมด (Correspondence, RFA, Drawing) ได้อย่างรวดเร็ว +

+
+ +
+

การแจ้งเตือน (Notifications)

+

+ (Req 6.7) ระบบใช้ **N8N (Automation)** เป็นศูนย์กลางการแจ้งเตือน + เมื่อมีเหตุการณ์สำคัญ (เช่น มีการสร้างใบเวียนใหม่) NestJS (Backend) จะยิง Webhook ไปที่ N8N + จากนั้น N8N จะทำหน้าที่ส่งการแจ้งเตือนไปยัง **Line Notify** หรือ Email ตาม Workflow ที่ตั้งค่าไว้ +

+
+
+
+ + +
+

สำรวจโครงสร้างข้อมูล (Data Explorer)

+

+ ระบบ DMS มีตารางข้อมูลมากกว่า 44 ตารางเพื่อรองรับฟีเจอร์ที่ซับซ้อน เลือกตารางจากรายการด้านล่างเพื่อดูโครงสร้าง, ประเภทข้อมูล, และความสัมพันธ์ (Foreign Keys) +

+ +
+
+ + +
+ +
+

+

+ +
+ + + + + + + + + + + +
Column (Field)TypeDescription / Relation
+
+
+
+
+ +
+ +
+

Interactive Report generated for DMS v1.3.0

+
+ + + \ No newline at end of file diff --git a/docs/Docker compose all.yaml b/docs/Docker compose all.yaml index 7d0a73e..fc67da5 100644 --- a/docs/Docker compose all.yaml +++ b/docs/Docker compose all.yaml @@ -1,194 +1,194 @@ -# version: '3.8' - -# ========================================================== -# Volumes (พื้นที่จัดเก็บข้อมูลถาวร) -# ========================================================== -volumes: - # (จากไฟล์เดิม) - backend_node_modules: - - # (ที่เพิ่มใหม่) - db_data: # 2.4. Database - npm_data: # 2.8. Reverse Proxy - npm_letsencrypt: # 2.8. SSL Certs - es_data: # 6.2. Elasticsearch - n8n_data: # 2.7. n8n - gitea_data: # 2.2. Gitea - -# ========================================================== -# Services (บริการทั้งหมดของระบบ) -# ========================================================== -services: - # -------------------------------------------------------- - # Service 1: Reverse Proxy (Nginx Proxy Manager) - # -------------------------------------------------------- - npm: - image: 'jc21/nginx-proxy-manager:latest' - container_name: npm - restart: unless-stopped - ports: - - '80:80' # HTTP - - '443:443' # HTTPS - - '81:81' # Admin UI - volumes: - - npm_data:/data - - npm_letsencrypt:/etc/letsencrypt - networks: - - lcbp3 - - # -------------------------------------------------------- - # Service 2: Database (MariaDB) - # -------------------------------------------------------- - mariadb: - image: mariadb:10.11 - container_name: mariadb - restart: unless-stopped - ports: - - "3306:3306" - volumes: - - db_data:/var/lib/mysql - environment: - - MYSQL_ROOT_PASSWORD=YOUR_STRONG_ROOT_PASSWORD - - MYSQL_DATABASE=lcbp3 - - MYSQL_USER=center - - MYSQL_PASSWORD=Center#2025 - - TZ=Asia/Bangkok - networks: - - lcbp3 - - # -------------------------------------------------------- - # Service 3: Database UI (phpMyAdmin) - # -------------------------------------------------------- - pma: - image: phpmyadmin:5-apache - container_name: pma - restart: unless-stopped - ports: - - "8080:80" - environment: - - PMA_HOST=mariadb - - PMA_PORT=3306 - - UPLOAD_LIMIT=256M - networks: - - lcbp3 - depends_on: - - mariadb - - # -------------------------------------------------------- - # Service 4: Backend (NestJS) - # (ปรับแก้จากไฟล์ ของคุณ) - # -------------------------------------------------------- - backend: - build: - context: /share/Container/lcbp3/backend - container_name: backend - restart: unless-stopped - stdin_open: true - tty: true - command: npm run start:dev - networks: - - lcbp3 - ports: - - "3000:3000" - environment: - # --- Database Connection (จากไฟล์เดิม) --- - - DB_HOST=mariadb - - DB_PORT=3306 - - DB_USER=center - - DB_PASSWORD=Center#2025 - - DB_NAME=lcbp3 - - PORT=3000 - # --- Security (จากไฟล์เดิม) --- - - JWT_SECRET=9a6d8705a6695ab9bae4ca1cd46c72a6379aa72404b96e2c5b59af881bb55c639dd583afdce5a885c68e188da55ce6dbc1fb4aa9cd4055ceb51507e56204e4ca - - JWT_EXPIRES_IN=1d - # --- (เพิ่มใหม่) Environment Variables ที่ Service ต้องการ --- - - STORAGE_PATH=/app/storage # (Path ภายใน Container ที่เชื่อมกับ /share/dms-data) - - ELASTICSEARCH_NODE=http://elasticsearch:9200 - - N8N_WEBHOOK_URL=http://n8n:5678/webhook/lcbp3-notify - volumes: - - /share/Container/lcbp3/backend:/app - - /share/dms-data:/app/storage # (เชื่อม Path จริงบน QNAP เข้ากับ Container) - - /share/Container/dms/logs/backend:/app/logs:rw - - backend_node_modules:/app/node_modules - depends_on: - - mariadb - - # -------------------------------------------------------- - # Service 5: Frontend (Next.js) - # -------------------------------------------------------- - frontend: - build: - context: /share/Container/lcbp3/frontend - container_name: frontend - restart: unless-stopped - command: npm run dev - ports: - - "3001:3000" # (ใช้ Host Port 3001) - networks: - - lcbp3 - volumes: - - /share/Container/lcbp3/frontend:/app - - /share/Container/lcbp3/frontend/node_modules:/app/node_modules - environment: - # (Frontend ต้องเรียก API ผ่าน Domain ที่ NPM จัดการ) - - NEXT_PUBLIC_API_URL=https://backend.np-dms.work - depends_on: - - backend - - # -------------------------------------------------------- - # Service 6: Search (Elasticsearch) - # -------------------------------------------------------- - elasticsearch: - image: elasticsearch:8.11.0 # (แนะนำให้ระบุเวอร์ชัน) - container_name: elasticsearch - restart: unless-stopped - ports: - - "9200:9200" - volumes: - - es_data:/usr/share/elasticsearch/data - environment: - - discovery.type=single-node - - xpack.security.enabled=false # (ปิดการยืนยันตัวตนสำหรับ Dev) - - ES_JAVA_OPTS=-Xms512m -Xmx512m # (จำกัด RAM) - networks: - - lcbp3 - - # -------------------------------------------------------- - # Service 7: Workflow (n8n) - # -------------------------------------------------------- - n8n: - image: n8nio/n8n:latest - container_name: n8n - restart: unless-stopped - ports: - - "5678:5678" - volumes: - - n8n_data:/home/node/.n8n - environment: - - TZ=Asia/Bangkok - networks: - - lcbp3 - - # -------------------------------------------------------- - # Service 8: Code Hosting (Gitea) - # -------------------------------------------------------- - gitea: - image: gitea/gitea:latest - container_name: gitea - restart: unless-stopped - ports: - - "3002:3000" # (ใช้ Host Port 3002) - - "2222:22" # (ใช้ Host Port 2222 สำหรับ SSH) - volumes: - - gitea_data:/data - networks: - - lcbp3 - depends_on: - - mariadb - -# ========================================================== -# Networks (เครือข่ายกลาง) -# ========================================================== -networks: - lcbp3: +# version: '3.8' + +# ========================================================== +# Volumes (พื้นที่จัดเก็บข้อมูลถาวร) +# ========================================================== +volumes: + # (จากไฟล์เดิม) + backend_node_modules: + + # (ที่เพิ่มใหม่) + db_data: # 2.4. Database + npm_data: # 2.8. Reverse Proxy + npm_letsencrypt: # 2.8. SSL Certs + es_data: # 6.2. Elasticsearch + n8n_data: # 2.7. n8n + gitea_data: # 2.2. Gitea + +# ========================================================== +# Services (บริการทั้งหมดของระบบ) +# ========================================================== +services: + # -------------------------------------------------------- + # Service 1: Reverse Proxy (Nginx Proxy Manager) + # -------------------------------------------------------- + npm: + image: 'jc21/nginx-proxy-manager:latest' + container_name: npm + restart: unless-stopped + ports: + - '80:80' # HTTP + - '443:443' # HTTPS + - '81:81' # Admin UI + volumes: + - npm_data:/data + - npm_letsencrypt:/etc/letsencrypt + networks: + - lcbp3 + + # -------------------------------------------------------- + # Service 2: Database (MariaDB) + # -------------------------------------------------------- + mariadb: + image: mariadb:10.11 + container_name: mariadb + restart: unless-stopped + ports: + - "3306:3306" + volumes: + - db_data:/var/lib/mysql + environment: + - MYSQL_ROOT_PASSWORD=YOUR_STRONG_ROOT_PASSWORD + - MYSQL_DATABASE=lcbp3 + - MYSQL_USER=center + - MYSQL_PASSWORD=Center#2025 + - TZ=Asia/Bangkok + networks: + - lcbp3 + + # -------------------------------------------------------- + # Service 3: Database UI (phpMyAdmin) + # -------------------------------------------------------- + pma: + image: phpmyadmin:5-apache + container_name: pma + restart: unless-stopped + ports: + - "8080:80" + environment: + - PMA_HOST=mariadb + - PMA_PORT=3306 + - UPLOAD_LIMIT=256M + networks: + - lcbp3 + depends_on: + - mariadb + + # -------------------------------------------------------- + # Service 4: Backend (NestJS) + # (ปรับแก้จากไฟล์ ของคุณ) + # -------------------------------------------------------- + backend: + build: + context: /share/Container/lcbp3/backend + container_name: backend + restart: unless-stopped + stdin_open: true + tty: true + command: npm run start:dev + networks: + - lcbp3 + ports: + - "3000:3000" + environment: + # --- Database Connection (จากไฟล์เดิม) --- + - DB_HOST=mariadb + - DB_PORT=3306 + - DB_USER=center + - DB_PASSWORD=Center#2025 + - DB_NAME=lcbp3 + - PORT=3000 + # --- Security (จากไฟล์เดิม) --- + - JWT_SECRET=9a6d8705a6695ab9bae4ca1cd46c72a6379aa72404b96e2c5b59af881bb55c639dd583afdce5a885c68e188da55ce6dbc1fb4aa9cd4055ceb51507e56204e4ca + - JWT_EXPIRES_IN=1d + # --- (เพิ่มใหม่) Environment Variables ที่ Service ต้องการ --- + - STORAGE_PATH=/app/storage # (Path ภายใน Container ที่เชื่อมกับ /share/dms-data) + - ELASTICSEARCH_NODE=http://elasticsearch:9200 + - N8N_WEBHOOK_URL=http://n8n:5678/webhook/lcbp3-notify + volumes: + - /share/Container/lcbp3/backend:/app + - /share/dms-data:/app/storage # (เชื่อม Path จริงบน QNAP เข้ากับ Container) + - /share/Container/dms/logs/backend:/app/logs:rw + - backend_node_modules:/app/node_modules + depends_on: + - mariadb + + # -------------------------------------------------------- + # Service 5: Frontend (Next.js) + # -------------------------------------------------------- + frontend: + build: + context: /share/Container/lcbp3/frontend + container_name: frontend + restart: unless-stopped + command: npm run dev + ports: + - "3001:3000" # (ใช้ Host Port 3001) + networks: + - lcbp3 + volumes: + - /share/Container/lcbp3/frontend:/app + - /share/Container/lcbp3/frontend/node_modules:/app/node_modules + environment: + # (Frontend ต้องเรียก API ผ่าน Domain ที่ NPM จัดการ) + - NEXT_PUBLIC_API_URL=https://backend.np-dms.work + depends_on: + - backend + + # -------------------------------------------------------- + # Service 6: Search (Elasticsearch) + # -------------------------------------------------------- + elasticsearch: + image: elasticsearch:8.11.0 # (แนะนำให้ระบุเวอร์ชัน) + container_name: elasticsearch + restart: unless-stopped + ports: + - "9200:9200" + volumes: + - es_data:/usr/share/elasticsearch/data + environment: + - discovery.type=single-node + - xpack.security.enabled=false # (ปิดการยืนยันตัวตนสำหรับ Dev) + - ES_JAVA_OPTS=-Xms512m -Xmx512m # (จำกัด RAM) + networks: + - lcbp3 + + # -------------------------------------------------------- + # Service 7: Workflow (n8n) + # -------------------------------------------------------- + n8n: + image: n8nio/n8n:latest + container_name: n8n + restart: unless-stopped + ports: + - "5678:5678" + volumes: + - n8n_data:/home/node/.n8n + environment: + - TZ=Asia/Bangkok + networks: + - lcbp3 + + # -------------------------------------------------------- + # Service 8: Code Hosting (Gitea) + # -------------------------------------------------------- + gitea: + image: gitea/gitea:latest + container_name: gitea + restart: unless-stopped + ports: + - "3002:3000" # (ใช้ Host Port 3002) + - "2222:22" # (ใช้ Host Port 2222 สำหรับ SSH) + volumes: + - gitea_data:/data + networks: + - lcbp3 + depends_on: + - mariadb + +# ========================================================== +# Networks (เครือข่ายกลาง) +# ========================================================== +networks: + lcbp3: external: true \ No newline at end of file diff --git a/docs/LCBP3-DMS_V1_4_0_Backend_Development_Plan.md b/docs/LCBP3-DMS_V1_4_0_Backend_Development_Plan.md index eb3a275..314b4fd 100644 --- a/docs/LCBP3-DMS_V1_4_0_Backend_Development_Plan.md +++ b/docs/LCBP3-DMS_V1_4_0_Backend_Development_Plan.md @@ -1,655 +1,655 @@ -# 📋 แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.0 - -## 🎯 ภาพรวมโครงการ - -พัฒนา Backend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่รองรับการจัดการเอกสารที่ซับซ้อน มีระบบ Workflow การอนุมัติ และการควบคุมสิทธิ์แบบ RBAC 3 ระดับ - ---- - -## 📐 สถาปัตยกรรมระบบ - -### Technology Stack - -- **Framework:** NestJS (TypeScript, ESM) -- **Database:** MariaDB 10.11 -- **ORM:** TypeORM -- **Authentication:** JWT + Passport -- **Authorization:** CASL (RBAC) -- **File Upload:** Multer -- **Search:** Elasticsearch -- **Notification:** Nodemailer + n8n (Line Integration) -- **Scheduling:** @nestjs/schedule (Cron Jobs) -- **Documentation:** Swagger - -### โครงสร้างโมดูล (Domain-Driven) - -```tree -src/ -├── common/ # Shared Module -│ ├── auth/ # JWT, Guards -│ ├── config/ # Configuration -│ ├── decorators/ # @RequirePermission -│ ├── entities/ # Base Entities -│ ├── exceptions/ # Global Filters -│ ├── file-storage/ # FileStorageService -│ ├── guards/ # RBAC Guard -│ ├── interceptors/ # Audit, Transform -│ └── services/ # Notification, etc. -├── modules/ -│ ├── user/ # Users, Roles, Permissions -│ ├── project/ # Projects, Contracts, Organizations -│ ├── master/ # Master Data -│ ├── correspondence/ # Correspondence Management -│ ├── rfa/ # RFA & Workflows -│ ├── drawing/ # Shop/Contract Drawings -│ ├── circulation/ # Internal Circulation -│ ├── transmittal/ # Transmittals -│ ├── search/ # Elasticsearch -│ └── document-numbering/ # Internal Service -└── database/ # Migrations & Seeds -``` - ---- - -## 🗓️ แผนการพัฒนาแบบ Phase-Based - -## **Phase 0: Infrastructure Setup (สัปดาห์ที่ 1)** - -Milestone: สร้างโครงสร้างพื้นฐานและเชื่อมต่อ Services - -### Phase 0: Tasks - -- **[✅]T0.1 Setup QNAP Container Station** - - - สร้าง Docker Network: `lcbp3` - - Setup docker-compose.yml สำหรับ: - - MariaDB (db.np-dms.work) - - PHPMyAdmin (pma.np-dms.work) - - Backend (backend.np-dms.work) - - Nginx Proxy Manager (npm.np-dms.work) - - กำหนด Environment Variables ใน docker-compose.yml (ไม่ใช้ .env) - - Deliverable: Services ทั้งหมดรันได้และเชื่อมต่อกันผ่าน Network - -- **[✅]T0.2 Initialize NestJS Project** - - - สร้างโปรเจกต์ใหม่ด้วย Nest CLI - - ติดตั้ง Dependencies: - - ```bash - npm install @nestjs/typeorm typeorm mysql2 - npm install @nestjs/config - npm install class-validator class-transformer - npm install @nestjs/jwt @nestjs/passport passport passport-jwt - npm install casl - npm install @nestjs/platform-express multer - npm install @nestjs/swagger - npm install helmet rate-limiter-flexible - npm install bcrypt - npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @types/multer supertest - npm install @nestjs/cache-manager cache-manager - npm install @nestjs/schedule - npm install @nestjs/config - npm install @nestjs/elasticsearch @elastic/elasticsearch - npm install nodemailer @types/nodemailer - npm install uuid @types/uuid - ``` - -```` - - - Setup โครงสร้างโฟลเดอร์ตาม Domain-Driven Architecture - - Deliverable: Project Structure พร้อม, แสดง Swagger ที่ `/api` - -- **[✅]T0.3 Setup Database Connection** - - - Import SQL Schema v1.4.0 เข้า MariaDB - - Run Seed Data (organizations, users, roles, permissions) - - Configure TypeORM ใน AppModule - - ทดสอบ Connection - - Deliverable: Database พร้อมใช้งาน, มี Seed Data - -- **[✅]T0.4 Setup Git Repository** - - สร้าง Repository ใน Gitea (git.np-dms.work) - - Setup .gitignore, README.md - - Commit Initial Project - - Deliverable: Code อยู่ใน Version Control - ---- - -## **Phase 1: Core Foundation (สัปดาห์ที่ 2-3)** - -Milestone: ระบบ Authentication, Authorization และ Base Entities - -### Phase 1: Tasks - -- **[ ] T1.1 CommonModule - Base Infrastructure** - - [ ] สร้าง Base Entity (id, created_at, updated_at, deleted_at) - - [ ] สร้าง Global Exception Filter - - [ ] สร้าง Response Transform Interceptor - - [ ] สร้าง Audit Log Interceptor - - [ ] สร้าง RequestContextService - สำหรับเก็บข้อมูลระหว่าง Request - - [ ] สร้าง ConfigService - Centralized configuration management - - [ ] สร้าง CryptoService - สำหรับ encryption/decryption - - [ ] Deliverable: Common Services พร้อมใช้ - -- **[ ] T1.2 AuthModule - JWT Authentication** - - [ ] สร้าง Entity: User - - [ ] สร้าง AuthService: - - [ ] `login(username, password)` → JWT Token - - [ ] `validateUser(username, password)` → User | null - - [ ] Password Hashing (bcrypt) - - [ ] สร้าง JWT Strategy (Passport) - - [ ] สร้าง JwtAuthGuard - - [ ] สร้าง Controllers: - - [ ] `POST /auth/login` → { access_token } - - [ ] `POST /auth/register` (Admin only) - - [ ] `GET /auth/profile` (Protected) - - [ ] Deliverable: ล็อกอิน/ล็อกเอาต์ทำงานได้ - -- **[ ] T1.3 UserModule - User Management** - - [ ] สร้าง Entities: User, Role, Permission, UserRole - - [ ] สร้าง UserService CRUD - - [ ] สร้าง RoleService CRUD - - [ ] สร้าง PermissionService (Read-Only, จาก Seed) - - [ ] สร้าง Controllers: - - [ ] `GET /users` → List Users (Paginated) - - [ ] `GET /users/:id` → User Detail - - [ ] `POST /users` → Create User - - [ ] `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 - - [ ] Deliverable: จัดการผู้ใช้และ Role ได้ - -- **[ ] T1.4 RBAC Guard - Authorization** - - [ ] สร้าง `@RequirePermission()` Decorator - - [ ] สร้าง RbacGuard ที่ตรวจสอบ: - - [ ] Global Permissions - - [ ] Organization Permissions - - [ ] Project Permissions - - [ ] Contract Permissions - - [ ] Permission Hierarchy Logic - -```bash - // Current: 3-level hierarchy - // Recommended: 4-level hierarchy (Global → Organization → Project → Contract) - @Injectable() - export class RbacGuard implements CanActivate { - async canActivate(context: ExecutionContext): Promise { - const requiredPermission = this.getRequiredPermission(context); - const user = this.getUser(context); - - // Check permissions in order: Global → Org → Project → Contract - return await this.checkGlobalPermissions(user, requiredPermission) || - await this.checkOrgPermissions(user, requiredPermission) || - await this.checkProjectPermissions(user, requiredPermission) || - await this.checkContractPermissions(user, requiredPermission); - } - } - -```` - -- [ ] Integration กับ CASL -- [ ] Deliverable: ระบบสิทธิ์ทำงานได้ทั้ง 4 ระดับ - -- **T1.5 ProjectModule - Base Structures** - - [ ] สร้าง Entities: - - [ ] Organization - - [ ] Project - - [ ] Contract - - [ ] ProjectOrganization (Junction) - - [ ] ContractOrganization (Junction) - - [ ] UserAssignment Entity - สำหรับจัดการ user assignments ตาม scope - - [ ] สร้าง Services & Controllers: - - [ ] `GET /organizations` → List - - [ ] `POST /projects` → Create (Superadmin) - - [ ] `GET /projects/:id/contracts` → List Contracts - - [ ] `POST /projects/:id/contracts` → Create Contract - - [ ] UserAssignment - สำหรับจัดการ user assignments ตาม scope - - [ ] ProjectOrganization - สำหรับจัดการความสัมพันธ์ project-organization - - [ ] ContractOrganization - สำหรับจัดการความสัมพันธ์ contract-organization - - [ ] Deliverable: จัดการโครงสร้างโปรเจกต์ได้ - ---- - -## **Phase 2: Master Data & File Management (สัปดาห์ที่ 4)** - -Milestone: Master Data และระบบจัดการไฟล์ - -### Phase 2: Tasks - -- **[ ] T2.1 MasterModule - Master Data Management** - - - [ ] สร้าง Entities: - - [ ] CorrespondenceType - - [ ] CorrespondenceStatus - - [ ] RfaType - - [ ] RfaStatusCode - - [ ] RfaApproveCode - - [ ] CirculationStatusCode - - [ ] Tag - - [ ] สร้าง Services & Controllers (CRUD): - - [ ] `GET /master/correspondence-types` - - [ ] `POST /master/tags` → Create Tag - - [ ] `GET /master/tags` → List Tags (Autocomplete) - - [ ] Deliverable: Admin จัดการ Master Data ได้ - -- **[ ] T2.2 FileStorageService - Central File Management** - - - [ ] สร้าง Attachment Entity - - [ ] สร้าง FileStorageService: (การจัดเก็บไฟล์ในรูปแบบ centralized storage, ครอบคลุมการจัดการไฟล์แนบทั้งหมด, Security Measures) - - [ ] `uploadFile(file: Express.Multer.File)` → Attachment - - [ ] `getFilePath(attachmentId)` → string - - [ ] `deleteFile(attachmentId)` → boolean - - [ ] จัดเก็บไฟล์ใน `/share/dms-data/uploads/{YYYY}/{MM}/` - - [ ] สร้าง Controller: - - [ ] `POST /files/upload` → { attachment_id, url } - - [ ] `GET /files/:id/download` → File Stream (Protected) - - [ ] Access Control: ตรวจสอบสิทธิ์ผ่าน Junction Table - - [ ] Deliverable: อัปโหลด/ดาวน์โหลดไฟล์ได้อย่างปลอดภัย - -- **[ ] T2.3 DocumentNumberingModule - Internal Service** - - [ ] สร้าง Entities: - - [ ] DocumentNumberFormat - - [ ] DocumentNumberCounter - - [ ] สร้าง DocumentNumberingService: รวม Stored Procedure (sp_get_next_document_number), Error Handling และ Retry Logic - - [ ] `generateNextNumber(projectId, orgId, typeId, year)` → string - - [ ] เรียก Stored Procedure: `sp_get_next_document_number` - - [ ] Format ตาม Template: `{ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}` - - **ไม่มี Controller** (Internal Service เท่านั้น) - - [ ] Deliverable: Service สร้างเลขที่เอกสารได้ถูกต้อง - ---- - -## **Phase 3: Correspondence & RFA Core (สัปดาห์ที่ 5-6)** - -Milestone: ระบบเอกสารโต้ตอบและ RFA - -### Phase 3: Tasks - -- **[ ] T3.1 CorrespondenceModule - Basic CRUD** - - - [ ] สร้าง Entities: - - [ ] Correspondence - - [ ] CorrespondenceRevision - - [ ] CorrespondenceRecipient - - [ ] CorrespondenceTag - - [ ] CorrespondenceReference - - [ ] CorrespondenceAttachment - - [ ] สร้าง CorrespondenceService: Complex Business Rules, State Machine สำหรับ Status Transitions - - [ ] `create(dto)` → Correspondence - - [ ] สร้าง Correspondence + Revision แรก (rev 0) - - [ ] เรียก DocumentNumberingService - - [ ] สร้าง Recipients (TO/CC) - - [ ] สร้าง Tags - - [ ] สร้าง Attachments - - [ ] `update(id, dto)` → Correspondence - - [ ] สร้าง Revision ใหม่ - - [ ] Update `is_current` flag - - [ ] `findAll(filters)` → Paginated List - - [ ] `findById(id)` → Correspondence with Current Revision - - [ ] สร้าง Controllers: - - [ ] `POST /correspondences` → Create - - [ ] `GET /correspondences` → List (Filter by type, status, org) - - [ ] `GET /correspondences/:id` → Detail - - [ ] `PUT /correspondences/:id` → Update (Create new revision) - - [ ] `DELETE /correspondences/:id` → Soft Delete (Admin only) - - [ ] Deliverable: สร้าง/แก้ไข/ดูเอกสารได้ - -- **[ ] T3.2 CorrespondenceModule - Advanced Features** - - - [ ] Implement Status Transitions: - - [ ] `DRAFT` → `SUBMITTED` (Document Control) - - [ ] `SUBMITTED` → `CLOSED` (Admin) - - [ ] `SUBMITTED` → `CANCELLED` (Admin + Reason) - - [ ] Implement References: - - [ ] `POST /correspondences/:id/references` → Link Documents - - [ ] Implement Search (Basic): - - `GET /correspondences/search?q=...` - - [ ] Deliverable: Workflow พื้นฐานทำงานได้ - -- **[ ] T3.3 RfaModule - Basic CRUD** - - [ ] สร้าง Entities: - - [ ] Rfa - - [ ] RfaRevision - - [ ] RfaItem (Junction to Shop Drawings) - - [ ] สร้าง RfaService: Complex Business Rules - - [ ] `create(dto)` → Rfa - - [ ] สร้าง Correspondence + Rfa + RfaRevision - - [ ] เชื่อม Shop Drawing Revisions (สำหรับ RFA_DWG) - - [ ] `findAll(filters)` → Paginated List - - [ ] `findById(id)` → Rfa with Items - - [ ] สร้าง Controllers: - - [ ] `POST /rfas` → Create - - [ ] `GET /rfas` → List - - [ ] `GET /rfas/:id` → Detail - - [ ] Deliverable: สร้าง RFA และเชื่อม Shop Drawings ได้ - ---- - -## **Phase 4: Drawing Management (สัปดาห์ที่ 7)** - -Milestone: ระบบจัดการแบบ - -### Phase 4: Tasks - -- **[ ] T4.1 DrawingModule - Contract Drawings** - - - [ ] สร้าง Entities: - - [ ] ContractDrawing - - [ ] ContractDrawingVolume - - [ ] ContractDrawingCat - - [ ] ContractDrawingSubCat - - [ ] ContractDrawingSubcatCatMap - - [ ] ContractDrawingAttachment - - [ ] สร้าง ContractDrawingService CRUD - - [ ] สร้าง Controllers: - - [ ] `GET /drawings/contract` → List - - [ ] `POST /drawings/contract` → Create (Admin) - - [ ] `GET /drawings/contract/:id` → Detail - - [ ] Deliverable: จัดการ Contract Drawings ได้ - -- **[ ] T4.2 DrawingModule - Shop Drawings** - - [ ] สร้าง Entities: - - [ ] ShopDrawing - - [ ] ShopDrawingRevision - - [ ] ShopDrawingMainCategory - - [ ] ShopDrawingSubCategory - - [ ] ShopDrawingRevisionContractRef - - [ ] ShopDrawingRevisionAttachment - - [ ] สร้าง ShopDrawingService CRUD - - [ ] สร้าง Controllers: - - [ ] `GET /drawings/shop` → List - - [ ] `POST /drawings/shop` → Create - - [ ] `POST /drawings/shop/:id/revisions` → Create Revision - - [ ] `GET /drawings/shop/:id` → Detail with Revisions - - [ ] Link Shop Drawing Revision → Contract Drawings - - [ ] Deliverable: จัดการ Shop Drawings และ Revisions ได้ - ---- - -## **Phase 5: Workflow Systems (สัปดาห์ที่ 8-9)** - -Milestone: ระบบ Workflow ทั้งหมด - -### Phase 5: Tasks - -- **[ ] T5.1 RfaModule - Workflow Implementation** - - - [ ] สร้าง Entities: - - [ ] RfaWorkflowTemplate - - [ ] RfaWorkflowTemplateStep - - [ ] RfaWorkflow (Transaction Log) - - [ ] สร้าง RfaWorkflowService: Advanced Workflow Features - - [ ] `initiateWorkflow(rfaId, templateId)` → void - - [ ] สร้าง RfaWorkflow records ตาม Template - - [ ] กำหนด Step 1 เป็น PENDING - - [ ] `completeStep(rfaId, stepNumber, action, comments)` → void - - [ ] Update Status → COMPLETED - - [ ] Set Next Step → PENDING - - [ ] Send Notifications - - [ ] `rejectStep(rfaId, stepNumber, reason)` → void - - [ ] Update Status → REJECTED - - [ ] Send back to Originator - - [ ] สร้าง Controllers: - - [ ] `POST /rfas/:id/workflow/start` → Start Workflow - - [ ] `POST /rfas/:id/workflow/steps/:stepNumber/complete` → Complete Step - - [ ] `GET /rfas/:id/workflow` → Get Workflow Status - - [ ] Deliverable: RFA Workflow ทำงานได้ - -- **[ ] T5.2 CirculationModule - Internal Routing** - - - [ ] สร้าง Entities: - - [ ] Circulation - - [ ] CirculationTemplate - - [ ] CirculationTemplateAssignee - - [ ] CirculationRouting (Transaction Log) - - [ ] CirculationAttachment - - [ ] สร้าง CirculationService: - - [ ] `create(correspondenceId, dto)` → Circulation - - [ ] สร้าง Circulation (1:1 กับ Correspondence) - - [ ] สร้าง Routing ตาม Template - - [ ] `assignUser(circulationId, stepNumber, userId)` → void - - [ ] `completeStep(circulationId, stepNumber, comments)` → void - - [ ] `close(circulationId)` → void (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) - - สร้าง Controllers: - - [ ] `POST /circulations` → Create - - [ ] `GET /circulations/:id` → Detail - - [ ] `POST /circulations/:id/steps/:stepNumber/complete` → Complete - - [ ] `POST /circulations/:id/close` → Close - - [ ] Deliverable: ใบเวียนภายในองค์กรทำงานได้ - -- **[ ] T5.3 TransmittalModule - Document Forwarding** - - [ ] สร้าง Entities: - - [ ] Transmittal - - [ ] TransmittalItem - - [ ] สร้าง TransmittalService: - - [ ] `create(dto)` → Transmittal - - [ ] สร้าง Correspondence + Transmittal - - [ ] เชื่อม Multiple Correspondences เป็น Items - - [ ] สร้าง Controllers: - - [ ] `POST /transmittals` → Create - - [ ] `GET /transmittals` → List - - [ ] `GET /transmittals/:id` → Detail with Items - - [ ] Deliverable: สร้าง Transmittal ได้ - ---- - -## **Phase 6: Advanced Features (สัปดาห์ที่ 10-11)** - -Milestone: ฟีเจอร์ขั้นสูง - -### Phase 6: Tasks - -- **[ ] T6.1 SearchModule - Elasticsearch Integration** - - - [ ] Setup Elasticsearch Container ใน docker-compose.yml - - [ ] สร้าง SearchService: - - [ ] `indexDocument(entity)` → void - - [ ] `updateDocument(entity)` → void - - [ ] `deleteDocument(entity)` → void - - [ ] `search(query, filters)` → SearchResult[] - - [ ] Index ทุกครั้งที่ Create/Update: - - [ ] Correspondence - - [ ] RFA - - [ ] Shop Drawing - - [ ] Contract Drawing - - [ ] Circulation - - [ ] Transmittal - - [ ] สร้าง Controllers: - - [ ] `GET /search?q=...&type=...&from=...&to=...` → Results - - [ ] Deliverable: ค้นหาขั้นสูงทำงานได้ - -- **[ ] T6.2 NotificationModule - Email & Line** - - - [ ] สร้าง NotificationService: - - [ ] `sendEmail(to, subject, body)` → void (Nodemailer) - - [ ] `sendLine(userId, message)` → void (ผ่าน n8n Webhook) - - [ ] `createSystemNotification(userId, message, entityType, entityId)` → void - - [ ] Integrate กับ Workflow Events: - - [ ] เมื่อสร้าง Correspondence ใหม่ → แจ้ง Recipients - - [ ] เมื่อสร้าง Circulation → แจ้ง Assignees - - [ ] เมื่อ RFA Workflow ถึง Step → แจ้ง Responsible Org - - [ ] เมื่อใกล้ถึง Deadline → แจ้ง (Optional) - - [ ] สร้าง Controllers: - - [ ] `GET /notifications` → List User's Notifications - - [ ] `PUT /notifications/:id/read` → Mark as Read - - [ ] Deliverable: ระบบแจ้งเตือนทำงานได้ - -- **[ ] T6.3 Reporting & Analytics** - - - [ ] สร้าง ReportService: - - [ ] `getCorrespondenceSummary(projectId, from, to)` → Report - - [ ] `getRfaSummary(projectId, from, to)` → Report - - [ ] `getActivityLog(userId, from, to)` → Report - - [ ] ใช้ Views จาก Database: - - [ ] `v_current_correspondences` - - [ ] `v_current_rfas` - - [ ] `v_user_tasks` - - [ ] `v_audit_log_details` - - [ ] สร้าง Controllers: - - [ ] `GET /reports/correspondence` → Summary (CSV, PDF) - - [ ] `GET /reports/rfa` → Summary - - [ ] `GET /reports/activity` → User Activity - - [ ] Deliverable: สร้างรายงานได้ - -- **T6.4 Audit Log & Activity Feed** - - [ ] AuditLogInterceptor ทำงานอัตโนมัติแล้ว (Phase 1) - - [ ] สร้าง AuditLogService: - - [ ] `log(userId, action, entityType, entityId, details)` → void - - [ ] `getUserActivity(userId, limit)` → AuditLog[] - - [ ] สร้าง Controllers: - - [ ] `GET /audit-logs` → List (Admin only) - - [ ] `GET /audit-logs/user/:userId` → User's Activity - - [ ] Deliverable: ดู Audit Log ได้ - ---- - -## **Phase 7: Testing & Optimization (สัปดาห์ที่ 12-13)** - -Milestone: ทดสอบและปรับปรุงประสิทธิภาพ - -### Phase 7: Tasks - -- **[ ] T7.1 Unit Testing** - - - [ ] เขียน Unit Tests สำหรับ Services สำคัญ: - - [ ] AuthService (login, validateUser) - - [ ] RbacGuard (permission checks) - - [ ] DocumentNumberingService (number generation) - - [ ] CorrespondenceService (create, update) - - [ ] RfaWorkflowService (workflow logic) - - [ ] Target: 70% Code Coverage - - [ ] Deliverable: Unit Tests ผ่านทั้งหมด - -- **[ ] T7.2 Integration Testing** - - - [ ] เขียน Integration Tests: - - [ ] Authentication Flow (login → access protected route) - - [ ] Document Creation Flow (create correspondence → attach files) - - [ ] RFA Workflow Flow (start → step 1 → step 2 → complete) - - [ ] Circulation Flow (create → assign → complete → close) - - [ ] ทดสอบ SQL Views (v_user_all_permissions, v_user_tasks) - - [ ] ใช้ Test Database แยกต่างหาก - - [ ] Deliverable: Integration Tests ผ่าน - -- **[ ] T7.3 E2E Testing** - - - [ ] เขียน E2E Tests: - - [ ] User Registration & Login - - [ ] Create Correspondence (Full Flow) - - [ ] Create RFA with Shop Drawings - - [ ] Complete RFA Workflow - - [ ] Search Documents - - [ ] Deliverable: E2E Tests ผ่าน - -- **[ ] T7.4 Performance Optimization** - - - [ ] Implement Caching: - - [ ] Cache Master Data (Roles, Permissions) - - [ ] Cache User Permissions (ใช้ @nestjs/cache-manager) - - [ ] Database Optimization: - - [ ] Review Indexes - - [ ] Optimize Queries (N+1 Problem) - - I[ ] mplement Pagination ทุก List Endpoint - - [ ] Deliverable: Response Time < 200ms (90th percentile) - -- **[ ] T7.5 Security Hardening** - - [ ] Implement Rate Limiting (ใช้ rate-limiter-flexible) - - [ ] Setup Helmet (Security Headers) - - [ ] Review CORS Configuration - - [ ] Input Validation (ตรวจสอบ DTOs ทั้งหมด) - - [ ] Deliverable: Security Checklist ผ่าน - ---- - -## **Phase 8: Documentation & Deployment (สัปดาห์ที่ 14)** - -Milestone: เอกสารและ Deploy สู่ Production - -### Phase 8: Tasks - -- **[ ] T8.1 API Documentation** - - - [ ] ครบทุก Endpoint ใน Swagger: - - [ ] ใส่ Description, Example Request/Response - - [ ] ระบุ Required Permissions - - [ ] ใส่ Error Responses - - [ ] Export Swagger JSON → Frontend Team - - [ ] Deliverable: Swagger Docs สมบูรณ์ - -- **[ ] T8.2 Technical Documentation** - - - [ ] เขียนเอกสาร: - - [ ] Architecture Overview - - [ ] Module Structure - - [ ] Database Schema Diagram - - [ ] API Design Patterns - - [ ] Deployment Guide - - [ ] Deliverable: Technical Docs พร้อม - -- **[ ] T8.3 Deployment Preparation** - - - [ ] สร้าง Production docker-compose.yml - - [ ] Setup Environment Variables ใน QNAP - - [ ] Setup Nginx Proxy Manager (SSL Certificate) - - [ ] Setup Backup Scripts (Database + Files) - - [ ] Deliverable: Deployment Guide พร้อม - -- **[ ] T8.4 Production Deployment** - - - [ ] Deploy Backend ไปยัง backend.np-dms.work - - [ ] ทดสอบ API ผ่าน Postman - - [ ] Monitor Logs (Winston) - - [ ] Setup Health Check Endpoint (`GET /health`) - - [ ] Deliverable: Backend รันบน Production - -- **T8.5 Handover to Frontend Team** - - [ ] Demo API ให้ Frontend Team - - [ ] ส่งมอบ Swagger Documentation - - [ ] ส่งมอบ Postman Collection - - [ ] Workshop: วิธีใช้ Authentication & RBAC - - [ ] Deliverable: Frontend เริ่มพัฒนาได้ - ---- - -## 📊 สรุป Timeline - -| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | -| ------- | -------------- | ------------ | ---------------------------- | -| Phase 0 | 1 สัปดาห์ | 4 | Infrastructure Ready | -| Phase 1 | 2 สัปดาห์ | 5 | Auth & User Management | -| Phase 2 | 1 สัปดาห์ | 3 | Master Data & File Storage | -| Phase 3 | 2 สัปดาห์ | 3 | Correspondence & RFA Core | -| Phase 4 | 1 สัปดาห์ | 2 | Drawing Management | -| Phase 5 | 2 สัปดาห์ | 3 | Workflow Systems | -| Phase 6 | 2 สัปดาห์ | 4 | Advanced Features | -| Phase 7 | 2 สัปดาห์ | 5 | Testing & Optimization | -| Phase 8 | 1 สัปดาห์ | 5 | Documentation & Deploy | -| **รวม** | **14 สัปดาห์** | **34 Tasks** | **Production-Ready Backend** | - ---- - -## 🎯 Critical Success Factors - -1. **Database First**: ใช้ Schema v1.4.0 เป็นหลัก ไม่แก้ไข Schema โดยไม่จำเป็น -2. **Emphasizing Soft Delete**: Service ทั้งหมดที่ทำการ Query ข้อมูล (เช่น findAll, findById) ต้อง ใช้ Global Filter หรือ Default Scope ของ TypeORM เพื่อกรอง WHERE deleted_at IS NULL เสมอ -3. **API Contract**: ทุก Endpoint ต้องมี Swagger Documentation สมบูรณ์ -4. **Security**: RBAC ต้องทำงานถูกต้อง 100% ก่อน Deploy -5. **Testing**: Code Coverage อย่างน้อย 70% ก่อน Production -6. **Performance**: Response Time < 200ms (90th percentile) -7. **Documentation**: เอกสารต้องครบถ้วนเพื่อ Handover ให้ Frontend Team - ---- - -## 🚀 ขั้นตอนถัดไป - -1. **Approve แผนนี้** → ปรับแต่งตาม Feedback -2. **Setup Phase 0** → เริ่มสร้าง Infrastructure -3. **Daily Standup** → รายงานความก้าวหน้าทุกวัน -4. **Weekly Review** → ทบทวนความก้าวหน้าทุกสัปดาห์ -5. **Deploy to Production** → Week 14 - ---- - -**หมายเหตุ:** แผนนี้สามารถปรับแต่งได้ตามความต้องการและข้อจำกัดของทีม หาก Phase ใดใช้เวลามากกว่าที่คาดการณ์ ควรปรับ Timeline ให้เหมาะสม +# 📋 แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.0 + +## 🎯 ภาพรวมโครงการ + +พัฒนา Backend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่รองรับการจัดการเอกสารที่ซับซ้อน มีระบบ Workflow การอนุมัติ และการควบคุมสิทธิ์แบบ RBAC 3 ระดับ + +--- + +## 📐 สถาปัตยกรรมระบบ + +### Technology Stack + +- **Framework:** NestJS (TypeScript, ESM) +- **Database:** MariaDB 10.11 +- **ORM:** TypeORM +- **Authentication:** JWT + Passport +- **Authorization:** CASL (RBAC) +- **File Upload:** Multer +- **Search:** Elasticsearch +- **Notification:** Nodemailer + n8n (Line Integration) +- **Scheduling:** @nestjs/schedule (Cron Jobs) +- **Documentation:** Swagger + +### โครงสร้างโมดูล (Domain-Driven) + +```tree +src/ +├── common/ # Shared Module +│ ├── auth/ # JWT, Guards +│ ├── config/ # Configuration +│ ├── decorators/ # @RequirePermission +│ ├── entities/ # Base Entities +│ ├── exceptions/ # Global Filters +│ ├── file-storage/ # FileStorageService +│ ├── guards/ # RBAC Guard +│ ├── interceptors/ # Audit, Transform +│ └── services/ # Notification, etc. +├── modules/ +│ ├── user/ # Users, Roles, Permissions +│ ├── project/ # Projects, Contracts, Organizations +│ ├── master/ # Master Data +│ ├── correspondence/ # Correspondence Management +│ ├── rfa/ # RFA & Workflows +│ ├── drawing/ # Shop/Contract Drawings +│ ├── circulation/ # Internal Circulation +│ ├── transmittal/ # Transmittals +│ ├── search/ # Elasticsearch +│ └── document-numbering/ # Internal Service +└── database/ # Migrations & Seeds +``` + +--- + +## 🗓️ แผนการพัฒนาแบบ Phase-Based + +## **Phase 0: Infrastructure Setup (สัปดาห์ที่ 1)** + +Milestone: สร้างโครงสร้างพื้นฐานและเชื่อมต่อ Services + +### Phase 0: Tasks + +- **[✅]T0.1 Setup QNAP Container Station** + + - สร้าง Docker Network: `lcbp3` + - Setup docker-compose.yml สำหรับ: + - MariaDB (db.np-dms.work) + - PHPMyAdmin (pma.np-dms.work) + - Backend (backend.np-dms.work) + - Nginx Proxy Manager (npm.np-dms.work) + - กำหนด Environment Variables ใน docker-compose.yml (ไม่ใช้ .env) + - Deliverable: Services ทั้งหมดรันได้และเชื่อมต่อกันผ่าน Network + +- **[✅]T0.2 Initialize NestJS Project** + + - สร้างโปรเจกต์ใหม่ด้วย Nest CLI + - ติดตั้ง Dependencies: + + ```bash + npm install @nestjs/typeorm typeorm mysql2 + npm install @nestjs/config + npm install class-validator class-transformer + npm install @nestjs/jwt @nestjs/passport passport passport-jwt + npm install casl + npm install @nestjs/platform-express multer + npm install @nestjs/swagger + npm install helmet rate-limiter-flexible + npm install bcrypt + npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @types/multer supertest + npm install @nestjs/cache-manager cache-manager + npm install @nestjs/schedule + npm install @nestjs/config + npm install @nestjs/elasticsearch @elastic/elasticsearch + npm install nodemailer @types/nodemailer + npm install uuid @types/uuid + ``` + +```` + + - Setup โครงสร้างโฟลเดอร์ตาม Domain-Driven Architecture + - Deliverable: Project Structure พร้อม, แสดง Swagger ที่ `/api` + +- **[✅]T0.3 Setup Database Connection** + + - Import SQL Schema v1.4.0 เข้า MariaDB + - Run Seed Data (organizations, users, roles, permissions) + - Configure TypeORM ใน AppModule + - ทดสอบ Connection + - Deliverable: Database พร้อมใช้งาน, มี Seed Data + +- **[✅]T0.4 Setup Git Repository** + - สร้าง Repository ใน Gitea (git.np-dms.work) + - Setup .gitignore, README.md + - Commit Initial Project + - Deliverable: Code อยู่ใน Version Control + +--- + +## **Phase 1: Core Foundation (สัปดาห์ที่ 2-3)** + +Milestone: ระบบ Authentication, Authorization และ Base Entities + +### Phase 1: Tasks + +- **[ ] T1.1 CommonModule - Base Infrastructure** + - [ ] สร้าง Base Entity (id, created_at, updated_at, deleted_at) + - [ ] สร้าง Global Exception Filter + - [ ] สร้าง Response Transform Interceptor + - [ ] สร้าง Audit Log Interceptor + - [ ] สร้าง RequestContextService - สำหรับเก็บข้อมูลระหว่าง Request + - [ ] สร้าง ConfigService - Centralized configuration management + - [ ] สร้าง CryptoService - สำหรับ encryption/decryption + - [ ] Deliverable: Common Services พร้อมใช้ + +- **[ ] T1.2 AuthModule - JWT Authentication** + - [ ] สร้าง Entity: User + - [ ] สร้าง AuthService: + - [ ] `login(username, password)` → JWT Token + - [ ] `validateUser(username, password)` → User | null + - [ ] Password Hashing (bcrypt) + - [ ] สร้าง JWT Strategy (Passport) + - [ ] สร้าง JwtAuthGuard + - [ ] สร้าง Controllers: + - [ ] `POST /auth/login` → { access_token } + - [ ] `POST /auth/register` (Admin only) + - [ ] `GET /auth/profile` (Protected) + - [ ] Deliverable: ล็อกอิน/ล็อกเอาต์ทำงานได้ + +- **[ ] T1.3 UserModule - User Management** + - [ ] สร้าง Entities: User, Role, Permission, UserRole + - [ ] สร้าง UserService CRUD + - [ ] สร้าง RoleService CRUD + - [ ] สร้าง PermissionService (Read-Only, จาก Seed) + - [ ] สร้าง Controllers: + - [ ] `GET /users` → List Users (Paginated) + - [ ] `GET /users/:id` → User Detail + - [ ] `POST /users` → Create User + - [ ] `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 + - [ ] Deliverable: จัดการผู้ใช้และ Role ได้ + +- **[ ] T1.4 RBAC Guard - Authorization** + - [ ] สร้าง `@RequirePermission()` Decorator + - [ ] สร้าง RbacGuard ที่ตรวจสอบ: + - [ ] Global Permissions + - [ ] Organization Permissions + - [ ] Project Permissions + - [ ] Contract Permissions + - [ ] Permission Hierarchy Logic + +```bash + // Current: 3-level hierarchy + // Recommended: 4-level hierarchy (Global → Organization → Project → Contract) + @Injectable() + export class RbacGuard implements CanActivate { + async canActivate(context: ExecutionContext): Promise { + const requiredPermission = this.getRequiredPermission(context); + const user = this.getUser(context); + + // Check permissions in order: Global → Org → Project → Contract + return await this.checkGlobalPermissions(user, requiredPermission) || + await this.checkOrgPermissions(user, requiredPermission) || + await this.checkProjectPermissions(user, requiredPermission) || + await this.checkContractPermissions(user, requiredPermission); + } + } + +```` + +- [ ] Integration กับ CASL +- [ ] Deliverable: ระบบสิทธิ์ทำงานได้ทั้ง 4 ระดับ + +- **T1.5 ProjectModule - Base Structures** + - [ ] สร้าง Entities: + - [ ] Organization + - [ ] Project + - [ ] Contract + - [ ] ProjectOrganization (Junction) + - [ ] ContractOrganization (Junction) + - [ ] UserAssignment Entity - สำหรับจัดการ user assignments ตาม scope + - [ ] สร้าง Services & Controllers: + - [ ] `GET /organizations` → List + - [ ] `POST /projects` → Create (Superadmin) + - [ ] `GET /projects/:id/contracts` → List Contracts + - [ ] `POST /projects/:id/contracts` → Create Contract + - [ ] UserAssignment - สำหรับจัดการ user assignments ตาม scope + - [ ] ProjectOrganization - สำหรับจัดการความสัมพันธ์ project-organization + - [ ] ContractOrganization - สำหรับจัดการความสัมพันธ์ contract-organization + - [ ] Deliverable: จัดการโครงสร้างโปรเจกต์ได้ + +--- + +## **Phase 2: Master Data & File Management (สัปดาห์ที่ 4)** + +Milestone: Master Data และระบบจัดการไฟล์ + +### Phase 2: Tasks + +- **[ ] T2.1 MasterModule - Master Data Management** + + - [ ] สร้าง Entities: + - [ ] CorrespondenceType + - [ ] CorrespondenceStatus + - [ ] RfaType + - [ ] RfaStatusCode + - [ ] RfaApproveCode + - [ ] CirculationStatusCode + - [ ] Tag + - [ ] สร้าง Services & Controllers (CRUD): + - [ ] `GET /master/correspondence-types` + - [ ] `POST /master/tags` → Create Tag + - [ ] `GET /master/tags` → List Tags (Autocomplete) + - [ ] Deliverable: Admin จัดการ Master Data ได้ + +- **[ ] T2.2 FileStorageService - Central File Management** + + - [ ] สร้าง Attachment Entity + - [ ] สร้าง FileStorageService: (การจัดเก็บไฟล์ในรูปแบบ centralized storage, ครอบคลุมการจัดการไฟล์แนบทั้งหมด, Security Measures) + - [ ] `uploadFile(file: Express.Multer.File)` → Attachment + - [ ] `getFilePath(attachmentId)` → string + - [ ] `deleteFile(attachmentId)` → boolean + - [ ] จัดเก็บไฟล์ใน `/share/dms-data/uploads/{YYYY}/{MM}/` + - [ ] สร้าง Controller: + - [ ] `POST /files/upload` → { attachment_id, url } + - [ ] `GET /files/:id/download` → File Stream (Protected) + - [ ] Access Control: ตรวจสอบสิทธิ์ผ่าน Junction Table + - [ ] Deliverable: อัปโหลด/ดาวน์โหลดไฟล์ได้อย่างปลอดภัย + +- **[ ] T2.3 DocumentNumberingModule - Internal Service** + - [ ] สร้าง Entities: + - [ ] DocumentNumberFormat + - [ ] DocumentNumberCounter + - [ ] สร้าง DocumentNumberingService: รวม Stored Procedure (sp_get_next_document_number), Error Handling และ Retry Logic + - [ ] `generateNextNumber(projectId, orgId, typeId, year)` → string + - [ ] เรียก Stored Procedure: `sp_get_next_document_number` + - [ ] Format ตาม Template: `{ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}` + - **ไม่มี Controller** (Internal Service เท่านั้น) + - [ ] Deliverable: Service สร้างเลขที่เอกสารได้ถูกต้อง + +--- + +## **Phase 3: Correspondence & RFA Core (สัปดาห์ที่ 5-6)** + +Milestone: ระบบเอกสารโต้ตอบและ RFA + +### Phase 3: Tasks + +- **[ ] T3.1 CorrespondenceModule - Basic CRUD** + + - [ ] สร้าง Entities: + - [ ] Correspondence + - [ ] CorrespondenceRevision + - [ ] CorrespondenceRecipient + - [ ] CorrespondenceTag + - [ ] CorrespondenceReference + - [ ] CorrespondenceAttachment + - [ ] สร้าง CorrespondenceService: Complex Business Rules, State Machine สำหรับ Status Transitions + - [ ] `create(dto)` → Correspondence + - [ ] สร้าง Correspondence + Revision แรก (rev 0) + - [ ] เรียก DocumentNumberingService + - [ ] สร้าง Recipients (TO/CC) + - [ ] สร้าง Tags + - [ ] สร้าง Attachments + - [ ] `update(id, dto)` → Correspondence + - [ ] สร้าง Revision ใหม่ + - [ ] Update `is_current` flag + - [ ] `findAll(filters)` → Paginated List + - [ ] `findById(id)` → Correspondence with Current Revision + - [ ] สร้าง Controllers: + - [ ] `POST /correspondences` → Create + - [ ] `GET /correspondences` → List (Filter by type, status, org) + - [ ] `GET /correspondences/:id` → Detail + - [ ] `PUT /correspondences/:id` → Update (Create new revision) + - [ ] `DELETE /correspondences/:id` → Soft Delete (Admin only) + - [ ] Deliverable: สร้าง/แก้ไข/ดูเอกสารได้ + +- **[ ] T3.2 CorrespondenceModule - Advanced Features** + + - [ ] Implement Status Transitions: + - [ ] `DRAFT` → `SUBMITTED` (Document Control) + - [ ] `SUBMITTED` → `CLOSED` (Admin) + - [ ] `SUBMITTED` → `CANCELLED` (Admin + Reason) + - [ ] Implement References: + - [ ] `POST /correspondences/:id/references` → Link Documents + - [ ] Implement Search (Basic): + - `GET /correspondences/search?q=...` + - [ ] Deliverable: Workflow พื้นฐานทำงานได้ + +- **[ ] T3.3 RfaModule - Basic CRUD** + - [ ] สร้าง Entities: + - [ ] Rfa + - [ ] RfaRevision + - [ ] RfaItem (Junction to Shop Drawings) + - [ ] สร้าง RfaService: Complex Business Rules + - [ ] `create(dto)` → Rfa + - [ ] สร้าง Correspondence + Rfa + RfaRevision + - [ ] เชื่อม Shop Drawing Revisions (สำหรับ RFA_DWG) + - [ ] `findAll(filters)` → Paginated List + - [ ] `findById(id)` → Rfa with Items + - [ ] สร้าง Controllers: + - [ ] `POST /rfas` → Create + - [ ] `GET /rfas` → List + - [ ] `GET /rfas/:id` → Detail + - [ ] Deliverable: สร้าง RFA และเชื่อม Shop Drawings ได้ + +--- + +## **Phase 4: Drawing Management (สัปดาห์ที่ 7)** + +Milestone: ระบบจัดการแบบ + +### Phase 4: Tasks + +- **[ ] T4.1 DrawingModule - Contract Drawings** + + - [ ] สร้าง Entities: + - [ ] ContractDrawing + - [ ] ContractDrawingVolume + - [ ] ContractDrawingCat + - [ ] ContractDrawingSubCat + - [ ] ContractDrawingSubcatCatMap + - [ ] ContractDrawingAttachment + - [ ] สร้าง ContractDrawingService CRUD + - [ ] สร้าง Controllers: + - [ ] `GET /drawings/contract` → List + - [ ] `POST /drawings/contract` → Create (Admin) + - [ ] `GET /drawings/contract/:id` → Detail + - [ ] Deliverable: จัดการ Contract Drawings ได้ + +- **[ ] T4.2 DrawingModule - Shop Drawings** + - [ ] สร้าง Entities: + - [ ] ShopDrawing + - [ ] ShopDrawingRevision + - [ ] ShopDrawingMainCategory + - [ ] ShopDrawingSubCategory + - [ ] ShopDrawingRevisionContractRef + - [ ] ShopDrawingRevisionAttachment + - [ ] สร้าง ShopDrawingService CRUD + - [ ] สร้าง Controllers: + - [ ] `GET /drawings/shop` → List + - [ ] `POST /drawings/shop` → Create + - [ ] `POST /drawings/shop/:id/revisions` → Create Revision + - [ ] `GET /drawings/shop/:id` → Detail with Revisions + - [ ] Link Shop Drawing Revision → Contract Drawings + - [ ] Deliverable: จัดการ Shop Drawings และ Revisions ได้ + +--- + +## **Phase 5: Workflow Systems (สัปดาห์ที่ 8-9)** + +Milestone: ระบบ Workflow ทั้งหมด + +### Phase 5: Tasks + +- **[ ] T5.1 RfaModule - Workflow Implementation** + + - [ ] สร้าง Entities: + - [ ] RfaWorkflowTemplate + - [ ] RfaWorkflowTemplateStep + - [ ] RfaWorkflow (Transaction Log) + - [ ] สร้าง RfaWorkflowService: Advanced Workflow Features + - [ ] `initiateWorkflow(rfaId, templateId)` → void + - [ ] สร้าง RfaWorkflow records ตาม Template + - [ ] กำหนด Step 1 เป็น PENDING + - [ ] `completeStep(rfaId, stepNumber, action, comments)` → void + - [ ] Update Status → COMPLETED + - [ ] Set Next Step → PENDING + - [ ] Send Notifications + - [ ] `rejectStep(rfaId, stepNumber, reason)` → void + - [ ] Update Status → REJECTED + - [ ] Send back to Originator + - [ ] สร้าง Controllers: + - [ ] `POST /rfas/:id/workflow/start` → Start Workflow + - [ ] `POST /rfas/:id/workflow/steps/:stepNumber/complete` → Complete Step + - [ ] `GET /rfas/:id/workflow` → Get Workflow Status + - [ ] Deliverable: RFA Workflow ทำงานได้ + +- **[ ] T5.2 CirculationModule - Internal Routing** + + - [ ] สร้าง Entities: + - [ ] Circulation + - [ ] CirculationTemplate + - [ ] CirculationTemplateAssignee + - [ ] CirculationRouting (Transaction Log) + - [ ] CirculationAttachment + - [ ] สร้าง CirculationService: + - [ ] `create(correspondenceId, dto)` → Circulation + - [ ] สร้าง Circulation (1:1 กับ Correspondence) + - [ ] สร้าง Routing ตาม Template + - [ ] `assignUser(circulationId, stepNumber, userId)` → void + - [ ] `completeStep(circulationId, stepNumber, comments)` → void + - [ ] `close(circulationId)` → void (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) + - สร้าง Controllers: + - [ ] `POST /circulations` → Create + - [ ] `GET /circulations/:id` → Detail + - [ ] `POST /circulations/:id/steps/:stepNumber/complete` → Complete + - [ ] `POST /circulations/:id/close` → Close + - [ ] Deliverable: ใบเวียนภายในองค์กรทำงานได้ + +- **[ ] T5.3 TransmittalModule - Document Forwarding** + - [ ] สร้าง Entities: + - [ ] Transmittal + - [ ] TransmittalItem + - [ ] สร้าง TransmittalService: + - [ ] `create(dto)` → Transmittal + - [ ] สร้าง Correspondence + Transmittal + - [ ] เชื่อม Multiple Correspondences เป็น Items + - [ ] สร้าง Controllers: + - [ ] `POST /transmittals` → Create + - [ ] `GET /transmittals` → List + - [ ] `GET /transmittals/:id` → Detail with Items + - [ ] Deliverable: สร้าง Transmittal ได้ + +--- + +## **Phase 6: Advanced Features (สัปดาห์ที่ 10-11)** + +Milestone: ฟีเจอร์ขั้นสูง + +### Phase 6: Tasks + +- **[ ] T6.1 SearchModule - Elasticsearch Integration** + + - [ ] Setup Elasticsearch Container ใน docker-compose.yml + - [ ] สร้าง SearchService: + - [ ] `indexDocument(entity)` → void + - [ ] `updateDocument(entity)` → void + - [ ] `deleteDocument(entity)` → void + - [ ] `search(query, filters)` → SearchResult[] + - [ ] Index ทุกครั้งที่ Create/Update: + - [ ] Correspondence + - [ ] RFA + - [ ] Shop Drawing + - [ ] Contract Drawing + - [ ] Circulation + - [ ] Transmittal + - [ ] สร้าง Controllers: + - [ ] `GET /search?q=...&type=...&from=...&to=...` → Results + - [ ] Deliverable: ค้นหาขั้นสูงทำงานได้ + +- **[ ] T6.2 NotificationModule - Email & Line** + + - [ ] สร้าง NotificationService: + - [ ] `sendEmail(to, subject, body)` → void (Nodemailer) + - [ ] `sendLine(userId, message)` → void (ผ่าน n8n Webhook) + - [ ] `createSystemNotification(userId, message, entityType, entityId)` → void + - [ ] Integrate กับ Workflow Events: + - [ ] เมื่อสร้าง Correspondence ใหม่ → แจ้ง Recipients + - [ ] เมื่อสร้าง Circulation → แจ้ง Assignees + - [ ] เมื่อ RFA Workflow ถึง Step → แจ้ง Responsible Org + - [ ] เมื่อใกล้ถึง Deadline → แจ้ง (Optional) + - [ ] สร้าง Controllers: + - [ ] `GET /notifications` → List User's Notifications + - [ ] `PUT /notifications/:id/read` → Mark as Read + - [ ] Deliverable: ระบบแจ้งเตือนทำงานได้ + +- **[ ] T6.3 Reporting & Analytics** + + - [ ] สร้าง ReportService: + - [ ] `getCorrespondenceSummary(projectId, from, to)` → Report + - [ ] `getRfaSummary(projectId, from, to)` → Report + - [ ] `getActivityLog(userId, from, to)` → Report + - [ ] ใช้ Views จาก Database: + - [ ] `v_current_correspondences` + - [ ] `v_current_rfas` + - [ ] `v_user_tasks` + - [ ] `v_audit_log_details` + - [ ] สร้าง Controllers: + - [ ] `GET /reports/correspondence` → Summary (CSV, PDF) + - [ ] `GET /reports/rfa` → Summary + - [ ] `GET /reports/activity` → User Activity + - [ ] Deliverable: สร้างรายงานได้ + +- **T6.4 Audit Log & Activity Feed** + - [ ] AuditLogInterceptor ทำงานอัตโนมัติแล้ว (Phase 1) + - [ ] สร้าง AuditLogService: + - [ ] `log(userId, action, entityType, entityId, details)` → void + - [ ] `getUserActivity(userId, limit)` → AuditLog[] + - [ ] สร้าง Controllers: + - [ ] `GET /audit-logs` → List (Admin only) + - [ ] `GET /audit-logs/user/:userId` → User's Activity + - [ ] Deliverable: ดู Audit Log ได้ + +--- + +## **Phase 7: Testing & Optimization (สัปดาห์ที่ 12-13)** + +Milestone: ทดสอบและปรับปรุงประสิทธิภาพ + +### Phase 7: Tasks + +- **[ ] T7.1 Unit Testing** + + - [ ] เขียน Unit Tests สำหรับ Services สำคัญ: + - [ ] AuthService (login, validateUser) + - [ ] RbacGuard (permission checks) + - [ ] DocumentNumberingService (number generation) + - [ ] CorrespondenceService (create, update) + - [ ] RfaWorkflowService (workflow logic) + - [ ] Target: 70% Code Coverage + - [ ] Deliverable: Unit Tests ผ่านทั้งหมด + +- **[ ] T7.2 Integration Testing** + + - [ ] เขียน Integration Tests: + - [ ] Authentication Flow (login → access protected route) + - [ ] Document Creation Flow (create correspondence → attach files) + - [ ] RFA Workflow Flow (start → step 1 → step 2 → complete) + - [ ] Circulation Flow (create → assign → complete → close) + - [ ] ทดสอบ SQL Views (v_user_all_permissions, v_user_tasks) + - [ ] ใช้ Test Database แยกต่างหาก + - [ ] Deliverable: Integration Tests ผ่าน + +- **[ ] T7.3 E2E Testing** + + - [ ] เขียน E2E Tests: + - [ ] User Registration & Login + - [ ] Create Correspondence (Full Flow) + - [ ] Create RFA with Shop Drawings + - [ ] Complete RFA Workflow + - [ ] Search Documents + - [ ] Deliverable: E2E Tests ผ่าน + +- **[ ] T7.4 Performance Optimization** + + - [ ] Implement Caching: + - [ ] Cache Master Data (Roles, Permissions) + - [ ] Cache User Permissions (ใช้ @nestjs/cache-manager) + - [ ] Database Optimization: + - [ ] Review Indexes + - [ ] Optimize Queries (N+1 Problem) + - I[ ] mplement Pagination ทุก List Endpoint + - [ ] Deliverable: Response Time < 200ms (90th percentile) + +- **[ ] T7.5 Security Hardening** + - [ ] Implement Rate Limiting (ใช้ rate-limiter-flexible) + - [ ] Setup Helmet (Security Headers) + - [ ] Review CORS Configuration + - [ ] Input Validation (ตรวจสอบ DTOs ทั้งหมด) + - [ ] Deliverable: Security Checklist ผ่าน + +--- + +## **Phase 8: Documentation & Deployment (สัปดาห์ที่ 14)** + +Milestone: เอกสารและ Deploy สู่ Production + +### Phase 8: Tasks + +- **[ ] T8.1 API Documentation** + + - [ ] ครบทุก Endpoint ใน Swagger: + - [ ] ใส่ Description, Example Request/Response + - [ ] ระบุ Required Permissions + - [ ] ใส่ Error Responses + - [ ] Export Swagger JSON → Frontend Team + - [ ] Deliverable: Swagger Docs สมบูรณ์ + +- **[ ] T8.2 Technical Documentation** + + - [ ] เขียนเอกสาร: + - [ ] Architecture Overview + - [ ] Module Structure + - [ ] Database Schema Diagram + - [ ] API Design Patterns + - [ ] Deployment Guide + - [ ] Deliverable: Technical Docs พร้อม + +- **[ ] T8.3 Deployment Preparation** + + - [ ] สร้าง Production docker-compose.yml + - [ ] Setup Environment Variables ใน QNAP + - [ ] Setup Nginx Proxy Manager (SSL Certificate) + - [ ] Setup Backup Scripts (Database + Files) + - [ ] Deliverable: Deployment Guide พร้อม + +- **[ ] T8.4 Production Deployment** + + - [ ] Deploy Backend ไปยัง backend.np-dms.work + - [ ] ทดสอบ API ผ่าน Postman + - [ ] Monitor Logs (Winston) + - [ ] Setup Health Check Endpoint (`GET /health`) + - [ ] Deliverable: Backend รันบน Production + +- **T8.5 Handover to Frontend Team** + - [ ] Demo API ให้ Frontend Team + - [ ] ส่งมอบ Swagger Documentation + - [ ] ส่งมอบ Postman Collection + - [ ] Workshop: วิธีใช้ Authentication & RBAC + - [ ] Deliverable: Frontend เริ่มพัฒนาได้ + +--- + +## 📊 สรุป Timeline + +| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | +| ------- | -------------- | ------------ | ---------------------------- | +| Phase 0 | 1 สัปดาห์ | 4 | Infrastructure Ready | +| Phase 1 | 2 สัปดาห์ | 5 | Auth & User Management | +| Phase 2 | 1 สัปดาห์ | 3 | Master Data & File Storage | +| Phase 3 | 2 สัปดาห์ | 3 | Correspondence & RFA Core | +| Phase 4 | 1 สัปดาห์ | 2 | Drawing Management | +| Phase 5 | 2 สัปดาห์ | 3 | Workflow Systems | +| Phase 6 | 2 สัปดาห์ | 4 | Advanced Features | +| Phase 7 | 2 สัปดาห์ | 5 | Testing & Optimization | +| Phase 8 | 1 สัปดาห์ | 5 | Documentation & Deploy | +| **รวม** | **14 สัปดาห์** | **34 Tasks** | **Production-Ready Backend** | + +--- + +## 🎯 Critical Success Factors + +1. **Database First**: ใช้ Schema v1.4.0 เป็นหลัก ไม่แก้ไข Schema โดยไม่จำเป็น +2. **Emphasizing Soft Delete**: Service ทั้งหมดที่ทำการ Query ข้อมูล (เช่น findAll, findById) ต้อง ใช้ Global Filter หรือ Default Scope ของ TypeORM เพื่อกรอง WHERE deleted_at IS NULL เสมอ +3. **API Contract**: ทุก Endpoint ต้องมี Swagger Documentation สมบูรณ์ +4. **Security**: RBAC ต้องทำงานถูกต้อง 100% ก่อน Deploy +5. **Testing**: Code Coverage อย่างน้อย 70% ก่อน Production +6. **Performance**: Response Time < 200ms (90th percentile) +7. **Documentation**: เอกสารต้องครบถ้วนเพื่อ Handover ให้ Frontend Team + +--- + +## 🚀 ขั้นตอนถัดไป + +1. **Approve แผนนี้** → ปรับแต่งตาม Feedback +2. **Setup Phase 0** → เริ่มสร้าง Infrastructure +3. **Daily Standup** → รายงานความก้าวหน้าทุกวัน +4. **Weekly Review** → ทบทวนความก้าวหน้าทุกสัปดาห์ +5. **Deploy to Production** → Week 14 + +--- + +**หมายเหตุ:** แผนนี้สามารถปรับแต่งได้ตามความต้องการและข้อจำกัดของทีม หาก Phase ใดใช้เวลามากกว่าที่คาดการณ์ ควรปรับ Timeline ให้เหมาะสม diff --git a/docs/LCBP3-DMS_V1_4_0_Data_Dictionary.md b/docs/LCBP3-DMS_V1_4_0_Data_Dictionary.md index 2395191..1319ff0 100644 --- a/docs/LCBP3-DMS_V1_4_0_Data_Dictionary.md +++ b/docs/LCBP3-DMS_V1_4_0_Data_Dictionary.md @@ -1,2835 +1,2835 @@ -# **ตารางฐานข้อมูล (Data Dictionary) - LCBP3-DMS (V1.4.0)** - -เอกสารนี้สรุปโครงสร้างตาราง, 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_a | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ | -| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด | - -**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_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 หมายความว่า จะลบประเภทหนังสือหรือสถานะไม่ได้ ถ้ายังมีการใช้งานอยู่ในตารางนี้ - ---- - -### 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: เพื่อทราบ] | - -**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 เพื่อรักษาประวัติการดำเนินการไว้ - ---- - -## **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) -- FULLTEXT INDEX (content) - Enables full-text searching - -**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/code/name -- originator_id/code/name -- revision_id, revision_number, revision_label -- title, document_date, issued_date, received_date, due_date -- correspondence_status_id/code/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/code/name -- correspondence_id, correspondence_number -- project_id/code/name -- originator_id/name -- revision_id, revision_number, revision_label -- title, document_date, issued_date, received_date, approved_date -- rfa_status_code_id/code/name -- rfa_approve_code_id/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/code/name -- project_id/code/name -- organization_id/code/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/code/name -- user_id, username, first_name, last_name -- organization_id/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/code/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/code/name -- correspondence_type_id/code/name -- status_id/code/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) - -6. **Search**: - - `FULLTEXT idx_search_indices_content` on (content) - ---- - -## 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 +# **ตารางฐานข้อมูล (Data Dictionary) - LCBP3-DMS (V1.4.0)** + +เอกสารนี้สรุปโครงสร้างตาราง, 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_a | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ | +| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด | + +**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_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 หมายความว่า จะลบประเภทหนังสือหรือสถานะไม่ได้ ถ้ายังมีการใช้งานอยู่ในตารางนี้ + +--- + +### 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: เพื่อทราบ] | + +**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 เพื่อรักษาประวัติการดำเนินการไว้ + +--- + +## **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) +- FULLTEXT INDEX (content) - Enables full-text searching + +**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/code/name +- originator_id/code/name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, due_date +- correspondence_status_id/code/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/code/name +- correspondence_id, correspondence_number +- project_id/code/name +- originator_id/name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, approved_date +- rfa_status_code_id/code/name +- rfa_approve_code_id/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/code/name +- project_id/code/name +- organization_id/code/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/code/name +- user_id, username, first_name, last_name +- organization_id/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/code/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/code/name +- correspondence_type_id/code/name +- status_id/code/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) + +6. **Search**: + - `FULLTEXT idx_search_indices_content` on (content) + +--- + +## 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 diff --git a/docs/LCBP3-DMS_V1_4_0_Frontend_Development_Plan.md b/docs/LCBP3-DMS_V1_4_0_Frontend_Development_Plan.md index 5de23e4..8aab89d 100644 --- a/docs/LCBP3-DMS_V1_4_0_Frontend_Development_Plan.md +++ b/docs/LCBP3-DMS_V1_4_0_Frontend_Development_Plan.md @@ -1,833 +1,833 @@ -# 📋 แผนการพัฒนา Frontend (NestJS) - LCBP3-DMS v1.4.0 - -# 📋 แผนการพัฒนา Frontend (Next.js) - LCBP3-DMS v1.4.0 - -## 🎯 ภาพรวมโครงการ - -พัฒนา Frontend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่ทันสมัย responsive และใช้งานง่าย รองรับการจัดการเอกสารที่ซับซ้อน มี Dashboard แบบ Real-time และระบบ Workflow Visualization - ---- - -## 📐 สถาปัตยกรรมระบบ - -### Technology Stack - -- **Framework:** Next.js 14+ (App Router, React 18+, TypeScript, ESM) -- **Styling:** Tailwind CSS + PostCSS -- **Component Library:** shadcn/ui (Radix UI) -- **State Management:** - - **Server State:** TanStack Query (React Query) - - **Global Client State:** Zustand - - **Form State:** React Hook Form + Zod -- **Data Fetching:** Axios + TanStack Query -- **Authentication:** NextAuth.js (JWT Strategy) -- **File Upload:** React Dropzone -- **Tables:** TanStack Table -- **Charts:** Recharts -- **Date Picker:** date-fns + shadcn/ui Calendar -- **Icons:** Lucide React -- **Testing:** - - **Unit/Integration:** Vitest + React Testing Library - - **E2E:** Playwright - - **API Mocking:** Mock Service Worker (MSW) - -### โครงสร้างโปรเจกต์ - -``` -app/ -├── (public)/ # Public routes (Landing, Login) -│ ├── page.tsx # Landing Page -│ └── login/ # Login Page -├── (protected)/ # Protected routes -│ ├── layout.tsx # App Shell (Navbar + Sidebar) -│ ├── dashboard/ # Dashboard -│ ├── correspondences/ # Correspondence Management -│ ├── rfas/ # RFA Management -│ ├── drawings/ # Drawing Management -│ ├── circulations/ # Circulation Management -│ ├── transmittals/ # Transmittal Management -│ ├── search/ # Advanced Search -│ ├── reports/ # Reports -│ ├── admin/ # Admin Panel -│ └── profile/ # User Profile -├── api/ # API Routes (if needed) -components/ -├── ui/ # shadcn/ui components -├── features/ # Feature-specific components -│ ├── auth/ -│ ├── correspondence/ -│ ├── rfa/ -│ ├── drawing/ -│ ├── circulation/ -│ └── common/ -└── layouts/ # Layout components -lib/ -├── api/ # API client & hooks -├── stores/ # Zustand stores -├── utils/ # Utility functions -├── hooks/ # Custom hooks -└── types/ # TypeScript types -public/ -├── images/ -└── fonts/ -``` - ---- - -## 🗓️ แผนการพัฒนาแบบ Phase-Based - -## **Phase 0: Setup & Infrastructure (สัปดาห์ที่ 1)** - -### Milestone: สร้างโครงสร้างพื้นฐานและ Development Environment - -#### Tasks: - -**T0.1 Initialize Next.js Project** - -- สร้างโปรเจกต์ด้วย `create-next-app`: - ```bash - npx create-next-app@latest lcbp3-frontend --typescript --tailwind --app --src-dir=false - ``` -- เลือก Options: - - ✅ TypeScript - - ✅ ESLint - - ✅ Tailwind CSS - - ✅ App Router - - ✅ Import alias (@/\*) -- Setup .gitignore, README.md -- Deliverable: ✅ โปรเจกต์เริ่มต้นพร้อม - -**T0.2 Install Core Dependencies** - -```bash -# State Management & Data Fetching -npm install @tanstack/react-query zustand -npm install axios -npm install react-hook-form @hookform/resolvers zod - -# UI Components & Styling -npm install clsx tailwind-merge -npm install lucide-react -npm install date-fns - -# File Upload -npm install react-dropzone - -# Authentication -npm install next-auth - -# Development Tools -npm install -D @types/node -``` - -- Deliverable: ✅ Dependencies ติดตั้งสมบูรณ์ - -**T0.3 Setup shadcn/ui** - -```bash -npx shadcn-ui@latest init -``` - -- เลือก Style: Default -- เลือก Base Color: Slate -- ติดตั้ง Components เบื้องต้น: - ```bash - npx shadcn-ui@latest add button input label card table dropdown-menu - npx shadcn-ui@latest add dialog sheet toast alert - npx shadcn-ui@latest add form select textarea checkbox - npx shadcn-ui@latest add calendar popover - ``` -- Deliverable: ✅ shadcn/ui พร้อมใช้งาน - -**T0.4 Configure Tailwind CSS** - -- แก้ไข `tailwind.config.ts`: - - เพิ่ม Custom Colors (ตาม Brand) - - เพิ่ม Custom Fonts (ภาษาไทย: Noto Sans Thai) - - Configure Container, Spacing -- สร้าง `app/globals.css` พร้อม Custom Styles -- Deliverable: ✅ Tailwind พร้อมใช้ - -**T0.5 Setup Development Environment** - -- สร้าง `.env.local`: - ``` - NEXT_PUBLIC_API_URL=http://backend.np-dms.work/api - NEXTAUTH_URL=http://localhost:3000 - NEXTAUTH_SECRET=... - ``` -- Setup ESLint Rules (ไทย Comments OK) -- Setup Prettier -- Setup VS Code Settings -- Deliverable: ✅ Dev Environment พร้อม - -**T0.6 Setup Git & Docker** - -- Push โปรเจกต์ไปยัง Gitea (git.np-dms.work) -- สร้าง Dockerfile สำหรับ Next.js: - ```dockerfile - FROM node:20-alpine - WORKDIR /app - COPY package*.json ./ - RUN npm ci - COPY . . - RUN npm run build - EXPOSE 3000 - CMD ["npm", "start"] - ``` -- สร้าง docker-compose.yml (เชื่อม Network `lcbp3`) -- Deliverable: ✅ Project อยู่ใน Git + Docker พร้อม - ---- - -## **Phase 1: Authentication & App Shell (สัปดาห์ที่ 2-3)** - -### Milestone: ระบบ Login และ Layout หลัก - -#### Tasks: - -**T1.1 Setup API Client** - -- สร้าง `lib/api/client.ts`: - - ```typescript - import axios from "axios"; - - export const apiClient = axios.create({ - baseURL: process.env.NEXT_PUBLIC_API_URL, - headers: { - "Content-Type": "application/json", - }, - }); - - // Request Interceptor (Add JWT Token) - apiClient.interceptors.request.use((config) => { - const token = localStorage.getItem("access_token"); - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } - return config; - }); - - // Response Interceptor (Handle Errors) - apiClient.interceptors.response.use( - (response) => response, - (error) => { - if (error.response?.status === 401) { - // Redirect to login - window.location.href = "/login"; - } - return Promise.reject(error); - } - ); - ``` - -- Deliverable: ✅ API Client พร้อมใช้ - -**T1.2 Setup TanStack Query** - -- สร้าง `app/providers.tsx`: - - ```typescript - "use client"; - import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; - import { useState } from "react"; - - export function Providers({ children }: { children: React.ReactNode }) { - const [queryClient] = useState( - () => - new QueryClient({ - defaultOptions: { - queries: { - staleTime: 60 * 1000, // 1 minute - refetchOnWindowFocus: false, - }, - }, - }) - ); - - return ( - {children} - ); - } - ``` - -- Wrap ใน `app/layout.tsx` -- Deliverable: ✅ React Query พร้อม - -**T1.3 Create Auth Store (Zustand)** - -- สร้าง `lib/stores/auth-store.ts`: - - ```typescript - import { create } from "zustand"; - import { persist } from "zustand/middleware"; - - interface User { - user_id: number; - username: string; - email: string; - first_name: string; - last_name: string; - primary_organization_id: number; - permissions: string[]; - } - - interface AuthStore { - user: User | null; - token: string | null; - setAuth: (user: User, token: string) => void; - clearAuth: () => void; - hasPermission: (permission: string) => boolean; - } - - export const useAuthStore = create()( - persist( - (set, get) => ({ - user: null, - token: null, - setAuth: (user, token) => { - set({ user, token }); - localStorage.setItem("access_token", token); - }, - clearAuth: () => { - set({ user: null, token: null }); - localStorage.removeItem("access_token"); - }, - hasPermission: (permission) => { - const user = get().user; - return user?.permissions.includes(permission) || false; - }, - }), - { - name: "auth-storage", - } - ) - ); - ``` - -- Deliverable: ✅ Auth Store พร้อม - -**T1.4 Create Login Page** - -- สร้าง `app/(public)/login/page.tsx`: - - ```typescript - "use client"; - import { useForm } from "react-hook-form"; - import { zodResolver } from "@hookform/resolvers/zod"; - import * as z from "zod"; - import { Button } from "@/components/ui/button"; - import { Input } from "@/components/ui/input"; - import { useRouter } from "next/navigation"; - import { useAuthStore } from "@/lib/stores/auth-store"; - import { apiClient } from "@/lib/api/client"; - - const loginSchema = z.object({ - username: z.string().min(1, "กรุณากรอกชื่อผู้ใช้"), - password: z.string().min(1, "กรุณากรอกรหัสผ่าน"), - }); - - export default function LoginPage() { - const router = useRouter(); - const setAuth = useAuthStore((state) => state.setAuth); - const form = useForm({ - resolver: zodResolver(loginSchema), - }); - - const handleLogin = async (data: z.infer) => { - try { - const response = await apiClient.post("/auth/login", data); - const { access_token, user } = response.data; - setAuth(user, access_token); - router.push("/dashboard"); - } catch (error) { - console.error("Login failed:", error); - // แสดง Toast Error - } - }; - - return ( -
-
- {/* Form Fields */} -
-
- ); - } - ``` - -- Deliverable: ✅ หน้า Login พร้อม - -**T1.5 Create Landing Page** - -- สร้าง `app/(public)/page.tsx`: - - Hero Section พร้อมข้อมูลโครงการ - - Feature Highlights - - CTA Button → Login -- ใช้ Tailwind + Animations -- Deliverable: ✅ Landing Page สวยงาม - -**T1.6 Create Protected Layout (App Shell)** - -- สร้าง `app/(protected)/layout.tsx`: - - ```typescript - "use client"; - import { useEffect } from "react"; - import { useRouter } from "next/navigation"; - import { useAuthStore } from "@/lib/stores/auth-store"; - import Navbar from "@/components/layouts/navbar"; - import Sidebar from "@/components/layouts/sidebar"; - - export default function ProtectedLayout({ - children, - }: { - children: React.ReactNode; - }) { - const router = useRouter(); - const user = useAuthStore((state) => state.user); - - useEffect(() => { - if (!user) { - router.push("/login"); - } - }, [user, router]); - - if (!user) return null; - - return ( -
- -
- -
{children}
-
-
- ); - } - ``` - -- Deliverable: ✅ App Shell พร้อม - -**T1.7 Create Navbar Component** - -- สร้าง `components/layouts/navbar.tsx`: - - แสดงชื่อระบบ - - แสดงชื่อผู้ใช้ + Avatar - - Dropdown Menu: - - Profile - - Settings - - Logout -- Responsive (Mobile Hamburger Menu) -- Deliverable: ✅ Navbar พร้อม - -**T1.8 Create Sidebar Component** - -- สร้าง `components/layouts/sidebar.tsx`: - - เมนูหลัก: - - Dashboard - - Correspondences - - RFAs - - Drawings (Shop & Contract) - - Circulations - - Transmittals - - Search - - Reports - - เมนู Admin (แสดงตามสิทธิ์): - - Users - - Roles & Permissions - - Master Data - - Document Numbering -- Collapsible Sidebar -- Active State Highlighting -- Deliverable: ✅ Sidebar พร้อม - ---- - -## **Phase 2: Dashboard & Common Components (สัปดาห์ที่ 4)** - -### Milestone: Dashboard และ Reusable Components - -#### Tasks: - -**T2.1 Create Reusable Components** - -- `components/features/common/data-table.tsx`: - - ใช้ TanStack Table - - รองรับ Pagination, Sorting, Filtering - - Responsive -- `components/features/common/file-upload.tsx`: - - ใช้ React Dropzone - - รองรับ Multi-file Upload - - Drag & Drop - - Progress Bar -- `components/features/common/status-badge.tsx`: - - แสดง Status แบบสีสัน (Draft, Submitted, Approved) -- `components/features/common/permission-guard.tsx`: - - ซ่อน/แสดง Component ตามสิทธิ์ -- Deliverable: ✅ Reusable Components พร้อม - -**T2.2 Create Dashboard Page** - -- สร้าง `app/(protected)/dashboard/page.tsx`: - - **KPI Cards Section**: - - จำนวนเอกสารทั้งหมด - - งานที่รอดำเนินการ - - เอกสารที่เกินกำหนด - - RFA ที่รออนุมัติ - - **My Tasks Table**: - - ดึงข้อมูลจาก `/api/circulations/my-tasks` (ใช้ `v_user_tasks`) - - แสดงรายการงานที่ต้องทำ (Pending, In Progress) - - Columns: Document Number, Title, Type, Status, Deadline, Actions - - คลิกแถวแล้วไปยังหน้า Detail - - **Recent Activity Feed**: - - ดึงข้อมูลจาก `/api/audit-logs/user/:userId` - - แสดง 10 รายการล่าสุด -- Deliverable: ✅ Dashboard ครบถ้วน - -**T2.3 Create API Hooks** - -- สร้าง `lib/api/hooks/use-my-tasks.ts`: - - ```typescript - import { useQuery } from "@tanstack/react-query"; - import { apiClient } from "../client"; - - export function useMyTasks() { - return useQuery({ - queryKey: ["my-tasks"], - queryFn: async () => { - const response = await apiClient.get("/circulations/my-tasks"); - return response.data; - }, - }); - } - ``` - -- สร้าง Hooks เพิ่มเติม: - - `use-dashboard-stats.ts` - - `use-recent-activity.ts` -- Deliverable: ✅ API Hooks พร้อม - ---- - -## **Phase 3: Correspondence Management (สัปดาห์ที่ 5-6)** - -### Milestone: ระบบจัดการเอกสารโต้ตอบ - -#### Tasks: - -**T3.1 Create Correspondence List Page** - -- สร้าง `app/(protected)/correspondences/page.tsx`: - - Data Table แสดงรายการเอกสาร - - Columns: - - Document Number (คลิกไปยัง Detail) - - Title - - Type - - Status (Badge) - - Originator - - Date - - Actions (View, Edit, Delete) - - Filters: - - ประเภทเอกสาร (Dropdown) - - สถานะ (Dropdown) - - วันที่ (Date Range Picker) - - องค์กร (Dropdown) - - Pagination (Server-side) - - Search Box - - Create Button (ตรวจสิทธิ์) -- Deliverable: ✅ หน้ารายการเอกสาร - -**T3.2 Create Correspondence Detail Page** - -- สร้าง `app/(protected)/correspondences/[id]/page.tsx`: - - **Header Section**: - - Document Number (ใหญ่) - - Status Badge - - Action Buttons: Edit, Delete, Export PDF - - **Metadata Section**: - - Title, Description - - Document Date, Issued Date, Received Date - - Originator, Recipients (TO/CC) - - Due Date (ถ้ามี) - - **Revision History**: - - แสดง Revisions ทั้งหมดเป็น Timeline - - แต่ละ Revision แสดง: Revision Number, Date, Changes, User - - **Attachments**: - - แสดงไฟล์แนบทั้งหมด - - กำหนดไฟล์หลัก (Main Document) ด้วยไอคอน - - ปุ่ม Download - - **References**: - - แสดงเอกสารที่อ้างถึง (Links) - - **Tags**: - - แสดง Tags แบบ Chips - - **Activity Log**: - - แสดง Audit Log ของเอกสารนี้ -- Deliverable: ✅ หน้า Detail ครบถ้วน - -**T3.3 Create Correspondence Form (Create/Edit)** - -- สร้าง `app/(protected)/correspondences/create/page.tsx`: -- สร้าง `app/(protected)/correspondences/[id]/edit/page.tsx`: -- Form Fields: - - **Basic Info**: - - Correspondence Type (Dropdown) - - Title (Text) - - Description (Textarea) - - Document Date (Date Picker) - - **Recipients**: - - TO: Multi-select Organizations - - CC: Multi-select Organizations - - **References**: - - Search & Select เอกสารอื่นๆ - - **Tags**: - - Autocomplete Tag Input - - **Attachments**: - - File Upload (Multi-file) - - กำหนด Main Document (Radio) - - **Deadline**: - - Due Date (Date Picker) -- Validation ด้วย Zod -- Submit → API → Redirect to Detail -- Deliverable: ✅ ฟอร์มสร้าง/แก้ไข - -**T3.4 Create Status Management** - -- ใน Detail Page เพิ่มปุ่ม Status Actions: - - **Draft → Submit** (Document Control) - - **Submit → Close** (Admin) - - **Submit → Cancel** (Admin + Dialog ให้กรอกเหตุผล) -- แสดง Confirmation Dialog ก่อนเปลี่ยนสถานะ -- Deliverable: ✅ เปลี่ยนสถานะได้ - ---- - -## **Phase 4: RFA & Workflow Visualization (สัปดาห์ที่ 7-8)** - -### Milestone: ระบบ RFA และ Workflow - -#### Tasks: - -**T4.1 Create RFA List Page** - -- สร้าง `app/(protected)/rfas/page.tsx`: - - คล้าย Correspondence List - - Columns เพิ่มเติม: - - RFA Type (DWG, DOC, MAT) - - Approval Status (Badge) - - Approval Code (1A, 3R, etc.) - - Filters: - - RFA Type - - Status - - Approval Code -- Deliverable: ✅ หน้ารายการ RFA - -**T4.2 Create RFA Detail Page** - -- สร้าง `app/(protected)/rfas/[id]/page.tsx`: - - คล้าย Correspondence Detail - - เพิ่ม Section: - - **Shop Drawings** (สำหรับ RFA_DWG): - - แสดงรายการ Shop Drawings ที่เชื่อมโยง - - แสดง Revision ของแต่ละแบบ - - Link ไปยัง Shop Drawing Detail - - **Workflow Visualization** (ดูรายละเอียดใน T4.3) -- Deliverable: ✅ หน้า Detail RFA - -**T4.3 Create Workflow Visualization Component** - -- สร้าง `components/features/rfa/workflow-visualizer.tsx`: - - **Layout**: Steps แนวนอน (Timeline) - - **Step States**: - - ✅ **Completed**: สีเขียว, ไอคอน Check - - ⏳ **Active**: สีฟ้า, ปุ่ม Action เปิดใช้งาน - - ⏸️ **Pending**: สีเทา, ปุ่ม disabled - - ❌ **Rejected**: สีแดง - - **Step Info Card** (เมื่อคลิก): - - Organization - - Assigned User - - Action Type (Review, Approve) - - Status - - Comments - - Completed Date - - **Actions** (สำหรับ Active Step): - - ปุ่ม "อนุมัติ" (Approve) - - ปุ่ม "ปฏิเสธ" (Reject) - - Dialog ให้กรอก Comments - - **Admin Override**: - - ปุ่ม "ไปยังขั้นตอนต่อไป" (Skip Step) - - ปุ่ม "ย้อนกลับ" (Previous Step) -- Deliverable: ✅ Workflow Component พร้อม - -**T4.4 Create RFA Form (Create/Edit)** - -- สร้าง `app/(protected)/rfas/create/page.tsx`: -- Form Fields: - - RFA Type (Dropdown) - - Title, Description - - Document Date - - **Shop Drawings Section** (สำหรับ RFA_DWG): - - Search & Select Shop Drawings - - แสดง Revisions ที่มี (Dropdown) - - เพิ่มได้หลายแบบ - - Attachments - - Workflow Template (Dropdown) -- Submit → สร้าง RFA + Start Workflow -- Deliverable: ✅ ฟอร์ม RFA พร้อม - -**T4.5 Implement Workflow Actions API** - -- สร้าง `lib/api/hooks/use-rfa-workflow.ts`: - - ```typescript - import { useMutation, useQueryClient } from "@tanstack/react-query"; - import { apiClient } from "../client"; - - export function useCompleteWorkflowStep() { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async ({ rfaId, stepNumber, action, comments }) => { - const response = await apiClient.post( - `/rfas/${rfaId}/workflow/steps/${stepNumber}/complete`, - { action, comments } - ); - return response.data; - }, - onSuccess: (_, variables) => { - queryClient.invalidateQueries(["rfa", variables.rfaId]); - queryClient.invalidateQueries(["my-tasks"]); - }, - }); - } - ``` - -- Hooks เพิ่มเติม: - - `use-reject-workflow-step.ts` - - `use-start-workflow.ts` -- Deliverable: ✅ Workflow Actions ทำงานได้ - ---- - -## **Phase 5: Drawing Management (สัปดาห์ที่ 9)** - -### Milestone: ระบบจัดการแบบ - -#### Tasks: - -**T5.1 Create Shop Drawing List Page** - -- สร้าง `app/(protected)/drawings/shop/page.tsx`: - - Data Table: - - Drawing Number - - Title - - Main Category - - Sub Category - - Current Revision - - Actions - - Filters: - - Category (Dropdown) - - Sub Category (Dropdown) - - Create Button -- Deliverable: ✅ หน้ารายการ Shop Drawings - -**T5.2 Create Shop Drawing Detail Page** - -- สร้าง `app/(protected)/drawings/shop/[id]/page.tsx`: - - **Header**: Drawing Number, Title - - **Current Revision Info**: - - Revision Number, Date - - Description - - Attachments (PDF, DWG) - - **Contract Drawing References**: - - แสดง Contract Drawings ที่อ้างถึง - - Links ไปยัง Contract Drawing Detail - - **Revision History**: - - แสดง Revisions ทั้งหมดเป็น Timeline - - **Related RFAs**: - - แสดง RFAs ที่เชื่อมโยงกับแบบนี้ -- Deliverable: ✅ หน้า Detail Shop Drawing - -**T5.3 Create Shop Drawing Form** - -- สร้าง `app/(protected)/drawings/shop/create/page.tsx`: -- Form Fields: - - Drawing Number (Auto-generate หรือ Manual) - - Title - - Main Category (Dropdown) - - Sub Category (Dropdown, dependent on Main) - - **Contract Drawing References**: - - Search & Select Contract Drawings (Multi-select) - - **Revision Info**: - - Revision Number (Auto) - - Revision Label (A, B, C) - - Description - - **Attachments**: - - Upload PDF (Main) - - Upload DWG (Optional) - - Upload Other Files -- Deliverable: ✅ ฟอร์ม Shop Drawing - -**T5.4 Create Contract Drawing List & Detail** - -- สร้าง `app/(protected)/drawings/contract/page.tsx`: - - Data Table: - - Drawing Number - - Title - - Volume - - Category - - Sub Category - - Filters: - - Volume (Dropdown) - - Category (Dropdown) -- สร้าง `app/(protected)/drawings/contract/[id]/page.tsx`: - - แสดง Metadata - - Attachments - - **Referenced By**: - - แสดง Shop Drawings ที่อ้างถึงแบบนี้ -- Deliverable: ✅ Contract Drawing Pages - ---- - -## **Phase 6: Circulation & Transmittal (สัปดาห์ที่ 10)** - -### Milestone: ระบบใบเวียนและเอกสารนำส่ง - -#### Tasks: - -**T6.1 Create Circulation List Page** - -- สร้าง `app/(protected)/circulations/page.tsx`: - - Data Table: - - Circulation Number - - Subject - - Status (Badge) - - Created By - - Created Date - - Filters: - - Status - - Date Range -- Deliverable: ✅ หน้ารายการใบเวียน - -**T6.2 Create Circulation Detail & Workflow** - -- สร้าง `app/(protected)/circulations/[id]/page.tsx`: - - **Header**: Circulation Number, Subject, Status - - **Linked Correspondence**: - - Link ไปยังเอกสารต้นทาง - - **Workflow Visualization**: - - คล้าย RFA Workflow - - แสดง Steps: - - Organization - - Assigned Users (Main, Action, Information) - - Status - - Comments - - Deadline - - **Actions** (สำหรับ Assigned User): - - ปุ่ม "ดำเนินการเสร็จสิ้น" (Complete) - - Dialog ให้กรอก Comments - - **Close Circulation** (Document Control): - - ปุ่ม "ปิดใบเวียน" (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) -- Deliverable: ✅ หน้า Detail ใบเวียน +# 📋 แผนการพัฒนา Frontend (NestJS) - LCBP3-DMS v1.4.0 + +# 📋 แผนการพัฒนา Frontend (Next.js) - LCBP3-DMS v1.4.0 + +## 🎯 ภาพรวมโครงการ + +พัฒนา Frontend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่ทันสมัย responsive และใช้งานง่าย รองรับการจัดการเอกสารที่ซับซ้อน มี Dashboard แบบ Real-time และระบบ Workflow Visualization + +--- + +## 📐 สถาปัตยกรรมระบบ + +### Technology Stack + +- **Framework:** Next.js 14+ (App Router, React 18+, TypeScript, ESM) +- **Styling:** Tailwind CSS + PostCSS +- **Component Library:** shadcn/ui (Radix UI) +- **State Management:** + - **Server State:** TanStack Query (React Query) + - **Global Client State:** Zustand + - **Form State:** React Hook Form + Zod +- **Data Fetching:** Axios + TanStack Query +- **Authentication:** NextAuth.js (JWT Strategy) +- **File Upload:** React Dropzone +- **Tables:** TanStack Table +- **Charts:** Recharts +- **Date Picker:** date-fns + shadcn/ui Calendar +- **Icons:** Lucide React +- **Testing:** + - **Unit/Integration:** Vitest + React Testing Library + - **E2E:** Playwright + - **API Mocking:** Mock Service Worker (MSW) + +### โครงสร้างโปรเจกต์ + +``` +app/ +├── (public)/ # Public routes (Landing, Login) +│ ├── page.tsx # Landing Page +│ └── login/ # Login Page +├── (protected)/ # Protected routes +│ ├── layout.tsx # App Shell (Navbar + Sidebar) +│ ├── dashboard/ # Dashboard +│ ├── correspondences/ # Correspondence Management +│ ├── rfas/ # RFA Management +│ ├── drawings/ # Drawing Management +│ ├── circulations/ # Circulation Management +│ ├── transmittals/ # Transmittal Management +│ ├── search/ # Advanced Search +│ ├── reports/ # Reports +│ ├── admin/ # Admin Panel +│ └── profile/ # User Profile +├── api/ # API Routes (if needed) +components/ +├── ui/ # shadcn/ui components +├── features/ # Feature-specific components +│ ├── auth/ +│ ├── correspondence/ +│ ├── rfa/ +│ ├── drawing/ +│ ├── circulation/ +│ └── common/ +└── layouts/ # Layout components +lib/ +├── api/ # API client & hooks +├── stores/ # Zustand stores +├── utils/ # Utility functions +├── hooks/ # Custom hooks +└── types/ # TypeScript types +public/ +├── images/ +└── fonts/ +``` + +--- + +## 🗓️ แผนการพัฒนาแบบ Phase-Based + +## **Phase 0: Setup & Infrastructure (สัปดาห์ที่ 1)** + +### Milestone: สร้างโครงสร้างพื้นฐานและ Development Environment + +#### Tasks: + +**T0.1 Initialize Next.js Project** + +- สร้างโปรเจกต์ด้วย `create-next-app`: + ```bash + npx create-next-app@latest lcbp3-frontend --typescript --tailwind --app --src-dir=false + ``` +- เลือก Options: + - ✅ TypeScript + - ✅ ESLint + - ✅ Tailwind CSS + - ✅ App Router + - ✅ Import alias (@/\*) +- Setup .gitignore, README.md +- Deliverable: ✅ โปรเจกต์เริ่มต้นพร้อม + +**T0.2 Install Core Dependencies** + +```bash +# State Management & Data Fetching +npm install @tanstack/react-query zustand +npm install axios +npm install react-hook-form @hookform/resolvers zod + +# UI Components & Styling +npm install clsx tailwind-merge +npm install lucide-react +npm install date-fns + +# File Upload +npm install react-dropzone + +# Authentication +npm install next-auth + +# Development Tools +npm install -D @types/node +``` + +- Deliverable: ✅ Dependencies ติดตั้งสมบูรณ์ + +**T0.3 Setup shadcn/ui** + +```bash +npx shadcn-ui@latest init +``` + +- เลือก Style: Default +- เลือก Base Color: Slate +- ติดตั้ง Components เบื้องต้น: + ```bash + npx shadcn-ui@latest add button input label card table dropdown-menu + npx shadcn-ui@latest add dialog sheet toast alert + npx shadcn-ui@latest add form select textarea checkbox + npx shadcn-ui@latest add calendar popover + ``` +- Deliverable: ✅ shadcn/ui พร้อมใช้งาน + +**T0.4 Configure Tailwind CSS** + +- แก้ไข `tailwind.config.ts`: + - เพิ่ม Custom Colors (ตาม Brand) + - เพิ่ม Custom Fonts (ภาษาไทย: Noto Sans Thai) + - Configure Container, Spacing +- สร้าง `app/globals.css` พร้อม Custom Styles +- Deliverable: ✅ Tailwind พร้อมใช้ + +**T0.5 Setup Development Environment** + +- สร้าง `.env.local`: + ``` + NEXT_PUBLIC_API_URL=http://backend.np-dms.work/api + NEXTAUTH_URL=http://localhost:3000 + NEXTAUTH_SECRET=... + ``` +- Setup ESLint Rules (ไทย Comments OK) +- Setup Prettier +- Setup VS Code Settings +- Deliverable: ✅ Dev Environment พร้อม + +**T0.6 Setup Git & Docker** + +- Push โปรเจกต์ไปยัง Gitea (git.np-dms.work) +- สร้าง Dockerfile สำหรับ Next.js: + ```dockerfile + FROM node:20-alpine + WORKDIR /app + COPY package*.json ./ + RUN npm ci + COPY . . + RUN npm run build + EXPOSE 3000 + CMD ["npm", "start"] + ``` +- สร้าง docker-compose.yml (เชื่อม Network `lcbp3`) +- Deliverable: ✅ Project อยู่ใน Git + Docker พร้อม + +--- + +## **Phase 1: Authentication & App Shell (สัปดาห์ที่ 2-3)** + +### Milestone: ระบบ Login และ Layout หลัก + +#### Tasks: + +**T1.1 Setup API Client** + +- สร้าง `lib/api/client.ts`: + + ```typescript + import axios from "axios"; + + export const apiClient = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + headers: { + "Content-Type": "application/json", + }, + }); + + // Request Interceptor (Add JWT Token) + apiClient.interceptors.request.use((config) => { + const token = localStorage.getItem("access_token"); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }); + + // Response Interceptor (Handle Errors) + apiClient.interceptors.response.use( + (response) => response, + (error) => { + if (error.response?.status === 401) { + // Redirect to login + window.location.href = "/login"; + } + return Promise.reject(error); + } + ); + ``` + +- Deliverable: ✅ API Client พร้อมใช้ + +**T1.2 Setup TanStack Query** + +- สร้าง `app/providers.tsx`: + + ```typescript + "use client"; + import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + import { useState } from "react"; + + export function Providers({ children }: { children: React.ReactNode }) { + const [queryClient] = useState( + () => + new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, // 1 minute + refetchOnWindowFocus: false, + }, + }, + }) + ); + + return ( + {children} + ); + } + ``` + +- Wrap ใน `app/layout.tsx` +- Deliverable: ✅ React Query พร้อม + +**T1.3 Create Auth Store (Zustand)** + +- สร้าง `lib/stores/auth-store.ts`: + + ```typescript + import { create } from "zustand"; + import { persist } from "zustand/middleware"; + + interface User { + user_id: number; + username: string; + email: string; + first_name: string; + last_name: string; + primary_organization_id: number; + permissions: string[]; + } + + interface AuthStore { + user: User | null; + token: string | null; + setAuth: (user: User, token: string) => void; + clearAuth: () => void; + hasPermission: (permission: string) => boolean; + } + + export const useAuthStore = create()( + persist( + (set, get) => ({ + user: null, + token: null, + setAuth: (user, token) => { + set({ user, token }); + localStorage.setItem("access_token", token); + }, + clearAuth: () => { + set({ user: null, token: null }); + localStorage.removeItem("access_token"); + }, + hasPermission: (permission) => { + const user = get().user; + return user?.permissions.includes(permission) || false; + }, + }), + { + name: "auth-storage", + } + ) + ); + ``` + +- Deliverable: ✅ Auth Store พร้อม + +**T1.4 Create Login Page** + +- สร้าง `app/(public)/login/page.tsx`: + + ```typescript + "use client"; + import { useForm } from "react-hook-form"; + import { zodResolver } from "@hookform/resolvers/zod"; + import * as z from "zod"; + import { Button } from "@/components/ui/button"; + import { Input } from "@/components/ui/input"; + import { useRouter } from "next/navigation"; + import { useAuthStore } from "@/lib/stores/auth-store"; + import { apiClient } from "@/lib/api/client"; + + const loginSchema = z.object({ + username: z.string().min(1, "กรุณากรอกชื่อผู้ใช้"), + password: z.string().min(1, "กรุณากรอกรหัสผ่าน"), + }); + + export default function LoginPage() { + const router = useRouter(); + const setAuth = useAuthStore((state) => state.setAuth); + const form = useForm({ + resolver: zodResolver(loginSchema), + }); + + const handleLogin = async (data: z.infer) => { + try { + const response = await apiClient.post("/auth/login", data); + const { access_token, user } = response.data; + setAuth(user, access_token); + router.push("/dashboard"); + } catch (error) { + console.error("Login failed:", error); + // แสดง Toast Error + } + }; + + return ( +
+
+ {/* Form Fields */} +
+
+ ); + } + ``` + +- Deliverable: ✅ หน้า Login พร้อม + +**T1.5 Create Landing Page** + +- สร้าง `app/(public)/page.tsx`: + - Hero Section พร้อมข้อมูลโครงการ + - Feature Highlights + - CTA Button → Login +- ใช้ Tailwind + Animations +- Deliverable: ✅ Landing Page สวยงาม + +**T1.6 Create Protected Layout (App Shell)** + +- สร้าง `app/(protected)/layout.tsx`: + + ```typescript + "use client"; + import { useEffect } from "react"; + import { useRouter } from "next/navigation"; + import { useAuthStore } from "@/lib/stores/auth-store"; + import Navbar from "@/components/layouts/navbar"; + import Sidebar from "@/components/layouts/sidebar"; + + export default function ProtectedLayout({ + children, + }: { + children: React.ReactNode; + }) { + const router = useRouter(); + const user = useAuthStore((state) => state.user); + + useEffect(() => { + if (!user) { + router.push("/login"); + } + }, [user, router]); + + if (!user) return null; + + return ( +
+ +
+ +
{children}
+
+
+ ); + } + ``` + +- Deliverable: ✅ App Shell พร้อม + +**T1.7 Create Navbar Component** + +- สร้าง `components/layouts/navbar.tsx`: + - แสดงชื่อระบบ + - แสดงชื่อผู้ใช้ + Avatar + - Dropdown Menu: + - Profile + - Settings + - Logout +- Responsive (Mobile Hamburger Menu) +- Deliverable: ✅ Navbar พร้อม + +**T1.8 Create Sidebar Component** + +- สร้าง `components/layouts/sidebar.tsx`: + - เมนูหลัก: + - Dashboard + - Correspondences + - RFAs + - Drawings (Shop & Contract) + - Circulations + - Transmittals + - Search + - Reports + - เมนู Admin (แสดงตามสิทธิ์): + - Users + - Roles & Permissions + - Master Data + - Document Numbering +- Collapsible Sidebar +- Active State Highlighting +- Deliverable: ✅ Sidebar พร้อม + +--- + +## **Phase 2: Dashboard & Common Components (สัปดาห์ที่ 4)** + +### Milestone: Dashboard และ Reusable Components + +#### Tasks: + +**T2.1 Create Reusable Components** + +- `components/features/common/data-table.tsx`: + - ใช้ TanStack Table + - รองรับ Pagination, Sorting, Filtering + - Responsive +- `components/features/common/file-upload.tsx`: + - ใช้ React Dropzone + - รองรับ Multi-file Upload + - Drag & Drop + - Progress Bar +- `components/features/common/status-badge.tsx`: + - แสดง Status แบบสีสัน (Draft, Submitted, Approved) +- `components/features/common/permission-guard.tsx`: + - ซ่อน/แสดง Component ตามสิทธิ์ +- Deliverable: ✅ Reusable Components พร้อม + +**T2.2 Create Dashboard Page** + +- สร้าง `app/(protected)/dashboard/page.tsx`: + - **KPI Cards Section**: + - จำนวนเอกสารทั้งหมด + - งานที่รอดำเนินการ + - เอกสารที่เกินกำหนด + - RFA ที่รออนุมัติ + - **My Tasks Table**: + - ดึงข้อมูลจาก `/api/circulations/my-tasks` (ใช้ `v_user_tasks`) + - แสดงรายการงานที่ต้องทำ (Pending, In Progress) + - Columns: Document Number, Title, Type, Status, Deadline, Actions + - คลิกแถวแล้วไปยังหน้า Detail + - **Recent Activity Feed**: + - ดึงข้อมูลจาก `/api/audit-logs/user/:userId` + - แสดง 10 รายการล่าสุด +- Deliverable: ✅ Dashboard ครบถ้วน + +**T2.3 Create API Hooks** + +- สร้าง `lib/api/hooks/use-my-tasks.ts`: + + ```typescript + import { useQuery } from "@tanstack/react-query"; + import { apiClient } from "../client"; + + export function useMyTasks() { + return useQuery({ + queryKey: ["my-tasks"], + queryFn: async () => { + const response = await apiClient.get("/circulations/my-tasks"); + return response.data; + }, + }); + } + ``` + +- สร้าง Hooks เพิ่มเติม: + - `use-dashboard-stats.ts` + - `use-recent-activity.ts` +- Deliverable: ✅ API Hooks พร้อม + +--- + +## **Phase 3: Correspondence Management (สัปดาห์ที่ 5-6)** + +### Milestone: ระบบจัดการเอกสารโต้ตอบ + +#### Tasks: + +**T3.1 Create Correspondence List Page** + +- สร้าง `app/(protected)/correspondences/page.tsx`: + - Data Table แสดงรายการเอกสาร + - Columns: + - Document Number (คลิกไปยัง Detail) + - Title + - Type + - Status (Badge) + - Originator + - Date + - Actions (View, Edit, Delete) + - Filters: + - ประเภทเอกสาร (Dropdown) + - สถานะ (Dropdown) + - วันที่ (Date Range Picker) + - องค์กร (Dropdown) + - Pagination (Server-side) + - Search Box + - Create Button (ตรวจสิทธิ์) +- Deliverable: ✅ หน้ารายการเอกสาร + +**T3.2 Create Correspondence Detail Page** + +- สร้าง `app/(protected)/correspondences/[id]/page.tsx`: + - **Header Section**: + - Document Number (ใหญ่) + - Status Badge + - Action Buttons: Edit, Delete, Export PDF + - **Metadata Section**: + - Title, Description + - Document Date, Issued Date, Received Date + - Originator, Recipients (TO/CC) + - Due Date (ถ้ามี) + - **Revision History**: + - แสดง Revisions ทั้งหมดเป็น Timeline + - แต่ละ Revision แสดง: Revision Number, Date, Changes, User + - **Attachments**: + - แสดงไฟล์แนบทั้งหมด + - กำหนดไฟล์หลัก (Main Document) ด้วยไอคอน + - ปุ่ม Download + - **References**: + - แสดงเอกสารที่อ้างถึง (Links) + - **Tags**: + - แสดง Tags แบบ Chips + - **Activity Log**: + - แสดง Audit Log ของเอกสารนี้ +- Deliverable: ✅ หน้า Detail ครบถ้วน + +**T3.3 Create Correspondence Form (Create/Edit)** + +- สร้าง `app/(protected)/correspondences/create/page.tsx`: +- สร้าง `app/(protected)/correspondences/[id]/edit/page.tsx`: +- Form Fields: + - **Basic Info**: + - Correspondence Type (Dropdown) + - Title (Text) + - Description (Textarea) + - Document Date (Date Picker) + - **Recipients**: + - TO: Multi-select Organizations + - CC: Multi-select Organizations + - **References**: + - Search & Select เอกสารอื่นๆ + - **Tags**: + - Autocomplete Tag Input + - **Attachments**: + - File Upload (Multi-file) + - กำหนด Main Document (Radio) + - **Deadline**: + - Due Date (Date Picker) +- Validation ด้วย Zod +- Submit → API → Redirect to Detail +- Deliverable: ✅ ฟอร์มสร้าง/แก้ไข + +**T3.4 Create Status Management** + +- ใน Detail Page เพิ่มปุ่ม Status Actions: + - **Draft → Submit** (Document Control) + - **Submit → Close** (Admin) + - **Submit → Cancel** (Admin + Dialog ให้กรอกเหตุผล) +- แสดง Confirmation Dialog ก่อนเปลี่ยนสถานะ +- Deliverable: ✅ เปลี่ยนสถานะได้ + +--- + +## **Phase 4: RFA & Workflow Visualization (สัปดาห์ที่ 7-8)** + +### Milestone: ระบบ RFA และ Workflow + +#### Tasks: + +**T4.1 Create RFA List Page** + +- สร้าง `app/(protected)/rfas/page.tsx`: + - คล้าย Correspondence List + - Columns เพิ่มเติม: + - RFA Type (DWG, DOC, MAT) + - Approval Status (Badge) + - Approval Code (1A, 3R, etc.) + - Filters: + - RFA Type + - Status + - Approval Code +- Deliverable: ✅ หน้ารายการ RFA + +**T4.2 Create RFA Detail Page** + +- สร้าง `app/(protected)/rfas/[id]/page.tsx`: + - คล้าย Correspondence Detail + - เพิ่ม Section: + - **Shop Drawings** (สำหรับ RFA_DWG): + - แสดงรายการ Shop Drawings ที่เชื่อมโยง + - แสดง Revision ของแต่ละแบบ + - Link ไปยัง Shop Drawing Detail + - **Workflow Visualization** (ดูรายละเอียดใน T4.3) +- Deliverable: ✅ หน้า Detail RFA + +**T4.3 Create Workflow Visualization Component** + +- สร้าง `components/features/rfa/workflow-visualizer.tsx`: + - **Layout**: Steps แนวนอน (Timeline) + - **Step States**: + - ✅ **Completed**: สีเขียว, ไอคอน Check + - ⏳ **Active**: สีฟ้า, ปุ่ม Action เปิดใช้งาน + - ⏸️ **Pending**: สีเทา, ปุ่ม disabled + - ❌ **Rejected**: สีแดง + - **Step Info Card** (เมื่อคลิก): + - Organization + - Assigned User + - Action Type (Review, Approve) + - Status + - Comments + - Completed Date + - **Actions** (สำหรับ Active Step): + - ปุ่ม "อนุมัติ" (Approve) + - ปุ่ม "ปฏิเสธ" (Reject) + - Dialog ให้กรอก Comments + - **Admin Override**: + - ปุ่ม "ไปยังขั้นตอนต่อไป" (Skip Step) + - ปุ่ม "ย้อนกลับ" (Previous Step) +- Deliverable: ✅ Workflow Component พร้อม + +**T4.4 Create RFA Form (Create/Edit)** + +- สร้าง `app/(protected)/rfas/create/page.tsx`: +- Form Fields: + - RFA Type (Dropdown) + - Title, Description + - Document Date + - **Shop Drawings Section** (สำหรับ RFA_DWG): + - Search & Select Shop Drawings + - แสดง Revisions ที่มี (Dropdown) + - เพิ่มได้หลายแบบ + - Attachments + - Workflow Template (Dropdown) +- Submit → สร้าง RFA + Start Workflow +- Deliverable: ✅ ฟอร์ม RFA พร้อม + +**T4.5 Implement Workflow Actions API** + +- สร้าง `lib/api/hooks/use-rfa-workflow.ts`: + + ```typescript + import { useMutation, useQueryClient } from "@tanstack/react-query"; + import { apiClient } from "../client"; + + export function useCompleteWorkflowStep() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ rfaId, stepNumber, action, comments }) => { + const response = await apiClient.post( + `/rfas/${rfaId}/workflow/steps/${stepNumber}/complete`, + { action, comments } + ); + return response.data; + }, + onSuccess: (_, variables) => { + queryClient.invalidateQueries(["rfa", variables.rfaId]); + queryClient.invalidateQueries(["my-tasks"]); + }, + }); + } + ``` + +- Hooks เพิ่มเติม: + - `use-reject-workflow-step.ts` + - `use-start-workflow.ts` +- Deliverable: ✅ Workflow Actions ทำงานได้ + +--- + +## **Phase 5: Drawing Management (สัปดาห์ที่ 9)** + +### Milestone: ระบบจัดการแบบ + +#### Tasks: + +**T5.1 Create Shop Drawing List Page** + +- สร้าง `app/(protected)/drawings/shop/page.tsx`: + - Data Table: + - Drawing Number + - Title + - Main Category + - Sub Category + - Current Revision + - Actions + - Filters: + - Category (Dropdown) + - Sub Category (Dropdown) + - Create Button +- Deliverable: ✅ หน้ารายการ Shop Drawings + +**T5.2 Create Shop Drawing Detail Page** + +- สร้าง `app/(protected)/drawings/shop/[id]/page.tsx`: + - **Header**: Drawing Number, Title + - **Current Revision Info**: + - Revision Number, Date + - Description + - Attachments (PDF, DWG) + - **Contract Drawing References**: + - แสดง Contract Drawings ที่อ้างถึง + - Links ไปยัง Contract Drawing Detail + - **Revision History**: + - แสดง Revisions ทั้งหมดเป็น Timeline + - **Related RFAs**: + - แสดง RFAs ที่เชื่อมโยงกับแบบนี้ +- Deliverable: ✅ หน้า Detail Shop Drawing + +**T5.3 Create Shop Drawing Form** + +- สร้าง `app/(protected)/drawings/shop/create/page.tsx`: +- Form Fields: + - Drawing Number (Auto-generate หรือ Manual) + - Title + - Main Category (Dropdown) + - Sub Category (Dropdown, dependent on Main) + - **Contract Drawing References**: + - Search & Select Contract Drawings (Multi-select) + - **Revision Info**: + - Revision Number (Auto) + - Revision Label (A, B, C) + - Description + - **Attachments**: + - Upload PDF (Main) + - Upload DWG (Optional) + - Upload Other Files +- Deliverable: ✅ ฟอร์ม Shop Drawing + +**T5.4 Create Contract Drawing List & Detail** + +- สร้าง `app/(protected)/drawings/contract/page.tsx`: + - Data Table: + - Drawing Number + - Title + - Volume + - Category + - Sub Category + - Filters: + - Volume (Dropdown) + - Category (Dropdown) +- สร้าง `app/(protected)/drawings/contract/[id]/page.tsx`: + - แสดง Metadata + - Attachments + - **Referenced By**: + - แสดง Shop Drawings ที่อ้างถึงแบบนี้ +- Deliverable: ✅ Contract Drawing Pages + +--- + +## **Phase 6: Circulation & Transmittal (สัปดาห์ที่ 10)** + +### Milestone: ระบบใบเวียนและเอกสารนำส่ง + +#### Tasks: + +**T6.1 Create Circulation List Page** + +- สร้าง `app/(protected)/circulations/page.tsx`: + - Data Table: + - Circulation Number + - Subject + - Status (Badge) + - Created By + - Created Date + - Filters: + - Status + - Date Range +- Deliverable: ✅ หน้ารายการใบเวียน + +**T6.2 Create Circulation Detail & Workflow** + +- สร้าง `app/(protected)/circulations/[id]/page.tsx`: + - **Header**: Circulation Number, Subject, Status + - **Linked Correspondence**: + - Link ไปยังเอกสารต้นทาง + - **Workflow Visualization**: + - คล้าย RFA Workflow + - แสดง Steps: + - Organization + - Assigned Users (Main, Action, Information) + - Status + - Comments + - Deadline + - **Actions** (สำหรับ Assigned User): + - ปุ่ม "ดำเนินการเสร็จสิ้น" (Complete) + - Dialog ให้กรอก Comments + - **Close Circulation** (Document Control): + - ปุ่ม "ปิดใบเวียน" (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) +- Deliverable: ✅ หน้า Detail ใบเวียน diff --git a/docs/LCBP3-DMS_V1_4_0_FullStackJS.md b/docs/LCBP3-DMS_V1_4_0_FullStackJS.md index b1ee793..a054d4c 100644 --- a/docs/LCBP3-DMS_V1_4_0_FullStackJS.md +++ b/docs/LCBP3-DMS_V1_4_0_FullStackJS.md @@ -1,480 +1,480 @@ -# **Documents Management Sytem Version 1.4.0: แนวทางการพัฒนา FullStackJS** - -## **🧠 ปรัชญาทั่วไป** - -แนวทางปฏิบัติที่ดีที่สุดแบบครบวงจรสำหรับการพัฒนา NestJS Backend, NextJS Frontend และ Tailwind-based UI/UX ในสภาพแวดล้อม TypeScript มุ่งเน้นที่ ความชัดเจน (clarity), ความง่ายในการบำรุงรักษา (maintainability), ความสอดคล้องกัน (consistency) และ การเข้าถึงได้ (accessibility) ตลอดทั้งสแต็ก - -## **⚙️ แนวทางทั่วไปสำหรับ TypeScript** - -### **หลักการพื้นฐาน** - -* ใช้ **ภาษาอังกฤษ** สำหรับโค้ด -* ใช้ **ภาษาไทย** สำหรับ comment และเอกสารทั้งหมด -* กำหนดไทป์ (type) อย่างชัดเจนสำหรับตัวแปร, พารามิเตอร์ และค่าที่ส่งกลับ (return values) ทั้งหมด -* หลีกเลี่ยงการใช้ any; ให้สร้างไทป์ (types) หรืออินเทอร์เฟซ (interfaces) ที่กำหนดเอง -* ใช้ **JSDoc** สำหรับคลาส (classes) และเมธอด (methods) ที่เป็น public -* ส่งออก (Export) **สัญลักษณ์หลัก (main symbol) เพียงหนึ่งเดียว** ต่อไฟล์ -* หลีกเลี่ยงบรรทัดว่างภายในฟังก์ชัน -* ระบุ // File: path/filename ในบรรทัดแรกของทุกไฟล์ -* ระบุ // บันทึกการแก้ไข, หากมีการแก้ไขเพิ่มในอนาคต ให้เพิ่มบันทึก - -### **ข้อตกลงในการตั้งชื่อ (Naming Conventions)** - -| Entity (สิ่งที่ตั้งชื่อ) | Convention (รูปแบบ) | Example (ตัวอย่าง) | -| :---- | :---- | :---- | -| Classes | PascalCase | UserService | -| Property | snake_sase | 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) - -## **🧩 ฟังก์ชัน (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) ให้เป็นระดับเดียวในแต่ละฟังก์ชัน - -## **🧱 การจัดการข้อมูล (Data Handling)** - -* ห่อหุ้มข้อมูล (Encapsulate) ในไทป์แบบผสม (composite types) -* ใช้ **immutability** (การไม่เปลี่ยนแปลงค่า) ด้วย readonly และ as const -* ทำการตรวจสอบความถูกต้องของข้อมูล (Validations) ในคลาสหรือ DTOs ไม่ใช่ภายในฟังก์ชันทางธุรกิจ -* ตรวจสอบความถูกต้องของข้อมูลโดยใช้ DTOs ที่มีไทป์กำหนดเสมอ - -## **🧰 คลาส (Classes)** - -* ปฏิบัติตามหลักการ **SOLID** -* ควรใช้ **composition มากกว่า inheritance** (Prefer composition over inheritance) -* กำหนด **interfaces** สำหรับสัญญา (contracts) -* ให้คลาสมุ่งเน้นการทำงานเฉพาะอย่างและมีขนาดเล็ก (\< 200 บรรทัด, \< 10 เมธอด, \< 10 properties) - -## **🚨 การจัดการข้อผิดพลาด (Error Handling)** - -* ใช้ Exceptions สำหรับข้อผิดพลาดที่ไม่คาดคิด -* ดักจับ (Catch) ข้อผิดพลาดเพื่อแก้ไขหรือเพิ่มบริบท (context) เท่านั้น; หากไม่เช่นนั้น ให้ใช้ global error handlers -* ระบุข้อความข้อผิดพลาด (error messages) ที่มีความหมายเสมอ - -## **🧪 การทดสอบ (ทั่วไป) (Testing (General))** - -* ใช้รูปแบบ **Arrange–Act–Assert** -* ใช้ชื่อตัวแปรในการทดสอบที่สื่อความหมาย (inputData, expectedOutput) -* เขียน **unit tests** สำหรับ public methods ทั้งหมด -* จำลอง (Mock) การพึ่งพาภายนอก (external dependencies) -* เพิ่ม **acceptance tests** ต่อโมดูลโดยใช้รูปแบบ Given–When-Then - -## **🏗️ แบ็กเอนด์ (NestJS) (Backend (NestJS))** - -### **หลักการ** - -* **สถาปัตยกรรมแบบโมดูลาร์ (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 - -### **ฟังก์ชันหลัก (Core Functionalities)** - -* Global **filters** สำหรับการจัดการ exception -* **Middlewares** สำหรับการจัดการ request -* **Guards** สำหรับการอนุญาต (permissions) และ RBAC -* **Interceptors** สำหรับการแปลงข้อมูล response และการบันทึก log - -### **ข้อจำกัดในการ 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] - -### **โครงสร้างโมดูลตามโดเมน (Domain-Driven Module Structure)** - -เพื่อให้สอดคล้องกับสคีมา SQL (LCBP3-DMS) เราจะใช้โครงสร้างโมดูลแบบ **Domain-Driven (แบ่งตามขอบเขตธุรกิจ)** แทนการแบ่งตามฟังก์ชัน: - -1. **CommonModule:** - * เก็บ Services ที่ใช้ร่วมกัน เช่น DatabaseModule, FileStorageService (จัดการไฟล์ใน QNAP), AuditLogService, NotificationService - * จัดการ audit_logs - * NotificationService ต้องรองรับ Triggers ที่ระบุใน Requirement 6.7 [cite: 6.7] -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. **UserModule:** - * จัดการ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles - * **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ: - * สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3] -4. **ProjectModule:** - * จัดการ projects, organizations, contracts, project_parties, contract_parties -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] -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) สำหรับการส่งต่อเอกสารทั่วไประหว่างองค์กร -7. **RfaModule:** - * จัดการ rfas, rfa_revisions, rfa_items - * รับผิดชอบเวิร์กโฟลว์ **"RFA Workflows"** (rfa_workflows, rfa_workflow_templates, rfa_workflow_template_steps, rfa_status_transitions) สำหรับการอนุมัติเอกสารทางเทคนิค -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(ตารางเชื่อมไฟล์แนบ) -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)สำหรับการเวียนเอกสาร **ภายในองค์กร** -10. **TransmittalModule:** - * จัดการ transmittals และ transmittal_items -11. **SearchModule:** - * ให้บริการค้นหาขั้นสูง (Advanced Search) [cite: 6.2] โดยใช้ **Elasticsearch** เพื่อรองรับการค้นหาแบบ Full-text จากชื่อเรื่อง, รายละเอียด, เลขที่เอกสาร, ประเภท, วันที่, และ Tags - * ระบบจะใช้ Elasticsearch Engine ในการจัดทำดัชนีเพื่อการค้นหาข้อมูลเชิงลึกจากเนื้อหาของเอกสาร โดยข้อมูลจะถูกส่งไปทำดัชนีจาก Backend (NestJS) ทุกครั้งที่มีการสร้างหรือแก้ไขเอกสาร -12. **DocumentNumberingModule:** - * **สถานะ:** เป็น Module ภายใน (Internal Module) ไม่เปิด API สู่ภายนอก - * **หน้าที่:** ให้บริการ DocumentNumberingService ที่ Module อื่น (เช่น CorrespondenceModule) จะ Inject ไปใช้งาน - * **ตรรกะ:** รับผิดชอบการสร้างเลขที่เอกสาร โดยการเรียกใช้ Stored Procedure *sp_get_next_document_number** เพื่อป้องกัน Race Condition - -### **สถาปัตยกรรมระบบ (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 -│ ├── 📁 guards # Custom Guards (RBAC Guard) -│ ├── 📁 interceptors # Interceptors (Audit Log, Transform) -│ └── 📁 services # Shared Services (NotificationService) -├── 📁 modules -│ ├── 📁 user # UserModule (จัดการ Users, Roles, Permissions) -│ ├── 📁 project # ProjectModule (จัดการ Projects, Organizations, Contracts) -│ ├── 📁 correspondence # CorrespondenceModule (จัดการเอกสารโต้ตอบ) -│ ├── 📁 rfa # RfaModule (จัดการเอกสารขออนุมัติ) -│ ├── 📁 drawing # DrawingModule (จัดการแบบแปลน) -│ ├── 📁 circulation # CirculationModule (จัดการใบเวียน) -│ ├── 📁 transmittal # TransmittalModule (จัดการเอกสารนำส่ง) -│ ├── 📁 search # SearchModule (ค้นหาขั้นสูงด้วย Elasticsearch) -│ └── 📁 document-numbering # DocumentNumberingModule (Internal Module) -└── 📁 database # Database Migration & Seeding Scripts -``` - -### **เเทคโนโลยีที่ใช้ (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 | - -เราจะแบ่งการทดสอบเป็น 3 ระดับ โดยใช้ **Jest** และ @nestjs/testing: - -* **Unit Tests (การทดสอบหน่วยย่อย):** - * **เป้าหมาย:** ทดสอบ Logic ภายใน Service, Guard, หรือ Pipe โดยจำลอง (Mock) Dependencies ทั้งหมด - * **สิ่งที่ต้องทดสอบ:** Business Logic (เช่น การเปลี่ยนสถานะ Workflow, การตรวจสอบ Deadline) [cite: 2.9.1], ตรรกะการตรวจสอบสิทธิ์ (Auth Guard) ทั้ง 4 ระดับ -* **Integration Tests (การทดสอบการบูรณาการ):** - * **เป้าหมาย:** ทดสอบการทำงานร่วมกันของ Controller -> Service -> Repository (Database) - * **เทคนิค:** ใช้ **Test Database แยกต่างหาก** (ห้ามใช้ Dev DB) และใช้ supertest เพื่อยิง HTTP Request จริงไปยัง App - * **สิ่งที่ต้องทดสอบ:** การเรียก sp\get\next\document\number [cite: 2.9.3] และการทำงานของ Views (เช่น v_user_tasks) -* **E2E (End-to-End) Tests:** - * **เป้าหมาย:** ทดสอบ API Contract ว่า Response Body Shape ตรงตามเอกสาร Swagger เพื่อรับประกันทีม Frontend - -### **🗄️ 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 - -### **การไหลของข้อมูล (Data Flow)** - -1. Request: ผ่าน Nginx Proxy Manager -> NestJS Controller -2. Authentication: JWT Guard ตรวจสอบ Token และดึงข้อมูล User -3. Authorization: RBAC Guard (ใช้ CASL) ตรวจสอบสิทธิ์จาก Decorators (@RequirePermission) -4. Validation: Validation Pipe (ใช้ class-validator) ตรวจสอบ DTO -5. Business Logic: Service Layer ประมวลผลตรรกะทางธุรกิจ -6. Data Access: Repository Layer (ใช้ TypeORM) ติดต่อกับฐานข้อมูล MariaDB -7. Response: ส่งกลับไปยัง Frontend พร้อมสถานะและข้อมูลที่เหมาะสม - -# **🖥️ ฟรอนต์เอนด์ (NextJS / React / UI) (Frontend (NextJS / React / UI))** - -### **โปรไฟล์นักพัฒนา (Developer Profile)** - -วิศวกร TypeScript + React/NextJS ระดับ Senior -เชี่ยวชาญ TailwindCSS, Shadcn/UI, และ Radix สำหรับการพัฒนา UI - -### **แนวทางการพัฒนาโค้ด (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 โมดูลที่จำเป็นต้องใช้อย่างชัดเจนเสมอ - -### **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 - -### **การตรวจสอบฟอร์มและข้อผิดพลาด (Form Validation & Errors)** - -* ใช้ไลบรารีฝั่ง client เช่น zod และ react-hook-form -* แสดงข้อผิดพลาดด้วย **alert components** หรือข้อความ inline -* ต้องมี labels, placeholders, และข้อความ feedback - -### **🧪 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]) - -### **🗄️ 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]) - -# **🔗 แนวทางการบูรณาการ 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 | - -## **🗂️ ข้อตกลงเฉพาะสำหรับ DMS (LCBP3-DMS)** - -ส่วนนี้ขยายแนวทาง FullStackJS ทั่วไปสำหรับโปรเจกต์ **LCBP3-DMS** โดยมุ่งเน้นไปที่เวิร์กโฟลว์การอนุมัติเอกสาร (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation) - -### **🧩 RBAC และการควบคุมสิทธิ์ (RBAC & Permission Control)** - -ใช้ Decorators เพื่อบังคับใช้สิทธิ์การเข้าถึง โดยอ้างอิงสิทธิ์จากตาราง permissions - -@RequirePermission('rfas.respond') // ต้องตรงกับ 'permission\code' -@Put(':id') -updateRFA(@Param('id') id: string) { - return this.rfaService.update(id); -} - -### **Roles (บทบาท)** - -* **Superadmin**: ไม่มีข้อจำกัดใดๆ [cite: 4.3] -* **Admin**: มีสิทธิ์เต็มที่ในองค์กร [cite: 4.3] -* **Document Control**: เพิ่ม/แก้ไข/ลบ เอกสารในองค์กร [cite: 4.3] -* **Editor**: สามารถ เพิ่ม/แก้ไข เอกสารที่กำหนด [cite: 4.3] -* **Viewer**: สามารถดู เอกสาร [cite: 4.3] - -### **ตัวอย่าง 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] - -## **🧾 มาตรฐาน 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) | - -## **📂 การจัดการไฟล์ (File Handling) (ปรับปรุงใหม่)** - -### **มาตรฐานการอัปโหลดไฟล์ (File Upload Standard)** - -* **ตรรกะใหม่:** การอัปโหลดไฟล์ทั้งหมดจะถูกจัดการโดย FileStorageService และบันทึกข้อมูลไฟล์ลงในตาราง attachments (ตารางกลาง) -* ไฟล์จะถูกเชื่อมโยงไปยัง 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 -* ขนาดสูงสุด: **50 MB** -* จัดเก็บนอก webroot -* ให้บริการไฟล์ผ่าน endpoint ที่ปลอดภัย /files/:attachment_id/download - -### **การควบคุมการเข้าถึง (Access Control)** - -การเข้าถึงไฟล์ไม่ใช่การเข้าถึงโดยตรง endpoint /files/:attachment_id/download จะต้อง: - -1. ค้นหาระเบียน attachment -2. ตรวจสอบว่า attachment_id นี้ เชื่อมโยงกับ Entity ใด (เช่น correspondence, circulation, shop_drawing_revision, contract_drawing) ผ่านตารางเชื่อม -3. ตรวจสอบว่าผู้ใช้มีสิทธิ์ (permission) ในการดู Entity ต้นทางนั้นๆ หรือไม่ - -## **🔟 การจัดการเลขที่เอกสาร (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 นี้จะเรียกใช้ Stored Procedure **sp_get_next_document_number** [cite: 2.9.3] ซึ่ง Procedure นี้จะจัดการ Database Transaction และ Row Lock (FOR UPDATE) ภายใน DB เพื่อรับประกันการป้องกัน Race Condition - -## **📊 การรายงานและการส่งออก (Reporting & Exports)** - -### **วิวสำหรับการรายงาน (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 เหล่านี้ทำหน้าที่เป็นแหล่งข้อมูลหลักสำหรับการรายงานฝั่งเซิร์ฟเวอร์และการส่งออกข้อมูล - -### **กฎการส่งออก (Export Rules)** - -* Export formats: CSV, Excel, PDF. -* จัดเตรียมมุมมองสำหรับพิมพ์ (Print view). -* รวมลิงก์ไปยังต้นทาง (เช่น /rfas/:id). - -## **🧮 ฟรอนต์เอนด์: รูปแบบ DataTable และฟอร์ม (Frontend: DataTable & Form Patterns)** - -### **DataTable (Server‑Side)** - -* Endpoint: /api/{module}?page=1\&pageSize=20\&sort=...\&filter=... -* ต้องรองรับ: การแบ่งหน้า (pagination), การเรียงลำดับ (sorting), การค้นหา (search), การกรอง (filters) -* แสดง revision ล่าสุดแบบ inline เสมอ (สำหรับ RFA/Drawing) - -### **มาตรฐานฟอร์ม (Form Standards)** - -* ต้องมีการใช้งาน Dropdowns แบบขึ้นต่อกัน (Dependent dropdowns) (ตามที่สคีมารองรับ): - * Project → Contract Drawing Volumes - * Contract Drawing Category → Sub-Category - * RFA (ประเภท Shop Drawing) → Shop Drawing Revisions ที่เชื่อมโยงได้ -* **(ใหม่)** การอัปโหลดไฟล์: ต้องรองรับ **Multi-file upload (Drag-and-Drop)** [cite: 5.7] -* **(ใหม่)** UI ต้องอนุญาตให้ผู้ใช้กำหนดว่าไฟล์ใดเป็น **"เอกสารหลัก"** หรือ "เอกสารแนบประกอบ" [cite: 5.7] -* ส่ง (Submit) ผ่าน API พร้อม feedback แบบ toast - -### **ข้อกำหนด 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])** - -## **🧭 แดชบอร์ดและฟีดกิจกรรม (Dashboard & Activity Feed)** - -### **การ์ดบนแดชบอร์ด (Dashboard Cards)** - -* แสดง Correspondences, RFAs, Circulations, Shop Drawing Revision ล่าสุด -* รวมสรุป KPI (เช่น "RFAs ที่รอการอนุมัติ", "Shop Drawing ที่รอการอนุมัติ") [cite: 5.3] -* รวมลิงก์ด่วนไปยังโมดูลต่างๆ - -### **ฟีดกิจกรรม (Activity Feed)** - -* แสดงรายการ v\audit\log\details ล่าสุด (10 รายการ) ที่เกี่ยวข้องกับผู้ใช้ - -// ตัวอย่าง API response -[ - { user: 'editor01', action: 'Updated RFA (LCBP3-RFA-001)', time: '2025-11-04T09:30Z' } -] - -## **🛡️ ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (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) -* **(ใหม่) Backup & Recovery [cite: 6.6]:** ต้องมีแผนสำรองข้อมูลทั้ง Database (MariaDB) และ File Storage (/share/dms-data) อย่างน้อยวันละ 1 ครั้ง -* **(ใหม่) Notification Strategy [cite: 6.7]:** ระบบแจ้งเตือน (Email/Line) ต้องถูก Trigger เมื่อมีเอกสารใหม่ส่งถึง, มีการมอบหมายงานใหม่ (Circulation), หรือ (ทางเลือก) เมื่องานเสร็จ/ใกล้ถึงกำหนด - -## **✅ มาตรฐานที่นำไปใช้แล้ว (จาก SQL v1.1.0) (Implemented Standards (from SQL v1.1.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) ถูกรวมอยู่ในสคริปต์สคีมาแล้ว - -## **🧩 การปรับปรุงที่แนะนำ (สำหรับอนาคต) (Recommended Enhancements (Future))** - -* ✅ สร้าง Background job (โดยใช้ **n8n** เพื่อเชื่อมต่อกับ **Line** [cite: 2.7] และ/หรือใช้สำหรับการแจ้งเตือน RFA ที่ใกล้ถึงกำหนด due_date [cite: 6.7]) -* ✅ เพิ่ม job ล้างข้อมูลเป็นระยะสำหรับ attachments ที่ไม่ถูกเชื่อมโยงกับ Entity ใดๆ เลย (ไฟล์กำพร้า) +# **Documents Management Sytem Version 1.4.0: แนวทางการพัฒนา FullStackJS** + +## **🧠 ปรัชญาทั่วไป** + +แนวทางปฏิบัติที่ดีที่สุดแบบครบวงจรสำหรับการพัฒนา NestJS Backend, NextJS Frontend และ Tailwind-based UI/UX ในสภาพแวดล้อม TypeScript มุ่งเน้นที่ ความชัดเจน (clarity), ความง่ายในการบำรุงรักษา (maintainability), ความสอดคล้องกัน (consistency) และ การเข้าถึงได้ (accessibility) ตลอดทั้งสแต็ก + +## **⚙️ แนวทางทั่วไปสำหรับ TypeScript** + +### **หลักการพื้นฐาน** + +* ใช้ **ภาษาอังกฤษ** สำหรับโค้ด +* ใช้ **ภาษาไทย** สำหรับ comment และเอกสารทั้งหมด +* กำหนดไทป์ (type) อย่างชัดเจนสำหรับตัวแปร, พารามิเตอร์ และค่าที่ส่งกลับ (return values) ทั้งหมด +* หลีกเลี่ยงการใช้ any; ให้สร้างไทป์ (types) หรืออินเทอร์เฟซ (interfaces) ที่กำหนดเอง +* ใช้ **JSDoc** สำหรับคลาส (classes) และเมธอด (methods) ที่เป็น public +* ส่งออก (Export) **สัญลักษณ์หลัก (main symbol) เพียงหนึ่งเดียว** ต่อไฟล์ +* หลีกเลี่ยงบรรทัดว่างภายในฟังก์ชัน +* ระบุ // File: path/filename ในบรรทัดแรกของทุกไฟล์ +* ระบุ // บันทึกการแก้ไข, หากมีการแก้ไขเพิ่มในอนาคต ให้เพิ่มบันทึก + +### **ข้อตกลงในการตั้งชื่อ (Naming Conventions)** + +| Entity (สิ่งที่ตั้งชื่อ) | Convention (รูปแบบ) | Example (ตัวอย่าง) | +| :---- | :---- | :---- | +| Classes | PascalCase | UserService | +| Property | snake_sase | 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) + +## **🧩 ฟังก์ชัน (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) ให้เป็นระดับเดียวในแต่ละฟังก์ชัน + +## **🧱 การจัดการข้อมูล (Data Handling)** + +* ห่อหุ้มข้อมูล (Encapsulate) ในไทป์แบบผสม (composite types) +* ใช้ **immutability** (การไม่เปลี่ยนแปลงค่า) ด้วย readonly และ as const +* ทำการตรวจสอบความถูกต้องของข้อมูล (Validations) ในคลาสหรือ DTOs ไม่ใช่ภายในฟังก์ชันทางธุรกิจ +* ตรวจสอบความถูกต้องของข้อมูลโดยใช้ DTOs ที่มีไทป์กำหนดเสมอ + +## **🧰 คลาส (Classes)** + +* ปฏิบัติตามหลักการ **SOLID** +* ควรใช้ **composition มากกว่า inheritance** (Prefer composition over inheritance) +* กำหนด **interfaces** สำหรับสัญญา (contracts) +* ให้คลาสมุ่งเน้นการทำงานเฉพาะอย่างและมีขนาดเล็ก (\< 200 บรรทัด, \< 10 เมธอด, \< 10 properties) + +## **🚨 การจัดการข้อผิดพลาด (Error Handling)** + +* ใช้ Exceptions สำหรับข้อผิดพลาดที่ไม่คาดคิด +* ดักจับ (Catch) ข้อผิดพลาดเพื่อแก้ไขหรือเพิ่มบริบท (context) เท่านั้น; หากไม่เช่นนั้น ให้ใช้ global error handlers +* ระบุข้อความข้อผิดพลาด (error messages) ที่มีความหมายเสมอ + +## **🧪 การทดสอบ (ทั่วไป) (Testing (General))** + +* ใช้รูปแบบ **Arrange–Act–Assert** +* ใช้ชื่อตัวแปรในการทดสอบที่สื่อความหมาย (inputData, expectedOutput) +* เขียน **unit tests** สำหรับ public methods ทั้งหมด +* จำลอง (Mock) การพึ่งพาภายนอก (external dependencies) +* เพิ่ม **acceptance tests** ต่อโมดูลโดยใช้รูปแบบ Given–When-Then + +## **🏗️ แบ็กเอนด์ (NestJS) (Backend (NestJS))** + +### **หลักการ** + +* **สถาปัตยกรรมแบบโมดูลาร์ (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 + +### **ฟังก์ชันหลัก (Core Functionalities)** + +* Global **filters** สำหรับการจัดการ exception +* **Middlewares** สำหรับการจัดการ request +* **Guards** สำหรับการอนุญาต (permissions) และ RBAC +* **Interceptors** สำหรับการแปลงข้อมูล response และการบันทึก log + +### **ข้อจำกัดในการ 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] + +### **โครงสร้างโมดูลตามโดเมน (Domain-Driven Module Structure)** + +เพื่อให้สอดคล้องกับสคีมา SQL (LCBP3-DMS) เราจะใช้โครงสร้างโมดูลแบบ **Domain-Driven (แบ่งตามขอบเขตธุรกิจ)** แทนการแบ่งตามฟังก์ชัน: + +1. **CommonModule:** + * เก็บ Services ที่ใช้ร่วมกัน เช่น DatabaseModule, FileStorageService (จัดการไฟล์ใน QNAP), AuditLogService, NotificationService + * จัดการ audit_logs + * NotificationService ต้องรองรับ Triggers ที่ระบุใน Requirement 6.7 [cite: 6.7] +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. **UserModule:** + * จัดการ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles + * **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ: + * สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3] +4. **ProjectModule:** + * จัดการ projects, organizations, contracts, project_parties, contract_parties +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] +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) สำหรับการส่งต่อเอกสารทั่วไประหว่างองค์กร +7. **RfaModule:** + * จัดการ rfas, rfa_revisions, rfa_items + * รับผิดชอบเวิร์กโฟลว์ **"RFA Workflows"** (rfa_workflows, rfa_workflow_templates, rfa_workflow_template_steps, rfa_status_transitions) สำหรับการอนุมัติเอกสารทางเทคนิค +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(ตารางเชื่อมไฟล์แนบ) +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)สำหรับการเวียนเอกสาร **ภายในองค์กร** +10. **TransmittalModule:** + * จัดการ transmittals และ transmittal_items +11. **SearchModule:** + * ให้บริการค้นหาขั้นสูง (Advanced Search) [cite: 6.2] โดยใช้ **Elasticsearch** เพื่อรองรับการค้นหาแบบ Full-text จากชื่อเรื่อง, รายละเอียด, เลขที่เอกสาร, ประเภท, วันที่, และ Tags + * ระบบจะใช้ Elasticsearch Engine ในการจัดทำดัชนีเพื่อการค้นหาข้อมูลเชิงลึกจากเนื้อหาของเอกสาร โดยข้อมูลจะถูกส่งไปทำดัชนีจาก Backend (NestJS) ทุกครั้งที่มีการสร้างหรือแก้ไขเอกสาร +12. **DocumentNumberingModule:** + * **สถานะ:** เป็น Module ภายใน (Internal Module) ไม่เปิด API สู่ภายนอก + * **หน้าที่:** ให้บริการ DocumentNumberingService ที่ Module อื่น (เช่น CorrespondenceModule) จะ Inject ไปใช้งาน + * **ตรรกะ:** รับผิดชอบการสร้างเลขที่เอกสาร โดยการเรียกใช้ Stored Procedure *sp_get_next_document_number** เพื่อป้องกัน Race Condition + +### **สถาปัตยกรรมระบบ (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 +│ ├── 📁 guards # Custom Guards (RBAC Guard) +│ ├── 📁 interceptors # Interceptors (Audit Log, Transform) +│ └── 📁 services # Shared Services (NotificationService) +├── 📁 modules +│ ├── 📁 user # UserModule (จัดการ Users, Roles, Permissions) +│ ├── 📁 project # ProjectModule (จัดการ Projects, Organizations, Contracts) +│ ├── 📁 correspondence # CorrespondenceModule (จัดการเอกสารโต้ตอบ) +│ ├── 📁 rfa # RfaModule (จัดการเอกสารขออนุมัติ) +│ ├── 📁 drawing # DrawingModule (จัดการแบบแปลน) +│ ├── 📁 circulation # CirculationModule (จัดการใบเวียน) +│ ├── 📁 transmittal # TransmittalModule (จัดการเอกสารนำส่ง) +│ ├── 📁 search # SearchModule (ค้นหาขั้นสูงด้วย Elasticsearch) +│ └── 📁 document-numbering # DocumentNumberingModule (Internal Module) +└── 📁 database # Database Migration & Seeding Scripts +``` + +### **เเทคโนโลยีที่ใช้ (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 | + +เราจะแบ่งการทดสอบเป็น 3 ระดับ โดยใช้ **Jest** และ @nestjs/testing: + +* **Unit Tests (การทดสอบหน่วยย่อย):** + * **เป้าหมาย:** ทดสอบ Logic ภายใน Service, Guard, หรือ Pipe โดยจำลอง (Mock) Dependencies ทั้งหมด + * **สิ่งที่ต้องทดสอบ:** Business Logic (เช่น การเปลี่ยนสถานะ Workflow, การตรวจสอบ Deadline) [cite: 2.9.1], ตรรกะการตรวจสอบสิทธิ์ (Auth Guard) ทั้ง 4 ระดับ +* **Integration Tests (การทดสอบการบูรณาการ):** + * **เป้าหมาย:** ทดสอบการทำงานร่วมกันของ Controller -> Service -> Repository (Database) + * **เทคนิค:** ใช้ **Test Database แยกต่างหาก** (ห้ามใช้ Dev DB) และใช้ supertest เพื่อยิง HTTP Request จริงไปยัง App + * **สิ่งที่ต้องทดสอบ:** การเรียก sp\get\next\document\number [cite: 2.9.3] และการทำงานของ Views (เช่น v_user_tasks) +* **E2E (End-to-End) Tests:** + * **เป้าหมาย:** ทดสอบ API Contract ว่า Response Body Shape ตรงตามเอกสาร Swagger เพื่อรับประกันทีม Frontend + +### **🗄️ 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 + +### **การไหลของข้อมูล (Data Flow)** + +1. Request: ผ่าน Nginx Proxy Manager -> NestJS Controller +2. Authentication: JWT Guard ตรวจสอบ Token และดึงข้อมูล User +3. Authorization: RBAC Guard (ใช้ CASL) ตรวจสอบสิทธิ์จาก Decorators (@RequirePermission) +4. Validation: Validation Pipe (ใช้ class-validator) ตรวจสอบ DTO +5. Business Logic: Service Layer ประมวลผลตรรกะทางธุรกิจ +6. Data Access: Repository Layer (ใช้ TypeORM) ติดต่อกับฐานข้อมูล MariaDB +7. Response: ส่งกลับไปยัง Frontend พร้อมสถานะและข้อมูลที่เหมาะสม + +# **🖥️ ฟรอนต์เอนด์ (NextJS / React / UI) (Frontend (NextJS / React / UI))** + +### **โปรไฟล์นักพัฒนา (Developer Profile)** + +วิศวกร TypeScript + React/NextJS ระดับ Senior +เชี่ยวชาญ TailwindCSS, Shadcn/UI, และ Radix สำหรับการพัฒนา UI + +### **แนวทางการพัฒนาโค้ด (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 โมดูลที่จำเป็นต้องใช้อย่างชัดเจนเสมอ + +### **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 + +### **การตรวจสอบฟอร์มและข้อผิดพลาด (Form Validation & Errors)** + +* ใช้ไลบรารีฝั่ง client เช่น zod และ react-hook-form +* แสดงข้อผิดพลาดด้วย **alert components** หรือข้อความ inline +* ต้องมี labels, placeholders, และข้อความ feedback + +### **🧪 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]) + +### **🗄️ 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]) + +# **🔗 แนวทางการบูรณาการ 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 | + +## **🗂️ ข้อตกลงเฉพาะสำหรับ DMS (LCBP3-DMS)** + +ส่วนนี้ขยายแนวทาง FullStackJS ทั่วไปสำหรับโปรเจกต์ **LCBP3-DMS** โดยมุ่งเน้นไปที่เวิร์กโฟลว์การอนุมัติเอกสาร (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation) + +### **🧩 RBAC และการควบคุมสิทธิ์ (RBAC & Permission Control)** + +ใช้ Decorators เพื่อบังคับใช้สิทธิ์การเข้าถึง โดยอ้างอิงสิทธิ์จากตาราง permissions + +@RequirePermission('rfas.respond') // ต้องตรงกับ 'permission\code' +@Put(':id') +updateRFA(@Param('id') id: string) { + return this.rfaService.update(id); +} + +### **Roles (บทบาท)** + +* **Superadmin**: ไม่มีข้อจำกัดใดๆ [cite: 4.3] +* **Admin**: มีสิทธิ์เต็มที่ในองค์กร [cite: 4.3] +* **Document Control**: เพิ่ม/แก้ไข/ลบ เอกสารในองค์กร [cite: 4.3] +* **Editor**: สามารถ เพิ่ม/แก้ไข เอกสารที่กำหนด [cite: 4.3] +* **Viewer**: สามารถดู เอกสาร [cite: 4.3] + +### **ตัวอย่าง 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] + +## **🧾 มาตรฐาน 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) | + +## **📂 การจัดการไฟล์ (File Handling) (ปรับปรุงใหม่)** + +### **มาตรฐานการอัปโหลดไฟล์ (File Upload Standard)** + +* **ตรรกะใหม่:** การอัปโหลดไฟล์ทั้งหมดจะถูกจัดการโดย FileStorageService และบันทึกข้อมูลไฟล์ลงในตาราง attachments (ตารางกลาง) +* ไฟล์จะถูกเชื่อมโยงไปยัง 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 +* ขนาดสูงสุด: **50 MB** +* จัดเก็บนอก webroot +* ให้บริการไฟล์ผ่าน endpoint ที่ปลอดภัย /files/:attachment_id/download + +### **การควบคุมการเข้าถึง (Access Control)** + +การเข้าถึงไฟล์ไม่ใช่การเข้าถึงโดยตรง endpoint /files/:attachment_id/download จะต้อง: + +1. ค้นหาระเบียน attachment +2. ตรวจสอบว่า attachment_id นี้ เชื่อมโยงกับ Entity ใด (เช่น correspondence, circulation, shop_drawing_revision, contract_drawing) ผ่านตารางเชื่อม +3. ตรวจสอบว่าผู้ใช้มีสิทธิ์ (permission) ในการดู Entity ต้นทางนั้นๆ หรือไม่ + +## **🔟 การจัดการเลขที่เอกสาร (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 นี้จะเรียกใช้ Stored Procedure **sp_get_next_document_number** [cite: 2.9.3] ซึ่ง Procedure นี้จะจัดการ Database Transaction และ Row Lock (FOR UPDATE) ภายใน DB เพื่อรับประกันการป้องกัน Race Condition + +## **📊 การรายงานและการส่งออก (Reporting & Exports)** + +### **วิวสำหรับการรายงาน (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 เหล่านี้ทำหน้าที่เป็นแหล่งข้อมูลหลักสำหรับการรายงานฝั่งเซิร์ฟเวอร์และการส่งออกข้อมูล + +### **กฎการส่งออก (Export Rules)** + +* Export formats: CSV, Excel, PDF. +* จัดเตรียมมุมมองสำหรับพิมพ์ (Print view). +* รวมลิงก์ไปยังต้นทาง (เช่น /rfas/:id). + +## **🧮 ฟรอนต์เอนด์: รูปแบบ DataTable และฟอร์ม (Frontend: DataTable & Form Patterns)** + +### **DataTable (Server‑Side)** + +* Endpoint: /api/{module}?page=1\&pageSize=20\&sort=...\&filter=... +* ต้องรองรับ: การแบ่งหน้า (pagination), การเรียงลำดับ (sorting), การค้นหา (search), การกรอง (filters) +* แสดง revision ล่าสุดแบบ inline เสมอ (สำหรับ RFA/Drawing) + +### **มาตรฐานฟอร์ม (Form Standards)** + +* ต้องมีการใช้งาน Dropdowns แบบขึ้นต่อกัน (Dependent dropdowns) (ตามที่สคีมารองรับ): + * Project → Contract Drawing Volumes + * Contract Drawing Category → Sub-Category + * RFA (ประเภท Shop Drawing) → Shop Drawing Revisions ที่เชื่อมโยงได้ +* **(ใหม่)** การอัปโหลดไฟล์: ต้องรองรับ **Multi-file upload (Drag-and-Drop)** [cite: 5.7] +* **(ใหม่)** UI ต้องอนุญาตให้ผู้ใช้กำหนดว่าไฟล์ใดเป็น **"เอกสารหลัก"** หรือ "เอกสารแนบประกอบ" [cite: 5.7] +* ส่ง (Submit) ผ่าน API พร้อม feedback แบบ toast + +### **ข้อกำหนด 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])** + +## **🧭 แดชบอร์ดและฟีดกิจกรรม (Dashboard & Activity Feed)** + +### **การ์ดบนแดชบอร์ด (Dashboard Cards)** + +* แสดง Correspondences, RFAs, Circulations, Shop Drawing Revision ล่าสุด +* รวมสรุป KPI (เช่น "RFAs ที่รอการอนุมัติ", "Shop Drawing ที่รอการอนุมัติ") [cite: 5.3] +* รวมลิงก์ด่วนไปยังโมดูลต่างๆ + +### **ฟีดกิจกรรม (Activity Feed)** + +* แสดงรายการ v\audit\log\details ล่าสุด (10 รายการ) ที่เกี่ยวข้องกับผู้ใช้ + +// ตัวอย่าง API response +[ + { user: 'editor01', action: 'Updated RFA (LCBP3-RFA-001)', time: '2025-11-04T09:30Z' } +] + +## **🛡️ ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (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) +* **(ใหม่) Backup & Recovery [cite: 6.6]:** ต้องมีแผนสำรองข้อมูลทั้ง Database (MariaDB) และ File Storage (/share/dms-data) อย่างน้อยวันละ 1 ครั้ง +* **(ใหม่) Notification Strategy [cite: 6.7]:** ระบบแจ้งเตือน (Email/Line) ต้องถูก Trigger เมื่อมีเอกสารใหม่ส่งถึง, มีการมอบหมายงานใหม่ (Circulation), หรือ (ทางเลือก) เมื่องานเสร็จ/ใกล้ถึงกำหนด + +## **✅ มาตรฐานที่นำไปใช้แล้ว (จาก SQL v1.1.0) (Implemented Standards (from SQL v1.1.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) ถูกรวมอยู่ในสคริปต์สคีมาแล้ว + +## **🧩 การปรับปรุงที่แนะนำ (สำหรับอนาคต) (Recommended Enhancements (Future))** + +* ✅ สร้าง Background job (โดยใช้ **n8n** เพื่อเชื่อมต่อกับ **Line** [cite: 2.7] และ/หรือใช้สำหรับการแจ้งเตือน RFA ที่ใกล้ถึงกำหนด due_date [cite: 6.7]) +* ✅ เพิ่ม job ล้างข้อมูลเป็นระยะสำหรับ attachments ที่ไม่ถูกเชื่อมโยงกับ Entity ใดๆ เลย (ไฟล์กำพร้า) diff --git a/docs/LCBP3-DMS_V1_4_0_requirements.md b/docs/LCBP3-DMS_V1_4_0_requirements.md index 6de7ee9..464b480 100644 --- a/docs/LCBP3-DMS_V1_4_0_requirements.md +++ b/docs/LCBP3-DMS_V1_4_0_requirements.md @@ -1,245 +1,245 @@ -# **📝 Documents Management Sytem Version 1.4.0: Application Requirements Specification** - -## **📌 1. วัตถุประสงค์** - -สร้างเว็บแอปพลิเคชั่นสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System)ที่สามารถจัดการและควบคุม การสื่อสารด้วยเอกสารที่ซับซ้อน อย่างมีประสิทธิภาพ - -- มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร -- ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล -- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ - -## **🛠️ 2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)** - -ใช้สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา, Domain: np-dms.work, มี fix ip, รัน docker command ใน application ของ Container Station ได้โดยตรง, ประกอบด้วย - -- **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 on Windows 11 - - Domain: np-dms.work, www.np-dms.work - - ip: 159.192.126.103 - - Docker Network: ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 เพื่อให้สามารถสื่อสารกันได้ - - Data Storage: /share/dms-data บน QNAP - - ข้อจำกัด: ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น -- **2.2. Code Hosting:** - - Application name: git - - Service: Gitea (Self-hosted on QNAP) - - Service name: gitea - - Domain: git.np-dms.work - - หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน -- **2.3. Backend / Data Platform:** - - Application name: lcbp3-backend - - Service: NestJS - - Service name: backend - - Domain: backend.np-dms.work - - Framework: NestJS (Node.js, TypeScript, ESM) - - หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ -- **2.4. Database:** - - Application name: lcbp3-db - - Service: mariadb:10.11 - - Service name: mariadb - - Domain: db.np-dms.work - - หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด - - Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล -- **2.5. Database management:** - - Application name: lcbp3-db - - Service: phpmyadmin:5-apache - - Service name: pma - - Domain: pma.np-dms.work - - หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI -- **2.6. Frontend:** - - Application name: lcbp3-frontend - - Service: next.js - - 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 -- **2.7. Workflow automation:** - - Application name: lcbp3-n8n - - Service: n8nio/n8n:latest - - Service name: n8n - - Domain: n8n.np-dms.work - - หน้าที่: จัดการ workflow ระหว่าง Backend และ Line -- **2.8. Reverse Proxy:** - - 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) ให้อัตโนมัติ -- **2.9. การจัดการตรรกะทางธุรกิจ (Business Logic Implementation):** - - 2.9.1. ตรรกะทางธุรกิจที่ซับซ้อนทั้งหมด (เช่น การเปลี่ยนสถานะ Workflow [cite: 3.5.4, 3.6.5], การบังคับใช้สิทธิ์ [cite: 4.4], การตรวจสอบ Deadline [cite: 3.2.5]) **จะถูกจัดการในฝั่ง Backend (NestJS)** [cite: 2.3] เพื่อให้สามารถบำรุงรักษาและทดสอบได้ง่าย (Testability) - - 2.9.2. **จะไม่มีการใช้ SQL Triggers** เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก - - 2.9.3. **ข้อยกเว้น:** ตรรกะเดียวที่จะอยู่ในฐานข้อมูลคือ **Stored Procedure** สำหรับการสร้างเลขที่เอกสาร (Document Numbering) [cite: 3.10] เพื่อป้องกันการซ้ำซ้อนของข้อมูล (Race Condition) - -## **📦 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. Routings : ต้องรองรับกระบวนการส่งต่อเอกสาร (Routing) ตามลำดับ เช่น - - ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Wouting ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) - - 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.6.5. Workflow การอนุมัติ: ต้องรองรับกระบวนการอนุมัติที่ซับซ้อนและเป็นลำดับ เช่น - - ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Workflow ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) - - 3.6.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. การจัดเก็บ: (ปรับปรุงตามสถาปัตยกรรมใหม่)** - - เอกสารและไฟล์แนบทั้งหมดจะถูกจัดเก็บในโฟลเดอร์บน Server (/share/dms-data/) [cite: 2.1] - - ข้อมูล Metadata ของไฟล์ (เช่น ชื่อไฟล์, ขนาด, path) จะถูกเก็บในตาราง attachments (ตารางกลาง) - - ไฟล์จะถูกเชื่อมโยงกับเอกสารประเภทต่างๆ ผ่านตารางเชื่อม (Junction tables) เช่น correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments ,และ contracy_drawing_attachments - - สถาปัตยกรรมแบบรวมศูนย์นี้ _แทนที่_ แนวคิดเดิมที่จะแยกโฟลเดอร์ตามประเภทเอกสาร เพื่อรองรับการขยายระบบที่ดีกว่า -- **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}) โดยกำหนดแยกตามโครงการและประเภทเอกสาร - -## **🔐 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. กระบวนการเริ่มต้นใช้งาน (Onboarding Workflow) ที่สมบูรณ์** - - - 4.1. **สร้างองค์กร (Organization)** - - - **Superadmin** สร้างองค์กรใหม่ (เช่น บริษัท A) - - **Superadmin** แต่งตั้งผู้ใช้อย่างน้อย 1 คนให้เป็น **Org Admin** หรือ **Document Control** ของบริษัท A - - - 4.2. **เพิ่มผู้ใช้ในองค์กร** - - - **Org Admin** ของบริษัท A เพิ่มผู้ใช้อื่นๆ (Editor, Viewer) เข้ามาในองค์กรของตน - - - 4.3. **มอบหมายผู้ใช้ให้กับโครงการ (Project)** - - - **Project Manager** ของโครงการ X (ซึ่งอาจมาจากบริษัท A หรือบริษัทอื่น) ทำการ "เชิญ" หรือ "มอบหมาย" ผู้ใช้จากองค์กรต่างๆ ที่เกี่ยวข้องเข้ามาในโครงการ X - - ในขั้นตอนนี้ **Project Manager** จะกำหนด **บทบาทระดับโครงการ** (เช่น Project Member, หรืออาจไม่มีบทบาทพิเศษ ให้ใช้สิทธิ์จากระดับองค์กรไปก่อน) - - - 4.4. **เมอบหมายผู้ใช้ให้กับสัญญา (Contract)** - - **Contract Admin** ของสัญญา Y (ซึ่งเป็นส่วนหนึ่งของโครงการ X) ทำการเลือกผู้ใช้ที่อยู่ในโครงการ X แล้ว มอบหมายให้เข้ามาในสัญญา Y - - ในขั้นตอนนี้ **Contract Admin** จะกำหนด **บทบาทระดับสัญญา** (เช่น Contract Member) และสิทธิ์เฉพาะที่จำเป็น - -- **4.5. การจัดการข้อมูลหลัก (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 | - -## **👥 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 ที่ผู้ใช้ต้องดำเนินการ -- **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) - -## **6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)** - -- **6.1. การบันทึกการกระทำ (Audit Log):** ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง -- **6.2. การค้นหา (Search):** ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag -- **6.3. การทำรายงาน (Reporting):** สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้ -- **6.4. ประสิทธิภาพ (Performance):** มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก -- **6.5. ความปลอดภัย (Security):** - - มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force - - การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด -- **6.6. การสำรองข้อมูลและการกู้คืน (Backup & Recovery):** - - ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง - - ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้ -- **6.7. กลยุทธ์การแจ้งเตือน (Notification Strategy):** - - ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ ดังนี้: - 1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา - 2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา - 3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ) - 4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5] +# **📝 Documents Management Sytem Version 1.4.0: Application Requirements Specification** + +## **📌 1. วัตถุประสงค์** + +สร้างเว็บแอปพลิเคชั่นสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System)ที่สามารถจัดการและควบคุม การสื่อสารด้วยเอกสารที่ซับซ้อน อย่างมีประสิทธิภาพ + +- มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร +- ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล +- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์ + +## **🛠️ 2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)** + +ใช้สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา, Domain: np-dms.work, มี fix ip, รัน docker command ใน application ของ Container Station ได้โดยตรง, ประกอบด้วย + +- **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 on Windows 11 + - Domain: np-dms.work, www.np-dms.work + - ip: 159.192.126.103 + - Docker Network: ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 เพื่อให้สามารถสื่อสารกันได้ + - Data Storage: /share/dms-data บน QNAP + - ข้อจำกัด: ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น +- **2.2. Code Hosting:** + - Application name: git + - Service: Gitea (Self-hosted on QNAP) + - Service name: gitea + - Domain: git.np-dms.work + - หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน +- **2.3. Backend / Data Platform:** + - Application name: lcbp3-backend + - Service: NestJS + - Service name: backend + - Domain: backend.np-dms.work + - Framework: NestJS (Node.js, TypeScript, ESM) + - หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ +- **2.4. Database:** + - Application name: lcbp3-db + - Service: mariadb:10.11 + - Service name: mariadb + - Domain: db.np-dms.work + - หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด + - Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล +- **2.5. Database management:** + - Application name: lcbp3-db + - Service: phpmyadmin:5-apache + - Service name: pma + - Domain: pma.np-dms.work + - หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI +- **2.6. Frontend:** + - Application name: lcbp3-frontend + - Service: next.js + - 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 +- **2.7. Workflow automation:** + - Application name: lcbp3-n8n + - Service: n8nio/n8n:latest + - Service name: n8n + - Domain: n8n.np-dms.work + - หน้าที่: จัดการ workflow ระหว่าง Backend และ Line +- **2.8. Reverse Proxy:** + - 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) ให้อัตโนมัติ +- **2.9. การจัดการตรรกะทางธุรกิจ (Business Logic Implementation):** + - 2.9.1. ตรรกะทางธุรกิจที่ซับซ้อนทั้งหมด (เช่น การเปลี่ยนสถานะ Workflow [cite: 3.5.4, 3.6.5], การบังคับใช้สิทธิ์ [cite: 4.4], การตรวจสอบ Deadline [cite: 3.2.5]) **จะถูกจัดการในฝั่ง Backend (NestJS)** [cite: 2.3] เพื่อให้สามารถบำรุงรักษาและทดสอบได้ง่าย (Testability) + - 2.9.2. **จะไม่มีการใช้ SQL Triggers** เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก + - 2.9.3. **ข้อยกเว้น:** ตรรกะเดียวที่จะอยู่ในฐานข้อมูลคือ **Stored Procedure** สำหรับการสร้างเลขที่เอกสาร (Document Numbering) [cite: 3.10] เพื่อป้องกันการซ้ำซ้อนของข้อมูล (Race Condition) + +## **📦 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. Routings : ต้องรองรับกระบวนการส่งต่อเอกสาร (Routing) ตามลำดับ เช่น + - ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Wouting ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) + - 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.6.5. Workflow การอนุมัติ: ต้องรองรับกระบวนการอนุมัติที่ซับซ้อนและเป็นลำดับ เช่น + - ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Workflow ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป) + - 3.6.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. การจัดเก็บ: (ปรับปรุงตามสถาปัตยกรรมใหม่)** + - เอกสารและไฟล์แนบทั้งหมดจะถูกจัดเก็บในโฟลเดอร์บน Server (/share/dms-data/) [cite: 2.1] + - ข้อมูล Metadata ของไฟล์ (เช่น ชื่อไฟล์, ขนาด, path) จะถูกเก็บในตาราง attachments (ตารางกลาง) + - ไฟล์จะถูกเชื่อมโยงกับเอกสารประเภทต่างๆ ผ่านตารางเชื่อม (Junction tables) เช่น correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments ,และ contracy_drawing_attachments + - สถาปัตยกรรมแบบรวมศูนย์นี้ _แทนที่_ แนวคิดเดิมที่จะแยกโฟลเดอร์ตามประเภทเอกสาร เพื่อรองรับการขยายระบบที่ดีกว่า +- **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}) โดยกำหนดแยกตามโครงการและประเภทเอกสาร + +## **🔐 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. กระบวนการเริ่มต้นใช้งาน (Onboarding Workflow) ที่สมบูรณ์** + + - 4.1. **สร้างองค์กร (Organization)** + + - **Superadmin** สร้างองค์กรใหม่ (เช่น บริษัท A) + - **Superadmin** แต่งตั้งผู้ใช้อย่างน้อย 1 คนให้เป็น **Org Admin** หรือ **Document Control** ของบริษัท A + + - 4.2. **เพิ่มผู้ใช้ในองค์กร** + + - **Org Admin** ของบริษัท A เพิ่มผู้ใช้อื่นๆ (Editor, Viewer) เข้ามาในองค์กรของตน + + - 4.3. **มอบหมายผู้ใช้ให้กับโครงการ (Project)** + + - **Project Manager** ของโครงการ X (ซึ่งอาจมาจากบริษัท A หรือบริษัทอื่น) ทำการ "เชิญ" หรือ "มอบหมาย" ผู้ใช้จากองค์กรต่างๆ ที่เกี่ยวข้องเข้ามาในโครงการ X + - ในขั้นตอนนี้ **Project Manager** จะกำหนด **บทบาทระดับโครงการ** (เช่น Project Member, หรืออาจไม่มีบทบาทพิเศษ ให้ใช้สิทธิ์จากระดับองค์กรไปก่อน) + + - 4.4. **เมอบหมายผู้ใช้ให้กับสัญญา (Contract)** + - **Contract Admin** ของสัญญา Y (ซึ่งเป็นส่วนหนึ่งของโครงการ X) ทำการเลือกผู้ใช้ที่อยู่ในโครงการ X แล้ว มอบหมายให้เข้ามาในสัญญา Y + - ในขั้นตอนนี้ **Contract Admin** จะกำหนด **บทบาทระดับสัญญา** (เช่น Contract Member) และสิทธิ์เฉพาะที่จำเป็น + +- **4.5. การจัดการข้อมูลหลัก (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 | + +## **👥 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 ที่ผู้ใช้ต้องดำเนินการ +- **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) + +## **6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)** + +- **6.1. การบันทึกการกระทำ (Audit Log):** ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง +- **6.2. การค้นหา (Search):** ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag +- **6.3. การทำรายงาน (Reporting):** สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้ +- **6.4. ประสิทธิภาพ (Performance):** มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก +- **6.5. ความปลอดภัย (Security):** + - มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force + - การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด +- **6.6. การสำรองข้อมูลและการกู้คืน (Backup & Recovery):** + - ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง + - ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้ +- **6.7. กลยุทธ์การแจ้งเตือน (Notification Strategy):** + - ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ ดังนี้: + 1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา + 2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา + 3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ) + 4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5] diff --git a/docs/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md b/docs/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md new file mode 100644 index 0000000..299a42b --- /dev/null +++ b/docs/LCBP3-DMS_V1_4_1_Backend_Development_Plan.md @@ -0,0 +1,1052 @@ +# 📋 **แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.1 (ปรับปรุงโดย deepseek)** + +**ปรับปรุงตาม Requirements v1.4.0 ที่อัปเดตแล้ว* +**Routing และ รูปแบบ JSON details** + +--- + +## 🎯 **ภาพรวมโครงการ** + +พัฒนา Backend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่รองรับการจัดการเอกสารที่ซับซ้อน มีระบบ Workflow การอนุมัติ และการควบคุมสิทธิ์แบบ RBAC 4 ระดับ พร้อมมาตรการความปลอดภัยที่ทันสมัย + +--- + +## 📐 **สถาปัตยกรรมระบบ** + +### **Technology Stack** + +- **Framework:** NestJS (TypeScript, ESM) +- **Database:** MariaDB 10.11 +- **ORM:** TypeORM +- **Authentication:** JWT + Passport +- **Authorization:** CASL (RBAC 4-level) +- **File Upload:** Multer + Virus Scanning (ClamAV) +- **Search:** Elasticsearch +- **Notification:** Nodemailer + n8n (Line Integration) +- **Caching:** Redis +- **Resilience:** Circuit Breaker, Retry Patterns +- **Security:** Helmet, CSRF Protection, Rate Limiting +- **Monitoring:** Winston, Health Checks, Metrics +- **Scheduling:** @nestjs/schedule (Cron Jobs) +- **Documentation:** Swagger + +### **โครงสร้างโมดูล (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) +│ ├── guards/ # RBAC Guard, RateLimitGuard +│ ├── interceptors/ # Audit, Transform, Performance +│ ├── resilience/ # Circuit Breaker, Retry Patterns +│ ├── security/ # Input Validation, XSS Protection +│ └── services/ # Notification, Caching, Monitoring +├── 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 +│ └── document-numbering/ # Internal Service (Redis Locking) +└── database/ # Migrations & Seeds +``` + +--- + +## 🗓️ **แผนการพัฒนาแบบ Phase-Based** + +### **Dependency Diagram (ภาพรวม)** + + ```mermaid + %% Phase 0: Infrastructure + subgraph Phase 0 [Phase 0: Infrastructure Setup] + T0_1[T0.1: Setup QNAP Container Station] + T0_2[T0.2: Initialize NestJS Project] + T0_3[T0.3: Setup Database Connection] + T0_4[T0.4: Setup Git Repository] + end + + %% Phase 1: Core Foundation + subgraph Phase 1 [Phase 1: Core Foundation & Security] + T1_1[T1.1: CommonModule - Base Infrastructure] + T1_2[T1.2: AuthModule - JWT Authentication] + T1_3[T1.3: UserModule - User Management] + T1_4[T1.4: RBAC Guard - 4-Level Authorization] + T1_5[T1.5: ProjectModule - Base Structures] + end + + %% Phase 2: Security & File Management + subgraph Phase 2 [Phase 2: Security & File Management] + T2_1[T2.1: MasterModule - Master Data Management] + T2_2[T2.2: FileStorageService - Secure File Management] + T2_3[T2.3: DocumentNumberingModule - App-Level Locking] + T2_4[T2.4: SecurityModule - Enhanced Security] + T2_5[T2.5: JSON Details & Schema Management] + end + + %% Phase 3: Correspondence & RFA Core + subgraph Phase 3 [Phase 3: Correspondence & RFA Core] + T3_1[T3.1: CorrespondenceModule - Basic CRUD] + T3_2[T3.2: CorrespondenceModule - Advanced Features] + T3_3[T3.3: RfaModule - Basic CRUD] + T3_4[T3.4: Correspondence Routing] + end + + %% Phase 4: Drawing Management + subgraph Phase 4 [Phase 4: Drawing Management] + T4_1[T4.1: DrawingModule - Contract Drawings] + T4_2[T4.2: DrawingModule - Shop Drawings] + end + + %% Phase 5: Workflow Systems & Resilience + subgraph Phase 5 [Phase 5: Workflow Systems & Resilience] + T5_1[T5.1: RfaModule - Workflow Implementation] + T5_2[T5.2: CirculationModule - Internal Routing] + T5_3[T5.3: TransmittalModule - Document Forwarding] + end + + %% Phase 6: Advanced Features & Monitoring + subgraph Phase 6 [Phase 6: Advanced Features & Monitoring] + T6_1[T6.1: SearchModule - Elasticsearch Integration] + T6_2[T6.2: NotificationModule - Email & Line] + T6_3[T6.3: MonitoringModule - Observability] + T6_4[T6.4: ResilienceModule - Circuit Breaker & Retry] + end + + %% Phase 7: Testing & Optimization + subgraph Phase 7 [Phase 7: Testing & Optimization] + T7_1[T7.1: Unit Testing] + T7_2[T7.2: Integration Testing] + T7_3[T7.3: E2E Testing] + T7_4[T7.4: Performance Testing] + T7_5[T7.5: Security Testing] + T7_6[T7.6: Performance Optimization] + end + + %% Phase 8: Documentation & Deployment + subgraph Phase 8 [Phase 8: Documentation & Deployment] + T8_1[T8.1: API Documentation] + T8_2[T8.2: Technical Documentation] + T8_3[T8.3: Security Hardening] + T8_4[T8.4: Deployment Preparation] + T8_5[T8.5: Production Deployment] + T8_6[T8.6: Handover to Frontend Team] + end + + %% Dependencies + T0_1 --> T0_2 + T0_2 --> T0_3 + T0_3 --> T0_4 + + T0_2 --> T1_1 + T0_3 --> T1_1 + T1_1 --> T1_2 + T1_1 --> T1_3 + T1_1 --> T1_4 + T1_1 --> T1_5 + T1_2 --> T1_3 + T1_3 --> T1_4 + + T0_3 --> T2_1 + T1_1 --> T2_1 + T1_5 --> T2_1 + T1_1 --> T2_2 + T1_4 --> T2_2 + T1_1 --> T2_3 + T1_1 --> T2_4 + T1_1 --> T2_5 + + T1_1 --> T3_1 + T1_2 --> T3_1 + T1_3 --> T3_1 + T1_4 --> T3_1 + T1_5 --> T3_1 + T2_3 --> T3_1 + T2_2 --> T3_1 + T2_5 --> T3_1 + T3_1 --> T3_2 + T3_1 --> T3_3 + T1_5 --> T3_3 + T3_1 --> T3_4 + T2_5 --> T3_4 + + T1_1 --> T4_1 + T1_2 --> T4_1 + T1_4 --> T4_1 + T1_5 --> T4_1 + T2_2 --> T4_1 + T4_1 --> T4_2 + + T3_3 --> T5_1 + T4_2 --> T5_1 + T2_5 --> T5_1 + T3_1 --> T5_2 + T2_5 --> T5_2 + T3_1 --> T5_3 + + T3_1 --> T6_1 + T3_3 --> T6_1 + T4_2 --> T6_1 + T5_2 --> T6_1 + T5_3 --> T6_1 + T1_1 --> T6_2 + T6_4 --> T6_2 + T1_1 --> T6_3 + T1_1 --> T6_4 + + %% All development phases must be complete before testing + T1_5 --> T7_1 + T2_5 --> T7_1 + T3_4 --> T7_1 + T4_2 --> T7_1 + T5_3 --> T7_1 + T6_4 --> T7_1 + T7_1 --> T7_2 + T7_2 --> T7_3 + T7_3 --> T7_4 + T7_4 --> T7_5 + T7_5 --> T7_6 + + %% Testing must be complete before deployment + T7_6 --> T8_1 + T8_1 --> T8_2 + T8_2 --> T8_3 + T8_3 --> T8_4 + T8_4 --> T8_5 + T8_5 --> T8_6 + ``` + +## **Phase 0: Infrastructure Setup (สัปดาห์ที่ 1)** + +**Milestone:** สร้างโครงสร้างพื้นฐานและเชื่อมต่อ Services พร้อม Security Baseline + +### **Phase 0: Tasks** + +- **[✅] T0.1 Setup QNAP Container Station** + - สร้าง Docker Network: `lcbp3` + - Setup docker-compose.yml สำหรับ: + - MariaDB (db.np-dms.work) + - PHPMyAdmin (pma.np-dms.work) + - Redis (cache.np-dms.work) + - Elasticsearch (search.np-dms.work) + - Backend (backend.np-dms.work) + - Nginx Proxy Manager (npm.np-dms.work) + - กำหนด Environment Variables ใน docker-compose.yml (ไม่ใช้ .env) + - **Security:** Setup network segmentation และ firewall rules + - [ ] **Deliverable:** Services ทั้งหมดรันได้และเชื่อมต่อกันผ่าน Network + - **Dependencies:** None (Task เริ่มต้น) + +- **[✅] T0.2 Initialize NestJS Project** + - สร้างโปรเจกต์ใหม่ด้วย Nest CLI + - ติดตั้ง Dependencies: + + ```bash + # Core + npm install @nestjs/core @nestjs/common @nestjs/platform-express + npm install @nestjs/typeorm typeorm mysql2 + npm install @nestjs/config class-validator class-transformer + + # Auth & Security + npm install @nestjs/jwt @nestjs/passport passport passport-jwt + npm install casl helmet csurf rate-limiter-flexible bcrypt crypto + + # File & Search + npm install multer @nestjs/elasticsearch @elastic/elasticsearch + npm install clamscan @types/multer + + # Resilience & Caching + npm install @nestjs/cache-manager cache-manager cache-manager-redis-store + npm install @nestjs/circuit-breaker + + # Monitoring & Scheduling + npm install @nestjs/schedule @nestjs/monitoring winston + + # Documentation + npm install @nestjs/swagger + + # Development + npm install --save-dev @nestjs/testing jest @types/jest supertest + npm install --save-dev @types/passport-jwt @types/bcrypt @types/crypto + ``` + + - Setup โครงสร้างโฟลเดอร์ตาม Domain-Driven Architecture + - **Security:** Initialize security headers และ CORS configuration + - [ ] **Deliverable:** Project Structure พร้อม, แสดง Swagger ที่ `/api` + - **Dependencies:** T0.1 (ต้องมี Docker Network และ Environment พร้อมก่อนสร้าง Project) + +- **[✅] T0.3 Setup Database Connection** + + - Import SQL Schema v1.4.0 เข้า MariaDB + - Run Seed Data (organizations, users, roles, permissions) + - Configure TypeORM ใน AppModule + - **Security:** Setup database connection encryption + - ทดสอบ Connection + - [ ] **Deliverable:** Database พร้อมใช้งาน, มี Seed Data + - **Dependencies:** T0.1 (ต้องมี MariaDB Container รันแล้ว), T0.2 (ต้องมี NestJS Project พร้อมสำหรับตั้งค่า TypeORM) + +- **[✅] 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.2 (ต้องมี Project และโครงสร้างพื้นฐานก่อนจะ Commit) + +--- + +## **Phase 1: Core Foundation & Security (สัปดาห์ที่ 2-3)** + +**Milestone:** ระบบ Authentication, Authorization, Security Baseline และ Base Entities + +### **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 + - [ ] สร้าง RequestContextService - สำหรับเก็บข้อมูลระหว่าง Request + - [ ] สร้าง ConfigService - Centralized configuration management + - [ ] สร้าง CryptoService - สำหรับ encryption/decryption + - [ ] **Security:** Implement input validation pipeline + - [ ] **Deliverable:** Common Services พร้อมใช้ + - [ ] **Dependencies:** T0.2, T0.3 (ต้องมี Project และ Database Connection พร้อมสำหรับสร้าง Base Entity และ Services) + +- **[ ] 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 (ต้องใช้ Base Entity, ConfigService, CryptoService), T0.3 (ต้องเชื่อมต่อกับ User table) + +- **[ ] T1.3 UserModule - User Management** + - [ ] สร้าง Entities: User, Role, Permission, UserRole, UserAssignment + - [ ] สร้าง UserService CRUD (พร้อม soft delete) + - [ ] สร้าง RoleService CRUD + - [ ] สร้าง PermissionService (Read-Only, จาก Seed) + - [ ] สร้าง UserAssignmentService - สำหรับจัดการ user assignments ตาม scope + - [ ] สร้าง 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 ได้ + - [ ] **Dependencies:** T1.1 (Base Entity, Global Exception Filter), T1.2 (สำหรับ Authentication และ Authorization) + +- **[ ] 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 (Decorators, RequestContextService), T1.3 (ต้องมีโครงสร้าง User, Role, Permission ที่สมบูรณ์) + +- **[ ] 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 (Base Entity, Security), T1.2 (สำหรับการป้องกันการเข้าถึง), T0.3 (ต้องเชื่อมต่อกับ project/organization tables) + +--- + +## **Phase 2: Security & File Management (สัปดาห์ที่ 4)** + +**Milestone:** Master Data, ระบบจัดการไฟล์ที่มีความปลอดภัย, Document Numbering, JSON details system พร้อมใช้งาน พร้อม validation และ security + +### **Phase 2: Tasks** + +- **[ ] T2.1 MasterModule - Master Data Management** + - [ ] สร้าง Entities: + - [ ] CorrespondenceType + - [ ] CorrespondenceStatus + - [ ] RfaType + - [ ] RfaStatusCode + - [ ] RfaApproveCode + - [ ] CirculationStatusCode + - [ ] Tag + - [ ] สร้าง Services & Controllers (CRUD): + - [ ] GET /master/correspondence-types + - [ ] POST /master/tags → Create Tag + - [ ] GET /master/tags → List Tags (Autocomplete) + - [ ] **Security:** Implement admin-only access สำหรับ master data + - [ ] **Deliverable:** Admin จัดการ Master Data ได้ + - [ ] **Dependencies:** T0.3 (ต้องเชื่อมต่อกับ master tables), T1.1 (Security patterns), T1.5 (Project context สำหรับ master data บางอย่าง) + +- **[ ] T2.2 FileStorageService - Secure File Management** + - [ ] สร้าง Attachment Entity + - [ ] สร้าง FileStorageService: + - [ ] uploadFile(file: Express.Multer.File, userId: number)` → Attachment + - [ ] Virus scanning ด้วย ClamAV + - [ ] File type validation (white-list: PDF, DWG, DOCX, XLSX, PPTX, ZIP) + - [ ] File size check (max 50MB) + - [ ] Generate checksum (SHA-256) + - [ ] getFilePath(attachmentId)` → string + - [ ] deleteFile(attachmentId)` → boolean + - [ ] จัดเก็บไฟล์ใน /share/dms-data/uploads/{YYYY}/{MM}/ + - [ ] สร้าง Controller: + - [ ] POST /files/upload → { attachment_id, url } (Protected) + - [ ] GET /files/:id/download → File Stream (Protected + Expiration) + - [ ] **Security:** Access Control - ตรวจสอบสิทธิ์ผ่าน Junction Table + - [ ] **Deliverable:** อัปโหลด/ดาวน์โหลดไฟล์ได้อย่างปลอดภัย + - [ ] **Dependencies:** T1.1 (Base Entity สำหรับ Attachment, CryptoService สำหรับ checksum), T1.4 (RBAC Guard สำหรับการควบคุมการเข้าถึงไฟล์) + +- **[ ] T2.3 DocumentNumberingModule - Application-Level Locking** + - [ ] สร้าง Entities: + - [ ] DocumentNumberFormat + - [ ] DocumentNumberCounter + - [ ] สร้าง DocumentNumberingService: + - [ ] generateNextNumber(projectId, orgId, typeId, year) → string + - [ ] ใช้ **Redis distributed locking** แทน stored procedure + - [ ] Retry mechanism ด้วย exponential backoff + - [ ] Fallback mechanism เมื่อการขอเลขล้มเหลว + - [ ] Format ตาม Template: {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4} + - **ไม่มี Controller** (Internal Service เท่านั้น) + - [ ] **Security:** Implement audit log ทุกครั้งที่มีการ generate เลขที่ + - [ ] **Deliverable:** Service สร้างเลขที่เอกสารได้ถูกต้องและปลอดภัย + - [ ] **Dependencies:** T1.1 (Base patterns, Audit Log), T0.3 (ต้องเชื่อมต่อกับ document_number_formats/counters) + +- **[ ] 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 (Input Validation Pipeline) + +### **Phase 2.5: JSON Details & Schema Management (สัปดาห์ที่ 4)** + +- [ ]Dependencies: T1.1 (Validation patterns, Security) +- **[ ] T2.5.1 JsonSchemaModule - Schema Management** + - [ ] สร้าง JsonSchemaService: + - [ ] validate(schemaId: string, data: any): ValidationResult + - [ ] getSchema(schemaId: string, version?: string): object + - [ ] registerSchema(schemaId: string, schema: object): void + - [ ] migrateData(data: any, fromVersion: string, toVersion: string): any + - [ ] สร้าง predefined schemas สำหรับ: + - [ ] Correspondence types (RFI, RFA, TRANSMITTAL, etc.) + - [ ] Routing types (TEMPLATE, INSTANCE, ACTION) + - [ ] Audit types (AUDIT_LOG, SECURITY_SCAN) + - [ ] Implement schema versioning และ compatibility + - [ ] **Deliverable:** JSON schema system ทำงานได้ + +- **[ ] T2.5.2 DetailsService - Data Processing** + - [ ] สร้าง DetailsService: + - [ ] processDetails(type: string, input: any): ProcessedDetails + - [ ] sanitize(input: any): any (XSS prevention, SQL injection protection) + - [ ] compress(data: any): string (JSON compression) + - [ ] decompress(compressed: string): any + - [ ] Implement data transformation pipelines: + - [ ] Input validation และ sanitization + - [ ] Data normalization + - [ ] Default value population + - [ ] **Deliverable:** Data processing service ทำงานได้ + +- **[ ] T2.5.3 JSON Security & Validation** + - [ ] สร้าง SecurityService สำหรับ JSON: + - [ ] sanitizeJson(input: any): any (Remove dangerous properties) + - [ ] validateSize(data: any): boolean (Max 50KB check) + - [ ] encryptSensitiveFields(data: any): any + - [ ] Implement validation rules: + - [ ] Type validation + - [ ] Format validation (email, date, URL) + - [ ] Custom validation rules + - [ ] **Deliverable:** JSON security measures ทำงานได้ + +- **T2.5.4 Integration with Existing Modules** + - [ ] Integrate กับ CorrespondenceModule: + - [ ] Auto-validate details ก่อน save + - [ ] Auto-populate default values + - [ ] Handle version migration + - [ ] Integrate กับ RoutingModule: + - [ ] Validate routing configuration details + - [ ] Process step action details + - [ ] Integrate กับ AuditModule: + - [ ] Validate audit log details + - [ ] Compress large audit data + - [ ] **Deliverable:** JSON details integrated กับทุก modules + +--- + +## **Phase 3: Correspondence & RFA Core (สัปดาห์ที่ 5-6)** + +**Milestone:** ระบบเอกสารโต้ตอบและ RFA พร้อม Security และ Audit, ระบบส่งต่อเอกสารระหว่างองค์กรทำงานได้ + +### **Phase 3A: Tasks (สัปดาห์ที่ 5)** + +- **[ ] T3.1 CorrespondenceModule - Basic CRUD** + - [ ] สร้าง Entities: + - [ ] Correspondence + - [ ] CorrespondenceRevision + - [ ] CorrespondenceRecipient + - [ ] CorrespondenceTag + - [ ] CorrespondenceReference + - [ ] CorrespondenceAttachment + - [ ] สร้าง CorrespondenceService: + - [ ] create(dto) → Correspondence + - [ ] สร้าง Correspondence + Revision แรก (rev 0) + - [ ] เรียก DocumentNumberingService (Redis locking) + - [ ] สร้าง Recipients (TO/CC) + - [ ] สร้าง Tags + - [ ] สร้าง Attachments (ผ่าน FileStorageService) + - [ ] update(id, dto) → Correspondence + - [ ] สร้าง Revision ใหม่ + - [ ] Update is_current flag + - [ ] findAll(filters) → Paginated List + - [ ] findById(id) → Correspondence with Current Revision + - [ ] สร้าง Controllers: + - [ ] POST /correspondences → Create + - [ ] GET /correspondences → List (Filter by type, status, org) + - [ ] GET /correspondences/:id → Detail + - [ ] PUT /correspondences/:id → Update (Create new revision) + - [ ] DELETE /correspondences/:id → Soft Delete (Admin only) + - [ ] **Security:** Implement permission checks สำหรับ document access + - [ ] **Deliverable:** สร้าง/แก้ไข/ดูเอกสารได้ + - [ ] **Dependencies:** T1.1 (Base Entity, Audit, Transform), T1.2 (Authentication), T1.3 (User context), T1.4 (RBAC), T1.5 (Project/Organization context), T2.3 (DocumentNumberingService), T2.2 (FileStorageService), T2.5 (JSON Details Validation) + +- **[ ] T3.2 CorrespondenceModule - Advanced Features** + - [ ] Implement Status Transitions: + - [ ] DRAFT → SUBMITTED (Document Control) + - [ ] SUBMITTED → CLOSED (Admin) + - [ ] SUBMITTED → CANCELLED (Admin + Reason) + - [ ] Implement References: + - [ ] POST /correspondences/:id/references → Link Documents + - [ ] Implement Search (Basic): + - [ ] GET /correspondences/search?q=... + - [ ] **Security:** Implement state transition validation + - [ ] **Deliverable:** Workflow พื้นฐานทำงานได้ + - [ ] **Dependencies:** T3.1 (ต้องมี Basic CRUD ก่อน) + +- **[ ] T3.3 RfaModule - Basic CRUD** + - [ ] สร้าง Entities: + - [ ] Rfa + - [ ] RfaRevision + - [ ] RfaItem (Junction to Shop Drawings) + - [ ] สร้าง RfaService: + - [ ] create(dto) → Rfa + - [ ] สร้าง Correspondence + Rfa + RfaRevision + - [ ] เชื่อม Shop Drawing Revisions (สำหรับ RFA_DWG) + - [ ] findAll(filters) → Paginated List + - [ ] findById(id) → Rfa with Items + - [ ] สร้าง Controllers: + - [ ] POST /rfas → Create + - [ ] GET /rfas → List + - [ ] GET /rfas/:id → Detail + - [ ] **Security:** Implement permission checks สำหรับ RFA operations + - [ ] **Deliverable:** สร้าง RFA และเชื่อม Shop Drawings ได้ + - [ ] **Dependencies:** T3.1 (RFA คือ Correspondence ประเภทหนึ่ง), T1.5 (Project context), T4.2 (สำหรับการเชื่อมโยงกับ Shop Drawings) + +### **Phase 3B: Correspondence Routing (สัปดาห์ที่ 6)** + +- [ ] **Dependencies:** T3.1 (ต้องมี Correspondence ก่อนจะส่งต่อได้), T2.5 (สำหรับ JSON Details ของ Routing) + +- **[ ] T3.4.1 CorrespondenceRoutingModule - Template Management** + - [ ] สร้าง Entities: + - [ ] CorrespondenceRoutingTemplate + - [ ] CorrespondenceRoutingTemplateStep + - [ ] สร้าง Services: + - [ ] createTemplate(dto) → Create routing template + - [ ] addStep(templateId, stepDto) → Add step to template + - [ ] getTemplates(projectId) → List available templates + - [ ] สร้าง Controllers: + - [ ] POST /routing/templates → Create template (Admin only) + - [ ] GET /routing/templates → List templates + - [ ] POST /routing/templates/:id/steps → Add step + +- **[ ] T3.4.2 CorrespondenceRoutingModule - Routing Execution** + - [ ] สร้าง Entity: CorrespondenceRouting + - [ ] สร้าง RoutingService: + - [ ] initiateRouting(correspondenceId, templateId) → void + - [ ] สร้าง routing instances ตาม template steps + - [ ] คำนวณ due dates จาก expected_days + - [ ] ส่ง notifications ไปยังองค์กรแรก + - [ ] processStep(routingId, action, comments) → void + - [ ] อัพเดทสถานะขั้นตอนปัจจุบัน + - [ ] ส่ง notification ไปยังขั้นตอนต่อไป + - [ ] อัพเดท due dates สำหรับขั้นตอนต่อไป + - [ ] สร้าง Controllers: + - [ ] POST /correspondences/:id/routing/start → Start routing + - [ ] POST /routing/:id/process → Process routing step + - [ ] GET /correspondences/:id/routing → Get routing status + - [ ] ตัวอย่าง Implementation Details + +- **[ ] T3.4.3 WorkflowEngineModule - State Management** + - [ ] สร้าง WorkflowEngineService: + - [ ] validateTransition(currentStatus, targetStatus) → boolean + - [ ] getAllowedTransitions(status) → string[] + - [ ] autoAdvanceStatus(correspondenceId) → void + - [ ] สร้าง DeadlineService: + - [ ] checkDeadlines() → void (Cron job) + - [ ] sendReminder(routingId) → void + - [ ] escalateOverdue(routingId) → void + +- **[ ] T3.4.4 Routing UI Integration** + - [ ] สร้าง API endpoints สำหรับ frontend: + - [ ] GET /routing/available-templates → Templates for current project + - [ ] GET /routing/my-pending → Pending routing steps for current user + - [ ] POST /routing/bulk-action → Process multiple routing steps + - [ ] **Security:** Implement permission checks สำหรับ routing operations + - [ ] **Deliverable:** ระบบส่งต่อเอกสารทำงานได้สมบูรณ์ + +--- + +## **Phase 4: Drawing Management (สัปดาห์ที่ 7)** + +**Milestone:** ระบบจัดการแบบพร้อม File Security + +### **Phase 4: Tasks** + +- **[ ] T4.1 DrawingModule - Contract Drawings** + - [ ] สร้าง Entities: + - [ ] ContractDrawing + - [ ] ContractDrawingVolume + - [ ] ContractDrawingCat + - [ ] ContractDrawingSubCat + - [ ] ContractDrawingSubcatCatMap + - [ ] ContractDrawingAttachment + - [ ] สร้าง ContractDrawingService CRUD + - [ ] สร้าง Controllers: + - [ ] GET /drawings/contract → List + - [ ] POST /drawings/contract → Create (Admin) + - [ ] GET /drawings/contract/:id → Detail + - [ ] **Security:** Implement access control สำหรับ contract drawings + - [ ] **Deliverable:** จัดการ Contract Drawings ได้ + - [ ] **Dependencies:** T1.1 (Base Entity, Security), T1.2 (Authentication), T1.4 (RBAC), T1.5 (Project context), T2.2 (FileStorageService) + +- **[ ] T4.2 DrawingModule - Shop Drawings** + - [ ] สร้าง Entities: + - [ ] ShopDrawing + - [ ] ShopDrawingRevision + - [ ] ShopDrawingMainCategory + - [ ] ShopDrawingSubCategory + - [ ] ShopDrawingRevisionContractRef + - [ ] ShopDrawingRevisionAttachment + - [ ] สร้าง ShopDrawingService CRUD + - [ ] สร้าง Controllers: + - [ ] GET /drawings/shop → List + - [ ] POST /drawings/shop → Create + - [ ] POST /drawings/shop/:id/revisions → Create Revision + - [ ] GET /drawings/shop/:id → Detail with Revisions + - [ ] Link Shop Drawing Revision → Contract Drawings + - [ ] **Security:** Implement virus scanning สำหรับ drawing files + - [ ] **Deliverable:** จัดการ Shop Drawings และ Revisions ได้ + - [ ] ependencies: T4.1 (Shop Drawings อ้างอิงถึง Contract Drawings) + +--- + +## **Phase 5: Workflow Systems & Resilience (สัปดาห์ที่ 8-9)** + +**Milestone:** ระบบ Workflow ทั้งหมดพร้อม Resilience Patterns + +### **Phase 5: Tasks** + +- **[ ] T5.1 RfaModule - Workflow Implementation** + - [ ] สร้าง Entities: + - [ ] RfaWorkflowTemplate + - [ ] RfaWorkflowTemplateStep + - [ ] RfaWorkflow (Transaction Log) + - [ ] สร้าง RfaWorkflowService: + - [ ] initiateWorkflow(rfaId, templateId) → void + - [ ] สร้าง RfaWorkflow records ตาม Template + - [ ] กำหนด Step 1 เป็น PENDING + - [ ] completeStep(rfaId, stepNumber, action, comments) → void + - [ ] Update Status → COMPLETED + - [ ] Set Next Step → PENDING + - [ ] Send Notifications + - [ ] rejectStep(rfaId, stepNumber, reason) → void + - [ ] Update Status → REJECTED + - [ ] Send back to Originator + - [ ] สร้าง Controllers: + - [ ] POST /rfas/:id/workflow/start → Start Workflow + - [ ] POST /rfas/:id/workflow/steps/:stepNumber/complete → Complete Step + - [ ] GET /rfas/:id/workflow → Get Workflow Status + - [ ] **Resilience:** Implement circuit breaker สำหรับ notification services + - [ ] **Deliverable:** RFA Workflow ทำงานได้ + - [ ] **Dependencies:** T3.3 (ต้องมี RFA Basic CRUD ก่อน), T4.2 (RFA_DWG ต้องเชื่อมโยงกับ Shop Drawings), T2.5 (JSON Details สำหรับ Workflow), T6.2 (NotificationService สำหรับแจ้งเตือน) + +- **[ ] T5.2 CirculationModule - Internal Routing** + - [ ] สร้าง Entities: + - [ ] Circulation + - [ ] CirculationTemplate + - [ ] CirculationTemplateAssignee + - [ ] CirculationRouting (Transaction Log) + - [ ] CirculationAttachment + - [ ] สร้าง CirculationService: + - [ ] create(correspondenceId, dto) → Circulation + - [ ] สร้าง Circulation (1:1 กับ Correspondence) + - [ ] สร้าง Routing ตาม Template + - [ ] assignUser(circulationId, stepNumber, userId) → void + - [ ] completeStep(circulationId, stepNumber, comments) → void + - [ ] close(circulationId) → void (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) + - สร้าง Controllers: + - [ ] POST /circulations → Create + - [ ] GET /circulations/:id → Detail + - [ ] POST /circulations/:id/steps/:stepNumber/complete → Complete + - [ ] POST /circulations/:id/close → Close + - [ ] **Resilience:** Implement retry mechanism สำหรับ assignment notifications + - [ ] **Deliverable:** ใบเวียนภายในองค์กรทำงานได้ + - [ ] **Dependencies:** T3.1 (Circulation คือ Correspondence ประเภทหนึ่ง), T2.5 (JSON Details), T6.2 (NotificationService) + +- **[ ] T5.3 TransmittalModule - Document Forwarding** + - [ ] สร้าง Entities: + - [ ] Transmittal + - [ ] TransmittalItem + - [ ] สร้าง TransmittalService: + - [ ] create(dto) → Transmittal + - [ ] สร้าง Correspondence + Transmittal + - [ ] เชื่อม Multiple Correspondences เป็น Items + - [ ] สร้าง Controllers: + - [ ] POST /transmittals → Create + - [ ] GET /transmittals → List + - [ ] GET /transmittals/:id → Detail with Items + - [ ] **Security:** Implement access control สำหรับ transmittal items + - [ ] **Deliverable:** สร้าง Transmittal ได้ + - [ ] **Dependencies:** T3.1 (Transmittal คือ Correspondence ประเภทหนึ่ง) + +--- + +## **Phase 6: Advanced Features & Monitoring (สัปดาห์ที่ 10-11)** + +**Milestone:** ฟีเจอร์ขั้นสูงพร้อม Monitoring และ Observability + +### **Phase 6: Tasks** + +- **[ ] T6.1 SearchModule - Elasticsearch Integration** + - [ ] Setup Elasticsearch Container ใน docker-compose.yml + - [ ] สร้าง SearchService: + - [ ] indexDocument(entity) → void + - [ ] updateDocument(entity) → void + - [ ] deleteDocument(entity) → void + - [ ] search(query, filters) → SearchResult[] + - [ ] Index ทุกครั้งที่ Create/Update: + - [ ] Correspondence + - [ ] RFA + - [ ] Shop Drawing + - [ ] Contract Drawing + - [ ] Circulation + - [ ] Transmittal + - [ ] สร้าง Controllers: + - [ ] GET /search?q=...&type=...&from=...&to=... → Results + - [ ] **Resilience:** Implement circuit breaker สำหรับ Elasticsearch + - [ ] **Deliverable:** ค้นหาขั้นสูงทำงานได้ + - [ ] **Dependencies:** T3.1, T3.3, T4.2, T5.2, T5.3 (ต้องมีข้อมูลจาก Modules หลักเหล่านี้ให้ทำการ Index) + +- **[ ] T6.2 NotificationModule - Email & Line** + - [ ] สร้าง NotificationService: + - [ ] sendEmail(to, subject, body) → void (Nodemailer) + - [ ] sendLine(userId, message) → void (ผ่าน n8n Webhook) + - [ ] createSystemNotification(userId, message, entityType, entityId) → void + - [ ] Integrate กับ Workflow Events: + - [ ] เมื่อสร้าง Correspondence ใหม่ → แจ้ง Recipients + - [ ] เมื่อสร้าง Circulation → แจ้ง Assignees + - [ ] เมื่อ RFA Workflow ถึง Step → แจ้ง Responsible Org + - [ ] เมื่อใกล้ถึง Deadline → แจ้ง (Optional) + - [ ] สร้าง Controllers: + - [ ] GET /notifications → List User's Notifications + - [ ] PUT /notifications/:id/read → Mark as Read + - [ ] **Resilience:** Implement retry mechanism ด้วย exponential backoff + - [ ] **Deliverable:** ระบบแจ้งเตือนทำงานได้ + - [ ] **Dependencies:** T1.1 (ConfigService สำหรับ Email credentials), T6.4 (Resilience patterns สำหรับการส่งภายนอก) + +- **[ ] T6.3 MonitoringModule - Observability** + - [ ] สร้าง Health Check Controller: + - [ ] GET /health → Database, Redis, Elasticsearch status + - [ ] สร้าง Metrics Service: + - [ ] API response times + - [ ] Error rates + - [ ] Cache hit ratios + - [ ] Business metrics (documents created, workflow completion) + - [ ] สร้าง Performance Interceptor: + - [ ] Track request duration + - [ ] Alert if response time > 200ms + - [ ] สร้าง Logging Service: + - [ ] Structured logging (JSON format) + - [ ] Log aggregation + - [ ] **Deliverable:** Monitoring system ทำงานได้ + - [ ] **Dependencies:** T1.1 (Base patterns) + +- **[ ] T6.4 ResilienceModule - Circuit Breaker & Retry** + - [ ] สร้าง Circuit Breaker Service: + - [ ] @CircuitBreaker() decorator สำหรับ external calls + - [ ] Configurable timeout และ error thresholds + - [ ] สร้าง Retry Service: + - [ ] @Retry() decorator ด้วย exponential backoff + - [ ] สร้าง Fallback Strategies: + - [ ] Graceful degradation สำหรับ non-critical features + - [ ] Implement สำหรับ: + - [ ] Email notifications + - [ ] LINE notifications + - [ ] Elasticsearch queries + - [ ] File virus scanning + - [ ] **Deliverable:** Resilience patterns ทำงานได้ + - [ ] **Dependencies:** T1.1 (Base patterns) + +--- + +## **Phase 7: Testing & Optimization (สัปดาห์ที่ 12-13)** + +**Milestone:** ทดสอบและปรับปรุงประสิทธิภาพพร้อม Security Audit + +### **Phase 7: Tasks** + +- **[ ] T7.1 Unit Testing** + - [ ] เขียน Unit Tests สำหรับ Services สำคัญ: + - [ ] AuthService (login, validateUser) + - [ ] RbacGuard (permission checks ทั้ง 4 ระดับ) + - [ ] DocumentNumberingService (Redis locking) + - [ ] FileStorageService (virus scanning, validation) + - [ ] CorrespondenceService (create, update, status transitions) + - [ ] RfaWorkflowService (workflow logic) + - [ ] **Security:** ทดสอบ security scenarios (SQL injection, XSS attempts) + - [ ] Target: 80% Code Coverage + - [ ] **Deliverable:** Unit Tests ผ่านทั้งหมด + - [ ] **Dependencies:** ทุก Development Tasks ใน Phase 1-6 ต้องเสร็จสิ้นก่อนเริ่มเขียน Tests อย่างจริงจัง (โดยเฉพาะ T1.4, T2.3, T3.1, T5.1) + +- **[ ] T7.2 Integration Testing** + - [ ] เขียน Integration Tests: + - [ ] Authentication Flow (login → access protected route) + - [ ] Document Creation Flow (create correspondence → attach files → virus scan) + - [ ] RFA Workflow Flow (start → step 1 → step 2 → complete) + - [ ] Circulation Flow (create → assign → complete → close) + - [ ] ทดสอบ SQL Views (v_user_all_permissions, v_user_tasks) + - [ ] ใช้ Test Database แยกต่างหาก + - [ ] **Security:** ทดสอบ rate limiting และ permission enforcement + - [ ] **Deliverable:** Integration Tests ผ่าน + - [ ] **Dependencies:** T7.1 (Unit Tests ควรผ่านก่อน) + +- **[ ] T7.3 E2E Testing** + - [ ] เขียน E2E Tests: + - [ ] User Registration & Login + - [ ] Create Correspondence (Full Flow) + - [ ] Create RFA with Shop Drawings + - [ ] Complete RFA Workflow + - [ ] Search Documents + - [ ] **Security:** ทดสอบ file upload security + - [ ] **Deliverable:** E2E Tests ผ่าน + - [ ] **Dependencies:** T7.2 (Integration Tests ควรผ่านก่อน) + +- **[ ] T7.4 Performance Testing** + - [ ] Load Testing: + - [ ] 100 concurrent users + - [ ] API response time < 200ms (90th percentile) + - [ ] Search performance < 500ms + - [ ] Stress Testing: + - [ ] Find breaking points + - [ ] Database connection limits + - [ ] Endurance Testing: + - [ ] 24-hour continuous operation + - [ ] **Deliverable:** Performance targets บรรลุ + - [ ] **Dependencies:** T7.3 (E2E Tests ควรผ่านก่อน) + +- **[ ] T7.5 Security Testing** + - [ ] Penetration Testing: + - [ ] OWASP Top 10 vulnerabilities + - [ ] SQL Injection attempts + - [ ] XSS attempts + - [ ] CSRF attempts + - [ ] Security Audit: + - [ ] Code review สำหรับ security flaws + - [ ] Dependency vulnerability scanning + - [ ] File Upload Security Testing: + - [ ] Virus scanning effectiveness + - [ ] File type bypass attempts + - [ ] **Deliverable:** Security tests ผ่าน + - [ ] **Dependencies:** T7.4 (Performance Tests ควรผ่านก่อน) + +- **[ ] T7.6 Performance Optimization** + - [ ] Implement Caching: + - [ ] Cache Master Data (Roles, Permissions, Organizations) + - [ ] Cache User Permissions + - [ ] Cache Search Results + - [ ] Database Optimization: + - [ ] Review Indexes + - [ ] Optimize Queries (N+1 Problem) + - [ ] Implement Pagination ทุก List Endpoint + - [ ] **Deliverable:** Response Time < 200ms (90th percentile) + - [ ] **Dependencies:** T7.4 (ผลจาก Performance Testing จะบอกว่าจุดไหนต้อง Optimization) + +--- + +## **Phase 8: Documentation & Deployment (สัปดาห์ที่ 14)** + +**Milestone:** เอกสารและ Deploy สู่ Production พร้อม Security Hardening + +### **Phase 8: Tasks** + +- **[ ] T8.1 API Documentation** + - [ ] ครบทุก Endpoint ใน Swagger: + - [ ] ใส่ Description, Example Request/Response + - [ ] ระบุ Required Permissions + - [ ] ใส่ Error Responses + - [ ] Export Swagger JSON → Frontend Team + - [ ] **Security:** เอกสารต้องไม่เปิดเผย sensitive information + - [ ] **Deliverable:** Swagger Docs สมบูรณ์ + - [ ] **Dependencies:** ทุก Development Tasks ต้องเสร็จสิ้น + +- **[ ] T8.2 Technical Documentation** + - [ ] เขียนเอกสาร: + - [ ] Architecture Overview + - [ ] Module Structure + - [ ] Database Schema Diagram + - [ ] API Design Patterns + - [ ] Security Implementation Guide + - [ ] Deployment Guide + - [ ] Disaster Recovery Procedures + - [ ] **Deliverable:** Technical Docs พร้อม + - [ ] **Dependencies:** T8.1 + +- **[ ] T8.3 Security Hardening** + - [ ] Final Security Review: + - [ ] Environment variables security + - [ ] Database encryption + - [ ] File storage security + - [ ] API security headers + - [ ] Implement Security Monitoring: + - [ ] Failed login attempts tracking + - [ ] Suspicious activity alerts + - [ ] Rate limiting monitoring + - [ ] **Deliverable:** Security checklist ผ่าน + - [ ] **Dependencies:** T7.5 (Security Testing ต้องผ่าน) + +- **[ ] T8.4 Deployment Preparation** + - [ ] สร้าง Production docker-compose.yml + - [ ] Setup Environment Variables ใน QNAP + - [ ] Setup Nginx Proxy Manager (SSL Certificate) + - [ ] Setup Backup Scripts (Database + Files) + - [ ] Setup Monitoring และ Alerting + - [ ] **Deliverable:** Deployment Guide พร้อม + - [ ] **Dependencies:** T8.2, T8.3 + +- **[ ] T8.5 Production Deployment** + - [ ] Deploy Backend ไปยัง backend.np-dms.work + - [ ] ทดสอบ API ผ่าน Postman + - [ ] Monitor Logs (Winston) + - [ ] Setup Health Check Endpoint (GET /health) + - [ ] Verify Security Measures: + - [ ] HTTPS enforcement + - [ ] Security headers + - [ ] Rate limiting + - [ ] Virus scanning + - [ ] **Deliverable:** Backend รันบน Production + - [ ] **Dependencies:** T8.4 + +- **[ ] T8.6 Handover to Frontend Team** + - [ ] Demo API ให้ Frontend Team + - [ ] ส่งมอบ Swagger Documentation + - [ ] ส่งมอบ Postman Collection + - [ ] Workshop: วิธีใช้ Authentication & RBAC + - [ ] **Deliverable:** Frontend เริ่มพัฒนาได้ + - [ ] **Dependencies:** T8.5 + +--- + +## 📊 **สรุป Timeline** + +| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | +| ------- | -------------- | ------------ | ------------------------------------ | +| Phase 0 | 1 สัปดาห์ | 4 | Infrastructure Ready + Security Base | +| Phase 1 | 2 สัปดาห์ | 5 | Auth & User Management + RBAC | +| Phase 2 | 1 สัปดาห์ | 4 | Security & File Management | +| Phase 3 | 2 สัปดาห์ | 3 | Correspondence & RFA Core | +| Phase 4 | 1 สัปดาห์ | 2 | Drawing Management | +| Phase 5 | 2 สัปดาห์ | 3 | Workflow Systems + Resilience | +| Phase 6 | 2 สัปดาห์ | 4 | Advanced Features + Monitoring | +| Phase 7 | 2 สัปดาห์ | 6 | Testing & Optimization | +| Phase 8 | 1 สัปดาห์ | 6 | Documentation & Deploy | +| **รวม** | **14 สัปดาห์** | **37 Tasks** | **Production-Ready Backend** | + +--- + +## 🎯 **Critical Success Factors** + +1. **Security First:** ทุก Task ต้องพิจารณาด้านความปลอดภัยเป็นหลัก +2. **Database Integrity:** ใช้ Schema v1.4.1 เป็นหลัก ไม่แก้ไข Schema โดยไม่จำเป็น +3. **Soft Delete:** Service ทั้งหมดต้องใช้ Global Filter กรอง WHERE deleted_at IS NULL +4. **API Contract:** ทุก Endpoint ต้องมี Swagger Documentation สมบูรณ์ +5. **Security Compliance:** RBAC ต้องทำงานถูกต้อง 100% ก่อน Deploy +6. **Testing Coverage:** Code Coverage อย่างน้อย 80% ก่อน Production +7. **Performance Targets:** Response Time < 200ms (90th percentile) +8. **Resilience:** ระบบต้องทนทานต่อ failures ของ external services +9. **Monitoring:** ต้องมี comprehensive monitoring และ alerting +10. **Documentation:** เอกสารต้องครบถ้วนเพื่อ Handover ให้ Frontend Team + +--- + +## 🚀 **ขั้นตอนถัดไป** + +1. **Approve แผนนี้** → ปรับแต่งตาม Feedback +2. **Setup Phase 0** → เริ่มสร้าง Infrastructure +3. **Daily Standup** → รายงานความก้าวหน้าทุกวัน +4. **Weekly Review** → ทบทวนความก้าวหน้าทุกสัปดาห์ +5. **Security Review** → ทุก Phase ต้องผ่าน security review +6. **Deploy to Production** → Week 14 + +--- + +## 📋 **Security Checklist ทุก Phase** + +- **Phase 1-2: Foundation Security** + - [ ] Input validation implemented + - [ ] XSS protection enabled + - [ ] CSRF protection implemented + - [ ] Rate limiting configured + - [ ] Secure password hashing (bcrypt) + +- **Phase 3-5: Application Security** + - [ ] RBAC 4-level working correctly + - [ ] File upload security (virus scanning, type validation) + - [ ] Audit logging for critical operations + - [ ] SQL injection prevention + +- **Phase 6-8: Production Security** + - [ ] HTTPS enforcement + - [ ] Security headers configured + - [ ] Environment variables secured + - [ ] Monitoring and alerting active + - [ ] Backup and recovery tested + +## **หมายเหตุ:** แผนนี้สามารถปรับแต่งได้ตามความต้องการและข้อจำกัดของทีม หาก Phase ใดใช้เวลามากกว่าที่คาดการณ์ ควรปรับ Timeline ให้เหมาะสม โดยให้ความสำคัญกับ Security และ Quality diff --git a/docs/LCBP3-DMS_V1_4_1_Data_Dictionary.md b/docs/LCBP3-DMS_V1_4_1_Data_Dictionary.md new file mode 100644 index 0000000..5a10755 --- /dev/null +++ b/docs/LCBP3-DMS_V1_4_1_Data_Dictionary.md @@ -0,0 +1,2837 @@ +# **ตารางฐานข้อมูล (Data Dictionary) - LCBP3-DMS (V1.4.1)** + +เอกสารนี้สรุปโครงสร้างตาราง, 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) +- FULLTEXT INDEX (content) - Enables full-text searching + +**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) + +6. **Search**: + - `FULLTEXT idx_search_indices_content` on (content) + +--- + +## 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 diff --git a/docs/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md b/docs/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md new file mode 100644 index 0000000..c605ada --- /dev/null +++ b/docs/LCBP3-DMS_V1_4_1_Frontend_Development_Plan.md @@ -0,0 +1,2491 @@ +# 📋 **แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.1 (ปรับปรุงโดย Claude -> deepseek)** + +## 🎯 ภาพรวมโครงการ + +พัฒนา Frontend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่ทันสมัย responsive และใช้งานง่าย รองรับการจัดการเอกสารที่ซับซ้อน มี Dashboard แบบ Real-time และระบบ Workflow Visualization เน้น Security, Performance และ User Experience ตาม Requirements v1.4.1 อย่างครบถ้วน + +--- + +## 📐 สถาปัตยกรรมระบบ + +### **Technology Stack** + +- **Framework:** Next.js 14+ (App Router, React 18+, TypeScript, ESM) +- **Styling:** Tailwind CSS + PostCSS +- **Component Library:** shadcn/ui (Radix UI) +- **State Management:** + - **Server State:** TanStack Query (React Query) + - **Global Client State:** Zustand + - **Form State:** React Hook Form + Zod +- **Data Fetching:** Axios + TanStack Query +- **Authentication:** NextAuth.js (JWT Strategy) +- **File Upload:** React Dropzone +- **Tables:** TanStack Table +- **Charts:** Recharts +- **Date Picker:** date-fns + shadcn/ui Calendar +- **Icons:** Lucide React +- **Security & File Processing** clamscan + js-file-download + dompurify +- **JSON Schema & Validation** ajv + ajv-formats + jsonpath + json-schema-ref-parser +- **Performance Monitoring** web-vitals + @axe-core/react +- **Advanced UI Components** react-json-view-lite + react-window (สำหรับ Virtual Scrolling) +- **Testing:** + - **Unit/Integration:** Vitest + React Testing Library + - **E2E:** Playwright + - **API Mocking:** Mock Service Worker (MSW) + +### **โครงสร้างโปรเจกต์** + + ```tree + app/ + ├── (public)/ # Public routes (Landing, Login) + │ ├── page.tsx # Landing Page + │ └── login/ # Login Page + ├── (protected)/ # Protected routes + │ ├── layout.tsx # App Shell (Navbar + Sidebar) + │ ├── dashboard/ # Dashboard + │ ├── correspondences/ # Correspondence Management + │ ├── rfas/ # RFA Management + │ ├── drawings/ # Drawing Management + │ ├── circulations/ # Circulation Management + │ ├── transmittals/ # Transmittal Management + │ ├── search/ # Advanced Search + │ ├── reports/ # Reports + │ ├── admin/ # Admin Panel + │ └── profile/ # User Profile + ├── api/ # API Routes (if needed) + components/ + ├── ui/ # shadcn/ui components + ├── features/ # Feature-specific components + │ ├── auth/ + │ ├── correspondence/ + │ ├── rfa/ + │ ├── drawing/ + │ ├── circulation/ + │ ├── common/ + │ ├── security/ # NEW: Security components + │ │ ├── file-upload-security.tsx + │ │ ├── virus-scan-status.tsx + │ │ └── security-audit-log.tsx + │ ├── json-details/ # NEW: JSON Details management + │ │ ├── json-details-form.tsx + │ │ ├── schema-validator.tsx + │ │ └── dynamic-form-generator.tsx + │ ├── routing/ # NEW: Correspondence routing + │ │ ├── routing-template-manager.tsx + │ │ ├── routing-workflow-visualizer.tsx + │ └── pending-routings-list.tsx + │ └── monitoring/ # NEW: Performance monitoring + │ ├── performance-metrics.tsx + │ ├── real-time-monitor.tsx + │ └── cache-status.tsx + └── layouts/ # Layout components + lib/ + ├── api/ # API client & hooks + ├── stores/ # Zustand stores + ├── utils/ # Utility functions + ├── hooks/ # Custom hooks + ├── types/ # TypeScript types + ├── security/ # NEW: Security utilities + │ ├── file-scanner.ts + │ ├── virus-scan-client.ts + │ └── security-headers.ts + ├── json-schemas/ # NEW: JSON schema management + │ ├── schemas/ + │ ├── validators/ + │ └── transformers/ + └── monitoring/ # NEW: Monitoring utilities + │ ├── performance.ts + │ ├── error-tracking.ts + │ └── metrics-collector.ts + public/ + ├── images/ + └── fonts/ + ``` + +--- + +## 🗓️ แผนการพัฒนาแบบ Phase-Based + +### **Phase 0: Setup & Infrastructure (สัปดาห์ที่ 1)** + +**Milestone:** สร้างโครงสร้างพื้นฐานและ Development Environment + +- **T0.1 Initialize Next.js Project** + + - สร้างโปรเจกต์ด้วย create-next-app: + + ```bash + npx create-next-app@latest lcbp3-frontend --typescript --tailwind --app --src-dir=false + ``` + + - เลือก Options: + - ✅ TypeScript + - ✅ ESLint + - ✅ Tailwind CSS + - ✅ App Router + - ✅ Import alias (@/*) + - Setup .gitignore, README.md + - Deliverable: ✅ โปรเจกต์เริ่มต้นพร้อม + +- **T0.2 Install Core Dependencies** + + ```bash + # State Management & Data Fetching + npm install @tanstack/react-query zustand + npm install axios + npm install react-hook-form @hookform/resolvers zod + + # UI Components & Styling + npm install clsx tailwind-merge + npm install lucide-react + npm install date-fns + + # File Upload + npm install react-dropzone + + # Authentication + npm install next-auth + + # Development Tools + npm install -D @types/node + + # Security & File Processing + npm install clamscan js-file-download + npm install dompurify @types/dompurify + + # JSON Schema & Validation + npm install ajv ajv-formats + npm install jsonpath json-schema-ref-parser + + # Performance Monitoring + npm install web-vitals + npm install @axe-core/react + + # Advanced UI Components + npm install react-json-view-lite + npm install react-window # สำหรับ Virtual Scrolling + ``` + + - Deliverable: ✅ Dependencies ติดตั้งสมบูรณ์ + +- **T0.3 Setup shadcn/ui** + + ```bash + npx shadcn-ui@latest init + ``` + + - เลือก Style: Default + - เลือก Base Color: Slate + - ติดตั้ง Components เบื้องต้น: + + ```bash + npx shadcn-ui@latest add button input label card table dropdown-menu + npx shadcn-ui@latest add dialog sheet toast alert + npx shadcn-ui@latest add form select textarea checkbox + npx shadcn-ui@latest add calendar popover + ``` + + - Deliverable: ✅ shadcn/ui พร้อมใช้งาน + +- **T0.4 Configure Tailwind CSS** + - แก้ไข tailwind.config.ts: + - เพิ่ม Custom Colors (ตาม Brand) + - เพิ่ม Custom Fonts (ภาษาไทย: Noto Sans Thai) + - Configure Container, Spacing + - สร้าง app/globals.css พร้อม Custom Styles + - Deliverable: ✅ Tailwind พร้อมใช้ + +- **T0.5 Setup Development Environment** + - สร้าง .env.local: + + ```yml + NEXT_PUBLIC_API_URL=http://backend.np-dms.work/api + NEXTAUTH_URL=http://localhost:3000 + NEXTAUTH_SECRET=... + ``` + + - Setup ESLint Rules (ไทย Comments OK) + - Setup Prettier + - Setup VS Code Settings + - Deliverable: ✅ Dev Environment พร้อม + +- **T0.6 Setup Git & Docker** + - Push โปรเจกต์ไปยัง Gitea (git.np-dms.work) + - สร้าง Dockerfile สำหรับ Next.js: + + ```dockerfile + FROM node:20-alpine + WORKDIR /app + COPY package*.json ./ + RUN npm ci + COPY . . + RUN npm run build + EXPOSE 3000 + CMD ["npm", "start"] + ``` + + - สร้าง docker-compose.yml (เชื่อม Network `lcbp3`) + - Deliverable: ✅ Project อยู่ใน Git + Docker พร้อม + +--- + +### **Phase 1: Authentication & App Shell (สัปดาห์ที่ 2-3)** + +**Milestone:** ระบบ Login และ Layout หลัก + +- **T1.1 Setup API Client** + - สร้าง lib/api/client.ts: + + ```typescript + import axios from axios; + + export const apiClient = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + headers: { + Content-Type: application/json, + }, + }); + + // Request Interceptor (Add JWT Token) + apiClient.interceptors.request.use((config) => { + const token = localStorage.getItem(access_token); + if (token) { + config.headers.Authorization = Bearer ${token}; + } + return config; + }); + + // Response Interceptor (Handle Errors) + apiClient.interceptors.response.use( + (response) => response, + (error) => { + if (error.response?.status === 401) { + // Redirect to login + window.location.href = /login; + } + return Promise.reject(error); + } + ); + ``` + + - Deliverable: ✅ API Client พร้อมใช้ + +- **T1.2 Setup TanStack Query** + +- สร้าง `app/providers.tsx`: + + ```typescript + 'use client'; + import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + import { useState } from 'react'; + + export function Providers({ children }: { children: React.ReactNode }) { + const [queryClient] = useState(() => new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, // 1 minute + refetchOnWindowFocus: false, + }, + }, + })); + + return ( + + {children} + + ); + } + ``` + +- Wrap ใน `app/layout.tsx` +- Deliverable: ✅ React Query พร้อม + +- **T1.3 Create Auth Store (Zustand)** + +- สร้าง `lib/stores/auth-store.ts`: + + ```typescript + import { create } from 'zustand'; + import { persist } from 'zustand/middleware'; + + interface User { + user_id: number; + username: string; + email: string; + first_name: string; + last_name: string; + primary_organization_id: number; + permissions: string[]; + } + + interface AuthStore { + user: User | null; + token: string | null; + setAuth: (user: User, token: string) => void; + clearAuth: () => void; + hasPermission: (permission: string) => boolean; + } + + export const useAuthStore = create()( + persist( + (set, get) => ({ + user: null, + token: null, + setAuth: (user, token) => { + set({ user, token }); + localStorage.setItem('access_token', token); + }, + clearAuth: () => { + set({ user: null, token: null }); + localStorage.removeItem('access_token'); + }, + hasPermission: (permission) => { + const user = get().user; + return user?.permissions.includes(permission) || false; + }, + }), + { + name: 'auth-storage', + } + ) + ); + ``` + +- Deliverable: ✅ Auth Store พร้อม + +- **T1.4 Create Login Page** + +- สร้าง `app/(public)/login/page.tsx`: + + ```typescript + 'use client'; + import { useForm } from 'react-hook-form'; + import { zodResolver } from '@hookform/resolvers/zod'; + import * as z from 'zod'; + import { Button } from '@/components/ui/button'; + import { Input } from '@/components/ui/input'; + import { useRouter } from 'next/navigation'; + import { useAuthStore } from '@/lib/stores/auth-store'; + import { apiClient } from '@/lib/api/client'; + + const loginSchema = z.object({ + username: z.string().min(1, 'กรุณากรอกชื่อผู้ใช้'), + password: z.string().min(1, 'กรุณากรอกรหัสผ่าน'), + }); + + export default function LoginPage() { + const router = useRouter(); + const setAuth = useAuthStore((state) => state.setAuth); + const form = useForm({ + resolver: zodResolver(loginSchema), + }); + + const handleLogin = async (data: z.infer) => { + try { + const response = await apiClient.post('/auth/login', data); + const { access_token, user } = response.data; + setAuth(user, access_token); + router.push('/dashboard'); + } catch (error) { + console.error('Login failed:', error); + // แสดง Toast Error + } + }; + + return ( +
+
+ {/* Form Fields */} +
+
+ ); + } + ``` + +- Deliverable: ✅ หน้า Login พร้อม + +- **T1.5 Create Landing Page** + +- สร้าง `app/(public)/page.tsx`: + - Hero Section พร้อมข้อมูลโครงการ + - Feature Highlights + - CTA Button → Login +- ใช้ Tailwind + Animations +- Deliverable: ✅ Landing Page สวยงาม + +- **T1.6 Create Protected Layout (App Shell)** + +- สร้าง `app/(protected)/layout.tsx`: + + ```typescript + 'use client'; + import { useEffect } from 'react'; + import { useRouter } from 'next/navigation'; + import { useAuthStore } from '@/lib/stores/auth-store'; + import Navbar from '@/components/layouts/navbar'; + import Sidebar from '@/components/layouts/sidebar'; + + export default function ProtectedLayout({ children }: { children: React.ReactNode }) { + const router = useRouter(); + const user = useAuthStore((state) => state.user); + + useEffect(() => { + if (!user) { + router.push('/login'); + } + }, [user, router]); + + if (!user) return null; + + return ( +
+ +
+ +
+ {children} +
+
+
+ ); + } + ``` + +- Deliverable: ✅ App Shell พร้อม + +- **T1.7 Create Navbar Component** + +- สร้าง `components/layouts/navbar.tsx`: + - แสดงชื่อระบบ + - แสดงชื่อผู้ใช้ + Avatar + - Dropdown Menu: + - Profile + - Settings + - Logout +- Responsive (Mobile Hamburger Menu) +- Deliverable: ✅ Navbar พร้อม + +- **T1.8 Create Sidebar Component** + +- สร้าง `components/layouts/sidebar.tsx`: + - เมนูหลัก: + - Dashboard + - Correspondences + - RFAs + - Drawings (Shop & Contract) + - Circulations + - Transmittals + - Search + - Reports + - เมนู Admin (แสดงตามสิทธิ์): + - Users + - Roles & Permissions + - Master Data + - Document Numbering +- Collapsible Sidebar +- Active State Highlighting +- Deliverable: ✅ Sidebar พร้อม + +- **T1.9 Setup Global Error Handling & Resilience** + + ```typescript + +// สร้าง lib/error-handling/global-error-handler.ts +export class GlobalErrorHandler { + static setup() { + // API Error Interceptors + apiClient.interceptors.response.use( + (response) => response, + (error) => { + this.handleApiError(error); + return Promise.reject(error); + } + ); + + // Frontend Error Boundary + window.addEventListener('error', this.handleWindowError); + window.addEventListener('unhandledrejection', this.handlePromiseRejection); + } + + static handleApiError(error: any) { + // Circuit Breaker Pattern + if (error.response?.status >= 500) { + this.circuitBreaker.recordFailure(); + } + + // User-friendly Error Messages + const userMessage = this.getUserFriendlyMessage(error); + this.showErrorToast(userMessage); + + // Error Reporting + this.reportErrorToService(error); + } +} + + ``` + +T1.10 Setup Security Foundation + + ```typescript +// สร้าง lib/security/security-config.ts +export const SecurityConfig = { + fileUpload: { + allowedTypes: ['pdf', 'dwg', 'docx', 'xlsx', 'zip'], + maxSize: 50 * 1024 * 1024, // 50MB + virusScanRequired: true, + scanTimeout: 30000 // 30 seconds + }, + rateLimiting: { + maxRequests: { + anonymous: 100, + viewer: 500, + editor: 1000, + documentControl: 2000, + admin: 5000 + } + } +}; + ``` + +--- + +### **Phase 2: Dashboard & Common Components (สัปดาห์ที่ 4)** + +**Milestone:** Dashboard และ Reusable Components + +- **T2.1 Create Reusable Components** + +- `components/features/common/data-table.tsx`: + - ใช้ TanStack Table + - รองรับ Pagination, Sorting, Filtering + - Responsive +- `components/features/common/file-upload.tsx`: + - ใช้ React Dropzone + - รองรับ Multi-file Upload + - Drag & Drop + - Progress Bar +- `components/features/common/status-badge.tsx`: + - แสดง Status แบบสีสัน (Draft, Submitted, Approved) +- `components/features/common/permission-guard.tsx`: + - ซ่อน/แสดง Component ตามสิทธิ์ + - +- Deliverable: ✅ Reusable Components พร้อม + +- **T2.2 Create Dashboard Page** + +- สร้าง `app/(protected)/dashboard/page.tsx`: + - **KPI Cards Section**: + - จำนวนเอกสารทั้งหมด + - งานที่รอดำเนินการ + - เอกสารที่เกินกำหนด + - RFA ที่รออนุมัติ + - **My Tasks Table**: + - ดึงข้อมูลจาก `/api/circulations/my-tasks` (ใช้ `v_user_tasks`) + - แสดงรายการงานที่ต้องทำ (Pending, In Progress) + - Columns: Document Number, Title, Type, Status, Deadline, Actions + - คลิกแถวแล้วไปยังหน้า Detail + - **Recent Activity Feed**: + - ดึงข้อมูลจาก `/api/audit-logs/user/:userId` + - แสดง 10 รายการล่าสุด +- Deliverable: ✅ Dashboard ครบถ้วน + +- **T2.3 Create API Hooks** + +- สร้าง `lib/api/hooks/use-my-tasks.ts`: + + ```typescript + import { useQuery } from '@tanstack/react-query'; + import { apiClient } from '../client'; + + export function useMyTasks() { + return useQuery({ + queryKey: ['my-tasks'], + queryFn: async () => { + const response = await apiClient.get('/circulations/my-tasks'); + return response.data; + }, + }); + } + ``` + +- สร้าง Hooks เพิ่มเติม: + - `use-dashboard-stats.ts` + - `use-recent-activity.ts` +- Deliverable: ✅ API Hooks พร้อม + +--- + +### **Phase 3: Correspondence Management (สัปดาห์ที่ 5-6)** + +**Milestone:** ระบบจัดการเอกสารโต้ตอบ + +- **T3.1 Create Correspondence List Page** + +- สร้าง `app/(protected)/correspondences/page.tsx`: + - Data Table แสดงรายการเอกสาร + - Columns: + - Document Number (คลิกไปยัง Detail) + - Title + - Type + - Status (Badge) + - Originator + - Date + - Actions (View, Edit, Delete) + - Filters: + - ประเภทเอกสาร (Dropdown) + - สถานะ (Dropdown) + - วันที่ (Date Range Picker) + - องค์กร (Dropdown) + - Pagination (Server-side) + - Search Box + - Create Button (ตรวจสิทธิ์) +- Deliverable: ✅ หน้ารายการเอกสาร + +- **T3.2 Create Correspondence Detail Page** + +- สร้าง `app/(protected)/correspondences/[id]/page.tsx`: + - **Header Section**: + - Document Number (ใหญ่) + - Status Badge + - Action Buttons: Edit, Delete, Export PDF + - **Metadata Section**: + - Title, Description + - Document Date, Issued Date, Received Date + - Originator, Recipients (TO/CC) + - Due Date (ถ้ามี) + - **Revision History**: + - แสดง Revisions ทั้งหมดเป็น Timeline + - แต่ละ Revision แสดง: Revision Number, Date, Changes, User + - **Attachments**: + - แสดงไฟล์แนบทั้งหมด + - กำหนดไฟล์หลัก (Main Document) ด้วยไอคอน + - ปุ่ม Download + - **References**: + - แสดงเอกสารที่อ้างถึง (Links) + - - **Tags**: + - แสดง Tags แบบ Chips + - **Activity Log**: + - แสดง Audit Log ของเอกสารนี้ +- Deliverable: ✅ หน้า Detail ครบถ้วน + +- **T3.3 Create Correspondence Form (Create/Edit)** + +- สร้าง `app/(protected)/correspondences/create/page.tsx`: +- สร้าง `app/(protected)/correspondences/[id]/edit/page.tsx`: +- Form Fields: + - **Basic Info**: + - Correspondence Type (Dropdown) + - Title (Text) + - Description (Textarea) + - Document Date (Date Picker) + - **Recipients**: + - TO: Multi-select Organizations + - CC: Multi-select Organizations + - **References**: + - Search & Select เอกสารอื่นๆ + - - **Tags**: + - Autocomplete Tag Input + - **Attachments**: + - File Upload (Multi-file) + - กำหนด Main Document (Radio) + - **Deadline**: + - Due Date (Date Picker) +- Validation ด้วย Zod +- Submit → API → Redirect to Detail +- Deliverable: ✅ ฟอร์มสร้าง/แก้ไข + +- **T3.4 Create Status Management** + +- ใน Detail Page เพิ่มปุ่ม Status Actions: + - **Draft → Submit** (Document Control) + - **Submit → Close** (Admin) + - **Submit → Cancel** (Admin + Dialog ให้กรอกเหตุผล) +- แสดง Confirmation Dialog ก่อนเปลี่ยนสถานะ +- Deliverable: ✅ เปลี่ยนสถานะได้ + +--- + +### **Phase 4: RFA & Workflow Visualization (สัปดาห์ที่ 7-8)** + +**Milestone:** ระบบ RFA และ Workflow + +- **T4.1 Create RFA List Page** + +- สร้าง `app/(protected)/rfas/page.tsx`: + - คล้าย Correspondence List + - Columns เพิ่มเติม: + - RFA Type (DWG, DOC, MAT) + - Approval Status (Badge) + - Approval Code (1A, 3R, etc.) + - Filters: + - RFA Type + - Status + - Approval Code +- Deliverable: ✅ หน้ารายการ RFA + +- **T4.2 Create RFA Detail Page** + +- สร้าง `app/(protected)/rfas/[id]/page.tsx`: + - คล้าย Correspondence Detail + - เพิ่ม Section: + - **Shop Drawings** (สำหรับ RFA_DWG): + - แสดงรายการ Shop Drawings ที่เชื่อมโยง + - แสดง Revision ของแต่ละแบบ + - Link ไปยัง Shop Drawing Detail + - **Workflow Visualization** (ดูรายละเอียดใน T4.3) +- Deliverable: ✅ หน้า Detail RFA + +- **T4.3 Create Workflow Visualization Component** + +- สร้าง `components/features/rfa/workflow-visualizer.tsx`: + - **Layout**: Steps แนวนอน (Timeline) + - **Step States**: + - ✅ **Completed**: สีเขียว, ไอคอน Check + - ⏳ **Active**: สีฟ้า, ปุ่ม Action เปิดใช้งาน + - ⏸️ **Pending**: สีเทา, ปุ่ม disabled + - ❌ **Rejected**: สีแดง + - **Step Info Card** (เมื่อคลิก): + - Organization + - Assigned User + - Action Type (Review, Approve) + - Status + - Comments + - Completed Date + - **Actions** (สำหรับ Active Step): + - ปุ่ม "อนุมัติ" (Approve) + - ปุ่ม "ปฏิเสธ" (Reject) + - Dialog ให้กรอก Comments + - **Admin Override**: + - ปุ่ม "ไปยังขั้นตอนต่อไป" (Skip Step) + - ปุ่ม "ย้อนกลับ" (Previous Step) +- Deliverable: ✅ Workflow Component พร้อม + +- **T4.4 Create RFA Form (Create/Edit)** + +- สร้าง `app/(protected)/rfas/create/page.tsx`: +- Form Fields: + - RFA Type (Dropdown) + - Title, Description + - Document Date + - **Shop Drawings Section** (สำหรับ RFA_DWG): + - Search & Select Shop Drawings + - แสดง Revisions ที่มี (Dropdown) + - เพิ่มได้หลายแบบ + - Attachments + - Workflow Template (Dropdown) +- Submit → สร้าง RFA + Start Workflow +- Deliverable: ✅ ฟอร์ม RFA พร้อม + +- **T4.5 Implement Workflow Actions API** + +- สร้าง `lib/api/hooks/use-rfa-workflow.ts`: + + ```typescript + import { useMutation, useQueryClient } from '@tanstack/react-query'; + import { apiClient } from '../client'; + + export function useCompleteWorkflowStep() { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ rfaId, stepNumber, action, comments }) => { + const response = await apiClient.post( + `/rfas/${rfaId}/workflow/steps/${stepNumber}/complete`, + { action, comments } + ); + return response.data; + }, + onSuccess: (_, variables) => { + queryClient.invalidateQueries(['rfa', variables.rfaId]); + queryClient.invalidateQueries(['my-tasks']); + }, + }); + } + ``` + +- Hooks เพิ่มเติม: + - `use-reject-workflow-step.ts` + - `use-start-workflow.ts` +- Deliverable: ✅ Workflow Actions ทำงานได้ + +--- + +### **Phase 5: Drawing Management (สัปดาห์ที่ 9)** + +**Milestone:** ระบบจัดการแบบ + +- **T5.1 Create Shop Drawing List Page** + +- สร้าง `app/(protected)/drawings/shop/page.tsx`: + - Data Table: + - Drawing Number + - Title + - Main Category + - Sub Category + - Current Revision + - Actions + - Filters: + - Category (Dropdown) + - Sub Category (Dropdown) + - Create Button +- Deliverable: ✅ หน้ารายการ Shop Drawings + +- **T5.2 Create Shop Drawing Detail Page** + +- สร้าง `app/(protected)/drawings/shop/[id]/page.tsx`: + - **Header**: Drawing Number, Title + - **Current Revision Info**: + - Revision Number, Date + - Description + - Attachments (PDF, DWG) + - **Contract Drawing References**: + - แสดง Contract Drawings ที่อ้างถึง + - Links ไปยัง Contract Drawing Detail + - **Revision History**: + - แสดง Revisions ทั้งหมดเป็น Timeline + - **Related RFAs**: + - แสดง RFAs ที่เชื่อมโยงกับแบบนี้ +- Deliverable: ✅ หน้า Detail Shop Drawing + +- **T5.3 Create Shop Drawing Form** + +- สร้าง `app/(protected)/drawings/shop/create/page.tsx`: +- Form Fields: + - Drawing Number (Auto-generate หรือ Manual) + - Title + - Main Category (Dropdown) + - Sub Category (Dropdown, dependent on Main) + - **Contract Drawing References**: + - Search & Select Contract Drawings (Multi-select) + - **Revision Info**: + - Revision Number (Auto) + - Revision Label (A, B, C) + - Description + - **Attachments**: + - Upload PDF (Main) + - Upload DWG (Optional) + - Upload Other Files +- Deliverable: ✅ ฟอร์ม Shop Drawing + +- **T5.4 Create Contract Drawing List & Detail** + +- สร้าง `app/(protected)/drawings/contract/page.tsx`: + - Data Table: + - Drawing Number + - Title + - Volume + - Category + - Sub Category + - Filters: + - Volume (Dropdown) + - Category (Dropdown) +- สร้าง `app/(protected)/drawings/contract/[id]/page.tsx`: + - แสดง Metadata + - Attachments + - **Referenced By**: + - แสดง Shop Drawings ที่อ้างถึงแบบนี้ +- Deliverable: ✅ Contract Drawing Pages + +--- + +### **Phase 6: Circulation & Transmittal (สัปดาห์ที่ 10)** + +**Milestone:** ระบบใบเวียนและเอกสารนำส่ง + +- **T6.1 Create Circulation List Page** + +- สร้าง `app/(protected)/circulations/page.tsx`: + - Data Table: + - Circulation Number + - Subject + - Status (Badge) + - Created By + - Created Date + - Filters: + - Status + - Date Range +- Deliverable: ✅ หน้ารายการใบเวียน + +- **T6.2 Create Circulation Detail & Workflow** + +- สร้าง `app/(protected)/circulations/[id]/page.tsx`: + - **Header**: Circulation Number, Subject, Status + - **Linked Correspondence**: + - Link ไปยังเอกสารต้นทาง + - **Workflow Visualization**: + - คล้าย RFA Workflow + - แสดง Steps: + - Organization + - Assigned Users (Main, Action, Information) + - Status + - Comments + - Deadline + - **Actions** (สำหรับ Assigned User): + - ปุ่ม "ดำเนินการเสร็จสิ้น" (Complete) + - Dialog ให้กรอก Comments + - **Close Circulation** (Document Control): + - ปุ่ม "ปิดใบเวียน" (เมื่อตอบกลับองค์กรผู้ส่งแล้ว) +- Deliverable: ✅ หน้า Detail ใบเวียน + +- **T6.3 Create Circulation Form** + +- สร้าง `app/(protected)/circulations/create/page.tsx`: +- Form Fields: + - **Linked Correspondence**: + - Search & Select Correspondence (Required) + - **Subject**: (Text) + - **Workflow Template** (Optional): + - Dropdown เลือก Template ที่มีอยู่ + - หรือสร้างแบบ Custom + - **Assignees Section**: + - **Main** (ผู้รับผิดชอบหลัก): + - Multi-select Users + - กำหนด Deadline (Date Picker) + - **Action** (ผู้ร่วมปฏิบัติงาน): + - Multi-select Users + - กำหนด Deadline + - **Information** (ผู้ที่ต้องรับทราบ): + - Multi-select Users + - ไม่ต้องกำหนด Deadline + - **Attachments** (Optional): + - อัปโหลดไฟล์เพิ่มเติมนอกจากเอกสารต้นทาง +- Submit → สร้าง Circulation + Send Notifications +- Deliverable: ✅ ฟอร์มสร้างใบเวียน + +- **T6.4 Create Circulation Template Management** + +- สร้าง `app/(protected)/admin/circulation-templates/page.tsx`: + - List Templates + - Create/Edit Template + - Template Form Fields: + - Template Name + - Description + - **Steps**: + - Step Number (Auto) + - Organization (Dropdown) + - Role (Dropdown, Optional) + - Duration (Days) + - Is Optional (Checkbox) +- Deliverable: ✅ จัดการ Template ได้ + +- **T6.5 Create Transmittal Pages** + +- สร้าง `app/(protected)/transmittals/page.tsx`: + - Data Table: + - Transmittal Number + - Purpose (Badge) + - TO Organization + - Date + - Item Count +- สร้าง `app/(protected)/transmittals/[id]/page.tsx`: + - Header: Transmittal Number, Purpose + - Metadata: Originator, Recipients, Date, Remarks + - **Items Section**: + - Data Table: + - Document Number (Link) + - Title + - Type + - Quantity + - Remarks +- สร้าง `app/(protected)/transmittals/create/page.tsx`: + - Form Fields: + - Purpose (Dropdown: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW) + - TO Organization (Dropdown) + - CC Organizations (Multi-select) + - Remarks (Textarea) + - **Items**: + - Search & Select Correspondences/RFAs (Multi-select) + - กำหนด Quantity และ Remarks ต่อแต่ละรายการ + - Attachments (Cover Letter) +- Deliverable: ✅ ระบบ Transmittal ครบถ้วน + +--- + +### **Phase 7: Search & Reports (สัปดาห์ที่ 11)** + +**Milestone:** ระบบค้นหาและรายงาน + +- **T7.1 Create Advanced Search Page** + +- สร้าง `app/(protected)/search/page.tsx`: + - **Search Filters Panel** (Sidebar): + - Document Type (Checkboxes): + - Correspondence + - RFA + - Shop Drawing + - Contract Drawing + - Circulation + - Transmittal + - Text Search: + - Keyword (Full-text) + - Document Number + - Title + - Date Range: + - From Date (Date Picker) + - To Date (Date Picker) + - Status (Multi-select) + - Organization (Multi-select) + - Tags (Autocomplete Multi-select) + - Project (Dropdown) + - Contract (Dropdown) + - **Search Results Panel** (Main Area): + - Data Table: + - Document Type (Icon + Badge) + - Document Number (Link) + - Title + - Status + - Date + - Organization + - Match Score (จาก Elasticsearch) + - Pagination + - Sort Options (Relevance, Date, Title) + - **Export Results**: + - ปุ่ม "Export to CSV" + - ปุ่ม "Export to Excel" +- Deliverable: ✅ หน้าค้นหาขั้นสูง + +- **T7.2 Create Search API Hooks** + +- สร้าง `lib/api/hooks/use-search.ts`: + + ```typescript + import { useQuery } from '@tanstack/react-query'; + import { apiClient } from '../client'; + + interface SearchFilters { + q?: string; + types?: string[]; + statuses?: string[]; + organizations?: number[]; + tags?: number[]; + from?: string; + to?: string; + page?: number; + pageSize?: number; + } + + export function useSearch(filters: SearchFilters) { + return useQuery({ + queryKey: ['search', filters], + queryFn: async () => { + const response = await apiClient.get('/search', { params: filters }); + return response.data; + }, + enabled: !!filters.q || Object.keys(filters).length > 1, + }); + } + ``` + +- Deliverable: ✅ Search Hooks พร้อม + +- **T7.3 Create Reports Page** + +- สร้าง `app/(protected)/reports/page.tsx`: + - **Report Types** (Tabs): + - **Correspondence Summary**: + - Filter: Project, Date Range, Type + - Chart: Bar Chart (จำนวนเอกสารแยกตามประเภท) + - Table: รายการเอกสารพร้อมสถิติ + - **RFA Summary**: + - Filter: Project, Date Range, Type + - Chart: Pie Chart (สัดส่วนสถานะ) + - Table: รายการ RFA พร้อมสถิติ + - **Activity Report**: + - Filter: User, Date Range + - Timeline: แสดงกิจกรรมตามเวลา + - Table: รายละเอียดกิจกรรม + - **Overdue Report**: + - แสดงเอกสาร/งานที่เกินกำหนด + - Group by: Type, Organization, User + - **Export Options**: + - PDF Report (ใช้ Browser Print) + - Excel Export +- Deliverable: ✅ หน้ารายงาน + +- **T7.4 Integrate Charts (Recharts)** + +```bash +npm install recharts +``` + +- สร้าง Chart Components: + - `components/features/reports/bar-chart.tsx` + - `components/features/reports/pie-chart.tsx` + - `components/features/reports/line-chart.tsx` +- ใช้ใน Reports Page +- Deliverable: ✅ Charts พร้อมใช้ + +--- + +### **Phase 8: Admin Panel (สัปดาห์ที่ 12-13)** + +**Milestone:** ระบบจัดการสำหรับ Admin + +- **T8.1 Create User Management Pages** + +- สร้าง `app/(protected)/admin/users/page.tsx`: + - **Permission Guard**: ตรวจสอบสิทธิ์ `users.view` + - Data Table: + - Username + - Name + - Email + - Organization + - Roles (Badges) + - Status (Active/Inactive) + - Actions (Edit, Deactivate, Reset Password) + - Filters: + - Organization + - Role + - Status + - Create User Button +- สร้าง `app/(protected)/admin/users/create/page.tsx`: + - Form Fields: + - Username (Text, Unique) + - Email (Email, Unique) + - Password (Password) + - First Name, Last Name (Text) + - Line ID (Text, Optional) + - Primary Organization (Dropdown) + - **Global Role** (Dropdown, Optional) + - **Project Roles** (Multi-select): + - เลือก Project + Role + - **Contract Roles** (Multi-select): + - เลือก Contract + Role +- สร้าง `app/(protected)/admin/users/[id]/edit/page.tsx`: + - คล้าย Create Form + - เพิ่ม: Deactivate Button, Reset Password Button +- Deliverable: ✅ จัดการผู้ใช้ได้ + +- **T8.2 Create Role & Permission Management** + +- สร้าง `app/(protected)/admin/roles/page.tsx`: + - **Permission Guard**: `roles.view` + - List Roles (Cards): + - Role Name + - Scope (Global, Organization, Project, Contract) + - Permission Count + - Edit Button +- สร้าง `app/(protected)/admin/roles/[id]/edit/page.tsx`: + - Form Fields: + - Role Name (Text) + - Scope (Radio: Global, Organization, Project, Contract) + - Description (Textarea) + - **Permissions** (Grouped Checkboxes): + - Group by Module: + - System Management + - User Management + - Project Management + - Document Management + - Workflow Management + - Search & Reporting + - แต่ละ Permission มี Checkbox + Description +- Deliverable: ✅ จัดการ Roles และ Permissions ได้ + +- **T8.3 Create Master Data Management** + +- สร้าง `app/(protected)/admin/master-data/page.tsx`: + - - **Tabs**: + - **Correspondence Types**: + - List + Create/Edit Form + - Fields: Type Code, Type Name, Sort Order + - **RFA Types**: + - List + Create/Edit Form + - **Status Codes**: + - List + Create/Edit Form + - - **Tags**: + - List + Create/Edit Form + - **Drawing Categories**: + - List + Create/Edit Form (Main & Sub Categories) +- Deliverable: ✅ จัดการ Master Data ได้ + +- **T8.4 Create Document Numbering Management** + +- สร้าง `app/(protected)/admin/document-numbering/page.tsx`: + - **Document Number Formats Section**: + - Data Table: + - Project + - Document Type + - Format Template + - Example + - Actions (Edit, Delete) + - Create Format Button + - **Create/Edit Format Form**: + - Project (Dropdown) + - Document Type (Dropdown) + - Format Template (Text): + - แสดง Available Placeholders: + - `{ORG_CODE}` - รหัสองค์กร + - `{TYPE_CODE}` - รหัสประเภทเอกสาร + - `{YEAR}` - ปี พ.ศ. 4 หลัก + - `{YEAR_SHORT}` - ปี พ.ศ. 2 หลัก + - `{SEQ:n}` - เลขลำดับ (n = จำนวนหลัก) + - Live Preview: แสดงตัวอย่างเลขที่จากรูปแบบที่กรอก + - Description (Textarea) + - **Current Counters Section** (Read-only): + - Data Table: + - Project + - Organization + - Document Type + - Year + - Last Number +- Deliverable: ✅ จัดการ Document Numbering ได้ + +- **T8.5 Create Organization Onboarding Workflow** + +- สร้าง `app/(protected)/admin/organizations/page.tsx`: + - **Permission Guard**: `organizations.manage` (Superadmin only) + - List Organizations: + - Organization Code + - Organization Name + - Status + - Org Admin (ชื่อ) + - Actions + - Create Organization Button +- สร้าง `app/(protected)/admin/organizations/create/page.tsx`: + - **Step 1: Organization Info**: + - Organization Code (Text, Unique) + - Organization Name (Text) + - Description (Textarea) + - **Step 2: Appoint Org Admin**: + - Search & Select User + - หรือ Create New User + - Assign Role: "Org Admin" + - **Step 3: Confirmation**: + - แสดงสรุปข้อมูล + - Submit → สร้างองค์กรและ Assign Admin +- Deliverable: ✅ Superadmin สามารถ Onboard องค์กรใหม่ได้ + +- **T8.6 Create Project & Contract Management** + +- สร้าง `app/(protected)/admin/projects/page.tsx`: + - **Permission Guard**: `projects.view` + - List Projects + - Create/Edit Project + - **Project Detail Page**: + - Metadata + - **Participating Organizations** (Multi-select) + - **Contracts Section**: + - List Contracts ในโครงการนี้ + - Create Contract Button + - **Members Section**: + - List Users ในโครงการ + - Assign User to Project Button + - กำหนด Project Role +- สร้าง `app/(protected)/admin/contracts/[id]/page.tsx`: + - Contract Info + - **Contract Organizations**: + - Organization + Role in Contract (Owner, Designer, Contractor) + - **Members Section**: + - Assign User to Contract + - กำหนด Contract Role +- Deliverable: ✅ จัดการ Projects และ Contracts ได้ + +--- + +### **Phase 9: User Profile & Settings (สัปดาห์ที่ 14)** + +**Milestone:** หน้าโปรไฟล์และการตั้งค่า + +- **T9.1 Create Profile Page** + +- สร้าง `app/(protected)/profile/page.tsx`: + - **User Info Section**: + - Avatar (Upload รูปภาพ) + - Username (Read-only) + - Email (Editable) + - First Name, Last Name (Editable) + - Line ID (Editable) + - Primary Organization (Read-only) + - **My Roles Section**: + - แสดง Roles ทั้งหมดที่ได้รับ: + - Global Role + - Organization Roles + - Project Roles + - Contract Roles + - แสดงเป็น Cards พร้อม Scope และ Permissions + - **Change Password Section**: + - Current Password (Password) + - New Password (Password) + - Confirm Password (Password) + - Submit Button + - **Notification Settings**: + - Email Notifications (Toggle) + - Line Notifications (Toggle) + - ประเภทการแจ้งเตือน (Checkboxes): + - New Document Received + - Task Assigned + - Document Approved/Rejected + - Approaching Deadline +- Deliverable: ✅ หน้า Profile พร้อม + +- **T9.2 Create Settings Page** (Optional) + +- สร้าง `app/(protected)/settings/page.tsx`: + - **Display Preferences**: + - Language (ไทย/English) - Future + - Date Format (DD/MM/YYYY, MM/DD/YYYY) + - Time Zone (Auto-detect) + - - **Table Preferences**: + - Default Page Size (10, 20, 50) + - Default Sort Order + - **Export Preferences**: + - Default Export Format (CSV, Excel) +- Deliverable: ✅ หน้า Settings พร้อม + +--- + +### **Phase 10: Testing & Optimization (สัปดาห์ที่ 15-16)** + +**Milestone:** ทดสอบและปรับปรุงประสิทธิภาพ + +- **T10.1 Setup Testing Environment** + +- ติดตั้ง Testing Libraries: + + ```bash + npm install -D vitest @testing-library/react @testing-library/jest-dom + npm install -D @testing-library/user-event + npm install -D msw + npm install -D @playwright/test + ``` + +- สร้าง `vitest.config.ts` +- สร้าง `playwright.config.ts` +- Setup MSW Handlers: `lib/mocks/handlers.ts` +- Deliverable: ✅ Testing Setup พร้อม + +- **T10.2 Unit Testing** + +- เขียน Unit Tests สำหรับ: + - **Utilities**: `lib/utils/*.ts` + - **Hooks**: `lib/api/hooks/*.ts` + - **Components**: + - `components/features/common/data-table.test.tsx` + - `components/features/common/file-upload.test.tsx` + - `components/features/common/status-badge.test.tsx` +- Target: 70% Code Coverage +- Deliverable: ✅ Unit Tests ผ่านทั้งหมด + +- **T10.3 Integration Testing** + +- เขียน Integration Tests สำหรับ: + - **Authentication Flow**: + - Login → Dashboard → Logout + - **Correspondence Flow**: + - List → Create → Detail → Edit + - **RFA Workflow**: + - Create RFA → Start Workflow → Complete Step + - **Circulation Flow**: + - Create → Assign → Complete → Close +- ใช้ MSW เพื่อ Mock API Responses +- Deliverable: ✅ Integration Tests ผ่าน + +- **T10.4 E2E Testing (Playwright)** + +- เขียน E2E Tests สำหรับ Critical User Flows: + - **User Registration & Login** + - **Create Correspondence (Full Flow)**: + - Fill Form → Upload Files → Submit → Verify in List + - **Create RFA with Shop Drawings**: + - Select Drawings → Fill Form → Start Workflow → Verify Status + - **Complete RFA Workflow**: + - Login as Reviewer → Go to RFA → Complete Step → Verify Next Step + - **Search Documents**: + - Enter Query → Verify Results → Click Result → Verify Detail +- Deliverable: ✅ E2E Tests ผ่าน + +- **T10.5 Performance Optimization** + +- **Code Splitting**: + - ใช้ Dynamic Imports สำหรับ Heavy Components + - Example: + + ```typescript + const RfaWorkflowVisualizer = dynamic( + () => import('@/components/features/rfa/workflow-visualizer'), + { loading: () => } + ); + ``` + +- **Image Optimization**: + - ใช้ Next.js `` Component + - Setup Image CDN (ถ้ามี) +- **Data Fetching Optimization**: + - ใช้ `prefetchQuery` สำหรับ Critical Data + - Implement Infinite Scroll สำหรับ Long Lists +- **Bundle Size Analysis**: + + ```bash + npm run build + npx @next/bundle-analyzer + ``` + + - ลบ Dependencies ที่ไม่ใช้ + - Tree Shaking +- Deliverable: ✅ Performance Metrics ดีขึ้น + +- **T10.6 Accessibility (a11y) Testing** + +- ติดตั้ง `@axe-core/react` +- รัน axe DevTools ในหน้าสำคัญ +- แก้ไข Issues: + - Missing alt text + - Low contrast ratios + - Missing ARIA labels + - Keyboard navigation +- Target: Zero Critical/Serious Issues +- Deliverable: ✅ Accessibility Checklist ผ่าน + +- **T10.7 Responsive Design Testing** + +- ทดสอบบนอุปกรณ์ต่างๆ: + - Desktop (1920x1080, 1366x768) + - Tablet (768x1024) + - Mobile (375x667, 414x896) +- แก้ไข Layout Issues: + - Sidebar Collapse บน Mobile + - Table Horizontal Scroll + - Form Layout +- Deliverable: ✅ Responsive บนทุกอุปกรณ์ + +- **T10.8 Security Hardening** + +- **Input Sanitization**: + - ตรวจสอบ XSS ใน User Inputs + - ใช้ DOMPurify สำหรับ Rich Text (ถ้ามี) +- **CSRF Protection**: + - Verify NextAuth CSRF Tokens +- **Content Security Policy**: + - Setup CSP Headers ใน `next.config.js` +- **API Security**: + - ตรวจสอบว่า Token ถูกส่งใน Headers ถูกต้อง + - Handle 401/403 Errors properly +- Deliverable: ✅ Security Checklist ผ่าน + +--- + +### **Phase 11: Documentation & Deployment (สัปดาห์ที่ 17)** + +**Milestone:** เอกสารและ Deploy สู่ Production + +- **T11.1 Component Documentation (Storybook)** (Optional) + +- ติดตั้ง Storybook: + + ```bash + npx storybook@latest init + ``` + +- เขียน Stories สำหรับ Reusable Components: + - Button, Input, Card, Table + - FileUpload, StatusBadge, PermissionGuard + - WorkflowVisualizer +- Deliverable: ✅ Storybook พร้อม + +- **T11.2 User Documentation** + +- เขียนคู่มือผู้ใช้งาน (ภาษาไทย): + - **Getting Started**: + - วิธีล็อกอิน + - ภาพรวม Dashboard + - **Correspondence Management**: + - วิธีสร้างเอกสาร + - วิธีแก้ไขและเปลี่ยนสถานะ + - วิธีค้นหาเอกสาร + - **RFA Workflow**: + - วิธีสร้าง RFA + - วิธีตรวจสอบและอนุมัติ + - วิธีติดตาม Workflow + - **Circulation**: + - วิธีสร้างใบเวียน + - วิธีดำเนินการตามใบเวียน + - **Drawing Management**: + - วิธีอัปโหลดแบบ + - วิธีเชื่อมโยงแบบ + - **Admin Functions**: + - วิธีจัดการผู้ใช้ + - วิธีกำหนดสิทธิ์ + - วิธีจัดการ Master Data +- Format: PDF + Online (ใน `/docs` Route) +- Deliverable: ✅ User Guide พร้อม + +- **T11.3 Developer Documentation** + +- เขียนเอกสารทางเทคนิค: + - **Architecture Overview**: + - Tech Stack + - Folder Structure + - State Management Strategy + - **Component Guidelines**: + - Naming Conventions + - Props Interface Design + - Styling Best Practices + - **API Integration**: + - API Client Setup + - React Query Usage + - Error Handling + - - **Testing Guidelines**: + - Unit Test Examples + - Integration Test Examples + - E2E Test Examples + - **Deployment Guide**: + - Build Process + - Environment Variables + - Docker Configuration +- Format: Markdown (ใน `/docs` Folder) +- Deliverable: ✅ Dev Docs พร้อม + +- **T11.4 Deployment Preparation** + +- **Production Build**: + + ```bash + npm run build + ``` + + - ตรวจสอบ Build Output + - แก้ไข Build Errors/Warnings +- **Environment Variables**: + - สร้างไฟล์ `.env.production` + - กำหนดค่าใน docker-compose.yml: + + ```yaml + environment: + - NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api + - NEXTAUTH_URL=https://lcbp3.np-dms.work + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} + ``` + +- **Docker Image**: + - Build Image: + + ```bash + docker build -t lcbp3-frontend:v1.0.0 . + ``` + + - Test Image Locally +- Deliverable: ✅ Production Build พร้อม + +- **T11.5 Deploy to QNAP** + +- **Upload Image to QNAP**: + - Export Image: + + ```bash + docker save lcbp3-frontend:v1.0.0 > frontend.tar + ``` + + - Upload ผ่าน QNAP File Station + - Import ใน Container Station +- **Update docker-compose.yml** บน QNAP: + + ```yaml + services: + frontend: + image: lcbp3-frontend:v1.0.0 + container_name: frontend + networks: + - lcbp3 + environment: + - NEXT_PUBLIC_API_URL=https://backend.np-dms.work/api + - NEXTAUTH_URL=https://lcbp3.np-dms.work + - NEXTAUTH_SECRET=${NEXTAUTH_SECRET} + restart: unless-stopped + ``` + +- **Start Container**: + - รันผ่าน Container Station UI + - ตรวจสอบ Logs +- Deliverable: ✅ Frontend รันบน Production + +- **T11.6 Configure Nginx Proxy Manager** + +- เพิ่ม Proxy Host ใหม่: + - Domain: `lcbp3.np-dms.work` + - Forward to: `frontend:3000` + - Enable Websocket Support + - Enable SSL (Let's Encrypt) + - Force HTTPS +- ทดสอบเข้าถึงผ่าน HTTPS +- Deliverable: ✅ HTTPS พร้อมใช้ + +- **T11.7 Monitoring & Health Check** + +- สร้าง Health Check Endpoint (ถ้า Backend ยังไม่มี): + + ```typescript + // app/api/health/route.ts + export async function GET() { + return Response.json({ status: 'ok', timestamp: new Date().toISOString() }); + } + ``` + +- Setup Monitoring: + - QNAP Container Resource Monitor + - Log Aggregation (ถ้ามี) +- Deliverable: ✅ Monitoring Setup + +- **T11.8 User Acceptance Testing (UAT)** + +- เตรียม UAT Checklist: + - ✅ ล็อกอินได้ทุก Role + - ✅ สร้าง Correspondence ได้ + - ✅ สร้าง RFA และ Start Workflow ได้ + - ✅ Workflow อนุมัติได้ถูกต้อง + - ✅ สร้าง Circulation และมอบหมายงานได้ + - ✅ อัปโหลดไฟล์ได้ + - ✅ ค้นหาเอกสารได้ + - ✅ สร้างรายงานได้ + - ✅ Admin จัดการผู้ใช้และสิทธิ์ได้ +- เชิญ Stakeholders ทดสอบ +- เก็บ Feedback +- แก้ไข Bugs/Issues +- Deliverable: ✅ UAT ผ่าน + +- **T11.9 Training & Handover** + +- จัด Workshop สำหรับผู้ใช้งาน: + - **Session 1: Basic Users** (2 ชม.): + - วิธีใช้ Dashboard + - วิธีสร้างและจัดการเอกสาร + - วิธีตอบกลับ Circulation + - **Session 2: Document Control** (2 ชม.): + - วิธีจัดการเอกสารขั้นสูง + - วิธีควบคุม Workflow + - วิธีดู Reports + - **Session 3: Admin** (2 ชม.): + - วิธีจัดการผู้ใช้และสิทธิ์ + - วิธีจัดการ Master Data + - วิธีตั้งค่า Document Numbering +- บันทึก Video Training (Optional) +- Deliverable: ✅ Users ใช้งานเป็น + +--- + +## 📊 สรุป Timeline + +| Phase | ระยะเวลา | จำนวนงาน | Output หลัก | +| -------- | ------------ | ------------ | ----------------------------- | +| Phase 0 | 1 สัปดาห์ | 6 | Infrastructure Ready | +| Phase 1 | 2 สัปดาห์ | 8 | Auth & App Shell | +| Phase 2 | 1 สัปดาห์ | 3 | Dashboard & Components | +| Phase 3 | 2 สัปดาห์ | 4 | Correspondence Management | +| Phase 4 | 2 สัปดาห์ | 5 | RFA & Workflow | +| Phase 5 | 1 สัปดาห์ | 4 | Drawing Management | +| Phase 6 | 1 สัปดาห์ | 5 | Circulation & Transmittal | +| Phase 7 | 1 สัปดาห์ | 4 | Search & Reports | +| Phase 8 | 2 สัปดาห์ | 6 | Admin Panel | +| Phase 9 | 1 สัปดาห์ | 2 | Profile & Settings | +| Phase 10 | 2 สัปดาห์ | 8 | Testing & Optimization | +| Phase 11 | 1 สัปดาห์ | 9 | Documentation & Deploy | +| **รวม** | **17 สัปดาห์** | **64 Tasks** | **Production-Ready Frontend** | + +--- + +## 🎯 Critical Success Factors + +1. **API Contract**: รอ Backend Swagger Documentation ก่อนเริ่ม Phase 3 +2. **Design System**: สร้าง Design System ใน Figma ก่อนเริ่ม Phase 1 +3. **Component Library**: สร้าง Reusable Components ให้ครบใน Phase 2 ก่อนไปต่ + +## 🎯 Critical Success Factors (ต่อ) + +4. **Mobile-First Approach**: ออกแบบ Mobile ก่อน แล้วค่อย Scale up เป็น Desktop +5. **Accessibility**: ทดสอบ Keyboard Navigation และ Screen Reader ทุก Phase +6. **Performance Budget**: + - Initial Load < 3s + - Time to Interactive < 5s + - Bundle Size < 500KB (gzipped) +7. **User Feedback Loop**: ทำ Usability Testing ทุก 2 สัปดาห์ +8. **Code Quality**: Code Review ทุก PR, ห้าม Merge โดยไม่มี Tests + +--- + +## 🔄 Dependencies & Integration Points + +### Backend Dependencies (Critical Path) + +| Frontend Feature | Required Backend API | Blocker Phase | +| ------------------- | --------------------------------- | ------------- | +| Login | `POST /auth/login` | Phase 1 | +| Dashboard | `GET /api/circulations/my-tasks` | Phase 2 | +| Correspondence CRUD | `/api/correspondences/*` | Phase 3 | +| RFA Workflow | `/api/rfas/*/workflow/*` | Phase 4 | +| Drawing Management | `/api/drawings/*` | Phase 5 | +| Circulation | `/api/circulations/*` | Phase 6 | +| Search | `GET /api/search` (Elasticsearch) | Phase 7 | +| Reports | `/api/reports/*` | Phase 7 | +| User Management | `/api/users/*`, `/api/roles/*` | Phase 8 | + +**รอเอกสาร Swagger ให้ครบก่อน Phase 3** เพื่อหลีกเลี่ยงการแก้ไขซ้ำ + +--- + +## 🛠️ Development Workflow + +### Daily Routine + +``` +09:00-09:15 Daily Standup (อะไรทำเสร็จ, จะทำอะไร, มีอุปสรรคอะไร) +09:15-12:00 Development (Focus Time) +12:00-13:00 Lunch +13:00-16:00 Development + Code Review +16:00-16:30 Testing (Manual + Automated) +16:30-17:00 Documentation + Git Commit +``` + +### Git Workflow + +```bash +# Feature Branch Strategy +git checkout -g develop +git pull origin develop +git checkout -b feature/T3.1-correspondence-list + +# Work on Feature... +git add . +git commit -m "feat(correspondence): implement list page with filters + +- Add DataTable component +- Implement server-side pagination +- Add filters for type, status, date range +- Add search functionality + +Closes #T3.1" + +# Push และสร้าง Pull Request +git push origin feature/T3.1-correspondence-list +``` + +### Commit Message Convention + +``` +(): + + + +