# **ตารางฐานข้อมูล (Data Dictionary) - LCBP3-DMS (V1.4.3)**

เอกสารนี้สรุปโครงสร้างตาราง, Foreign Keys (FK), และ Constraints ที่สำคัญทั้งหมดในฐานข้อมูล LCBP3-DMS (v1.4) เพื่อใช้เป็นเอกสารอ้างอิงสำหรับทีมพัฒนา Backend (NestJS) และ Frontend (Next.js) โดยอิงจาก Requirements และ SQL Script ล่าสุด [GEMINI]

**สถานะ:** FINAL GUIDELINE
**วันที่:** 2025-11-19
**อ้างอิง:** Requirements v1.4.2 & FullStackJS Guidelines v1.4.2
**Classification:** Internal Technical Documentation

---

## **1. 🏢 Core & Master Data Tables (องค์กร, โครงการ, สัญญา)**

### 1.1 organization_roles

**Purpose**: Master table for organization role types in the system

| Column Name | Data Type   | Constraints                 | Description                                                      |
| ----------- | ----------- | --------------------------- | ---------------------------------------------------------------- |
| id          | INT         | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for organization role                          |
| role_name   | VARCHAR(20) | NOT NULL, UNIQUE            | Role name (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY) |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (role_name)

**Business Rules**:

- Predefined system roles for organization types
- Cannot be deleted if referenced by organizations

---

### 1.2 organizations

**Purpose**: Master table storing all organizations involved in the system

| Column Name       | Data Type    | Constraints                         | Description                              |
| ----------------- | ------------ | ----------------------------------- | ---------------------------------------- |
| id                | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique identifier for organization       |
| organization_code | VARCHAR(20)  | NOT NULL, UNIQUE                    | Organization code (e.g., 'กทท.', 'TEAM') |
| organization_name | VARCHAR(255) | NOT NULL                            | Full organization name                   |
| is_active         | BOOLEAN      | DEFAULT TRUE                        | Active status (1=active, 0=inactive)     |
| created_at        | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                |
| updated_at        | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                    |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (organization_code)
- INDEX (is_active)

**Relationships**:

- Referenced by: users, project_organizations, contract_organizations, correspondences, circulations

**Seed Data**: Pre-populated with 15 organizations including:

- Port Authority of Thailand (กทท.)
- Project supervision consultants (สค©.3-xx)
- Design consultants (TEAM)
- Construction supervisors (คคง.)
- Contractors (ผรม.1-4)
- Third parties (EN, CAR)

---

### 1.3 projects

**Purpose**: Master table for all projects in the system

| Column Name  | Data Type    | Constraints                 | Description                   |
| ------------ | ------------ | --------------------------- | ----------------------------- |
| id           | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for project |
| project_code | VARCHAR(50)  | NOT NULL, UNIQUE            | Project code (e.g., 'LCBP3')  |
| project_name | VARCHAR(255) | NOT NULL                    | Full project name             |
| is_active    | TINYINT(1)   | DEFAULT 1                   | Active status                 |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (project_code)
- INDEX (is_active)

**Relationships**:

- Referenced by: contracts, correspondences, document_number_formats, drawings

**Seed Data**: 5 projects for Laem Chabang Port Phase 3 (LCBP3) including main project and 4 sub-contracts

---

### 1.4 contracts

**Purpose**: Master table for contracts within projects

| Column Name   | Data Type    | Constraints                         | Description                    |
| ------------- | ------------ | ----------------------------------- | ------------------------------ |
| id            | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique identifier for contract |
| project_id    | INT          | NOT NULL, FK                        | Reference to projects table    |
| contract_code | VARCHAR(50)  | NOT NULL, UNIQUE                    | Contract code                  |
| contract_name | VARCHAR(255) | NOT NULL                            | Full contract name             |
| description   | TEXT         | NULL                                | Contract description           |
| start_date    | DATE         | NULL                                | Contract start date            |
| end_date      | DATE         | NULL                                | Contract end date              |
| is_active     | BOOLEAN      | DEFAULT TRUE                        | Active status                  |
| created_at    | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp      |
| updated_at    | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp          |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (contract_code)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- INDEX (project_id, is_active)

**Relationships**:

- Parent: projects
- Referenced by: contract_organizations, user_assignments

**Seed Data**: 7 contracts including design, supervision, construction, and environmental monitoring

---

## **2. 👥 Users & RBAC Tables (ผู้ใช้, สิทธิ์, บทบาท)**

### 2.1 users

**Purpose**: Master table storing all system users

| Column Name             | Data Type    | Constraints                         | Description                      |
| ----------------------- | ------------ | ----------------------------------- | -------------------------------- |
| user_id                 | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique identifier for user       |
| username                | VARCHAR(50)  | NOT NULL, UNIQUE                    | Login username                   |
| password_hash           | VARCHAR(255) | NOT NULL                            | Hashed password (bcrypt)         |
| first_name              | VARCHAR(50)  | NULL                                | User's first name                |
| last_name               | VARCHAR(50)  | NULL                                | User's last name                 |
| email                   | VARCHAR(100) | NOT NULL, UNIQUE                    | Email address                    |
| line_id                 | VARCHAR(100) | NULL                                | LINE messenger ID                |
| primary_organization_id | INT          | NULL, FK                            | Primary organization affiliation |
| is_active               | TINYINT(1)   | DEFAULT 1                           | Active status                    |
| failed_attempts         | INT          | DEFAULT 0                           | Failed login attempts counter    |
| locked_until            | DATETIME     | NULL                                | Account lock expiration time     |
| last_login_at           | TIMESTAMP    | NULL                                | Last successful login timestamp  |
| created_at              | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp        |
| updated_at              | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp            |
| 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

**Security Features**:

- Password stored as bcrypt hash
- Account locking after failed attempts
- Last login tracking

**Seed Data**: 3 initial users (superadmin, editor01, viewer01)

---

### 2.2 roles

**Purpose**: Master table defining system roles with scope levels

| Column Name | Data Type    | Constraints                 | Description                                          |
| ----------- | ------------ | --------------------------- | ---------------------------------------------------- |
| role_id     | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for role                           |
| role_name   | VARCHAR(100) | NOT NULL                    | Role name (e.g., 'Superadmin', 'Document Control')   |
| scope       | ENUM         | NOT NULL                    | Scope level: Global, Organization, Project, Contract |
| description | TEXT         | NULL                        | Role description                                     |
| is_system   | BOOLEAN      | DEFAULT FALSE               | System role flag (cannot be deleted)                 |

**Indexes**:

- PRIMARY KEY (role_id)
- INDEX (scope)

**Relationships**:

- Referenced by: role_permissions, user_assignments

**Seed Data**: 7 predefined roles

1. Superadmin (Global) - Full system access
2. Org Admin (Organization) - Organization management
3. Document Control (Organization) - Document lifecycle management
4. Editor (Organization) - Document creation and editing
5. Viewer (Organization) - Read-only access
6. Project Manager (Project) - Project-level management
7. Contract Admin (Contract) - Contract-specific administration

---

### 2.3 permissions

**Purpose**: Master table defining all system permissions

| Column Name     | Data Type    | Constraints                 | Description                                            |
| --------------- | ------------ | --------------------------- | ------------------------------------------------------ |
| permission_id   | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for permission                       |
| permission_name | VARCHAR(100) | NOT NULL, UNIQUE            | Permission code (e.g., 'rfas.create', 'document.view') |
| description     | TEXT         | NULL                        | Permission description                                 |
| module          | VARCHAR(50)  | NULL                        | Related module name                                    |
| scope_level     | ENUM         | NULL                        | Scope: GLOBAL, ORG, PROJECT                            |
| is_active       | TINYINT(1)   | DEFAULT 1                   | Active status                                          |

**Indexes**:

- PRIMARY KEY (permission_id)
- UNIQUE (permission_name)
- INDEX (module)
- INDEX (scope_level)
- INDEX (is_active)

**Relationships**:

- Referenced by: role_permissions

**Permission Categories**:

1. **System Management** (1): system.manage_all
2. **Organization Management** (2-5): create, edit, delete, view
3. **Project Management** (6-9, 23-26): create, edit, delete, view, manage members/contracts/reports
4. **Role & Permission Management** (10-13): create, edit, delete roles, assign permissions
5. **Master Data Management** (14-17): document types, statuses, categories, tags
6. **User Management** (18-22): create, edit, delete, view, assign organization
7. **Contract Management** (27-28): manage members, view
8. **Document Management** (29-44): CRUD operations, workflows, circulation
9. **Search & Reporting** (48-49): advanced search, generate reports

**Total Permissions**: 49

---

### 2.4 role_permissions

**Purpose**: Junction table mapping roles to permissions (M:N)

| Column Name   | Data Type | Constraints     | Description                    |
| ------------- | --------- | --------------- | ------------------------------ |
| role_id       | INT       | PRIMARY KEY, FK | Reference to roles table       |
| permission_id | INT       | PRIMARY KEY, FK | Reference to permissions table |

**Indexes**:

- PRIMARY KEY (role_id, permission_id)
- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE
- FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE
- INDEX (permission_id)

**Relationships**:

- Parent: roles, permissions

**Seed Data**: Complete permission mappings for all 7 roles

- Superadmin: All 49 permissions
- Org Admin: 15 permissions (user/org/tag management, view access)
- Document Control: 26 permissions (full document lifecycle)
- Editor: 12 permissions (document CRUD without admin powers)
- Viewer: 2 permissions (view and search only)
- Project Manager: 23 permissions (project management + document CRUD)
- Contract Admin: 15 permissions (contract management + document CRUD)

---

### 2.5 user_assignments

**Purpose**: Junction table assigning users to roles with scope context

| Column Name         | Data Type | Constraints                 | Description                        |
| ------------------- | --------- | --------------------------- | ---------------------------------- |
| id                  | INT       | PRIMARY KEY, AUTO_INCREMENT | Unique identifier                  |
| user_id             | INT       | NOT NULL, FK                | Reference to users table           |
| role_id             | INT       | NOT NULL, FK                | Reference to roles table           |
| organization_id     | INT       | NULL, FK                    | Organization scope (if applicable) |
| project_id          | INT       | NULL, FK                    | Project scope (if applicable)      |
| contract_id         | INT       | NULL, FK                    | Contract scope (if applicable)     |
| assigned_by_user_id | INT       | NULL, FK                    | User who made the assignment       |
| assigned_at         | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP   | Assignment timestamp               |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
- FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id)
- INDEX (user_id, role_id)
- INDEX (organization_id)
- INDEX (project_id)
- INDEX (contract_id)

**Constraints**:

- CHECK: Only one scope field (organization_id, project_id, contract_id) can be NOT NULL, or all NULL for Global scope

**Relationships**:

- Parent: users, roles, organizations, projects, contracts

**Business Rules**:

- User can have multiple role assignments with different scopes
- Scope inheritance: Contract → Project → Organization → Global
- Global scope: all scope fields are NULL

---

### 2.6 project_organizations

**Purpose**: Junction table linking projects to participating organizations (M:N)

| Column Name     | Data Type | Constraints     | Description                      |
| --------------- | --------- | --------------- | -------------------------------- |
| project_id      | INT       | PRIMARY KEY, FK | Reference to projects table      |
| organization_id | INT       | PRIMARY KEY, FK | Reference to organizations table |

