diff --git a/specs/01-requirements/01-04-access-control.md b/specs/01-requirements/business-rules/01-04-access-control.md
similarity index 100%
rename from specs/01-requirements/01-04-access-control.md
rename to specs/01-requirements/business-rules/01-04-access-control.md
diff --git a/specs/03-implementation/03-04-document-numbering.md b/specs/01-requirements/business-rules/03-04-document-numbering.md
similarity index 100%
rename from specs/03-implementation/03-04-document-numbering.md
rename to specs/01-requirements/business-rules/03-04-document-numbering.md
diff --git a/specs/05-decisions/ADR-004-rbac-implementation.md b/specs/01-requirements/business-rules/ADR-004-rbac-implementation.md
similarity index 100%
rename from specs/05-decisions/ADR-004-rbac-implementation.md
rename to specs/01-requirements/business-rules/ADR-004-rbac-implementation.md
diff --git a/specs/01-requirements/01-03.11-document-numbering.md b/specs/01-requirements/business-rules/document-numbering.md
similarity index 100%
rename from specs/01-requirements/01-03.11-document-numbering.md
rename to specs/01-requirements/business-rules/document-numbering.md
diff --git a/specs/01-requirements/01-03.1-project-management.md b/specs/01-requirements/modules/01-03.1-project-management.md
similarity index 100%
rename from specs/01-requirements/01-03.1-project-management.md
rename to specs/01-requirements/modules/01-03.1-project-management.md
diff --git a/specs/01-requirements/01-03.12-json-details.md b/specs/01-requirements/modules/01-03.12-json-details.md
similarity index 100%
rename from specs/01-requirements/01-03.12-json-details.md
rename to specs/01-requirements/modules/01-03.12-json-details.md
diff --git a/specs/01-requirements/01-03.2-correspondence.md b/specs/01-requirements/modules/01-03.2-correspondence.md
similarity index 100%
rename from specs/01-requirements/01-03.2-correspondence.md
rename to specs/01-requirements/modules/01-03.2-correspondence.md
diff --git a/specs/01-requirements/01-03.3-rfa.md b/specs/01-requirements/modules/01-03.3-rfa.md
similarity index 100%
rename from specs/01-requirements/01-03.3-rfa.md
rename to specs/01-requirements/modules/01-03.3-rfa.md
diff --git a/specs/01-requirements/01-03.4-contract-drawing.md b/specs/01-requirements/modules/01-03.4-contract-drawing.md
similarity index 100%
rename from specs/01-requirements/01-03.4-contract-drawing.md
rename to specs/01-requirements/modules/01-03.4-contract-drawing.md
diff --git a/specs/01-requirements/01-03.5-shop-drawing.md b/specs/01-requirements/modules/01-03.5-shop-drawing.md
similarity index 100%
rename from specs/01-requirements/01-03.5-shop-drawing.md
rename to specs/01-requirements/modules/01-03.5-shop-drawing.md
diff --git a/specs/01-requirements/01-03.6-unified-workflow.md b/specs/01-requirements/modules/01-03.6-unified-workflow.md
similarity index 100%
rename from specs/01-requirements/01-03.6-unified-workflow.md
rename to specs/01-requirements/modules/01-03.6-unified-workflow.md
diff --git a/specs/01-requirements/01-03.7-transmittals.md b/specs/01-requirements/modules/01-03.7-transmittals.md
similarity index 100%
rename from specs/01-requirements/01-03.7-transmittals.md
rename to specs/01-requirements/modules/01-03.7-transmittals.md
diff --git a/specs/01-requirements/01-03.8-circulation-sheet.md b/specs/01-requirements/modules/01-03.8-circulation-sheet.md
similarity index 100%
rename from specs/01-requirements/01-03.8-circulation-sheet.md
rename to specs/01-requirements/modules/01-03.8-circulation-sheet.md
diff --git a/specs/01-requirements/01-03.9-logs.md b/specs/01-requirements/modules/01-03.9-logs.md
similarity index 100%
rename from specs/01-requirements/01-03.9-logs.md
rename to specs/01-requirements/modules/01-03.9-logs.md
diff --git a/specs/01-requirements/01-02-architecture.md b/specs/02-architecture/01-02-architecture.md
similarity index 100%
rename from specs/01-requirements/01-02-architecture.md
rename to specs/02-architecture/01-02-architecture.md
diff --git a/specs/08-infrastructure/03_Securities.md b/specs/02-architecture/03_Securities.md
similarity index 100%
rename from specs/08-infrastructure/03_Securities.md
rename to specs/02-architecture/03_Securities.md
diff --git a/specs/05-decisions/ADR-007-api-design-error-handling.md b/specs/02-architecture/ADR-007-api-design-error-handling.md
similarity index 100%
rename from specs/05-decisions/ADR-007-api-design-error-handling.md
rename to specs/02-architecture/ADR-007-api-design-error-handling.md
diff --git a/specs/03-Data-and-Storage/03-01-data-dictionary.md b/specs/03-Data-and-Storage/03-01-data-dictionary.md
new file mode 100644
index 0000000..18906d4
--- /dev/null
+++ b/specs/03-Data-and-Storage/03-01-data-dictionary.md
@@ -0,0 +1,2115 @@
+---
+title: 'Data & Storage: Data Dictionary and Data Model Architecture'
+version: 1.8.0
+status: released
+owner: Nattanin Peancharoen
+last_updated: 2026-02-22
+related:
+ - specs/01-requirements/02-architecture.md
+ - specs/01-requirements/03-functional-requirements.md
+ - docs/4_Data_Dictionary_V1_4_5.md
+ - docs/8_lcbp3_v1_4_5.sql
+---
+
+# 1. Data Model Architecture Overview
+
+## 📋 1.1 Overview
+เอกสารนี้อธิบายสถาปัตยกรรมของ Data Model สำหรับระบบ LCBP3-DMS โดยครอบคลุมโครงสร้างฐานข้อมูล, ความสัมพันธ์ระหว่างตาราง, และหลักการออกแบบที่สำคัญ
+
+## 🎯 1.2 Design Principles
+### 1. Separation of Concerns
+
+- **Master-Revision Pattern**: แยกข้อมูลที่ไม่เปลี่ยนแปลง (Master) จากข้อมูลที่มีการแก้ไข (Revisions)
+ - `correspondences` (Master) ↔ `correspondence_revisions` (Revisions)
+ - `rfas` (Master) ↔ `rfa_revisions` (Revisions)
+ - `shop_drawings` (Master) ↔ `shop_drawing_revisions` (Revisions)
+
+### 2. Data Integrity
+
+- **Foreign Key Constraints**: ใช้ FK ทุกความสัมพันธ์เพื่อรักษาความสมบูรณ์ของข้อมูล
+- **Soft Delete**: ใช้ `deleted_at` แทนการลบข้อมูลจริง เพื่อรักษาประวัติ
+- **Optimistic Locking**: ใช้ `version` column ใน `document_number_counters` ป้องกัน Race Condition
+
+### 3. Flexibility & Extensibility
+
+- **JSON Details Field**: เก็บข้อมูลเฉพาะประเภทใน `correspondence_revisions.details`
+- **Virtual Columns**: สร้าง Index จาก JSON fields สำหรับ Performance
+- **Master Data Tables**: แยกข้อมูล Master (Types, Status, Codes) เพื่อความยืดหยุ่น
+
+### 4. Security & Audit
+
+- **RBAC (Role-Based Access Control)**: ระบบสิทธิ์แบบ Hierarchical Scope
+- **Audit Trail**: บันทึกผู้สร้าง/แก้ไข และเวลาในทุกตาราง
+- **Two-Phase File Upload**: ป้องกันไฟล์ขยะด้วย Temporary Storage
+
+# 2. Database Schema Overview (ERD)
+### Entity Relationship Diagram
+
+```mermaid
+erDiagram
+ %% Core Entities
+ organizations ||--o{ users : "employs"
+ projects ||--o{ contracts : "contains"
+ projects ||--o{ correspondences : "manages"
+
+ %% RBAC
+ users ||--o{ user_assignments : "has"
+ roles ||--o{ user_assignments : "assigned_to"
+ roles ||--o{ role_permissions : "has"
+ permissions ||--o{ role_permissions : "granted_by"
+
+ %% Correspondences
+ correspondences ||--o{ correspondence_revisions : "has_revisions"
+ correspondence_types ||--o{ correspondences : "categorizes"
+ correspondence_status ||--o{ correspondence_revisions : "defines_state"
+ disciplines ||--o{ correspondences : "classifies"
+
+ %% RFAs
+ rfas ||--o{ rfa_revisions : "has_revisions"
+ rfa_types ||--o{ rfas : "categorizes"
+ rfa_status_codes ||--o{ rfa_revisions : "defines_state"
+ rfa_approve_codes ||--o{ rfa_revisions : "defines_result"
+ disciplines ||--o{ rfas : "classifies"
+
+ %% Drawings
+ shop_drawings ||--o{ shop_drawing_revisions : "has_revisions"
+ shop_drawing_main_categories ||--o{ shop_drawings : "categorizes"
+ shop_drawing_sub_categories ||--o{ shop_drawings : "sub_categorizes"
+
+ %% Attachments
+ attachments ||--o{ correspondence_attachments : "attached_to"
+ correspondences ||--o{ correspondence_attachments : "has"
+```
+
+---
+
+# 3. Data Dictionary V1.8.0
+
+> หมายเหตุ: PK = Primary Key, FK = Foreign Key, AI = AUTO_INCREMENT. รูปแบบ Soft Delete จะปรากฏ Column `deleted_at DATETIME NULL` เป็นมาตรฐาน
+
+## **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 (
+ CONTRACTOR,
+ THIRD PARTY
+) |
+| 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 | ** 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 |
+| deleted_at | DATETIME | NULL | Soft delete timestamp | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (organization_code) - INDEX (is_active) ** Relationships **: - Referenced by: users,
+ project_organizations,
+ contract_organizations,
+ correspondences,
+ circulations ---
+
+ ### 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 |
+| 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 |
+** INDEXES **: - PRIMARY KEY (id) - UNIQUE (project_code) - INDEX (is_active) ** Relationships **: - Referenced by: contracts,
+ correspondences,
+ document_number_formats,
+ drawings ---
+
+ ### 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 |
+| deleted_at | DATETIME | NULL | Soft delete 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 ---
+
+ ### 1.5 disciplines (NEW v1.5.1)
+
+ * * Purpose **: เก็บข้อมูลสาขางาน (Disciplines) แยกตามสัญญา (Req 6B) | COLUMN Name | Data TYPE | Constraints | Description | |: -------------- | :----------- | :----------- | :--------------------- |
+ | id | INT | PK,
+ AI | UNIQUE identifier | | contract_id | INT | FK,
+ NOT NULL | ผูกกับสัญญา | | discipline_code | VARCHAR(10) | NOT NULL | รหัสสาขา (เช่น GEN, STR) | | code_name_th | VARCHAR(255) | NULL | ชื่อไทย | | code_name_en | VARCHAR(255) | NULL | ชื่ออังกฤษ | | is_active | TINYINT(1) | DEFAULT 1 | สถานะการใช้งาน | ** INDEXES **: - UNIQUE (contract_id, discipline_code) ---
+
+ ## **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 | | deleted_at | DATETIME | NULL | Deleted at | ** 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 ---
+
+ ### 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) |
+| 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 |
+** INDEXES **: - PRIMARY KEY (role_id) - INDEX (scope) ** Relationships **: - Referenced by: role_permissions,
+ user_assignments ---
+
+ ### 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 |
+| 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 |
+** INDEXES **: - PRIMARY KEY (permission_id) - UNIQUE (permission_name) - INDEX (module) - INDEX (scope_level) - INDEX (is_active) ** Relationships **: - Referenced by: role_permissions ---
+
+ ### 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 ---
+
+ ### 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) ** Relationships **: - Parent: users,
+ roles,
+ organizations,
+ projects,
+ contracts ---
+
+ ### 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 ---
+
+ ### 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
+
+---
+
+### 2.8 user_preferences (NEW v1.5.1)
+
+**Purpose**: เก็บการตั้งค่าส่วนตัวของผู้ใช้ (Req 5.5, 6.8.3)
+
+| Column Name | Data Type | Constraints | Description |
+| :----------- | :---------- | :---------------- | :-------------- |
+| user_id | INT | PK, FK | User ID |
+| notify_email | BOOLEAN | DEFAULT TRUE | รับอีเมลแจ้งเตือน |
+| notify_line | BOOLEAN | DEFAULT TRUE | รับไลน์แจ้งเตือน |
+| digest_mode | BOOLEAN | DEFAULT FALSE | รับแจ้งเตือนแบบรวม |
+| ui_theme | VARCHAR(20) | DEFAULT ' light ' | UI Theme |
+
+---
+
+### 2.9 refresh_tokens (NEW v1.5.1)
+
+**Purpose**: เก็บ Refresh Tokens สำหรับการทำ Authentication และ Token Rotation
+
+| Column Name | Data Type | Constraints | Description |
+| :---------------- | :----------- | :------------------------ | :------------------------------------ |
+| token_id | INT | PK, AI | Unique Token ID |
+| user_id | INT | FK, NOT NULL | เจ้าของ Token |
+| token_hash | VARCHAR(255) | NOT NULL | Hash ของ Refresh Token (Security) |
+| expires_at | DATETIME | NOT NULL | วันหมดอายุของ Token |
+| is_revoked | BOOLEAN | DEFAULT FALSE | สถานะถูกยกเลิก (True = ใช้งานไม่ได้) |
+| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้าง |
+| replaced_by_token | VARCHAR(255) | NULL | Token ใหม่ที่มาแทนที่ (กรณี Token Rotation) |
+
+**Indexes**:
+
+* PRIMARY KEY (token_id)
+* FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
+* INDEX (user_id)
+
+**Relationships**:
+
+* Parent: users
+
+---
+
+## **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 |
+| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
+| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last update timestamp |
+| deleted_at | DATETIME | NULL | Soft delete timestamp |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* UNIQUE (type_code)
+* INDEX (is_active)
+* INDEX (sort_order)
+
+**Relationships**:
+
+* Referenced by: correspondences, document_number_formats, document_number_counters
+
+---
+
+### 3.2 correspondence_sub_types (NEW v1.5.1)
+
+**Purpose**: เก็บประเภทหนังสือย่อย (Sub Types) สำหรับ Mapping เลขรหัส (Req 6B)
+
+| Column Name | Data Type | Constraints | Description |
+| :--------------------- | :----------- | :----------- | :------------------------ |
+| id | INT | PK, AI | Unique identifier |
+| contract_id | INT | FK, NOT NULL | ผูกกับสัญญา |
+| correspondence_type_id | INT | FK, NOT NULL | ผูกกับประเภทเอกสารหลัก |
+| sub_type_code | VARCHAR(20) | NOT NULL | รหัสย่อย (เช่น MAT, SHP) |
+| sub_type_name | VARCHAR(255) | NULL | ชื่อประเภทหนังสือย่อย |
+| sub_type_number | VARCHAR(10) | NULL | เลขรหัสสำหรับ Running Number |
+
+---
+
+### 3.3 correspondences (UPDATE v1.7.0)
+
+**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 |
+| **discipline_id** | **INT** | **NULL, FK** | **[NEW] สาขางาน (ถ้ามี)** |
+| 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 (discipline_id) REFERENCES disciplines(id) ON DELETE SET NULL**
+* 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, **disciplines**, projects, organizations, users
+* Children: correspondence_revisions, correspondence_recipients, correspondence_tags, correspondence_references, correspondence_attachments, circulations, transmittals
+
+---
+
+### 3.4 correspondence_revisions (UPDATE v1.7.0)
+
+**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 |
+| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
+
+| v_doc_subtype | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
+| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
+
+**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)
+* INDEX (correspondence_status_id)
+* INDEX (is_current)
+* INDEX (document_date)
+* INDEX (issued_date)
+* INDEX (v_ref_project_id)
+* INDEX (v_doc_subtype)
+
+---
+
+### 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
+
+---
+
+### 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)
+
+---
+
+## **4. 📐 approval: RFA Tables (เอกสารขออนุมัติ, Workflows)**
+
+### 4.1 rfa_types (UPDATE v1.7.0)
+
+**Purpose**: Master table for RFA (Request for Approval) types
+
+| Column Name | Data Type | Constraints | Description |
+| :----------- | :----------- | :-------------------------- | :------------------------------ |
+| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
+| contract_id | INT | NOT NULL, FK | Contract reference |
+| type_code | VARCHAR(20) | NOT NULL | Type code (DWG, DOC, MAT, etc.) |
+| type_name_th | VARCHAR(100) | NOT NULL | Full type name (TH) |
+| type_name_en | VARCHAR(100) | NOT NULL | Full type name (EN) |
+| remark | TEXT | NULL | Remark |
+| is_active | TINYINT(1) | DEFAULT 1 | Active status |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* UNIQUE (contract_id, type_code)
+* FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
+* INDEX (is_active)
+
+**Relationships**:
+
+* Referenced by: rfas
+
+---
+
+### 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
+
+---
+
+### 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
+
+---
+
+### 4.4 rfas (UPDATE v1.7.0)
+
+**Purpose**: Master table for RFA documents (non-revisioned data)
+
+| Column Name | Data Type | Constraints | Description |
+| :---------- | :-------- | :------------------------ | :------------------------------------------ |
+| id | INT | PK, FK | Master RFA ID (Shared with correspondences) |
+| 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 (id) REFERENCES correspondences(id) ON DELETE CASCADE
+* 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: correspondences, rfa_types, users
+* Children: rfa_revisions
+
+---
+
+### 4.5 rfa_revisions (UPDATE v1.7.0)
+
+**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 |
+
+| 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 |
+| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
+| v_ref_drawing_count | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Drawing Count จาก JSON details เพื่อทำ Index |
+| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* 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)
+* INDEX (v_ref_drawing_count): ตัวอย่างการ Index ข้อมูลตัวเลขใน JSON
+
+**Relationships**:
+
+* Parent: correspondences, rfas, rfa_status_codes, rfa_approve_codes, users
+* Children: rfa_items
+
+---
+
+### 4.6 rfa_items
+
+**Purpose**: Junction table linking RFA revisions to shop drawing revisions (M:N)
+
+| Column Name | Data Type | Constraints | Description |
+| :----------------------- | :-------- | :-------------- | :----------------------- |
+| rfa_revision_id | INT | PRIMARY KEY, FK | RFA Revision ID |
+| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Shop drawing revision ID |
+
+**Indexes**:
+
+* PRIMARY KEY (rfa_revision_id, shop_drawing_revision_id)
+* FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(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
+
+---
+
+
+---
+
+## **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 (UPDATE v1.7.0)
+
+**Purpose**: Junction table mapping sub-categories to main categories (M:N)
+
+| Column Name | Data Type | Constraints | Description |
+| ----------- | --------- | ------------------------------- | -------------------------- |
+| **id** | **INT** | **PRIMARY KEY, AUTO_INCREMENT** | **Unique mapping ID** |
+| project_id | INT | NOT NULL, FK | Reference to projects |
+| sub_cat_id | INT | NOT NULL, FK | Reference to sub-category |
+| cat_id | INT | NOT NULL, FK | Reference to main category |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* **UNIQUE 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
+* Referenced by: contract_drawings
+
+**Business Rules**:
+
+* Allows flexible categorization
+* One sub-category can belong to multiple main categories
+* Composite uniqueness enforced via UNIQUE constraint
+
+---
+
+### 5.5 contract_drawings (UPDATE v1.7.0)
+
+**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 |
+| **map_cat_id** | **INT** | **NULL, FK** | **[CHANGED] Reference to mapping table** |
+| volume_id | INT | NULL, FK | Reference to volume |
+| **volume_page** | **INT** | **NULL** | **[NEW] Page number within 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 (map_cat_id) REFERENCES contract_drawing_subcat_cat_maps(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 (map_cat_id)
+* INDEX (volume_id)
+* INDEX (deleted_at)
+
+**Relationships**:
+
+* Parent: projects, contract_drawing_subcat_cat_maps, 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
+* **map_cat_id references the mapping table for flexible categorization**
+
+---
+
+### 5.6 shop_drawing_main_categories (UPDATE v1.7.0)
+
+**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 |
+| **project_id** | **INT** | **NOT NULL, FK** | **[NEW] Reference to projects** |
+| 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)
+* **FOREIGN KEY (project_id) REFERENCES projects(id)**
+* UNIQUE (main_category_code)
+* INDEX (is_active)
+* INDEX (sort_order)
+
+**Relationships**:
+
+* **Parent: projects**
+* Referenced by: shop_drawings, asbuilt_drawings
+
+**Business Rules**:
+
+* **[CHANGED] Project-specific categories (was global)**
+* Typically represents engineering disciplines
+
+---
+
+### 5.7 shop_drawing_sub_categories (UPDATE v1.7.0)
+
+**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 |
+| **project_id** | **INT** | **NOT NULL, FK** | **[NEW] Reference to projects** |
+| 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 |
+| 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)
+* **FOREIGN KEY (project_id) REFERENCES projects(id)**
+* UNIQUE (sub_category_code)
+* INDEX (is_active)
+* INDEX (sort_order)
+
+**Relationships**:
+
+* **Parent: projects**
+* Referenced by: shop_drawings, asbuilt_drawings
+
+**Business Rules**:
+
+* **[CHANGED] Project-specific sub-categories (was global)**
+* **[REMOVED] No longer hierarchical under main categories**
+* Represents specific drawing types or components
+
+---
+
+### 5.8 shop_drawings (UPDATE v1.7.0)
+
+**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 |
+| 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
+* **[CHANGED] Title moved to shop_drawing_revisions table**
+
+---
+
+### 5.9 shop_drawing_revisions (UPDATE v1.7.0)
+
+**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 |
+| **title** | **VARCHAR(500)** | **NOT NULL** | **[NEW] Drawing title** |
+| description | TEXT | NULL | Revision description/changes |
+| **legacy_drawing_number** | **VARCHAR(100)** | **NULL** | **[NEW] Original/legacy drawing number** |
+| 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, asbuilt_revision_shop_revisions_refs
+
+**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
+* **[NEW] Title stored at revision level for version-specific naming**
+* **[NEW] legacy_drawing_number supports data migration from old systems**
+
+---
+
+### 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
+
+---
+
+### 5.11 asbuilt_drawings (NEW v1.7.0)
+
+**Purpose**: Master table for AS Built drawings (final construction records)
+
+| 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 | AS Built drawing number |
+| 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: asbuilt_drawing_revisions
+
+**Business Rules**:
+
+* Drawing numbers are globally unique across all projects
+* Represents final as-built construction drawings
+* Can have multiple revisions
+* Soft delete preserves history
+* Uses same category structure as shop drawings
+
+---
+
+### 5.12 asbuilt_drawing_revisions (NEW v1.7.0)
+
+**Purpose**: Child table storing revision history of AS Built drawings (1:N)
+
+| Column Name | Data Type | Constraints | Description |
+| --------------------- | ------------ | --------------------------- | ------------------------------ |
+| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
+| asbuilt_drawing_id | INT | NOT NULL, FK | Master AS Built 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 |
+| title | VARCHAR(500) | NOT NULL | Drawing title |
+| description | TEXT | NULL | Revision description/changes |
+| legacy_drawing_number | VARCHAR(100) | NULL | Original/legacy drawing number |
+| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* FOREIGN KEY (asbuilt_drawing_id) REFERENCES asbuilt_drawings(id) ON DELETE CASCADE
+* UNIQUE KEY (asbuilt_drawing_id, revision_number)
+* INDEX (revision_date)
+
+**Relationships**:
+
+* Parent: asbuilt_drawings
+* Referenced by: asbuilt_revision_shop_revisions_refs, asbuilt_drawing_revision_attachments
+
+**Business Rules**:
+
+* Revision numbers are sequential starting from 0
+* Each revision can reference multiple shop drawing revisions
+* Each revision can have multiple file attachments
+* Title stored at revision level for version-specific naming
+* legacy_drawing_number supports data migration from old systems
+
+---
+
+### 5.13 asbuilt_revision_shop_revisions_refs (NEW v1.7.0)
+
+**Purpose**: Junction table linking AS Built drawing revisions to shop drawing revisions (M:N)
+
+| Column Name | Data Type | Constraints | Description |
+| --------------------------- | --------- | --------------- | ---------------------------------- |
+| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
+| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
+
+**Indexes**:
+
+* PRIMARY KEY (asbuilt_drawing_revision_id, shop_drawing_revision_id)
+* FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(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: asbuilt_drawing_revisions, shop_drawing_revisions
+
+**Business Rules**:
+
+* Tracks which shop drawings each AS Built drawing revision is based on
+* Maintains construction document lineage
+* One AS Built revision can reference multiple shop drawing revisions
+* Supports traceability from final construction to approved shop drawings
+
+---
+
+### 5.14 asbuilt_drawing_revision_attachments (NEW v1.7.0)
+
+**Purpose**: Junction table linking AS Built drawing revisions to file attachments (M:N)
+
+| Column Name | Data Type | Constraints | Description |
+| --------------------------- | ------------------------------------- | --------------- | ------------------------------------- |
+| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
+| attachment_id | INT | PRIMARY KEY, FK | Reference to attachment file |
+| file_type | ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') | NULL | File type classification |
+| is_main_document | BOOLEAN | DEFAULT FALSE | Main document flag (1 = primary file) |
+
+**Indexes**:
+
+* PRIMARY KEY (asbuilt_drawing_revision_id, attachment_id)
+* FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
+* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
+* INDEX (attachment_id)
+
+**Relationships**:
+
+* Parent: asbuilt_drawing_revisions, attachments
+
+**Business Rules**:
+
+* Each AS Built revision can have multiple file attachments
+* File types: PDF (documents), DWG (CAD files), SOURCE (source files), OTHER (miscellaneous)
+* One attachment can be marked as main document per revision
+* Cascade delete when revision is deleted
+
+---
+
+## **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
+
+---
+
+## **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 |
+| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
+| temp_id\* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
+| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
+| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
+
+**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 System Tables (ระบบเลขที่เอกสาร)**
+
+### 9.1 document_number_formats
+
+**Purpose**: Master table defining numbering formats for each document 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_string | VARCHAR(100) | NOT NULL | Format pattern (e.g., {ORG}-{TYPE}-{YYYY}-#) |
+| description | TEXT | NULL | Format description |
+| reset_annually | BOOLEAN | DEFAULT TRUE | Start sequence new every year |
+| is_active | TINYINT(1) | DEFAULT 1 | Active status |
+
+**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 (is_active)
+
+**Relationships**:
+
+* Parent: projects, correspondence_types
+
+**Business Rules**:
+
+* Defines how document numbers are constructed
+* Supports placeholders: {PROJ}, {ORG}, {TYPE}, {YYYY}, {MM}, {#}
+
+---
+
+### 9.2 document_number_counters (UPDATE v1.7.0)
+
+**Purpose**: Transaction table tracking running numbers (High Concurrency)
+
+| Column Name | Data Type | Constraints | Description |
+| -------------------------- | ----------- | ------------- | ----------------------------------------------- |
+| project_id | INT | PK, NOT NULL | โครงการ |
+| originator_organization_id | INT | PK, NOT NULL | องค์กรผู้ส่ง |
+| recipient_organization_id | INT | PK, NOT NULL | องค์กรผู้รับ (0 = no recipient / RFA) |
+| correspondence_type_id | INT | PK, NULL | ประเภทเอกสาร (NULL = default) |
+| sub_type_id | INT | PK, DEFAULT 0 | ประเภทย่อย สำหรับ TRANSMITTAL (0 = ไม่ระบุ) |
+| rfa_type_id | INT | PK, DEFAULT 0 | ประเภท RFA (0 = ไม่ใช่ RFA) |
+| discipline_id | INT | PK, DEFAULT 0 | สาขางาน (0 = ไม่ระบุ) |
+| reset_scope | VARCHAR(20) | PK, NOT NULL | Scope of reset (YEAR_2024, MONTH_2024_01, NONE) |
+| last_number | INT | DEFAULT 0 | เลขล่าสุดที่ถูกใช้งานไปแล้ว |
+| version | INT | DEFAULT 0 | Optimistic Lock Version |
+| updated_at | DATETIME(6) | ON UPDATE | เวลาที่อัปเดตล่าสุด |
+
+**Indexes**:
+
+* **PRIMARY KEY (project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)**
+* INDEX idx_counter_lookup (project_id, correspondence_type_id, reset_scope)
+* INDEX idx_counter_org (originator_organization_id, reset_scope)
+
+**Business Rules**:
+
+* **Composite Primary Key 8 Columns**: เพื่อรองรับการรันเลขที่ซับซ้อนและ Reset Scope ที่หลากหลาย
+* **Concurrency Control**: ใช้ Redis Lock หรือ Optimistic Locking (version)
+* **Reset Scope**: ใช้ Field `reset_scope` ควบคุมการ Reset แทน `current_year` แบบเดิม
+
+---
+
+### 9.3 document_number_audit (UPDATE v1.7.0)
+
+**Purpose**: Audit log for document number generation (Debugging & Tracking)
+
+| Column Name | Data Type | Constraints | Description |
+| :------------------------- | :----------- | :----------------- | :-------------------------------------- |
+| id | INT | PK, AI | ID ของ audit record |
+| document_id | INT | NULL, FK | ID ของเอกสารที่สร้างเลขที่ (NULL if failed) |
+| document_type | VARCHAR(50) | NULL | ประเภทเอกสาร |
+| document_number | VARCHAR(100) | NOT NULL | เลขที่เอกสารที่สร้าง (ผลลัพธ์) |
+| operation | ENUM | DEFAULT 'CONFIRM' | RESERVE, CONFIRM, MANUAL_OVERRIDE, etc. |
+| status | ENUM | DEFAULT 'RESERVED' | RESERVED, CONFIRMED, CANCELLED, VOID |
+| counter_key | JSON | NOT NULL | Counter key ที่ใช้ (JSON 8 fields) |
+| reservation_token | VARCHAR(36) | NULL | Token การจอง |
+| idempotency_key | VARCHAR(128) | NULL | Idempotency Key from request |
+| originator_organization_id | INT | NULL | องค์กรผู้ส่ง |
+| recipient_organization_id | INT | NULL | องค์กรผู้รับ |
+| template_used | VARCHAR(200) | NOT NULL | Template ที่ใช้ในการสร้าง |
+| old_value | TEXT | NULL | Previous value |
+| new_value | TEXT | NULL | New value |
+| user_id | INT | NULL, FK | ผู้ขอสร้างเลขที่ |
+| is_success | BOOLEAN | DEFAULT TRUE | สถานะความสำเร็จ |
+| created_at | TIMESTAMP | DEFAULT NOW | วันที่/เวลาที่สร้าง |
+| total_duration_ms | INT | NULL | เวลารวมทั้งหมดในการสร้าง (ms) |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* FOREIGN KEY (document_id) REFERENCES correspondences(id) ON DELETE CASCADE
+* FOREIGN KEY (user_id) REFERENCES users(user_id)
+* INDEX (document_id)
+* INDEX (user_id)
+* INDEX (status)
+* INDEX (operation)
+* INDEX (document_number)
+* INDEX (reservation_token)
+* INDEX (created_at)
+
+---
+
+### 9.4 document_number_errors (UPDATE v1.7.0)
+
+**Purpose**: Error log for failed document number generation
+
+| Column Name | Data Type | Constraints | Description |
+| :------------ | :-------- | :---------- | :--------------------------------------------- |
+| id | INT | PK, AI | ID ของ error record |
+| error_type | ENUM | NOT NULL | LOCK_TIMEOUT, VERSION_CONFLICT, DB_ERROR, etc. |
+| error_message | TEXT | NULL | ข้อความ error |
+| stack_trace | TEXT | NULL | Stack trace สำหรับ debugging |
+| context_data | JSON | NULL | Context ของ request |
+| user_id | INT | NULL | ผู้ที่เกิด error |
+| created_at | TIMESTAMP | DEFAULT NOW | วันที่เกิด error |
+| resolved_at | TIMESTAMP | NULL | วันที่แก้ไขแล้ว |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* INDEX (error_type)
+* INDEX (created_at)
+* INDEX (user_id)
+* INDEX (resolved_at)
+
+---
+
+### 9.5 document_number_reservations (NEW v1.7.0)
+
+**Purpose**: Two-Phase Commit table for document number reservation
+
+| Column Name | Data Type | Constraints | Description |
+| :--------------------- | :----------- | :--------------- | :----------------------------------- |
+| id | INT | PK, AI | Unique ID |
+| token | VARCHAR(36) | UNIQUE, NOT NULL | UUID v4 Reservation Token |
+| document_number | VARCHAR(100) | UNIQUE, NOT NULL | เลขที่เอกสารที่จอง |
+| document_number_status | ENUM | DEFAULT RESERVED | RESERVED, CONFIRMED, CANCELLED, VOID |
+| document_id | INT | NULL, FK | ID ของเอกสาร (เมื่อ Confirm แล้ว) |
+| expires_at | DATETIME(6) | NOT NULL | เวลาหมดอายุการจอง |
+| project_id | INT | NOT NULL, FK | Project Context |
+| user_id | INT | NOT NULL, FK | User Context |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* FOREIGN KEY (document_id) REFERENCES correspondence_revisions(id) ON DELETE SET NULL
+* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
+* FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
+* FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
+* INDEX idx_token (token)
+* INDEX idx_status (document_number_status)
+* INDEX idx_status_expires (document_number_status, expires_at)
+* INDEX idx_document_id (document_id)
+* INDEX idx_user_id (user_id)
+* INDEX idx_reserved_at (reserved_at)
+
+---
+
+## **10. ⚙️ Unified Workflow Engine Tables (UPDATE v1.7.0)**
+
+### 10.1 workflow_definitions
+
+**Purpose**: เก็บแม่แบบ (Template) ของ Workflow (Definition / DSL)
+
+| Column Name | Data Type | Constraints | Description |
+| :------------ | :---------- | :----------- | :------------------------------------- |
+| id | CHAR(36) | PK, UUID | Unique Workflow Definition ID |
+| workflow_code | VARCHAR(50) | NOT NULL | รหัส Workflow (เช่น RFA_FLOW_V1) |
+| version | INT | DEFAULT 1 | หมายเลข Version |
+| description | TEXT | NULL | คำอธิบาย Workflow |
+| dsl | JSON | NOT NULL | นิยาม Workflow ต้นฉบับ (YAML/JSON Format) |
+| compiled | JSON | NOT NULL | โครงสร้าง Execution Tree ที่ Compile แล้ว |
+| is_active | BOOLEAN | DEFAULT TRUE | สถานะการใช้งาน |
+| created_at | TIMESTAMP | DEFAULT NOW | วันที่สร้าง |
+| updated_at | TIMESTAMP | ON UPDATE | วันที่แก้ไขล่าสุด |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* UNIQUE KEY (workflow_code, version)
+* INDEX (is_active)
+
+---
+
+### 10.2 workflow_instances
+
+**Purpose**: เก็บสถานะของ Workflow ที่กำลังรันอยู่จริง (Runtime)
+
+| Column Name | Data Type | Constraints | Description |
+| :------------ | :---------- | :--------------- | :--------------------------------------------- |
+| id | CHAR(36) | PK, UUID | Unique Instance ID |
+| definition_id | CHAR(36) | FK, NOT NULL | อ้างอิง Definition ที่ใช้ |
+| entity_type | VARCHAR(50) | NOT NULL | ประเภทเอกสาร (rfa_revision, correspondence...) |
+| entity_id | VARCHAR(50) | NOT NULL | ID ของเอกสาร |
+| current_state | VARCHAR(50) | NOT NULL | สถานะปัจจุบัน |
+| status | ENUM | DEFAULT 'ACTIVE' | ACTIVE, COMPLETED, CANCELLED, TERMINATED |
+| context | JSON | NULL | ตัวแปร Context สำหรับตัดสินใจ |
+| created_at | TIMESTAMP | DEFAULT NOW | เวลาที่สร้าง |
+| updated_at | TIMESTAMP | ON UPDATE | เวลาที่อัปเดตล่าสุด |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE
+* INDEX (entity_type, entity_id)
+* INDEX (current_state)
+
+---
+
+### 10.3 workflow_histories
+
+**Purpose**: เก็บประวัติการดำเนินการในแต่ละ Step (Audit Trail)
+
+| Column Name | Data Type | Constraints | Description |
+| :---------------- | :---------- | :----------- | :-------------------- |
+| id | CHAR(36) | PK, UUID | Unique ID |
+| instance_id | CHAR(36) | FK, NOT NULL | อ้างอิง Instance |
+| from_state | VARCHAR(50) | NOT NULL | สถานะต้นทาง |
+| to_state | VARCHAR(50) | NOT NULL | สถานะปลายทาง |
+| action | VARCHAR(50) | NOT NULL | Action ที่กระทำ |
+| action_by_user_id | INT | FK, NULL | User ID ผู้กระทำ |
+| comment | TEXT | NULL | ความเห็น |
+| metadata | JSON | NULL | Snapshot ข้อมูล ณ ขณะนั้น |
+| created_at | TIMESTAMP | DEFAULT NOW | เวลาที่กระทำ |
+
+**Indexes**:
+
+* PRIMARY KEY (id)
+* FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE
+* INDEX (instance_id)
+* INDEX (action_by_user_id)
+
+---
+
+## **11. 🖥️ System & Logs Tables (ระบบ, บันทึก)**
+
+> **Audit Logging Architecture:**
+### 1. Audit Logging
+
+**Table: `audit_logs`**
+
+บันทึกการเปลี่ยนแปลงสำคัญ:
+
+- User actions (CREATE, UPDATE, DELETE)
+- Entity type และ Entity ID
+- Old/New values (JSON)
+- IP Address, User Agent
+
+### 2. User Preferences
+
+**Table: `user_preferences`**
+
+เก็บการตั้งค่าส่วนตัว:
+
+- Language preference
+- Notification settings
+- UI preferences (JSON)
+
+### 3. JSON Schema Validation
+
+**Table: `json_schemas`**
+
+เก็บ Schema สำหรับ Validate JSON fields:
+
+- `correspondence_revisions.details`
+- `user_preferences.preferences`
+
+---
+
+
+### 11.1 json_schemas (UPDATE v1.7.0)
+
+**Purpose**: เก็บ Schema สำหรับ Validate JSON Columns (Req 3.12)
+
+| Column Name | Data Type | Constraints | Description |
+| :---------------- | :----------- | :----------- | :------------------------------- |
+| id | INT | PK, AI | Unique ID |
+| schema_code | VARCHAR(100) | NOT NULL | รหัส Schema (เช่น RFA_DWG) |
+| version | INT | DEFAULT 1 | เวอร์ชันของ Schema |
+| table_name | VARCHAR(100) | NOT NULL | ชื่อตารางเป้าหมาย |
+| schema_definition | JSON | NOT NULL | JSON Schema Definition |
+| ui_schema | JSON | NULL | โครงสร้าง UI Schema สำหรับ Frontend |
+| virtual_columns | JSON | NULL | Config สำหรับสร้าง Virtual Columns |
+| migration_script | JSON | NULL | Script สำหรับแปลงข้อมูล |
+| is_active | BOOLEAN | DEFAULT TRUE | สถานะการใช้งาน |
+
+
+---
+
+### 11.2 audit_logs (UPDATE v1.7.0)
+
+**Purpose**: Centralized audit logging for all system actions (Req 6.1)
+
+| Column Name | Data Type | Constraints | Description |
+| :----------- | :----------- | :------------------------ | :------------------------------------------- |
+| audit_id | BIGINT | PK, AI | Unique log ID |
+| request_id | VARCHAR(100) | NULL | Trace ID linking to app logs |
+| user_id | INT | NULL, FK | User who performed action |
+| action | VARCHAR(100) | NOT NULL | Action name (e.g. rfa.create) |
+| severity | ENUM | DEFAULT 'INFO' | INFO, WARN, ERROR, CRITICAL |
+| entity_type | VARCHAR(50) | NULL | Module/Table name (e.g. rfa, correspondence) |
+| entity_id | VARCHAR(50) | NULL | ID of affected entity |
+| details_json | JSON | NULL | Context data / Old & New values |
+| ip_address | VARCHAR(45) | NULL | User IP address |
+| user_agent | VARCHAR(255) | NULL | User browser/client info |
+| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Log timestamp |
+
+**Indexes**:
+
+* PRIMARY KEY (audit_id, created_at) -- **Partition Key**
+* INDEX idx_audit_user (user_id)
+* INDEX idx_audit_action (action)
+* INDEX idx_audit_entity (entity_type, entity_id)
+* INDEX idx_audit_created (created_at)
+
+**Partitioning**:
+* **PARTITION BY RANGE (YEAR(created_at))**: แบ่ง Partition รายปี เพื่อประสิทธิภาพในการเก็บข้อมูลระยะยาว
+
+---
+
+### 11.3 notifications (UPDATE v1.7.0)
+
+**Purpose**: System notifications for users
+
+| 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 |
+| message | TEXT | NOT NULL | Notification body |
+| notification_type | ENUM | NOT NULL | Type: EMAIL, LINE, SYSTEM |
+| is_read | BOOLEAN | DEFAULT FALSE | Read status |
+| entity_type | VARCHAR(50) | NULL | Related Entity Type |
+| entity_id | INT | NULL | Related Entity ID |
+| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Notification timestamp |
+
+**Indexes**:
+
+* PRIMARY KEY (id, created_at) -- **Partition Key**
+* INDEX idx_notif_user (user_id)
+* INDEX idx_notif_type (notification_type)
+* INDEX idx_notif_read (is_read)
+* INDEX idx_notif_created (created_at)
+
+**Partitioning**:
+* **PARTITION BY RANGE (YEAR(created_at))**: แบ่ง Partition รายปี
+
+---
+
+## **12. 🔍 Views (มุมมองข้อมูล)**
+
+### 12.1 v_current_correspondences
+
+**Purpose**: แสดงข้อมูล Correspondence Revision ล่าสุด (is_current = TRUE)
+
+### 12.2 v_current_rfas
+
+**Purpose**: แสดงข้อมูล RFA Revision ล่าสุด พร้อม Status และ Approve Code
+
+### 12.3 v_user_tasks (Unified Workflow)
+
+**Purpose**: รวมรายการงานที่ยังค้างอยู่ (Status = ACTIVE) จากทุกระบบ (RFA, Circulation, Correspondence) เพื่อนำไปแสดงใน Dashboard
+
+### 12.4 v_audit_log_details
+
+**Purpose**: แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ
+
+### 12.5 v_user_all_permissions
+
+**Purpose**: รวมสิทธิ์ทั้งหมด (Global + Project + Organization) ของผู้ใช้ทุกคน
+
+### 12.6 v_documents_with_attachments
+
+**Purpose**: แสดงเอกสารทั้งหมดที่มีไฟล์แนบ (Correspondence, Circulation, Drawings)
+
+### 12.7 v_document_statistics
+
+**Purpose**: แสดงสถิติเอกสารตามประเภทและสถานะ
+
+---
+
+## **13. 📊 Index Summaries (สรุป Index)**
+
+> **Performance Optimization Strategy:**
+### 1. Indexing Strategy
+
+**Primary Indexes:**
+
+- Primary Keys (AUTO_INCREMENT)
+- Foreign Keys (automatic in InnoDB)
+- Unique Constraints (business keys)
+
+**Secondary Indexes:**
+
+```sql
+-- Correspondence search
+CREATE INDEX idx_corr_type_status ON correspondence_revisions(correspondence_type_id, correspondence_status_id);
+CREATE INDEX idx_corr_date ON correspondence_revisions(document_date);
+
+-- Virtual columns for JSON
+CREATE INDEX idx_v_ref_project ON correspondence_revisions(v_ref_project_id);
+CREATE INDEX idx_v_doc_subtype ON correspondence_revisions(v_doc_subtype);
+
+-- User lookup
+CREATE INDEX idx_user_email ON users(email);
+CREATE INDEX idx_user_org ON users(primary_organization_id, is_active);
+```
+
+### 2. Virtual Columns
+
+ใช้ Virtual Columns สำหรับ Index JSON fields:
+
+```sql
+ALTER TABLE correspondence_revisions
+ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.ref_project_id'))) VIRTUAL,
+ADD INDEX idx_v_ref_project(v_ref_project_id);
+```
+
+### 3. Partitioning (Future)
+
+พิจารณา Partition ตาราง `audit_logs` ตามปี:
+
+```sql
+ALTER TABLE audit_logs
+PARTITION BY RANGE (YEAR(created_at)) (
+ PARTITION p2024 VALUES LESS THAN (2025),
+ PARTITION p2025 VALUES LESS THAN (2026),
+ PARTITION p_future VALUES LESS THAN MAXVALUE
+);
+```
+
+---
+
+
+### 13.1 Performance Indexes
+
+| Table Name | Index Columns | Purpose |
+| :----------------------- | :------------------------------------------------ | :----------------------------- |
+| correspondences | (project_id, correspondence_number) | Fast lookup by document number |
+| correspondences | (correspondence_type_id) | Filter by type |
+| correspondence_revisions | (correspondence_id, is_current) | Get current revision |
+| rfas | (rfa_type_id) | Filter by RFA type |
+| rfa_revisions | (rfa_id, is_current) | Get current RFA revision |
+| rfa_revisions | (rfa_status_code_id) | Filter by status |
+| audit_logs | (created_at) | Date range queries |
+| audit_logs | (user_id) | User activity history |
+| audit_logs | (module, action) | Action type analysis |
+| notifications | (user_id, is_read) | Unread notifications query |
+| document_number_counters | (project_id, correspondence_type_id, reset_scope) | Running number generation |
+| workflow_instances | (entity_type, entity_id) | Workflow lookup by document ID |
+| workflow_instances | (current_state) | Monitor active workflows |
+
+### 13.2 Unique Constraints
+
+| Table Name | Columns | Description |
+| :---------------------- | :----------------------------------- | :--------------------------------- |
+| users | (username) | Unique login name |
+| users | (email) | Unique email address |
+| organizations | (organization_code) | Unique organization code |
+| projects | (project_code) | Unique project code |
+| contracts | (contract_code) | Unique contract code |
+| correspondences | (project_id, correspondence_number) | Unique document number per project |
+| shop_drawings | (drawing_number) | Unique shop drawing number |
+| document_number_formats | (project_id, correspondence_type_id) | One format per type per project |
+| workflow_definitions | (workflow_code, version) | Unique workflow code per version |
+
+---
+
+## **14. 🛡️ Data Integrity Constraints (ความถูกต้องของข้อมูล)**
+
+### 14.1 Soft Delete Policy
+
+* **Tables with `deleted_at`**:
+ * users
+ * organizations
+ * projects
+ * contracts
+ * correspondences
+ * rfas
+ * shop_drawings
+ * contract_drawings
+* **Rule**: Records are never physically deleted. `deleted_at` is set to timestamp.
+* **Query Rule**: All standard queries MUST include `WHERE deleted_at IS NULL`.
+
+### 14.2 Foreign Key Cascades
+
+* **ON DELETE CASCADE**:
+ * Used for child tables that cannot exist without parent (e.g., `correspondence_revisions`, `rfa_revisions`, `correspondence_attachments`).
+* **ON DELETE RESTRICT**:
+ * Used for master data references to prevent accidental deletion of used data (e.g., `correspondence_types`, `organizations`).
+* **ON DELETE SET NULL**:
+ * Used for optional references (e.g., `created_by`, `originator_id`).
+
+---
+
+## **15. 🔐 Security & Permissions Model (ความปลอดภัย)**
+
+### 15.1 Row-Level Security (RLS) Logic
+
+* **Organization Scope**: Users can only see documents where `originator_id` OR `recipient_organization_id` matches their organization.
+* **Project Scope**: Users can only see documents within projects they are assigned to.
+* **Confidentiality**: Documents marked `is_confidential` are visible ONLY to specific roles or users.
+
+### 15.2 Role-Based Access Control (RBAC)
+
+* **Permissions** are granular (e.g., `correspondence.view`, `correspondence.create`).
+* **Roles** aggregate permissions (e.g., `Document Controller` = `view` + `create` + `edit`).
+* **Assignments** link Users to Roles within a Context (Global, Project, or Organization).
+
+---
+
+## **16. 🔄 Data Migration & Seeding (การย้ายข้อมูล)**
+
+### 16.1 Initial Seeding (V1.7.0)
+
+1. **Master Data**:
+ * `organizations`: Owner, Consultant, Contractor
+ * `projects`: LCBP3
+ * `correspondence_types`: LETTER, MEMO, TRANSMITTAL, RFA
+ * `rfa_types`: DWG, MAT, DOC, RFI
+ * `rfa_status_codes`: DFT, PEND, APPR, REJ
+ * `disciplines`: GEN, STR, ARC, MEP
+2. **System Users**:
+ * `admin`: Super Admin
+ * `system`: System Bot for automated tasks
+
+### 16.2 Migration Strategy
+
+* **Schema Migration**: Use TypeORM Migrations or raw SQL scripts (versioned).
+* **Data Migration**:
+ * **V1.6.0 -> V1.7.0**:
+ * Run SQL script `9_lcbp3_v1_7_0.sql`
+ * Migrate `document_number_counters` to 8-col composite PK.
+ * Initialize `document_number_reservations`.
+ * Update `json_schemas` with new columns.
+
+---
+
+## **17. 📈 Monitoring & Maintenance (การดูแลรักษา)**
+
+### 17.1 Database Maintenance
+
+* **Daily**: Incremental Backup.
+* **Weekly**: Full Backup + `OPTIMIZE TABLE` for heavy tables (`audit_logs`, `notifications`).
+* **Monthly**: Archive old `audit_logs` partitions to cold storage.
+
+### 17.2 Health Checks
+
+* Monitor `document_number_errors` for numbering failures.
+* Monitor `workflow_instances` for stuck workflows (`status = ' IN_PROGRESS '` > 7 days).
+* Check `document_number_counters` for gaps or resets.
+
+---
+
+## **18. 📚 Best Practices**
+### 1. Naming Conventions
+
+- **Tables**: `snake_case`, plural (e.g., `correspondences`, `users`)
+- **Columns**: `snake_case` (e.g., `correspondence_number`, `created_at`)
+- **Foreign Keys**: `{referenced_table_singular}_id` (e.g., `project_id`, `user_id`)
+- **Junction Tables**: `{table1}_{table2}` (e.g., `correspondence_tags`)
+
+### 2. Timestamp Columns
+
+ทุกตารางควรมี:
+
+- `created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP`
+- `updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`
+
+### 3. Soft Delete
+
+ใช้ `deleted_at DATETIME NULL` แทนการลบจริง:
+
+```sql
+-- Soft delete
+UPDATE correspondences SET deleted_at = NOW() WHERE id = 1;
+
+-- Query active records
+SELECT * FROM correspondences WHERE deleted_at IS NULL;
+```
+
+### 4. JSON Field Guidelines
+
+- ใช้สำหรับข้อมูลที่ไม่ต้อง Query บ่อย
+- สร้าง Virtual Columns สำหรับ fields ที่ต้อง Index
+- Validate ด้วย JSON Schema
+- Document structure ใน Data Dictionary
+
+---
+
+---
+
+## **19. 📖 Glossary (คำศัพท์)**
+
+* **RFA**: Request for Approval (เอกสารขออนุมัติ)
+* **Transmittal**: Document Transmittal Sheet (ใบนำส่งเอกสาร)
+* **Shop Drawing**: แบบก่อสร้างที่ผู้รับเหมาจัดทำ
+* **Contract Drawing**: แบบสัญญา (แบบตั้งต้น)
+* **Revision**: ฉบับแก้ไข (0, 1, 2, A, B, C)
+* **Originator**: ผู้จัดทำ/ผู้ส่งเอกสาร
+* **Recipient**: ผู้รับเอกสาร
+* **Workflow**: กระบวนการทำงาน/อนุมัติ
+* **Discipline**: สาขางาน (เช่น โยธา, สถาปัตย์, ไฟฟ้า)
+
+---
+
+**End of Data Dictionary V1.8.0**
+
diff --git a/specs/03-Data-and-Storage/03-02-db-indexing.md b/specs/03-Data-and-Storage/03-02-db-indexing.md
new file mode 100644
index 0000000..bc4d4ff
--- /dev/null
+++ b/specs/03-Data-and-Storage/03-02-db-indexing.md
@@ -0,0 +1,131 @@
+# Database Indexing & Performance Strategy
+**Version:** 1.0.0
+**Context:** Production-scale (100k+ documents, High Concurrency)
+**Database:** MySQL 8.x (On-Premise via Docker)
+
+## 1. Core Principles (หลักการสำคัญ)
+ในการออกแบบ Database Index สำหรับระบบ DMS ให้ยึดหลักการตัดสินใจดังนี้:
+1. **Data Integrity First:** ใช้ `UNIQUE INDEX` เพื่อเป็นปราการด่านสุดท้ายป้องกันการเกิด Duplicate Document Number และ Revision ซ้ำซ้อน (แม้ Application Layer จะมี Logic ดักไว้แล้วก็ตาม)
+2. **Soft-Delete Awareness:** ทุก Index ที่เกี่ยวข้องกับความถูกต้องของข้อมูล ต้องคำนึงถึงคอลัมน์ `deleted_at` เพื่อไม่ให้เอกสารที่ถูกลบไปแล้ว มาขัดขวางการสร้างเอกสารใหม่ที่ใช้เลขเดิม
+3. **Foreign Key Performance:** สร้าง B-Tree Index ให้กับ Foreign Key (FK) ทุกตัว เพื่อรองรับการ JOIN ข้อมูลที่รวดเร็ว โดยเฉพาะการดึง Workflow และ Routing
+4. **Write-Heavy Resilience:** ตารางประเภท `audit_logs` ให้เน้น Index เฉพาะที่จำเป็น (`created_at`, `user_id`, `action`) เพื่อไม่ให้กระทบประสิทธิภาพการ Insert
+
+---
+
+## 2. Document Control Indexes (ป้องกัน Duplicate & Conflict)
+หัวใจของ DMS คือห้ามมีเอกสารเลขซ้ำในระบบที่ Active อยู่
+
+### 2.1 Unique Document Number & Revision
+เพื่อรองรับระบบ Soft Delete (`deleted_at`) ใน MySQL การตั้ง Unique Index จำเป็นต้องมีเทคนิคเพื่อจัดการกับค่า `NULL` (เนื่องจาก MySQL มองว่า `NULL` ไม่เท่ากับ `NULL` จึงอาจทำให้เกิด Duplicate ได้ถ้าตั้งค่าไม่รัดกุม)
+
+**SQL Recommendation (Functional Index - MySQL 8.0+):**
+```sql
+-- ป้องกันการสร้าง Document No และ Revision ซ้ำ สำหรับเอกสารที่ยังไม่ถูกลบ (Active)
+ALTER TABLE `documents`
+ADD UNIQUE INDEX `idx_unique_active_doc_rev` (
+ `document_no`,
+ `revision`,
+ (IF(`deleted_at` IS NULL, 1, NULL))
+);
+
+```
+
+*เหตุผล:* โครงสร้างนี้รับประกันว่าจะมี `document_no` + `revision` ที่ Active ได้เพียง 1 รายการเท่านั้น แต่สามารถมีรายการที่ถูกลบ (`deleted_at` มีค่า) ซ้ำกันได้
+
+### 2.2 Current/Superseded Flag Index
+
+การค้นหาว่าเอกสารไหนเป็น "Latest Revision" จะเกิดขึ้นบ่อยมาก
+
+```sql
+-- ใช้สำหรับ Filter เอกสารที่เป็นเวอร์ชันล่าสุดอย่างรวดเร็ว
+ALTER TABLE `documents`
+ADD INDEX `idx_doc_status_is_current` (`is_current`, `status`, `project_id`);
+
+```
+
+---
+
+## 3. High-Concurrency Search Indexes (รองรับ 100k+ Docs)
+
+สำหรับการทำ Filter และ Search บนหน้า Dashboard หรือรายการเอกสาร
+
+### 3.1 Pagination & Sorting
+
+การ Query ข้อมูลแบบแบ่งหน้า (Pagination) พร้อมเรียงลำดับวันที่ มักเกิดปัญหา "Filesort" ที่ทำให้ CPU โหลดหนัก
+
+```sql
+-- สำหรับหน้า Dashboard ที่เรียงตามวันที่อัปเดตล่าสุด
+ALTER TABLE `documents`
+ADD INDEX `idx_project_updated` (`project_id`, `updated_at` DESC);
+
+-- สำหรับ Inbox / Pending Tasks ของ User
+ALTER TABLE `workflow_instances`
+ADD INDEX `idx_assignee_status` (`assignee_id`, `status`, `created_at` DESC);
+
+```
+
+### 3.2 Full-Text Search (ทางเลือกเบื้องต้นก่อนใช้ Elasticsearch)
+
+หากผู้ใช้ต้องการค้นหาจากชื่อเอกสาร (`title`) หรือเนื้อหาบางส่วน
+
+```sql
+-- สร้าง Full-Text Index สำหรับคำค้นหา
+ALTER TABLE `documents`
+ADD FULLTEXT INDEX `ft_idx_doc_title` (`title`, `subject`);
+
+```
+
+*(หมายเหตุ: หากอนาคตมีระบบ OCR หรือค้นหาในเนื้อหาไฟล์ PDF ให้พิจารณาขยับไปใช้ Elasticsearch แยกต่างหาก ไม่ควรเก็บ Full-Text ขนาดใหญ่ไว้ใน MySQL)*
+
+---
+
+## 4. RBAC & Security Indexes
+
+เพื่อป้องกันปัญหาคอขวด (Bottleneck) ตอนเช็คสิทธิ์ (RBAC validation) ก่อนให้เข้าถึงเอกสาร
+
+```sql
+-- ตาราง user_permissions
+ALTER TABLE `user_permissions`
+ADD UNIQUE INDEX `idx_user_role_project` (`user_id`, `role_id`, `project_id`);
+
+-- ตาราง document_access_logs (Audit)
+ALTER TABLE `audit_logs`
+ADD INDEX `idx_audit_user_action` (`user_id`, `action`, `created_at`);
+
+```
+
+---
+
+## 5. Audit Log Strategy (การจัดการตารางประวัติ)
+
+ตาราง `audit_logs` จะโตเร็วมาก (Insert-only) คาดว่าจะมีหลักล้าน Record อย่างรวดเร็ว
+
+**คำแนะนำสำหรับ On-Premise:**
+
+1. **Partitioning:** แนะนำให้ทำ Table Partitioning ตามเดือน (Monthly) หรือปี (Yearly) บนคอลัมน์ `created_at`
+2. **Minimal Indexing:** ห้ามสร้าง Index เยอะเกินความจำเป็นในตารางนี้ แนะนำแค่:
+* `INDEX(document_id, created_at)` สำหรับดู History ของเอกสารนั้นๆ
+* `INDEX(user_id, created_at)` สำหรับตรวจสอบพฤติกรรมผู้ใช้ต้องสงสัย (Security Audit)
+
+
+
+```sql
+-- ตัวอย่างการ Index สำหรับดูกระแสของเอกสาร
+ALTER TABLE `audit_logs`
+ADD INDEX `idx_entity_history` (`entity_type`, `entity_id`, `created_at` DESC);
+
+```
+
+---
+
+## 6. Maintenance & Optimization (DevOps/Admin)
+
+เนื่องจากระบบอยู่บน On-Prem NAS (QNAP/ASUSTOR) ทรัพยากร I/O ของดิสก์มีจำกัด (Disk IOPS)
+
+* **Index Defragmentation:** ให้กำหนด Scheduled Task (ผ่าน Cronjob หรือ MySQL Event) มารัน `OPTIMIZE TABLE` ทุกๆ ไตรมาส สำหรับตารางที่มีการ Delete/Update บ่อย (ช่วยคืนพื้นที่ดิสก์และลด I/O)
+* **Slow Query Monitoring:** ใน `04-infrastructure-ops/04-01-docker-compose.md` ต้องเปิดใช้งาน `slow_query_log=1` และตั้ง `long_query_time=2` เพื่อตรวจสอบว่ามี Query ใดทำงานแบบ Full Table Scan (ไม่ใช้ Index) หรือไม่
+
+
+## 💡 คำแนะนำเพิ่มเติมจาก Architect (Architect's Notes):
+1. **เรื่อง Soft Delete กับ Unique Constraint:** เป็นจุดที่นักพัฒนาพลาดกันบ่อยที่สุด ถ้าระบบอนุญาตให้ลบ `DOC-001 Rev.0` แล้วสร้าง `DOC-001 Rev.0` ใหม่ได้ การจัดการ Unique Constraint บน MySQL ต้องใช้ Functional Index (ตามตัวอย่างในข้อ 2.1) เพื่อป้องกันการตีกันของค่า `NULL` ในฐานข้อมูล
+2. **ลดภาระ QNAP/ASUSTOR:** อุปกรณ์จำพวก NAS On-Premise มักจะมีปัญหาเรื่อง Random Read/Write Disk I/O การใช้ **Composite Index** แบบครบคลุม (Covering Index) จะช่วยให้ MySQL คืนค่าได้จาก Index Tree โดยตรง ไม่ต้องกระโดดไปอ่าน Data File จริง ซึ่งจะช่วยรีด Performance ของ NAS ได้สูงสุดครับ
diff --git a/specs/03-Data-and-Storage/03-03-file-storage.md b/specs/03-Data-and-Storage/03-03-file-storage.md
new file mode 100644
index 0000000..288ddc1
--- /dev/null
+++ b/specs/03-Data-and-Storage/03-03-file-storage.md
@@ -0,0 +1,315 @@
+# 3.3 File Storage and Handling
+
+---
+title: 'Data & Storage: File Storage and Handling (Two-Phase)'
+version: 1.8.0
+status: drafted
+owner: Nattanin Peancharoen
+last_updated: 2026-02-22
+related:
+- specs/01-requirements/01-03.10-file-handling.md (Merged)
+- specs/03-Data-and-Storage/ADR-003-file-storage-approach.md (Merged)
+- specs/02-architecture/02-01-system-architecture.md
+- ADR-006-security-best-practices
+---
+
+## 1. Overview and Core Infrastructure Requirements
+
+เอกสารฉบับนี้รวบรวมข้อกำหนดการจัดการไฟล์และการจัดเก็บไฟล์ (File Storage Approach) สำหรับ LCBP3-DMS โดยมีข้อบังคับด้าน Infrastructure และ Security ที่สำคัญมากดังต่อไปนี้:
+
+### 1.1 Infrastructure Requirement (การจัดเก็บและ Mount Volume)
+**สำคัญ (CRITICAL SPECIFICATION):**
+1. **Outside Webroot:** ไฟล์รูปและเอกสารทั้งหมดต้องถูกจัดเก็บไว้ **ภายนอก Webroot ของ Application** ห้ามเก็บไฟล์รูปหรือเอกสารไว้ใน Container หรือโฟลเดอร์ Webroot เด็ดขาด เพื่อป้องกันการเข้าถึงไฟล์โดยตรงจากสาธารณะ (Direct Public Access)
+2. **QNAP Volume Mount:** ต้องใช้ **QNAP Volume Mount เข้า Docker** (Mount external volume from QNAP NAS to Docker container) สำหรับเป็นพื้นที่เก็บไฟล์ Storage ให้ Container ดึงไปใช้งาน
+3. **Authenticated Endpoint:** ไฟล์ต้องถูกเข้าถึงและให้บริการดาวน์โหลดผ่าน Authenticated Endpoint ในฝั่ง Backend เท่านั้น โดยต้องผ่านการตรวจสอบสิทธิ์ (RBAC / Junction Table) เสียก่อน
+
+### 1.2 Access & Security Rules
+- **Virus Scan:** ต้องมีการ scan virus สำหรับไฟล์ที่อัปโหลดทั้งหมด โดยใช้ ClamAV หรือบริการ third-party ก่อนการบันทึก
+- **Whitelist File Types:** อนุญาตเฉพาะเอกสารตามที่กำหนด: PDF, DWG, DOCX, XLSX, ZIP
+- **Max File Size:** ขนาดไฟล์สูงสุดไม่เกิน 50MB ต่อไฟล์ (Total max 500MB per form submission)
+- **Expiration Time:** Download links ที่สร้างขึ้นต้องมี expiration time (default: 24 ชั่วโมง)
+- **Integrity Check:** ต้องมี file integrity check (checksum เป็น SHA-256) เพื่อป้องกันการแก้ไขไฟล์ภายหลัง
+- **Audit Logging:** ต้องบันทึก audit log ทุกครั้งที่มีการดาวน์โหลดไฟล์สำคัญ
+
+---
+
+## 2. Two-Phase File Storage Approach (ADR-003)
+
+### 2.1 Context and Problem Statement
+LCBP3-DMS ต้องจัดการ File Uploads สำหรับ Attachments ของเอกสาร (PDF, DWG, DOCX, etc.) โดยต้องรับมือกับปัญหา:
+1. **Orphan Files:** User อัปโหลดไฟล์แล้วไม่ Submit Form ทำให้ไฟล์ค้างใน Storage
+2. **Transaction Integrity:** ถ้า Database Transaction Rollback ไฟล์ยังอยู่ใน Storage ต้องสอดคล้องกับ Database Record
+3. **Virus Scanning:** ต้อง Scan ไฟล์ก่อน Save เข้าระบบถาวร
+4. **File Validation:** ตรวจสอบ Type, Size, และสร้าง Checksum
+5. **Storage Organization:** จัดเก็บไฟล์แยกเป็นสัดส่วน (เพื่อไม่ให้ QNAP Storage กระจัดกระจายและจำกัดขนาดได้)
+
+### 2.2 Decision Drivers
+- **Data Integrity:** File และ Database Record ต้อง Consistent
+- **Security:** ป้องกัน Virus และ Malicious Files
+- **User Experience:** Upload ต้องรวดเร็ว ไม่ Block UI (ถ้าอัปโหลดพร้อม Submit อาจทำให้ระบบดูค้าง)
+- **Storage Efficiency:** ไม่เก็บไฟล์ที่ไม่ถูกใช้งาน (Orphan files)
+- **Auditability:** ติดตามประวัติ File Operations ได้
+
+### 2.3 Considered Options & Decision
+- **Option 1:** Direct Upload to Permanent Storage (ทิ้งไฟล์ถ้า Transaction Fail / ได้ Orphan Files) - ❌
+- **Option 2:** Upload after Form Submission (UX แย่ ผู้ใช้ต้องรออัปโหลดรวดเดียวท้ายสุด) - ❌
+- **Option 3: Two-Phase Storage (Temp → Permanent) ⭐ (Selected Option)** - ✅
+
+**แนวทาง Two-Phase Storage (Temp → Permanent):**
+1. **Phase 1 (Upload):** ไฟล์ถูกอัปโหลดเข้าโฟลเดอร์ `temp/` ได้รับ `temp_id`
+2. **Phase 2 (Commit):** เมื่อ User กด Submit ฟอร์มสำเร็จ ระบบจะย้ายไฟล์จาก `temp/` ไปยัง `permanent/{YYYY}/{MM}/` และบันทึกลง Database ใน Transaction เดียวกัน
+3. **Cleanup:** มี Cron Job ทำหน้าที่ลบไฟล์ใน `temp/` ที่ค้างเกินกำหนด (เช่น 24 ชั่วโมง)
+
+---
+
+## 3. Implementation Details
+
+### 3.1 Database Schema
+```sql
+CREATE TABLE attachments (
+ id INT PRIMARY KEY AUTO_INCREMENT,
+ original_filename VARCHAR(255) NOT NULL,
+ stored_filename VARCHAR(255) NOT NULL, -- UUID-based
+ file_path VARCHAR(500) NOT NULL, -- QNAP Mount path
+ mime_type VARCHAR(100) NOT NULL,
+ file_size INT NOT NULL,
+ checksum VARCHAR(64) NULL, -- SHA-256
+
+ -- Two-Phase Fields
+ is_temporary BOOLEAN DEFAULT TRUE,
+ temp_id VARCHAR(100) NULL, -- UUID for temp reference
+ expires_at DATETIME NULL, -- Temp file expiration
+
+ uploaded_by_user_id INT NOT NULL,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+
+ FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id),
+ INDEX idx_temp_files (is_temporary, expires_at)
+);
+```
+
+### 3.2 Two-Phase Storage Flow
+```mermaid
+sequenceDiagram
+ participant User as Client
+ participant BE as Backend
+ participant Virus as ClamAV
+ participant TempStorage as Temp Storage (QNAP Volume)
+ participant PermStorage as Permanent Storage (QNAP Volume)
+ participant DB as Database
+
+ Note over User,DB: Phase 1: Upload to Temporary Storage
+ User->>BE: POST /attachments/upload (file)
+ BE->>BE: Validate file type, size
+ BE->>Virus: Scan virus
+
+ alt File is CLEAN
+ Virus-->>BE: CLEAN
+ BE->>BE: Generate temp_id (UUID)
+ BE->>BE: Calculate SHA-256 checksum
+ BE->>TempStorage: Save to temp/{temp_id}
+ BE->>DB: INSERT attachment
(is_temporary=TRUE, expires_at=NOW+24h)
+ BE-->>User: { temp_id, expires_at }
+ else File is INFECTED
+ Virus-->>BE: INFECTED
+ BE-->>User: Error: Virus detected
+ end
+
+ Note over User,DB: Phase 2: Commit to Permanent Storage
+ User->>BE: POST /correspondences
{ temp_file_ids: [temp_id] }
+ BE->>DB: BEGIN Transaction
+ BE->>DB: INSERT correspondence
+
+ loop For each temp_file_id
+ BE->>TempStorage: Read temp file
+ BE->>PermStorage: Move to permanent/{YYYY}/{MM}/{UUID}
+ BE->>DB: UPDATE attachment
(is_temporary=FALSE, file_path=new_path)
+ BE->>DB: INSERT correspondence_attachments
+ BE->>TempStorage: DELETE temp file
+ end
+
+ BE->>DB: COMMIT Transaction
+ BE-->>User: Success
+
+ Note over BE,TempStorage: Cleanup Job (Every 6 hours)
+ BE->>DB: SELECT expired temp files
+ BE->>TempStorage: DELETE expired physical files
+ BE->>DB: DELETE attachment records
+```
+
+### 3.3 NestJS Service Implementation
+
+```typescript
+// file-storage.service.ts
+import { Injectable, BadRequestException, Logger } from '@nestjs/common';
+import { ConfigService } from '@nestjs/config';
+import { Cron } from '@nestjs/schedule';
+import { createHash } from 'crypto';
+import * as fs from 'fs-extra';
+import * as path from 'path';
+import { v4 as uuidv4 } from 'uuid';
+import { LessThan } from 'typeorm';
+
+@Injectable()
+export class FileStorageService {
+ private readonly TEMP_DIR: string;
+ private readonly PERMANENT_DIR: string;
+ private readonly TEMP_EXPIRY_HOURS = 24;
+ private readonly logger = new Logger(FileStorageService.name);
+
+ constructor(private config: ConfigService) {
+ // 💡 Must point to the QNAP Volume mount inside the container!
+ this.TEMP_DIR = this.config.get('STORAGE_PATH') + '/temp';
+ this.PERMANENT_DIR = this.config.get('STORAGE_PATH') + '/permanent';
+ }
+
+ // Phase 1: Upload to Temporary
+ async uploadToTemp(file: Express.Multer.File): Promise {
+ // 1. Validate file (Size & Type)
+ this.validateFile(file);
+
+ // 2. Virus scan (ClamAV)
+ await this.virusScan(file);
+
+ // 3. Generate temp ID and File paths
+ const tempId = uuidv4();
+ const storedFilename = `${tempId}_${file.originalname}`;
+ const tempPath = path.join(this.TEMP_DIR, storedFilename);
+
+ // 4. Calculate checksum
+ const checksum = await this.calculateChecksum(file.buffer);
+
+ // 5. Save to temp directory (Outside Webroot via volume mount)
+ await fs.writeFile(tempPath, file.buffer);
+
+ // 6. Create attachment record in DB (Example assuming typeorm usage)
+ const attachment = await this.attachmentRepo.save({
+ original_filename: file.originalname,
+ stored_filename: storedFilename,
+ file_path: tempPath,
+ mime_type: file.mimetype,
+ file_size: file.size,
+ checksum,
+ is_temporary: true,
+ temp_id: tempId,
+ expires_at: new Date(Date.now() + this.TEMP_EXPIRY_HOURS * 3600 * 1000),
+ uploaded_by_user_id: this.currentUserId,
+ });
+
+ return {
+ temp_id: tempId,
+ expires_at: attachment.expires_at,
+ filename: file.originalname,
+ size: file.size,
+ };
+ }
+
+ // Phase 2: Commit to Permanent (within Transaction Manager)
+ async commitFiles(tempIds: string[], entityId: number, entityType: string, manager: EntityManager): Promise {
+ const attachments = [];
+
+ for (const tempId of tempIds) {
+ const tempAttachment = await manager.findOne(Attachment, { where: { temp_id: tempId, is_temporary: true } });
+ if (!tempAttachment) throw new Error(`Temporary file not found: ${tempId}`);
+ if (tempAttachment.expires_at < new Date()) throw new Error(`Temporary file expired: ${tempId}`);
+
+ // Generate permanent path: permanent/YYYY/MM
+ const now = new Date();
+ const year = now.getFullYear();
+ const month = (now.getMonth() + 1).toString().padStart(2, '0');
+ const permanentDir = path.join(this.PERMANENT_DIR, year.toString(), month);
+ await fs.ensureDir(permanentDir);
+
+ const permanentFilename = `${uuidv4()}_${tempAttachment.original_filename}`;
+ const permanentPath = path.join(permanentDir, permanentFilename);
+
+ // Move file physically in QNAP Volume
+ await fs.move(tempAttachment.file_path, permanentPath);
+
+ // Update Database record
+ await manager.update(Attachment, { id: tempAttachment.id }, {
+ file_path: permanentPath,
+ stored_filename: permanentFilename,
+ is_temporary: false,
+ temp_id: null,
+ expires_at: null,
+ });
+
+ attachments.push(tempAttachment);
+ }
+ return attachments;
+ }
+
+ // Phase 3: Cleanup Job (Cron)
+ @Cron('0 */6 * * *') // Every 6 hours
+ async cleanupExpiredFiles(): Promise {
+ const expiredFiles = await this.attachmentRepo.find({
+ where: { is_temporary: true, expires_at: LessThan(new Date()) },
+ });
+
+ for (const file of expiredFiles) {
+ try {
+ await fs.remove(file.file_path);
+ await this.attachmentRepo.remove(file);
+ this.logger.log(`Cleaned up expired file: ${file.temp_id}`);
+ } catch (error) {
+ this.logger.error(`Failed to cleanup file: ${file.temp_id}`, error);
+ }
+ }
+ }
+
+ private validateFile(file: Express.Multer.File): void {
+ const allowedTypes = [
+ 'application/pdf',
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ // ... (DOCX, WHiteListed Mimetypes + XLSX, DWG, ZIP)
+ ];
+ const maxSize = 50 * 1024 * 1024; // 50MB
+ if (!allowedTypes.includes(file.mimetype)) throw new BadRequestException('Invalid file type');
+ if (file.size > maxSize) throw new BadRequestException('File too large (max 50MB)');
+ // 💡 Add Magic Number Verification logic in real implementation to avoid simple extension spoofing
+ }
+
+ private async virusScan(file: Express.Multer.File): Promise {
+ // ClamAV integration example
+ // const scanner = await this.clamAV.scan(file.buffer);
+ // if (scanner.isInfected) throw new BadRequestException('Virus detected in file');
+ }
+
+ private async calculateChecksum(buffer: Buffer): Promise {
+ return createHash('sha256').update(buffer).digest('hex');
+ }
+}
+```
+
+### 3.4 API Controller Context
+ในส่วนของตัว Controller ฝ่ายรับข้อมูลจะต้องแยกระหว่าง Uploading กับ Comit:
+1. `POST /attachments/upload` ใช้เพื่อรับไฟล์และ Return `temp_id` แก่ User ทันที
+2. `POST /correspondences` หรือ Object อื่นๆ ใช้เพื่อ Commit Database โดยจะรับ `temp_file_ids: []` พ่วงมากับ Body form
+
+---
+
+## 4. Consequences & Mitigation Strategies
+
+### Positive Consequences
+1. ✅ **Fast Upload UX:** User upload แบบ Async ก่อน Submit ดำเนินการลื่นไหล
+2. ✅ **No Orphan Files:** เกิดระบบ Auto-cleanup จัดการไฟล์หมดอายุโดยอัตโนมัติ ไม่เปลืองสเปซ QNAP
+3. ✅ **Transaction Safe:** Rollback ได้สมบูรณ์หากบันทึกฐานข้อมูลผิดพลาด ไฟล์จะถูก Cron จัดการให้ทีหลังไม่ตกค้างในระบบ
+4. ✅ **Security:** Virus scan ไฟล์ก่อน Commit เข้าถึงข้อมูล Sensitive Area
+5. ✅ **Audit Trail:** ติดตามประวัติ Operations ต่างๆ เพื่อความโปร่งใส
+6. ✅ **Storage Organization:** จัดเก็บอย่างเป็นระเบียบ ด้วยรูปแบบ YYYY/MM ลดคอขวด IO Operations ในระบบ
+
+### Negative Consequences & Mitigations
+1. ❌ **Complexity:** ต้อง Implement 2 phases ซึ่งซับซ้อนขึ้น
+ 👉 *Mitigation:* รวบ Logic ทุกอย่างให้เป็น Service ชั้นเดียว (`FileStorageService`) เพื่อให้จัดการง่ายและเรียกใช้ง่ายที่สุด
+2. ❌ **Extra Storage:** ต้องใช้พื้นที่ QNAP ในส่วน Temp directory ควบคู่ไปกับแบบ Permanent
+ 👉 *Mitigation:* คอย Monitor และปรับรอบความถี่ของการ Cleanup หากไฟล์มีปริมาณไหลเวียนเยอะมาก
+3. ❌ **Edge Cases:** อาจเกิดประเด็นเรื่อง File lock หรือ missing temp files
+ 👉 *Mitigation:* ทำ Proper error handling พร้อม Logging ให้ตรวจสอบได้ง่าย
+
+---
+
+## 5. Performance Optimization Consideration
+- **Streaming:** ใช้ multipart/form-data streaming เพิ่อลดภาระ Memory ของฝั่งเครื่องเซิฟเวอร์ (NestJS) ขณะสูบไฟล์ใหญ่ๆ
+- **Compression:** พิจารณาเรื่องการบีบอัดสำหรับไฟล์ขนาดใหญ่หรือบางประเภท
+- **Deduplication Check:** สามารถใช้งาน Field `checksum` ดักการ Commit ด้วยข้อมูลชุดเดิมที่เคยถูกอัปโหลดเพื่อประหยัดพื้นที่จัดเก็บ (Deduplicate)
diff --git a/specs/02-architecture/02-03-data-model.md b/specs/03-Data-and-Storage/99-archives/02-03-data-model.md
similarity index 100%
rename from specs/02-architecture/02-03-data-model.md
rename to specs/03-Data-and-Storage/99-archives/02-03-data-model.md
diff --git a/specs/07-database/data-dictionary-v1.7.0.md b/specs/03-Data-and-Storage/99-archives/data-dictionary-v1.7.0.md
similarity index 100%
rename from specs/07-database/data-dictionary-v1.7.0.md
rename to specs/03-Data-and-Storage/99-archives/data-dictionary-v1.7.0.md
diff --git a/specs/07-database/deltas/01-add-reference-date.sql b/specs/03-Data-and-Storage/deltas/01-add-reference-date.sql
similarity index 100%
rename from specs/07-database/deltas/01-add-reference-date.sql
rename to specs/03-Data-and-Storage/deltas/01-add-reference-date.sql
diff --git a/specs/07-database/deltas/02-add-rbac-bulk-permission.sql b/specs/03-Data-and-Storage/deltas/02-add-rbac-bulk-permission.sql
similarity index 100%
rename from specs/07-database/deltas/02-add-rbac-bulk-permission.sql
rename to specs/03-Data-and-Storage/deltas/02-add-rbac-bulk-permission.sql
diff --git a/specs/07-database/fix-project-permissions.sql b/specs/03-Data-and-Storage/fix-project-permissions.sql
similarity index 100%
rename from specs/07-database/fix-project-permissions.sql
rename to specs/03-Data-and-Storage/fix-project-permissions.sql
diff --git a/specs/07-database/lcbp3-v1.7.0-schema.sql b/specs/03-Data-and-Storage/lcbp3-v1.7.0-schema.sql
similarity index 100%
rename from specs/07-database/lcbp3-v1.7.0-schema.sql
rename to specs/03-Data-and-Storage/lcbp3-v1.7.0-schema.sql
diff --git a/specs/07-database/lcbp3-v1.7.0-seed-basic.sql b/specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-basic.sql
similarity index 100%
rename from specs/07-database/lcbp3-v1.7.0-seed-basic.sql
rename to specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-basic.sql
diff --git a/specs/07-database/lcbp3-v1.7.0-seed-contractdrawing.sql b/specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-contractdrawing.sql
similarity index 100%
rename from specs/07-database/lcbp3-v1.7.0-seed-contractdrawing.sql
rename to specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-contractdrawing.sql
diff --git a/specs/07-database/lcbp3-v1.7.0-seed-permissions.sql b/specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-permissions.sql
similarity index 100%
rename from specs/07-database/lcbp3-v1.7.0-seed-permissions.sql
rename to specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-permissions.sql
diff --git a/specs/07-database/lcbp3-v1.7.0-seed-shopdrawing.sql b/specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-shopdrawing.sql
similarity index 100%
rename from specs/07-database/lcbp3-v1.7.0-seed-shopdrawing.sql
rename to specs/03-Data-and-Storage/lcbp3-v1.7.0-seed-shopdrawing.sql
diff --git a/specs/07-database/permissions-verification.sql b/specs/03-Data-and-Storage/permissions-verification.sql
similarity index 100%
rename from specs/07-database/permissions-verification.sql
rename to specs/03-Data-and-Storage/permissions-verification.sql
diff --git a/specs/01-requirements/01-03.10-file-handling.md b/specs/99-archives/01-03.10-file-handling.md
similarity index 100%
rename from specs/01-requirements/01-03.10-file-handling.md
rename to specs/99-archives/01-03.10-file-handling.md
diff --git a/specs/05-decisions/ADR-003-file-storage-approach.md b/specs/99-archives/ADR-003-file-storage-approach.md
similarity index 100%
rename from specs/05-decisions/ADR-003-file-storage-approach.md
rename to specs/99-archives/ADR-003-file-storage-approach.md
diff --git a/specs/09-history/2025-12-06_p0-build-fixes.md b/specs/99-archives/history/2025-12-06_p0-build-fixes.md
similarity index 100%
rename from specs/09-history/2025-12-06_p0-build-fixes.md
rename to specs/99-archives/history/2025-12-06_p0-build-fixes.md
diff --git a/specs/09-history/2025-12-06_p1-frontend-plan.md b/specs/99-archives/history/2025-12-06_p1-frontend-plan.md
similarity index 100%
rename from specs/09-history/2025-12-06_p1-frontend-plan.md
rename to specs/99-archives/history/2025-12-06_p1-frontend-plan.md
diff --git a/specs/09-history/2025-12-06_p2-completion.md b/specs/99-archives/history/2025-12-06_p2-completion.md
similarity index 100%
rename from specs/09-history/2025-12-06_p2-completion.md
rename to specs/99-archives/history/2025-12-06_p2-completion.md
diff --git a/specs/09-history/2025-12-06_p3-admin-panel-plan.md b/specs/99-archives/history/2025-12-06_p3-admin-panel-plan.md
similarity index 100%
rename from specs/09-history/2025-12-06_p3-admin-panel-plan.md
rename to specs/99-archives/history/2025-12-06_p3-admin-panel-plan.md
diff --git a/specs/09-history/2025-12-07_p4-fe-dashboard-admin.md b/specs/99-archives/history/2025-12-07_p4-fe-dashboard-admin.md
similarity index 100%
rename from specs/09-history/2025-12-07_p4-fe-dashboard-admin.md
rename to specs/99-archives/history/2025-12-07_p4-fe-dashboard-admin.md
diff --git a/specs/09-history/2025-12-10_organizations-refactoring.md b/specs/99-archives/history/2025-12-10_organizations-refactoring.md
similarity index 100%
rename from specs/09-history/2025-12-10_organizations-refactoring.md
rename to specs/99-archives/history/2025-12-10_organizations-refactoring.md
diff --git a/specs/09-history/2025-12-11-admin-console-fixes.md b/specs/99-archives/history/2025-12-11-admin-console-fixes.md
similarity index 100%
rename from specs/09-history/2025-12-11-admin-console-fixes.md
rename to specs/99-archives/history/2025-12-11-admin-console-fixes.md
diff --git a/specs/09-history/2025-12-11-admin-ux-refactor.md b/specs/99-archives/history/2025-12-11-admin-ux-refactor.md
similarity index 100%
rename from specs/09-history/2025-12-11-admin-ux-refactor.md
rename to specs/99-archives/history/2025-12-11-admin-ux-refactor.md
diff --git a/specs/09-history/2025-12-11-correspondence-refactor.md b/specs/99-archives/history/2025-12-11-correspondence-refactor.md
similarity index 100%
rename from specs/09-history/2025-12-11-correspondence-refactor.md
rename to specs/99-archives/history/2025-12-11-correspondence-refactor.md
diff --git a/specs/09-history/2025-12-11-frontend-tests.md b/specs/99-archives/history/2025-12-11-frontend-tests.md
similarity index 100%
rename from specs/09-history/2025-12-11-frontend-tests.md
rename to specs/99-archives/history/2025-12-11-frontend-tests.md
diff --git a/specs/09-history/2025-12-11_frontend-integration-review.md b/specs/99-archives/history/2025-12-11_frontend-integration-review.md
similarity index 100%
rename from specs/09-history/2025-12-11_frontend-integration-review.md
rename to specs/99-archives/history/2025-12-11_frontend-integration-review.md
diff --git a/specs/09-history/2025-12-13-schema-v160-document-number-fixes.md b/specs/99-archives/history/2025-12-13-schema-v160-document-number-fixes.md
similarity index 100%
rename from specs/09-history/2025-12-13-schema-v160-document-number-fixes.md
rename to specs/99-archives/history/2025-12-13-schema-v160-document-number-fixes.md
diff --git a/specs/09-history/2025-12-17-document-numbering-v162-alignment.md b/specs/99-archives/history/2025-12-17-document-numbering-v162-alignment.md
similarity index 100%
rename from specs/09-history/2025-12-17-document-numbering-v162-alignment.md
rename to specs/99-archives/history/2025-12-17-document-numbering-v162-alignment.md
diff --git a/specs/09-history/2025-12-18-refactor-document-numbering.md b/specs/99-archives/history/2025-12-18-refactor-document-numbering.md
similarity index 100%
rename from specs/09-history/2025-12-18-refactor-document-numbering.md
rename to specs/99-archives/history/2025-12-18-refactor-document-numbering.md
diff --git a/specs/09-history/2025-12-20-Revise-Schema.md b/specs/99-archives/history/2025-12-20-Revise-Schema.md
similarity index 100%
rename from specs/09-history/2025-12-20-Revise-Schema.md
rename to specs/99-archives/history/2025-12-20-Revise-Schema.md
diff --git a/specs/09-history/2025-12-23-document-numbering-form-refactoring.md b/specs/99-archives/history/2025-12-23-document-numbering-form-refactoring.md
similarity index 100%
rename from specs/09-history/2025-12-23-document-numbering-form-refactoring.md
rename to specs/99-archives/history/2025-12-23-document-numbering-form-refactoring.md
diff --git a/specs/09-history/2025-12-23-frontend-refactor-v170.md b/specs/99-archives/history/2025-12-23-frontend-refactor-v170.md
similarity index 100%
rename from specs/09-history/2025-12-23-frontend-refactor-v170.md
rename to specs/99-archives/history/2025-12-23-frontend-refactor-v170.md
diff --git a/specs/09-history/2025-12-24-document-numbering-fixes.md b/specs/99-archives/history/2025-12-24-document-numbering-fixes.md
similarity index 100%
rename from specs/09-history/2025-12-24-document-numbering-fixes.md
rename to specs/99-archives/history/2025-12-24-document-numbering-fixes.md
diff --git a/specs/09-history/2025-12-25-drawing-admin-panel-implementation.md b/specs/99-archives/history/2025-12-25-drawing-admin-panel-implementation.md
similarity index 100%
rename from specs/09-history/2025-12-25-drawing-admin-panel-implementation.md
rename to specs/99-archives/history/2025-12-25-drawing-admin-panel-implementation.md
diff --git a/specs/09-history/2025-12-25-drawing-module-refactor.md b/specs/99-archives/history/2025-12-25-drawing-module-refactor.md
similarity index 100%
rename from specs/09-history/2025-12-25-drawing-module-refactor.md
rename to specs/99-archives/history/2025-12-25-drawing-module-refactor.md
diff --git a/specs/09-history/2025-12-25-drawing-revision-schema-update.md b/specs/99-archives/history/2025-12-25-drawing-revision-schema-update.md
similarity index 100%
rename from specs/09-history/2025-12-25-drawing-revision-schema-update.md
rename to specs/99-archives/history/2025-12-25-drawing-revision-schema-update.md
diff --git a/specs/09-history/20251208-TASK-BE-004-document-numbering.md b/specs/99-archives/history/20251208-TASK-BE-004-document-numbering.md
similarity index 100%
rename from specs/09-history/20251208-TASK-BE-004-document-numbering.md
rename to specs/99-archives/history/20251208-TASK-BE-004-document-numbering.md
diff --git a/specs/09-history/20251208-TASK-FE-012-numbering-config-ui.md b/specs/99-archives/history/20251208-TASK-FE-012-numbering-config-ui.md
similarity index 100%
rename from specs/09-history/20251208-TASK-FE-012-numbering-config-ui.md
rename to specs/99-archives/history/20251208-TASK-FE-012-numbering-config-ui.md
diff --git a/specs/09-history/20251216-document-numbering-backend-methods.md b/specs/99-archives/history/20251216-document-numbering-backend-methods.md
similarity index 100%
rename from specs/09-history/20251216-document-numbering-backend-methods.md
rename to specs/99-archives/history/20251216-document-numbering-backend-methods.md
diff --git a/specs/09-history/P0 implementation walkthrough.md b/specs/99-archives/history/P0 implementation walkthrough.md
similarity index 100%
rename from specs/09-history/P0 implementation walkthrough.md
rename to specs/99-archives/history/P0 implementation walkthrough.md
diff --git a/specs/09-history/P0 test-results.md b/specs/99-archives/history/P0 test-results.md
similarity index 100%
rename from specs/09-history/P0 test-results.md
rename to specs/99-archives/history/P0 test-results.md
diff --git a/specs/09-history/TASK-BE-001-database-migrations.md b/specs/99-archives/history/TASK-BE-001-database-migrations.md
similarity index 100%
rename from specs/09-history/TASK-BE-001-database-migrations.md
rename to specs/99-archives/history/TASK-BE-001-database-migrations.md
diff --git a/specs/09-history/TASK-BE-002-auth-rbac.md b/specs/99-archives/history/TASK-BE-002-auth-rbac.md
similarity index 100%
rename from specs/09-history/TASK-BE-002-auth-rbac.md
rename to specs/99-archives/history/TASK-BE-002-auth-rbac.md
diff --git a/specs/09-history/TASK-BE-003-file-storage.md b/specs/99-archives/history/TASK-BE-003-file-storage.md
similarity index 100%
rename from specs/09-history/TASK-BE-003-file-storage.md
rename to specs/99-archives/history/TASK-BE-003-file-storage.md
diff --git a/specs/09-history/TASK-BE-004-document-numbering.md b/specs/99-archives/history/TASK-BE-004-document-numbering.md
similarity index 100%
rename from specs/09-history/TASK-BE-004-document-numbering.md
rename to specs/99-archives/history/TASK-BE-004-document-numbering.md
diff --git a/specs/09-history/TASK-BE-005-correspondence-module.md b/specs/99-archives/history/TASK-BE-005-correspondence-module.md
similarity index 100%
rename from specs/09-history/TASK-BE-005-correspondence-module.md
rename to specs/99-archives/history/TASK-BE-005-correspondence-module.md
diff --git a/specs/09-history/TASK-BE-006-workflow-engine.md b/specs/99-archives/history/TASK-BE-006-workflow-engine.md
similarity index 100%
rename from specs/09-history/TASK-BE-006-workflow-engine.md
rename to specs/99-archives/history/TASK-BE-006-workflow-engine.md
diff --git a/specs/09-history/TASK-BE-007-rfa-module.md b/specs/99-archives/history/TASK-BE-007-rfa-module.md
similarity index 100%
rename from specs/09-history/TASK-BE-007-rfa-module.md
rename to specs/99-archives/history/TASK-BE-007-rfa-module.md
diff --git a/specs/09-history/TASK-BE-008-drawing-module.md b/specs/99-archives/history/TASK-BE-008-drawing-module.md
similarity index 100%
rename from specs/09-history/TASK-BE-008-drawing-module.md
rename to specs/99-archives/history/TASK-BE-008-drawing-module.md
diff --git a/specs/09-history/TASK-BE-009-circulation-transmittal.md b/specs/99-archives/history/TASK-BE-009-circulation-transmittal.md
similarity index 100%
rename from specs/09-history/TASK-BE-009-circulation-transmittal.md
rename to specs/99-archives/history/TASK-BE-009-circulation-transmittal.md
diff --git a/specs/09-history/TASK-BE-011-notification-audit.md b/specs/99-archives/history/TASK-BE-011-notification-audit.md
similarity index 100%
rename from specs/09-history/TASK-BE-011-notification-audit.md
rename to specs/99-archives/history/TASK-BE-011-notification-audit.md
diff --git a/specs/09-history/TASK-BE-012-master-data-management.md b/specs/99-archives/history/TASK-BE-012-master-data-management.md
similarity index 100%
rename from specs/09-history/TASK-BE-012-master-data-management.md
rename to specs/99-archives/history/TASK-BE-012-master-data-management.md
diff --git a/specs/09-history/TASK-BE-013-user-management.md b/specs/99-archives/history/TASK-BE-013-user-management.md
similarity index 100%
rename from specs/09-history/TASK-BE-013-user-management.md
rename to specs/99-archives/history/TASK-BE-013-user-management.md
diff --git a/specs/09-history/TASK-FE-001-frontend-setup.md b/specs/99-archives/history/TASK-FE-001-frontend-setup.md
similarity index 100%
rename from specs/09-history/TASK-FE-001-frontend-setup.md
rename to specs/99-archives/history/TASK-FE-001-frontend-setup.md
diff --git a/specs/09-history/TASK-FE-002-auth-ui.md b/specs/99-archives/history/TASK-FE-002-auth-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-002-auth-ui.md
rename to specs/99-archives/history/TASK-FE-002-auth-ui.md
diff --git a/specs/09-history/TASK-FE-003-layout-navigation.md b/specs/99-archives/history/TASK-FE-003-layout-navigation.md
similarity index 100%
rename from specs/09-history/TASK-FE-003-layout-navigation.md
rename to specs/99-archives/history/TASK-FE-003-layout-navigation.md
diff --git a/specs/09-history/TASK-FE-004-correspondence-ui.md b/specs/99-archives/history/TASK-FE-004-correspondence-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-004-correspondence-ui.md
rename to specs/99-archives/history/TASK-FE-004-correspondence-ui.md
diff --git a/specs/09-history/TASK-FE-005-common-components.md b/specs/99-archives/history/TASK-FE-005-common-components.md
similarity index 100%
rename from specs/09-history/TASK-FE-005-common-components.md
rename to specs/99-archives/history/TASK-FE-005-common-components.md
diff --git a/specs/09-history/TASK-FE-006-rfa-ui.md b/specs/99-archives/history/TASK-FE-006-rfa-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-006-rfa-ui.md
rename to specs/99-archives/history/TASK-FE-006-rfa-ui.md
diff --git a/specs/09-history/TASK-FE-007-drawing-ui.md b/specs/99-archives/history/TASK-FE-007-drawing-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-007-drawing-ui.md
rename to specs/99-archives/history/TASK-FE-007-drawing-ui.md
diff --git a/specs/09-history/TASK-FE-008-search-ui.md b/specs/99-archives/history/TASK-FE-008-search-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-008-search-ui.md
rename to specs/99-archives/history/TASK-FE-008-search-ui.md
diff --git a/specs/09-history/TASK-FE-009-dashboard-notifications.md b/specs/99-archives/history/TASK-FE-009-dashboard-notifications.md
similarity index 100%
rename from specs/09-history/TASK-FE-009-dashboard-notifications.md
rename to specs/99-archives/history/TASK-FE-009-dashboard-notifications.md
diff --git a/specs/09-history/TASK-FE-010-admin-panel.md b/specs/99-archives/history/TASK-FE-010-admin-panel.md
similarity index 100%
rename from specs/09-history/TASK-FE-010-admin-panel.md
rename to specs/99-archives/history/TASK-FE-010-admin-panel.md
diff --git a/specs/09-history/TASK-FE-011-workflow-config-ui.md b/specs/99-archives/history/TASK-FE-011-workflow-config-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-011-workflow-config-ui.md
rename to specs/99-archives/history/TASK-FE-011-workflow-config-ui.md
diff --git a/specs/09-history/TASK-FE-012-numbering-config-ui.md b/specs/99-archives/history/TASK-FE-012-numbering-config-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-012-numbering-config-ui.md
rename to specs/99-archives/history/TASK-FE-012-numbering-config-ui.md
diff --git a/specs/09-history/TASK-FE-013-circulation-transmittal-ui.md b/specs/99-archives/history/TASK-FE-013-circulation-transmittal-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-013-circulation-transmittal-ui.md
rename to specs/99-archives/history/TASK-FE-013-circulation-transmittal-ui.md
diff --git a/specs/09-history/TASK-FE-014-reference-data-ui.md b/specs/99-archives/history/TASK-FE-014-reference-data-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-014-reference-data-ui.md
rename to specs/99-archives/history/TASK-FE-014-reference-data-ui.md
diff --git a/specs/09-history/TASK-FE-015-security-admin-ui.md b/specs/99-archives/history/TASK-FE-015-security-admin-ui.md
similarity index 100%
rename from specs/09-history/TASK-FE-015-security-admin-ui.md
rename to specs/99-archives/history/TASK-FE-015-security-admin-ui.md
diff --git a/specs/09-history/patch-add-editor-workflow-permission.sql b/specs/99-archives/history/patch-add-editor-workflow-permission.sql
similarity index 100%
rename from specs/09-history/patch-add-editor-workflow-permission.sql
rename to specs/99-archives/history/patch-add-editor-workflow-permission.sql
diff --git a/specs/09-history/patch-drop-recipient-fk.sql b/specs/99-archives/history/patch-drop-recipient-fk.sql
similarity index 100%
rename from specs/09-history/patch-drop-recipient-fk.sql
rename to specs/99-archives/history/patch-drop-recipient-fk.sql
diff --git a/specs/09-history/patch-fix-workflow-compiled.sql b/specs/99-archives/history/patch-fix-workflow-compiled.sql
similarity index 100%
rename from specs/09-history/patch-fix-workflow-compiled.sql
rename to specs/99-archives/history/patch-fix-workflow-compiled.sql
diff --git a/specs/06-tasks/README.md b/specs/99-archives/tasks/README.md
similarity index 100%
rename from specs/06-tasks/README.md
rename to specs/99-archives/tasks/README.md
diff --git a/specs/06-tasks/REQ-009-DocumentNumbering.md b/specs/99-archives/tasks/REQ-009-DocumentNumbering.md
similarity index 100%
rename from specs/06-tasks/REQ-009-DocumentNumbering.md
rename to specs/99-archives/tasks/REQ-009-DocumentNumbering.md
diff --git a/specs/06-tasks/TASK-BE-010-search-elasticsearch.md b/specs/99-archives/tasks/TASK-BE-010-search-elasticsearch.md
similarity index 100%
rename from specs/06-tasks/TASK-BE-010-search-elasticsearch.md
rename to specs/99-archives/tasks/TASK-BE-010-search-elasticsearch.md
diff --git a/specs/06-tasks/TASK-BE-014-testing-documentation.md b/specs/99-archives/tasks/TASK-BE-014-testing-documentation.md
similarity index 100%
rename from specs/06-tasks/TASK-BE-014-testing-documentation.md
rename to specs/99-archives/tasks/TASK-BE-014-testing-documentation.md
diff --git a/specs/06-tasks/TASK-BE-015-schema-v160-migration.md b/specs/99-archives/tasks/TASK-BE-015-schema-v160-migration.md
similarity index 100%
rename from specs/06-tasks/TASK-BE-015-schema-v160-migration.md
rename to specs/99-archives/tasks/TASK-BE-015-schema-v160-migration.md
diff --git a/specs/06-tasks/TASK-BE-017-document-numbering-refactor.md b/specs/99-archives/tasks/TASK-BE-017-document-numbering-refactor.md
similarity index 100%
rename from specs/06-tasks/TASK-BE-017-document-numbering-refactor.md
rename to specs/99-archives/tasks/TASK-BE-017-document-numbering-refactor.md
diff --git a/specs/06-tasks/TASK-BE-018-v170-refactor.md b/specs/99-archives/tasks/TASK-BE-018-v170-refactor.md
similarity index 100%
rename from specs/06-tasks/TASK-BE-018-v170-refactor.md
rename to specs/99-archives/tasks/TASK-BE-018-v170-refactor.md
diff --git a/specs/06-tasks/TASK-BEFE-001-Refactor-260218.md b/specs/99-archives/tasks/TASK-BEFE-001-Refactor-260218.md
similarity index 100%
rename from specs/06-tasks/TASK-BEFE-001-Refactor-260218.md
rename to specs/99-archives/tasks/TASK-BEFE-001-Refactor-260218.md
diff --git a/specs/06-tasks/TASK-FE-016-schema-v160-adaptation.md b/specs/99-archives/tasks/TASK-FE-016-schema-v160-adaptation.md
similarity index 100%
rename from specs/06-tasks/TASK-FE-016-schema-v160-adaptation.md
rename to specs/99-archives/tasks/TASK-FE-016-schema-v160-adaptation.md
diff --git a/specs/06-tasks/TASK-FE-017-document-numbering-refactor.md b/specs/99-archives/tasks/TASK-FE-017-document-numbering-refactor.md
similarity index 100%
rename from specs/06-tasks/TASK-FE-017-document-numbering-refactor.md
rename to specs/99-archives/tasks/TASK-FE-017-document-numbering-refactor.md
diff --git a/specs/06-tasks/TASK-FE-019-v170-refactor.md b/specs/99-archives/tasks/TASK-FE-019-v170-refactor.md
similarity index 100%
rename from specs/06-tasks/TASK-FE-019-v170-refactor.md
rename to specs/99-archives/tasks/TASK-FE-019-v170-refactor.md
diff --git a/specs/06-tasks/backend-audit-results.md b/specs/99-archives/tasks/backend-audit-results.md
similarity index 100%
rename from specs/06-tasks/backend-audit-results.md
rename to specs/99-archives/tasks/backend-audit-results.md
diff --git a/specs/06-tasks/backend-progress-report.md b/specs/99-archives/tasks/backend-progress-report.md
similarity index 100%
rename from specs/06-tasks/backend-progress-report.md
rename to specs/99-archives/tasks/backend-progress-report.md
diff --git a/specs/06-tasks/frontend-progress-report.md b/specs/99-archives/tasks/frontend-progress-report.md
similarity index 100%
rename from specs/06-tasks/frontend-progress-report.md
rename to specs/99-archives/tasks/frontend-progress-report.md
diff --git a/specs/06-tasks/project-implementation-report.md b/specs/99-archives/tasks/project-implementation-report.md
similarity index 100%
rename from specs/06-tasks/project-implementation-report.md
rename to specs/99-archives/tasks/project-implementation-report.md
diff --git a/specs/README.md b/specs/README.md
new file mode 100644
index 0000000..e69de29