251117:2200 rebase .md

This commit is contained in:
2025-11-17 22:09:01 +07:00
parent a16e154531
commit 94b63dc359
61 changed files with 45157 additions and 34798 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,40 @@
-- ==========================================================
-- DMS v1.0.1 Patch
-- Reason: Add missing relationship for Req 2.5.3
-- Update: Changed link to revision-level instead of master-level based on clarification.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=1;
-- สร้างตารางเชื่อมระหว่าง shop_drawing_revisions (ตารางลูก) กับ contract_drawings (Master)
-- เพื่อรองรับ Requirement 2.5.3 ที่ระบุว่า Shop Drawing "แต่ละ revision" สามารถอ้างอิง Contract Drawing ที่แตกต่างกันได้
CREATE TABLE shop_drawing_revision_contract_refs (
shop_drawing_revision_id INT NOT NULL COMMENT 'ID ของ Shop Drawing Revision (FK -> shop_drawing_revisions.id)',
contract_drawing_id INT NOT NULL COMMENT 'ID ของ Contract Drawing ที่อ้างอิง (FK -> contract_drawings.condwg_id)',
-- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id),
-- เพิ่ม KEY เพื่อประสิทธิภาพในการค้นหา
KEY idx_sdrcr_rev (shop_drawing_revision_id),
KEY idx_sdrcr_cd (contract_drawing_id),
-- Foreign Keys
CONSTRAINT fk_sdrcr_revision
FOREIGN KEY (shop_drawing_revision_id)
REFERENCES shop_drawing_revisions(id) -- เชื่อมโยงกับตาราง Revision
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT fk_sdrcr_contract_drawing
FOREIGN KEY (contract_drawing_id)
REFERENCES contract_drawings(condwg_id)
ON DELETE CASCADE
ON UPDATE CASCADE
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci
COMMENT='ตารางเชื่อมโยง Shop Drawing Revisions กับ Contract Drawings (Req 2.5.3)';
-- ==========================================================
-- DMS v1.0.1 Patch
-- Reason: Add missing relationship for Req 2.5.3
-- Update: Changed link to revision-level instead of master-level based on clarification.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=1;
-- สร้างตารางเชื่อมระหว่าง shop_drawing_revisions (ตารางลูก) กับ contract_drawings (Master)
-- เพื่อรองรับ Requirement 2.5.3 ที่ระบุว่า Shop Drawing "แต่ละ revision" สามารถอ้างอิง Contract Drawing ที่แตกต่างกันได้
CREATE TABLE shop_drawing_revision_contract_refs (
shop_drawing_revision_id INT NOT NULL COMMENT 'ID ของ Shop Drawing Revision (FK -> shop_drawing_revisions.id)',
contract_drawing_id INT NOT NULL COMMENT 'ID ของ Contract Drawing ที่อ้างอิง (FK -> contract_drawings.condwg_id)',
-- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id),
-- เพิ่ม KEY เพื่อประสิทธิภาพในการค้นหา
KEY idx_sdrcr_rev (shop_drawing_revision_id),
KEY idx_sdrcr_cd (contract_drawing_id),
-- Foreign Keys
CONSTRAINT fk_sdrcr_revision
FOREIGN KEY (shop_drawing_revision_id)
REFERENCES shop_drawing_revisions(id) -- เชื่อมโยงกับตาราง Revision
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT fk_sdrcr_contract_drawing
FOREIGN KEY (contract_drawing_id)
REFERENCES contract_drawings(condwg_id)
ON DELETE CASCADE
ON UPDATE CASCADE
)
ENGINE=InnoDB
DEFAULT CHARSET=utf8mb4
COLLATE=utf8mb4_general_ci
COMMENT='ตารางเชื่อมโยง Shop Drawing Revisions กับ Contract Drawings (Req 2.5.3)';

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,385 +1,385 @@
-- ปรับปรุงโครงสร้าง Correspondence เพื่อจัดการ Revisions ได้ดีขึ้น
-- 1. แยก Master และ Revision ชัดเจน
CREATE TABLE correspondence_master (
master_id INT AUTO_INCREMENT PRIMARY KEY,
correspondence_number VARCHAR(100) NOT NULL,
project_id INT NOT NULL,
correspondence_type_id INT NOT NULL,
-- Metadata ที่ไม่เปลี่ยนแปลงตาม Revision
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
deleted_at DATETIME NULL,
-- Track current/latest revision
current_revision_id INT NULL,
latest_revision_number INT NOT NULL DEFAULT 0,
CONSTRAINT uq_corr_no_per_project UNIQUE (project_id, correspondence_number),
CONSTRAINT fk_cm_project FOREIGN KEY (project_id)
REFERENCES projects(project_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_cm_type FOREIGN KEY (correspondence_type_id)
REFERENCES correspondence_types(type_id) ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cm_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 2. Revision ที่เก็บข้อมูลที่เปลี่ยนแปลงได้
CREATE TABLE correspondence_revisions_new (
revision_id INT AUTO_INCREMENT PRIMARY KEY,
master_id INT NOT NULL,
revision_number INT NOT NULL, -- เปลี่ยนเป็น INT เพื่อง่ายต่อการเรียงลำดับ
revision_label VARCHAR(10) NULL, -- A, B, C สำหรับแสดงผล
-- สถานะเฉพาะของ Revision นี้
correspondence_status_id INT NOT NULL,
is_current BOOLEAN NOT NULL DEFAULT FALSE,
-- ข้อมูลที่เปลี่ยนแปลงตาม Revision
originator_id INT NULL,
recipient_id INT NULL,
title VARCHAR(255) NOT NULL,
keywords VARCHAR(255) NULL,
issued_date DATETIME NULL,
pdf_path VARCHAR(500) NULL,
details JSON NULL,
-- Audit
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
change_reason TEXT NULL,
CONSTRAINT uq_master_revision_number UNIQUE (master_id, revision_number),
CONSTRAINT uq_master_current UNIQUE (master_id, is_current), -- ใช้ partial unique index ได้ดีกว่า
CONSTRAINT fk_cr_master FOREIGN KEY (master_id)
REFERENCES correspondence_master(master_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_cr_status FOREIGN KEY (correspondence_status_id)
REFERENCES correspondence_status(status_id) ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cr_originator FOREIGN KEY (originator_id)
REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_cr_recipient FOREIGN KEY (recipient_id)
REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_cr_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 3. Update current_revision_id FK
ALTER TABLE correspondence_master
ADD CONSTRAINT fk_cm_current_revision
FOREIGN KEY (current_revision_id)
REFERENCES correspondence_revisions_new(revision_id)
ON UPDATE CASCADE ON DELETE SET NULL;
-- 4. View สำหรับ Query ง่าย (รวม Master + Current Revision)
CREATE OR REPLACE VIEW v_current_correspondences AS
SELECT
cm.master_id,
cm.correspondence_number,
cm.project_id,
cm.correspondence_type_id,
cr.revision_id,
cr.revision_number,
cr.revision_label,
cr.correspondence_status_id,
cr.title,
cr.keywords,
cr.originator_id,
cr.recipient_id,
cr.issued_date,
cr.pdf_path,
cr.details,
cm.created_at AS master_created_at,
cr.created_at AS revision_created_at,
cm.latest_revision_number
FROM correspondence_master cm
INNER JOIN correspondence_revisions_new cr
ON cm.current_revision_id = cr.revision_id
WHERE cm.deleted_at IS NULL;
-- 5. Stored Procedure สำหรับสร้าง Revision ใหม่
DELIMITER $$
CREATE PROCEDURE sp_create_correspondence_revision(
IN p_master_id INT,
IN p_title VARCHAR(255),
IN p_originator_id INT,
IN p_recipient_id INT,
IN p_status_id INT,
IN p_created_by INT,
IN p_change_reason TEXT,
OUT p_revision_id INT
)
BEGIN
DECLARE v_next_revision_number INT;
DECLARE v_revision_label VARCHAR(10);
-- Get next revision number
SELECT COALESCE(MAX(revision_number), 0) + 1
INTO v_next_revision_number
FROM correspondence_revisions_new
WHERE master_id = p_master_id;
-- Generate label (0->Original, 1->A, 2->B, etc.)
IF v_next_revision_number = 0 THEN
SET v_revision_label = 'Original';
ELSE
SET v_revision_label = CHAR(64 + v_next_revision_number); -- A, B, C...
END IF;
-- Mark all previous revisions as not current
UPDATE correspondence_revisions_new
SET is_current = FALSE
WHERE master_id = p_master_id;
-- Insert new revision
INSERT INTO correspondence_revisions_new (
master_id, revision_number, revision_label,
correspondence_status_id, is_current,
title, originator_id, recipient_id,
created_by, change_reason
) VALUES (
p_master_id, v_next_revision_number, v_revision_label,
p_status_id, TRUE,
p_title, p_originator_id, p_recipient_id,
p_created_by, p_change_reason
);
SET p_revision_id = LAST_INSERT_ID();
-- Update master
UPDATE correspondence_master
SET
current_revision_id = p_revision_id,
latest_revision_number = v_next_revision_number
WHERE master_id = p_master_id;
END$$
DELIMITER ;
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- ปรับปรุง Technical Documents เพื่อความชัดเจนและเชื่อมโยงกับ Correspondence
-- 1. Technical Document Master
CREATE TABLE technical_document_master (
master_id INT AUTO_INCREMENT PRIMARY KEY,
document_number VARCHAR(100) NOT NULL,
document_type_id INT NOT NULL,
project_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
-- Link to RFA Correspondence (optional - เพราะอาจยังไม่ได้ส่ง RFA)
rfa_correspondence_id INT NULL,
-- Tracking
current_revision_id INT NULL,
latest_revision_number INT NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
deleted_at DATETIME NULL,
CONSTRAINT uq_techdoc_no_project UNIQUE (project_id, document_number),
CONSTRAINT fk_tdm_type FOREIGN KEY (document_type_id)
REFERENCES technicaldoc_types(document_types_id),
CONSTRAINT fk_tdm_project FOREIGN KEY (project_id)
REFERENCES projects(project_id) ON DELETE CASCADE,
CONSTRAINT fk_tdm_rfa_corr FOREIGN KEY (rfa_correspondence_id)
REFERENCES correspondences(corr_id) ON DELETE SET NULL,
CONSTRAINT fk_tdm_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 2. Technical Document Revisions
CREATE TABLE technical_document_revisions (
revision_id INT AUTO_INCREMENT PRIMARY KEY,
master_id INT NOT NULL,
revision_number INT NOT NULL,
revision_label VARCHAR(10) NULL, -- A, B, C
status_code_id INT NOT NULL,
approve_code_id INT NULL,
is_current BOOLEAN NOT NULL DEFAULT FALSE,
-- File references
pdf_path VARCHAR(500) NULL,
dwg_path VARCHAR(500) NULL, -- สำหรับ DWG type
-- Metadata
revision_description TEXT NULL,
submitted_date DATE NULL,
approved_date DATE NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
updated_by INT NULL,
CONSTRAINT uq_master_rev_number UNIQUE (master_id, revision_number),
CONSTRAINT fk_tdr_master FOREIGN KEY (master_id)
REFERENCES technical_document_master(master_id) ON DELETE CASCADE,
CONSTRAINT fk_tdr_status FOREIGN KEY (status_code_id)
REFERENCES technicaldoc_status_codes(status_code_id),
CONSTRAINT fk_tdr_approve FOREIGN KEY (approve_code_id)
REFERENCES technicaldoc_approve_codes(approve_code_id) ON DELETE SET NULL,
CONSTRAINT fk_tdr_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON DELETE SET NULL,
CONSTRAINT fk_tdr_updated_by FOREIGN KEY (updated_by)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 3. Shop Drawing References (Many-to-Many with Revisions)
CREATE TABLE technical_document_shop_drawing_refs (
tech_doc_revision_id INT NOT NULL,
shop_drawing_rev_id INT NOT NULL,
reference_type ENUM('SUPERSEDES', 'RELATED', 'AS_PER') DEFAULT 'RELATED',
PRIMARY KEY (tech_doc_revision_id, shop_drawing_rev_id),
CONSTRAINT fk_tdsdr_tech_doc FOREIGN KEY (tech_doc_revision_id)
REFERENCES technical_document_revisions(revision_id) ON DELETE CASCADE,
CONSTRAINT fk_tdsdr_shop_dwg FOREIGN KEY (shop_drawing_rev_id)
REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 4. Contract Drawing References (Many-to-Many)
CREATE TABLE technical_document_contract_drawing_refs (
tech_doc_revision_id INT NOT NULL,
contract_drawing_id INT NOT NULL,
reference_type ENUM('AS_PER', 'RELATED') DEFAULT 'AS_PER',
sheet_numbers VARCHAR(255) NULL, -- "Sheet 1-5, 10" เก็บเป็น text
PRIMARY KEY (tech_doc_revision_id, contract_drawing_id),
CONSTRAINT fk_tdcdr_tech_doc FOREIGN KEY (tech_doc_revision_id)
REFERENCES technical_document_revisions(revision_id) ON DELETE CASCADE,
CONSTRAINT fk_tdcdr_contract_dwg FOREIGN KEY (contract_drawing_id)
REFERENCES contract_drawings(condwg_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 5. ปรับปรุง RFA Workflow ให้ชัดเจนขึ้น
CREATE TABLE rfa_workflow_instances (
workflow_id INT AUTO_INCREMENT PRIMARY KEY,
rfa_correspondence_id INT NOT NULL UNIQUE,
template_id INT NULL, -- อ้างอิงถึงแม่แบบที่ใช้
current_step_sequence INT NOT NULL DEFAULT 1,
overall_status ENUM('DRAFT', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'RETURNED', 'CANCELLED')
NOT NULL DEFAULT 'DRAFT',
started_at DATETIME NULL,
completed_at DATETIME NULL,
CONSTRAINT fk_rwi_corr FOREIGN KEY (rfa_correspondence_id)
REFERENCES correspondences(corr_id) ON DELETE CASCADE,
CONSTRAINT fk_rwi_template FOREIGN KEY (template_id)
REFERENCES technicaldoc_workflow_templates(template_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 6. RFA Workflow Steps (แยกจาก technicaldoc_workflows เดิม)
CREATE TABLE rfa_workflow_steps (
step_id INT AUTO_INCREMENT PRIMARY KEY,
workflow_id INT NOT NULL,
sequence INT NOT NULL,
org_id INT NOT NULL,
step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION')
NOT NULL DEFAULT 'FOR_APPROVAL',
status ENUM('PENDING', 'ACTIVE', 'APPROVED', 'REJECTED',
'APPROVED_WITH_COMMENTS', 'SKIPPED', 'RETURNED')
NOT NULL DEFAULT 'PENDING',
approve_code_id INT NULL, -- Link to approve codes
comments TEXT NULL,
activated_at DATETIME NULL,
processed_at DATETIME NULL,
processed_by_user_id INT NULL,
deadline_date DATE NULL,
CONSTRAINT uq_workflow_sequence UNIQUE (workflow_id, sequence),
CONSTRAINT fk_rws_workflow FOREIGN KEY (workflow_id)
REFERENCES rfa_workflow_instances(workflow_id) ON DELETE CASCADE,
CONSTRAINT fk_rws_org FOREIGN KEY (org_id)
REFERENCES organizations(org_id),
CONSTRAINT fk_rws_approve_code FOREIGN KEY (approve_code_id)
REFERENCES technicaldoc_approve_codes(approve_code_id),
CONSTRAINT fk_rws_user FOREIGN KEY (processed_by_user_id)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 7. View: Current Technical Documents with Latest Revision
CREATE OR REPLACE VIEW v_current_technical_documents AS
SELECT
tdm.master_id,
tdm.document_number,
tdm.document_type_id,
dt.code AS document_type_code,
dt.name AS document_type_name,
tdm.project_id,
tdm.title,
tdm.rfa_correspondence_id,
tdr.revision_id,
tdr.revision_number,
tdr.revision_label,
tdr.status_code_id,
sc.code AS status_code,
sc.description AS status_description,
tdr.approve_code_id,
ac.code AS approve_code,
ac.description AS approve_description,
tdr.pdf_path,
tdr.dwg_path,
tdr.submitted_date,
tdr.approved_date,
tdm.created_at,
tdr.created_at AS revision_created_at
FROM technical_document_master tdm
INNER JOIN technical_document_revisions tdr
ON tdm.current_revision_id = tdr.revision_id
INNER JOIN technicaldoc_types dt
ON tdm.document_type_id = dt.document_types_id
LEFT JOIN technicaldoc_status_codes sc
ON tdr.status_code_id = sc.status_code_id
LEFT JOIN technicaldoc_approve_codes ac
ON tdr.approve_code_id = ac.approve_code_id
WHERE tdm.deleted_at IS NULL;
-- 8. View: RFA Workflow Status
CREATE OR REPLACE VIEW v_rfa_workflow_status AS
SELECT
rwi.workflow_id,
rwi.rfa_correspondence_id,
c.correspondence_number AS rfa_number,
rwi.overall_status,
rwi.current_step_sequence,
rws.step_id AS current_step_id,
rws.org_id AS current_org_id,
o.org_name AS current_org_name,
rws.status AS current_step_status,
rws.deadline_date,
rwi.started_at,
DATEDIFF(CURDATE(), rwi.started_at) AS days_in_progress,
(SELECT COUNT(*) FROM rfa_workflow_steps WHERE workflow_id = rwi.workflow_id) AS total_steps,
(SELECT COUNT(*) FROM rfa_workflow_steps WHERE workflow_id = rwi.workflow_id AND status IN ('APPROVED','APPROVED_WITH_COMMENTS')) AS completed_steps
FROM rfa_workflow_instances rwi
INNER JOIN correspondences c ON rwi.rfa_correspondence_id = c.corr_id
LEFT JOIN rfa_workflow_steps rws
ON rwi.workflow_id = rws.workflow_id
AND rwi.current_step_sequence = rws.sequence
LEFT JOIN organizations o ON rws.org_id = o.org_id
-- ปรับปรุงโครงสร้าง Correspondence เพื่อจัดการ Revisions ได้ดีขึ้น
-- 1. แยก Master และ Revision ชัดเจน
CREATE TABLE correspondence_master (
master_id INT AUTO_INCREMENT PRIMARY KEY,
correspondence_number VARCHAR(100) NOT NULL,
project_id INT NOT NULL,
correspondence_type_id INT NOT NULL,
-- Metadata ที่ไม่เปลี่ยนแปลงตาม Revision
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
deleted_at DATETIME NULL,
-- Track current/latest revision
current_revision_id INT NULL,
latest_revision_number INT NOT NULL DEFAULT 0,
CONSTRAINT uq_corr_no_per_project UNIQUE (project_id, correspondence_number),
CONSTRAINT fk_cm_project FOREIGN KEY (project_id)
REFERENCES projects(project_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_cm_type FOREIGN KEY (correspondence_type_id)
REFERENCES correspondence_types(type_id) ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cm_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 2. Revision ที่เก็บข้อมูลที่เปลี่ยนแปลงได้
CREATE TABLE correspondence_revisions_new (
revision_id INT AUTO_INCREMENT PRIMARY KEY,
master_id INT NOT NULL,
revision_number INT NOT NULL, -- เปลี่ยนเป็น INT เพื่อง่ายต่อการเรียงลำดับ
revision_label VARCHAR(10) NULL, -- A, B, C สำหรับแสดงผล
-- สถานะเฉพาะของ Revision นี้
correspondence_status_id INT NOT NULL,
is_current BOOLEAN NOT NULL DEFAULT FALSE,
-- ข้อมูลที่เปลี่ยนแปลงตาม Revision
originator_id INT NULL,
recipient_id INT NULL,
title VARCHAR(255) NOT NULL,
keywords VARCHAR(255) NULL,
issued_date DATETIME NULL,
pdf_path VARCHAR(500) NULL,
details JSON NULL,
-- Audit
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
change_reason TEXT NULL,
CONSTRAINT uq_master_revision_number UNIQUE (master_id, revision_number),
CONSTRAINT uq_master_current UNIQUE (master_id, is_current), -- ใช้ partial unique index ได้ดีกว่า
CONSTRAINT fk_cr_master FOREIGN KEY (master_id)
REFERENCES correspondence_master(master_id) ON UPDATE CASCADE ON DELETE CASCADE,
CONSTRAINT fk_cr_status FOREIGN KEY (correspondence_status_id)
REFERENCES correspondence_status(status_id) ON UPDATE CASCADE ON DELETE RESTRICT,
CONSTRAINT fk_cr_originator FOREIGN KEY (originator_id)
REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_cr_recipient FOREIGN KEY (recipient_id)
REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE SET NULL,
CONSTRAINT fk_cr_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON UPDATE CASCADE ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 3. Update current_revision_id FK
ALTER TABLE correspondence_master
ADD CONSTRAINT fk_cm_current_revision
FOREIGN KEY (current_revision_id)
REFERENCES correspondence_revisions_new(revision_id)
ON UPDATE CASCADE ON DELETE SET NULL;
-- 4. View สำหรับ Query ง่าย (รวม Master + Current Revision)
CREATE OR REPLACE VIEW v_current_correspondences AS
SELECT
cm.master_id,
cm.correspondence_number,
cm.project_id,
cm.correspondence_type_id,
cr.revision_id,
cr.revision_number,
cr.revision_label,
cr.correspondence_status_id,
cr.title,
cr.keywords,
cr.originator_id,
cr.recipient_id,
cr.issued_date,
cr.pdf_path,
cr.details,
cm.created_at AS master_created_at,
cr.created_at AS revision_created_at,
cm.latest_revision_number
FROM correspondence_master cm
INNER JOIN correspondence_revisions_new cr
ON cm.current_revision_id = cr.revision_id
WHERE cm.deleted_at IS NULL;
-- 5. Stored Procedure สำหรับสร้าง Revision ใหม่
DELIMITER $$
CREATE PROCEDURE sp_create_correspondence_revision(
IN p_master_id INT,
IN p_title VARCHAR(255),
IN p_originator_id INT,
IN p_recipient_id INT,
IN p_status_id INT,
IN p_created_by INT,
IN p_change_reason TEXT,
OUT p_revision_id INT
)
BEGIN
DECLARE v_next_revision_number INT;
DECLARE v_revision_label VARCHAR(10);
-- Get next revision number
SELECT COALESCE(MAX(revision_number), 0) + 1
INTO v_next_revision_number
FROM correspondence_revisions_new
WHERE master_id = p_master_id;
-- Generate label (0->Original, 1->A, 2->B, etc.)
IF v_next_revision_number = 0 THEN
SET v_revision_label = 'Original';
ELSE
SET v_revision_label = CHAR(64 + v_next_revision_number); -- A, B, C...
END IF;
-- Mark all previous revisions as not current
UPDATE correspondence_revisions_new
SET is_current = FALSE
WHERE master_id = p_master_id;
-- Insert new revision
INSERT INTO correspondence_revisions_new (
master_id, revision_number, revision_label,
correspondence_status_id, is_current,
title, originator_id, recipient_id,
created_by, change_reason
) VALUES (
p_master_id, v_next_revision_number, v_revision_label,
p_status_id, TRUE,
p_title, p_originator_id, p_recipient_id,
p_created_by, p_change_reason
);
SET p_revision_id = LAST_INSERT_ID();
-- Update master
UPDATE correspondence_master
SET
current_revision_id = p_revision_id,
latest_revision_number = v_next_revision_number
WHERE master_id = p_master_id;
END$$
DELIMITER ;
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- *****************************************************
-- ปรับปรุง Technical Documents เพื่อความชัดเจนและเชื่อมโยงกับ Correspondence
-- 1. Technical Document Master
CREATE TABLE technical_document_master (
master_id INT AUTO_INCREMENT PRIMARY KEY,
document_number VARCHAR(100) NOT NULL,
document_type_id INT NOT NULL,
project_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
-- Link to RFA Correspondence (optional - เพราะอาจยังไม่ได้ส่ง RFA)
rfa_correspondence_id INT NULL,
-- Tracking
current_revision_id INT NULL,
latest_revision_number INT NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
deleted_at DATETIME NULL,
CONSTRAINT uq_techdoc_no_project UNIQUE (project_id, document_number),
CONSTRAINT fk_tdm_type FOREIGN KEY (document_type_id)
REFERENCES technicaldoc_types(document_types_id),
CONSTRAINT fk_tdm_project FOREIGN KEY (project_id)
REFERENCES projects(project_id) ON DELETE CASCADE,
CONSTRAINT fk_tdm_rfa_corr FOREIGN KEY (rfa_correspondence_id)
REFERENCES correspondences(corr_id) ON DELETE SET NULL,
CONSTRAINT fk_tdm_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 2. Technical Document Revisions
CREATE TABLE technical_document_revisions (
revision_id INT AUTO_INCREMENT PRIMARY KEY,
master_id INT NOT NULL,
revision_number INT NOT NULL,
revision_label VARCHAR(10) NULL, -- A, B, C
status_code_id INT NOT NULL,
approve_code_id INT NULL,
is_current BOOLEAN NOT NULL DEFAULT FALSE,
-- File references
pdf_path VARCHAR(500) NULL,
dwg_path VARCHAR(500) NULL, -- สำหรับ DWG type
-- Metadata
revision_description TEXT NULL,
submitted_date DATE NULL,
approved_date DATE NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
created_by INT NULL,
updated_by INT NULL,
CONSTRAINT uq_master_rev_number UNIQUE (master_id, revision_number),
CONSTRAINT fk_tdr_master FOREIGN KEY (master_id)
REFERENCES technical_document_master(master_id) ON DELETE CASCADE,
CONSTRAINT fk_tdr_status FOREIGN KEY (status_code_id)
REFERENCES technicaldoc_status_codes(status_code_id),
CONSTRAINT fk_tdr_approve FOREIGN KEY (approve_code_id)
REFERENCES technicaldoc_approve_codes(approve_code_id) ON DELETE SET NULL,
CONSTRAINT fk_tdr_created_by FOREIGN KEY (created_by)
REFERENCES users(user_id) ON DELETE SET NULL,
CONSTRAINT fk_tdr_updated_by FOREIGN KEY (updated_by)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 3. Shop Drawing References (Many-to-Many with Revisions)
CREATE TABLE technical_document_shop_drawing_refs (
tech_doc_revision_id INT NOT NULL,
shop_drawing_rev_id INT NOT NULL,
reference_type ENUM('SUPERSEDES', 'RELATED', 'AS_PER') DEFAULT 'RELATED',
PRIMARY KEY (tech_doc_revision_id, shop_drawing_rev_id),
CONSTRAINT fk_tdsdr_tech_doc FOREIGN KEY (tech_doc_revision_id)
REFERENCES technical_document_revisions(revision_id) ON DELETE CASCADE,
CONSTRAINT fk_tdsdr_shop_dwg FOREIGN KEY (shop_drawing_rev_id)
REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 4. Contract Drawing References (Many-to-Many)
CREATE TABLE technical_document_contract_drawing_refs (
tech_doc_revision_id INT NOT NULL,
contract_drawing_id INT NOT NULL,
reference_type ENUM('AS_PER', 'RELATED') DEFAULT 'AS_PER',
sheet_numbers VARCHAR(255) NULL, -- "Sheet 1-5, 10" เก็บเป็น text
PRIMARY KEY (tech_doc_revision_id, contract_drawing_id),
CONSTRAINT fk_tdcdr_tech_doc FOREIGN KEY (tech_doc_revision_id)
REFERENCES technical_document_revisions(revision_id) ON DELETE CASCADE,
CONSTRAINT fk_tdcdr_contract_dwg FOREIGN KEY (contract_drawing_id)
REFERENCES contract_drawings(condwg_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 5. ปรับปรุง RFA Workflow ให้ชัดเจนขึ้น
CREATE TABLE rfa_workflow_instances (
workflow_id INT AUTO_INCREMENT PRIMARY KEY,
rfa_correspondence_id INT NOT NULL UNIQUE,
template_id INT NULL, -- อ้างอิงถึงแม่แบบที่ใช้
current_step_sequence INT NOT NULL DEFAULT 1,
overall_status ENUM('DRAFT', 'IN_PROGRESS', 'APPROVED', 'REJECTED', 'RETURNED', 'CANCELLED')
NOT NULL DEFAULT 'DRAFT',
started_at DATETIME NULL,
completed_at DATETIME NULL,
CONSTRAINT fk_rwi_corr FOREIGN KEY (rfa_correspondence_id)
REFERENCES correspondences(corr_id) ON DELETE CASCADE,
CONSTRAINT fk_rwi_template FOREIGN KEY (template_id)
REFERENCES technicaldoc_workflow_templates(template_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 6. RFA Workflow Steps (แยกจาก technicaldoc_workflows เดิม)
CREATE TABLE rfa_workflow_steps (
step_id INT AUTO_INCREMENT PRIMARY KEY,
workflow_id INT NOT NULL,
sequence INT NOT NULL,
org_id INT NOT NULL,
step_purpose ENUM('FOR_APPROVAL', 'FOR_REVIEW', 'FOR_INFORMATION')
NOT NULL DEFAULT 'FOR_APPROVAL',
status ENUM('PENDING', 'ACTIVE', 'APPROVED', 'REJECTED',
'APPROVED_WITH_COMMENTS', 'SKIPPED', 'RETURNED')
NOT NULL DEFAULT 'PENDING',
approve_code_id INT NULL, -- Link to approve codes
comments TEXT NULL,
activated_at DATETIME NULL,
processed_at DATETIME NULL,
processed_by_user_id INT NULL,
deadline_date DATE NULL,
CONSTRAINT uq_workflow_sequence UNIQUE (workflow_id, sequence),
CONSTRAINT fk_rws_workflow FOREIGN KEY (workflow_id)
REFERENCES rfa_workflow_instances(workflow_id) ON DELETE CASCADE,
CONSTRAINT fk_rws_org FOREIGN KEY (org_id)
REFERENCES organizations(org_id),
CONSTRAINT fk_rws_approve_code FOREIGN KEY (approve_code_id)
REFERENCES technicaldoc_approve_codes(approve_code_id),
CONSTRAINT fk_rws_user FOREIGN KEY (processed_by_user_id)
REFERENCES users(user_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- 7. View: Current Technical Documents with Latest Revision
CREATE OR REPLACE VIEW v_current_technical_documents AS
SELECT
tdm.master_id,
tdm.document_number,
tdm.document_type_id,
dt.code AS document_type_code,
dt.name AS document_type_name,
tdm.project_id,
tdm.title,
tdm.rfa_correspondence_id,
tdr.revision_id,
tdr.revision_number,
tdr.revision_label,
tdr.status_code_id,
sc.code AS status_code,
sc.description AS status_description,
tdr.approve_code_id,
ac.code AS approve_code,
ac.description AS approve_description,
tdr.pdf_path,
tdr.dwg_path,
tdr.submitted_date,
tdr.approved_date,
tdm.created_at,
tdr.created_at AS revision_created_at
FROM technical_document_master tdm
INNER JOIN technical_document_revisions tdr
ON tdm.current_revision_id = tdr.revision_id
INNER JOIN technicaldoc_types dt
ON tdm.document_type_id = dt.document_types_id
LEFT JOIN technicaldoc_status_codes sc
ON tdr.status_code_id = sc.status_code_id
LEFT JOIN technicaldoc_approve_codes ac
ON tdr.approve_code_id = ac.approve_code_id
WHERE tdm.deleted_at IS NULL;
-- 8. View: RFA Workflow Status
CREATE OR REPLACE VIEW v_rfa_workflow_status AS
SELECT
rwi.workflow_id,
rwi.rfa_correspondence_id,
c.correspondence_number AS rfa_number,
rwi.overall_status,
rwi.current_step_sequence,
rws.step_id AS current_step_id,
rws.org_id AS current_org_id,
o.org_name AS current_org_name,
rws.status AS current_step_status,
rws.deadline_date,
rwi.started_at,
DATEDIFF(CURDATE(), rwi.started_at) AS days_in_progress,
(SELECT COUNT(*) FROM rfa_workflow_steps WHERE workflow_id = rwi.workflow_id) AS total_steps,
(SELECT COUNT(*) FROM rfa_workflow_steps WHERE workflow_id = rwi.workflow_id AND status IN ('APPROVED','APPROVED_WITH_COMMENTS')) AS completed_steps
FROM rfa_workflow_instances rwi
INNER JOIN correspondences c ON rwi.rfa_correspondence_id = c.corr_id
LEFT JOIN rfa_workflow_steps rws
ON rwi.workflow_id = rws.workflow_id
AND rwi.current_step_sequence = rws.sequence
LEFT JOIN organizations o ON rws.org_id = o.org_id
WHERE rwi.overall_status NOT IN ('CANCELLED', 'APPROVED');

View File

@@ -1,215 +1,215 @@
-- ==========================================================
-- DMS v0.5.0
-- Database v5.1 - Deploy Script Schema (Revised)
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 10.11
-- Notes:
-- - Removed 'rfas' table.
-- - Added 'contracts' and 'contract_parties' tables.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- Drop tables in reverse order of creation due to dependencies
DROP TABLE IF EXISTS audit_logs;
DROP TABLE IF EXISTS user_project_roles;
DROP TABLE IF EXISTS user_roles;
DROP TABLE IF EXISTS role_permissions;
DROP TABLE IF EXISTS permissions;
DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS project_parties;
DROP TABLE IF EXISTS contract_parties; -- New
DROP TABLE IF EXISTS contracts; -- New
DROP TABLE IF EXISTS projects;
DROP TABLE IF EXISTS organizations;
DROP TABLE IF EXISTS correspondence_references;
DROP TABLE IF EXISTS correspondence_cc_recipients;
DROP TABLE IF EXISTS correspondences;
DROP TABLE IF EXISTS email_details;
DROP TABLE IF EXISTS instruction_details;
DROP TABLE IF EXISTS letter_details;
DROP TABLE IF EXISTS memorandum_details;
DROP TABLE IF EXISTS minutes_of_meeting_details;
DROP TABLE IF EXISTS rfi_details;
DROP TABLE IF EXISTS rfa_items;
DROP TABLE IF EXISTS transmittal_items;
DROP TABLE IF EXISTS transmittals;
DROP TABLE IF EXISTS technicaldocs;
DROP TABLE IF EXISTS shop_drawing_revisions;
DROP TABLE IF EXISTS shop_drawings;
DROP TABLE IF EXISTS shop_drawing_sub_categories;
DROP TABLE IF EXISTS shop_drawing_main_categories;
DROP TABLE IF EXISTS contract_dwg_subcat_cat_map;
DROP TABLE IF EXISTS contract_dwg_sub_cat;
DROP TABLE IF EXISTS contract_dwg_cat;
DROP TABLE IF EXISTS contract_drawings;
DROP TABLE IF EXISTS cir_action_documents;
DROP TABLE IF EXISTS cir_actions;
DROP TABLE IF EXISTS cir_recipients;
DROP TABLE IF EXISTS circulations;
DROP TABLE IF EXISTS cir_status_codes;
DROP TABLE IF EXISTS correspondence_status_transitions;
DROP TABLE IF EXISTS correspondence_statuses;
DROP TABLE IF EXISTS correspondence_types;
DROP TABLE IF EXISTS correspondence_tags;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS global_default_roles;
SET FOREIGN_KEY_CHECKS=1;
-- ==========================================================
-- Table Creation
-- ==========================================================
-- Organizations Table
CREATE TABLE organizations (
org_id INT NOT NULL AUTO_INCREMENT,
org_code VARCHAR(20) NOT NULL,
org_name VARCHAR(255) NOT NULL,
primary_role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (org_id),
UNIQUE KEY ux_organizations_code (org_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Projects Table
CREATE TABLE projects (
project_id INT NOT NULL AUTO_INCREMENT,
project_code VARCHAR(20) NOT NULL,
project_name VARCHAR(255) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (project_id),
UNIQUE KEY ux_projects_code (project_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- NEW: Contracts Table
-- Stores information about each contract.
CREATE TABLE contracts (
contract_id INT NOT NULL AUTO_INCREMENT,
contract_code VARCHAR(50) NOT NULL,
contract_name VARCHAR(255) NOT NULL,
description TEXT,
start_date DATE,
end_date DATE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (contract_id),
UNIQUE KEY ux_contracts_code (contract_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- NEW: Contract Parties Table
-- Links contracts, projects, and organizations together.
CREATE TABLE contract_parties (
contract_id INT NOT NULL,
project_id INT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (contract_id, project_id, org_id),
CONSTRAINT fk_cp_contract FOREIGN KEY (contract_id) REFERENCES contracts(contract_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Users Table (RBAC)
CREATE TABLE users (
user_id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
org_id INT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
last_login TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (user_id),
UNIQUE KEY ux_users_username (username),
UNIQUE KEY ux_users_email (email),
CONSTRAINT fk_users_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Roles Table (RBAC)
CREATE TABLE roles (
role_id INT NOT NULL AUTO_INCREMENT,
role_code VARCHAR(50) NOT NULL,
role_name VARCHAR(100) NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (role_id),
UNIQUE KEY ux_roles_code (role_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Permissions Table (RBAC)
CREATE TABLE permissions (
permission_id INT NOT NULL AUTO_INCREMENT,
permission_code VARCHAR(100) NOT NULL,
description TEXT,
PRIMARY KEY (permission_id),
UNIQUE KEY ux_permissions_code (permission_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Role Permissions Junction Table (RBAC)
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
CONSTRAINT fk_rp_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_rp_permission FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- User Roles Junction Table (RBAC) - For global/system-level roles
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- User Project Roles Junction Table (RBAC) - For project-specific roles
CREATE TABLE user_project_roles (
user_id INT NOT NULL,
project_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, project_id, role_id),
CONSTRAINT fk_upr_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Project Parties Table
CREATE TABLE project_parties (
project_id INT NOT NULL,
org_id INT NOT NULL,
role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NOT NULL,
is_contractor TINYINT(1) GENERATED ALWAYS AS (IF(role = 'CONTRACTOR', 1, NULL)) STORED,
PRIMARY KEY (project_id, org_id),
UNIQUE KEY uq_project_parties_contractor (project_id, is_contractor),
CONSTRAINT fk_pp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_pp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ... (The rest of your original script for correspondences, technical docs, etc. remains here) ...
-- Global default roles (template defaults)
CREATE TABLE global_default_roles (
id TINYINT NOT NULL DEFAULT 1,
role ENUM('OWNER','DESIGNER','CONSULTANT') NOT NULL,
position TINYINT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (id, role, position),
UNIQUE KEY ux_gdr_unique_org_per_role (id, role, org_id),
CONSTRAINT fk_gdr_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ==========================================================
-- DMS v0.5.0
-- Database v5.1 - Deploy Script Schema (Revised)
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 10.11
-- Notes:
-- - Removed 'rfas' table.
-- - Added 'contracts' and 'contract_parties' tables.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- Drop tables in reverse order of creation due to dependencies
DROP TABLE IF EXISTS audit_logs;
DROP TABLE IF EXISTS user_project_roles;
DROP TABLE IF EXISTS user_roles;
DROP TABLE IF EXISTS role_permissions;
DROP TABLE IF EXISTS permissions;
DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS project_parties;
DROP TABLE IF EXISTS contract_parties; -- New
DROP TABLE IF EXISTS contracts; -- New
DROP TABLE IF EXISTS projects;
DROP TABLE IF EXISTS organizations;
DROP TABLE IF EXISTS correspondence_references;
DROP TABLE IF EXISTS correspondence_cc_recipients;
DROP TABLE IF EXISTS correspondences;
DROP TABLE IF EXISTS email_details;
DROP TABLE IF EXISTS instruction_details;
DROP TABLE IF EXISTS letter_details;
DROP TABLE IF EXISTS memorandum_details;
DROP TABLE IF EXISTS minutes_of_meeting_details;
DROP TABLE IF EXISTS rfi_details;
DROP TABLE IF EXISTS rfa_items;
DROP TABLE IF EXISTS transmittal_items;
DROP TABLE IF EXISTS transmittals;
DROP TABLE IF EXISTS technicaldocs;
DROP TABLE IF EXISTS shop_drawing_revisions;
DROP TABLE IF EXISTS shop_drawings;
DROP TABLE IF EXISTS shop_drawing_sub_categories;
DROP TABLE IF EXISTS shop_drawing_main_categories;
DROP TABLE IF EXISTS contract_dwg_subcat_cat_map;
DROP TABLE IF EXISTS contract_dwg_sub_cat;
DROP TABLE IF EXISTS contract_dwg_cat;
DROP TABLE IF EXISTS contract_drawings;
DROP TABLE IF EXISTS cir_action_documents;
DROP TABLE IF EXISTS cir_actions;
DROP TABLE IF EXISTS cir_recipients;
DROP TABLE IF EXISTS circulations;
DROP TABLE IF EXISTS cir_status_codes;
DROP TABLE IF EXISTS correspondence_status_transitions;
DROP TABLE IF EXISTS correspondence_statuses;
DROP TABLE IF EXISTS correspondence_types;
DROP TABLE IF EXISTS correspondence_tags;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS global_default_roles;
SET FOREIGN_KEY_CHECKS=1;
-- ==========================================================
-- Table Creation
-- ==========================================================
-- Organizations Table
CREATE TABLE organizations (
org_id INT NOT NULL AUTO_INCREMENT,
org_code VARCHAR(20) NOT NULL,
org_name VARCHAR(255) NOT NULL,
primary_role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (org_id),
UNIQUE KEY ux_organizations_code (org_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Projects Table
CREATE TABLE projects (
project_id INT NOT NULL AUTO_INCREMENT,
project_code VARCHAR(20) NOT NULL,
project_name VARCHAR(255) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (project_id),
UNIQUE KEY ux_projects_code (project_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- NEW: Contracts Table
-- Stores information about each contract.
CREATE TABLE contracts (
contract_id INT NOT NULL AUTO_INCREMENT,
contract_code VARCHAR(50) NOT NULL,
contract_name VARCHAR(255) NOT NULL,
description TEXT,
start_date DATE,
end_date DATE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (contract_id),
UNIQUE KEY ux_contracts_code (contract_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- NEW: Contract Parties Table
-- Links contracts, projects, and organizations together.
CREATE TABLE contract_parties (
contract_id INT NOT NULL,
project_id INT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (contract_id, project_id, org_id),
CONSTRAINT fk_cp_contract FOREIGN KEY (contract_id) REFERENCES contracts(contract_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Users Table (RBAC)
CREATE TABLE users (
user_id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
org_id INT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
last_login TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (user_id),
UNIQUE KEY ux_users_username (username),
UNIQUE KEY ux_users_email (email),
CONSTRAINT fk_users_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Roles Table (RBAC)
CREATE TABLE roles (
role_id INT NOT NULL AUTO_INCREMENT,
role_code VARCHAR(50) NOT NULL,
role_name VARCHAR(100) NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (role_id),
UNIQUE KEY ux_roles_code (role_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Permissions Table (RBAC)
CREATE TABLE permissions (
permission_id INT NOT NULL AUTO_INCREMENT,
permission_code VARCHAR(100) NOT NULL,
description TEXT,
PRIMARY KEY (permission_id),
UNIQUE KEY ux_permissions_code (permission_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Role Permissions Junction Table (RBAC)
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
CONSTRAINT fk_rp_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_rp_permission FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- User Roles Junction Table (RBAC) - For global/system-level roles
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- User Project Roles Junction Table (RBAC) - For project-specific roles
CREATE TABLE user_project_roles (
user_id INT NOT NULL,
project_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, project_id, role_id),
CONSTRAINT fk_upr_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Project Parties Table
CREATE TABLE project_parties (
project_id INT NOT NULL,
org_id INT NOT NULL,
role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NOT NULL,
is_contractor TINYINT(1) GENERATED ALWAYS AS (IF(role = 'CONTRACTOR', 1, NULL)) STORED,
PRIMARY KEY (project_id, org_id),
UNIQUE KEY uq_project_parties_contractor (project_id, is_contractor),
CONSTRAINT fk_pp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_pp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ... (The rest of your original script for correspondences, technical docs, etc. remains here) ...
-- Global default roles (template defaults)
CREATE TABLE global_default_roles (
id TINYINT NOT NULL DEFAULT 1,
role ENUM('OWNER','DESIGNER','CONSULTANT') NOT NULL,
position TINYINT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (id, role, position),
UNIQUE KEY ux_gdr_unique_org_per_role (id, role, org_id),
CONSTRAINT fk_gdr_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- ... (The rest of your original script continues from here) ...

View File

@@ -1,316 +1,316 @@
-- ==========================================================
-- DMS v0.5.0
-- Database v5.1 - Deploy Script Schema (Complete & Revised)
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 10.11
-- Notes:
-- - This is a consolidated and updated version.
-- - Removed 'rfas' table.
-- - Added tables for Contracts, detailed Circulations,
-- Technical Document Workflow, and Correspondence Revisions.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- Drop tables in reverse order of creation due to dependencies
DROP TABLE IF EXISTS audit_logs;
DROP TABLE IF EXISTS user_project_roles;
DROP TABLE IF EXISTS user_roles;
DROP TABLE IF EXISTS role_permissions;
DROP TABLE IF EXISTS permissions;
DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS project_parties;
DROP TABLE IF EXISTS contract_parties;
DROP TABLE IF EXISTS contracts;
DROP TABLE IF EXISTS projects;
DROP TABLE IF EXISTS organizations;
DROP TABLE IF EXISTS correspondence_references;
DROP TABLE IF EXISTS correspondence_revisions;
DROP TABLE IF EXISTS correspondence_cc_recipients;
DROP TABLE IF EXISTS technical_doc_workflows;
DROP TABLE IF EXISTS correspondences;
DROP TABLE IF EXISTS email_details;
DROP TABLE IF EXISTS instruction_details;
DROP TABLE IF EXISTS letter_details;
DROP TABLE IF EXISTS memorandum_details;
DROP TABLE IF EXISTS minutes_of_meeting_details;
DROP TABLE IF EXISTS rfi_details;
DROP TABLE IF EXISTS rfa_items;
DROP TABLE IF EXISTS transmittal_items;
DROP TABLE IF EXISTS transmittals;
DROP TABLE IF EXISTS technicaldocs;
DROP TABLE IF EXISTS shop_drawing_revisions;
DROP TABLE IF EXISTS shop_drawings;
DROP TABLE IF EXISTS shop_drawing_sub_categories;
DROP TABLE IF EXISTS shop_drawing_main_categories;
DROP TABLE IF EXISTS contract_dwg_subcat_cat_map;
DROP TABLE IF EXISTS contract_dwg_sub_cat;
DROP TABLE IF EXISTS contract_dwg_cat;
DROP TABLE IF EXISTS contract_drawings;
DROP TABLE IF EXISTS circulation_assignees;
DROP TABLE IF EXISTS circulations;
DROP TABLE IF EXISTS cir_action_documents;
DROP TABLE IF EXISTS cir_actions;
DROP TABLE IF EXISTS cir_recipients;
DROP TABLE IF EXISTS cir_status_codes;
DROP TABLE IF EXISTS correspondence_status_transitions;
DROP TABLE IF EXISTS correspondence_statuses;
DROP TABLE IF EXISTS correspondence_types;
DROP TABLE IF EXISTS correspondence_tags;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS global_default_roles;
SET FOREIGN_KEY_CHECKS=1;
-- ==========================================================
-- Table Creation
-- ==========================================================
CREATE TABLE organizations (
org_id INT NOT NULL AUTO_INCREMENT,
org_code VARCHAR(20) NOT NULL,
org_name VARCHAR(255) NOT NULL,
primary_role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (org_id),
UNIQUE KEY ux_organizations_code (org_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE projects (
project_id INT NOT NULL AUTO_INCREMENT,
project_code VARCHAR(20) NOT NULL,
project_name VARCHAR(255) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (project_id),
UNIQUE KEY ux_projects_code (project_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE contracts (
contract_id INT NOT NULL AUTO_INCREMENT,
contract_code VARCHAR(50) NOT NULL,
contract_name VARCHAR(255) NOT NULL,
description TEXT,
start_date DATE,
end_date DATE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (contract_id),
UNIQUE KEY ux_contracts_code (contract_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE contract_parties (
contract_id INT NOT NULL,
project_id INT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (contract_id, project_id, org_id),
CONSTRAINT fk_cp_contract FOREIGN KEY (contract_id) REFERENCES contracts(contract_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE users (
user_id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
org_id INT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
last_login TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (user_id),
UNIQUE KEY ux_users_username (username),
UNIQUE KEY ux_users_email (email),
CONSTRAINT fk_users_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE roles (
role_id INT NOT NULL AUTO_INCREMENT,
role_code VARCHAR(50) NOT NULL,
role_name VARCHAR(100) NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (role_id),
UNIQUE KEY ux_roles_code (role_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE permissions (
permission_id INT NOT NULL AUTO_INCREMENT,
permission_code VARCHAR(100) NOT NULL,
description TEXT,
PRIMARY KEY (permission_id),
UNIQUE KEY ux_permissions_code (permission_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
CONSTRAINT fk_rp_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_rp_permission FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE user_project_roles (
user_id INT NOT NULL,
project_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, project_id, role_id),
CONSTRAINT fk_upr_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE project_parties (
project_id INT NOT NULL,
org_id INT NOT NULL,
role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NOT NULL,
is_contractor TINYINT(1) GENERATED ALWAYS AS (IF(role = 'CONTRACTOR', 1, NULL)) STORED,
PRIMARY KEY (project_id, org_id),
UNIQUE KEY uq_project_parties_contractor (project_id, is_contractor),
CONSTRAINT fk_pp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_pp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Correspondence & Workflow Tables
CREATE TABLE correspondence_types (
type_id INT NOT NULL AUTO_INCREMENT,
type_code VARCHAR(20) NOT NULL,
type_name VARCHAR(100) NOT NULL,
PRIMARY KEY (type_id),
UNIQUE KEY ux_ct_code (type_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_statuses (
status_id INT NOT NULL AUTO_INCREMENT,
status_code VARCHAR(20) NOT NULL,
status_name VARCHAR(100) NOT NULL,
PRIMARY KEY (status_id),
UNIQUE KEY ux_cs_code (status_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondences (
id INT NOT NULL AUTO_INCREMENT,
project_id INT NOT NULL,
document_number VARCHAR(100) NOT NULL,
title VARCHAR(500) NOT NULL,
issue_date DATE NOT NULL,
status_id INT NOT NULL,
type_id INT NOT NULL,
originator_org_id INT NOT NULL,
created_by_user_id INT NOT NULL,
root_id INT NULL, -- for revisions
version INT NOT NULL DEFAULT 1,
submitted_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY ux_corr_doc_num (document_number),
CONSTRAINT fk_corr_project FOREIGN KEY (project_id) REFERENCES projects(project_id),
CONSTRAINT fk_corr_status FOREIGN KEY (status_id) REFERENCES correspondence_statuses(status_id),
CONSTRAINT fk_corr_type FOREIGN KEY (type_id) REFERENCES correspondence_types(type_id),
CONSTRAINT fk_corr_org FOREIGN KEY (originator_org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_corr_user FOREIGN KEY (created_by_user_id) REFERENCES users(user_id),
CONSTRAINT fk_corr_root FOREIGN KEY (root_id) REFERENCES correspondences(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_revisions (
revision_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
version_number INT NOT NULL,
change_reason TEXT NOT NULL,
changed_by_user_id INT NOT NULL,
changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
document_data_json JSON NOT NULL,
PRIMARY KEY (revision_id),
CONSTRAINT fk_cr_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE,
CONSTRAINT fk_cr_user FOREIGN KEY (changed_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE cir_status_codes (
code VARCHAR(20) NOT NULL,
description VARCHAR(255),
sort_order INT NOT NULL,
PRIMARY KEY (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE circulations (
cir_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
org_id INT NOT NULL,
cir_subject VARCHAR(500) NOT NULL,
cir_status_code VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
created_by_user_id INT NOT NULL,
submitted_at TIMESTAMP NULL,
closed_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (cir_id),
CONSTRAINT fk_cir_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id),
CONSTRAINT fk_cir_org FOREIGN KEY (org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_cir_status FOREIGN KEY (cir_status_code) REFERENCES cir_status_codes(code),
CONSTRAINT fk_cir_user FOREIGN KEY (created_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE circulation_assignees (
assignee_id INT NOT NULL AUTO_INCREMENT,
cir_id INT NOT NULL,
user_id INT NOT NULL,
assignee_type ENUM('MAIN', 'ACTION', 'INFO') NOT NULL,
deadline DATE NULL,
action_taken TEXT NULL,
is_completed BOOLEAN NOT NULL DEFAULT FALSE,
completed_at TIMESTAMP NULL,
PRIMARY KEY (assignee_id),
UNIQUE KEY ux_cir_user_type (cir_id, user_id, assignee_type),
CONSTRAINT fk_ca_cir FOREIGN KEY (cir_id) REFERENCES circulations(cir_id) ON DELETE CASCADE,
CONSTRAINT fk_ca_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE technical_doc_workflows (
workflow_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
sequence INT NOT NULL,
org_id INT NOT NULL,
status ENUM('PENDING', 'APPROVED', 'REJECTED', 'APPROVED_WITH_COMMENTS', 'FORWARDED', 'RETURNED') NOT NULL DEFAULT 'PENDING',
comments TEXT,
processed_by_user_id INT NULL,
processed_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (workflow_id),
UNIQUE KEY ux_workflow_corr_sequence (correspondence_id, sequence),
CONSTRAINT fk_tdw_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE,
CONSTRAINT fk_tdw_org FOREIGN KEY (org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_tdw_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_status_transitions(
type_id INT NOT NULL,
from_status_id INT NOT NULL,
to_status_id INT NOT NULL,
PRIMARY KEY (type_id, from_status_id, to_status_id),
CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(type_id),
CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_statuses(status_id),
CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_statuses(status_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- (The rest of your original tables for technical docs, drawings, etc. would follow here)
-- ==========================================================
-- DMS v0.5.0
-- Database v5.1 - Deploy Script Schema (Complete & Revised)
-- Server: Container Station on QNAP TS-473A
-- Database service: MariaDB 10.11
-- Notes:
-- - This is a consolidated and updated version.
-- - Removed 'rfas' table.
-- - Added tables for Contracts, detailed Circulations,
-- Technical Document Workflow, and Correspondence Revisions.
-- ==========================================================
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- Drop tables in reverse order of creation due to dependencies
DROP TABLE IF EXISTS audit_logs;
DROP TABLE IF EXISTS user_project_roles;
DROP TABLE IF EXISTS user_roles;
DROP TABLE IF EXISTS role_permissions;
DROP TABLE IF EXISTS permissions;
DROP TABLE IF EXISTS roles;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS project_parties;
DROP TABLE IF EXISTS contract_parties;
DROP TABLE IF EXISTS contracts;
DROP TABLE IF EXISTS projects;
DROP TABLE IF EXISTS organizations;
DROP TABLE IF EXISTS correspondence_references;
DROP TABLE IF EXISTS correspondence_revisions;
DROP TABLE IF EXISTS correspondence_cc_recipients;
DROP TABLE IF EXISTS technical_doc_workflows;
DROP TABLE IF EXISTS correspondences;
DROP TABLE IF EXISTS email_details;
DROP TABLE IF EXISTS instruction_details;
DROP TABLE IF EXISTS letter_details;
DROP TABLE IF EXISTS memorandum_details;
DROP TABLE IF EXISTS minutes_of_meeting_details;
DROP TABLE IF EXISTS rfi_details;
DROP TABLE IF EXISTS rfa_items;
DROP TABLE IF EXISTS transmittal_items;
DROP TABLE IF EXISTS transmittals;
DROP TABLE IF EXISTS technicaldocs;
DROP TABLE IF EXISTS shop_drawing_revisions;
DROP TABLE IF EXISTS shop_drawings;
DROP TABLE IF EXISTS shop_drawing_sub_categories;
DROP TABLE IF EXISTS shop_drawing_main_categories;
DROP TABLE IF EXISTS contract_dwg_subcat_cat_map;
DROP TABLE IF EXISTS contract_dwg_sub_cat;
DROP TABLE IF EXISTS contract_dwg_cat;
DROP TABLE IF EXISTS contract_drawings;
DROP TABLE IF EXISTS circulation_assignees;
DROP TABLE IF EXISTS circulations;
DROP TABLE IF EXISTS cir_action_documents;
DROP TABLE IF EXISTS cir_actions;
DROP TABLE IF EXISTS cir_recipients;
DROP TABLE IF EXISTS cir_status_codes;
DROP TABLE IF EXISTS correspondence_status_transitions;
DROP TABLE IF EXISTS correspondence_statuses;
DROP TABLE IF EXISTS correspondence_types;
DROP TABLE IF EXISTS correspondence_tags;
DROP TABLE IF EXISTS tags;
DROP TABLE IF EXISTS global_default_roles;
SET FOREIGN_KEY_CHECKS=1;
-- ==========================================================
-- Table Creation
-- ==========================================================
CREATE TABLE organizations (
org_id INT NOT NULL AUTO_INCREMENT,
org_code VARCHAR(20) NOT NULL,
org_name VARCHAR(255) NOT NULL,
primary_role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (org_id),
UNIQUE KEY ux_organizations_code (org_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE projects (
project_id INT NOT NULL AUTO_INCREMENT,
project_code VARCHAR(20) NOT NULL,
project_name VARCHAR(255) NOT NULL,
description TEXT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (project_id),
UNIQUE KEY ux_projects_code (project_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE contracts (
contract_id INT NOT NULL AUTO_INCREMENT,
contract_code VARCHAR(50) NOT NULL,
contract_name VARCHAR(255) NOT NULL,
description TEXT,
start_date DATE,
end_date DATE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (contract_id),
UNIQUE KEY ux_contracts_code (contract_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE contract_parties (
contract_id INT NOT NULL,
project_id INT NOT NULL,
org_id INT NOT NULL,
PRIMARY KEY (contract_id, project_id, org_id),
CONSTRAINT fk_cp_contract FOREIGN KEY (contract_id) REFERENCES contracts(contract_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_cp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE users (
user_id INT NOT NULL AUTO_INCREMENT,
username VARCHAR(50) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
email VARCHAR(100) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
org_id INT,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
last_login TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (user_id),
UNIQUE KEY ux_users_username (username),
UNIQUE KEY ux_users_email (email),
CONSTRAINT fk_users_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE SET NULL ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE roles (
role_id INT NOT NULL AUTO_INCREMENT,
role_code VARCHAR(50) NOT NULL,
role_name VARCHAR(100) NOT NULL,
description TEXT,
is_system BOOLEAN NOT NULL DEFAULT FALSE,
PRIMARY KEY (role_id),
UNIQUE KEY ux_roles_code (role_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE permissions (
permission_id INT NOT NULL AUTO_INCREMENT,
permission_code VARCHAR(100) NOT NULL,
description TEXT,
PRIMARY KEY (permission_id),
UNIQUE KEY ux_permissions_code (permission_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE role_permissions (
role_id INT NOT NULL,
permission_id INT NOT NULL,
PRIMARY KEY (role_id, permission_id),
CONSTRAINT fk_rp_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_rp_permission FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE user_roles (
user_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, role_id),
CONSTRAINT fk_ur_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_ur_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE user_project_roles (
user_id INT NOT NULL,
project_id INT NOT NULL,
role_id INT NOT NULL,
PRIMARY KEY (user_id, project_id, role_id),
CONSTRAINT fk_upr_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_upr_role FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE project_parties (
project_id INT NOT NULL,
org_id INT NOT NULL,
role ENUM('OWNER','DESIGNER','CONSULTANT','CONTRACTOR') NOT NULL,
is_contractor TINYINT(1) GENERATED ALWAYS AS (IF(role = 'CONTRACTOR', 1, NULL)) STORED,
PRIMARY KEY (project_id, org_id),
UNIQUE KEY uq_project_parties_contractor (project_id, is_contractor),
CONSTRAINT fk_pp_project FOREIGN KEY (project_id) REFERENCES projects(project_id) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT fk_pp_org FOREIGN KEY (org_id) REFERENCES organizations(org_id) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- Correspondence & Workflow Tables
CREATE TABLE correspondence_types (
type_id INT NOT NULL AUTO_INCREMENT,
type_code VARCHAR(20) NOT NULL,
type_name VARCHAR(100) NOT NULL,
PRIMARY KEY (type_id),
UNIQUE KEY ux_ct_code (type_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_statuses (
status_id INT NOT NULL AUTO_INCREMENT,
status_code VARCHAR(20) NOT NULL,
status_name VARCHAR(100) NOT NULL,
PRIMARY KEY (status_id),
UNIQUE KEY ux_cs_code (status_code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondences (
id INT NOT NULL AUTO_INCREMENT,
project_id INT NOT NULL,
document_number VARCHAR(100) NOT NULL,
title VARCHAR(500) NOT NULL,
issue_date DATE NOT NULL,
status_id INT NOT NULL,
type_id INT NOT NULL,
originator_org_id INT NOT NULL,
created_by_user_id INT NOT NULL,
root_id INT NULL, -- for revisions
version INT NOT NULL DEFAULT 1,
submitted_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY ux_corr_doc_num (document_number),
CONSTRAINT fk_corr_project FOREIGN KEY (project_id) REFERENCES projects(project_id),
CONSTRAINT fk_corr_status FOREIGN KEY (status_id) REFERENCES correspondence_statuses(status_id),
CONSTRAINT fk_corr_type FOREIGN KEY (type_id) REFERENCES correspondence_types(type_id),
CONSTRAINT fk_corr_org FOREIGN KEY (originator_org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_corr_user FOREIGN KEY (created_by_user_id) REFERENCES users(user_id),
CONSTRAINT fk_corr_root FOREIGN KEY (root_id) REFERENCES correspondences(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_revisions (
revision_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
version_number INT NOT NULL,
change_reason TEXT NOT NULL,
changed_by_user_id INT NOT NULL,
changed_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
document_data_json JSON NOT NULL,
PRIMARY KEY (revision_id),
CONSTRAINT fk_cr_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE,
CONSTRAINT fk_cr_user FOREIGN KEY (changed_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE cir_status_codes (
code VARCHAR(20) NOT NULL,
description VARCHAR(255),
sort_order INT NOT NULL,
PRIMARY KEY (code)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE circulations (
cir_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
org_id INT NOT NULL,
cir_subject VARCHAR(500) NOT NULL,
cir_status_code VARCHAR(20) NOT NULL DEFAULT 'DRAFT',
created_by_user_id INT NOT NULL,
submitted_at TIMESTAMP NULL,
closed_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (cir_id),
CONSTRAINT fk_cir_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id),
CONSTRAINT fk_cir_org FOREIGN KEY (org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_cir_status FOREIGN KEY (cir_status_code) REFERENCES cir_status_codes(code),
CONSTRAINT fk_cir_user FOREIGN KEY (created_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE circulation_assignees (
assignee_id INT NOT NULL AUTO_INCREMENT,
cir_id INT NOT NULL,
user_id INT NOT NULL,
assignee_type ENUM('MAIN', 'ACTION', 'INFO') NOT NULL,
deadline DATE NULL,
action_taken TEXT NULL,
is_completed BOOLEAN NOT NULL DEFAULT FALSE,
completed_at TIMESTAMP NULL,
PRIMARY KEY (assignee_id),
UNIQUE KEY ux_cir_user_type (cir_id, user_id, assignee_type),
CONSTRAINT fk_ca_cir FOREIGN KEY (cir_id) REFERENCES circulations(cir_id) ON DELETE CASCADE,
CONSTRAINT fk_ca_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE technical_doc_workflows (
workflow_id INT NOT NULL AUTO_INCREMENT,
correspondence_id INT NOT NULL,
sequence INT NOT NULL,
org_id INT NOT NULL,
status ENUM('PENDING', 'APPROVED', 'REJECTED', 'APPROVED_WITH_COMMENTS', 'FORWARDED', 'RETURNED') NOT NULL DEFAULT 'PENDING',
comments TEXT,
processed_by_user_id INT NULL,
processed_at TIMESTAMP NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (workflow_id),
UNIQUE KEY ux_workflow_corr_sequence (correspondence_id, sequence),
CONSTRAINT fk_tdw_correspondence FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE,
CONSTRAINT fk_tdw_org FOREIGN KEY (org_id) REFERENCES organizations(org_id),
CONSTRAINT fk_tdw_user FOREIGN KEY (processed_by_user_id) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
CREATE TABLE correspondence_status_transitions(
type_id INT NOT NULL,
from_status_id INT NOT NULL,
to_status_id INT NOT NULL,
PRIMARY KEY (type_id, from_status_id, to_status_id),
CONSTRAINT fk_cst_type FOREIGN KEY (type_id) REFERENCES correspondence_types(type_id),
CONSTRAINT fk_cst_from FOREIGN KEY (from_status_id) REFERENCES correspondence_statuses(status_id),
CONSTRAINT fk_cst_to FOREIGN KEY (to_status_id) REFERENCES correspondence_statuses(status_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-- (The rest of your original tables for technical docs, drawings, etc. would follow here)
-- For brevity, I've included the core structure. You can append the remaining tables from your original file.

View File

@@ -1,143 +1,143 @@
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- ==========================================================
-- 1. ลบ Stored Procedure เก่า (ย้าย Logic ไป NestJS)
-- ==========================================================
-- ตรรกะนี้ (การสร้าง Revision) จะถูกย้ายไปจัดการใน
-- CorrespondenceService และ RfaService ของ NestJS
DROP PROCEDURE IF EXISTS sp_create_correspondence_revision;
-- ==========================================================
-- 2. สร้าง Stored Procedure ใหม่ (สำหรับ Document Numbering)
-- ==========================================================
-- ใช้ Procedure นี้เพื่อจัดการ Race Condition
-- ในการดึงเลขที่เอกสารล่าสุด
-- ----------------------------------------------------------
DELIMITER $$
CREATE PROCEDURE sp_get_next_document_number(
IN p_project_id INT,
IN p_organization_id INT,
IN p_type_id INT,
IN p_year INT,
OUT p_next_number INT
)
BEGIN
DECLARE v_last_number INT;
-- 1. พยายามดึงแถวปัจจุบันและ "ล็อก" (FOR UPDATE)
-- เพื่อป้องกันไม่ให้ Transaction อื่นอ่านค่านี้จนกว่าเราจะเสร็จ
SELECT last_number
INTO v_last_number
FROM document_number_counters
WHERE
project_id = p_project_id AND
originator_organization_id = p_organization_id AND
correspondence_type_id = p_type_id AND
current_year = p_year
FOR UPDATE;
-- 2. ตรวจสอบว่าพบบแถวหรือไม่
IF v_last_number IS NULL THEN
-- 2a. ไม่พบ (นี่คือเลขที่ "1" ของ Key นี้)
INSERT INTO document_number_counters
(project_id, originator_organization_id, correspondence_type_id, current_year, last_number)
VALUES
(p_project_id, p_organization_id, p_type_id, p_year, 1);
SET p_next_number = 1;
ELSE
-- 2b. พบ (บวกเลขที่เดิม)
SET p_next_number = v_last_number + 1;
UPDATE document_number_counters
SET last_number = p_next_number
WHERE
project_id = p_project_id AND
originator_organization_id = p_organization_id AND
correspondence_type_id = p_type_id AND
current_year = p_year;
END IF;
-- (Transaction จะ Commit อัตโนมัติเมื่อ Procedure จบ)
END$$
DELIMITER ;
-- ==========================================================
-- 3. สร้าง Views (สำหรับช่วย Backend/Frontend)
-- ==========================================================
-- ----------------------------------------------------------
-- View 1: v_user_tasks (สำหรับ Dashboard "งานของฉัน" Req 5.3)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_user_tasks AS
SELECT
ca.user_id,
c.id AS circulation_id,
c.organization_id,
c.circulation_no,
c.circulation_subject,
ca.assignee_type,
ca.deadline,
c.created_at,
c.correspondence_id
FROM circulations c
JOIN circulation_assignees ca ON c.id = ca.circulation_id
WHERE
ca.is_completed = FALSE
AND ca.assignee_type IN ('MAIN', 'ACTION');
-- ----------------------------------------------------------
-- View 2: v_audit_log_details (สำหรับ Activity Feed Req 6.1)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_audit_log_details AS
SELECT
al.audit_id,
al.user_id,
al.action,
al.entity_type,
al.entity_id,
al.details_json,
al.ip_address,
al.created_at,
u.username,
u.first_name,
u.last_name,
u.email
FROM audit_logs al
LEFT JOIN users u ON al.user_id = u.user_id;
-- ----------------------------------------------------------
-- View 3: v_user_all_permissions (สำหรับ RBAC 2 ระดับ Req 4.2)
-- ----------------------------------------------------------
-- View นี้จะรวมสิทธิ์ 2 ระดับ (Global และ Project)
-- (หมายเหตุ: ไม่รวม Contract-level เนื่องจากยังไม่มีตาราง)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_user_all_permissions AS
-- 1. สิทธิ์ระดับ Global (project_id IS NULL)
SELECT
ur.user_id,
NULL AS project_id,
p.permission_code
FROM user_roles ur
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id
UNION
-- 2. สิทธิ์ระดับ Project
SELECT
upr.user_id,
upr.project_id,
p.permission_code
FROM user_project_roles upr
JOIN role_permissions rp ON upr.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id;
SET NAMES utf8mb4;
SET time_zone = '+07:00';
SET FOREIGN_KEY_CHECKS=0;
-- ==========================================================
-- 1. ลบ Stored Procedure เก่า (ย้าย Logic ไป NestJS)
-- ==========================================================
-- ตรรกะนี้ (การสร้าง Revision) จะถูกย้ายไปจัดการใน
-- CorrespondenceService และ RfaService ของ NestJS
DROP PROCEDURE IF EXISTS sp_create_correspondence_revision;
-- ==========================================================
-- 2. สร้าง Stored Procedure ใหม่ (สำหรับ Document Numbering)
-- ==========================================================
-- ใช้ Procedure นี้เพื่อจัดการ Race Condition
-- ในการดึงเลขที่เอกสารล่าสุด
-- ----------------------------------------------------------
DELIMITER $$
CREATE PROCEDURE sp_get_next_document_number(
IN p_project_id INT,
IN p_organization_id INT,
IN p_type_id INT,
IN p_year INT,
OUT p_next_number INT
)
BEGIN
DECLARE v_last_number INT;
-- 1. พยายามดึงแถวปัจจุบันและ "ล็อก" (FOR UPDATE)
-- เพื่อป้องกันไม่ให้ Transaction อื่นอ่านค่านี้จนกว่าเราจะเสร็จ
SELECT last_number
INTO v_last_number
FROM document_number_counters
WHERE
project_id = p_project_id AND
originator_organization_id = p_organization_id AND
correspondence_type_id = p_type_id AND
current_year = p_year
FOR UPDATE;
-- 2. ตรวจสอบว่าพบบแถวหรือไม่
IF v_last_number IS NULL THEN
-- 2a. ไม่พบ (นี่คือเลขที่ "1" ของ Key นี้)
INSERT INTO document_number_counters
(project_id, originator_organization_id, correspondence_type_id, current_year, last_number)
VALUES
(p_project_id, p_organization_id, p_type_id, p_year, 1);
SET p_next_number = 1;
ELSE
-- 2b. พบ (บวกเลขที่เดิม)
SET p_next_number = v_last_number + 1;
UPDATE document_number_counters
SET last_number = p_next_number
WHERE
project_id = p_project_id AND
originator_organization_id = p_organization_id AND
correspondence_type_id = p_type_id AND
current_year = p_year;
END IF;
-- (Transaction จะ Commit อัตโนมัติเมื่อ Procedure จบ)
END$$
DELIMITER ;
-- ==========================================================
-- 3. สร้าง Views (สำหรับช่วย Backend/Frontend)
-- ==========================================================
-- ----------------------------------------------------------
-- View 1: v_user_tasks (สำหรับ Dashboard "งานของฉัน" Req 5.3)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_user_tasks AS
SELECT
ca.user_id,
c.id AS circulation_id,
c.organization_id,
c.circulation_no,
c.circulation_subject,
ca.assignee_type,
ca.deadline,
c.created_at,
c.correspondence_id
FROM circulations c
JOIN circulation_assignees ca ON c.id = ca.circulation_id
WHERE
ca.is_completed = FALSE
AND ca.assignee_type IN ('MAIN', 'ACTION');
-- ----------------------------------------------------------
-- View 2: v_audit_log_details (สำหรับ Activity Feed Req 6.1)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_audit_log_details AS
SELECT
al.audit_id,
al.user_id,
al.action,
al.entity_type,
al.entity_id,
al.details_json,
al.ip_address,
al.created_at,
u.username,
u.first_name,
u.last_name,
u.email
FROM audit_logs al
LEFT JOIN users u ON al.user_id = u.user_id;
-- ----------------------------------------------------------
-- View 3: v_user_all_permissions (สำหรับ RBAC 2 ระดับ Req 4.2)
-- ----------------------------------------------------------
-- View นี้จะรวมสิทธิ์ 2 ระดับ (Global และ Project)
-- (หมายเหตุ: ไม่รวม Contract-level เนื่องจากยังไม่มีตาราง)
-- ----------------------------------------------------------
CREATE OR REPLACE VIEW v_user_all_permissions AS
-- 1. สิทธิ์ระดับ Global (project_id IS NULL)
SELECT
ur.user_id,
NULL AS project_id,
p.permission_code
FROM user_roles ur
JOIN role_permissions rp ON ur.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id
UNION
-- 2. สิทธิ์ระดับ Project
SELECT
upr.user_id,
upr.project_id,
p.permission_code
FROM user_project_roles upr
JOIN role_permissions rp ON upr.role_id = rp.role_id
JOIN permissions p ON rp.permission_id = p.permission_id;
SET FOREIGN_KEY_CHECKS=1;

View File

@@ -1,47 +1,47 @@
-- trigger
DROP TRIGGER IF EXISTS trg_rfa_revisions_is_current;
DROP TRIGGER IF EXISTS trg_rfa_revisions_is_current_upd;
DROP TRIGGER IF EXISTS trg_rfa_revisions_auto_reset;
-- ============================================================
-- ⚙️ TRIGGERS
-- ============================================================
-- ป้องกันไม่ให้มี revision ที่ is_current=TRUE ซ้ำใน rfa_id เดียวกัน
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_is_current
BEFORE INSERT ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE THEN
IF (SELECT COUNT(*) FROM rfa_revisions WHERE rfa_id = NEW.rfa_id AND is_current = TRUE) > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot insert more than one current revision per RFA.';
END IF;
END IF;
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_is_current_upd
BEFORE UPDATE ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE AND (OLD.is_current IS NULL OR OLD.is_current = FALSE) THEN
IF (SELECT COUNT(*) FROM rfa_revisions WHERE rfa_id = NEW.rfa_id AND is_current = TRUE AND correspondences_id <> OLD.correspondences_id) > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot set more than one current revision per RFA.';
END IF;
END IF;
END$$
DELIMITER ;
-- ทำให้เวลาสร้าง revision ใหม่ที่ is_current=TRUE ระบบจะ set revision เดิมเป็น FALSE อัตโนมัติ
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_auto_reset
BEFORE INSERT ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE THEN
UPDATE rfa_revisions
SET is_current = FALSE
WHERE rfa_id = NEW.rfa_id;
END IF;
END$$
-- trigger
DROP TRIGGER IF EXISTS trg_rfa_revisions_is_current;
DROP TRIGGER IF EXISTS trg_rfa_revisions_is_current_upd;
DROP TRIGGER IF EXISTS trg_rfa_revisions_auto_reset;
-- ============================================================
-- ⚙️ TRIGGERS
-- ============================================================
-- ป้องกันไม่ให้มี revision ที่ is_current=TRUE ซ้ำใน rfa_id เดียวกัน
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_is_current
BEFORE INSERT ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE THEN
IF (SELECT COUNT(*) FROM rfa_revisions WHERE rfa_id = NEW.rfa_id AND is_current = TRUE) > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot insert more than one current revision per RFA.';
END IF;
END IF;
END$$
DELIMITER ;
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_is_current_upd
BEFORE UPDATE ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE AND (OLD.is_current IS NULL OR OLD.is_current = FALSE) THEN
IF (SELECT COUNT(*) FROM rfa_revisions WHERE rfa_id = NEW.rfa_id AND is_current = TRUE AND correspondences_id <> OLD.correspondences_id) > 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Cannot set more than one current revision per RFA.';
END IF;
END IF;
END$$
DELIMITER ;
-- ทำให้เวลาสร้าง revision ใหม่ที่ is_current=TRUE ระบบจะ set revision เดิมเป็น FALSE อัตโนมัติ
DELIMITER $$
CREATE TRIGGER trg_rfa_revisions_auto_reset
BEFORE INSERT ON rfa_revisions
FOR EACH ROW
BEGIN
IF NEW.is_current = TRUE THEN
UPDATE rfa_revisions
SET is_current = FALSE
WHERE rfa_id = NEW.rfa_id;
END IF;
END$$
DELIMITER ;