**Indexes**:

- PRIMARY KEY (project_id, organization_id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
- INDEX (organization_id)

**Relationships**:

- Parent: projects, organizations

**Seed Data**: Pre-populated with project-organization relationships

---

### 2.7 contract_organizations

**Purpose**: Junction table linking contracts to participating organizations with roles (M:N)

| Column Name      | Data Type    | Constraints     | Description                                                               |
| ---------------- | ------------ | --------------- | ------------------------------------------------------------------------- |
| contract_id      | INT          | PRIMARY KEY, FK | Reference to contracts table                                              |
| organization_id  | INT          | PRIMARY KEY, FK | Reference to organizations table                                          |
| role_in_contract | VARCHAR(100) | NULL            | Organization's role in contract (Owner, Designer, Consultant, Contractor) |

**Indexes**:

- PRIMARY KEY (contract_id, organization_id)
- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
- INDEX (organization_id)
- INDEX (role_in_contract)

**Relationships**:

- Parent: contracts, organizations

**Seed Data**: Pre-populated with contract-organization-role relationships

---

## **3. ✉️ Correspondences Tables (เอกสารหลัก, Revisions, Workflows)**

### 3.1 correspondence_types

**Purpose**: Master table for correspondence document types

| Column Name | Data Type    | Constraints                 | Description                                   |
| ----------- | ------------ | --------------------------- | --------------------------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier                             |
| type_code   | VARCHAR(50)  | NOT NULL, UNIQUE            | Type code (e.g., 'RFA', 'RFI', 'TRANSMITTAL') |
| type_name   | VARCHAR(255) | NOT NULL                    | Full type name                                |
| sort_order  | INT          | DEFAULT 0                   | Display order                                 |
| is_active   | TINYINT(1)   | DEFAULT 1                   | Active status                                 |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (type_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: correspondences, document_number_formats, document_number_counters

**Seed Data**: 10 correspondence types including RFA, RFI, TRANSMITTAL, EMAIL, INSTRUCTION, LETTER, MEMO, MOM, NOTICE, OTHER

---

### 3.2 correspondence_status

**Purpose**: Master table for correspondence status codes

| Column Name | Data Type    | Constraints                 | Description                           |
| ----------- | ------------ | --------------------------- | ------------------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier                     |
| status_code | VARCHAR(50)  | NOT NULL, UNIQUE            | Status code (e.g., 'DRAFT', 'SUBOWN') |
| status_name | VARCHAR(255) | NOT NULL                    | Full status name                      |
| sort_order  | INT          | DEFAULT 0                   | Display order                         |
| is_active   | TINYINT(1)   | DEFAULT 1                   | Active status                         |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (status_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: correspondence_revisions

**Seed Data**: 23 status codes covering draft, submission, reply, resubmission, closure, and cancellation states by different parties (Owner, Designer, CSC, Contractor)

---

### 3.3 correspondences

**Purpose**: Master table for correspondence documents (non-revisioned data)

| Column Name               | Data Type    | Constraints                 | Description                                |
| ------------------------- | ------------ | --------------------------- | ------------------------------------------ |
| id                        | INT          | PRIMARY KEY, AUTO_INCREMENT | Master correspondence ID                   |
| correspondence_number     | VARCHAR(100) | NOT NULL                    | Document number (from numbering system)    |
| correspondence_type_id    | INT          | NOT NULL, FK                | Reference to correspondence_types          |
| is_internal_communication | TINYINT(1)   | DEFAULT 0                   | Internal (1) or external (0) communication |
| project_id                | INT          | NOT NULL, FK                | Reference to projects table                |
| originator_id             | INT          | NULL, FK                    | Originating organization                   |
| created_at                | DATETIME     | DEFAULT CURRENT_TIMESTAMP   | Record creation timestamp                  |
| created_by                | INT          | NULL, FK                    | User who created the record                |
| deleted_at                | DATETIME     | NULL                        | Soft delete timestamp                      |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- UNIQUE KEY (project_id, correspondence_number)
- INDEX (correspondence_type_id)
- INDEX (originator_id)
- INDEX (deleted_at)

**Relationships**:

- Parent: correspondence_types, projects, organizations, users
- Children: correspondence_revisions, correspondence_recipients, correspondence_tags, correspondence_references, correspondence_attachments, circulations, transmittals

**Business Rules**:

- One correspondence can have multiple revisions
- Correspondence number must be unique within a project
- Soft delete preserves history

---

### 3.4 correspondence_revisions

**Purpose**: Child table storing revision history of correspondences (1:N)

| Column Name              | Data Type    | Constraints                       | Description                                              |
| ------------------------ | ------------ | --------------------------------- | -------------------------------------------------------- |
| id                       | INT          | PRIMARY KEY, AUTO_INCREMENT       | Unique revision ID                                       |
| correspondence_id        | INT          | NOT NULL, FK                      | Master correspondence ID                                 |
| revision_number          | INT          | NOT NULL                          | Revision sequence (0, 1, 2...)                           |
| revision_label           | VARCHAR(10)  | NULL                              | Display revision (A, B, 1.1...)                          |
| is_current               | BOOLEAN      | DEFAULT FALSE                     | Current revision flag                                    |
| correspondence_status_id | INT          | NOT NULL, FK                      | Current status of this revision                          |
| title                    | VARCHAR(255) | NOT NULL                          | Document title                                           |
| document_date            | DATE         | NULL                              | Document date                                            |
| issued_date              | DATETIME     | NULL                              | Issue date                                               |
| received_date            | DATETIME     | NULL                              | Received date                                            |
| due_date                 | DATETIME     | NULL                              | Due date for response                                    |
| description              | TEXT         | NULL                              | Revision description                                     |
| details                  | JSON         | NULL                              | Type-specific details (e.g., RFI questions)              |
| created_at               | DATETIME     | DEFAULT CURRENT_TIMESTAMP         | Revision creation timestamp                              |
| created_by               | INT          | NULL, FK                          | User who created revision                                |
| updated_by               | INT          | NULL, FK                          | User who last updated                                    |
| v_ref_project_id         | INT          | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
| v_ref_type               | VARCHAR(50)  | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON 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) - Only one current revision per correspondence
- INDEX (correspondence_status_id)
- INDEX (is_current)
- INDEX (document_date)
- INDEX (issued_date)
- INDEX (v_ref_project_id)
- INDEX (v_ref_type)

**Relationships**:

- Parent: correspondences, correspondence_status, users

**Business Rules**:

- Only one revision can be marked as current (is_current=TRUE) per correspondence
- Revision numbers are sequential starting from 0
- JSON details field allows type-specific data storage

---

### 3.5 correspondence_recipients

**Purpose**: Junction table for correspondence recipients (TO/CC) (M:N)

| Column Name               | Data Type        | Constraints     | Description                  |
| ------------------------- | ---------------- | --------------- | ---------------------------- |
| correspondence_id         | INT              | PRIMARY KEY, FK | Reference to correspondences |
| recipient_organization_id | INT              | PRIMARY KEY, FK | Recipient organization       |
| recipient_type            | ENUM('TO', 'CC') | PRIMARY KEY     | Recipient type               |

**Indexes**:

- PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type)
- FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE
- FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
- INDEX (recipient_organization_id)
- INDEX (recipient_type)

**Relationships**:

- Parent: correspondences, organizations

**Business Rules**:

- One organization can be both TO and CC recipient
- Cascade delete when correspondence is deleted

---

### 3.6 tags

**Purpose**: Master table for document tagging system

| Column Name | Data Type    | Constraints                         | Description               |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique tag ID             |
| tag_name    | VARCHAR(100) | NOT NULL, UNIQUE                    | Tag name                  |
| description | TEXT         | NULL                                | Tag description           |
| created_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp |
| updated_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp     |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (tag_name)
- INDEX (tag_name) - For autocomplete

**Relationships**:

- Referenced by: correspondence_tags

---

### 3.7 correspondence_tags

**Purpose**: Junction table linking correspondences to tags (M:N)

| Column Name       | Data Type | Constraints     | Description                  |
| ----------------- | --------- | --------------- | ---------------------------- |
| correspondence_id | INT       | PRIMARY KEY, FK | Reference to correspondences |
| tag_id            | INT       | PRIMARY KEY, FK | Reference to tags            |

**Indexes**:

- PRIMARY KEY (correspondence_id, tag_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
- INDEX (tag_id)

**Relationships**:

- Parent: correspondences, tags

---

### 3.8 correspondence_references

**Purpose**: Junction table for cross-referencing correspondences (M:N)

| Column Name           | Data Type | Constraints     | Description                           |
| --------------------- | --------- | --------------- | ------------------------------------- |
| src_correspondence_id | INT       | PRIMARY KEY, FK | Source correspondence ID              |
| tgt_correspondence_id | INT       | PRIMARY KEY, FK | Target (referenced) correspondence ID |

**Indexes**:

- PRIMARY KEY (src_correspondence_id, tgt_correspondence_id)
- FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- INDEX (tgt_correspondence_id)

**Relationships**:

- Parent: correspondences (both sides)

**Business Rules**:

- Allows tracing document relationships
- Bi-directional references should be created as separate records

### 3.9 ตาราง correspondence_routing_templates

**Purpose**: เก็บข้อมูลแม่แบบ (Template) ของสายงานการส่งต่อเอกสารเพื่อขออนุมัติ ทำให้ไม่ต้องกำหนดขั้นตอนซ้ำทุกครั้ง สามารถสร้างเป็นแม่แบบทั่วไป หรือเฉพาะสำหรับโครงการใดโครงการหนึ่งได้

| Column Name     | Data Type    | Constraints                                                     | Description                                                                                |
| --------------- | ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
| id              | INT          | PRIMARY KEY, AUTO_INCREMENT                                     | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ                                                   |
| template_name   | VARCHAR(255) | NOT NULL                                                        | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ"                                                 |
| description     | TEXT         | NULL                                                            | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้                                                                |
| project_id      | INT          | NULL                                                            | ID ของโครงการที่แม่แบบนี้สังกัดอยู่ (ถ้ามี) **ค่า NULL หมายถึง** เป็น "แม่แบบทั่วไป" ที่สามารถใช้กับทุกโครงการได้ |
| created_at      | TIMESTAMP    | NOT NULL, DEFAULT CURRENT_TIMESTAMP                             | วันที่และเวลาที่สร้างแม่แบบนี้                                                                      |
| updated_at      | TIMESTAMP    | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด                                                            |
| is_active       | BOOLEAN      | DEFAULT TRUE                                                    | สถานะใช้งาน                                                                                 |
| workflow_config | JSON         | NULL                                                            | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ                       |

**Indexes**:

- ux_routing_template_name_project (template_name, project_id): ดัชนีแบบ UNIQUE ป้องกันการมีชื่อแม่แบบซ้ำกันในแต่ละโครงการ หรือในส่วนของแม่แบบทั่วไป (ที่ project_id เป็น NULL)

**ความสัมพันธ์ Foreign Key:**

- fk_crt_project: อ้างอิงไปยัง projects(id)
- ON DELETE CASCAD`: ถ้าโครงการ (projects) ถูกลบ แม่แบบที่เกี่ยวข้องกับโครงการนั้นๆ จะถูกลบไปด้วยทั้งหมด

---

### 3.10 ตาราง correspondence_routing_template_steps

**Purpose**: เก็บรายละเอียดของแต่ละขั้นตอน (Steps) ภายในแม่แบบสายงาน (correspondence_routing_templates) กำหนดว่าจะส่งไปที่องค์กรไหน ลำดับเป็นเท่าไร และเพื่อวัตถุประสงค์อะไร

| Column Name        | Data Type | Constraints                 | Description                                                                                                                  |
| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| id                 | INT       | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน                                                                                                               |
| template_id        | INT       | NOT NULL                    | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่                                                                                                     |
| sequence           | INT       | NOT NULL                    | ลำดับของขั้นตอน (1, 2, 3, ...)                                                                                                   |
| to_organization_id | INT       | NOT NULL                    | ID ขององค์กรที่เป็นผู้รับในขั้นตอนนี้                                                                                                   |
| step_purpose       | ENUM      | NOT NULL,DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้ **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ] |
| expected_days      | INT       | NULL                        | วันที่คาดหวัง                                                                                                                    |

**Indexes**:

- ux_cor_template_sequence (template_id, sequence): ดัชนีแบบ UNIQUE ป้องกันการมีลำดับขั้นตอนซ้ำกันภายในแม่แบบเดียวกัน

**ความสัมพันธ์ Foreign Key:**

- fk_cwts_template: อ้างอิงไปยัง correspondence_routing_templates(id)
- ON DELETE CASCADE: ถ้าแม่แบบถูกลบ ขั้นตอนทั้งหมดภายในแม่แบบนั้นจะถูกลบไปด้วย
- fk_cwts_org: อ้างอิงไปยัง organizations(id)
- ON DELETE CASCADE: ถ้าองค์กรถูกลบ ขั้นตอนที่ชี้ไปยังองค์กรนั้นจะถูกลบ

---

### 3.11 ตาราง correspondence_routings

**Purpose**: เป็นตารางที่เก็บข้อมูลการส่งต่อเอกสารจริง (Instance/Run-time) ติดตามประวัติการเคลื่อนย้ายของแต่ละเอกสาร ว่าผ่านใครมาบ้าง อยู่ที่ใคร และสถานะปัจจุบันคืออะไร

| Column Name          | Data Type | Constraints                         | Description                                                                                                                                              |
| -------------------- | --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id                   | INT       | PRIMARY KEY, AUTO_INCREMENT         | ID หลักของรายการส่งต่อ                                                                                                                                      |
| correspondence_id    | INT       | NOT NUL                             | ID ของเอกสาร (FK ไปยัง correspondence_revisions)                                                                                                          |
| template_id          | INT       | NULL                                | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง)                                                                                                            |
| sequence             | INT       | NOT NULL                            | ลำดับของขั้นตอนการส่งต่อจริง                                                                                                                                    |
| from_organization_id | INT       | NOT NULL                            | ID ขององค์กรผู้ส่ง                                                                                                                                           |
| to_organization_id   | INT       | NOT NULL                            | ID ขององค์กรผู้รับ                                                                                                                                           |
| step_purpose         | ENUM      | NOT NULL, DEFAULT FOR_REVIEW        | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้จริง  **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ, FOR_ACTION: เพื่อดำเนินการ] |
| status               | ENUM      | NOT NULL, DEFAULT SENT              | [ACTIONED: ดำเนินการแล้ว, FORWARDED: ส่งต่อแล้ว, REPLIE: ตอบกลับแล้ว]                                                                                            |
| comments             | TEXT      | NULL                                | หมายเหตุหรือความคิดเห็นในการส่งต่อ                                                                                                                             |
| due_date             | DATETIME  | NULL                                | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้                                                                                                                            |
| processed_by_user_id | INT       | NULL                                | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ                                                                                                                            |
| processed_at         | TIMESTAMP | NULL                                | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น                                                                                                                                    |
| created_at           | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้                                                                                                                                     |
| state_context        | JSON      | NULL                                | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot)                                                                                                          |

**Indexes**:

- ux_cor_routing_sequence (correspondence_id, sequence): ดัชนีแบบ UNIQUE ทำให้มั่นใจได้ว่าแต่ละเอกสารจะมีขั้นตอนการส่งต่อตามลำดับได้เพียงชุดเดียว

**ความสัมพันธ์ Foreign Key:**

- fk_crs_correspondence: อ้างอิงไปยัง correspondence_revisions(correspondence_id)
- ON DELETE CASCADE`: ถ้าเอกสาร (revision) ถูกลบ ประวัติการส่งต่อทั้งหมดจะถูกลบไปด้วย
- fk_crs_template: อ้างอิงไปยัง correspondence_routing_templates(id)
- ON DELETE SET NULL: ถ้าแม่แบบถูกลบ ค่า template_id ในตารางนี้จะถูกเปลี่ยนเป็น NULL เพื่อรักษาประวัติการส่งต่อไว้
- fk_crs_from_org: อ้างอิงไปยัง organizations(id)
- ON DELETE CASCADE: ถ้าองค์กรผู้ส่งถูกลบ รายการส่งต่อนี้จะถูกลบ
- fk_crs_to_org: อ้างอิงไปยัง organizations(id)
- ON DELETE CASCADE: ถ้าองค์กรผู้รับถูกลบ รายการส่งต่อนี้จะถูกลบ
- fk_crs_processed_by_user: อ้างอิงไปยัง users(user_id)
- ON DELETE SET NULL: ถ้าผู้ใช้ถูกลบ ค่า processed_by_user_id จะถูกเปลี่ยนเป็น NULL เพื่อรักษาประวัติการดำเนินการไว้

---

### 3.12 ตาราง correspondence_status_transitions

**Purpose**: ตารางนี้ใช้กำหนดกฎ (State Machine) ว่าสถานะใดสามารถเปลี่ยนไปเป็นสถานะใดได้บ้าง โดยขึ้นอยู่กับประเภทของหนังสือ เพื่อควบคุมการไหลของสถานะให้ถูกต้องตามข้อบังคับ

| Column Name    | Data Type | Constraints | Description                                     |
| -------------- | --------- | ----------- | ----------------------------------------------- |
| type_id        | INT       | PRIMARY KEY | ID ของประเภทหนังสือ (เช่น หนังสือภายใน, หนังสือภายนอก) |
| from_status_id | INT       | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง)                      |
| to_status_id   | INT       | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ)                 |

