690514:2019 204-rfa-approval-refactor #01
CI / CD Pipeline / build (push) Successful in 6m1s
CI / CD Pipeline / deploy (push) Failing after 6m42s

This commit is contained in:
2026-05-14 20:19:21 +07:00
parent 07cc6d47b1
commit 0240d80da5
183 changed files with 20050 additions and 1017 deletions
@@ -1,6 +1,6 @@
---
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
version: 1.8.7
version: 1.9.0
status: released
owner: Nattanin Peancharoen
last_updated: 2026-04-14
@@ -84,7 +84,7 @@ erDiagram
---
# 3. Data Dictionary V1.8.0
# 3. Data Dictionary V1.9.0
> หมายเหตุ: PK = Primary Key, FK = Foreign Key, AI = AUTO_INCREMENT. รูปแบบ Soft Delete จะปรากฏ Column `deleted_at DATETIME NULL` เป็นมาตรฐาน
@@ -165,14 +165,14 @@ erDiagram
### 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,
AUTO_INCREMENT | UNIQUE identifier FOR role | | **uuid** | **UUID** | **NOT NULL, DEFAULT (UUID()), UNIQUE** | **[delta-11] UUID Public Identifier (ADR-019) — ใช้ใน API Response / distribution_recipients** | | 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,
** INDEXES **: - PRIMARY KEY (role_id) - **UNIQUE INDEX idx_roles_uuid (uuid) [delta-11]** - INDEX (scope) ** Relationships \*\*: - Referenced by: role_permissions,
user_assignments ---
### 2.3 permissions
@@ -2337,4 +2337,258 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal)
---
**End of Data Dictionary V1.8.0**
**End of Data Dictionary V1.9.0**
---
## **20. 🔍 RFA Approval System Tables (NEW v1.9.0)**
> Feature Branch: `1-rfa-approval-refactor` | Schema File: `lcbp3-v1.9.0-rfa-approval-schema.sql`
### 20.1 review_teams
**Purpose**: ทีมตรวจสอบแยกตาม Discipline สำหรับ RFA Approval
| Column Name | Data Type | Constraints | Description |
| --------------------- | ------------ | ------------------------------- | --------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
| project_id | INT | NOT NULL, FK | Reference to projects |
| name | VARCHAR(100) | NOT NULL | Team name |
| description | VARCHAR(255) | NULL | Team description |
| default_for_rfa_types | TEXT | NULL | Comma-separated RFA type codes (e.g. SDW,DDW) |
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
**Indexes**:
- PRIMARY KEY (id)
- UNIQUE KEY uq_review_teams_uuid (uuid)
- KEY idx_review_teams_project (project_id, is_active)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
**Relationships**:
- Parent: projects
- Children: review_team_members, review_tasks
- Referenced by: distribution_recipients (recipient_type='TEAM')
---
### 20.2 review_team_members
**Purpose**: สมาชิกในทีมแยกตาม Discipline (M:N ระหว่าง review_teams และ users)
| Column Name | Data Type | Constraints | Description |
| -------------- | ----------- | ------------------------------- | ------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
| team_id | INT | NOT NULL, FK | Reference to review_teams |
| user_id | INT | NOT NULL, FK | Reference to users (user_id) |
| discipline_id | INT | NOT NULL, FK | Reference to disciplines |
| role | ENUM | NOT NULL, DEFAULT 'REVIEWER' | REVIEWER, LEAD, MANAGER |
| priority_order | INT | NOT NULL, DEFAULT 0 | Assignment priority within discipline |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
**Indexes**:
- PRIMARY KEY (id)
- UNIQUE KEY uq_review_team_members_uuid (uuid)
- UNIQUE KEY uq_team_user_discipline (team_id, user_id, discipline_id)
- FOREIGN KEY (team_id) REFERENCES review_teams(id) ON DELETE CASCADE
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
- FOREIGN KEY (discipline_id) REFERENCES disciplines(id)
---
### 20.3 response_codes
**Purpose**: รหัสตอบกลับมาตรฐาน (Approval Matrix) สำหรับ RFA Review
| Column Name | Data Type | Constraints | Description |
| --------------- | --------- | ----------------------------------- | -------------------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
| code | VARCHAR(10)| NOT NULL | Response code (1A, 1B, 1C, 1D, 1E, 1F, 1G, 2, 3, 4) |
| sub_status | VARCHAR(10)| NULL | Optional sub-status |
| category | ENUM | NOT NULL | ENGINEERING, MATERIAL, CONTRACT, TESTING, ESG |
| description_th | TEXT | NOT NULL | Thai description |
| description_en | TEXT | NOT NULL | English description |
| implications | JSON | NULL | {affectsSchedule, affectsCost, requiresContractReview} |
| notify_roles | TEXT | NULL | Comma-separated roles to notify (e.g. CONTRACT_MANAGER,QS_MANAGER) |
| is_active | TINYINT(1)| NOT NULL, DEFAULT 1 | Active status |
| is_system | TINYINT(1)| NOT NULL, DEFAULT 0 | System default — cannot delete |
| created_at | DATETIME(6)| NOT NULL, DEFAULT NOW | Record creation timestamp |
**Indexes**:
- PRIMARY KEY (id)
- UNIQUE KEY uq_response_codes_uuid (uuid)
- UNIQUE KEY uq_response_code_category (code, category)
- KEY idx_rc_category_active (category, is_active)
---
### 20.4 response_code_rules
**Purpose**: กฎการใช้ Response Code ต่อโครงการ/ประเภทเอกสาร (Project-level Override)
| Column Name | Data Type | Constraints | Description |
| ---------------------- | ----------- | ---------------------------- | ---------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
| project_id | INT | NULL, FK | NULL = global default |
| document_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
| response_code_id | INT | NOT NULL, FK | Reference to response_codes |
| is_enabled | TINYINT(1) | NOT NULL, DEFAULT 1 | Enable/disable this code for the context |
| requires_comments | TINYINT(1) | NOT NULL, DEFAULT 0 | Force comment when selecting this code |
| triggers_notification | TINYINT(1) | NOT NULL, DEFAULT 0 | Send notification when used |
| parent_rule_id | INT | NULL, FK | For inheritance tracking |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
**Business Rules**:
- `project_id = NULL` = Global default rule (applies to all projects)
- Project-level rules override global defaults
- UNIQUE constraint: (project_id, document_type_id, response_code_id)
---
### 20.5 review_tasks
**Purpose**: งานตรวจสอบสำหรับแต่ละ Discipline ใน Parallel Review Flow
| Column Name | Data Type | Constraints | Description |
| ------------------------ | ----------- | ----------------------------------- | --------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
| rfa_revision_id | INT | NOT NULL, FK | Reference to rfa_revisions |
| team_id | INT | NOT NULL, FK | Reference to review_teams |
| discipline_id | INT | NOT NULL, FK | Reference to disciplines |
| assigned_to_user_id | INT | NULL, FK | Assigned reviewer (NULL = auto-assign) |
| status | ENUM | NOT NULL, DEFAULT 'PENDING' | PENDING, IN_PROGRESS, COMPLETED, DELEGATED, EXPIRED, CANCELLED |
| due_date | DATE | NULL | Review due date |
| response_code_id | INT | NULL, FK | Selected response code |
| comments | TEXT | NULL | Reviewer comments |
| attachments | JSON | NULL | Array of attachment publicIds (ADR-021) |
| delegated_from_user_id | INT | NULL | Original assignee when task was delegated |
| completed_at | TIMESTAMP | NULL | Completion timestamp |
| version | INT | NOT NULL, DEFAULT 1 | Optimistic locking (ADR-002) |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
**Indexes**:
- UNIQUE KEY uq_review_task_per_revision_discipline (rfa_revision_id, team_id, discipline_id)
- KEY idx_review_tasks_assigned (assigned_to_user_id, status)
- FOREIGN KEY (assigned_to_user_id) REFERENCES users(user_id) ON DELETE SET NULL
**Business Rules**:
- One task per revision + team + discipline combination (UNIQUE enforced)
- `version` column used for optimistic locking (ADR-002)
- `attachments` JSON stores publicIds — resolved via AttachmentsService (no FK)
---
### 20.6 delegations
**Purpose**: การมอบหมายงาน (Task Delegation) ระหว่างผู้ใช้
| Column Name | Data Type | Constraints | Description |
| ------------------ | ----------- | ------------------------------- | ----------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
| delegator_user_id | INT | NOT NULL, FK | ผู้มอบหมาย → users(user_id) |
| delegate_user_id | INT | NOT NULL, FK | ผู้รับมอบหมาย → users(user_id) |
| start_date | DATE | NOT NULL | วันที่เริ่มการมอบหมาย |
| end_date | DATE | NULL | วันที่สิ้นสุด — BullMQ job flip is_active=0 เมื่อผ่าน (ADR-008) |
| scope | ENUM | NOT NULL, DEFAULT 'ALL' | ALL, RFA_ONLY, CORRESPONDENCE_ONLY, SPECIFIC_TYPES |
| document_types | TEXT | NULL | Comma-separated doc type codes when scope=SPECIFIC_TYPES |
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Managed by BullMQ scheduler — ห้าม flip manual |
| reason | TEXT | NULL | เหตุผลการมอบหมาย |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
**Business Rules**:
- **Auto-expiry**: BullMQ Scheduled Job ตรวจ `end_date < NOW()` และ flip `is_active = 0` (ADR-008)
- **Active check query**: `WHERE delegator_user_id = ? AND is_active = 1 AND start_date <= NOW() AND (end_date IS NULL OR end_date >= NOW())`
- ห้ามเปลี่ยน `is_active` โดยตรงในระดับ Service — ใช้ BullMQ เท่านั้น
---
### 20.7 reminder_rules
**Purpose**: กฎการแจ้งเตือน (Reminder/Escalation) สำหรับ Review Tasks
| Column Name | Data Type | Constraints | Description |
| -------------------------- | ----------- | ------------------------ | -------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
| name | VARCHAR(100)| NOT NULL | Rule name |
| project_id | INT | NULL, FK | NULL = global rule |
| document_type_id | INT | NULL, FK | NULL = applies to all types |
| trigger_days_before_due | INT | NOT NULL, DEFAULT 2 | วันก่อน due date ที่จะแจ้งเตือน |
| escalation_days_after_due | INT | NOT NULL, DEFAULT 1 | วันหลัง due date ที่จะ escalate |
| reminder_type | ENUM | NOT NULL | DUE_SOON, ON_DUE, OVERDUE, ESCALATION_L1, ESCALATION_L2 |
| recipients | TEXT | NOT NULL | Comma-separated: ASSIGNEE, MANAGER, PROJECT_MANAGER |
| message_template_th | TEXT | NOT NULL | Thai message template |
| message_template_en | TEXT | NOT NULL | English message template |
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
---
### 20.8 distribution_matrices
**Purpose**: ตารางกำหนดกฎการกระจายเอกสาร (Distribution Matrix) ตาม Response Code
| Column Name | Data Type | Constraints | Description |
| ---------------- | ----------- | ------------------------ | ---------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
| name | VARCHAR(100)| NOT NULL | Matrix name |
| project_id | INT | NULL, FK | NULL = global matrix |
| document_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
| response_code_id | INT | NULL, FK | NULL = applies to all codes |
| conditions | JSON | NULL | {codes:["1A","1B"], excludeCodes:["3","4"]} |
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
---
### 20.9 distribution_recipients
**Purpose**: ผู้รับเอกสารใน Distribution Matrix (Polymorphic Reference)
| Column Name | Data Type | Constraints | Description |
| -------------------- | ----------- | ------------------------ | ----------------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
| matrix_id | INT | NOT NULL, FK | Reference to distribution_matrices |
| recipient_type | ENUM | NOT NULL | USER, ORGANIZATION, TEAM, ROLE |
| recipient_public_id | UUID | NOT NULL | publicId ของ target entity (ดูตาราง Polymorphic Resolution ด้านล่าง) |
| delivery_method | ENUM | NOT NULL, DEFAULT 'BOTH' | EMAIL, IN_APP, BOTH |
| sequence | INT | NULL | For ordered delivery |
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
**Polymorphic Resolution Table**:
| recipient_type | recipient_public_id อ้างอิง | FK | หมายเหตุ |
|---|---|---|---|
| USER | users.uuid | Soft (no FK) | Resolved via UserService |
| ORGANIZATION | organizations.uuid | Soft (no FK) | Resolved via OrgService |
| TEAM | review_teams.uuid | Soft (no FK) | Q4A — ทีมตรวจสอบ RFA |
| ROLE | roles.uuid | Soft (no FK) | Q4B — ต้องใช้หลัง Apply delta-11 |
**Business Rules**:
- **ไม่มี FK constraint** บน recipient_public_id โดยออกแบบ (Polymorphic Pattern)
- Service Layer ต้องตรวจสอบ existence ก่อน save
- ใช้ index `idx_dr_type_recipient (recipient_type, recipient_public_id)` สำหรับ lookup performance
---
+36 -4
View File
@@ -1,8 +1,8 @@
# Database Indexing & Performance Strategy
**Version:** 1.0.0
**Version:** 1.1.0 (v1.9.0 Core Schema)
**Context:** Production-scale (100k+ documents, High Concurrency)
**Database:** MySQL 8.x (On-Premise via Docker)
**Database:** MariaDB 11.x (Native UUID Support)
## 1. Core Principles (หลักการสำคัญ)
@@ -64,13 +64,45 @@ ADD INDEX `idx_doc_status_is_current` (`is_current`, `status`, `project_id`);
ALTER TABLE `documents`
ADD INDEX `idx_project_updated` (`project_id`, `updated_at` DESC);
-- สำหรับ Inbox / Pending Tasks ของ User
-- สำหรับ Inbox / Pending Tasks ของ User (General Workflow)
ALTER TABLE `workflow_instances`
ADD INDEX `idx_assignee_status` (`assignee_id`, `status`, `created_at` DESC);
-- สำหรับ Optimistic Locking (ADR-001 v1.1)
ALTER TABLE `workflow_instances`
ADD INDEX `idx_wf_inst_version` (`id`, `version_no`);
```
### 3.2 Full-Text Search (ทางเลือกเบื้องต้นก่อนใช้ Elasticsearch)
### 3.2 RFA Parallel Review & Delegation (v1.9.0)
ในระบบ RFA ใหม่ มีการใช้ Parallel Review และการมอบหมายงาน (Delegation) ซึ่งต้องการ Index เฉพาะทาง:
```sql
-- สำหรับดึงงานที่รอตรวจแยกตาม Discipline และสถานะ
ALTER TABLE `review_tasks`
ADD INDEX `idx_review_tasks_lookup` (`assigned_to_user_id`, `status`, `discipline_id`);
-- สำหรับการตรวจสอบสิทธิ์การตรวจรายคน (Priority Order)
ALTER TABLE `review_team_members`
ADD INDEX `idx_rtm_lookup` (`team_id`, `user_id`, `is_active`);
-- สำหรับเช็คการมอบหมายงานที่ยังมีผลอยู่ (Managed by BullMQ)
ALTER TABLE `delegations`
ADD INDEX `idx_delegations_active_lookup` (`delegator_user_id`, `is_active`, `start_date`, `end_date`);
```
### 3.3 Polymorphic Distribution Matrix
เนื่องจาก `distribution_recipients` เก็บ `recipient_public_id` แบบ UUID (ADR-019) และไม่มี Hard FK:
```sql
-- สำหรับ Lookup ผู้รับเอกสารตามประเภท
ALTER TABLE `distribution_recipients`
ADD INDEX `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`);
```
### 3.4 Full-Text Search (ทางเลือกเบื้องต้นก่อนใช้ Elasticsearch)
หากผู้ใช้ต้องการค้นหาจากชื่อเอกสาร (`title`) หรือเนื้อหาบางส่วน
@@ -3,7 +3,7 @@
---
title: 'Data & Storage: File Storage and Handling (Two-Phase)'
version: 1.8.0
version: 1.9.0
status: drafted
owner: Nattanin Peancharoen
last_updated: 2026-02-22
@@ -80,23 +80,30 @@ LCBP3-DMS ต้องจัดการ File Uploads สำหรับ Attachm
```sql
CREATE TABLE attachments (
id INT PRIMARY KEY AUTO_INCREMENT,
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
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
reference_date DATE NULL COMMENT 'ADR-019/ADR-003: folder partition date',
-- Two-Phase Fields
is_temporary BOOLEAN DEFAULT TRUE,
temp_id VARCHAR(100) NULL, -- UUID for temp reference
expires_at DATETIME NULL, -- Temp file expiration
-- ADR-021: Integrated Workflow Context
workflow_history_id CHAR(36) NULL COMMENT 'FK to workflow_histories.id for step-specific attachments',
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)
FOREIGN KEY (workflow_history_id) REFERENCES workflow_histories(id) ON DELETE SET NULL,
INDEX idx_temp_files (is_temporary, expires_at),
INDEX idx_attachments_reference_date (reference_date)
);
```
@@ -2,7 +2,7 @@
| description | version |
| ------------------------------------------------------------------ | ------- |
| legacy PDF document migration to system v1.8.0 uses n8n and Ollama | 1.8.0 |
| legacy PDF document migration to system v1.9.0 uses n8n and Ollama | 1.9.0 |
> **Note:** Category Enum system-driven, Idempotency Contract, Duplicate Handling Clarification, Storage Enforcement, Audit Log Enhancement, Review Queue Integration, Revision Drift Protection, Execution Time, Encoding Normalization, Security Hardening, Orchestrator on QNAP, AI Physical Isolation (Desktop Desk-5439), Folder Standard (/share/np-dms/n8n), **AI Tag Extraction & Auto-Tagging**
@@ -0,0 +1,25 @@
-- =============================================================================
-- Delta 11: เพิ่ม uuid column ให้ roles table
-- =============================================================================
-- เหตุผล: roles ไม่มี uuid column ทำให้ distribution_recipients ไม่สามารถ
-- อ้างอิง ROLE type ด้วย publicId ได้ตาม ADR-019
-- ใช้ใน: distribution_recipients.recipient_public_id (recipient_type = 'ROLE')
-- ADR: ADR-019 (Hybrid Identifier Strategy)
-- Feature: v1.9.0 RFA Approval Refactor (1-rfa-approval-refactor)
-- Created: 2026-05-13
-- =============================================================================
ALTER TABLE `roles`
ADD COLUMN `uuid` UUID NOT NULL DEFAULT (UUID())
COMMENT 'UUID Public Identifier (ADR-019) — ใช้ใน API Response แทน role_id'
AFTER `role_id`;
-- สร้าง Unique Index สำหรับ uuid
CREATE UNIQUE INDEX `idx_roles_uuid` ON `roles` (`uuid`);
-- =============================================================================
-- หมายเหตุ: หลัง Apply delta นี้แล้ว
-- - roles ที่มีอยู่แล้วจะได้ uuid อัตโนมัติจาก DEFAULT (UUID())
-- - distribution_recipients.recipient_public_id (type=ROLE) ให้ใช้ roles.uuid
-- - TypeORM Entity: เพิ่ม @Column({ unique: true }) uuid: string; ใน RoleEntity
-- =============================================================================
@@ -1,6 +1,6 @@
-- ==========================================================
-- DMS v1.8.0 Migration Support Tables
-- ไฟล์นี้แยกจาก schema หลัก (lcbp3-v1.8.0-schema-01/02/03)
-- DMS v1.9.0 Migration Support Tables
-- ไฟล์นี้แยกจาก schema หลัก (lcbp3-v1.9.0-schema-01/02/03)
-- ใช้สำหรับ n8n Migration Workflow เท่านั้น
-- ลบได้ทั้งหมดหลัง Migration เสร็จสิ้น
-- ==========================================================
@@ -1,8 +1,8 @@
-- =============================================================================
-- LCBP3-DMS v1.9.0 — RFA Approval System Refactor Schema
-- Feature Branch: 1-rfa-approval-refactor
-- Feature Branch: 204-rfa-approval-refactor
-- ADR-009: No TypeORM migrations — edit SQL schema directly
-- Created: 2026-05-12
-- Created: 2026-05-13
-- =============================================================================
-- -----------------------------------------------------------------------------
@@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS `review_team_members` (
KEY `idx_rtm_team` (`team_id`),
KEY `idx_rtm_user` (`user_id`),
CONSTRAINT `fk_rtm_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE,
CONSTRAINT `fk_rtm_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS `review_tasks` (
CONSTRAINT `fk_rt_rfa_revision` FOREIGN KEY (`rfa_revision_id`) REFERENCES `rfa_revisions` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_rt_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`),
CONSTRAINT `fk_rt_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`),
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`user_id`) ON DELETE SET NULL,
CONSTRAINT `fk_rt_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -128,44 +128,44 @@ CREATE TABLE IF NOT EXISTS `review_tasks` (
-- 6. delegations — การมอบหมายงาน
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `delegations` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`delegator_id` INT NOT NULL COMMENT 'ผู้มอบหมาย',
`delegatee_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย',
`start_date` DATE NOT NULL,
`end_date` DATE NULL,
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`reason` TEXT NULL,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`delegator_user_id` INT NOT NULL COMMENT 'ผู้มอบหมาย (FK → users.user_id)',
`delegate_user_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย (FK → users.user_id)',
`start_date` DATE NOT NULL,
`end_date` DATE NULL COMMENT 'BullMQ job flips is_active=0 when end_date < NOW() (ADR-008)',
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
`is_active` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Managed by BullMQ scheduler — do not flip manually',
`reason` TEXT NULL,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_delegations_uuid` (`uuid`),
KEY `idx_delegations_active` (`delegator_id`, `is_active`, `start_date`, `end_date`),
KEY `idx_delegations_delegatee` (`delegatee_id`, `is_active`),
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_id`) REFERENCES `users` (`id`),
CONSTRAINT `fk_del_delegatee` FOREIGN KEY (`delegatee_id`) REFERENCES `users` (`id`)
KEY `idx_delegations_active` (`delegator_user_id`, `is_active`, `start_date`, `end_date`),
KEY `idx_delegations_delegate` (`delegate_user_id`, `is_active`),
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_user_id`) REFERENCES `users` (`user_id`),
CONSTRAINT `fk_del_delegate` FOREIGN KEY (`delegate_user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 7. reminder_rules — กฎการแจ้งเตือน
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `reminder_rules` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`name` VARCHAR(100) NOT NULL,
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NULL COMMENT 'NULL = all types',
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NULL COMMENT 'NULL = all types',
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
`reminder_type` ENUM('DUE_SOON','ON_DUE','OVERDUE','ESCALATION_L1','ESCALATION_L2') NOT NULL,
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
`message_template_th` TEXT NOT NULL,
`message_template_en` TEXT NOT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
`message_template_th` TEXT NOT NULL,
`message_template_en` TEXT NOT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_reminder_rules_uuid` (`uuid`),
KEY `idx_reminder_rules_active` (`is_active`, `project_id`)
@@ -175,15 +175,15 @@ CREATE TABLE IF NOT EXISTS `reminder_rules` (
-- 8. distribution_matrices — ตารางกระจายเอกสาร
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `distribution_matrices` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`name` VARCHAR(100) NOT NULL,
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NOT NULL,
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NOT NULL,
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_distribution_matrices_uuid` (`uuid`),
KEY `idx_distribution_lookup` (`document_type_id`, `response_code_id`, `is_active`),
@@ -195,22 +195,17 @@ CREATE TABLE IF NOT EXISTS `distribution_matrices` (
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `distribution_recipients` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()) COMMENT 'UUID Public Identifier (ADR-019)',
`matrix_id` INT NOT NULL,
`recipient_type` ENUM('USER','ORGANIZATION','TEAM','ROLE') NOT NULL,
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of user/org/team',
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of target: USER=users.uuid | ORGANIZATION=organizations.uuid | TEAM=review_teams.uuid | ROLE=roles.uuid',
`delivery_method` ENUM('EMAIL','IN_APP','BOTH') NOT NULL DEFAULT 'BOTH',
`sequence` INT NULL COMMENT 'For ordered delivery',
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_distribution_recipients_uuid` (`uuid`),
KEY `idx_dr_matrix` (`matrix_id`),
KEY `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`),
CONSTRAINT `fk_dr_matrix` FOREIGN KEY (`matrix_id`) REFERENCES `distribution_matrices` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- =============================================================================
-- Additional Indexes (Performance)
-- =============================================================================
-- (all created inline above with KEY statements)
-- =============================================================================
-- END OF SCHEMA v1.9.0
-- =============================================================================
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Polymorphic recipients — no FK on recipient_public_id by design.';
@@ -1,9 +1,9 @@
-- ==========================================================
-- DMS v1.8.0 Schema Part 1/3: DROP Statements
-- DMS v1.9.0 Schema Part 1/3: DROP Statements
-- รันไฟล์นี้ก่อน เพื่อล้างตารางเดิมทั้งหมด
-- ==========================================================
-- ==========================================================
-- DMS v1.8.0 Document Management System Database
-- DMS v1.9.0 Document Management System Database
-- Deploy Script Schema
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 11.8
@@ -14,7 +14,7 @@
-- reverse proxy: jc21/nginx-proxy-manager:latest
-- cron service: n8n
-- ==========================================================
-- [v1.8.0 UPDATE] Prepare migration
-- [v1.9.0 UPDATE] Prepare migration
-- Update: Upgraded from v1.7.0
-- Last Updated: 2026-02-27
-- Major Changes:
@@ -1,5 +1,5 @@
-- ==========================================================
-- DMS v1.8.0 Schema Part 2/3: CREATE TABLE Statements
-- DMS v1.9.0 Schema Part 2/3: CREATE TABLE Statements
-- รัน: mysql < 01-schema-drop.sql แล้วจึงรัน 02-schema-tables.sql
-- ==========================================================
SET NAMES utf8mb4;
@@ -113,6 +113,7 @@ CREATE TABLE refresh_tokens (
-- ตาราง Master เก็บ "บทบาท" ของผู้ใช้ในระบบ
CREATE TABLE roles (
role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID ของตาราง',
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
-- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'รหัสบทบาท (เช่น SUPER_ADMIN, ADMIN, EDITOR, VIEWER)',
role_name VARCHAR(100) NOT NULL COMMENT 'ชื่อบทบาท',
scope ENUM(
@@ -1464,3 +1465,216 @@ CREATE TABLE ai_audit_logs (
INDEX idx_ai_audit_model (ai_model),
INDEX idx_ai_audit_status (STATUS)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง Audit Log การทำงาน AI ทุกครั้ง (ADR-018 Rule 5 Audit Logging)';
-- =============================================================================
-- 20. RFA Approval System (v1.9.0)
-- =============================================================================
-- -----------------------------------------------------------------------------
-- 20.1 review_teams — ทีมตรวจสอบแยกตาม Discipline
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `review_teams` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`project_id` INT NOT NULL,
`name` VARCHAR(100) NOT NULL,
`description` VARCHAR(255) NULL,
`default_for_rfa_types` TEXT NULL COMMENT 'Comma-separated RFA type codes e.g. SDW,DDW',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_review_teams_uuid` (`uuid`),
KEY `idx_review_teams_project` (`project_id`, `is_active`),
CONSTRAINT `fk_review_teams_project` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.2 review_team_members — สมาชิกในทีมแยกตาม Discipline
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `review_team_members` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`team_id` INT NOT NULL,
`user_id` INT NOT NULL,
`discipline_id` INT NOT NULL,
`role` ENUM('REVIEWER','LEAD','MANAGER') NOT NULL DEFAULT 'REVIEWER',
`priority_order` INT NOT NULL DEFAULT 0,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_review_team_members_uuid` (`uuid`),
UNIQUE KEY `uq_team_user_discipline` (`team_id`, `user_id`, `discipline_id`),
KEY `idx_rtm_team` (`team_id`),
KEY `idx_rtm_user` (`user_id`),
CONSTRAINT `fk_rtm_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE,
CONSTRAINT `fk_rtm_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.3 response_codes — รหัสตอบกลับมาตรฐาน (Master Approval Matrix)
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `response_codes` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`code` VARCHAR(10) NOT NULL COMMENT '1A, 1B, 1C, 1D, 1E, 1F, 1G, 2, 3, 4',
`sub_status` VARCHAR(10) NULL,
`category` ENUM('ENGINEERING','MATERIAL','CONTRACT','TESTING','ESG') NOT NULL,
`description_th` TEXT NOT NULL,
`description_en` TEXT NOT NULL,
`implications` JSON NULL COMMENT '{"affectsSchedule":bool,"affectsCost":bool,"requiresContractReview":bool}',
`notify_roles` TEXT NULL COMMENT 'Comma-separated roles e.g. CONTRACT_MANAGER,QS_MANAGER',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`is_system` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'System default — cannot delete',
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_response_codes_uuid` (`uuid`),
UNIQUE KEY `uq_response_code_category` (`code`, `category`),
KEY `idx_rc_category_active` (`category`, `is_active`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.4 response_code_rules — กฎการใช้รหัสต่อโครงการ/ประเภทเอกสาร
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `response_code_rules` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`project_id` INT NULL COMMENT 'NULL = global default',
`document_type_id` INT NOT NULL,
`response_code_id` INT NOT NULL,
`is_enabled` TINYINT(1) NOT NULL DEFAULT 1,
`requires_comments` TINYINT(1) NOT NULL DEFAULT 0,
`triggers_notification` TINYINT(1) NOT NULL DEFAULT 0,
`parent_rule_id` INT NULL COMMENT 'For inheritance tracking',
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_response_code_rules_uuid` (`uuid`),
UNIQUE KEY `uq_rule_per_project_doctype_code` (`project_id`, `document_type_id`, `response_code_id`),
KEY `idx_response_rules_lookup` (`project_id`, `document_type_id`, `is_enabled`),
CONSTRAINT `fk_rcr_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`),
CONSTRAINT `fk_rcr_parent` FOREIGN KEY (`parent_rule_id`) REFERENCES `response_code_rules` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.5 review_tasks — งานตรวจสอบสำหรับแต่ละ Discipline (Parallel Review)
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `review_tasks` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`rfa_revision_id` INT NOT NULL,
`team_id` INT NOT NULL,
`discipline_id` INT NOT NULL,
`assigned_to_user_id` INT NULL COMMENT 'NULL = auto-assign by discipline',
`status` ENUM('PENDING','IN_PROGRESS','COMPLETED','DELEGATED','EXPIRED','CANCELLED') NOT NULL DEFAULT 'PENDING',
`due_date` DATE NULL,
`response_code_id` INT NULL,
`comments` TEXT NULL,
`attachments` JSON NULL COMMENT 'Array of attachment publicIds',
`delegated_from_user_id` INT NULL COMMENT 'Original assignee when delegated',
`completed_at` TIMESTAMP NULL,
`version` INT NOT NULL DEFAULT 1 COMMENT 'Optimistic locking (ADR-002)',
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_review_tasks_uuid` (`uuid`),
UNIQUE KEY `uq_review_task_per_revision_discipline` (`rfa_revision_id`, `team_id`, `discipline_id`),
KEY `idx_review_tasks_rfa_revision` (`rfa_revision_id`),
KEY `idx_review_tasks_status` (`status`),
KEY `idx_review_tasks_assigned` (`assigned_to_user_id`, `status`),
CONSTRAINT `fk_rt_rfa_revision` FOREIGN KEY (`rfa_revision_id`) REFERENCES `rfa_revisions` (`id`) ON DELETE CASCADE,
CONSTRAINT `fk_rt_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`),
CONSTRAINT `fk_rt_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`),
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`user_id`) ON DELETE SET NULL,
CONSTRAINT `fk_rt_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.6 delegations — การมอบหมายงาน
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `delegations` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`delegator_user_id` INT NOT NULL COMMENT 'ผู้มอบหมาย (FK → users.user_id)',
`delegate_user_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย (FK → users.user_id)',
`start_date` DATE NOT NULL,
`end_date` DATE NULL COMMENT 'BullMQ job flips is_active=0 when end_date < NOW() (ADR-008)',
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
`is_active` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Managed by BullMQ scheduler — do not flip manually',
`reason` TEXT NULL,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_delegations_uuid` (`uuid`),
KEY `idx_delegations_active` (`delegator_user_id`, `is_active`, `start_date`, `end_date`),
KEY `idx_delegations_delegate` (`delegate_user_id`, `is_active`),
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_user_id`) REFERENCES `users` (`user_id`),
CONSTRAINT `fk_del_delegate` FOREIGN KEY (`delegate_user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.7 reminder_rules — กฎการแจ้งเตือน
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `reminder_rules` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`name` VARCHAR(100) NOT NULL,
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NULL COMMENT 'NULL = all types',
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
`reminder_type` ENUM('DUE_SOON','ON_DUE','OVERDUE','ESCALATION_L1','ESCALATION_L2') NOT NULL,
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
`message_template_th` TEXT NOT NULL,
`message_template_en` TEXT NOT NULL,
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_reminder_rules_uuid` (`uuid`),
KEY `idx_reminder_rules_active` (`is_active`, `project_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.8 distribution_matrices — ตารางกระจายเอกสาร
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `distribution_matrices` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()),
`name` VARCHAR(100) NOT NULL,
`project_id` INT NULL COMMENT 'NULL = global',
`document_type_id` INT NOT NULL,
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_distribution_matrices_uuid` (`uuid`),
KEY `idx_distribution_lookup` (`document_type_id`, `response_code_id`, `is_active`),
CONSTRAINT `fk_dm_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.9 distribution_recipients — ผู้รับเอกสารใน Distribution Matrix
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `distribution_recipients` (
`id` INT NOT NULL AUTO_INCREMENT,
`uuid` UUID NOT NULL DEFAULT (UUID()) COMMENT 'UUID Public Identifier (ADR-019)',
`matrix_id` INT NOT NULL,
`recipient_type` ENUM('USER','ORGANIZATION','TEAM','ROLE') NOT NULL,
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of target: USER=users.uuid | ORGANIZATION=organizations.uuid | TEAM=review_teams.uuid | ROLE=roles.uuid',
`delivery_method` ENUM('EMAIL','IN_APP','BOTH') NOT NULL DEFAULT 'BOTH',
`sequence` INT NULL COMMENT 'For ordered delivery',
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_distribution_recipients_uuid` (`uuid`),
KEY `idx_dr_matrix` (`matrix_id`),
KEY `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`),
CONSTRAINT `fk_dr_matrix` FOREIGN KEY (`matrix_id`) REFERENCES `distribution_matrices` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
COMMENT='Polymorphic recipients — no FK on recipient_public_id (by design). ROLE type uses roles.uuid (ADR-019)';
-- =============================================================================
-- END OF SCHEMA v1.9.0
-- =============================================================================
@@ -1,5 +1,5 @@
-- ==========================================================
-- DMS v1.8.0 Schema Part 3/3: Views, Indexes, Partitioning
-- DMS v1.9.0 Schema Part 3/3: Views, Indexes, Partitioning
-- รัน: หลังจาก 02-schema-tables.sql เสร็จ
-- ==========================================================
SET NAMES utf8mb4;
@@ -1,5 +1,5 @@
-- ==========================================================
-- DMS v1.8.0 Document Management System Database
-- DMS v1.9.0 Document Management System Database
-- Deploy Script Schema
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 11.8
@@ -10,7 +10,7 @@
-- reverse proxy: jc21/nginx-proxy-manager:latest
-- cron service: n8n
-- ==========================================================
-- [v1.8.0 UPDATE] Prepare migration
-- [v1.9.0 UPDATE] Prepare migration
-- Update: Upgraded from v1.7.0
-- Last Updated: 2026-02-27
-- Major Changes:
@@ -1,5 +1,5 @@
-- ==========================================================
-- DMS v1.8.0 Document Management System Database
-- DMS v1.9.0 Document Management System Database
-- Seed Contract Drawing data
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 11.8
@@ -1,9 +1,9 @@
-- ==========================================================
-- DMS v1.8.0 - Permissions Seed Data (REORGANIZED)
-- File: specs/07-database/lcbp3-v1.8.0-seed-permissions.sql
-- DMS v1.9.0 - Permissions Seed Data (REORGANIZED)
-- File: specs/07-database/lcbp3-v1.9.0-seed-permissions.sql
-- Total Permissions: 85 (Reorganized with systematic ID allocation)
-- Created: 2025-12-13
-- Updated: 2026-02-28 (v1.8.0 merge)
-- Updated: 2026-02-28 (v1.9.0 merge)
-- ==========================================================
-- Clear existing data
TRUNCATE TABLE role_permissions;
@@ -1069,7 +1069,7 @@ VALUES -- Contract Management
-- ==========================================================
-- MERGED FROM fix-project-permissions.sql (v1.8.0 Update)
-- MERGED FROM fix-project-permissions.sql (v1.9.0 Update)
-- ==========================================================
-- Fix Project Permissions
-- File: specs/07-database/fix-project-permissions.sql
@@ -1099,4 +1099,65 @@ INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (6, 202);
-- 5. Grant project.view to Viewer (Role 5)
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (5, 202);
VALUES (5, 202);
-- ==========================================================
-- 18. AI Module Permissions (ID 181-190) — ADR-023 v1.1
-- Added: 2026-05-14 (Grilling Session)
-- ==========================================================
INSERT INTO permissions (
permission_id,
permission_name,
description,
module,
is_active
)
VALUES (
181,
'ai.suggest',
'รับ AI Suggestion เมื่อสร้าง/แก้ไขเอกสาร',
'ai',
1
),
(
182,
'ai.rag_query',
'ใช้ RAG Q&A สืบค้นเอกสาร',
'ai',
1
),
(
183,
'ai.migration_manage',
'จัดการ Migration Batch (Review/Import/Reject)',
'ai',
1
),
(
184,
'ai.audit_log_delete',
'Hard Delete ai_audit_logs (Superadmin Only)',
'ai',
1
);
-- Role 1: Superadmin — ได้รับทุก permission โดยอัตโนมัติผ่าน SELECT-all pattern (บรรทัด 825-829)
-- Role 2: Org Admin — ai.suggest, ai.rag_query, ai.migration_manage
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (2, 181),
-- ai.suggest
(2, 182),
-- ai.rag_query
(2, 183);
-- ai.migration_manage
-- Role 3: Document Control — ai.suggest, ai.rag_query, ai.migration_manage
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (3, 181),
-- ai.suggest
(3, 182),
-- ai.rag_query
(3, 183);
-- ai.migration_manage
-- ai.audit_log_delete (184) — Superadmin เท่านั้น, ไม่ grant ให้ Role อื่น
@@ -1,5 +1,5 @@
-- ==========================================================
-- DMS v1.8.0 Document Management System Database
-- DMS v1.9.0 Document Management System Database
-- Seed Shop Drawing data
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 11.8