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