**คีย์หลัก (Primary Key):**

- (type_id, from_status_id, to_status_id)`: คีย์หลักแบบประกอบ ทำให้แน่ใจว่าแต่ละประเภทหนังสือ การเปลี่ยนจากสถานะหนึ่งไปอีกสถานะหนึ่งจะซ้ำกันไม่ได้

**ความสัมพันธ์ Foreign Key:**

- fk_cst_type : อ้างอิงไปยัง correspondence_types(id)
- fk_cst_from : อ้างอิงไปยัง correspondence_status(id)
- fk_cst_to : อ้างอิงไปยัง correspondence_status(id)
- ไม่มีการกำหนด ON DELETE จึงเป็นค่าเริ่มต้น RESTRICT หมายความว่า จะลบประเภทหนังสือหรือสถานะไม่ได้ ถ้ายังมีการใช้งานอยู่ในตารางนี้

---

## **4. 📐 approval: RFA Tables (เอกสารขออนุมัติ, Workflows)**

### 4.1 rfa_types

**Purpose**: Master table for RFA (Request for Approval) types

| Column Name | Data Type    | Constraints                 | Description                     |
| ----------- | ------------ | --------------------------- | ------------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier               |
| type_code   | VARCHAR(20)  | NOT NULL, UNIQUE            | Type code (DWG, DOC, MAT, etc.) |
| type_name   | VARCHAR(100) | NOT NULL                    | Full type name                  |
| description | TEXT         | NULL                        | Type description                |
| sort_order  | INT          | DEFAULT 0                   | Display order                   |
| is_active   | TINYINT(1)   | DEFAULT 1                   | Active status                   |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (type_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: rfas

**Seed Data**: 11 RFA types including Shop Drawing (DWG), Document (DOC), Specification (SPC), Calculation (CAL), Test Report (TRP), Survey Report (SRY), QA/QC Document, Method Statement (MES), Material (MAT), As-Built (ASB), Other (OTH)

---

### 4.2 rfa_status_codes

**Purpose**: Master table for RFA status codes

| Column Name | Data Type    | Constraints                 | Description                       |
| ----------- | ------------ | --------------------------- | --------------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier                 |
| status_code | VARCHAR(20)  | NOT NULL, UNIQUE            | Status code (DFT, FAP, FRE, etc.) |
| status_name | VARCHAR(100) | NOT NULL                    | Full status name                  |
| description | TEXT         | NULL                        | Status description                |
| sort_order  | INT          | DEFAULT 0                   | Display order                     |
| is_active   | TINYINT(1)   | DEFAULT 1                   | Active status                     |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (status_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: rfa_revisions

**Seed Data**: 7 status codes

- DFT: Draft
- FAP: For Approve
- FRE: For Review
- FCO: For Construction
- ASB: AS-Built
- OBS: Obsolete
- CC: Canceled

---

### 4.3 rfa_approve_codes

**Purpose**: Master table for RFA approval result codes

| Column Name  | Data Type    | Constraints                 | Description                      |
| ------------ | ------------ | --------------------------- | -------------------------------- |
| id           | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique identifier                |
| approve_code | VARCHAR(20)  | NOT NULL, UNIQUE            | Approval code (1A, 1C, 3R, etc.) |
| approve_name | VARCHAR(100) | NOT NULL                    | Full approval name               |
| description  | TEXT         | NULL                        | Code description                 |
| sort_order   | INT          | DEFAULT 0                   | Display order                    |
| is_active    | TINYINT(1)   | DEFAULT 1                   | Active status                    |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (approve_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: rfa_revisions

**Seed Data**: 8 approval codes

- 1A: Approved by Authority
- 1C: Approved by CSC
- 1N: Approved As Note
- 1R: Approved with Remarks
- 3C: Consultant Comments
- 3R: Revise and Resubmit
- 4X: Reject
- 5N: No Further Action

---

### 4.4 rfas

**Purpose**: Master table for RFA documents (non-revisioned data)

| Column Name | Data Type | Constraints                 | Description                 |
| ----------- | --------- | --------------------------- | --------------------------- |
| id          | INT       | PRIMARY KEY, AUTO_INCREMENT | Master RFA ID               |
| rfa_type_id | INT       | NOT NULL, FK                | Reference to rfa_types      |
| created_at  | DATETIME  | DEFAULT CURRENT_TIMESTAMP   | Record creation timestamp   |
| created_by  | INT       | NULL, FK                    | User who created the record |
| deleted_at  | DATETIME  | NULL                        | Soft delete timestamp       |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id)
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- INDEX (rfa_type_id)
- INDEX (deleted_at)

**Relationships**:

- Parent: rfa_types, users
- Children: rfa_revisions

**Business Rules**:

- One RFA can have multiple revisions
- Soft delete preserves history

---

### 4.5 rfa_revisions

**Purpose**: Child table storing revision history of RFAs (1:N)

| Column Name         | Data Type    | Constraints                 | Description                                    |
| ------------------- | ------------ | --------------------------- | ---------------------------------------------- |
| id                  | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID                             |
| correspondence_id   | INT          | NOT NULL, FK                | Link to correspondence (RFA as correspondence) |
| rfa_id              | INT          | NOT NULL, FK                | Master RFA ID                                  |
| revision_number     | INT          | NOT NULL                    | Revision sequence (0, 1, 2...)                 |
| revision_label      | VARCHAR(10)  | NULL                        | Display revision (A, B, 1.1...)                |
| is_current          | BOOLEAN      | DEFAULT FALSE               | Current revision flag                          |
| rfa_status_code_id  | INT          | NOT NULL, FK                | Current RFA status                             |
| rfa_approve_code_id | INT          | NULL, FK                    | Approval result code                           |
| title               | VARCHAR(255) | NOT NULL                    | RFA title                                      |
| document_date       | DATE         | NULL                        | Document date                                  |
| issued_date         | DATE         | NULL                        | Issue date for approval                        |
| received_date       | DATETIME     | NULL                        | Received date                                  |
| approved_date       | DATE         | NULL                        | Approval date                                  |
| description         | TEXT         | NULL                        | Revision description                           |
| created_at          | DATETIME     | DEFAULT CURRENT_TIMESTAMP   | Revision creation timestamp                    |
| created_by          | INT          | NULL, FK                    | User who created revision                      |
| updated_by          | INT          | NULL, FK                    | User who last updated                          |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE
- FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id)
- FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL
- UNIQUE KEY (rfa_id, revision_number)
- UNIQUE KEY (rfa_id, is_current)
- INDEX (rfa_status_code_id)
- INDEX (rfa_approve_code_id)
- INDEX (is_current)

**Relationships**:

- Parent: correspondences, rfas, rfa_status_codes, rfa_approve_codes, users
- Children: rfa_items, rfa_workflows

**Business Rules**:

- RFA is a specialized type of correspondence
- Only one revision can be current per RFA
- Links to shop drawings through rfa_items

---

### 4.6 rfa_items

**Purpose**: Junction table linking RFA revisions to shop drawing revisions (M:N)

| Column Name              | Data Type | Constraints     | Description                    |
| ------------------------ | --------- | --------------- | ------------------------------ |
| rfarev_correspondence_id | INT       | PRIMARY KEY, FK | RFA revision correspondence ID |
| shop_drawing_revision_id | INT       | PRIMARY KEY, FK | Shop drawing revision ID       |

**Indexes**:

- PRIMARY KEY (rfarev_correspondence_id, shop_drawing_revision_id)
- FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- INDEX (shop_drawing_revision_id)

**Relationships**:

- Parent: rfa_revisions, shop_drawing_revisions

**Business Rules**:

- Used primarily for RFA type = 'DWG' (Shop Drawing)
- One RFA can contain multiple shop drawings
- One shop drawing can be referenced by multiple RFAs

---

### 4.7 rfa_workflow_templates

**Purpose**: Master table for RFA approval workflow templates

| Column Name     | Data Type    | Constraints                         | Description                                                          |
| --------------- | ------------ | ----------------------------------- | -------------------------------------------------------------------- |
| id              | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique template ID                                                   |
| template_name   | VARCHAR(100) | NOT NULL                            | Template name                                                        |
| description     | TEXT         | NULL                                | Template description                                                 |
| is_active       | TINYINT(1)   | DEFAULT 1                           | Active status                                                        |
| created_at      | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                                            |
| updated_at      | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                                                |
| workflow_config | JSON         | NULL                                | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |

**Indexes**:

- PRIMARY KEY (id)
- INDEX (is_active)
- INDEX (template_name)

**Relationships**:

- Children: rfa_workflow_template_steps

**Business Rules**:

- Defines reusable approval workflows for RFAs
- Can be assigned to specific RFA types or organizations

---

### 4.8 rfa_workflow_template_steps

**Purpose**: Child table defining steps in workflow templates

| Column Name     | Data Type | Constraints                 | Description                               |
| --------------- | --------- | --------------------------- | ----------------------------------------- |
| id              | INT       | PRIMARY KEY, AUTO_INCREMENT | Unique step ID                            |
| template_id     | INT       | NOT NULL, FK                | Reference to workflow template            |
| step_number     | INT       | NOT NULL                    | Step sequence order                       |
| organization_id | INT       | NOT NULL, FK                | Organization responsible for step         |
| role_id         | INT       | NULL, FK                    | Required role for this step               |
| action_type     | ENUM      | NULL                        | Action type: REVIEW, APPROVE, ACKNOWLEDGE |
| duration_days   | INT       | NULL                        | Expected duration in days                 |
| is_optional     | BOOLEAN   | DEFAULT FALSE               | Optional step flag                        |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (template_id) REFERENCES rfa_workflow_templates(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (role_id) REFERENCES roles(role_id)
- INDEX (template_id, step_number)
- INDEX (organization_id)

**Relationships**:

- Parent: rfa_workflow_templates, organizations, roles

**Business Rules**:

- Steps are executed in step_number order
- Optional steps can be skipped
- Duration used for deadline calculation

---

### 4.9 rfa_workflows

**Purpose**: Transaction log table tracking actual RFA approval workflow execution

| Column Name     | Data Type | Constraints                         | Description                                       |
| --------------- | --------- | ----------------------------------- | ------------------------------------------------- |
| id              | INT       | PRIMARY KEY, AUTO_INCREMENT         | Unique workflow log ID                            |
| rfa_revision_id | INT       | NOT NULL, FK                        | Reference to RFA revision                         |
| step_number     | INT       | NOT NULL                            | Current step number                               |
| organization_id | INT       | NOT NULL, FK                        | Organization responsible                          |
| assigned_to     | INT       | NULL, FK                            | Assigned user ID                                  |
| action_type     | ENUM      | NULL                                | Action type: REVIEW, APPROVE, ACKNOWLEDGE         |
| status          | ENUM      | NULL                                | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
| comments        | TEXT      | NULL                                | Comments/remarks                                  |
| completed_at    | DATETIME  | NULL                                | Completion timestamp                              |
| created_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                         |
| updated_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                             |
| state_context   | JSON*     | NULL                                | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot)   |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (assigned_to) REFERENCES users(user_id)
- INDEX (rfa_revision_id, step_number)
- INDEX (assigned_to, status)
- INDEX (status)

**Relationships**:

- Parent: rfa_revisions, organizations, users

**Business Rules**:

- Records actual workflow execution history
- Tracks who did what and when
- Multiple records per RFA revision (one per step)
- Status changes tracked via updated_at

---

## **5. 📐 Drawings Tables (แบบ, หมวดหมู่)**

### 5.1 contract_drawing_volumes

**Purpose**: Master table for contract drawing volume classification

| Column Name | Data Type    | Constraints                         | Description               |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique volume ID          |
| project_id  | INT          | NOT NULL, FK                        | Reference to projects     |
| volume_code | VARCHAR(50)  | NOT NULL                            | Volume code               |
| volume_name | VARCHAR(255) | NOT NULL                            | Volume name               |
| description | TEXT         | NULL                                | Volume description        |
| sort_order  | INT          | DEFAULT 0                           | Display order             |
| created_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp |
| updated_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp     |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, volume_code)
- INDEX (sort_order)

**Relationships**:

- Parent: projects
- Referenced by: contract_drawings

**Business Rules**:

- Volume codes must be unique within a project
- Used for organizing large sets of contract drawings

---

### 5.2 contract_drawing_cats

**Purpose**: Master table for contract drawing main categories

| Column Name | Data Type    | Constraints                         | Description               |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique category ID        |
| project_id  | INT          | NOT NULL, FK                        | Reference to projects     |
| cat_code    | VARCHAR(50)  | NOT NULL                            | Category code             |
| cat_name    | VARCHAR(255) | NOT NULL                            | Category name             |
| description | TEXT         | NULL                                | Category description      |
| sort_order  | INT          | DEFAULT 0                           | Display order             |
| created_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp |
| updated_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp     |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, cat_code)
- INDEX (sort_order)

**Relationships**:

- Parent: projects
- Referenced by: contract_drawing_subcat_cat_maps

**Business Rules**:

- Category codes must be unique within a project
- Hierarchical relationship with sub-categories via mapping table

---

### 5.3 contract_drawing_sub_cats

**Purpose**: Master table for contract drawing sub-categories

| Column Name  | Data Type    | Constraints                         | Description               |
| ------------ | ------------ | ----------------------------------- | ------------------------- |
| id           | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique sub-category ID    |
| project_id   | INT          | NOT NULL, FK                        | Reference to projects     |
| sub_cat_code | VARCHAR(50)  | NOT NULL                            | Sub-category code         |
| sub_cat_name | VARCHAR(255) | NOT NULL                            | Sub-category name         |
| description  | TEXT         | NULL                                | Sub-category description  |
| sort_order   | INT          | DEFAULT 0                           | Display order             |
| created_at   | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp |
| updated_at   | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp     |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, sub_cat_code)
- INDEX (sort_order)

**Relationships**:

- Parent: projects
- Referenced by: contract_drawings, contract_drawing_subcat_cat_maps

**Business Rules**:

- Sub-category codes must be unique within a project
- Can be mapped to multiple main categories via mapping table

---

### 5.4 contract_drawing_subcat_cat_maps

**Purpose**: Junction table mapping sub-categories to main categories (M:N)

| Column Name | Data Type | Constraints     | Description                |
| ----------- | --------- | --------------- | -------------------------- |
| project_id  | INT       | PRIMARY KEY, FK | Reference to projects      |
| sub_cat_id  | INT       | PRIMARY KEY, FK | Reference to sub-category  |
| cat_id      | INT       | PRIMARY KEY, FK | Reference to main category |

**Indexes**:

- PRIMARY KEY (project_id, sub_cat_id, cat_id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE
- FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE
- INDEX (sub_cat_id)
- INDEX (cat_id)

**Relationships**:

- Parent: projects, contract_drawing_sub_cats, contract_drawing_cats

**Business Rules**:

- Allows flexible categorization
- One sub-category can belong to multiple main categories
- All three fields required for uniqueness

---

### 5.5 contract_drawings

**Purpose**: Master table for contract drawings (from contract specifications)

| Column Name | Data Type    | Constraints                         | Description               |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id          | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique drawing ID         |
| project_id  | INT          | NOT NULL, FK                        | Reference to projects     |
| condwg_no   | VARCHAR(255) | NOT NULL                            | Contract drawing number   |
| title       | VARCHAR(255) | NOT NULL                            | Drawing title             |
| sub_cat_id  | INT          | NULL, FK                            | Reference to sub-category |
| volume_id   | INT          | NULL, FK                            | Reference to volume       |
| created_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp |
| updated_at  | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp     |
| deleted_at  | DATETIME     | NULL                                | Soft delete timestamp     |
| updated_by  | INT          | NULL, FK                            | User who last updated     |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT
- FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT
- FOREIGN KEY (updated_by) REFERENCES users(user_id)
- UNIQUE KEY (project_id, condwg_no)
- INDEX (sub_cat_id)
- INDEX (volume_id)
- INDEX (deleted_at)

**Relationships**:

- Parent: projects, contract_drawing_sub_cats, contract_drawing_volumes, users
- Referenced by: shop_drawing_revision_contract_refs, contract_drawing_attachments

**Business Rules**:

- Drawing numbers must be unique within a project
- Represents baseline/contract drawings
- Referenced by shop drawings for compliance tracking
- Soft delete preserves history

---

### 5.6 shop_drawing_main_categories

**Purpose**: Master table for shop drawing main categories (discipline-level)

| Column Name        | Data Type    | Constraints                         | Description                          |
| ------------------ | ------------ | ----------------------------------- | ------------------------------------ |
| id                 | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique category ID                   |
| main_category_code | VARCHAR(50)  | NOT NULL, UNIQUE                    | Category code (ARCH, STR, MEP, etc.) |
| main_category_name | VARCHAR(255) | NOT NULL                            | Category name                        |
| description        | TEXT         | NULL                                | Category description                 |
| sort_order         | INT          | DEFAULT 0                           | Display order                        |
| is_active          | TINYINT(1)   | DEFAULT 1                           | Active status                        |
| created_at         | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp            |
| updated_at         | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (main_category_code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: shop_drawing_sub_categories, shop_drawings

**Business Rules**:

- Global categories (not project-specific)
- Typically represents engineering disciplines

---

### 5.7 shop_drawing_sub_categories

**Purpose**: Master table for shop drawing sub-categories (component-level)

| Column Name       | Data Type    | Constraints                         | Description                                     |
| ----------------- | ------------ | ----------------------------------- | ----------------------------------------------- |
| id                | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique sub-category ID                          |
| sub_category_code | VARCHAR(50)  | NOT NULL, UNIQUE                    | Sub-category code (STR-COLUMN, ARCH-DOOR, etc.) |
| sub_category_name | VARCHAR(255) | NOT NULL                            | Sub-category name                               |
| main_category_id  | INT          | NOT NULL, FK                        | Reference to main category                      |
| description       | TEXT         | NULL                                | Sub-category description                        |
| sort_order        | INT          | DEFAULT 0                           | Display order                                   |
| is_active         | TINYINT(1)   | DEFAULT 1                           | Active status                                   |
| created_at        | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                       |
| updated_at        | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                           |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (sub_category_code)
- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
- INDEX (main_category_id)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Parent: shop_drawing_main_categories
- Referenced by: shop_drawings

**Business Rules**:

- Global sub-categories (not project-specific)
- Hierarchical under main categories
- Represents specific drawing types or components

---

### 5.8 shop_drawings

**Purpose**: Master table for shop drawings (contractor-submitted)

| Column Name      | Data Type    | Constraints                         | Description                |
| ---------------- | ------------ | ----------------------------------- | -------------------------- |
| id               | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique drawing ID          |
| project_id       | INT          | NOT NULL, FK                        | Reference to projects      |
| drawing_number   | VARCHAR(100) | NOT NULL, UNIQUE                    | Shop drawing number        |
| title            | VARCHAR(500) | NOT NULL                            | Drawing title              |
| main_category_id | INT          | NOT NULL, FK                        | Reference to main category |
| sub_category_id  | INT          | NOT NULL, FK                        | Reference to sub-category  |
| created_at       | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp  |
| updated_at       | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp      |
| deleted_at       | DATETIME     | NULL                                | Soft delete timestamp      |
| updated_by       | INT          | NULL, FK                            | User who last updated      |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (drawing_number)
- FOREIGN KEY (project_id) REFERENCES projects(id)
- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
- FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id)
- FOREIGN KEY (updated_by) REFERENCES users(user_id)
- INDEX (project_id)
- INDEX (main_category_id)
- INDEX (sub_category_id)
- INDEX (deleted_at)

**Relationships**:

- Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users
- Children: shop_drawing_revisions

**Business Rules**:

- Drawing numbers are globally unique across all projects
- Represents contractor shop drawings
- Can have multiple revisions
- Soft delete preserves history

---

### 5.9 shop_drawing_revisions

**Purpose**: Child table storing revision history of shop drawings (1:N)

| Column Name     | Data Type   | Constraints                 | Description                    |
| --------------- | ----------- | --------------------------- | ------------------------------ |
| id              | INT         | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID             |
| shop_drawing_id | INT         | NOT NULL, FK                | Master shop drawing ID         |
| revision_number | INT         | NOT NULL                    | Revision sequence (0, 1, 2...) |
| revision_label  | VARCHAR(10) | NULL                        | Display revision (A, B, C...)  |
| revision_date   | DATE        | NULL                        | Revision date                  |
| description     | TEXT        | NULL                        | Revision description/changes   |
| created_at      | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP   | Revision creation timestamp    |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE
- UNIQUE KEY (shop_drawing_id, revision_number)
- INDEX (revision_date)

**Relationships**:

- Parent: shop_drawings
- Referenced by: rfa_items, shop_drawing_revision_contract_refs, shop_drawing_revision_attachments

**Business Rules**:

- Revision numbers are sequential starting from 0
- Each revision can reference multiple contract drawings
- Each revision can have multiple file attachments
- Linked to RFAs for approval tracking

---

### 5.10 shop_drawing_revision_contract_refs

**Purpose**: Junction table linking shop drawing revisions to referenced contract drawings (M:N)

| Column Name              | Data Type | Constraints     | Description                        |
| ------------------------ | --------- | --------------- | ---------------------------------- |
| shop_drawing_revision_id | INT       | PRIMARY KEY, FK | Reference to shop drawing revision |
| contract_drawing_id      | INT       | PRIMARY KEY, FK | Reference to contract drawing      |

**Indexes**:

- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id)
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE
- INDEX (contract_drawing_id)

**Relationships**:

- Parent: shop_drawing_revisions, contract_drawings

**Business Rules**:

- Tracks which contract drawings each shop drawing revision is based on
- Ensures compliance with contract specifications
- One shop drawing revision can reference multiple contract drawings

---

## **6. 🔄 Circulations Tables (ใบเวียนภายใน)**

### 6.1 circulation_status_codes

**Purpose**: Master table for circulation workflow status codes

| Column Name | Data Type   | Constraints                 | Description                                         |
| ----------- | ----------- | --------------------------- | --------------------------------------------------- |
| id          | INT         | PRIMARY KEY, AUTO_INCREMENT | Unique status ID                                    |
| code        | VARCHAR(20) | NOT NULL, UNIQUE            | Status code (OPEN, IN_REVIEW, COMPLETED, CANCELLED) |
| description | VARCHAR(50) | NOT NULL                    | Status description                                  |
| sort_order  | INT         | DEFAULT 0                   | Display order                                       |
| is_active   | TINYINT(1)  | DEFAULT 1                   | Active status                                       |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (code)
- INDEX (is_active)
- INDEX (sort_order)

**Relationships**:

- Referenced by: circulations

**Seed Data**: 4 status codes

- OPEN: Initial status when created
- IN_REVIEW: Under review by recipients
- COMPLETED: All recipients have responded
- CANCELLED: Withdrawn/cancelled

---

### 6.2 circulations

**Purpose**: Master table for internal circulation sheets (document routing)

| Column Name             | Data Type    | Constraints                         | Description                               |
| ----------------------- | ------------ | ----------------------------------- | ----------------------------------------- |
| id                      | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique circulation ID                     |
| correspondence_id       | INT          | UNIQUE, FK                          | Link to correspondence (1:1 relationship) |
| organization_id         | INT          | NOT NULL, FK                        | Organization that owns this circulation   |
| circulation_no          | VARCHAR(100) | NOT NULL                            | Circulation sheet number                  |
| circulation_subject     | VARCHAR(500) | NOT NULL                            | Subject/title                             |
| circulation_status_code | VARCHAR(20)  | NOT NULL, FK                        | Current status code                       |
| created_by_user_id      | INT          | NOT NULL, FK                        | User who created circulation              |
| submitted_at            | TIMESTAMP    | NULL                                | Submission timestamp                      |
| closed_at               | TIMESTAMP    | NULL                                | Closure timestamp                         |
| created_at              | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                 |
| updated_at              | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                     |

**Indexes**:

- PRIMARY KEY (id)
- UNIQUE (correspondence_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id)
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code)
- FOREIGN KEY (created_by_user_id) REFERENCES users(user_id)
- INDEX (organization_id)
- INDEX (circulation_status_code)
- INDEX (created_by_user_id)

**Relationships**:

- Parent: correspondences, organizations, circulation_status_codes, users
- Children: circulation_routings, circulation_attachments

**Business Rules**:

- Internal document routing within organization
- One-to-one relationship with correspondences
- Tracks document review/approval workflow
- Status progression: OPEN → IN_REVIEW → COMPLETED/CANCELLED

---

### 6.3 circulation_templates

**Purpose**: Master table for circulation workflow templates

| Column Name     | Data Type    | Constraints                         | Description                 |
| --------------- | ------------ | ----------------------------------- | --------------------------- |
| id              | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique template ID          |
| template_name   | VARCHAR(100) | NOT NULL                            | Template name               |
| description     | TEXT         | NULL                                | Template description        |
| organization_id | INT          | NOT NULL, FK                        | Template owner organization |
| is_active       | TINYINT(1)   | DEFAULT 1                           | Active status               |
| created_at      | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp   |
| updated_at      | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp       |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- INDEX (organization_id)
- INDEX (is_active)

**Relationships**:

- Parent: organizations
- Children: circulation_template_assignees

**Business Rules**:

- Organization-specific templates
- Defines reusable routing workflows

---

### 6.4 circulation_template_assignees

**Purpose**: Child table defining steps in circulation templates

| Column Name     | Data Type | Constraints                 | Description                       |
| --------------- | --------- | --------------------------- | --------------------------------- |
| id              | INT       | PRIMARY KEY, AUTO_INCREMENT | Unique step ID                    |
| template_id     | INT       | NOT NULL, FK                | Reference to template             |
| step_number     | INT       | NOT NULL                    | Step sequence order               |
| organization_id | INT       | NOT NULL, FK                | Organization responsible for step |
| role_id         | INT       | NULL, FK                    | Required role for this step       |
| duration_days   | INT       | NULL                        | Expected duration in days         |
| is_optional     | BOOLEAN   | DEFAULT FALSE               | Optional step flag                |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (template_id) REFERENCES circulation_templates(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (role_id) REFERENCES roles(role_id)
- INDEX (template_id, step_number)
- INDEX (organization_id)

**Relationships**:

- Parent: circulation_templates, organizations, roles

**Business Rules**:

- Steps executed in step_number order
- Optional steps can be skipped
- Duration used for deadline calculation

---

### 6.5 circulation_routings

**Purpose**: Transaction log table tracking actual circulation routing execution

| Column Name     | Data Type | Constraints                         | Description                                       |
| --------------- | --------- | ----------------------------------- | ------------------------------------------------- |
| id              | INT       | PRIMARY KEY, AUTO_INCREMENT         | Unique routing log ID                             |
| circulation_id  | INT       | NOT NULL, FK                        | Reference to circulation                          |
| step_number     | INT       | NOT NULL                            | Current step number                               |
| organization_id | INT       | NOT NULL, FK                        | Organization responsible                          |
| assigned_to     | INT       | NULL, FK                            | Assigned user ID                                  |
| status          | ENUM      | NULL                                | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
| comments        | TEXT      | NULL                                | Comments/remarks                                  |
| completed_at    | DATETIME  | NULL                                | Completion timestamp                              |
| created_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                         |
| updated_at      | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                             |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (assigned_to) REFERENCES users(user_id)
- INDEX (circulation_id, step_number)
- INDEX (assigned_to, status)
- INDEX (status)

**Relationships**:

- Parent: circulations, organizations, users

**Business Rules**:

- Records actual routing history
- Multiple records per circulation (one per step)
- Tracks who reviewed/approved and when
- Used in v_user_tasks view for pending items

---

## **7. 📤 Transmittals Tables (เอกสารนำส่ง)**

### 7.1 transmittals

**Purpose**: Child table for transmittal-specific data (1:1 with correspondences)

| Column Name       | Data Type | Constraints     | Description                                               |
| ----------------- | --------- | --------------- | --------------------------------------------------------- |
| correspondence_id | INT       | PRIMARY KEY, FK | Reference to correspondences (1:1)                        |
| purpose           | ENUM      | NULL            | Purpose: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW, OTHER |
| remarks           | TEXT      | NULL            | Additional remarks                                        |

**Indexes**:

- PRIMARY KEY (correspondence_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- INDEX (purpose)

**Relationships**:

- Parent: correspondences
- Children: transmittal_items

**Business Rules**:

- One-to-one relationship with correspondences
- Transmittal is a correspondence type for forwarding documents
- Contains metadata about the transmission

---

### 7.2 transmittal_items

**Purpose**: Junction table listing documents included in transmittal (M:N)

| Column Name            | Data Type    | Constraints                 | Description                             |
| ---------------------- | ------------ | --------------------------- | --------------------------------------- |
| id                     | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique item ID                          |
| transmittal_id         | INT          | NOT NULL, FK                | Reference to transmittal                |
| item_correspondence_id | INT          | NOT NULL, FK                | Reference to document being transmitted |
| quantity               | INT          | DEFAULT 1                   | Number of copies                        |
| remarks                | VARCHAR(255) | NULL                        | Item-specific remarks                   |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE
- FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- UNIQUE KEY (transmittal_id, item_correspondence_id)
- INDEX (item_correspondence_id)

**Relationships**:

- Parent: transmittals, correspondences

**Business Rules**:

- One transmittal can contain multiple documents
- Tracks quantity of physical copies (if applicable)
- Links to any type of correspondence document

---

## **8. 📎 File Management Tables (ไฟล์แนบ)**

### 8.1 attachments

**Purpose**: Central repository for all file attachments in the system

| Column Name         | Data Type    | Constraints                 | Description                                                    |
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------- |
| id                  | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID                                           |
| original_filename   | VARCHAR(255) | NOT NULL                    | Original filename from upload                                  |
| stored_filename     | VARCHAR(255) | NOT NULL                    | System-generated unique filename                               |
| file_path           | VARCHAR(500) | NOT NULL                    | Full file path on server (/share/dms-data/)                    |
| mime_type           | VARCHAR(100) | NOT NULL                    | MIME type (application/pdf, image/jpeg, etc.)                  |
| file_size           | INT          | NOT NULL                    | File size in bytes                                             |
| uploaded_by_user_id | INT          | NOT NULL, FK                | User who uploaded file                                         |
| created_at          | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP   | Upload timestamp                                               |
| 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 Tables (การสร้างเลขที่เอกสาร)**

### 9.1 document_number_formats

**Purpose**: Master table defining document numbering templates per project and type

| Column Name            | Data Type    | Constraints                         | Description                                              |
| ---------------------- | ------------ | ----------------------------------- | -------------------------------------------------------- |
| id                     | INT          | PRIMARY KEY, AUTO_INCREMENT         | Unique format ID                                         |
| project_id             | INT          | NOT NULL, FK                        | Reference to projects                                    |
| correspondence_type_id | INT          | NOT NULL, FK                        | Reference to correspondence types                        |
| format_template        | VARCHAR(255) | NOT NULL                            | Template string (e.g., '{ORG_CODE}-{TYPE_CODE}-{SEQ:4}') |
| description            | TEXT         | NULL                                | Format description                                       |
| created_at             | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP           | Record creation timestamp                                |
| updated_at             | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp                                    |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, correspondence_type_id)
- INDEX (project_id)
- INDEX (correspondence_type_id)

**Relationships**:

- Parent: projects, correspondence_types

**Business Rules**:

- One format template per project per correspondence type combination
- Template placeholders: {ORG_CODE}, {TYPE_CODE}, {YEAR}, {SEQ:n} where n is zero-padding length
- Used by document numbering module to generate unique document numbers

---

### 9.2 document_number_counters

**Purpose**: Transaction table maintaining running sequence numbers for document numbering

| Column Name                | Data Type | Constraints     | Description                                     |
| -------------------------- | --------- | --------------- | ----------------------------------------------- |
| project_id                 | INT       | PRIMARY KEY, FK | Reference to projects                           |
| originator_organization_id | INT       | PRIMARY KEY, FK | Originating organization                        |
| correspondence_type_id     | INT       | PRIMARY KEY, FK | Reference to correspondence types               |
| current_year               | INT       | PRIMARY KEY     | Year (Buddhist calendar)                        |
| version                    | INT       | DEFAULT 0       | ใช้สำหรับ Optimistic Locking (ตรวจสอบค่าก่อน Update) |
| last_number                | INT       | DEFAULT 0       | Last assigned sequence number                   |

**Indexes**:

- PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, current_year)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
- INDEX (project_id)
- INDEX (originator_organization_id)
- INDEX (correspondence_type_id)
- INDEX (current_year)

**Relationships**:

- Parent: projects, organizations, correspondence_types

**Business Rules**:

- Composite primary key ensures unique counters per project/organization/type/year
- Counter resets each year
- Thread-safe increments handled by DocumentNumberingService in NestJS using Redis Lock and Optimistic Locking on version column
- last_number tracks highest assigned number
- Used with document_number_formats to generate complete document numbers

---

## **10. ⚙️ System & Logs Tables (ระบบและ Log)**

### 10.1 audit_logs

**Purpose**: Comprehensive audit trail for all significant system actions

| Column Name      | Data Type                                 | Constraints                       | Description                                              |
| ---------------- | ----------------------------------------- | --------------------------------- | -------------------------------------------------------- |
| audit_id         | BIGINT                                    | PRIMARY KEY, AUTO_INCREMENT       | Unique audit log ID                                      |
| user_id          | INT                                       | NULL, FK                          | User who performed action                                |
| action           | VARCHAR(100)                              | NOT NULL                          | Action code (e.g., 'rfa.create', 'login.success')        |
| entity_type      | VARCHAR(50)                               | NULL                              | Entity/module affected (e.g., 'rfa', 'correspondence')   |
| entity_id        | VARCHAR(50)                               | NULL                              | Primary ID of affected record                            |
| details_json     | JSON                                      | NULL                              | Additional context/details in JSON format                |
| ip_address       | VARCHAR(45)                               | NULL                              | Client IP address (supports IPv6)                        |
| user_agent       | VARCHAR(255)                              | NULL                              | Browser user agent string                                |
| created_at       | TIMESTAMP                                 | DEFAULT CURRENT_TIMESTAMP         | Action timestamp                                         |
| v_ref_project_id | INT                                       | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
| v_ref_type       | VARCHAR(50)                               | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details                  |
| request_id       | VARCHAR(100)                              | NULL                              | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs                |
| severity         | ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') | NULL                              | ระดับความรุนแรงของเหตุการณ์                                  |

**Indexes**:

- PRIMARY KEY (audit_id)
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL
- INDEX (user_id)
- INDEX (action)
- INDEX (entity_type, entity_id)
- INDEX (created_at)
- INDEX (ip_address)
- INDEX (v_ref_project_id)
- INDEX (v_ref_type)

**Relationships**:

- Parent: users

**Business Rules**:

- Immutable records (no updates/deletes)
- Captures all CRUD operations on sensitive data
- Includes authentication events (login, logout, failed attempts)
- JSON details field for flexible data storage
- Retention policy: typically 7 years for compliance
- Used for security audits, compliance reporting, and troubleshooting

**Common Actions**:

- Authentication: login.success, login.failed, logout
- Documents: correspondence.create, correspondence.update, rfa.submit
- Users: user.create, user.deactivate, role.assign
- Workflow: rfa.approve, rfa.reject, circulation.complete

---

### 10.2 notifications

**Purpose**: User notification queue for email, LINE, and in-system alerts

| Column Name       | Data Type    | Constraints                 | Description                                      |
| ----------------- | ------------ | --------------------------- | ------------------------------------------------ |
| id                | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique notification ID                           |
| user_id           | INT          | NOT NULL, FK                | Recipient user ID                                |
| title             | VARCHAR(255) | NOT NULL                    | Notification title/subject                       |
| message           | TEXT         | NOT NULL                    | Notification message body                        |
| notification_type | ENUM         | NOT NULL                    | Type: EMAIL, LINE, SYSTEM                        |
| is_read           | BOOLEAN      | DEFAULT FALSE               | Read status flag                                 |
| entity_type       | VARCHAR(50)  | NULL                        | Related entity type (e.g., 'rfa', 'circulation') |
| entity_id         | INT          | NULL                        | Related entity ID                                |
| created_at        | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP   | Notification creation timestamp                  |

**Indexes**:

- PRIMARY KEY (id)
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
- INDEX (user_id)
- INDEX (notification_type)
- INDEX (is_read)
- INDEX (entity_type, entity_id)
- INDEX (created_at)
- COMPOSITE INDEX (user_id, is_read, created_at) - For user notification listing

**Relationships**:

- Parent: users

**Business Rules**:

- EMAIL: Sent via SMTP to user's email address
- LINE: Sent via LINE Notify API to user's LINE ID
- SYSTEM: In-app notifications displayed in user interface
- Same notification can trigger multiple types (EMAIL + SYSTEM)
- entity_type/entity_id allow deep-linking to related records
- is_read flag only applicable for SYSTEM notifications
- Auto-cleanup: delete read notifications older than 30 days

**Common Notification Triggers**:

- Task assignment (circulation routing, RFA workflow)
- Document status changes (submitted, approved, rejected)
- Approaching deadlines
- System announcements
- Mention in comments

---

### 10.3 search_indices

**Purpose**: Full-text search index for document content

| Column Name | Data Type   | Constraints                 | Description                                 |
| ----------- | ----------- | --------------------------- | ------------------------------------------- |
| id          | INT         | PRIMARY KEY, AUTO_INCREMENT | Unique index ID                             |
| entity_type | VARCHAR(50) | NOT NULL                    | Entity type (e.g., 'correspondence', 'rfa') |
| entity_id   | INT         | NOT NULL                    | Entity primary ID                           |
| content     | TEXT        | NOT NULL                    | Searchable text content                     |
| indexed_at  | TIMESTAMP   | DEFAULT CURRENT_TIMESTAMP   | Last indexing timestamp                     |

**Indexes**:

- PRIMARY KEY (id)
- INDEX (entity_type, entity_id)
- INDEX (indexed_at)

**Business Rules**:

- Automatically populated/updated when documents change
- Content includes: title, description, document number, metadata
- May include OCR text from PDF attachments (future enhancement)
- Used by advanced search functionality
- Periodic re-indexing to catch missed updates
- Supports Boolean operators, phrase searching

**Search Features**:

- Natural language queries
- Wildcard support (\*, ?)
- Boolean operators (AND, OR, NOT)
- Phrase matching with quotes
- Result ranking by relevance

---

### 10.4 backup_logs

**Purpose**: Log table tracking database and file backup operations

| Column Name   | Data Type    | Constraints                 | Description                        |
| ------------- | ------------ | --------------------------- | ---------------------------------- |
| id            | INT          | PRIMARY KEY, AUTO_INCREMENT | Unique backup log ID               |
| backup_type   | ENUM         | NOT NULL                    | Type: DATABASE, FILES, FULL        |
| backup_path   | VARCHAR(500) | NOT NULL                    | Path to backup file/directory      |
| file_size     | BIGINT       | NULL                        | Backup file size in bytes          |
| status        | ENUM         | NOT NULL                    | Status: STARTED, COMPLETED, FAILED |
| started_at    | TIMESTAMP    | DEFAULT CURRENT_TIMESTAMP   | Backup start timestamp             |
| completed_at  | TIMESTAMP    | NULL                        | Backup completion timestamp        |
| error_message | TEXT         | NULL                        | Error details if failed            |

**Indexes**:

- PRIMARY KEY (id)
- INDEX (backup_type)
- INDEX (status)
- INDEX (started_at)
- INDEX (completed_at)

**Business Rules**:

- DATABASE: MariaDB dump of database schema and data
- FILES: Backup of attachment files from QNAP storage
- FULL: Complete system backup (database + files)
- Triggered by n8n cron jobs
- Backup retention policy defined in backup strategy
- Failed backups trigger alert notifications
- completed_at - started_at = backup duration

**Monitoring**:

- Alert if no successful backup in 24 hours
- Track backup size trends over time
- Verify backup integrity with test restores

---

### 10.5 json_schemas

**Purpose**: องรับ **Centralized JSON Schema Registry** เพื่อ Validate ข้อมูล JSON Details ของเอกสารแต่ละประเภท ตาม Requirements 6.11.1 และ Backend Plan T2.5.1

| Column Name           | Data Type      | Constraints      | Description                                        |
| :-------------------- | :------------- | :--------------- | :------------------------------------------------- |
| **id**                | `INT`          | PK, AI           | Unique Identifier                                  |
| **schema_code**       | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`)       |
| **version**           | `INT`          | NOT NULL         | เวอร์ชันของ Schema                                   |
| **schema_definition** | `JSON`         | NOT NULL         | โครงสร้าง JSON Schema (Standard JSON Schema format) |
| **is_active**         | `BOOLEAN`      | DEFAULT TRUE     | สถานะการใช้งาน                                      |
| **created_at**        | `TIMESTAMP`    |                  | วันที่สร้าง                                            |

