Files
lcbp3/specs/03-Data-and-Storage/03-01-data-dictionary.md
admin 276d06e950
All checks were successful
Build and Deploy / deploy (push) Successful in 2m49s
260228:1412 20260228: setup n8n
2026-02-28 14:12:48 +07:00

2200 lines
114 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
version: 1.8.0
status: released
owner: Nattanin Peancharoen
last_updated: 2026-02-28
related:
- specs/01-requirements/02-architecture.md
- specs/01-requirements/03-functional-requirements.md
---
# 1. Data Model Architecture Overview
## 📋 1.1 Overview
āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļ­āļ˜āļīāļšāļēāļĒāļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļ‚āļ­āļ‡ Data Model āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļš LCBP3-DMS āđ‚āļ”āļĒāļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ, āļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ•āļēāļĢāļēāļ‡, āđāļĨāļ°āļŦāļĨāļąāļāļāļēāļĢāļ­āļ­āļāđāļšāļšāļ—āļĩāđˆāļŠāļģāļ„āļąāļ
## ðŸŽŊ 1.2 Design Principles
### 1. Separation of Concerns
- **Master-Revision Pattern**: āđāļĒāļāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ (Master) āļˆāļēāļāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļĄāļĩāļāļēāļĢāđāļāđ‰āđ„āļ‚ (Revisions)
- `correspondences` (Master) ↔ `correspondence_revisions` (Revisions)
- `rfas` (Master) ↔ `rfa_revisions` (Revisions)
- `shop_drawings` (Master) ↔ `shop_drawing_revisions` (Revisions)
### 2. Data Integrity
- **Foreign Key Constraints**: āđƒāļŠāđ‰ FK āļ—āļļāļāļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđ€āļžāļ·āđˆāļ­āļĢāļąāļāļĐāļēāļ„āļ§āļēāļĄāļŠāļĄāļšāļđāļĢāļ“āđŒāļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ
- **Soft Delete**: āđƒāļŠāđ‰ `deleted_at` āđāļ—āļ™āļāļēāļĢāļĨāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļĢāļīāļ‡ āđ€āļžāļ·āđˆāļ­āļĢāļąāļāļĐāļēāļ›āļĢāļ°āļ§āļąāļ•āļī
- **Optimistic Locking**: āđƒāļŠāđ‰ `version` column āđƒāļ™ `document_number_counters` āļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition
### 3. Flexibility & Extensibility
- **JSON Details Field**: āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ›āļĢāļ°āđ€āļ āļ—āđƒāļ™ `correspondence_revisions.details`
- **Virtual Columns**: āļŠāļĢāđ‰āļēāļ‡ Index āļˆāļēāļ JSON fields āļŠāļģāļŦāļĢāļąāļš Performance
- **Master Data Tables**: āđāļĒāļāļ‚āđ‰āļ­āļĄāļđāļĨ Master (Types, Status, Codes) āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™
### 4. Security & Audit
- **RBAC (Role-Based Access Control)**: āļĢāļ°āļšāļšāļŠāļīāļ—āļ˜āļīāđŒāđāļšāļš Hierarchical Scope
- **Audit Trail**: āļšāļąāļ™āļ—āļķāļāļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚ āđāļĨāļ°āđ€āļ§āļĨāļēāđƒāļ™āļ—āļļāļāļ•āļēāļĢāļēāļ‡
- **Two-Phase File Upload**: āļ›āđ‰āļ­āļ‡āļāļąāļ™āđ„āļŸāļĨāđŒāļ‚āļĒāļ°āļ”āđ‰āļ§āļĒ Temporary Storage
# 2. Database Schema Overview (ERD)
### Entity Relationship Diagram
```mermaid
erDiagram
%% Core Entities
organizations ||--o{ users : "employs"
projects ||--o{ contracts : "contains"
projects ||--o{ correspondences : "manages"
%% RBAC
users ||--o{ user_assignments : "has"
roles ||--o{ user_assignments : "assigned_to"
roles ||--o{ role_permissions : "has"
permissions ||--o{ role_permissions : "granted_by"
%% Correspondences
correspondences ||--o{ correspondence_revisions : "has_revisions"
correspondence_types ||--o{ correspondences : "categorizes"
correspondence_status ||--o{ correspondence_revisions : "defines_state"
disciplines ||--o{ correspondences : "classifies"
%% RFAs
rfas ||--o{ rfa_revisions : "has_revisions"
rfa_types ||--o{ rfas : "categorizes"
rfa_status_codes ||--o{ rfa_revisions : "defines_state"
rfa_approve_codes ||--o{ rfa_revisions : "defines_result"
disciplines ||--o{ rfas : "classifies"
%% Drawings
shop_drawings ||--o{ shop_drawing_revisions : "has_revisions"
shop_drawing_main_categories ||--o{ shop_drawings : "categorizes"
shop_drawing_sub_categories ||--o{ shop_drawings : "sub_categorizes"
%% Attachments
attachments ||--o{ correspondence_attachments : "attached_to"
correspondences ||--o{ correspondence_attachments : "has"
```
---
# 3. Data Dictionary V1.8.0
> āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ: PK = Primary Key, FK = Foreign Key, AI = AUTO_INCREMENT. āļĢāļđāļ›āđāļšāļš Soft Delete āļˆāļ°āļ›āļĢāļēāļāļ Column `deleted_at DATETIME NULL` āđ€āļ›āđ‡āļ™āļĄāļēāļ•āļĢāļāļēāļ™
## **1. ðŸĒ Core & Master Data Tables (āļ­āļ‡āļ„āđŒāļāļĢ, āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļŠāļąāļāļāļē)**
### 1.1 organization_roles
* * Purpose **: MASTER TABLE FOR organization role TYPES IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ----------- | ----------- | --------------------------- | ---------------------------------------------------------------- |
| id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR organization role | | role_name | VARCHAR(20) | NOT NULL,
UNIQUE | Role name (
CONTRACTOR,
THIRD PARTY
) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (role_name) ** Business Rules **: - Predefined system roles FOR organization TYPES - Cannot be deleted IF referenced by organizations ---
### 1.2 organizations
* * Purpose **: MASTER TABLE storing ALL organizations involved IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ----------------- | ------------ | ----------------------------------- | ---------------------------------------- |
| id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR organization | | organization_code | VARCHAR(20) | NOT NULL,
UNIQUE | Organization code (e.g., 'āļāļ—āļ—.', 'TEAM') | | organization_name | VARCHAR(255) | NOT NULL | FULL organization name | | is_active | BOOLEAN | DEFAULT TRUE | Active STATUS (1 = active, 0 = inactive) | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last
UPDATE timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (organization_code) - INDEX (is_active) ** Relationships **: - Referenced by: users,
project_organizations,
contract_organizations,
correspondences,
circulations ---
### 1.3 projects
* * Purpose **: MASTER TABLE FOR ALL projects IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ------------ | ------------ | --------------------------- | ----------------------------- |
| id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR project | | project_code | VARCHAR(50) | NOT NULL,
UNIQUE | Project code (e.g., 'LCBP3') | | project_name | VARCHAR(255) | NOT NULL | FULL project name | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
** INDEXES **: - PRIMARY KEY (id) - UNIQUE (project_code) - INDEX (is_active) ** Relationships **: - Referenced by: contracts,
correspondences,
document_number_formats,
drawings ---
### 1.4 contracts
* * Purpose **: MASTER TABLE FOR contracts within projects | COLUMN Name | Data TYPE | Constraints | Description | | ------------- | ------------ | ----------------------------------- | ------------------------------ |
| id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR contract | | project_id | INT | NOT NULL,
FK | Reference TO projects TABLE | | contract_code | VARCHAR(50) | NOT NULL,
UNIQUE | Contract code | | contract_name | VARCHAR(255) | NOT NULL | FULL contract name | | description | TEXT | NULL | Contract description | | start_date | DATE | NULL | Contract START date | | end_date | DATE | NULL | Contract
END date | | is_active | BOOLEAN | DEFAULT TRUE | Active STATUS | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last
UPDATE timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (contract_code) - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - INDEX (project_id, is_active) ** Relationships **: - Parent: projects - Referenced by: contract_organizations,
user_assignments ---
### 1.5 disciplines (NEW v1.5.1)
* * Purpose **: āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļēāļ‚āļēāļ‡āļēāļ™ (Disciplines) āđāļĒāļāļ•āļēāļĄāļŠāļąāļāļāļē (Req 6B) | COLUMN Name | Data TYPE | Constraints | Description | |: -------------- | :----------- | :----------- | :--------------------- |
| id | INT | PK,
AI | UNIQUE identifier | | contract_id | INT | FK,
NOT NULL | āļœāļđāļāļāļąāļšāļŠāļąāļāļāļē | | discipline_code | VARCHAR(10) | NOT NULL | āļĢāļŦāļąāļŠāļŠāļēāļ‚āļē (āđ€āļŠāđˆāļ™ GEN, STR) | | code_name_th | VARCHAR(255) | NULL | āļŠāļ·āđˆāļ­āđ„āļ—āļĒ | | code_name_en | VARCHAR(255) | NULL | āļŠāļ·āđˆāļ­āļ­āļąāļ‡āļāļĪāļĐ | | is_active | TINYINT(1) | DEFAULT 1 | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ | ** INDEXES **: - UNIQUE (contract_id, discipline_code) ---
## **2. ðŸ‘Ĩ Users & RBAC Tables (āļœāļđāđ‰āđƒāļŠāđ‰, āļŠāļīāļ—āļ˜āļīāđŒ, āļšāļ—āļšāļēāļ—)**
### 2.1 users
* * Purpose **: MASTER TABLE storing ALL system users | COLUMN Name | Data TYPE | Constraints | Description | | ----------------------- | ------------ | ----------------------------------- | -------------------------------- |
| user_id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR user | | username | VARCHAR(50) | NOT NULL,
UNIQUE | Login username | | password_hash | VARCHAR(255) | NOT NULL | Hashed PASSWORD (bcrypt) | | first_name | VARCHAR(50) | NULL | User 's first name |
| last_name | VARCHAR(50) | NULL | User' s last name | | email | VARCHAR(100) | NOT NULL,
UNIQUE | Email address | | line_id | VARCHAR(100) | NULL | LINE messenger ID | | primary_organization_id | INT | NULL,
FK | PRIMARY organization affiliation | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS | | failed_attempts | INT | DEFAULT 0 | Failed login attempts counter | | locked_until | DATETIME | NULL | Account LOCK expiration time | | last_login_at | TIMESTAMP | NULL | Last successful login timestamp | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last
UPDATE timestamp | | deleted_at | DATETIME | NULL | Deleted at | ** INDEXES **: - PRIMARY KEY (user_id) - UNIQUE (username) - UNIQUE (email) - FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE
SET NULL - INDEX (is_active) - INDEX (email) ** Relationships **: - Parent: organizations (primary_organization_id) - Referenced by: user_assignments,
audit_logs,
notifications,
circulation_routings ---
### 2.2 roles
* * Purpose **: MASTER TABLE defining system roles WITH scope levels | COLUMN Name | Data TYPE | Constraints | Description | | ----------- | ------------ | --------------------------- | ---------------------------------------------------- |
| role_id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR role | | role_name | VARCHAR(100) | NOT NULL | Role name (e.g., 'Superadmin', 'Document Control') | | scope | ENUM | NOT NULL | Scope LEVEL: GLOBAL,
Organization,
Project,
Contract | | description | TEXT | NULL | Role description | | is_system | BOOLEAN | DEFAULT FALSE | System role flag (cannot be deleted) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
** INDEXES **: - PRIMARY KEY (role_id) - INDEX (scope) ** Relationships **: - Referenced by: role_permissions,
user_assignments ---
### 2.3 permissions
* * Purpose **: MASTER TABLE defining ALL system permissions | COLUMN Name | Data TYPE | Constraints | Description | | --------------- | ------------ | --------------------------- | ------------------------------------------------------ |
| permission_id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier FOR permission | | permission_name | VARCHAR(100) | NOT NULL,
UNIQUE | Permission code (e.g., 'rfas.create', 'document.view') | | description | TEXT | NULL | Permission description | | module | VARCHAR(50) | NULL | Related module name | | scope_level | ENUM | NULL | Scope: GLOBAL,
ORG,
PROJECT | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
** INDEXES **: - PRIMARY KEY (permission_id) - UNIQUE (permission_name) - INDEX (module) - INDEX (scope_level) - INDEX (is_active) ** Relationships **: - Referenced by: role_permissions ---
### 2.4 role_permissions
* * Purpose **: Junction TABLE mapping roles TO permissions (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | ------------- | --------- | --------------- | ------------------------------ |
| role_id | INT | PRIMARY KEY,
FK | Reference TO roles TABLE | | permission_id | INT | PRIMARY KEY,
FK | Reference TO permissions TABLE | ** INDEXES **: - PRIMARY KEY (role_id, permission_id) - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE - FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE - INDEX (permission_id) ** Relationships **: - Parent: roles,
permissions ---
### 2.5 user_assignments
* * Purpose **: Junction TABLE assigning users TO roles WITH scope context | COLUMN Name | Data TYPE | Constraints | Description | | ------------------- | --------- | --------------------------- | ---------------------------------- |
| id | INT | PRIMARY KEY,
AUTO_INCREMENT | UNIQUE identifier | | user_id | INT | NOT NULL,
FK | Reference TO users TABLE | | role_id | INT | NOT NULL,
FK | Reference TO roles TABLE | | organization_id | INT | NULL,
FK | Organization scope (IF applicable) | | project_id | INT | NULL,
FK | Project scope (IF applicable) | | contract_id | INT | NULL,
FK | Contract scope (IF applicable) | | assigned_by_user_id | INT | NULL,
FK | User who made the assignment | | assigned_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Assignment timestamp | ** INDEXES **: - PRIMARY KEY (id) - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE - FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id) - INDEX (user_id, role_id) - INDEX (organization_id) - INDEX (project_id) - INDEX (contract_id) ** Relationships **: - Parent: users,
roles,
organizations,
projects,
contracts ---
### 2.6 project_organizations
* * Purpose **: Junction TABLE linking projects TO participating organizations (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | --------------- | --------- | --------------- | -------------------------------- |
| project_id | INT | PRIMARY KEY,
FK | Reference TO projects TABLE | | organization_id | INT | PRIMARY KEY,
FK | Reference TO organizations TABLE | ** INDEXES **: - PRIMARY KEY (project_id, organization_id) - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE - INDEX (organization_id) ** Relationships **: - Parent: projects,
organizations ---
### 2.7 contract_organizations
* * Purpose **: Junction TABLE linking contracts TO participating organizations WITH roles (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | ---------------- | ------------ | --------------- | ------------------------------------------------------------------------- |
| contract_id | INT | PRIMARY KEY,
FK | Reference TO contracts TABLE | | organization_id | INT | PRIMARY KEY,
FK | Reference TO organizations TABLE | | role_in_contract | VARCHAR(100) | NULL | Organization 's role in contract (Owner, Designer, Consultant, Contractor) |
**Indexes**:
* PRIMARY KEY (contract_id, organization_id)
* FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
* FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
* INDEX (organization_id)
* INDEX (role_in_contract)
**Relationships**:
* Parent: contracts, organizations
---
### 2.8 user_preferences (NEW v1.5.1)
**Purpose**: āđ€āļāđ‡āļšāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāđˆāļ§āļ™āļ•āļąāļ§āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ (Req 5.5, 6.8.3)
| Column Name | Data Type | Constraints | Description |
| :----------- | :---------- | :---------------- | :-------------- |
| user_id | INT | PK, FK | User ID |
| notify_email | BOOLEAN | DEFAULT TRUE | āļĢāļąāļšāļ­āļĩāđ€āļĄāļĨāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ |
| notify_line | BOOLEAN | DEFAULT TRUE | āļĢāļąāļšāđ„āļĨāļ™āđŒāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ |
| digest_mode | BOOLEAN | DEFAULT FALSE | āļĢāļąāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđāļšāļšāļĢāļ§āļĄ |
| ui_theme | VARCHAR(20) | DEFAULT ' light ' | UI Theme |
---
### 2.9 refresh_tokens (NEW v1.5.1)
**Purpose**: āđ€āļāđ‡āļš Refresh Tokens āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ—āļģ Authentication āđāļĨāļ° Token Rotation
| Column Name | Data Type | Constraints | Description |
| :---------------- | :----------- | :------------------------ | :------------------------------------ |
| token_id | INT | PK, AI | Unique Token ID |
| user_id | INT | FK, NOT NULL | āđ€āļˆāđ‰āļēāļ‚āļ­āļ‡ Token |
| token_hash | VARCHAR(255) | NOT NULL | Hash āļ‚āļ­āļ‡ Refresh Token (Security) |
| expires_at | DATETIME | NOT NULL | āļ§āļąāļ™āļŦāļĄāļ”āļ­āļēāļĒāļļāļ‚āļ­āļ‡ Token |
| is_revoked | BOOLEAN | DEFAULT FALSE | āļŠāļ–āļēāļ™āļ°āļ–āļđāļāļĒāļāđ€āļĨāļīāļ (True = āđƒāļŠāđ‰āļ‡āļēāļ™āđ„āļĄāđˆāđ„āļ”āđ‰) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ |
| replaced_by_token | VARCHAR(255) | NULL | Token āđƒāļŦāļĄāđˆāļ—āļĩāđˆāļĄāļēāđāļ—āļ™āļ—āļĩāđˆ (āļāļĢāļ“āļĩ Token Rotation) |
**Indexes**:
* PRIMARY KEY (token_id)
* FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
* INDEX (user_id)
**Relationships**:
* Parent: users
---
## **3. ✉ïļ Correspondences Tables (āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ, Revisions, Workflows)**
### 3.1 correspondence_types
**Purpose**: Master table for correspondence document types
| Column Name | Data Type | Constraints | Description |
| ----------- | ------------ | --------------------------- | --------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| type_code | VARCHAR(50) | NOT NULL, UNIQUE | Type code (e.g., ' RFA ', ' RFI ', ' TRANSMITTAL ') |
| type_name | VARCHAR(255) | NOT NULL | Full type name |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (type_code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* Referenced by: correspondences, document_number_formats, document_number_counters
---
### 3.2 correspondence_sub_types (NEW v1.5.1)
**Purpose**: āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļąāļ‡āļŠāļ·āļ­āļĒāđˆāļ­āļĒ (Sub Types) āļŠāļģāļŦāļĢāļąāļš Mapping āđ€āļĨāļ‚āļĢāļŦāļąāļŠ (Req 6B)
| Column Name | Data Type | Constraints | Description |
| :--------------------- | :----------- | :----------- | :------------------------ |
| id | INT | PK, AI | Unique identifier |
| contract_id | INT | FK, NOT NULL | āļœāļđāļāļāļąāļšāļŠāļąāļāļāļē |
| correspondence_type_id | INT | FK, NOT NULL | āļœāļđāļāļāļąāļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ |
| sub_type_code | VARCHAR(20) | NOT NULL | āļĢāļŦāļąāļŠāļĒāđˆāļ­āļĒ (āđ€āļŠāđˆāļ™ MAT, SHP) |
| sub_type_name | VARCHAR(255) | NULL | āļŠāļ·āđˆāļ­āļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļąāļ‡āļŠāļ·āļ­āļĒāđˆāļ­āļĒ |
| sub_type_number | VARCHAR(10) | NULL | āđ€āļĨāļ‚āļĢāļŦāļąāļŠāļŠāļģāļŦāļĢāļąāļš Running Number |
---
### 3.3 correspondences (UPDATE v1.7.0)
**Purpose**: Master table for correspondence documents (non-revisioned data)
| Column Name | Data Type | Constraints | Description |
| ------------------------- | ------------ | --------------------------- | ------------------------------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master correspondence ID |
| correspondence_number | VARCHAR(100) | NOT NULL | Document number (from numbering system) |
| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
| **discipline_id** | **INT** | **NULL, FK** | **[NEW] āļŠāļēāļ‚āļēāļ‡āļēāļ™ (āļ–āđ‰āļēāļĄāļĩ)** |
| is_internal_communication | TINYINT(1) | DEFAULT 0 | Internal (1) or external (0) communication |
| project_id | INT | NOT NULL, FK | Reference to projects table |
| originator_id | INT | NULL, FK | Originating organization |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| created_by | INT | NULL, FK | User who created the record |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT
* **FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE SET NULL**
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL
* FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
* UNIQUE KEY (project_id, correspondence_number)
* INDEX (correspondence_type_id)
* INDEX (originator_id)
* INDEX (deleted_at)
**Relationships**:
* Parent: correspondence_types, **disciplines**, projects, organizations, users
* Children: correspondence_revisions, correspondence_recipients, correspondence_tags, correspondence_references, correspondence_attachments, circulations, transmittals
---
### 3.4 correspondence_revisions (UPDATE v1.7.0)
**Purpose**: Child table storing revision history of correspondences (1:N)
| Column Name | Data Type | Constraints | Description |
| ------------------------ | ------------ | --------------------------------- | -------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
| title | VARCHAR(255) | NOT NULL | Document title |
| document_date | DATE | NULL | Document date |
| issued_date | DATETIME | NULL | Issue date |
| received_date | DATETIME | NULL | Received date |
| due_date | DATETIME | NULL | Due date for response |
| description | TEXT | NULL | Revision description |
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
| created_by | INT | NULL, FK | User who created revision |
| updated_by | INT | NULL, FK | User who last updated |
| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Project ID āļˆāļēāļ JSON details āđ€āļžāļ·āđˆāļ­āļ—āļģ Index |
| v_doc_subtype | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Type āļˆāļēāļ JSON details |
| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT
* FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
* FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL
* UNIQUE KEY (correspondence_id, revision_number)
* UNIQUE KEY (correspondence_id, is_current)
* INDEX (correspondence_status_id)
* INDEX (is_current)
* INDEX (document_date)
* INDEX (issued_date)
* INDEX (v_ref_project_id)
* INDEX (v_doc_subtype)
---
### 3.5 correspondence_recipients
**Purpose**: Junction table for correspondence recipients (TO/CC) (M:N)
| Column Name | Data Type | Constraints | Description |
| ------------------------- | -------------------- | --------------- | ---------------------------- |
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences |
| recipient_organization_id | INT | PRIMARY KEY, FK | Recipient organization |
| recipient_type | ENUM(' TO ', ' CC ') | PRIMARY KEY | Recipient type |
**Indexes**:
* PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type)
* FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE
* FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
* INDEX (recipient_organization_id)
* INDEX (recipient_type)
**Relationships**:
* Parent: correspondences, organizations
---
### 3.6 tags
**Purpose**: Master table for document tagging system
| Column Name | Data Type | Constraints | Description |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique tag ID |
| tag_name | VARCHAR(100) | NOT NULL, UNIQUE | Tag name |
| description | TEXT | NULL | Tag description |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (tag_name)
* INDEX (tag_name) - For autocomplete
**Relationships**:
* Referenced by: correspondence_tags
---
### 3.7 correspondence_tags
**Purpose**: Junction table linking correspondences to tags (M:N)
| Column Name | Data Type | Constraints | Description |
| ----------------- | --------- | --------------- | ---------------------------- |
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences |
| tag_id | INT | PRIMARY KEY, FK | Reference to tags |
**Indexes**:
* PRIMARY KEY (correspondence_id, tag_id)
* FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
* INDEX (tag_id)
**Relationships**:
* Parent: correspondences, tags
---
### 3.8 correspondence_references
**Purpose**: Junction table for cross-referencing correspondences (M:N)
| Column Name | Data Type | Constraints | Description |
| --------------------- | --------- | --------------- | ------------------------------------- |
| src_correspondence_id | INT | PRIMARY KEY, FK | Source correspondence ID |
| tgt_correspondence_id | INT | PRIMARY KEY, FK | Target (referenced) correspondence ID |
**Indexes**:
* PRIMARY KEY (src_correspondence_id, tgt_correspondence_id)
* FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* INDEX (tgt_correspondence_id)
**Relationships**:
* Parent: correspondences (both sides)
---
## **4. 📐 approval: RFA Tables (āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī, Workflows)**
### 4.1 rfa_types (UPDATE v1.7.0)
**Purpose**: Master table for RFA (Request for Approval) types
| Column Name | Data Type | Constraints | Description |
| :----------- | :----------- | :-------------------------- | :------------------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| contract_id | INT | NOT NULL, FK | Contract reference |
| type_code | VARCHAR(20) | NOT NULL | Type code (DWG, DOC, MAT, etc.) |
| type_name_th | VARCHAR(100) | NOT NULL | Full type name (TH) |
| type_name_en | VARCHAR(100) | NOT NULL | Full type name (EN) |
| remark | TEXT | NULL | Remark |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (contract_id, type_code)
* FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
* INDEX (is_active)
**Relationships**:
* Referenced by: rfas
---
### 4.2 rfa_status_codes
**Purpose**: Master table for RFA status codes
| Column Name | Data Type | Constraints | Description |
| ----------- | ------------ | --------------------------- | --------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| status_code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (DFT, FAP, FRE, etc.) |
| status_name | VARCHAR(100) | NOT NULL | Full status name |
| description | TEXT | NULL | Status description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (status_code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* Referenced by: rfa_revisions
---
### 4.3 rfa_approve_codes
**Purpose**: Master table for RFA approval result codes
| Column Name | Data Type | Constraints | Description |
| ------------ | ------------ | --------------------------- | -------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| approve_code | VARCHAR(20) | NOT NULL, UNIQUE | Approval code (1A, 1C, 3R, etc.) |
| approve_name | VARCHAR(100) | NOT NULL | Full approval name |
| description | TEXT | NULL | Code description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (approve_code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* Referenced by: rfa_revisions
---
### 4.4 rfas (UPDATE v1.7.0)
**Purpose**: Master table for RFA documents (non-revisioned data)
| Column Name | Data Type | Constraints | Description |
| :---------- | :-------- | :------------------------ | :------------------------------------------ |
| id | INT | PK, FK | Master RFA ID (Shared with correspondences) |
| rfa_type_id | INT | NOT NULL, FK | Reference to rfa_types |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| created_by | INT | NULL, FK | User who created the record |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id)
* FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
* INDEX (rfa_type_id)
* INDEX (deleted_at)
**Relationships**:
* Parent: correspondences, rfa_types, users
* Children: rfa_revisions
---
### 4.5 rfa_revisions (UPDATE v1.7.0)
**Purpose**: Child table storing revision history of RFAs (1:N)
| Column Name | Data Type | Constraints | Description |
| ----------- | --------- | --------------------------- | ------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| rfa_id | INT | NOT NULL, FK | Master RFA ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
| title | VARCHAR(255) | NOT NULL | RFA title |
| document_date | DATE | NULL | Document date |
| issued_date | DATE | NULL | Issue date for approval |
| received_date | DATETIME | NULL | Received date |
| approved_date | DATE | NULL | Approval date |
| description | TEXT | NULL | Revision description |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
| created_by | INT | NULL, FK | User who created revision |
| updated_by | INT | NULL, FK | User who last updated |
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
| v_ref_drawing_count | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Drawing Count āļˆāļēāļ JSON details āđ€āļžāļ·āđˆāļ­āļ—āļģ Index |
| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE
* FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id)
* FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL
* FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
* FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL
* UNIQUE KEY (rfa_id, revision_number)
* UNIQUE KEY (rfa_id, is_current)
* INDEX (rfa_status_code_id)
* INDEX (rfa_approve_code_id)
* INDEX (is_current)
* INDEX (v_ref_drawing_count): āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āļāļēāļĢ Index āļ‚āđ‰āļ­āļĄāļđāļĨāļ•āļąāļ§āđ€āļĨāļ‚āđƒāļ™ JSON
**Relationships**:
* Parent: correspondences, rfas, rfa_status_codes, rfa_approve_codes, users
* Children: rfa_items
---
### 4.6 rfa_items
**Purpose**: Junction table linking RFA revisions to shop drawing revisions (M:N)
| Column Name | Data Type | Constraints | Description |
| :----------------------- | :-------- | :-------------- | :----------------------- |
| rfa_revision_id | INT | PRIMARY KEY, FK | RFA Revision ID |
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Shop drawing revision ID |
**Indexes**:
* PRIMARY KEY (rfa_revision_id, shop_drawing_revision_id)
* FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE
* FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
* INDEX (shop_drawing_revision_id)
**Relationships**:
* Parent: rfa_revisions, shop_drawing_revisions
**Business Rules**:
* Used primarily for RFA type = ' DWG ' (Shop Drawing)
* One RFA can contain multiple shop drawings
* One shop drawing can be referenced by multiple RFAs
---
---
## **5. 📐 Drawings Tables (āđāļšāļš, āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆ)**
### 5.1 contract_drawing_volumes
**Purpose**: Master table for contract drawing volume classification
| Column Name | Data Type | Constraints | Description |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique volume ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| volume_code | VARCHAR(50) | NOT NULL | Volume code |
| volume_name | VARCHAR(255) | NOT NULL | Volume name |
| description | TEXT | NULL | Volume description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* UNIQUE KEY (project_id, volume_code)
* INDEX (sort_order)
**Relationships**:
* Parent: projects
* Referenced by: contract_drawings
**Business Rules**:
* Volume codes must be unique within a project
* Used for organizing large sets of contract drawings
---
### 5.2 contract_drawing_cats
**Purpose**: Master table for contract drawing main categories
| Column Name | Data Type | Constraints | Description |
| ----------- | ------------ | ----------------------------------- | ------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| cat_code | VARCHAR(50) | NOT NULL | Category code |
| cat_name | VARCHAR(255) | NOT NULL | Category name |
| description | TEXT | NULL | Category description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* UNIQUE KEY (project_id, cat_code)
* INDEX (sort_order)
**Relationships**:
* Parent: projects
* Referenced by: contract_drawing_subcat_cat_maps
**Business Rules**:
* Category codes must be unique within a project
* Hierarchical relationship with sub-categories via mapping table
---
### 5.3 contract_drawing_sub_cats
**Purpose**: Master table for contract drawing sub-categories
| Column Name | Data Type | Constraints | Description |
| ------------ | ------------ | ----------------------------------- | ------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| sub_cat_code | VARCHAR(50) | NOT NULL | Sub-category code |
| sub_cat_name | VARCHAR(255) | NOT NULL | Sub-category name |
| description | TEXT | NULL | Sub-category description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* UNIQUE KEY (project_id, sub_cat_code)
* INDEX (sort_order)
**Relationships**:
* Parent: projects
* Referenced by: contract_drawings, contract_drawing_subcat_cat_maps
**Business Rules**:
* Sub-category codes must be unique within a project
* Can be mapped to multiple main categories via mapping table
---
### 5.4 contract_drawing_subcat_cat_maps (UPDATE v1.7.0)
**Purpose**: Junction table mapping sub-categories to main categories (M:N)
| Column Name | Data Type | Constraints | Description |
| ----------- | --------- | ------------------------------- | -------------------------- |
| **id** | **INT** | **PRIMARY KEY, AUTO_INCREMENT** | **Unique mapping ID** |
| project_id | INT | NOT NULL, FK | Reference to projects |
| sub_cat_id | INT | NOT NULL, FK | Reference to sub-category |
| cat_id | INT | NOT NULL, FK | Reference to main category |
**Indexes**:
* PRIMARY KEY (id)
* **UNIQUE KEY (project_id, sub_cat_id, cat_id)**
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE
* FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE
* INDEX (sub_cat_id)
* INDEX (cat_id)
**Relationships**:
* Parent: projects, contract_drawing_sub_cats, contract_drawing_cats
* Referenced by: contract_drawings
**Business Rules**:
* Allows flexible categorization
* One sub-category can belong to multiple main categories
* Composite uniqueness enforced via UNIQUE constraint
---
### 5.5 contract_drawings (UPDATE v1.7.0)
**Purpose**: Master table for contract drawings (from contract specifications)
| Column Name | Data Type | Constraints | Description |
| --------------- | ------------ | ----------------------------------- | ---------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| condwg_no | VARCHAR(255) | NOT NULL | Contract drawing number |
| title | VARCHAR(255) | NOT NULL | Drawing title |
| **map_cat_id** | **INT** | **NULL, FK** | **[CHANGED] Reference to mapping table** |
| volume_id | INT | NULL, FK | Reference to volume |
| **volume_page** | **INT** | **NULL** | **[NEW] Page number within volume** |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
| updated_by | INT | NULL, FK | User who last updated |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* **FOREIGN KEY (map_cat_id) REFERENCES contract_drawing_subcat_cat_maps(id) ON DELETE RESTRICT**
* FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT
* FOREIGN KEY (updated_by) REFERENCES users(user_id)
* UNIQUE KEY (project_id, condwg_no)
* INDEX (map_cat_id)
* INDEX (volume_id)
* INDEX (deleted_at)
**Relationships**:
* Parent: projects, contract_drawing_subcat_cat_maps, contract_drawing_volumes, users
* Referenced by: shop_drawing_revision_contract_refs, contract_drawing_attachments
**Business Rules**:
* Drawing numbers must be unique within a project
* Represents baseline/contract drawings
* Referenced by shop drawings for compliance tracking
* Soft delete preserves history
* **map_cat_id references the mapping table for flexible categorization**
---
### 5.6 shop_drawing_main_categories (UPDATE v1.7.0)
**Purpose**: Master table for shop drawing main categories (discipline-level)
| Column Name | Data Type | Constraints | Description |
| ------------------ | ------------ | ----------------------------------- | ------------------------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID |
| **project_id** | **INT** | **NOT NULL, FK** | **[NEW] Reference to projects** |
| main_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Category code (ARCH, STR, MEP, etc.) |
| main_category_name | VARCHAR(255) | NOT NULL | Category name |
| description | TEXT | NULL | Category description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* **FOREIGN KEY (project_id) REFERENCES projects(id)**
* UNIQUE (main_category_code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* **Parent: projects**
* Referenced by: shop_drawings, asbuilt_drawings
**Business Rules**:
* **[CHANGED] Project-specific categories (was global)**
* Typically represents engineering disciplines
---
### 5.7 shop_drawing_sub_categories (UPDATE v1.7.0)
**Purpose**: Master table for shop drawing sub-categories (component-level)
| Column Name | Data Type | Constraints | Description |
| ----------------- | ------------ | ----------------------------------- | ----------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID |
| **project_id** | **INT** | **NOT NULL, FK** | **[NEW] Reference to projects** |
| sub_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Sub-category code (STR-COLUMN, ARCH-DOOR, etc.) |
| sub_category_name | VARCHAR(255) | NOT NULL | Sub-category name |
| description | TEXT | NULL | Sub-category description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* **FOREIGN KEY (project_id) REFERENCES projects(id)**
* UNIQUE (sub_category_code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* **Parent: projects**
* Referenced by: shop_drawings, asbuilt_drawings
**Business Rules**:
* **[CHANGED] Project-specific sub-categories (was global)**
* **[REMOVED] No longer hierarchical under main categories**
* Represents specific drawing types or components
---
### 5.8 shop_drawings (UPDATE v1.7.0)
**Purpose**: Master table for shop drawings (contractor-submitted)
| Column Name | Data Type | Constraints | Description |
| ---------------- | ------------ | ----------------------------------- | -------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | Shop drawing number |
| main_category_id | INT | NOT NULL, FK | Reference to main category |
| sub_category_id | INT | NOT NULL, FK | Reference to sub-category |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
| updated_by | INT | NULL, FK | User who last updated |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (drawing_number)
* FOREIGN KEY (project_id) REFERENCES projects(id)
* FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
* FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id)
* FOREIGN KEY (updated_by) REFERENCES users(user_id)
* INDEX (project_id)
* INDEX (main_category_id)
* INDEX (sub_category_id)
* INDEX (deleted_at)
**Relationships**:
* Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users
* Children: shop_drawing_revisions
**Business Rules**:
* Drawing numbers are globally unique across all projects
* Represents contractor shop drawings
* Can have multiple revisions
* Soft delete preserves history
* **[CHANGED] Title moved to shop_drawing_revisions table**
---
### 5.9 shop_drawing_revisions (UPDATE v1.7.0)
**Purpose**: Child table storing revision history of shop drawings (1:N)
| Column Name | Data Type | Constraints | Description |
| ------------------------- | ---------------- | --------------------------- | ---------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| shop_drawing_id | INT | NOT NULL, FK | Master shop drawing ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) |
| revision_date | DATE | NULL | Revision date |
| **title** | **VARCHAR(500)** | **NOT NULL** | **[NEW] Drawing title** |
| description | TEXT | NULL | Revision description/changes |
| **legacy_drawing_number** | **VARCHAR(100)** | **NULL** | **[NEW] Original/legacy drawing number** |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE
* UNIQUE KEY (shop_drawing_id, revision_number)
* INDEX (revision_date)
**Relationships**:
* Parent: shop_drawings
* Referenced by: rfa_items, shop_drawing_revision_contract_refs, shop_drawing_revision_attachments, asbuilt_revision_shop_revisions_refs
**Business Rules**:
* Revision numbers are sequential starting from 0
* Each revision can reference multiple contract drawings
* Each revision can have multiple file attachments
* Linked to RFAs for approval tracking
* **[NEW] Title stored at revision level for version-specific naming**
* **[NEW] legacy_drawing_number supports data migration from old systems**
---
### 5.10 shop_drawing_revision_contract_refs
**Purpose**: Junction table linking shop drawing revisions to referenced contract drawings (M:N)
| Column Name | Data Type | Constraints | Description |
| ------------------------ | --------- | --------------- | ---------------------------------- |
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing |
**Indexes**:
* PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id)
* FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
* FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE
* INDEX (contract_drawing_id)
**Relationships**:
* Parent: shop_drawing_revisions, contract_drawings
**Business Rules**:
* Tracks which contract drawings each shop drawing revision is based on
* Ensures compliance with contract specifications
* One shop drawing revision can reference multiple contract drawings
---
### 5.11 asbuilt_drawings (NEW v1.7.0)
**Purpose**: Master table for AS Built drawings (final construction records)
| Column Name | Data Type | Constraints | Description |
| ---------------- | ------------ | ----------------------------------- | -------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | AS Built drawing number |
| main_category_id | INT | NOT NULL, FK | Reference to main category |
| sub_category_id | INT | NOT NULL, FK | Reference to sub-category |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
| updated_by | INT | NULL, FK | User who last updated |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (drawing_number)
* FOREIGN KEY (project_id) REFERENCES projects(id)
* FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
* FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id)
* FOREIGN KEY (updated_by) REFERENCES users(user_id)
* INDEX (project_id)
* INDEX (main_category_id)
* INDEX (sub_category_id)
* INDEX (deleted_at)
**Relationships**:
* Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users
* Children: asbuilt_drawing_revisions
**Business Rules**:
* Drawing numbers are globally unique across all projects
* Represents final as-built construction drawings
* Can have multiple revisions
* Soft delete preserves history
* Uses same category structure as shop drawings
---
### 5.12 asbuilt_drawing_revisions (NEW v1.7.0)
**Purpose**: Child table storing revision history of AS Built drawings (1:N)
| Column Name | Data Type | Constraints | Description |
| --------------------- | ------------ | --------------------------- | ------------------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| asbuilt_drawing_id | INT | NOT NULL, FK | Master AS Built drawing ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) |
| revision_date | DATE | NULL | Revision date |
| title | VARCHAR(500) | NOT NULL | Drawing title |
| description | TEXT | NULL | Revision description/changes |
| legacy_drawing_number | VARCHAR(100) | NULL | Original/legacy drawing number |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (asbuilt_drawing_id) REFERENCES asbuilt_drawings(id) ON DELETE CASCADE
* UNIQUE KEY (asbuilt_drawing_id, revision_number)
* INDEX (revision_date)
**Relationships**:
* Parent: asbuilt_drawings
* Referenced by: asbuilt_revision_shop_revisions_refs, asbuilt_drawing_revision_attachments
**Business Rules**:
* Revision numbers are sequential starting from 0
* Each revision can reference multiple shop drawing revisions
* Each revision can have multiple file attachments
* Title stored at revision level for version-specific naming
* legacy_drawing_number supports data migration from old systems
---
### 5.13 asbuilt_revision_shop_revisions_refs (NEW v1.7.0)
**Purpose**: Junction table linking AS Built drawing revisions to shop drawing revisions (M:N)
| Column Name | Data Type | Constraints | Description |
| --------------------------- | --------- | --------------- | ---------------------------------- |
| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
**Indexes**:
* PRIMARY KEY (asbuilt_drawing_revision_id, shop_drawing_revision_id)
* FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
* FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
* INDEX (shop_drawing_revision_id)
**Relationships**:
* Parent: asbuilt_drawing_revisions, shop_drawing_revisions
**Business Rules**:
* Tracks which shop drawings each AS Built drawing revision is based on
* Maintains construction document lineage
* One AS Built revision can reference multiple shop drawing revisions
* Supports traceability from final construction to approved shop drawings
---
### 5.14 asbuilt_drawing_revision_attachments (NEW v1.7.0)
**Purpose**: Junction table linking AS Built drawing revisions to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
| --------------------------- | ------------------------------------- | --------------- | ------------------------------------- |
| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachment file |
| file_type | ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') | NULL | File type classification |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main document flag (1 = primary file) |
**Indexes**:
* PRIMARY KEY (asbuilt_drawing_revision_id, attachment_id)
* FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
* INDEX (attachment_id)
**Relationships**:
* Parent: asbuilt_drawing_revisions, attachments
**Business Rules**:
* Each AS Built revision can have multiple file attachments
* File types: PDF (documents), DWG (CAD files), SOURCE (source files), OTHER (miscellaneous)
* One attachment can be marked as main document per revision
* Cascade delete when revision is deleted
---
## **6. 🔄 Circulations Tables (āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™)**
### 6.1 circulation_status_codes
**Purpose**: Master table for circulation workflow status codes
| Column Name | Data Type | Constraints | Description |
| ----------- | ----------- | --------------------------- | --------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique status ID |
| code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (OPEN, IN_REVIEW, COMPLETED, CANCELLED) |
| description | VARCHAR(50) | NOT NULL | Status description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (code)
* INDEX (is_active)
* INDEX (sort_order)
**Relationships**:
* Referenced by: circulations
**Seed Data**: 4 status codes
* OPEN: Initial status when created
* IN_REVIEW: Under review by recipients
* COMPLETED: All recipients have responded
* CANCELLED: Withdrawn/cancelled
---
### 6.2 circulations
**Purpose**: Master table for internal circulation sheets (document routing)
| Column Name | Data Type | Constraints | Description |
| ----------------------- | ------------ | ----------------------------------- | ----------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique circulation ID |
| correspondence_id | INT | UNIQUE, FK | Link to correspondence (1:1 relationship) |
| organization_id | INT | NOT NULL, FK | Organization that owns this circulation |
| circulation_no | VARCHAR(100) | NOT NULL | Circulation sheet number |
| circulation_subject | VARCHAR(500) | NOT NULL | Subject/title |
| circulation_status_code | VARCHAR(20) | NOT NULL, FK | Current status code |
| created_by_user_id | INT | NOT NULL, FK | User who created circulation |
| submitted_at | TIMESTAMP | NULL | Submission timestamp |
| closed_at | TIMESTAMP | NULL | Closure timestamp |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE (correspondence_id)
* FOREIGN KEY (correspondence_id) REFERENCES correspondences(id)
* FOREIGN KEY (organization_id) REFERENCES organizations(id)
* FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code)
* FOREIGN KEY (created_by_user_id) REFERENCES users(user_id)
* INDEX (organization_id)
* INDEX (circulation_status_code)
* INDEX (created_by_user_id)
**Relationships**:
* Parent: correspondences, organizations, circulation_status_codes, users
* Children: circulation_routings, circulation_attachments
**Business Rules**:
* Internal document routing within organization
* One-to-one relationship with correspondences
* Tracks document review/approval workflow
* Status progression: OPEN → IN_REVIEW → COMPLETED/CANCELLED
---
## **7. ðŸ“Ī Transmittals Tables (āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡)**
### 7.1 transmittals
**Purpose**: Child table for transmittal-specific data (1:1 with correspondences)
| Column Name | Data Type | Constraints | Description |
| ----------------- | --------- | --------------- | --------------------------------------------------------- |
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences (1:1) |
| purpose | ENUM | NULL | Purpose: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW, OTHER |
| remarks | TEXT | NULL | Additional remarks |
**Indexes**:
* PRIMARY KEY (correspondence_id)
* FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* INDEX (purpose)
**Relationships**:
* Parent: correspondences
* Children: transmittal_items
**Business Rules**:
* One-to-one relationship with correspondences
* Transmittal is a correspondence type for forwarding documents
* Contains metadata about the transmission
---
### 7.2 transmittal_items
**Purpose**: Junction table listing documents included in transmittal (M:N)
| Column Name | Data Type | Constraints | Description |
| ---------------------- | ------------ | --------------------------- | --------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique item ID |
| transmittal_id | INT | NOT NULL, FK | Reference to transmittal |
| item_correspondence_id | INT | NOT NULL, FK | Reference to document being transmitted |
| quantity | INT | DEFAULT 1 | Number of copies |
| remarks | VARCHAR(255) | NULL | Item-specific remarks |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE
* FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* UNIQUE KEY (transmittal_id, item_correspondence_id)
* INDEX (item_correspondence_id)
**Relationships**:
* Parent: transmittals, correspondences
**Business Rules**:
* One transmittal can contain multiple documents
* Tracks quantity of physical copies (if applicable)
* Links to any type of correspondence document
---
## **8. 📎 File Management Tables (āđ„āļŸāļĨāđŒāđāļ™āļš)**
### 8.1 attachments
**Purpose**: Central repository for all file attachments in the system
| Column Name | Data Type | Constraints | Description |
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) |
| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) |
| file_size | INT | NOT NULL | File size in bytes |
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
| is_temporary | BOOLEAN | DEFAULT TRUE | āļĢāļ°āļšāļļāļ§āđˆāļēāđ€āļ›āđ‡āļ™āđ„āļŸāļĨāđŒāļŠāļąāđˆāļ§āļ„āļĢāļēāļ§ (āļĒāļąāļ‡āđ„āļĄāđˆāđ„āļ”āđ‰ Commit) |
| temp_id\* | VARCHAR(100) | NULL | ID āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ•āļ­āļ™ Upload Phase 1 (āļ­āļēāļˆāđƒāļŠāđ‰āļĢāđˆāļ§āļĄāļāļąāļš id āļŦāļĢāļ·āļ­āđāļĒāļāļāđ‡āđ„āļ”āđ‰) |
| expires_at | DATETIME | NULL | āđ€āļ§āļĨāļēāļŦāļĄāļ”āļ­āļēāļĒāļļāļ‚āļ­āļ‡āđ„āļŸāļĨāđŒ Temp (āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰ Cron Job āļĨāļšāļ­āļ­āļ) |
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum āļŠāļģāļŦāļĢāļąāļš Verify File Integrity [Req 3.9.3] |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE
* INDEX (stored_filename)
* INDEX (mime_type)
* INDEX (uploaded_by_user_id)
* INDEX (created_at)
**Relationships**:
* Parent: users
* Referenced by: correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments, contract_drawing_attachments
**Business Rules**:
* Central storage prevents file duplication
* Stored filename prevents naming conflicts
* File path points to QNAP NAS storage
* Original filename preserved for download
* One file record can be linked to multiple documents
---
### 8.2 correspondence_attachments
**Purpose**: Junction table linking correspondences to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
| ----------------- | --------- | --------------- | ---------------------------- |
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
**Indexes**:
* PRIMARY KEY (correspondence_id, attachment_id)
* FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
* INDEX (attachment_id)
* INDEX (is_main_document)
**Relationships**:
* Parent: correspondences, attachments
**Business Rules**:
* One correspondence can have multiple attachments
* One attachment can be linked to multiple correspondences
* is_main_document identifies primary file (typically PDF)
---
### 8.3 circulation_attachments
**Purpose**: Junction table linking circulations to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
| ---------------- | --------- | --------------- | -------------------------- |
| circulation_id | INT | PRIMARY KEY, FK | Reference to circulations |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
**Indexes**:
* PRIMARY KEY (circulation_id, attachment_id)
* FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE
* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
* INDEX (attachment_id)
* INDEX (is_main_document)
**Relationships**:
* Parent: circulations, attachments
---
### 8.4 shop_drawing_revision_attachments
**Purpose**: Junction table linking shop drawing revisions to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
| ------------------------ | --------- | --------------- | ---------------------------------- |
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
**Indexes**:
* PRIMARY KEY (shop_drawing_revision_id, attachment_id)
* FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
* INDEX (attachment_id)
* INDEX (file_type)
* INDEX (is_main_document)
**Relationships**:
* Parent: shop_drawing_revisions, attachments
**Business Rules**:
* file_type categorizes drawing file formats
* Typically includes PDF for viewing and DWG for editing
* SOURCE may include native CAD files
---
### 8.5 contract_drawing_attachments
**Purpose**: Junction table linking contract drawings to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
| ------------------- | --------- | --------------- | ---------------------------------- |
| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
**Indexes**:
* PRIMARY KEY (contract_drawing_id, attachment_id)
* FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE
* FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
* INDEX (attachment_id)
* INDEX (file_type)
* INDEX (is_main_document)
**Relationships**:
* Parent: contract_drawings, attachments
---
## **9. ðŸ”Ē Document Numbering System Tables (āļĢāļ°āļšāļšāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ)**
### 9.1 document_number_formats
**Purpose**: Master table defining numbering formats for each document type
| Column Name | Data Type | Constraints | Description |
| ---------------------- | ------------ | --------------------------- | -------------------------------------------- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique format ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
| format_string | VARCHAR(100) | NOT NULL | Format pattern (e.g., {ORG}-{TYPE}-{YYYY}-#) |
| description | TEXT | NULL | Format description |
| reset_annually | BOOLEAN | DEFAULT TRUE | Start sequence new every year |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
* UNIQUE KEY (project_id, correspondence_type_id)
* INDEX (is_active)
**Relationships**:
* Parent: projects, correspondence_types
**Business Rules**:
* Defines how document numbers are constructed
* Supports placeholders: {PROJ}, {ORG}, {TYPE}, {YYYY}, {MM}, {#}
---
### 9.2 document_number_counters (UPDATE v1.7.0)
**Purpose**: Transaction table tracking running numbers (High Concurrency)
| Column Name | Data Type | Constraints | Description |
| -------------------------- | ----------- | ------------- | ----------------------------------------------- |
| project_id | INT | PK, NOT NULL | āđ‚āļ„āļĢāļ‡āļāļēāļĢ |
| originator_organization_id | INT | PK, NOT NULL | āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡ |
| recipient_organization_id | INT | PK, NOT NULL | āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļš (0 = no recipient / RFA) |
| correspondence_type_id | INT | PK, NULL | āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (NULL = default) |
| sub_type_id | INT | PK, DEFAULT 0 | āļ›āļĢāļ°āđ€āļ āļ—āļĒāđˆāļ­āļĒ āļŠāļģāļŦāļĢāļąāļš TRANSMITTAL (0 = āđ„āļĄāđˆāļĢāļ°āļšāļļ) |
| rfa_type_id | INT | PK, DEFAULT 0 | āļ›āļĢāļ°āđ€āļ āļ— RFA (0 = āđ„āļĄāđˆāđƒāļŠāđˆ RFA) |
| discipline_id | INT | PK, DEFAULT 0 | āļŠāļēāļ‚āļēāļ‡āļēāļ™ (0 = āđ„āļĄāđˆāļĢāļ°āļšāļļ) |
| reset_scope | VARCHAR(20) | PK, NOT NULL | Scope of reset (YEAR_2024, MONTH_2024_01, NONE) |
| last_number | INT | DEFAULT 0 | āđ€āļĨāļ‚āļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆāļ–āļđāļāđƒāļŠāđ‰āļ‡āļēāļ™āđ„āļ›āđāļĨāđ‰āļ§ |
| version | INT | DEFAULT 0 | Optimistic Lock Version |
| updated_at | DATETIME(6) | ON UPDATE | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ” |
**Indexes**:
* **PRIMARY KEY (project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)**
* INDEX idx_counter_lookup (project_id, correspondence_type_id, reset_scope)
* INDEX idx_counter_org (originator_organization_id, reset_scope)
**Business Rules**:
* **Composite Primary Key 8 Columns**: āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļĢāļąāļ™āđ€āļĨāļ‚āļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ° Reset Scope āļ—āļĩāđˆāļŦāļĨāļēāļāļŦāļĨāļēāļĒ
* **Concurrency Control**: āđƒāļŠāđ‰ Redis Lock āļŦāļĢāļ·āļ­ Optimistic Locking (version)
* **Reset Scope**: āđƒāļŠāđ‰ Field `reset_scope` āļ„āļ§āļšāļ„āļļāļĄāļāļēāļĢ Reset āđāļ—āļ™ `current_year` āđāļšāļšāđ€āļ”āļīāļĄ
---
### 9.3 document_number_audit (UPDATE v1.7.0)
**Purpose**: Audit log for document number generation (Debugging & Tracking)
| Column Name | Data Type | Constraints | Description |
| :------------------------- | :----------- | :----------------- | :-------------------------------------- |
| id | INT | PK, AI | ID āļ‚āļ­āļ‡ audit record |
| document_id | INT | NULL, FK | ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆ (NULL if failed) |
| document_type | VARCHAR(50) | NULL | āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ |
| document_number | VARCHAR(100) | NOT NULL | āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ (āļœāļĨāļĨāļąāļžāļ˜āđŒ) |
| operation | ENUM | DEFAULT 'CONFIRM' | RESERVE, CONFIRM, MANUAL_OVERRIDE, etc. |
| status | ENUM | DEFAULT 'RESERVED' | RESERVED, CONFIRMED, CANCELLED, VOID |
| counter_key | JSON | NOT NULL | Counter key āļ—āļĩāđˆāđƒāļŠāđ‰ (JSON 8 fields) |
| reservation_token | VARCHAR(36) | NULL | Token āļāļēāļĢāļˆāļ­āļ‡ |
| idempotency_key | VARCHAR(128) | NULL | Idempotency Key from request |
| originator_organization_id | INT | NULL | āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡ |
| recipient_organization_id | INT | NULL | āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļš |
| template_used | VARCHAR(200) | NOT NULL | Template āļ—āļĩāđˆāđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ |
| old_value | TEXT | NULL | Previous value |
| new_value | TEXT | NULL | New value |
| user_id | INT | NULL, FK | āļœāļđāđ‰āļ‚āļ­āļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆ |
| is_success | BOOLEAN | DEFAULT TRUE | āļŠāļ–āļēāļ™āļ°āļ„āļ§āļēāļĄāļŠāļģāđ€āļĢāđ‡āļˆ |
| created_at | TIMESTAMP | DEFAULT NOW | āļ§āļąāļ™āļ—āļĩāđˆ/āđ€āļ§āļĨāļēāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ |
| total_duration_ms | INT | NULL | āđ€āļ§āļĨāļēāļĢāļ§āļĄāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ (ms) |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (document_id) REFERENCES correspondences(id) ON DELETE CASCADE
* FOREIGN KEY (user_id) REFERENCES users(user_id)
* INDEX (document_id)
* INDEX (user_id)
* INDEX (status)
* INDEX (operation)
* INDEX (document_number)
* INDEX (reservation_token)
* INDEX (created_at)
---
### 9.4 document_number_errors (UPDATE v1.7.0)
**Purpose**: Error log for failed document number generation
| Column Name | Data Type | Constraints | Description |
| :------------ | :-------- | :---------- | :--------------------------------------------- |
| id | INT | PK, AI | ID āļ‚āļ­āļ‡ error record |
| error_type | ENUM | NOT NULL | LOCK_TIMEOUT, VERSION_CONFLICT, DB_ERROR, etc. |
| error_message | TEXT | NULL | āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ error |
| stack_trace | TEXT | NULL | Stack trace āļŠāļģāļŦāļĢāļąāļš debugging |
| context_data | JSON | NULL | Context āļ‚āļ­āļ‡ request |
| user_id | INT | NULL | āļœāļđāđ‰āļ—āļĩāđˆāđ€āļāļīāļ” error |
| created_at | TIMESTAMP | DEFAULT NOW | āļ§āļąāļ™āļ—āļĩāđˆāđ€āļāļīāļ” error |
| resolved_at | TIMESTAMP | NULL | āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āđāļĨāđ‰āļ§ |
**Indexes**:
* PRIMARY KEY (id)
* INDEX (error_type)
* INDEX (created_at)
* INDEX (user_id)
* INDEX (resolved_at)
---
### 9.5 document_number_reservations (NEW v1.7.0)
**Purpose**: Two-Phase Commit table for document number reservation
| Column Name | Data Type | Constraints | Description |
| :--------------------- | :----------- | :--------------- | :----------------------------------- |
| id | INT | PK, AI | Unique ID |
| token | VARCHAR(36) | UNIQUE, NOT NULL | UUID v4 Reservation Token |
| document_number | VARCHAR(100) | UNIQUE, NOT NULL | āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļˆāļ­āļ‡ |
| document_number_status | ENUM | DEFAULT RESERVED | RESERVED, CONFIRMED, CANCELLED, VOID |
| document_id | INT | NULL, FK | ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (āđ€āļĄāļ·āđˆāļ­ Confirm āđāļĨāđ‰āļ§) |
| expires_at | DATETIME(6) | NOT NULL | āđ€āļ§āļĨāļēāļŦāļĄāļ”āļ­āļēāļĒāļļāļāļēāļĢāļˆāļ­āļ‡ |
| project_id | INT | NOT NULL, FK | Project Context |
| user_id | INT | NOT NULL, FK | User Context |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (document_id) REFERENCES correspondence_revisions(id) ON DELETE SET NULL
* FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
* FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
* FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
* INDEX idx_token (token)
* INDEX idx_status (document_number_status)
* INDEX idx_status_expires (document_number_status, expires_at)
* INDEX idx_document_id (document_id)
* INDEX idx_user_id (user_id)
* INDEX idx_reserved_at (reserved_at)
---
## **10. ⚙ïļ Unified Workflow Engine Tables (UPDATE v1.7.0)**
### 10.1 workflow_definitions
**Purpose**: āđ€āļāđ‡āļšāđāļĄāđˆāđāļšāļš (Template) āļ‚āļ­āļ‡ Workflow (Definition / DSL)
| Column Name | Data Type | Constraints | Description |
| :------------ | :---------- | :----------- | :------------------------------------- |
| id | CHAR(36) | PK, UUID | Unique Workflow Definition ID |
| workflow_code | VARCHAR(50) | NOT NULL | āļĢāļŦāļąāļŠ Workflow (āđ€āļŠāđˆāļ™ RFA_FLOW_V1) |
| version | INT | DEFAULT 1 | āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version |
| description | TEXT | NULL | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ Workflow |
| dsl | JSON | NOT NULL | āļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļš (YAML/JSON Format) |
| compiled | JSON | NOT NULL | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Execution Tree āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§ |
| is_active | BOOLEAN | DEFAULT TRUE | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ |
| created_at | TIMESTAMP | DEFAULT NOW | āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ |
| updated_at | TIMESTAMP | ON UPDATE | āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ” |
**Indexes**:
* PRIMARY KEY (id)
* UNIQUE KEY (workflow_code, version)
* INDEX (is_active)
---
### 10.2 workflow_instances
**Purpose**: āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡ Workflow āļ—āļĩāđˆāļāļģāļĨāļąāļ‡āļĢāļąāļ™āļ­āļĒāļđāđˆāļˆāļĢāļīāļ‡ (Runtime)
| Column Name | Data Type | Constraints | Description |
| :------------ | :---------- | :--------------- | :--------------------------------------------- |
| id | CHAR(36) | PK, UUID | Unique Instance ID |
| definition_id | CHAR(36) | FK, NOT NULL | āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Definition āļ—āļĩāđˆāđƒāļŠāđ‰ |
| entity_type | VARCHAR(50) | NOT NULL | āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (rfa_revision, correspondence...) |
| entity_id | VARCHAR(50) | NOT NULL | ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ |
| current_state | VARCHAR(50) | NOT NULL | āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™ |
| status | ENUM | DEFAULT 'ACTIVE' | ACTIVE, COMPLETED, CANCELLED, TERMINATED |
| context | JSON | NULL | āļ•āļąāļ§āđāļ›āļĢ Context āļŠāļģāļŦāļĢāļąāļšāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆ |
| created_at | TIMESTAMP | DEFAULT NOW | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ |
| updated_at | TIMESTAMP | ON UPDATE | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ” |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE
* INDEX (entity_type, entity_id)
* INDEX (current_state)
---
### 10.3 workflow_histories
**Purpose**: āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āđāļ•āđˆāļĨāļ° Step (Audit Trail)
| Column Name | Data Type | Constraints | Description |
| :---------------- | :---------- | :----------- | :-------------------- |
| id | CHAR(36) | PK, UUID | Unique ID |
| instance_id | CHAR(36) | FK, NOT NULL | āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Instance |
| from_state | VARCHAR(50) | NOT NULL | āļŠāļ–āļēāļ™āļ°āļ•āđ‰āļ™āļ—āļēāļ‡ |
| to_state | VARCHAR(50) | NOT NULL | āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡ |
| action | VARCHAR(50) | NOT NULL | Action āļ—āļĩāđˆāļāļĢāļ°āļ—āļģ |
| action_by_user_id | INT | FK, NULL | User ID āļœāļđāđ‰āļāļĢāļ°āļ—āļģ |
| comment | TEXT | NULL | āļ„āļ§āļēāļĄāđ€āļŦāđ‡āļ™ |
| metadata | JSON | NULL | Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™ |
| created_at | TIMESTAMP | DEFAULT NOW | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļāļĢāļ°āļ—āļģ |
**Indexes**:
* PRIMARY KEY (id)
* FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE
* INDEX (instance_id)
* INDEX (action_by_user_id)
---
## **11. ðŸ–Ĩïļ System & Logs Tables (āļĢāļ°āļšāļš, āļšāļąāļ™āļ—āļķāļ)**
> **Audit Logging Architecture:**
### 1. Audit Logging
**Table: `audit_logs`**
āļšāļąāļ™āļ—āļķāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āļŠāļģāļ„āļąāļ:
- User actions (CREATE, UPDATE, DELETE)
- Entity type āđāļĨāļ° Entity ID
- Old/New values (JSON)
- IP Address, User Agent
### 2. User Preferences
**Table: `user_preferences`**
āđ€āļāđ‡āļšāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāđˆāļ§āļ™āļ•āļąāļ§:
- Language preference
- Notification settings
- UI preferences (JSON)
### 3. JSON Schema Validation
**Table: `json_schemas`**
āđ€āļāđ‡āļš Schema āļŠāļģāļŦāļĢāļąāļš Validate JSON fields:
- `correspondence_revisions.details`
- `user_preferences.preferences`
---
### 11.1 json_schemas (UPDATE v1.7.0)
**Purpose**: āđ€āļāđ‡āļš Schema āļŠāļģāļŦāļĢāļąāļš Validate JSON Columns (Req 3.12)
| Column Name | Data Type | Constraints | Description |
| :---------------- | :----------- | :----------- | :------------------------------- |
| id | INT | PK, AI | Unique ID |
| schema_code | VARCHAR(100) | NOT NULL | āļĢāļŦāļąāļŠ Schema (āđ€āļŠāđˆāļ™ RFA_DWG) |
| version | INT | DEFAULT 1 | āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡ Schema |
| table_name | VARCHAR(100) | NOT NULL | āļŠāļ·āđˆāļ­āļ•āļēāļĢāļēāļ‡āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ |
| schema_definition | JSON | NOT NULL | JSON Schema Definition |
| ui_schema | JSON | NULL | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ UI Schema āļŠāļģāļŦāļĢāļąāļš Frontend |
| virtual_columns | JSON | NULL | Config āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ Virtual Columns |
| migration_script | JSON | NULL | Script āļŠāļģāļŦāļĢāļąāļšāđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ |
| is_active | BOOLEAN | DEFAULT TRUE | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ |
---
### 11.2 audit_logs (UPDATE v1.7.0)
**Purpose**: Centralized audit logging for all system actions (Req 6.1)
| Column Name | Data Type | Constraints | Description |
| :----------- | :----------- | :------------------------ | :------------------------------------------- |
| audit_id | BIGINT | PK, AI | Unique log ID |
| request_id | VARCHAR(100) | NULL | Trace ID linking to app logs |
| user_id | INT | NULL, FK | User who performed action |
| action | VARCHAR(100) | NOT NULL | Action name (e.g. rfa.create) |
| severity | ENUM | DEFAULT 'INFO' | INFO, WARN, ERROR, CRITICAL |
| entity_type | VARCHAR(50) | NULL | Module/Table name (e.g. rfa, correspondence) |
| entity_id | VARCHAR(50) | NULL | ID of affected entity |
| details_json | JSON | NULL | Context data / Old & New values |
| ip_address | VARCHAR(45) | NULL | User IP address |
| user_agent | VARCHAR(255) | NULL | User browser/client info |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Log timestamp |
**Indexes**:
* PRIMARY KEY (audit_id, created_at) -- **Partition Key**
* INDEX idx_audit_user (user_id)
* INDEX idx_audit_action (action)
* INDEX idx_audit_entity (entity_type, entity_id)
* INDEX idx_audit_created (created_at)
**Partitioning**:
* **PARTITION BY RANGE (YEAR(created_at))**: āđāļšāđˆāļ‡ Partition āļĢāļēāļĒāļ›āļĩ āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāđƒāļ™āļāļēāļĢāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļĢāļ°āļĒāļ°āļĒāļēāļ§
---
### 11.3 notifications (UPDATE v1.7.0)
**Purpose**: System notifications for users
| Column Name | Data Type | Constraints | Description |
| :---------------- | :----------- | :-------------------------- | :------------------------ |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique notification ID |
| user_id | INT | NOT NULL, FK | Recipient user ID |
| title | VARCHAR(255) | NOT NULL | Notification title |
| message | TEXT | NOT NULL | Notification body |
| notification_type | ENUM | NOT NULL | Type: EMAIL, LINE, SYSTEM |
| is_read | BOOLEAN | DEFAULT FALSE | Read status |
| entity_type | VARCHAR(50) | NULL | Related Entity Type |
| entity_id | INT | NULL | Related Entity ID |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Notification timestamp |
**Indexes**:
* PRIMARY KEY (id, created_at) -- **Partition Key**
* INDEX idx_notif_user (user_id)
* INDEX idx_notif_type (notification_type)
* INDEX idx_notif_read (is_read)
* INDEX idx_notif_created (created_at)
**Partitioning**:
* **PARTITION BY RANGE (YEAR(created_at))**: āđāļšāđˆāļ‡ Partition āļĢāļēāļĒāļ›āļĩ
---
## **12. 🔍 Views (āļĄāļļāļĄāļĄāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ)**
### 12.1 v_current_correspondences
**Purpose**: āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ Correspondence Revision āļĨāđˆāļēāļŠāļļāļ” (is_current = TRUE)
### 12.2 v_current_rfas
**Purpose**: āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ RFA Revision āļĨāđˆāļēāļŠāļļāļ” āļžāļĢāđ‰āļ­āļĄ Status āđāļĨāļ° Approve Code
### 12.3 v_user_tasks (Unified Workflow)
**Purpose**: āļĢāļ§āļĄāļĢāļēāļĒāļāļēāļĢāļ‡āļēāļ™āļ—āļĩāđˆāļĒāļąāļ‡āļ„āđ‰āļēāļ‡āļ­āļĒāļđāđˆ (Status = ACTIVE) āļˆāļēāļāļ—āļļāļāļĢāļ°āļšāļš (RFA, Circulation, Correspondence) āđ€āļžāļ·āđˆāļ­āļ™āļģāđ„āļ›āđāļŠāļ”āļ‡āđƒāļ™ Dashboard
### 12.4 v_audit_log_details
**Purpose**: āđāļŠāļ”āļ‡ audit_logs āļžāļĢāđ‰āļ­āļĄāļ‚āđ‰āļ­āļĄāļđāļĨ username āđāļĨāļ° email āļ‚āļ­āļ‡āļœāļđāđ‰āļāļĢāļ°āļ—āļģ
### 12.5 v_user_all_permissions
**Purpose**: āļĢāļ§āļĄāļŠāļīāļ—āļ˜āļīāđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Global + Project + Organization) āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļļāļāļ„āļ™
### 12.6 v_documents_with_attachments
**Purpose**: āđāļŠāļ”āļ‡āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāļĄāļĩāđ„āļŸāļĨāđŒāđāļ™āļš (Correspondence, Circulation, Drawings)
### 12.7 v_document_statistics
**Purpose**: āđāļŠāļ”āļ‡āļŠāļ–āļīāļ•āļīāđ€āļ­āļāļŠāļēāļĢāļ•āļēāļĄāļ›āļĢāļ°āđ€āļ āļ—āđāļĨāļ°āļŠāļ–āļēāļ™āļ°
---
## **13. 📊 Index Summaries (āļŠāļĢāļļāļ› Index)**
> **Performance Optimization Strategy:**
### 1. Indexing Strategy
**Primary Indexes:**
- Primary Keys (AUTO_INCREMENT)
- Foreign Keys (automatic in InnoDB)
- Unique Constraints (business keys)
**Secondary Indexes:**
```sql
-- Correspondence search
CREATE INDEX idx_corr_type_status ON correspondence_revisions(correspondence_type_id, correspondence_status_id);
CREATE INDEX idx_corr_date ON correspondence_revisions(document_date);
-- Virtual columns for JSON
CREATE INDEX idx_v_ref_project ON correspondence_revisions(v_ref_project_id);
CREATE INDEX idx_v_doc_subtype ON correspondence_revisions(v_doc_subtype);
-- User lookup
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_user_org ON users(primary_organization_id, is_active);
```
### 2. Virtual Columns
āđƒāļŠāđ‰ Virtual Columns āļŠāļģāļŦāļĢāļąāļš Index JSON fields:
```sql
ALTER TABLE correspondence_revisions
ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.ref_project_id'))) VIRTUAL,
ADD INDEX idx_v_ref_project(v_ref_project_id);
```
### 3. Partitioning (Future)
āļžāļīāļˆāļēāļĢāļ“āļē Partition āļ•āļēāļĢāļēāļ‡ `audit_logs` āļ•āļēāļĄāļ›āļĩ:
```sql
ALTER TABLE audit_logs
PARTITION BY RANGE (YEAR(created_at)) (
PARTITION p2024 VALUES LESS THAN (2025),
PARTITION p2025 VALUES LESS THAN (2026),
PARTITION p_future VALUES LESS THAN MAXVALUE
);
```
---
### 13.1 Performance Indexes
| Table Name | Index Columns | Purpose |
| :----------------------- | :------------------------------------------------ | :----------------------------- |
| correspondences | (project_id, correspondence_number) | Fast lookup by document number |
| correspondences | (correspondence_type_id) | Filter by type |
| correspondence_revisions | (correspondence_id, is_current) | Get current revision |
| rfas | (rfa_type_id) | Filter by RFA type |
| rfa_revisions | (rfa_id, is_current) | Get current RFA revision |
| rfa_revisions | (rfa_status_code_id) | Filter by status |
| audit_logs | (created_at) | Date range queries |
| audit_logs | (user_id) | User activity history |
| audit_logs | (module, action) | Action type analysis |
| notifications | (user_id, is_read) | Unread notifications query |
| document_number_counters | (project_id, correspondence_type_id, reset_scope) | Running number generation |
| workflow_instances | (entity_type, entity_id) | Workflow lookup by document ID |
| workflow_instances | (current_state) | Monitor active workflows |
### 13.2 Unique Constraints
| Table Name | Columns | Description |
| :---------------------- | :----------------------------------- | :--------------------------------- |
| users | (username) | Unique login name |
| users | (email) | Unique email address |
| organizations | (organization_code) | Unique organization code |
| projects | (project_code) | Unique project code |
| contracts | (contract_code) | Unique contract code |
| correspondences | (project_id, correspondence_number) | Unique document number per project |
| shop_drawings | (drawing_number) | Unique shop drawing number |
| document_number_formats | (project_id, correspondence_type_id) | One format per type per project |
| workflow_definitions | (workflow_code, version) | Unique workflow code per version |
---
## **14. ðŸ›Ąïļ Data Integrity Constraints (āļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ)**
### 14.1 Soft Delete Policy
* **Tables with `deleted_at`**:
* users
* organizations
* projects
* contracts
* correspondences
* rfas
* shop_drawings
* contract_drawings
* **Rule**: Records are never physically deleted. `deleted_at` is set to timestamp.
* **Query Rule**: All standard queries MUST include `WHERE deleted_at IS NULL`.
### 14.2 Foreign Key Cascades
* **ON DELETE CASCADE**:
* Used for child tables that cannot exist without parent (e.g., `correspondence_revisions`, `rfa_revisions`, `correspondence_attachments`).
* **ON DELETE RESTRICT**:
* Used for master data references to prevent accidental deletion of used data (e.g., `correspondence_types`, `organizations`).
* **ON DELETE SET NULL**:
* Used for optional references (e.g., `created_by`, `originator_id`).
---
## **15. 🔐 Security & Permissions Model (āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ)**
### 15.1 Row-Level Security (RLS) Logic
* **Organization Scope**: Users can only see documents where `originator_id` OR `recipient_organization_id` matches their organization.
* **Project Scope**: Users can only see documents within projects they are assigned to.
* **Confidentiality**: Documents marked `is_confidential` are visible ONLY to specific roles or users.
### 15.2 Role-Based Access Control (RBAC)
* **Permissions** are granular (e.g., `correspondence.view`, `correspondence.create`).
* **Roles** aggregate permissions (e.g., `Document Controller` = `view` + `create` + `edit`).
* **Assignments** link Users to Roles within a Context (Global, Project, or Organization).
---
## **16. 🔄 Data Migration & Seeding (āļāļēāļĢāļĒāđ‰āļēāļĒāļ‚āđ‰āļ­āļĄāļđāļĨ)**
### 16.1 Initial Seeding (V1.7.0)
1. **Master Data**:
* `organizations`: Owner, Consultant, Contractor
* `projects`: LCBP3
* `correspondence_types`: LETTER, MEMO, TRANSMITTAL, RFA
* `rfa_types`: DWG, MAT, DOC, RFI
* `rfa_status_codes`: DFT, PEND, APPR, REJ
* `disciplines`: GEN, STR, ARC, MEP
2. **System Users**:
* `admin`: Super Admin
* `system`: System Bot for automated tasks
### 16.2 Migration Strategy
* **Schema Migration**: Use TypeORM Migrations or raw SQL scripts (versioned).
* **Data Migration**:
* **V1.6.0 -> V1.7.0**:
* Run SQL script `9_lcbp3_v1_7_0.sql`
* Migrate `document_number_counters` to 8-col composite PK.
* Initialize `document_number_reservations`.
* Update `json_schemas` with new columns.
---
### 16.3 Temporary Migration Tracking Tables (V1.8.0 n8n Migration)
āļ•āļēāļĢāļēāļ‡āđ€āļŦāļĨāđˆāļēāļ™āļĩāđ‰āļ–āļđāļāđƒāļŠāđ‰āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļāļĢāļ°āļšāļ§āļ™āļāļēāļĢ Migrate āđ€āļ­āļāļŠāļēāļĢ PDF 20,000 āļ‰āļšāļąāļšāļ”āđ‰āļ§āļĒ n8n (āļ”āļđāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āđƒāļ™ 3-05-n8n-migration-setup-guide.md) āđāļĨāļ°āđ„āļĄāđˆāđƒāļŠāđˆāļ•āļēāļĢāļēāļ‡ Business āļŦāļĨāļąāļāļ‚āļ­āļ‡āļĢāļ°āļšāļš
#### 16.3.1 migration_progress
**Purpose**: āđ€āļāđ‡āļš Checkpoint āļŠāļ–āļēāļ™āļ°āļāļēāļĢ Migrate
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| batch_id | VARCHAR(50) | PRIMARY KEY | āļĢāļŦāļąāļŠāļŠāļļāļ”āļāļēāļĢ Migrate |
| last_processed_index | INT | DEFAULT 0 | āļĨāļģāļ”āļąāļšāļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆāļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļœāđˆāļēāļ™ |
| status | ENUM | DEFAULT 'RUNNING' | āļŠāļ–āļēāļ™āļ° (RUNNING, COMPLETED, FAILED) |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | āđ€āļ§āļĨāļēāļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ” |
#### 16.3.2 migration_review_queue
**Purpose**: āļ„āļīāļ§āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢāđƒāļŦāđ‰āđ€āļˆāđ‰āļēāļŦāļ™āđ‰āļēāļ—āļĩāđˆāļ•āļĢāļ§āļˆāļŠāļ­āļš (Confidence āļ•āđˆāļģāļāļ§āđˆāļēāđ€āļāļ“āļ‘āđŒ)
*āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ: āđ€āļĄāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļšāļœāđˆāļēāļ™āđāļĨāļ°āļŠāļĢāđ‰āļēāļ‡ Correspondence āļˆāļĢāļīāļ‡āđāļĨāđ‰āļ§ āļ‚āđ‰āļ­āļĄāļđāļĨāđƒāļ™āļ™āļĩāđ‰āļ­āļēāļˆāļ–āļđāļāļĨāļšāļŦāļĢāļ·āļ­āđ€āļāđ‡āļšāđ€āļ›āđ‡āļ™ Log āđ„āļ”āđ‰*
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| document_number | VARCHAR(100) | NOT NULL, UNIQUE | āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (āļˆāļēāļ OCR) |
| title | TEXT | | āļŠāļ·āđˆāļ­āđ€āļĢāļ·āđˆāļ­āļ‡ |
| original_title | TEXT | | āļŠāļ·āđˆāļ­āđ€āļĢāļ·āđˆāļ­āļ‡āļ•āđ‰āļ™āļ‰āļšāļąāļšāļāđˆāļ­āļ™āļ•āļĢāļ§āļˆāļŠāļ­āļš |
| ai_suggested_category | VARCHAR(50) | | āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļ—āļĩāđˆ AI āđāļ™āļ°āļ™āļģ |
| ai_confidence | DECIMAL(4,3) | | āļ„āđˆāļēāļ„āļ§āļēāļĄāļĄāļąāđˆāļ™āđƒāļˆāļ‚āļ­āļ‡ AI (0.000 - 1.000) |
| ai_issues | JSON | | āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ›āļąāļāļŦāļēāļ—āļĩāđˆ AI āļžāļš |
| review_reason | VARCHAR(255) | | āđ€āļŦāļ•āļļāļœāļĨāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ•āļĢāļ§āļˆāļŠāļ­āļš (āđ€āļŠāđˆāļ™ Confidence āļ•āđˆāļģ) |
| status | ENUM | DEFAULT 'PENDING' | PENDING, APPROVED, REJECTED |
| reviewed_by | VARCHAR(100) | | āļœāļđāđ‰āļ•āļĢāļ§āļˆāļŠāļ­āļš |
| reviewed_at | TIMESTAMP | NULL | āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ•āļĢāļ§āļˆāļŠāļ­āļš |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | āļ§āļąāļ™āļ—āļĩāđˆāļšāļąāļ™āļ—āļķāļāđ€āļ‚āđ‰āļēāļ„āļīāļ§ |
#### 16.3.3 migration_errors
**Purpose**: āļšāļąāļ™āļ—āļķāļāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (Errors) āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ‚āļ­āļ‡ n8n workflow
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | INDEX | āļĢāļŦāļąāļŠāļŠāļļāļ”āļāļēāļĢ Migrate |
| document_number | VARCHAR(100) | | āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ |
| error_type | ENUM | INDEX | āļ›āļĢāļ°āđ€āļ āļ— Error (FILE_NOT_FOUND, AI_PARSE_ERROR, etc.) |
| error_message | TEXT | | āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ” Error |
| raw_ai_response | TEXT | | Raw response āļˆāļēāļ AI āļāļĢāļ“āļĩāđāļ›āļĨāļœāļĨāđ„āļĄāđˆāđ„āļ”āđ‰ |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | āļ§āļąāļ™āļ—āļĩāđˆāļšāļąāļ™āļ—āļķāļ |
#### 16.3.4 migration_fallback_state
**Purpose**: āļ•āļīāļ”āļ•āļēāļĄāļŠāļ–āļēāļ™āļ° Fallback āļ‚āļ­āļ‡ AI (āđ€āļŠāđˆāļ™ āđ€āļ›āļĨāļĩāđˆāļĒāļ™ Model āđ€āļĄāļ·āđˆāļ­ Error āļ–āļĩāđˆ)
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | UNIQUE | āļĢāļŦāļąāļŠāļŠāļļāļ”āļāļēāļĢ Migrate |
| recent_error_count | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™ Error āļĢāļ§āļ”āļĨāđˆāļēāļŠāļļāļ” |
| is_fallback_active | BOOLEAN | DEFAULT FALSE | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ Fallback Model |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | āđ€āļ§āļĨāļēāļ­āļąāļ›āđ€āļ”āļ•āļĨāđˆāļēāļŠāļļāļ” |
#### 16.3.5 import_transactions
**Purpose**: āļ›āđ‰āļ­āļ‡āļāļąāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļ‹āđ‰āļģ (Idempotency) āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļāļēāļĢ Patch āļ‚āđ‰āļ­āļĄāļđāļĨ
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| idempotency_key | VARCHAR(255) | UNIQUE, NOT NULL | Key āļŠāļģāļŦāļĢāļąāļšāđ€āļŠāđ‡āļ„āļ‹āđ‰āļģ |
| document_number | VARCHAR(100) | | āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ |
| batch_id | VARCHAR(100) | | āļĢāļŦāļąāļŠāļŠāļļāļ”āļāļēāļĢ Migrate |
| status_code | INT | DEFAULT 201 | HTTP Status āļ‚āļ­āļ‡āļāļēāļĢ Import |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | āļ§āļąāļ™āļ—āļĩāđˆāļšāļąāļ™āļ—āļķāļ |
#### 16.3.6 migration_daily_summary
**Purpose**: āļŠāļĢāļļāļ›āļĒāļ­āļ”āļāļēāļĢāļ—āļģāļ‡āļēāļ™āļĢāļēāļĒāļ§āļąāļ™āđāļĒāļāļ•āļēāļĄ Batch
| Column Name | Data Type | Constraints | Description |
| :--- | :--- | :--- | :--- |
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | UNIQUE KEY PART 1 | āļĢāļŦāļąāļŠāļŠāļļāļ”āļāļēāļĢ Migrate |
| summary_date | DATE | UNIQUE KEY PART 2 | āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāļļāļ› |
| total_processed | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™āļ—āļĩāđˆāļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļĢāļ§āļĄ |
| auto_ingested | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™āļ—āļĩāđˆāđ€āļ‚āđ‰āļēāļŠāļđāđˆāļĢāļ°āļšāļšāļŠāļģāđ€āļĢāđ‡āļˆ |
| sent_to_review | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™āļ—āļĩāđˆāļŠāđˆāļ‡āļ„āļīāļ§āļ•āļĢāļ§āļˆāļŠāļ­āļš |
| rejected | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™āļ—āļĩāđˆāļ–āļđāļāļ›āļāļīāđ€āļŠāļ˜ |
| errors | INT | DEFAULT 0 | āļˆāļģāļ™āļ§āļ™āļ—āļĩāđˆāđ€āļāļīāļ” Error |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | āļ§āļąāļ™āļ—āļĩāđˆāļšāļąāļ™āļ—āļķāļ |
---
## **17. 📈 Monitoring & Maintenance (āļāļēāļĢāļ”āļđāđāļĨāļĢāļąāļāļĐāļē)**
### 17.1 Database Maintenance
* **Daily**: Incremental Backup.
* **Weekly**: Full Backup + `OPTIMIZE TABLE` for heavy tables (`audit_logs`, `notifications`).
* **Monthly**: Archive old `audit_logs` partitions to cold storage.
### 17.2 Health Checks
* Monitor `document_number_errors` for numbering failures.
* Monitor `workflow_instances` for stuck workflows (`status = ' IN_PROGRESS '` > 7 days).
* Check `document_number_counters` for gaps or resets.
---
## **18. 📚 Best Practices**
### 1. Naming Conventions
- **Tables**: `snake_case`, plural (e.g., `correspondences`, `users`)
- **Columns**: `snake_case` (e.g., `correspondence_number`, `created_at`)
- **Foreign Keys**: `{referenced_table_singular}_id` (e.g., `project_id`, `user_id`)
- **Junction Tables**: `{table1}_{table2}` (e.g., `correspondence_tags`)
### 2. Timestamp Columns
āļ—āļļāļāļ•āļēāļĢāļēāļ‡āļ„āļ§āļĢāļĄāļĩ:
- `created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP`
- `updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP`
### 3. Soft Delete
āđƒāļŠāđ‰ `deleted_at DATETIME NULL` āđāļ—āļ™āļāļēāļĢāļĨāļšāļˆāļĢāļīāļ‡:
```sql
-- Soft delete
UPDATE correspondences SET deleted_at = NOW() WHERE id = 1;
-- Query active records
SELECT * FROM correspondences WHERE deleted_at IS NULL;
```
### 4. JSON Field Guidelines
- āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Query āļšāđˆāļ­āļĒ
- āļŠāļĢāđ‰āļēāļ‡ Virtual Columns āļŠāļģāļŦāļĢāļąāļš fields āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡ Index
- Validate āļ”āđ‰āļ§āļĒ JSON Schema
- Document structure āđƒāļ™ Data Dictionary
---
---
## **19. 📖 Glossary (āļ„āļģāļĻāļąāļžāļ—āđŒ)**
* **RFA**: Request for Approval (āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī)
* **Transmittal**: Document Transmittal Sheet (āđƒāļšāļ™āļģāļŠāđˆāļ‡āđ€āļ­āļāļŠāļēāļĢ)
* **Shop Drawing**: āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ—āļĩāđˆāļœāļđāđ‰āļĢāļąāļšāđ€āļŦāļĄāļēāļˆāļąāļ”āļ—āļģ
* **Contract Drawing**: āđāļšāļšāļŠāļąāļāļāļē (āđāļšāļšāļ•āļąāđ‰āļ‡āļ•āđ‰āļ™)
* **Revision**: āļ‰āļšāļąāļšāđāļāđ‰āđ„āļ‚ (0, 1, 2, A, B, C)
* **Originator**: āļœāļđāđ‰āļˆāļąāļ”āļ—āļģ/āļœāļđāđ‰āļŠāđˆāļ‡āđ€āļ­āļāļŠāļēāļĢ
* **Recipient**: āļœāļđāđ‰āļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢ
* **Workflow**: āļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™/āļ­āļ™āļļāļĄāļąāļ•āļī
* **Discipline**: āļŠāļēāļ‚āļēāļ‡āļēāļ™ (āđ€āļŠāđˆāļ™ āđ‚āļĒāļ˜āļē, āļŠāļ–āļēāļ›āļąāļ•āļĒāđŒ, āđ„āļŸāļŸāđ‰āļē)
---
**End of Data Dictionary V1.8.0**