### 10.6 user_preferences

**Purpose**: แยกข้อมูลการตั้งค่าส่วนตัว (เช่น Notification Settings) ออกจากตาราง Users เพื่อความยืดหยุ่น ตาม Requirements 5.5 และ 6.8.3

| Column Name      | Data Type     | Constraints     | Description                            |
| :--------------- | :------------ | :-------------- | :------------------------------------- |
| **user_id**      | `INT`         | PK, FK          | อ้างอิงตาราง users                       |
| **notify_email** | `BOOLEAN`     | DEFAULT TRUE    | รับแจ้งเตือนทางอีเมล                       |
| **notify_line**  | `BOOLEAN`     | DEFAULT TRUE    | รับแจ้งเตือนทาง LINE                      |
| **digest_mode**  | `BOOLEAN`     | DEFAULT TRUE    | รับแจ้งเตือนแบบรวม (Digest) แทน Real-time |
| **ui_theme**     | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark)                   |
| **updated_at**   | `TIMESTAMP`   |                 | วันที่แก้ไขล่าสุด                            |

## **11. 📊 Views & Procedures (วิว และ โปรซีเดอร์)**

### 11.1 v_current_correspondences

**Purpose**: View showing current revision of all non-RFA correspondences

**Columns**:

- correspondence_id, correspondence_number, correspondence_type_id/code/name
- project_id, project_code, project_iname
- organization_id, organization_code, organization_name
- revision_id, revision_number, revision_label
- title, document_date, issued_date, received_date, due_date
- correspondence_status_id, correspondence_status_code, correspondence_status_name
- created_by, created_by_username, revision_created_at

**Filters**:

- is_current = TRUE (only latest revision)
- correspondence_type NOT IN ('RFA') (excludes RFAs)
- deleted_at IS NULL (excludes soft-deleted records)

**Business Rules**:

- Provides flattened view of current correspondence state
- Joins correspondence_revisions with is_current flag
- Used by dashboard, document listing screens
- Excludes RFAs (they have separate view)

---

### 11.2 v_current_rfas

**Purpose**: View showing current revision of all RFA documents

**Columns**:

- rfa_id, rfa_type_id, rfa_type_code, rfa_type_name
- correspondence_id, correspondence_number
- project_id, project_code, project_iname
- organization_id, organization_name
- revision_id, revision_number, revision_label
- title, document_date, issued_date, received_date, approved_date
- rfa_status_code_id, rfa_status_code, rfa_status_name
- rfa_approve_code_id, rfa_approve_code, rfa_approve_code_name
- created_by, created_by_username, revision_created_at

**Filters**:

- is_current = TRUE
- deleted_at IS NULL (both rfas and correspondences)

**Business Rules**:

- Specialized view for RFA documents
- Includes RFA-specific fields (approval codes, approved date)
- Joins across rfas → rfa_revisions → correspondences
- Used by RFA management screens

---

### 11.3 v_contract_parties_all

**Purpose**: View showing all organization relationships across contracts and projects

**Columns**:

- contract_id, contract_code, contract_name
- project_id, project_code, project_iname
- organization_id, organization_code, organization_name
- role_in_contract

**Business Rules**:

- Joins contracts → projects → contract_organizations → organizations
- Shows only active contracts (is_active = TRUE)
- Used for permission checks and document routing
- Supports multi-organization projects/contracts

---

### 11.4 v_user_tasks

**Purpose**: View showing pending tasks assigned to users (action items)

**Columns**:

- routing_id, circulation_id, circulation_no, circulation_subject
- correspondence_id, correspondence_number
- project_id, project_code, project_name
- user_id, username, first_name, last_name
- organization_id, organization_name
- step_number, task_status, comments
- completed_at, assigned_at, circulation_created_at

**Filters**:

- status IN ('PENDING', 'IN_PROGRESS')
- assigned_to IS NOT NULL

**Business Rules**:

- Shows circulation routings requiring user action
- Used for "My Tasks" / "Inbox" functionality
- Excludes completed/cancelled tasks
- Ordered by creation date (oldest first)

---

### 11.5 v_audit_log_details

**Purpose**: View enriching audit logs with user information

**Columns**:

- audit_id, user_id, username, email, first_name, last_name
- action, entity_type, entity_id
- details_json, ip_address, user_agent
- created_at

**Business Rules**:

- Joins audit_logs with users table
- Used for audit trail reports
- Includes user details even if user later deleted (LEFT JOIN)

---

### 11.6 v_user_all_permissions

**Purpose**: View showing all effective permissions for users across all scopes

**Columns**:

- user_id, role_id, role_name
- permission_id, permission_name
- module, scope_level
- organization_id, project_id, contract_id
- permission_scope (GLOBAL, ORGANIZATION, PROJECT, CONTRACT)

**Business Rules**:

- UNION of permissions from Global, Organization, Project, and Contract scopes
- Used for authorization checks
- Considers role-permission mappings at all levels
- Only shows active permissions (is_active = 1)
- One row per user-permission-scope combination

**Usage Example**:

```sql
SELECT permission_name
FROM v_user_all_permissions
WHERE user_id = ?
  AND project_id = ?
  AND permission_name = 'document.edit';
```

---

### 11.7 v_documents_with_attachments

**Purpose**: View showing all documents and their attachment counts

**Columns**:

- document_type (CORRESPONDENCE, CIRCULATION, SHOP_DRAWING, CONTRACT_DRAWING)
- document_id, document_number
- project_id, project_code, project_name
- attachment_count, latest_attachment_date

**Business Rules**:

- UNION of all document types with attachments
- Used for document listing with file indicators
- Helps identify documents missing attachments
- Aggregates count per document

---

### 11.8 v_document_statistics

**Purpose**: View providing aggregated document statistics by project, type, and status

**Columns**:

- project_id, project_code, project_name
- correspondence_type_id, correspondence_type_code, correspondence_type_name
- status_id, status_code, status_name
- document_count, revision_count

**Business Rules**:

- CROSS JOIN creates all possible combinations
- LEFT JOIN shows zeros for combinations with no documents
- Only includes active projects, types, and statuses
- Used for dashboard charts and reports
- Groups by project → type → status

---

## Database Indexes Summary

### Performance Optimization Indexes

**Primary Indexes** (automatic with PRIMARY KEY):

- All tables have PRIMARY KEY with AUTO_INCREMENT

**Foreign Key Indexes** (automatic with FOREIGN KEY):

- All FK relationships automatically indexed

**Additional Performance Indexes**:

1. **Correspondence Tables**:

   - `idx_correspondences_type_project` on (correspondence_type_id, project_id)
   - `idx_corr_revisions_current_status` on (is_current, correspondence_status_id)
   - `idx_corr_revisions_correspondence_current` on (correspondence_id, is_current)
   - `idx_correspondences_project_type` on (project_id, correspondence_type_id)

2. **RFA Tables**:

   - `idx_rfa_revisions_current_status` on (is_current, rfa_status_code_id)
   - `idx_rfa_revisions_rfa_current` on (rfa_id, is_current)

3. **Circulation Tables**:

   - `idx_circulation_routings_status_assigned` on (status, assigned_to)
   - `idx_circulation_routings_circulation_status` on (circulation_id, status)

4. **Document Numbering**:

   - `idx_doc_counter_composite` on (project_id, originator_organization_id, correspondence_type_id, current_year)

5. **Audit & Notifications**:

   - `idx_audit_logs_reporting` on (created_at, entity_type, action)
   - `idx_notifications_user_unread` on (user_id, is_read, created_at)

---

## Data Integrity Constraints

### Foreign Key Constraints

**Cascade Delete**:

- Parent-child relationships where child should be deleted with parent
- Examples: correspondence_revisions, shop_drawing_revisions, project/contract relationships

**Restrict Delete**:

- Prevents deletion if references exist
- Examples: correspondence_types, rfa_status_codes, organizations (when referenced)

**Set NULL**:

- Preserves record but removes reference
- Examples: originator_id, created_by, updated_by

### Unique Constraints

1. **Globally Unique**:

   - usernames, emails
   - shop_drawing.drawing_number

2. **Unique Within Scope**:

   - (project_id, correspondence_number)
   - (project_id, condwg_no)
   - (correspondence_id, revision_number)
   - (rfa_id, revision_number)

3. **Composite Unique**:
   - (correspondence_id, is_current) - ensures only one current revision
   - (project_id, correspondence_type_id) - in document_number_formats

### Check Constraints

1. **user_assignments.chk_scope**:
   - Ensures only one scope field (organization_id, project_id, contract_id) is NOT NULL
   - OR all are NULL for Global scope

### Business Rule Constraints

1. **Soft Delete Pattern**:

   - deleted_at timestamp instead of hard delete
   - Preserves audit trail and relationships
   - Applied to: correspondences, rfas, shop_drawings, contract_drawings

2. **Current Revision Pattern**:

   - is_current flag with UNIQUE constraint
   - Ensures only one current revision per document

3. **Sequential Numbering**:
   - revision_number starts at 0, increments by 1
   - Enforced by application logic + stored procedure

---

## Security & Permissions Model

### Access Control Hierarchy

```tree
Global Scope (Superadmin)
  └── Organization Scope (Org Admin, Document Control)
      └── Project Scope (Project Manager)
          └── Contract Scope (Contract Admin)
```

### Permission Inheritance

- Users can have multiple role assignments at different scopes
- More specific scopes inherit access from broader scopes
- Permission checks evaluate all applicable scopes
- View v_user_all_permissions aggregates effective permissions

### Role-Based Access Control (RBAC)

**7 Predefined Roles**:

1. Superadmin (Global) - Full system access
2. Org Admin (Organization) - Organization management
3. Document Control (Organization) - Document lifecycle + admin powers
4. Editor (Organization) - Document CRUD
5. Viewer (Organization) - Read-only
6. Project Manager (Project) - Project + document management
7. Contract Admin (Contract) - Contract-specific management

### Permission Categories (49 total)

1. **System Management** (1): Full system control
2. **Organization Management** (4): CRUD on organizations
3. **Project Management** (8): CRUD + member/contract management
4. **Role & Permission Management** (4): RBAC administration
5. **Master Data Management** (4): Document types, categories, tags
6. **User Management** (5): User CRUD + organization assignment
7. **Contract Management** (2): Contract administration
8. **Document Management** (16): Full document lifecycle
9. **Workflow Management** (3): Approval workflow control
10. **Search & Reporting** (2): Advanced search, report generation

---

## Data Migration & Seeding

### Pre-populated Master Data

1. **organization_roles**: Not used in current implementation
2. **organizations**: 15 organizations (Owner, Consultants, Contractors, Third parties)
3. **projects**: 5 projects (LCBP3 + 4 sub-contracts)
4. **contracts**: 7 contracts
5. **users**: 3 initial users (superadmin, editor01, viewer01)
6. **roles**: 7 predefined roles
7. **permissions**: 49 system permissions
8. **role_permissions**: Complete permission mappings
9. **project_organizations**: Project-organization relationships
10. **contract_organizations**: Contract-organization-role relationships
11. **correspondence_types**: 10 types
12. **correspondence_status**: 23 status codes
13. **rfa_types**: 11 types
14. **rfa_status_codes**: 7 statuses
15. **rfa_approve_codes**: 8 approval codes
16. **circulation_status_codes**: 4 statuses

### Initial Passwords

All seed users have password: `password123`

- Hashed as: `$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq`
- **Must be changed on first login in production**

---

## Backup & Recovery Strategy

### Database Backup

**Strategy**:

- Daily full database backups
- Hourly incremental backups (transaction logs)
- Retention: 30 days online, 7 years archived

**Backup Method**:

- MariaDB mysqldump for logical backups
- MariaDB Backup (Mariabackup) for physical backups
- Automated via n8n cron workflows

### File Backup

**Strategy**:

- Daily backup of /share/dms-data/ directory
- QNAP snapshot every 4 hours
- Offsite replication to secondary NAS

**File Organization**:

```tree
/share/dms-data/
  ├── attachments/
  │   ├── 2025/
  │   │   ├── 01/
  │   │   │   └── {uuid}-{filename}
  │   │   └── 02/
  │   └── 2024/
  └── temp/
```

### Recovery Procedures

1. **Point-in-Time Recovery**: Using transaction logs
2. **Full Restore**: From latest full backup
3. **Selective Restore**: Individual tables or records
4. **File Recovery**: From QNAP snapshots or backup

---

## Performance Optimization

### Query Optimization

1. **Use Indexed Columns**: WHERE, JOIN, ORDER BY clauses
2. **Avoid SELECT**: Specify needed columns
3. **Use Views**: For complex, frequently-used queries
4. **Limit Result Sets**: Use LIMIT and pagination
5. **Analyze Slow Queries**: Enable slow query log

### Index Maintenance

```sql
-- Check index usage
SELECT * FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'lcbp3';

-- Optimize tables
OPTIMIZE TABLE correspondences;

-- Analyze tables
ANALYZE TABLE correspondences;
```

### Connection Pooling

- Backend uses connection pool (NestJS TypeORM)
- Min pool size: 5
- Max pool size: 20
- Idle timeout: 10 minutes

---

## Data Validation Rules

### Required Fields Validation

**At Application Level**:

- Email format validation
- Date range validation (start_date < end_date)
- File size limits (5MB per attachment)
- File type restrictions (PDF, DWG, DOC, XLS, images)

**At Database Level**:

- NOT NULL constraints
- UNIQUE constraints
- Foreign key constraints
- Check constraints (user_assignments scope)

### Business Logic Validation

1. **Document Workflow**:

   - Cannot edit submitted documents (unless Document Control)
   - Cannot skip workflow steps (unless forced)
   - Must provide approval comments

2. **User Management**:

   - Cannot delete users with active assignments
   - Cannot deactivate own account
   - Must have valid organization for non-Global roles

3. **File Management**:
   - Original filename preserved for audit
   - Unique stored filename prevents conflicts
   - File path must exist and be accessible

---

## Change Log & Versioning

### Database Version: v1.4.0

**Changes from v1.3.0**:

- Added comprehensive RBAC system (roles, permissions, user_assignments)
- Refactored organization-project-contract relationships
- Added junction tables for M:N relationships
- Implemented soft delete pattern
- Added full-text search support
- Enhanced audit logging with JSON details
- Added circulation workflow templates
- Improved document numbering with stored procedure
- Added comprehensive views for common queries
- Optimized indexes for performance

**Migration Path**:

- v1.3.0 → v1.4.0: Run migration script (not provided in this excerpt)
- Backup database before migration
- Test migration on staging environment first

---

## Technical Specifications

### Database Configuration

**MariaDB Server**:

- Version: 10.11
- Character Set: utf8mb4
- Collation: utf8mb4_general_ci
- Time Zone: +07:00 (Bangkok/Asia)
- SQL Mode: STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION

**Connection Settings**:

- Host: Container on QNAP TS-473A
- Port: 3306 (default)
- Max Connections: 100
- Max Packet Size: 64MB

### Table Engine

**InnoDB Features Used**:

- ACID compliance
- Foreign key constraints
- Row-level locking
- Crash recovery
- Transaction support

### Storage Requirements

**Estimated Initial Size**:

- Database: ~50 MB (with seed data)
- Indexes: ~20 MB
- Total: ~70 MB

**Growth Estimates**:

- 1,000 documents/month: +100 MB/month (database)
- 10 attachments/document @ 2MB avg: +20 GB/month (files)
- Plan for 1TB+ storage within first year

---

## Application Integration

### Backend (NestJS)

**TypeORM Entities**:

- One entity class per table
- Decorators for columns, relationships
- DTOs for data transfer
- Repositories for data access

**Key Modules**:

- AuthModule: Authentication, authorization
- DocumentsModule: Correspondence, RFA management
- DrawingsModule: Shop drawing, contract drawing
- WorkflowModule: Circulation, approval workflows
- FilesModule: Attachment management
- UsersModule: User, role, permission management

### Frontend (Next.js)

**Key Features**:

- Document listing/search
- Document creation wizards
- Workflow approval interface
- File upload/download
- User management console
- Dashboard analytics

### Integration Points

1. **Document Numbering**:

   - Call DocumentNumberingService.generateNextNumber() (NestJS) which handles Redis locking and retry logic
   - Format with template from document_number_formats
   - Store in correspondences.correspondence_number

2. **File Upload**:

   - Upload to QNAP /share/dms-data/
   - Create attachment record
   - Link via junction table

3. **Workflow Execution**:

   - Check rfa_workflow_templates
   - Create rfa_workflows records
   - Update status as steps complete
   - Send notifications

4. **Permission Checks**:
   - Query v_user_all_permissions
   - Cache results per session
   - Re-check on sensitive operations

---

## Monitoring & Maintenance

### Health Checks

```sql
-- Database size
SELECT
  table_schema,
  SUM(data_length + index_length) / 1024 / 1024 AS size_mb
FROM information_schema.TABLES
WHERE table_schema = 'dms_db'
GROUP BY table_schema;

-- Table sizes
SELECT
  table_name,
  (data_length + index_length) / 1024 / 1024 AS size_mb,
  table_rows
FROM information_schema.TABLES
WHERE table_schema = 'dms_db'
ORDER BY (data_length + index_length) DESC;

-- Active connections
SHOW PROCESSLIST;

-- Lock wait statistics
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
```

### Scheduled Maintenance

**Daily**:

- Full database backup
- Check backup log for failures
- Monitor disk space

**Weekly**:

- OPTIMIZE tables
- ANALYZE tables
- Review slow query log
- Check for deadlocks

**Monthly**:

- Review audit logs
- Clean up old notifications (30+ days)
- Archive old audit logs (7+ years)
- Verify backup integrity (test restore)

**Quarterly**:

- Review and optimize indexes
- Update database statistics
- Capacity planning review

---

## Glossary

**Terms**:

- **Correspondence**: Any formal document exchanged between parties
- **RFA**: Request for Approval - formal submittal for review/approval
- **Circulation**: Internal document routing workflow
- **Transmittal**: Cover sheet for forwarding multiple documents
- **Shop Drawing**: Detailed construction drawing from contractor
- **Contract Drawing**: Baseline drawing from contract specifications
- **Revision**: Version of a document
- **Originator**: Organization that creates/sends a document
- **Recipient**: Organization that receives a document (TO or CC)
- **Scope**: Level of permission application (Global, Organization, Project, Contract)

**Acronyms**:

- **DMS**: Document Management System
- **LCBP3**: Laem Chabang Port Phase 3
- **RBAC**: Role-Based Access Control
- **RFA**: Request for Approval
- **RFI**: Request for Information
- **CSC**: Construction Supervision Consultant
- **TO**: Primary recipient (action required)
- **CC**: Carbon copy (for information)
- **QNAP**: Network Attached Storage device manufacturer

---

## **Document Control:**

- **Document:** Data Dictionary v1.4.3
- **Version:** 1.4
- **Date:** 2025-11-22
- **Author:** NAP LCBP3-DMS & Gemini
- **Status:** FINAL
- **Classification:** Internal Technical Documentation
- **Approved By:** Nattanin

---

`End of Data Dictionary v1.4.3 (ฉบับปรับปรุง)`
