From a78c9941be5bdd30c739a00b1e9f844a8b1c9581 Mon Sep 17 00:00:00 2001 From: admin Date: Sat, 29 Nov 2025 16:50:34 +0700 Subject: [PATCH] 251129:1700 update to 1.4.5 --- .agent/rules/code-execution.md | 20 + .agent/rules/code-references.md | 11 + .agent/rules/coding-standards.md | 9 +- 0_Requirements_V1_4_5.md | 803 +++++ 1_FullStackJS_V1_4_5.md | 962 ++++++ 2_2_phase_6a_workflow_dsl.sql | 16 - 2_Backend_Plan_V1_4_5.md | 2137 +++++++++++++ 3_Frontend_Plan_V1_4_5.md | 978 ++++++ 4_Data_Dictionary_V1_4_5.md | 2673 +++++++++++++++++ 8_lcbp3_v1_4_5.sql | 1477 +++++++++ 8_lcbp3_v1_4_5_seed.sql | 2127 +++++++++++++ GEMINI.md | 37 + backend/package.json | 5 +- backend/src/Workflow DSL Specification.md | 125 + backend/src/database/seeds/run-seed.ts | 67 + .../seeds/workflow-definitions.seed.ts | 102 +- .../circulation-workflow.service.ts | 164 + .../modules/circulation/circulation.module.ts | 11 +- .../correspondence-workflow.service.ts | 153 + .../correspondence/correspondence.module.ts | 16 +- .../modules/json-schema/json-schema.module.ts | 8 +- .../json-schema/json-schema.service.ts | 534 +++- backend/src/modules/rfa/dto/create-rfa.dto.ts | 80 +- backend/src/modules/rfa/dto/submit-rfa.dto.ts | 13 + .../rfa/entities/rfa-revision.entity.ts | 27 +- .../rfa/entities/rfa-workflow.entity.ts | 20 +- .../src/modules/rfa/entities/rfa.entity.ts | 14 +- .../src/modules/rfa/rfa-workflow.service.ts | 193 ++ backend/src/modules/rfa/rfa.controller.ts | 34 +- backend/src/modules/rfa/rfa.module.ts | 41 +- backend/src/modules/rfa/rfa.service.ts | 189 +- .../dto/workflow-transition.dto.ts | 30 + .../entities/workflow-definition.entity.ts | 59 +- .../entities/workflow-history.entity.ts | 33 +- .../entities/workflow-instance.entity.ts | 46 +- .../workflow-engine/workflow-dsl.service.ts | 272 +- .../workflow-engine.controller.ts | 111 +- .../workflow-engine/workflow-engine.module.ts | 28 +- .../workflow-engine.service.ts | 54 +- .../workflow-engine/workflow-event.service.ts | 96 + docs/6B.md | 191 -- 8_lcbp3_v1_4_4.sql => docs/8_lcbp3_v1_4_4.sql | 56 + .../8_lcbp3_v1_4_4_seed.sql | 0 docs/DMS v1.3.0 - Interactive Report.html | 724 ----- .../Markdown/0_Requirements_V1_4_4.md | 110 +- .../Markdown/1_FullStackJS_V1_4_4.md | 0 .../Markdown/2_Backend_Plan_V1_4_4.Phase6A.md | 397 +++ .../2_Backend_Plan_V1_4_4.Phase_Addition.md | 709 +++++ .../Markdown/2_Backend_Plan_V1_4_4.md | 0 .../Markdown/3_Frontend_Plan_V1_4_4.md | 0 .../Markdown/4_Data_Dictionary_V1_4_4.md | 0 docs/frontend.md | 94 - docs/prompt.md | 460 +-- docs/ux_ui.md | 42 - users.sql | 173 -- 55 files changed, 14641 insertions(+), 2090 deletions(-) create mode 100644 .agent/rules/code-execution.md create mode 100644 .agent/rules/code-references.md create mode 100644 0_Requirements_V1_4_5.md create mode 100644 1_FullStackJS_V1_4_5.md delete mode 100644 2_2_phase_6a_workflow_dsl.sql create mode 100644 2_Backend_Plan_V1_4_5.md create mode 100644 3_Frontend_Plan_V1_4_5.md create mode 100644 4_Data_Dictionary_V1_4_5.md create mode 100644 8_lcbp3_v1_4_5.sql create mode 100644 8_lcbp3_v1_4_5_seed.sql create mode 100644 backend/src/Workflow DSL Specification.md create mode 100644 backend/src/database/seeds/run-seed.ts create mode 100644 backend/src/modules/circulation/circulation-workflow.service.ts create mode 100644 backend/src/modules/correspondence/correspondence-workflow.service.ts create mode 100644 backend/src/modules/rfa/dto/submit-rfa.dto.ts create mode 100644 backend/src/modules/rfa/rfa-workflow.service.ts create mode 100644 backend/src/modules/workflow-engine/dto/workflow-transition.dto.ts create mode 100644 backend/src/modules/workflow-engine/workflow-event.service.ts delete mode 100644 docs/6B.md rename 8_lcbp3_v1_4_4.sql => docs/8_lcbp3_v1_4_4.sql (95%) rename 8_lcbp3_v1_4_4_seed.sql => docs/8_lcbp3_v1_4_4_seed.sql (100%) delete mode 100644 docs/DMS v1.3.0 - Interactive Report.html rename 0_Requirements_V1_4_4.md => docs/Markdown/0_Requirements_V1_4_4.md (91%) rename 1_FullStackJS_V1_4_4.md => docs/Markdown/1_FullStackJS_V1_4_4.md (100%) create mode 100644 docs/Markdown/2_Backend_Plan_V1_4_4.Phase6A.md create mode 100644 docs/Markdown/2_Backend_Plan_V1_4_4.Phase_Addition.md rename 2_Backend_Plan_V1_4_4.md => docs/Markdown/2_Backend_Plan_V1_4_4.md (100%) rename 3_Frontend_Plan_V1_4_4.md => docs/Markdown/3_Frontend_Plan_V1_4_4.md (100%) rename 4_Data_Dictionary_V1_4_4.md => docs/Markdown/4_Data_Dictionary_V1_4_4.md (100%) delete mode 100644 docs/frontend.md delete mode 100644 docs/ux_ui.md delete mode 100644 users.sql diff --git a/.agent/rules/code-execution.md b/.agent/rules/code-execution.md new file mode 100644 index 0000000..b24d4c2 --- /dev/null +++ b/.agent/rules/code-execution.md @@ -0,0 +1,20 @@ +--- +trigger: always_on +--- + +--- + +description: Control which shell commands the agent may run automatically. +allowAuto: ["pnpm test:watch", "pnpm test:debug", "pnpm test:e2e", "git status"] +denyAuto: ["rm -rf", "Remove-Item", "git push --force", "curl | bash"] +alwaysReview: true +scopes: ["backend/src/**", "backend/test/**", "frontend/app/**"] + +--- + +# Execution Rules + +- Only auto-execute commands that are explicitly listed in `allowAuto`. +- Commands in denyAuto must always be blocked, even if manually requested. +- All shell operations that create, modify, or delete files in `backend/src/` or `backend/test/` or `frontend/app/`require human review. +- Alert if environment variables related to DB connection or secrets would be displayed or logged. diff --git a/.agent/rules/code-references.md b/.agent/rules/code-references.md new file mode 100644 index 0000000..d174d3b --- /dev/null +++ b/.agent/rules/code-references.md @@ -0,0 +1,11 @@ +--- +trigger: always_on +--- + +# Basic data: + +- For Application Requirements, refer to 0_Requirements_V1_4_4.md +- For detailed Full Stack JS, refer to 1_FullStackJS_V1_4_4.md +- For detailed Backend Development Plan, refer to 2_Backend_Plan_V1_4_4.md, 2_Backend_Plan_V1_4_4.Phase_Addition.md, 2_Backend_Plan_Phase6A_V1_4_3.md +- For detailed Frontend Development Plan, refer to 3_Frontend_Plan_V1_4_4.md +- For detailed DB schema, refer to 4_Data_Dictionary_V1_4_4.md, 8_lcbp3_v1_4_4.sql, 8_lcbp3_v1_4_4_seed.sql diff --git a/.agent/rules/coding-standards.md b/.agent/rules/coding-standards.md index 513a1d3..77d9a7e 100644 --- a/.agent/rules/coding-standards.md +++ b/.agent/rules/coding-standards.md @@ -1,6 +1,11 @@ --- trigger: always_on -glob: -description: --- +# 📏 Coding Standards (Strict Rules) + +1. **Language:** - Code logic & Naming: **English**. + - Comments & Documentation: **Thai Language (āļ āļēāļĐāļēāđ„āļ—āļĒ)**. +2. **File Structure:** Follow `kebab-case` for files (e.g., `user-service.ts`). +3. **Comments:** Add `// File: path/to/file` at the top of every file. +4. **Secrets:** NEVER hardcode secrets. Use `process.env` and assume `docker-compose.override.yml` is used for local dev. diff --git a/0_Requirements_V1_4_5.md b/0_Requirements_V1_4_5.md new file mode 100644 index 0000000..64cac50 --- /dev/null +++ b/0_Requirements_V1_4_5.md @@ -0,0 +1,803 @@ +# 📝 **Documents Management System Version 1.4.5: Application Requirements Specification** + +**āļŠāļ–āļēāļ™āļ°:** FINAL-Rev.05 +**āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-29 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļžāļ·āđ‰āļ™āļāļēāļ™:** v1.4.4 +**Classification:** Internal Technical Documentation + +## 📌 **1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ** + +āļŠāļĢāđ‰āļēāļ‡āđ€āļ§āđ‡āļšāđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļšāļšāļĢāļīāļŦāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Document Management System - DMS) āđāļšāļšāļ„āļĢāļšāļ§āļ‡āļˆāļĢ āļ—āļĩāđˆāđ€āļ™āđ‰āļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” āļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Integrity) āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§āđƒāļ™āļ­āļ™āļēāļ„āļ• (Scalability) āđ‚āļ”āļĒāđāļāđ‰āđ„āļ‚āļ›āļąāļāļŦāļē Race Condition āđāļĨāļ°āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāđ€āļŠāļ–āļĩāļĒāļĢāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒ āđāļĨāļ°āđƒāļŠāđ‰ Unified Workflow Engine āđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™ + +- āļĄāļĩāļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļŦāļĨāļąāļāđƒāļ™āļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ” āļˆāļąāļ”āđ€āļāđ‡āļš āļ„āđ‰āļ™āļŦāļē āđāļŠāļĢāđŒ āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ€āļ­āļāļŠāļēāļĢ +- āļŠāđˆāļ§āļĒāļĨāļ”āļāļēāļĢāđƒāļŠāđ‰āđ€āļ­āļāļŠāļēāļĢāļāļĢāļ°āļ”āļēāļĐ āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāđƒāļ™āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ +- āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļŠāļ°āļ”āļ§āļāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™āļĢāđˆāļ§āļĄāļāļąāļ™āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ­āļ‡āļāļĢāļ“āđŒ +- **āđ€āļŠāļĢāļīāļĄ:** āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļ‚āļ­āļ‡āļĢāļ°āļšāļšāļ”āđ‰āļ§āļĒāļĄāļēāļ•āļĢāļāļēāļĢāļ›āđ‰āļ­āļ‡āļāļąāļ™āļ—āļĩāđˆāļ—āļąāļ™āļŠāļĄāļąāļĒ +- **āđ€āļŠāļĢāļīāļĄ:** āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™āļ‚āļ­āļ‡āļĢāļ°āļšāļšāļ”āđ‰āļ§āļĒāļāļĨāđ„āļ resilience patterns +- **āđ€āļŠāļĢāļīāļĄ:** āļŠāļĢāđ‰āļēāļ‡āļĢāļ°āļšāļš monitoring āđāļĨāļ° observability āļ—āļĩāđˆāļ„āļĢāļ­āļšāļ„āļĨāļļāļĄ + +## 🛠ïļ **2. āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāđāļĨāļ°āđ€āļ—āļ„āđ‚āļ™āđ‚āļĨāļĒāļĩ (System Architecture & Technology Stack)** + +āđƒāļŠāđ‰āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāđāļšāļš Headless/API-First āļ—āļĩāđˆāļ—āļąāļ™āļŠāļĄāļąāļĒ āļ—āļģāļ‡āļēāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļšāļ™ QNAP Server āļœāđˆāļēāļ™ Container Station āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļŠāļ°āļ”āļ§āļāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđāļĨāļ°āļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļē + +**Domain:** `np-dms.work`, `www.np-dms.work` +**IP:** 159.192.126.103 +**Docker Network:** āļ—āļļāļ Service āļˆāļ°āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļœāđˆāļēāļ™āđ€āļ„āļĢāļ·āļ­āļ‚āđˆāļēāļĒāļāļĨāļēāļ‡āļŠāļ·āđˆāļ­ `lcbp3` āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļŠāļ·āđˆāļ­āļŠāļēāļĢāļāļąāļ™āđ„āļ”āđ‰ + +### **2.1 Infrastructure & Environment:** + +- **Server:** QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B) +- **Containerization:** Container Station (Docker & Docker Compose) āđƒāļŠāđ‰ UI āļ‚āļ­āļ‡ Container Station āđ€āļ›āđ‡āļ™āļŦāļĨāļąāļ āđƒāļ™āļāļēāļĢ configuration āđāļĨāļ°āļāļēāļĢāļĢāļąāļ™ docker command +- **Development Environment:** VS Code/Cursor on Windows 11 +- **Data Storage:** `/share/dms-data` āļšāļ™ QNAP +- **āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”:** āđ„āļĄāđˆāļŠāļēāļĄāļēāļĢāļ–āđƒāļŠāđ‰ .env āđƒāļ™āļāļēāļĢāļāļģāļŦāļ™āļ”āļ•āļąāļ§āđāļ›āļĢāļ āļēāļĒāļ™āļ­āļāđ„āļ”āđ‰ āļ•āđ‰āļ­āļ‡āļāļģāļŦāļ™āļ”āđƒāļ™ docker-compose.yml āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ + +### **2.2 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Configuration (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡):** + +- āđƒāļŠāđ‰ `docker-compose.yml` āļŠāļģāļŦāļĢāļąāļš environment variables āļ•āļēāļĄāļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āļ‚āļ­āļ‡ QNAP +- **Secrets Management (āđƒāļŦāļĄāđˆ):** + - āļŦāđ‰āļēāļĄāļĢāļ°āļšāļļ Sensitive Secrets (Password, Keys) āđƒāļ™ `docker-compose.yml` āļŦāļĨāļąāļ + - āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āđ„āļŸāļĨāđŒ `docker-compose.override.yml` (āļ—āļĩāđˆāļ–āļđāļ gitignore) āļŠāļģāļŦāļĢāļąāļš Inject Environment Variables āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļ„āļ§āļēāļĄāļĨāļąāļšāđƒāļ™āđāļ•āđˆāļĨāļ° Environment (Dev/Prod) + - āđ„āļŸāļĨāđŒ `docker-compose.yml` āļŦāļĨāļąāļāđƒāļŦāđ‰āđƒāļŠāđˆāļ„āđˆāļē Dummy āļŦāļĢāļ·āļ­āļ§āđˆāļēāļ‡āđ„āļ§āđ‰ +- **āđāļ•āđˆāļ•āđ‰āļ­āļ‡āļĄāļĩ mechanism āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ sensitive secrets āļ­āļĒāđˆāļēāļ‡āļ›āļĨāļ­āļ”āļ āļąāļĒ** āđ‚āļ”āļĒāđƒāļŠāđ‰: + - Docker secrets (āļ–āđ‰āļēāļĢāļ­āļ‡āļĢāļąāļš) + - External secret management (Hashicorp Vault) āļŦāļĢāļ·āļ­ + - Encrypted environment variables +- Development environment āļĒāļąāļ‡āđƒāļŠāđ‰ .env āđ„āļ”āđ‰ āđāļ•āđˆāļ•āđ‰āļ­āļ‡āđ„āļĄāđˆ commit āđ€āļ‚āđ‰āļē version control +- āļ•āđ‰āļ­āļ‡āļĄāļĩ configuration validation during application startup +- āļ•āđ‰āļ­āļ‡āđāļĒāļ configuration āļ•āļēāļĄ environment (development, staging, production) + +### **2.3 Core Services:** + +- **Code Hosting:** Gitea (Self-hosted on QNAP) + + - Application name: git + - Service name: gitea + - Domain: `git.np-dms.work` + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āđ€āļ›āđ‡āļ™āļĻāļđāļ™āļĒāđŒāļāļĨāļēāļ‡āđƒāļ™āļāļēāļĢāđ€āļāđ‡āļšāđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡āđ‚āļ„āđ‰āļ” (Source Code) āļŠāļģāļŦāļĢāļąāļšāļ—āļļāļāļŠāđˆāļ§āļ™ + +- **Backend / Data Platform:** NestJS + + - Application name: lcbp3-backend + - Service name: backend + - Domain: `backend.np-dms.work` + - Framework: NestJS (Node.js, TypeScript, ESM) + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Models), āļŠāļĢāđ‰āļēāļ‡ API, āļˆāļąāļ”āļāļēāļĢāļŠāļīāļ—āļ˜āļīāđŒāļœāļđāđ‰āđƒāļŠāđ‰ (Roles & Permissions), āđāļĨāļ°āļŠāļĢāđ‰āļēāļ‡ Workflow āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš + +- **Database:** MariaDB 10.11 + + - Application name: lcbp3-db + - Service name: mariadb + - Domain: `db.np-dms.work` + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļąāđ‰āļ‡āļŦāļĄāļ” + - Tooling: DBeaver (Community Edition), phpmyadmin āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļšāđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ + +- **Database Management:** phpMyAdmin + + - Application name: lcbp3-db + - Service: phpmyadmin:5-apache + - Service name: pma + - Domain: `pma.np-dms.work` + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ mariadb āļœāđˆāļēāļ™ Web UI + +- **Frontend:** Next.js + + - Application name: lcbp3-frontend + - Service name: frontend + - Domain: `lcbp3.np-dms.work` + - Framework: Next.js (App Router, React, TypeScript, ESM) + - Styling: Tailwind CSS + PostCSS + - Component Library: shadcn/ui + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļŠāļĢāđ‰āļēāļ‡āļŦāļ™āđ‰āļēāļ•āļēāđ€āļ§āđ‡āļšāđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāđƒāļŦāđ‰āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āđ€āļ‚āđ‰āļēāļĄāļēāļ”āļđ Dashboard, āļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ, āđāļĨāļ°āļ•āļīāļ”āļ•āļēāļĄāļ‡āļēāļ™ āđ‚āļ”āļĒāļˆāļ°āļŠāļ·āđˆāļ­āļŠāļēāļĢāļāļąāļš Backend āļœāđˆāļēāļ™ API + +- **Workflow Automation:** n8n + + - Application name: lcbp3-n8n + - Service: n8nio/n8n:latest + - Service name: n8n + - Domain: `n8n.np-dms.work` + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢ workflow āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Backend āđāļĨāļ° Line + +- **Reverse Proxy:** Nginx Proxy Manager + + - Application name: lcbp3-npm + - Service: Nginx Proxy Manager (nginx-proxy-manage: latest) + - Service name: npm + - Domain: `npm.np-dms.work` + - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āđ€āļ›āđ‡āļ™āļ”āđˆāļēāļ™āļŦāļ™āđ‰āļēāđƒāļ™āļāļēāļĢāļĢāļąāļš-āļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ āļˆāļąāļ”āļāļēāļĢāđ‚āļ”āđ€āļĄāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”, āļ—āļģāļŦāļ™āđ‰āļēāļ—āļĩāđˆāđ€āļ›āđ‡āļ™ Proxy āļŠāļĩāđ‰āđ„āļ›āļĒāļąāļ‡ Service āļ—āļĩāđˆāļ–āļđāļāļ•āđ‰āļ­āļ‡, āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢ SSL Certificate (HTTPS) āđƒāļŦāđ‰āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī + +- **Search Engine:** Elasticsearch +- **Cache:** Redis + +### **2.4 Business Logic & Consistency (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡):** + +- **2.4.1 Unified Workflow Engine (āļŦāļĨāļąāļ):** āļĢāļ°āļšāļšāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Correspondence, RFA, Circulation) āļ•āđ‰āļ­āļ‡ āđƒāļŠāđ‰ Engine āļāļĨāļēāļ‡āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ āđ‚āļ”āļĒāļāļģāļŦāļ™āļ” Logic āļœāđˆāļēāļ™ Workflow DSL (JSON Configuration) āđāļ—āļ™āļāļēāļĢāđ€āļ‚āļĩāļĒāļ™ Hard-coded āļĨāļ‡āđƒāļ™āļ•āļēāļĢāļēāļ‡ + +- **2.4.2 Separation of Concerns:** Module āļ•āđˆāļēāļ‡āđ† (RFA, Correspondence) āļˆāļ°āđ€āļāđ‡āļšāđ€āļ‰āļžāļēāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (Data) āļŠāđˆāļ§āļ™āļŠāļ–āļēāļ™āļ°āđāļĨāļ°āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (State Transition) āļˆāļ°āļ–āļđāļāļˆāļąāļ”āļāļēāļĢāđ‚āļ”āļĒ Workflow Engine + +- **2.4.3 Idempotency & Locking:** āđƒāļŠāđ‰āļāļĨāđ„āļāđ€āļ”āļīāļĄāđƒāļ™āļāļēāļĢāļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļ—āļģāļĢāļēāļĒāļāļēāļĢāļ‹āđ‰āļģ + +- **2.4.4 Optimistic Locking (āđƒāļŦāļĄāđˆ):** āđƒāļŠāđ‰ Version Column āđƒāļ™ Database āļ„āļ§āļšāļ„āļđāđˆāļāļąāļš Redis Lock āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ āđ€āļžāļ·āđˆāļ­āđ€āļ›āđ‡āļ™ Safety Net āļŠāļąāđ‰āļ™āļŠāļļāļ”āļ—āđ‰āļēāļĒ + +- **2.4.5** **āļˆāļ°āđ„āļĄāđˆāļĄāļĩāļāļēāļĢāđƒāļŠāđ‰ SQL Triggers** āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļ•āļĢāļĢāļāļ°āļ‹āđˆāļ­āļ™āđ€āļĢāđ‰āļ™ (Hidden Logic) āđāļĨāļ°āļ„āļ§āļēāļĄāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđƒāļ™āļāļēāļĢāļ”āļĩāļšāļąāļ + +### **2.5 Data Migration āđāļĨāļ° Schema Versioning:** + +- āļ•āđ‰āļ­āļ‡āļĄāļĩ database migration scripts āļŠāļģāļŦāļĢāļąāļšāļ—āļļāļ schema change āđ‚āļ”āļĒāđƒāļŠāđ‰ TypeORM migrations +- āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš rollback āļ‚āļ­āļ‡ migration āđ„āļ”āđ‰ +- āļ•āđ‰āļ­āļ‡āļĄāļĩ data seeding strategy āļŠāļģāļŦāļĢāļąāļš environment āļ•āđˆāļēāļ‡āđ† (development, staging, production) +- āļ•āđ‰āļ­āļ‡āļĄāļĩ version compatibility between schema versions +- Migration scripts āļ•āđ‰āļ­āļ‡āļœāđˆāļēāļ™āļāļēāļĢāļ—āļ”āļŠāļ­āļšāđƒāļ™ staging environment āļāđˆāļ­āļ™ production +- āļ•āđ‰āļ­āļ‡āļĄāļĩ database backup āļāđˆāļ­āļ™āļ—āļģ migration āđƒāļ™ production + +### **2.6 āļāļĨāļĒāļļāļ—āļ˜āđŒāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™āđāļĨāļ°āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (Resilience & Error Handling Strategy)** + +- 2.6.1 Circuit Breaker Pattern: āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš external service calls (Email, LINE, Elasticsearch) +- 2.6.2 Retry Mechanism: āļ”āđ‰āļ§āļĒ exponential backoff āļŠāļģāļŦāļĢāļąāļš transient failures +- 2.6.3 Fallback Strategies: Graceful degradation āđ€āļĄāļ·āđˆāļ­āļšāļĢāļīāļāļēāļĢāļ āļēāļĒāļ™āļ­āļāļĨāđ‰āļĄāđ€āļŦāļĨāļ§ +- 2.6.4 Error Handling: Error messages āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāđ€āļ›āļīāļ”āđ€āļœāļĒāļ‚āđ‰āļ­āļĄāļđāļĨ sensitive +- 2.6.5 Monitoring: Centralized error monitoring āđāļĨāļ° alerting system + +## **ðŸ“Ķ 3. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™ (Functional Requirements)** + +### **3.1. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāđāļĨāļ°āļ­āļ‡āļ„āđŒāļāļĢ** + +- 3.1.1. āđ‚āļ„āļĢāļ‡āļāļēāļĢ (Projects): āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āļŦāļĨāļēāļĒāđ‚āļ„āļĢāļ‡āļāļēāļĢāđ„āļ”āđ‰ (āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļĄāļĩ 4 āđ‚āļ„āļĢāļ‡āļāļēāļĢ āđāļĨāļ°āļˆāļ°āđ€āļžāļīāđˆāļĄāļ‚āļķāđ‰āļ™āđƒāļ™āļ­āļ™āļēāļ„āļ•) +- 3.1.2. āļŠāļąāļāļāļē (Contracts): āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āđāļ•āđˆāļĨāļ°āļŠāļąāļāļāļēāđ„āļ”āđ‰ āđƒāļ™āđāļ•āđˆāļĨāļ°āđ‚āļ„āļĢāļ‡āļāļēāļĢ āļĄāļĩāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļŠāļąāļāļāļē āļŦāļĢāļ·āļ­āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 1 āļŠāļąāļāļāļē +- 3.1.3. āļ­āļ‡āļ„āđŒāļāļĢ (Organizations): + - āļĄāļĩāļŦāļĨāļēāļĒāļ­āļ‡āļ„āđŒāļāļĢāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ āļ­āļ‡āļ„āđŒāļāļĢāļ“āđŒāļ—āļĩāđˆāđ€āļ›āđ‡āļ™ Owner, Designer āđāļĨāļ° Consultant āļŠāļēāļĄāļēāļĢāļ–āļ­āļĒāļđāđˆāđƒāļ™āļŦāļĨāļēāļĒāđ‚āļ„āļĢāļ‡āļāļēāļĢāđāļĨāļ°āļŦāļĨāļēāļĒāļŠāļąāļāļāļēāđ„āļ”āđ‰ + - Contractor āļˆāļ°āļ–āļ·āļ­ 1 āļŠāļąāļāļāļē āđāļĨāļ°āļ­āļĒāļđāđˆāđƒāļ™ 1 āđ‚āļ„āļĢāļ‡āļāļēāļĢāđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ + +### **3.2. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš (Correspondence Management)** + +- 3.2.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš (correspondences) āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ­āļ‡āļāļĢāļ“āļ·-āļ­āļ‡āļāļĢāļ“āđŒ āļ āļēāļĒāđƒāļ™ āđ‚āļ„āļĢāļ‡āļāļēāļĢ (Projects) āđāļĨāļ°āļĢāļ°āļŦāļ§āđˆāļēāļ‡ āļ­āļ‡āļ„āđŒāļāļĢ-āļ­āļ‡āļ„āđŒāļāļĢ āļ āļēāļĒāļ™āļ­āļ āđ‚āļ„āļĢāļ‡āļāļēāļĢ (Projects), āļĢāļ­āļ‡āļĢāļąāļš To (āļœāļđāđ‰āļĢāļąāļšāļŦāļĨāļąāļ) āđāļĨāļ° CC (āļœāļđāđ‰āļĢāļąāļšāļŠāļģāđ€āļ™āļē) āļŦāļĨāļēāļĒāļ­āļ‡āļ„āđŒāļāļĢ +- 3.2.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļĢāļđāļ›āđāļšāļš āđ„āļŸāļĨāđŒ PDF āļŦāļĨāļēāļĒāļ›āļĢāļ°āđ€āļ āļ— (Types) āđ€āļŠāđˆāļ™ āļˆāļ”āļŦāļĄāļēāļĒ (Letter), āļ­āļĩāđ€āļĄāļĨāđŒ (Email), Request for Information (RFI), āđāļĨāļ°āļŠāļēāļĄāļēāļĢāļ–āđ€āļžāļīāđˆāļĄāļ›āļĢāļ°āđ€āļ āļ—āđƒāļŦāļĄāđˆāđ„āļ”āđ‰āđƒāļ™āļ āļēāļĒāļŦāļĨāļąāļ‡ +- 3.2.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ (Correspondence): + - āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ (āđ€āļŠāđˆāļ™ Document Control) āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāļĢāļ­āđ„āļ§āđ‰āđƒāļ™āļŠāļ–āļēāļ™āļ° āļ‰āļšāļąāļšāļĢāđˆāļēāļ‡" (Draft) āđ„āļ”āđ‰ āļ‹āļķāđˆāļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļ•āđˆāļēāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļˆāļ°āļĄāļ­āļ‡āđ„āļĄāđˆāđ€āļŦāđ‡āļ™ + - āđ€āļĄāļ·āđˆāļ­āļāļ” "Submitted" āđāļĨāđ‰āļ§ āļāļēāļĢāđāļāđ‰āđ„āļ‚, āļ–āļ­āļ™āđ€āļ­āļāļŠāļēāļĢāļāļĨāļąāļšāđ„āļ›āļŠāļ–āļēāļ™āļ° Draft, āļŦāļĢāļ·āļ­āļĒāļāđ€āļĨāļīāļ (Cancel) āļˆāļ°āļ•āđ‰āļ­āļ‡āļ—āļģāđ‚āļ”āļĒāļœāļđāđ‰āđƒāļŠāđ‰āļĢāļ°āļ”āļąāļš Admin āļ‚āļķāđ‰āļ™āđ„āļ› āļžāļĢāđ‰āļ­āļĄāļĢāļ°āļšāļļāđ€āļŦāļ•āļļāļœāļĨ +- 3.2.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: + - āđ€āļ­āļāļŠāļēāļĢāļŠāļēāļĄāļēāļĢāļ–āļ­āđ‰āļēāļ‡āļ–āļķāļ‡ (Reference) āđ€āļ­āļāļŠāļēāļĢāļ‰āļšāļąāļšāļāđˆāļ­āļ™āļŦāļ™āđ‰āļēāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ‰āļšāļąāļš + - āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ” Tag āđ„āļ”āđ‰āļŦāļĨāļēāļĒ Tag āđ€āļžāļ·āđˆāļ­āļˆāļąāļ”āļāļĨāļļāđˆāļĄāđāļĨāļ°āđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ +- 3.2.5. Correspondence Routing & Workflow + - 3.2.5.1 Routing Templates (āđāļĄāđˆāđāļšāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­) + - āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĄāđˆāđāļšāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđ„āļ”āđ‰ + - āđāļĄāđˆāđāļšāļšāļŠāļēāļĄāļēāļĢāļ–āđ€āļ›āđ‡āļ™āđāļšāļšāļ—āļąāđˆāļ§āđ„āļ› (āđƒāļŠāđ‰āđ„āļ”āđ‰āļ—āļļāļāđ‚āļ„āļĢāļ‡āļāļēāļĢ) āļŦāļĢāļ·āļ­āđ€āļ‰āļžāļēāļ°āđ‚āļ„āļĢāļ‡āļāļēāļĢ + - āđāļ•āđˆāļĨāļ°āđāļĄāđˆāđāļšāļšāļ›āļĢāļ°āļāļ­āļšāļ”āđ‰āļ§āļĒāļĨāļģāļ”āļąāļšāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - āļāļēāļĢāļŠāđˆāļ‡āļˆāļēāļ Originator -> Organization 1 -> Organization 2 -> Organization 3 āđāļĨāđ‰āļ§āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄ (āđ‚āļ”āļĒāļ–āđ‰āļē āļ­āļ‡āļāļĢāļ“āđŒāđƒāļ”āđƒāļ™ Wouting āđƒāļŦāđ‰āļŠāđˆāļ‡āļāļĨāļąāļš āļāđ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĢāļ­āđƒāļŦāđ‰āļ–āļķāļ‡ āļ­āļ‡āļāļĢāļ“āļ·āđƒāļ™āļĨāļģāļ”āļąāļšāļ–āļąāļ”āđ„āļ›) + - 3.2.5.2 Routing Steps (āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­) āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđƒāļ™āđāļĄāđˆāđāļšāļšāļ•āđ‰āļ­āļ‡āļāļģāļŦāļ™āļ”: + - **āļĨāļģāļ”āļąāļšāļ‚āļąāđ‰āļ™āļ•āļ­āļ™** (Sequence) + - **āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļš** (To Organization) + - **āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ** (Purpose): āđ€āļžāļ·āđˆāļ­āļ­āļ™āļļāļĄāļąāļ•āļī (FOR_APPROVAL), āđ€āļžāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļš (FOR_REVIEW), āđ€āļžāļ·āđˆāļ­āļ—āļĢāļēāļš (FOR_INFORMATION), āđ€āļžāļ·āđˆāļ­āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (FOR_ACTION) + - **āļĢāļ°āļĒāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ„āļēāļ”āļŦāļ§āļąāļ‡** (Expected Duration) + - 3.2.5.3 Actual Routing Execution (āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āļˆāļĢāļīāļ‡) āđ€āļĄāļ·āđˆāļ­āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđāļĨāļ°āđ€āļĨāļ·āļ­āļāđƒāļŠāđ‰āđāļĄāđˆāđāļšāļš āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡: + - āļŠāļĢāđ‰āļēāļ‡āļĨāļģāļ”āļąāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āļ•āļēāļĄāđāļĄāđˆāđāļšāļš + - āļ•āļīāļ”āļ•āļēāļĄāļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™: āļŠāđˆāļ‡āđāļĨāđ‰āļ§ (SENT), āļāļģāļĨāļąāļ‡āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (IN_PROGRESS), āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđāļĨāđ‰āļ§ (ACTIONED), āļŠāđˆāļ‡āļ•āđˆāļ­āđāļĨāđ‰āļ§ (FORWARDED), āļ•āļ­āļšāļāļĨāļąāļšāđāļĨāđ‰āļ§ (REPLIED) + - āļĢāļ°āļšāļļāļ§āļąāļ™āļ„āļĢāļšāļāļģāļŦāļ™āļ” (Due Date) āļŠāļģāļŦāļĢāļąāļšāđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™ + - āļšāļąāļ™āļ—āļķāļāļœāļđāđ‰āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ + - 3.2.5.4 Routing Flexibility (āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™) + - āļŠāļēāļĄāļēāļĢāļ–āļ‚āđ‰āļēāļĄāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđ„āļ”āđ‰āđƒāļ™āļāļĢāļ“āļĩāļžāļīāđ€āļĻāļĐ (āđ‚āļ”āļĒāļœāļđāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ) + - āļŠāļēāļĄāļēāļĢāļ–āļŠāđˆāļ‡āļāļĨāļąāļšāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļēāđ„āļ”āđ‰ + - āļŠāļēāļĄāļēāļĢāļ–āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļ„āļīāļ”āđ€āļŦāđ‡āļ™āđƒāļ™āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™ + - āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļīāđ€āļĄāļ·āđˆāļ­āļ–āļķāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđƒāļŦāļĄāđˆāļŦāļĢāļ·āļ­āđƒāļāļĨāđ‰āļ„āļĢāļšāļāļģāļŦāļ™āļ” +- 3.2.6. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ: āļĄāļĩāļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒāļ”āļąāļ‡āļ™āļĩāđ‰ + - āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āļ§āļąāļ™āđāļĨāđ‰āļ§āđ€āļŠāļĢāđ‡āļˆ (Deadline) āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡ āļ­āļ‡āļāļĢāļ“āđŒ āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļœāļđāđ‰āļĢāļąāļšāđ„āļ”āđ‰ + - āļĄāļĩāļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ āđƒāļŦāđ‰āļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡āļ­āļ‡āļāļĢāļ“āđŒāļ—āļĩāđˆāđ€āļ›āđ‡āļ™ āļœāļđāđ‰āļĢāļąāļš/āļœāļđāđ‰āļŠāđˆāļ‡ āļ—āļĢāļēāļš āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆ āļŦāļĢāļ·āļ­āļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° + +### **3.3. āļāļēāļĢāļˆāļąāļ”āļāļēāđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē (Contract Drawing)** + +- 3.3.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē (Contract Drawing) āđƒāļŠāđ‰āđ€āļžāļ·āđˆāļ­āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš +- 3.3.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: āđ„āļŸāļĨāđŒ PDF +- 3.3.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ +- 3.3.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ āđƒāļ™ Shop Drawings, āļĄāļĩāļāļēāļĢāļˆāļąāļ”āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļ‚āļ­āļ‡ Contract Drawing + +### **3.4. āļāļēāļĢāļˆāļąāļ”āļāļēāđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ (Shop Drawing)** + +- 3.4.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ (Shop Drawing) āđƒāļŠāđ‰āđ€āđƒāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš āđ‚āļ”āļĒāļˆāļąāļ”āļŠāđˆāļ‡āļ”āđ‰āļ§āļĒ Request for Approval (RFA) +- 3.4.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: āđ„āļŸāļĨāđŒ PDF +- 3.4.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ +- 3.4.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ āđƒāļ™ Shop Drawings, āļĄāļĩāļāļēāļĢāļˆāļąāļ”āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļ‚āļ­āļ‡ Shop Drawings + +### **3.5. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Workflow (Unified Workflow)** + +- 3.5.1 Workflow Definition: + + - Admin āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚ Workflow Rule āđ„āļ”āđ‰āļœāđˆāļēāļ™āļŦāļ™āđ‰āļēāļˆāļ­ UI (DSL Editor) āļĢ + - āļ­āļ‡āļĢāļąāļšāļāļēāļĢāļāļģāļŦāļ™āļ” State, Transition, Required Role, Condition (JS Expression) + +- 3.5.2 Workflow Execution: + + - āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Instance āļ‚āļ­āļ‡ Workflow āļœāļđāļāļāļąāļšāđ€āļ­āļāļŠāļēāļĢ (Polymorphic) + - āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Action) āđ€āļŠāđˆāļ™ Approve, Reject, Comment, Return + - Auto-Action: āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļīāđ€āļĄāļ·āđˆāļ­āļ„āļĢāļšāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ (āđ€āļŠāđˆāļ™ Review āļ„āļĢāļšāļ—āļļāļāļ„āļ™) + +- 3.5.3 Flexibility: + + - āļĢāļ­āļ‡āļĢāļąāļš Parallel Review (āļŠāđˆāļ‡āđƒāļŦāđ‰āļŦāļĨāļēāļĒāļ„āļ™āļ•āļĢāļ§āļˆāļžāļĢāđ‰āļ­āļĄāļāļąāļ™) + - āļĢāļ­āļ‡āļĢāļąāļš Conditional Flow (āđ€āļŠāđˆāļ™ āļ–āđ‰āļēāļĒāļ­āļ”āđ€āļ‡āļīāļ™ > X āđƒāļŦāđ‰āđ€āļžāļīāđˆāļĄāļœāļđāđ‰āļ­āļ™āļļāļĄāļąāļ•āļī) + +- 3.5.4 Workflow āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī: +- āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ°āđ€āļ›āđ‡āļ™āļĨāļģāļ”āļąāļš āđ€āļŠāđˆāļ™ āļŠāđˆāļ‡āļˆāļēāļ Originator -> Organization 1 -> Organization 2 -> Organization 3 āđāļĨāđ‰āļ§āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄ (āđ‚āļ”āļĒāļ–āđ‰āļē āļ­āļ‡āļāļĢāļ“āđŒāđƒāļ”āđƒāļ™ Workflow āđƒāļŦāđ‰āļŠāđˆāļ‡āļāļĨāļąāļš āļāđ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĢāļ­āđƒāļŦāđ‰āļ–āļķāļ‡ āļ­āļ‡āļāļĢāļ“āļ·āđƒāļ™āļĨāļģāļ”āļąāļšāļ–āļąāļ”āđ„āļ›) + +- 3.5.5 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ: + - āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āļ§āļąāļ™āđāļĨāđ‰āļ§āđ€āļŠāļĢāđ‡āļˆ (Deadline) āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡ āļ­āļ‡āļāļĢāļ“āđŒ āļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™ Workflow āđ„āļ”āđ‰ + - āļĄāļĩāļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ āđƒāļŦāđ‰āļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡ āļ­āļ‡āļāļĢāļ“āđŒ āļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™ Workflow āļ—āļĢāļēāļš āđ€āļĄāļ·āđˆāļ­āļĄāļĩ RFA āđƒāļŦāļĄāđˆ āļŦāļĢāļ·āļ­āļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° + +### **3.6.āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (Transmittals)** + +- 3.6.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš āļ™āļģāļŠāđˆāļ‡ Request for Approval (RFAS) āļŦāļĨāļēāļĒāļ‰āļšāļąāļš āđ„āļ›āļĒāļąāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ­āļ·āđˆāļ™ +- 3.6.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: āđ„āļŸāļĨāđŒ PDF +- 3.6.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ +- 3.6.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ āđ€āļ›āđ‡āļ™āļŠāđˆāļ§āļ™āļŦāļ™āļķāđˆāļ‡āđƒāļ™ Correspondence + +### **3.7. āđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢ (Circulation Sheet)** + +- 3.7.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āļāļēāļĢāļŠāļ·āđˆāļ­āļŠāļēāļĢ āđ€āļ­āļāļŠāļēāļĢ (Correspondence) āļ—āļļāļāļ‰āļšāļąāļš āļˆāļ°āļĄāļĩāđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢāđ€āļžāļ·āđˆāļ­āļ„āļ§āļšāļ„āļļāļĄāđāļĨāļ°āļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™āļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ (āļŠāļēāļĄāļēāļĢāļ–āļ”āļđāđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰āđ€āļ‰āļžāļēāļ°āļ„āļ™āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ) +- 3.7.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: āđ„āļŸāļĨāđŒ PDF +- 3.7.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢāļ™āļąāđ‰āļ™ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ +- 3.7.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āļāļēāļĢāļĢāļ°āļšāļļāļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļš: + - āļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļŦāļĨāļąāļ (Main): āļĄāļĩāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ„āļ™ + - āļœāļđāđ‰āļĢāđˆāļ§āļĄāļ›āļāļīāļšāļąāļ•āļīāļ‡āļēāļ™ (Action): āļĄāļĩāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ„āļ™ + - āļœāļđāđ‰āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļĢāļąāļšāļ—āļĢāļēāļš (Information): āļĄāļĩāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ„āļ™ +- 3.7.5. āļāļēāļĢāļ•āļīāļ”āļ•āļēāļĄāļ‡āļēāļ™: + - āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āļ§āļąāļ™āđāļĨāđ‰āļ§āđ€āļŠāļĢāđ‡āļˆ (Deadline) āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ›āļĢāļ°āđ€āļ āļ— Main āđāļĨāļ° Action āđ„āļ”āđ‰ + - āļĄāļĩāļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđ€āļĄāļ·āđˆāļ­āļĄāļĩ Circulation āđƒāļŦāļĄāđˆ āđāļĨāļ°āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļĨāđˆāļ§āļ‡āļŦāļ™āđ‰āļēāļāđˆāļ­āļ™āļ–āļķāļ‡āļ§āļąāļ™āđāļĨāđ‰āļ§āđ€āļŠāļĢāđ‡āļˆ + - āļŠāļēāļĄāļēāļĢāļ–āļ›āļīāļ” Circulation āđ„āļ”āđ‰āđ€āļĄāļ·āđˆāļ­āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ•āļ­āļšāļāļĨāļąāļšāđ„āļ›āļĒāļąāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡ (Originator) āđāļĨāđ‰āļ§ āļŦāļĢāļ·āļ­ āļĢāļąāļšāļ—āļĢāļēāļšāđāļĨāđ‰āļ§ (For Information) + +### **3.8. āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđāļāđ‰āđ„āļ‚ (Revisions):** āļĢāļ°āļšāļšāļˆāļ°āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚ āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” + +### **3.9. āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒ (File Handling - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđƒāļŦāļāđˆ)** + +- **3.9.1 Two-Phase Storage Strategy:** + + 1. **Phase 1 (Upload):** āđ„āļŸāļĨāđŒāļ–āļđāļāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ€āļ‚āđ‰āļēāđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒ `temp/` āđāļĨāļ°āđ„āļ”āđ‰āļĢāļąāļš `temp_id` + 2. **Phase 2 (Commit):** āđ€āļĄāļ·āđˆāļ­ User āļāļ” Submit āļŸāļ­āļĢāđŒāļĄāļŠāļģāđ€āļĢāđ‡āļˆ āļĢāļ°āļšāļšāļˆāļ°āļĒāđ‰āļēāļĒāđ„āļŸāļĨāđŒāļˆāļēāļ `temp/` āđ„āļ›āļĒāļąāļ‡ `permanent/{YYYY}/{MM}/` āđāļĨāļ°āļšāļąāļ™āļ—āļķāļāļĨāļ‡ Database āļ āļēāļĒāđƒāļ™ Transaction āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ + 3. **Cleanup:** āļĄāļĩ Cron Job āļĨāļšāđ„āļŸāļĨāđŒāđƒāļ™ `temp/` āļ—āļĩāđˆāļ„āđ‰āļēāļ‡āđ€āļāļīāļ™ 24 āļŠāļĄ. (Orphan Files) + +- **3.9.2 Security:** + + - Virus Scan (ClamAV) āļāđˆāļ­āļ™āļĒāđ‰āļēāļĒāđ€āļ‚āđ‰āļē Permanent + - Whitelist File Types: PDF, DWG, DOCX, XLSX, ZIP + - Max Size: 50MB + - Access Control: āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒāļœāđˆāļēāļ™ Junction Table āļāđˆāļ­āļ™āđƒāļŦāđ‰ Download Link + +- **3.9.3 āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļ‚āļ­āļ‡āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒ:** + - āļ•āđ‰āļ­āļ‡āļĄāļĩāļāļēāļĢ scan virus āļŠāļģāļŦāļĢāļąāļšāđ„āļŸāļĨāđŒāļ—āļĩāđˆāļ­āļąāļ›āđ‚āļŦāļĨāļ”āļ—āļąāđ‰āļ‡āļŦāļĄāļ” āđ‚āļ”āļĒāđƒāļŠāđ‰ ClamAV āļŦāļĢāļ·āļ­āļšāļĢāļīāļāļēāļĢ third-party + - āļˆāļģāļāļąāļ”āļ›āļĢāļ°āđ€āļ āļ—āđ„āļŸāļĨāđŒāļ—āļĩāđˆāļ­āļ™āļļāļāļēāļ•: PDF, DWG, DOCX, XLSX, ZIP (āļ•āđ‰āļ­āļ‡āļĢāļ°āļšāļļāļĢāļēāļĒāļāļēāļĢāļ—āļĩāđˆāļŠāļąāļ”āđ€āļˆāļ™) + - āļ‚āļ™āļēāļ”āđ„āļŸāļĨāđŒāļŠāļđāļ‡āļŠāļļāļ”: 50MB āļ•āđˆāļ­āđ„āļŸāļĨāđŒ + - āđ„āļŸāļĨāđŒāļ•āđ‰āļ­āļ‡āļ–āļđāļāđ€āļāđ‡āļšāļ™āļ­āļ web root āđāļĨāļ°āđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ„āļ”āđ‰āļœāđˆāļēāļ™ authenticated endpoint āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ + - āļ•āđ‰āļ­āļ‡āļĄāļĩ file integrity check (checksum) āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāđāļāđ‰āđ„āļ‚āđ„āļŸāļĨāđŒ + - Download links āļ•āđ‰āļ­āļ‡āļĄāļĩ expiration time (default: 24 āļŠāļąāđˆāļ§āđ‚āļĄāļ‡) + - āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ audit log āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĄāļĩāļāļēāļĢāļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒāļŠāļģāļ„āļąāļ + +### **3.10. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Document Numbering - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** + +- 3.10.1. āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Running Number) āđ„āļ”āđ‰āđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļīāđāļĨāļ°āļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™āļŠāļđāļ‡ +- 3.10.2. **Logic āļāļēāļĢāļ™āļąāļšāđ€āļĨāļ‚ (Counter Logic):** āļāļēāļĢāļ™āļąāļšāđ€āļĨāļ‚āļˆāļ°āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđāļĒāļāļ•āļēāļĄ Key āļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļ‚āļķāđ‰āļ™: + - **Project** + **Originator** + **Type** + **Discipline** (āļ–āđ‰āļēāļĄāļĩ) + **Sub-Type** (āļ–āđ‰āļēāļĄāļĩ) + **Year** +- 3.10.3. **Format Template:** āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļāļģāļŦāļ™āļ”āļĢāļđāļ›āđāļšāļšāļ”āđ‰āļ§āļĒ **Token Replacement** āđ€āļŠāđˆāļ™: + - `{ORG}-{TYPE}-{DISCIPLINE}-{SEQ:4}-{REV}` -> `TEAM-RFA-STR-0001-A` + - āļĢāļ­āļ‡āļĢāļąāļš Token: `{PROJECT}`, `{ORG}`, `{TYPE}`, `{DISCIPLINE}`, `{SUBTYPE}`, `{SUBTYPE_NUM}`, `{YEAR}`, `{YEAR_SHORT}`, `{SEQ:n}` +- 3.10.4. **Transmittal Logic:** āļĢāļ­āļ‡āļĢāļąāļšāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļžāļīāđ€āļĻāļĐāļŠāļģāļŦāļĢāļąāļš Transmittal āļ—āļĩāđˆāđ€āļĨāļ‚āļ­āļēāļˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļ•āļēāļĄāļœāļđāđ‰āļĢāļąāļš (To Owner vs To Contractor) +- 3.10.5. **āļāļĨāđ„āļāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ:** āļĒāļąāļ‡āļ„āļ‡āđƒāļŠāđ‰ Redis Distributed Lock āđāļĨāļ° Optimistic Locking āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āđ€āļĨāļ‚āļ‹āđ‰āļģāļŦāļĢāļ·āļ­āļ‚āđ‰āļēāļĄ +- 3.10.6. āļ•āđ‰āļ­āļ‡āļĄāļĩ retry mechanism āđāļĨāļ° fallback strategy āđ€āļĄāļ·āđˆāļ­āļāļēāļĢ generate āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + +### **3.11 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ JSON Details (JSON & Performance - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** + +- **3.11.1 āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ** + + - āļˆāļąāļ”āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđāļšāļšāđ„āļ”āļ™āļēāļĄāļīāļāļ—āļĩāđˆāđ€āļ‰āļžāļēāļ°āđ€āļˆāļēāļ°āļˆāļ‡āļāļąāļšāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ—āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ + - āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§āļ‚āļ­āļ‡āļĢāļ°āļšāļšāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ database schema + - āļˆāļąāļ”āļāļēāļĢ metadata āđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ›āļĢāļ°āļāļ­āļšāļŠāļģāļŦāļĢāļąāļš correspondence, routing, āđāļĨāļ° workflows + +- **3.11.2 āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON Schema** + āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĄāļĩ predefined JSON schemas āļŠāļģāļŦāļĢāļąāļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāļ•āđˆāļēāļ‡āđ†: + + - **3.11.2.1 Correspondence Types** + - **GENERIC**: āļ‚āđ‰āļ­āļĄāļđāļĨāļžāļ·āđ‰āļ™āļāļēāļ™āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ—āļąāđˆāļ§āđ„āļ› + - **RFI**: āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ„āļģāļ–āļēāļĄāđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļēāļ‡āđ€āļ—āļ„āļ™āļīāļ„ + - **RFA**: āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļīāđāļšāļšāđāļĨāļ°āļ§āļąāļŠāļ”āļļ + - **TRANSMITTAL**: āļĢāļēāļĒāļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāđˆāļ‡āļ•āđˆāļ­ + - **LETTER**: āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļ”āļŦāļĄāļēāļĒāļ—āļēāļ‡āļāļēāļĢ + - **EMAIL**: āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļĩāđ€āļĄāļĨ + - **3.11.2.2 Rworkflow Types** + - **workflow_definitions**: āļāļŽāđāļĨāļ°āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - **workflow_histories**: āļŠāļ–āļēāļ™āļ°āđāļĨāļ°āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - **workflow_instances**: āļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™ + - **3.11.2.3 Audit Types** + - **AUDIT_LOG**: āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš + - **SECURITY_SCAN**: āļœāļĨāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ + +- **3.11.3 Virtual Columns (āđƒāļŦāļĄāđˆ):** āļŠāļģāļŦāļĢāļąāļš Field āđƒāļ™ JSON āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļē (Search) āļŦāļĢāļ·āļ­āļˆāļąāļ”āđ€āļĢāļĩāļĒāļ‡ (Sort) āļšāđˆāļ­āļĒāđ† **āļ•āđ‰āļ­āļ‡āļŠāļĢāđ‰āļēāļ‡ Generated Column (Virtual Column)** āđƒāļ™ Database āđāļĨāļ°āļ—āļģ Index āđ„āļ§āđ‰ āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāļŠāļđāļ‡āļŠāļļāļ” + +- **3.11.4 Validation Rules** + + - āļ•āđ‰āļ­āļ‡āļĄāļĩ JSON schema validation āļŠāļģāļŦāļĢāļąāļšāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ— + - āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš versioning āļ‚āļ­āļ‡ schema + - āļ•āđ‰āļ­āļ‡āļĄāļĩ default values āļŠāļģāļŦāļĢāļąāļš field āļ—āļĩāđˆāđ„āļĄāđˆāļšāļąāļ‡āļ„āļąāļš + - āļ•āđ‰āļ­āļ‡āļ•āļĢāļ§āļˆāļŠāļ­āļš data types āđāļĨāļ° format āđƒāļŦāđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡ + +- **3.11.5 Performance Requirements** + + - JSON field āļ•āđ‰āļ­āļ‡āļĄāļĩāļ‚āļ™āļēāļ”āđ„āļĄāđˆāđ€āļāļīāļ™ 50KB + - āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš indexing āļŠāļģāļŦāļĢāļąāļš field āļ—āļĩāđˆāđƒāļŠāđ‰āļ„āđ‰āļ™āļŦāļēāļšāđˆāļ­āļĒ + - āļ•āđ‰āļ­āļ‡āļĄāļĩ compression āļŠāļģāļŦāļĢāļąāļš JSON āļ‚āļ™āļēāļ”āđƒāļŦāļāđˆ + +- **3.11.6 Security Requirements** + - āļ•āđ‰āļ­āļ‡ sanitize JSON input āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ injection attacks + - āļ•āđ‰āļ­āļ‡ validate JSON structure āļāđˆāļ­āļ™āļšāļąāļ™āļ—āļķāļ + - āļ•āđ‰āļ­āļ‡ encrypt sensitive data āđƒāļ™ JSON fields + +### **3.12 āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļžāļīāđ€āļĻāļĐ** + +- **āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļŠāļđāļ‡ (Global) āļŦāļĢāļ·āļ­āļœāļđāđ‰āđ„āļ”āđ‰āļĢāļąāļšāļ­āļ™āļļāļāļēāļ•āđ€āļ›āđ‡āļ™āļāļĢāļ“āļĩāļžāļīāđ€āļĻāļĐ** + - āļŠāļēāļĄāļēāļĢāļ–āđ€āļĨāļ·āļ­āļ **āļŠāļĢāđ‰āļēāļ‡āđƒāļ™āļ™āļēāļĄāļ­āļ‡āļ„āđŒāļāļĢ (Create on behalf of)** āđ„āļ”āđ‰ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ­āļ­āļāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Running Number) āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ­āļ·āđˆāļ™āđ„āļ”āđ‰āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĨāđ‡āļ­āļāļ­āļīāļ™āđƒāļŦāļĄāđˆ + - āļŠāļēāļĄāļēāļĢāļ–āļ—āļģāļ‡āļēāļ™āđāļ—āļ™āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļ­āļ·āđˆāļ™āđ„āļ”āđ‰ Routing & Workflow āļ‚āļ­āļ‡ Correspondence, RFA, Circulation Sheet + +### 3.13. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ (Admin Panel for Master Data) + +- 3.13.1. **Disciplines Management:** Admin āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ– āđ€āļžāļīāđˆāļĄ/āļĨāļš/āđāļāđ‰āđ„āļ‚ āļŠāļēāļ‚āļēāļ‡āļēāļ™ (Disciplines) āđāļĒāļāļ•āļēāļĄāļŠāļąāļāļāļē (Contract) āđ„āļ”āđ‰ +- 3.13.2. **Sub-Type Mapping:** Admin āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ” Correspondence Sub-types āđāļĨāļ° Mapping āļĢāļŦāļąāļŠāļ•āļąāļ§āđ€āļĨāļ‚ (āđ€āļŠāđˆāļ™ MAT = 11) āđ„āļ”āđ‰ +- 3.13.3. **Numbering Format Configuration:** Admin āļ•āđ‰āļ­āļ‡āļĄāļĩ UI āļŠāļģāļŦāļĢāļąāļšāļ•āļąāđ‰āļ‡āļ„āđˆāļē Format Template āļ‚āļ­āļ‡āđāļ•āđˆāļĨāļ° Project/Type āđ„āļ”āđ‰āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđāļāđ‰āđ‚āļ„āđ‰āļ” + +## **🔐 4. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļŠāļīāļ—āļ˜āļīāđŒāđāļĨāļ°āļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ (Access Control Requirements)** + +### **4.1. āļ āļēāļžāļĢāļ§āļĄ:** āļœāļđāđ‰āđƒāļŠāđ‰āđāļĨāļ°āļ­āļ‡āļ„āđŒāļāļĢāļŠāļēāļĄāļēāļĢāļ–āļ”āļđāđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāđ„āļ”āđ‰āļ•āļēāļĄāļŠāļīāļ—āļ˜āļīāđŒāļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļš āđ‚āļ”āļĒāļĢāļ°āļšāļšāļŠāļīāļ—āļ˜āļīāđŒāļˆāļ°āđ€āļ›āđ‡āļ™āđāļšāļš Role-Based Access Control (RBAC) + +### **4.2. āļĨāļģāļ”āļąāļšāļŠāļąāđ‰āļ™āļ‚āļ­āļ‡āļŠāļīāļ—āļ˜āļīāđŒ (Permission Hierarchy)** + +- Global: āļŠāļīāļ—āļ˜āļīāđŒāļŠāļđāļ‡āļŠāļļāļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš +- Organization: āļŠāļīāļ—āļ˜āļīāđŒāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ āđ€āļ›āđ‡āļ™āļŠāļīāļ—āļ˜āļīāđŒāļžāļ·āđ‰āļ™āļāļēāļ™āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ +- Project: āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ āļˆāļ°āļ–āļđāļāļžāļīāļˆāļēāļĢāļ“āļēāđ€āļĄāļ·āđˆāļ­āļœāļđāđ‰āđƒāļŠāđ‰āļ­āļĒāļđāđˆāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢāļ™āļąāđ‰āļ™ +- Contract: āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āđƒāļ™āļŠāļąāļāļāļē āļˆāļ°āļ–āļđāļāļžāļīāļˆāļēāļĢāļ“āļēāđ€āļĄāļ·āđˆāļ­āļœāļđāđ‰āđƒāļŠāđ‰āļ­āļĒāļđāđˆāđƒāļ™āļŠāļąāļāļāļēāļ™āļąāđ‰āļ™ (āļŠāļąāļāļāļēāđ€āļ›āđ‡āļ™āļŠāđˆāļ§āļ™āļŦāļ™āļķāđˆāļ‡āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ) + +āļāļŽāļāļēāļĢāļšāļąāļ‡āļ„āļąāļšāđƒāļŠāđ‰: āđ€āļĄāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒ āļĢāļ°āļšāļšāļˆāļ°āļžāļīāļˆāļēāļĢāļ“āļēāļŠāļīāļ—āļ˜āļīāđŒāļˆāļēāļāļ—āļļāļāļĢāļ°āļ”āļąāļšāļ—āļĩāđˆāļœāļđāđ‰āđƒāļŠāđ‰āļĄāļĩ āđāļĨāļ°āđƒāļŠāđ‰ āļŠāļīāļ—āļ˜āļīāđŒāļ—āļĩāđˆāļĄāļēāļāļ—āļĩāđˆāļŠāļļāļ” (Most Permissive) āđ€āļ›āđ‡āļ™āļ•āļąāļ§āļ•āļąāļ”āļŠāļīāļ™ + +āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļœāļđāđ‰āđƒāļŠāđ‰ A āđ€āļ›āđ‡āļ™ Viewer āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ āđāļ•āđˆāļ–āļđāļāļĄāļ­āļšāļŦāļĄāļēāļĒāđ€āļ›āđ‡āļ™ Editor āđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ X āđ€āļĄāļ·āđˆāļ­āļ­āļĒāļđāđˆāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ X āļœāļđāđ‰āđƒāļŠāđ‰ A āļˆāļ°āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ + +### **4.3. āļāļēāļĢāļāļģāļŦāļ™āļ”āļšāļ—āļšāļēāļ— (Roles) āđāļĨāļ°āļ‚āļ­āļšāđ€āļ‚āļ• (Scope)** + +| āļšāļ—āļšāļēāļ— (Role) | āļ‚āļ­āļšāđ€āļ‚āļ• (Scope) | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ | āļŠāļīāļ—āļ˜āļīāđŒāļŦāļĨāļąāļ (Key Permissions) | +| :------------------- | :------------- | :---------------------- | :------------------------------------------------------------------------------------- | +| **Superadmin** | Global | āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļŠāļđāļ‡āļŠāļļāļ” | āļ—āļģāļ—āļļāļāļ­āļĒāđˆāļēāļ‡āđƒāļ™āļĢāļ°āļšāļš, āļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļĢāļ°āļ”āļąāļš Global | +| **Org Admin** | Organization | āļœāļđāđ‰āļ”āļđāđāļĨāļ­āļ‡āļ„āđŒāļāļĢ | āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļšāļ—āļšāļēāļ—/āļŠāļīāļ—āļ˜āļīāđŒāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | +| **Document Control** | Organization | āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚/āļĨāļšāđ€āļ­āļāļŠāļēāļĢ, āļāļģāļŦāļ™āļ”āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ | +| **Editor** | Organization | āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒ | +| **Viewer** | Organization | āļœāļđāđ‰āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ | +| **Project Manager** | Project | āļœāļđāđ‰āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ (āđ€āļžāļīāđˆāļĄ/āļĨāļš/āļĄāļ­āļšāļšāļ—āļšāļēāļ—), āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ | +| **Contract Admin** | Contract | āļœāļđāđ‰āļ”āļđāđāļĨāļŠāļąāļāļāļē | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āļŠāļąāļāļāļē, āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāđ€āļ‰āļžāļēāļ°āļŠāļąāļāļāļē (āļ–āđ‰āļēāļĄāļĩ), āļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļŠāļąāļāļāļē | + +### **4.4. Token Management (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** + +- **Payload Optimization:** āđƒāļ™ JWT Access Token āđƒāļŦāđ‰āđ€āļāđ‡āļšāđ€āļ‰āļžāļēāļ° `userId` āđāļĨāļ° `scope` āļ›āļąāļˆāļˆāļļāļšāļąāļ™āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ +- **Permission Caching:** āļŠāļīāļ—āļ˜āļīāđŒāļĨāļ°āđ€āļ­āļĩāļĒāļ” (Permissions List) āđƒāļŦāđ‰āđ€āļāđ‡āļšāđƒāļ™ **Redis** āđāļĨāļ°āļ”āļķāļ‡āļĄāļēāļ•āļĢāļ§āļˆāļŠāļ­āļšāđ€āļĄāļ·āđˆāļ­ Request āđ€āļ‚āđ‰āļēāļĄāļē āđ€āļžāļ·āđˆāļ­āļĨāļ”āļ‚āļ™āļēāļ” Token āđāļĨāļ°āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāđ€āļĢāđ‡āļ§ + +### **4.5. āļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āđƒāļŠāđ‰āļ‡āļēāļ™ (Onboarding Workflow) āļ—āļĩāđˆāļŠāļĄāļšāļđāļĢāļ“āđŒ** + +- **4.5.1. āļŠāļĢāđ‰āļēāļ‡āļ­āļ‡āļ„āđŒāļāļĢ (Organization)** + - **Superadmin** āļŠāļĢāđ‰āļēāļ‡āļ­āļ‡āļ„āđŒāļāļĢāđƒāļŦāļĄāđˆ (āđ€āļŠāđˆāļ™ āļšāļĢāļīāļĐāļąāļ— A) + - **Superadmin** āđāļ•āđˆāļ‡āļ•āļąāđ‰āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 1 āļ„āļ™āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™ **Org Admin** āļŦāļĢāļ·āļ­ **Document Control** āļ‚āļ­āļ‡āļšāļĢāļīāļĐāļąāļ— A +- **4.5.2. āđ€āļžāļīāđˆāļĄāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ** + - **Org Admin** āļ‚āļ­āļ‡āļšāļĢāļīāļĐāļąāļ— A āđ€āļžāļīāđˆāļĄāļœāļđāđ‰āđƒāļŠāđ‰āļ­āļ·āđˆāļ™āđ† (Editor, Viewer) āđ€āļ‚āđ‰āļēāļĄāļēāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢāļ‚āļ­āļ‡āļ•āļ™ +- **4.5.3. āļĄāļ­āļšāļŦāļĄāļēāļĒāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļŦāđ‰āļāļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Project)** + - **Project Manager** āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ X (āļ‹āļķāđˆāļ‡āļ­āļēāļˆāļĄāļēāļˆāļēāļāļšāļĢāļīāļĐāļąāļ— A āļŦāļĢāļ·āļ­āļšāļĢāļīāļĐāļąāļ—āļ­āļ·āđˆāļ™) āļ—āļģāļāļēāļĢ "āđ€āļŠāļīāļ" āļŦāļĢāļ·āļ­ "āļĄāļ­āļšāļŦāļĄāļēāļĒ" āļœāļđāđ‰āđƒāļŠāđ‰āļˆāļēāļāļ­āļ‡āļ„āđŒāļāļĢāļ•āđˆāļēāļ‡āđ† āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āđ€āļ‚āđ‰āļēāļĄāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ X + - āđƒāļ™āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ™āļĩāđ‰ **Project Manager** āļˆāļ°āļāļģāļŦāļ™āļ” **āļšāļ—āļšāļēāļ—āļĢāļ°āļ”āļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ** (āđ€āļŠāđˆāļ™ Project Member, āļŦāļĢāļ·āļ­āļ­āļēāļˆāđ„āļĄāđˆāļĄāļĩāļšāļ—āļšāļēāļ—āļžāļīāđ€āļĻāļĐ āđƒāļŦāđ‰āđƒāļŠāđ‰āļŠāļīāļ—āļ˜āļīāđŒāļˆāļēāļāļĢāļ°āļ”āļąāļšāļ­āļ‡āļ„āđŒāļāļĢāđ„āļ›āļāđˆāļ­āļ™) +- **4.5.4. āđ€āļĄāļ­āļšāļŦāļĄāļēāļĒāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļŦāđ‰āļāļąāļšāļŠāļąāļāļāļē (Contract)** + - **Contract Admin** āļ‚āļ­āļ‡āļŠāļąāļāļāļē Y (āļ‹āļķāđˆāļ‡āđ€āļ›āđ‡āļ™āļŠāđˆāļ§āļ™āļŦāļ™āļķāđˆāļ‡āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ X) āļ—āļģāļāļēāļĢāđ€āļĨāļ·āļ­āļāļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ X āđāļĨāđ‰āļ§ āļĄāļ­āļšāļŦāļĄāļēāļĒāđƒāļŦāđ‰āđ€āļ‚āđ‰āļēāļĄāļēāđƒāļ™āļŠāļąāļāļāļē Y + - āđƒāļ™āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ™āļĩāđ‰ **Contract Admin** āļˆāļ°āļāļģāļŦāļ™āļ” **āļšāļ—āļšāļēāļ—āļĢāļ°āļ”āļąāļšāļŠāļąāļāļāļē** (āđ€āļŠāđˆāļ™ Contract Member) āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ +- **4.5.5 Security Onboarding:** + - āļ•āđ‰āļ­āļ‡āļšāļąāļ‡āļ„āļąāļšāđ€āļ›āļĨāļĩāđˆāļĒāļ™ password āļ„āļĢāļąāđ‰āļ‡āđāļĢāļāļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļŦāļĄāđˆ + - āļ•āđ‰āļ­āļ‡āļĄāļĩ security awareness training āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļŠāļđāļ‡ + - āļ•āđ‰āļ­āļ‡āļĄāļĩ process āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļĢāļĩāđ€āļ‹āđ‡āļ• password āļ—āļĩāđˆāļ›āļĨāļ­āļ”āļ āļąāļĒ + - āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ audit log āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ permissions + +### **4.6. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ (Master Data Management) āļ—āļĩāđˆāđāļšāđˆāļ‡āļ•āļēāļĄāļĢāļ°āļ”āļąāļš** + +| āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ | āļœāļđāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢ | āļĢāļ°āļ”āļąāļš | +| :---------------------------------- | :------------------------------ | :--------------------------------- | +| āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (Correspondence, RFA) | **Superadmin** | Global | +| āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ (Draft, Approved, etc.) | **Superadmin** | Global | +| āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāđāļšāļš (Shop Drawing) | **Project Manager** | Project (āļŠāļĢāđ‰āļēāļ‡āđƒāļŦāļĄāđˆāđ„āļ”āđ‰āļ āļēāļĒāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ) | +| Tags | **Org Admin / Project Manager** | Organization / Project | +| āļšāļ—āļšāļēāļ—āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization | +| Document Numbering Formats | **Superadmin / Admin** | Global / Organization | + +## **ðŸ‘Ĩ 5. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User Interface & Experience)** + +### **5.1. Layout āļŦāļĨāļąāļ:** āļŦāļ™āđ‰āļēāđ€āļ§āđ‡āļšāđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš App Shell āļ—āļĩāđˆāļ›āļĢāļ°āļāļ­āļšāļ”āđ‰āļ§āļĒ + +- Navbar (āļŠāđˆāļ§āļ™āļšāļ™): āđāļŠāļ”āļ‡āļŠāļ·āđˆāļ­āļĢāļ°āļšāļš, āđ€āļĄāļ™āļđāļœāļđāđ‰āđƒāļŠāđ‰ (Profile), āđ€āļĄāļ™āļđāļŠāļģāļŦāļĢāļąāļš Document Control/āđ€āļĄāļ™āļđāļŠāļģāļŦāļĢāļąāļš Admin/Superadmin (āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰, āļˆāļąāļ”āļāļēāļĢāļŠāļīāļ—āļ˜āļīāđŒ), āđāļĨāļ°āļ›āļļāđˆāļĄ Login/Logout +- Sidebar (āļ”āđ‰āļēāļ™āļ‚āđ‰āļēāļ‡): āđ€āļ›āđ‡āļ™āđ€āļĄāļ™āļđāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļšāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āļŠāđˆāļ§āļ™āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” āđ€āļŠāđˆāļ™ Dashboard, Correspondences, RFA, Drawings +- Main Content Area: āļžāļ·āđ‰āļ™āļ—āļĩāđˆāļŠāļģāļŦāļĢāļąāļšāđāļŠāļ”āļ‡āđ€āļ™āļ·āđ‰āļ­āļŦāļēāļŦāļĨāļąāļāļ‚āļ­āļ‡āļŦāļ™āđ‰āļēāļ—āļĩāđˆāđ€āļĨāļ·āļ­āļ + +### **5.2. āļŦāļ™āđ‰āļē Landing Page:** āđ€āļ›āđ‡āļ™āļŦāļ™āđ‰āļēāđāļĢāļāļ—āļĩāđˆāđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļšāļēāļ‡āļŠāđˆāļ§āļ™āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĒāļąāļ‡āđ„āļĄāđˆāđ„āļ”āđ‰āļĨāđ‡āļ­āļāļ­āļīāļ™ + +### **5.3. āļŦāļ™āđ‰āļē Dashboard:** āđ€āļ›āđ‡āļ™āļŦāļ™āđ‰āļēāđāļĢāļāļŦāļĨāļąāļ‡āļˆāļēāļāļĨāđ‡āļ­āļāļ­āļīāļ™ āļ›āļĢāļ°āļāļ­āļšāļ”āđ‰āļ§āļĒ + +- āļāļēāļĢāđŒāļ”āļŠāļĢāļļāļ›āļ āļēāļžāļĢāļ§āļĄ (KPI Cards): āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļĢāļļāļ›āļ—āļĩāđˆāļŠāļģāļ„āļąāļāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ āđ€āļŠāđˆāļ™ āļˆāļģāļ™āļ§āļ™āđ€āļ­āļāļŠāļēāļĢ, āļ‡āļēāļ™āļ—āļĩāđˆāđ€āļāļīāļ™āļāļģāļŦāļ™āļ” +- āļ•āļēāļĢāļēāļ‡ "āļ‡āļēāļ™āļ‚āļ­āļ‡āļ‰āļąāļ™" (My Tasks Table): āđāļŠāļ”āļ‡āļĢāļēāļĒāļāļēāļĢāļ‡āļēāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļˆāļēāļ Circulation āļ—āļĩāđˆāļœāļđāđ‰āđƒāļŠāđ‰āļ•āđ‰āļ­āļ‡āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ +- Security Metrics: āđāļŠāļ”āļ‡āļˆāļģāļ™āļ§āļ™ files scanned, security incidents, failed login attempts + +### **5.4. āļāļēāļĢāļ•āļīāļ”āļ•āļēāļĄāļŠāļ–āļēāļ™āļ°:** āļ­āļ‡āļ„āđŒāļāļĢāļŠāļēāļĄāļēāļĢāļ–āļ•āļīāļ”āļ•āļēāļĄāļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļ‚āļ­āļ‡āļ•āļ™āđ€āļ­āļ‡ (Originator) āđāļĨāļ°āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāđˆāļ‡āļĄāļēāļ–āļķāļ‡āļ•āļ™āđ€āļ­āļ‡ (Recipient) + +### **5.5. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāđˆāļ§āļ™āļ•āļąāļ§ (Profile Page):** āļœāļđāđ‰āđƒāļŠāđ‰āļŠāļēāļĄāļēāļĢāļ–āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāđˆāļ§āļ™āļ•āļąāļ§āđāļĨāļ°āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļĢāļŦāļąāļŠāļœāđˆāļēāļ™āļ‚āļ­āļ‡āļ•āļ™āđ€āļ­āļ‡āđ„āļ”āđ‰ + +### **5.6. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļēāļ‡āđ€āļ—āļ„āļ™āļīāļ„ (RFA & Workflow):** āļœāļđāđ‰āđƒāļŠāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ”āļđ RFA āđƒāļ™āļĢāļđāļ›āđāļšāļš Workflow āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ„āļ”āđ‰āđƒāļ™āļŦāļ™āđ‰āļēāđ€āļ”āļĩāļĒāļ§, āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆāļĒāļąāļ‡āđ„āļĄāđˆāļ–āļķāļ‡āļŦāļĢāļ·āļ­āļœāđˆāļēāļ™āđ„āļ›āđāļĨāđ‰āļ§āļˆāļ°āđ€āļ›āđ‡āļ™āļĢāļđāļ›āđāļšāļš diable, āļŠāļēāļĄāļēāļĢāļ–āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđ„āļ”āđ‰āđ€āļ‰āļžāļēāļ°āđƒāļ™āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™ (active) āđ€āļŠāđˆāļ™ āļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāđ‰āļ§ āđ€āļžāļ·āđˆāļ­āđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ›, āļŠāļīāļ—āļ˜āļīāđŒ Document Control āļ‚āļķāđ‰āļ™āđ„āļ› āļŠāļēāļĄāļĢāļ–āļāļ” āđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ› āđ„āļ”āđ‰āļ—āļļāļāļ‚āļąāđ‰āļ™āļ•āļ­āļ™, āļāļēāļĢāļĒāđ‰āļ­āļ™āļāļĨāļąāļš āđ„āļ›āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē āļŠāļēāļĄāļēāļĢāļ–āļ—āļģāđ„āļ”āđ‰āđ‚āļ”āļĒ āļŠāļīāļ—āļ˜āļīāđŒ Document Control āļ‚āļķāđ‰āļ™āđ„āļ› + +### **5.7. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢ (Circulation):** āļœāļđāđ‰āđƒāļŠāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ”āļđ Circulation āđƒāļ™āļĢāļđāļ›āđāļšāļš Workflow āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ„āļ”āđ‰āđƒāļ™āļŦāļ™āđ‰āļēāđ€āļ”āļĩāļĒāļ§,āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆāļĒāļąāļ‡āđ„āļĄāđˆāļ–āļķāļ‡āļŦāļĢāļ·āļ­āļœāđˆāļēāļ™āđ„āļ›āđāļĨāđ‰āļ§āļˆāļ°āđ€āļ›āđ‡āļ™āļĢāļđāļ›āđāļšāļš diable, āļŠāļēāļĄāļēāļĢāļ–āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđ„āļ”āđ‰āđ€āļ‰āļžāļēāļ°āđƒāļ™āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™ (active) āđ€āļŠāđˆāļ™ āļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāđ‰āļ§ āđ€āļžāļ·āđˆāļ­āđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ›, āļŠāļīāļ—āļ˜āļīāđŒ Document Control āļ‚āļķāđ‰āļ™āđ„āļ› āļŠāļēāļĄāļĢāļ–āļāļ” āđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ› āđ„āļ”āđ‰āļ—āļļāļāļ‚āļąāđ‰āļ™āļ•āļ­āļ™, āļāļēāļĢāļĒāđ‰āļ­āļ™āļāļĨāļąāļš āđ„āļ›āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē āļŠāļēāļĄāļēāļĢāļ–āļ—āļģāđ„āļ”āđ‰āđ‚āļ”āļĒ āļŠāļīāļ—āļ˜āļīāđŒ Document Control āļ‚āļķāđ‰āļ™āđ„āļ› + +### **5.8. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (Transmittals):** āļœāļđāđ‰āđƒāļŠāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ”āļđ Transmittals āđƒāļ™āļĢāļđāļ›āđāļšāļšāļĢāļēāļĒāļāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ„āļ”āđ‰āđƒāļ™āļŦāļ™āđ‰āļēāđ€āļ”āļĩāļĒāļ§ + +### **5.9. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” UI/UX āļāļēāļĢāđāļ™āļšāđ„āļŸāļĨāđŒ (File Attachment UX):** + +- āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒāļŦāļĨāļēāļĒāđ„āļŸāļĨāđŒāļžāļĢāđ‰āļ­āļĄāļāļąāļ™ (Multi-file upload) āđ€āļŠāđˆāļ™ āļāļēāļĢāļĨāļēāļāđāļĨāļ°āļ§āļēāļ‡ (Drag-and-Drop) +- āđƒāļ™āļŦāļ™āđ‰āļēāļ­āļąāļ›āđ‚āļŦāļĨāļ” (āđ€āļŠāđˆāļ™ āļŠāļĢāđ‰āļēāļ‡ RFA āļŦāļĢāļ·āļ­ Correspondence) āļœāļđāđ‰āđƒāļŠāđ‰āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āđ„āļ”āđ‰āļ§āđˆāļēāđ„āļŸāļĨāđŒāđƒāļ”āđ€āļ›āđ‡āļ™ "āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ" (Main Document āđ€āļŠāđˆāļ™ PDF) āđāļĨāļ°āđ„āļŸāļĨāđŒāđƒāļ”āđ€āļ›āđ‡āļ™ "āđ€āļ­āļāļŠāļēāļĢāđāļ™āļšāļ›āļĢāļ°āļāļ­āļš" (Supporting Attachments āđ€āļŠāđˆāļ™ .dwg, .docx, .zip) +- **Security Feedback:** āđāļŠāļ”āļ‡ security warnings āļŠāļģāļŦāļĢāļąāļš file types āļ—āļĩāđˆāđ€āļŠāļĩāđˆāļĒāļ‡āļŦāļĢāļ·āļ­ files āļ—āļĩāđˆ fail virus scan +- **File Type Indicators:** āđāļŠāļ”āļ‡ file type icons āđāļĨāļ° security status + +### **5.10 Form & Interaction (āđƒāļŦāļĄāđˆ)** + +- **Dynamic Form Generator:** āđƒāļŠāđ‰ Component āļāļĨāļēāļ‡āļ—āļĩāđˆāļĢāļąāļš JSON Schema āđāļĨāđ‰āļ§ Render Form āļ­āļ­āļāļĄāļēāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī āđ€āļžāļ·āđˆāļ­āļĨāļ”āļ„āļ§āļēāļĄāļ‹āđ‰āļģāļ‹āđ‰āļ­āļ™āļ‚āļ­āļ‡āđ‚āļ„āđ‰āļ”āļŦāļ™āđ‰āļēāļšāđ‰āļēāļ™ āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ›āļĢāļ°āđ€āļ āļ—āđƒāļŦāļĄāđˆāđ† āđ„āļ”āđ‰āļ—āļąāļ™āļ—āļĩ +- **Optimistic Updates:** āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (āđ€āļŠāđˆāļ™ āļāļ” Approve, āļāļ” Read) āđƒāļŦāđ‰ UI āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āļ—āļąāļ™āļ—āļĩāđƒāļŦāđ‰āļœāļđāđ‰āđƒāļŠāđ‰āđ€āļŦāđ‡āļ™āļāđˆāļ­āļ™āļĢāļ­ API Response (Rollback āļ–āđ‰āļē Failed) + +### **5.11 Mobile Responsiveness (āđƒāļŦāļĄāđˆ)** + +- **Table Visualization:** āļšāļ™āļŦāļ™āđ‰āļēāļˆāļ­āļĄāļ·āļ­āļ–āļ·āļ­ āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļĄāļĩāļŦāļĨāļēāļĒ Column (āđ€āļŠāđˆāļ™ Correspondence List) āļ•āđ‰āļ­āļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨāđ€āļ›āđ‡āļ™āđāļšāļš **Card View** āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī +- **Navigation:** Sidebar āļ•āđ‰āļ­āļ‡āđ€āļ›āđ‡āļ™āđāļšāļš Collapsible Drawer + +### **5.12 Resilience & Offline Support (āđƒāļŦāļĄāđˆ)** + +- **Auto-Save Draft:** āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļāļ‚āđ‰āļ­āļĄāļđāļĨāļŸāļ­āļĢāđŒāļĄāļ—āļĩāđˆāļāļģāļĨāļąāļ‡āļāļĢāļ­āļāļĨāļ‡ **LocalStorage** āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļēāļĒāļāļĢāļ“āļĩāđ€āļ™āđ‡āļ•āļŦāļĨāļļāļ”āļŦāļĢāļ·āļ­āļ›āļīāļ” Browser āđ‚āļ”āļĒāđ„āļĄāđˆāđ„āļ”āđ‰āļ•āļąāđ‰āļ‡āđƒāļˆ +- **Graceful Degradation:** āļŦāļēāļ Service āļĢāļ­āļ‡ (āđ€āļŠāđˆāļ™ Search, Notification) āļĨāđˆāļĄ āļĢāļ°āļšāļšāļŦāļĨāļąāļ (CRUD) āļ•āđ‰āļ­āļ‡āļĒāļąāļ‡āļ—āļģāļ‡āļēāļ™āļ•āđˆāļ­āđ„āļ”āđ‰ + +## **ðŸ›Ąïļ 6. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ—āļĩāđˆāđ„āļĄāđˆāđƒāļŠāđˆāļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™ (Non-Functional Requirements)** + +### **6.1. āļāļēāļĢāļšāļąāļ™āļ—āļķāļāļāļēāļĢāļāļĢāļ°āļ—āļģ (Audit Log):** āļ—āļļāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļ—āļĩāđˆāļŠāļģāļ„āļąāļāļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ (āļŠāļĢāđ‰āļēāļ‡, āđāļāđ‰āđ„āļ‚, āļĨāļš, āļŠāđˆāļ‡) āļˆāļ°āļ–āļđāļāļšāļąāļ™āļ—āļķāļāđ„āļ§āđ‰āđƒāļ™ audit_logs āđ€āļžāļ·āđˆāļ­āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļĒāđ‰āļ­āļ™āļŦāļĨāļąāļ‡ + +- **6.1.1 āļ‚āļ­āļšāđ€āļ‚āļ•āļāļēāļĢāļšāļąāļ™āļ—āļķāļ Audit Log:** + + - āļ—āļļāļāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚/āļĨāļš āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļģāļ„āļąāļ (correspondences, RFAs, drawings, users, permissions) + - āļ—āļļāļāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ sensitive (user data, financial information) + - āļ—āļļāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° workflow (status transitions) + - āļ—āļļāļāļāļēāļĢāļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒāļŠāļģāļ„āļąāļ (contract documents, financial reports) + - āļ—āļļāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ permission āđāļĨāļ° role assignment + - āļ—āļļāļāļāļēāļĢāļĨāđ‡āļ­āļāļ­āļīāļ™āļ—āļĩāđˆāļŠāļģāđ€āļĢāđ‡āļˆāđāļĨāļ°āļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + - āļ—āļļāļāļāļēāļĢāļŠāđˆāļ‡āļ„āļģāļ‚āļ­ API āļ—āļĩāđˆāļŠāļģāļ„āļąāļ + +- **6.1.2 āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļāđƒāļ™ Audit Log:** + - āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (user_id) + - āļāļēāļĢāļāļĢāļ°āļ—āļģ (action) + - āļŠāļ™āļīāļ”āļ‚āļ­āļ‡ entity (entity_type) + - ID āļ‚āļ­āļ‡ entity (entity_id) + - āļ‚āđ‰āļ­āļĄāļđāļĨāļāđˆāļ­āļ™āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ (old_values) - āļŠāļģāļŦāļĢāļąāļš update operations + - āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ‡āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ (new_values) - āļŠāļģāļŦāļĢāļąāļš update operations + - IP address + - User agent + - Timestamp + - Request ID āļŠāļģāļŦāļĢāļąāļš tracing + +### **6.2. Data Archiving & Partitioning (āđƒāļŦāļĄāđˆ)** + +- āļŠāļģāļŦāļĢāļąāļšāļ•āļēāļĢāļēāļ‡āļ—āļĩāđˆāļĄāļĩāļ‚āļ™āļēāļ”āđƒāļŦāļāđˆāđāļĨāļ°āđ‚āļ•āđ€āļĢāđ‡āļ§ (āđ€āļŠāđˆāļ™ `audit_logs`, `notifications`, `correspondence_revisions`) āļ•āđ‰āļ­āļ‡āļ­āļ­āļāđāļšāļšāđ‚āļ”āļĒāļĢāļ­āļ‡āļĢāļąāļš **Table Partitioning** (āđāļšāđˆāļ‡āļ•āļēāļĄ Range āļ§āļąāļ™āļ—āļĩāđˆ āļŦāļĢāļ·āļ­ List) āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāđƒāļ™āļĢāļ°āļĒāļ°āļĒāļēāļ§ + +### **6.3. āļāļēāļĢāļ„āđ‰āļ™āļŦāļē (Search):** āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĄāļĩāļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ āļ—āļĩāđˆāļŠāļēāļĄāļēāļĢāļ–āļ„āđ‰āļ™āļŦāļēāđ€āļ­āļāļŠāļēāļĢ **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** āđāļĨāļ° **āđƒāļšāđ€āļ§āļĩāļĒāļ™ (Circulations)** āļˆāļēāļāļŦāļĨāļēāļĒāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļžāļĢāđ‰āļ­āļĄāļāļąāļ™āđ„āļ”āđ‰ āđ€āļŠāđˆāļ™ āļ„āđ‰āļ™āļŦāļēāļˆāļēāļāļŠāļ·āđˆāļ­āđ€āļĢāļ·āđˆāļ­āļ‡, āļ›āļĢāļ°āđ€āļ āļ—, āļ§āļąāļ™āļ—āļĩāđˆ, āđāļĨāļ° Tag + +### **6.4. āļāļēāļĢāļ—āļģāļĢāļēāļĒāļ‡āļēāļ™ (Reporting):** āļŠāļēāļĄāļēāļĢāļ–āļˆāļąāļ”āļ—āļģāļĢāļēāļĒāļ‡āļēāļ™āļŠāļĢāļļāļ›āđāļĒāļāļ›āļĢāļ°āđ€āļ āļ—āļ‚āļ­āļ‡ Correspondence āļ›āļĢāļ°āļˆāļģāļ§āļąāļ™, āļŠāļąāļ›āļ”āļēāļŦāđŒ, āđ€āļ”āļ·āļ­āļ™, āđāļĨāļ°āļ›āļĩāđ„āļ”āđ‰ + +### **6.5. āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž (Performance):** āļĄāļĩāļāļēāļĢāđƒāļŠāđ‰ Caching āļāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰āļšāđˆāļ­āļĒ āđāļĨāļ°āđƒāļŠāđ‰ Pagination āđƒāļ™āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļžāļ·āđˆāļ­āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļģāļ™āļ§āļ™āļĄāļēāļ + +- **6.5.1 āļ•āļąāļ§āļŠāļĩāđ‰āļ§āļąāļ”āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž:** + + - **API Response Time:** < 200ms (90th percentile) āļŠāļģāļŦāļĢāļąāļš operation āļ—āļąāđˆāļ§āđ„āļ› + - **Search Query Performance:** < 500ms āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ + - **File Upload Performance:** < 30 seconds āļŠāļģāļŦāļĢāļąāļšāđ„āļŸāļĨāđŒāļ‚āļ™āļēāļ” 50MB + - **Concurrent Users:** āļĢāļ­āļ‡āļĢāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āļžāļĢāđ‰āļ­āļĄāļāļąāļ™āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 100 āļ„āļ™ + - **Database Connection Pool:** āļ‚āļ™āļēāļ”āđ€āļŦāļĄāļēāļ°āļŠāļĄāļāļąāļš workload (default: min 5, max 20 connections) + - **Cache Hit Ratio:** > 80% āļŠāļģāļŦāļĢāļąāļš cached data + - **Application Startup Time:** < 30 seconds + +- **6.5.2 Caching Strategy:** + - **Master Data Cache:** Roles, Permissions, Organizations, Project metadata (TTL: 1 hour) + - **User Session Cache:** User permissions āđāļĨāļ° profile data (TTL: 30 minutes) + - **Search Result Cache:** Frequently searched queries (TTL: 15 minutes) + - **File Metadata Cache:** Attachment metadata (TTL: 1 hour) + - **Document Cache:** Frequently accessed document metadata (TTL: 30 minutes) + - **āļ•āđ‰āļ­āļ‡āļĄāļĩ cache invalidation strategy āļ—āļĩāđˆāļŠāļąāļ”āđ€āļˆāļ™:** + - Invalidate on update/delete operations + - Time-based expiration + - Manual cache clearance āļŠāļģāļŦāļĢāļąāļš admin operations + - āđƒāļŠāđ‰ Redis āđ€āļ›āđ‡āļ™ distributed cache backend + - āļ•āđ‰āļ­āļ‡āļĄāļĩ cache monitoring (hit/miss ratios) + +### **6.6. āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ (Security):** + +- āļĄāļĩāļĢāļ°āļšāļš Rate Limiting āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāđ‚āļˆāļĄāļ•āļĩāđāļšāļš Brute-force +- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Secret (āđ€āļŠāđˆāļ™ āļĢāļŦāļąāļŠāļœāđˆāļēāļ™ DB, JWT Secret) āļˆāļ°āļ•āđ‰āļ­āļ‡āļ—āļģāļœāđˆāļēāļ™ Environment Variable āļ‚āļ­āļ‡ Docker āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” + +- **6.6.1 Rate Limiting Strategy:** + + - **Anonymous Endpoints:** 100 requests/hour āļ•āđˆāļ­ IP address + - **Authenticated Endpoints:** + - Viewer: 500 requests/hour + - Editor: 1000 requests/hour + - Document Control: 2000 requests/hour + - Admin/Superadmin: 5000 requests/hour + - **File Upload Endpoints:** 50 requests/hour āļ•āđˆāļ­ user + - **Search Endpoints:** 500 requests/hour āļ•āđˆāļ­ user + - **Authentication Endpoints:** 10 requests/minute āļ•āđˆāļ­ IP address + - **āļ•āđ‰āļ­āļ‡āļĄāļĩ mechanism āļŠāļģāļŦāļĢāļąāļšāļĒāļāđ€āļ§āđ‰āļ™ rate limiting āļŠāļģāļŦāļĢāļąāļš trusted services** + - āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ log āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢ trigger rate limiting + +- **6.6.2 Error Handling āđāļĨāļ° Resilience:** + + - āļ•āđ‰āļ­āļ‡āļĄāļĩ circuit breaker pattern āļŠāļģāļŦāļĢāļąāļš external service calls + - āļ•āđ‰āļ­āļ‡āļĄāļĩ retry mechanism āļ”āđ‰āļ§āļĒ exponential backoff + - āļ•āđ‰āļ­āļ‡āļĄāļĩ graceful degradation āđ€āļĄāļ·āđˆāļ­āļšāļĢāļīāļāļēāļĢāļ āļēāļĒāļ™āļ­āļāļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + - Error messages āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāđ€āļ›āļīāļ”āđ€āļœāļĒāļ‚āđ‰āļ­āļĄāļđāļĨ sensitive + +- **6.6.3 Input Validation:** + + - āļ•āđ‰āļ­āļ‡āļĄāļĩ input validation āļ—āļąāđ‰āļ‡āļāļąāđˆāļ‡ client āđāļĨāļ° server (defense in depth) + - āļ•āđ‰āļ­āļ‡āļ›āđ‰āļ­āļ‡āļāļąāļ™ OWASP Top 10 vulnerabilities: + - SQL Injection (āđƒāļŠāđ‰ parameterized queries āļœāđˆāļēāļ™ ORM) + - XSS (input sanitization āđāļĨāļ° output encoding) + - CSRF (CSRF tokens āļŠāļģāļŦāļĢāļąāļš state-changing operations) + - āļ•āđ‰āļ­āļ‡ validate file uploads: + - File type (white-list approach) + - File size + - File content (magic number verification) + - āļ•āđ‰āļ­āļ‡ sanitize user inputs āļāđˆāļ­āļ™āđāļŠāļ”āļ‡āļœāļĨāđƒāļ™ UI + - āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰ content security policy (CSP) headers + - āļ•āđ‰āļ­āļ‡āļĄāļĩ request size limits āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ DoS attacks + +- **6.6.4 Session āđāļĨāļ° Token Management:** + - **JWT token expiration:** 8 hours āļŠāļģāļŦāļĢāļąāļš access token + - **Refresh token expiration:** 7 days + - **Refresh token mechanism:** āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš token rotation āđāļĨāļ° revocation + - **Token revocation on logout:** āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ revoked tokens āļˆāļ™āļāļ§āđˆāļēāļˆāļ° expire + - **Concurrent session management:** + - āļˆāļģāļāļąāļ”āļˆāļģāļ™āļ§āļ™ session āļžāļĢāđ‰āļ­āļĄāļāļąāļ™āđ„āļ”āđ‰ (default: 5 devices) + - āļ•āđ‰āļ­āļ‡āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđ€āļĄāļ·āđˆāļ­āļĄāļĩ login āļˆāļēāļ device/location āđƒāļŦāļĄāđˆ + - **Device fingerprinting:** āļŠāļģāļŦāļĢāļąāļš security āđāļĨāļ° audit purposes + - **Password policy:** + - āļ„āļ§āļēāļĄāļĒāļēāļ§āļ‚āļąāđ‰āļ™āļ•āđˆāļģ: 8 characters + - āļ•āđ‰āļ­āļ‡āļĄāļĩ uppercase, lowercase, number, special character + - āļ•āđ‰āļ­āļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™ password āļ—āļļāļ 90 āļ§āļąāļ™ + - āļ•āđ‰āļ­āļ‡āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāđƒāļŠāđ‰ password āļ—āļĩāđˆāđ€āļ„āļĒāđƒāļŠāđ‰āļĄāļēāđāļĨāđ‰āļ§ 5 āļ„āļĢāļąāđ‰āļ‡āļĨāđˆāļēāļŠāļļāļ” + +### **6.7. āļāļēāļĢāļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđāļĨāļ°āļāļēāļĢāļāļđāđ‰āļ„āļ·āļ™ (Backup & Recovery):** + +- āļĢāļ°āļšāļšāļˆāļ°āļ•āđ‰āļ­āļ‡āļĄāļĩāļāļĨāđ„āļāļāļēāļĢāļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļīāļŠāļģāļŦāļĢāļąāļšāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ MariaDB [cite: 2.4] āđāļĨāļ°āđ„āļŸāļĨāđŒāđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™ /share/dms-data [cite: 2.1] (āđ€āļŠāđˆāļ™ āđƒāļŠāđ‰ HBS 3 āļ‚āļ­āļ‡ QNAP āļŦāļĢāļ·āļ­āļŠāļ„āļĢāļīāļ›āļ•āđŒāļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ) āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒāļ§āļąāļ™āļĨāļ° 1 āļ„āļĢāļąāđ‰āļ‡ +- āļ•āđ‰āļ­āļ‡āļĄāļĩāđāļœāļ™āļāļēāļĢāļāļđāđ‰āļ„āļ·āļ™āļĢāļ°āļšāļš (Disaster Recovery Plan) āđƒāļ™āļāļĢāļ“āļĩāļ—āļĩāđˆ Server āļŦāļĨāļąāļ (QNAP) āđƒāļŠāđ‰āļ‡āļēāļ™āđ„āļĄāđˆāđ„āļ”āđ‰ + +- **6.7.1 āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļāļđāđ‰āļ„āļ·āļ™:** + - **Database Restoration Procedure:** + - āļŠāļĢāđ‰āļēāļ‡āļˆāļēāļ full backup āļĨāđˆāļēāļŠāļļāļ” + - Apply transaction logs āļ–āļķāļ‡ point-in-time āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢ + - Verify data integrity post-restoration + - **File Storage Restoration Procedure:** + - Restore āļˆāļēāļ QNAP snapshot āļŦāļĢāļ·āļ­ backup + - Verify file integrity āđāļĨāļ° permissions + - **Application Redeployment Procedure:** + - Deploy āļˆāļēāļ version āļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆāļĢāļđāđ‰āļ§āđˆāļēāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - Verify application health + - **Data Integrity Verification Post-Recovery:** + - Run data consistency checks + - Verify critical business data + - **Recovery Time Objective (RTO):** < 4 āļŠāļąāđˆāļ§āđ‚āļĄāļ‡ + - **Recovery Point Objective (RPO):** < 1 āļŠāļąāđˆāļ§āđ‚āļĄāļ‡ + +### **6.8. āļāļĨāļĒāļļāļ—āļ˜āđŒāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Notification Strategy - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡):** + +- **6.8.1 āļĢāļ°āļšāļšāļˆāļ°āļŠāđˆāļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (āļœāđˆāļēāļ™ Email āļŦāļĢāļ·āļ­ Line [cite: 2.7]) āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢāļāļĢāļ°āļ—āļģāļ—āļĩāđˆāļŠāļģāļ„āļąāļ** āļ”āļąāļ‡āļ™āļĩāđ‰: + + 1. āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆ (Correspondence, RFA) āļ–āļđāļāļŠāđˆāļ‡āļĄāļēāļ–āļķāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ“āđŒāļ‚āļ­āļ‡āđ€āļĢāļē + 2. āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđƒāļšāđ€āļ§āļĩāļĒāļ™ (Circulation) āđƒāļŦāļĄāđˆ āļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™āļĄāļēāļ—āļĩāđˆāđ€āļĢāļē + 3. (āļ—āļēāļ‡āđ€āļĨāļ·āļ­āļ) āđ€āļĄāļ·āđˆāļ­āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ€āļĢāļēāļŠāđˆāļ‡āđ„āļ› āļ–āļđāļāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (āđ€āļŠāđˆāļ™ āļ­āļ™āļļāļĄāļąāļ•āļī/āļ›āļāļīāđ€āļŠāļ˜) + 4. (āļ—āļēāļ‡āđ€āļĨāļ·āļ­āļ) āđ€āļĄāļ·āđˆāļ­āđƒāļāļĨāđ‰āļ–āļķāļ‡āļ§āļąāļ™āļ„āļĢāļšāļāļģāļŦāļ™āļ” (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5] + +- **6.8.2 Grouping/Digest (āđƒāļŦāļĄāđˆ):** āļāļĢāļ“āļĩāļĄāļĩāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ”āļĩāļĒāļ§āļāļąāļ™āļˆāļģāļ™āļ§āļ™āļĄāļēāļāđƒāļ™āļŠāđˆāļ§āļ‡āđ€āļ§āļĨāļēāļŠāļąāđ‰āļ™āđ† (āđ€āļŠāđˆāļ™ Approve āđ€āļ­āļāļŠāļēāļĢ 10 āļ‰āļšāļąāļšāļĢāļ§āļ”) āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡ **āļĢāļ§āļĄ (Batch)** āđ€āļ›āđ‡āļ™ 1 Email/Line Notification āđ€āļžāļ·āđˆāļ­āđ„āļĄāđˆāđƒāļŦāđ‰āļĢāļšāļāļ§āļ™āļœāļđāđ‰āđƒāļŠāđ‰ (Spamming) + +- **6.8.3 Notification Delivery Guarantees:** + - **At-least-once delivery:** āļŠāļģāļŦāļĢāļąāļš important notifications + - **Retry mechanism:** āļ”āđ‰āļ§āļĒ exponential backoff (max 3 reties) + - **Dead letter queue:** āļŠāļģāļŦāļĢāļąāļš notifications āļ—āļĩāđˆāļŠāđˆāļ‡āđ„āļĄāđˆāļŠāļģāđ€āļĢāđ‡āļˆāļŦāļĨāļąāļ‡āļˆāļēāļ retries + - **Delivery status tracking:** āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļāļŠāļ–āļēāļ™āļ°āļāļēāļĢāļŠāđˆāļ‡ notifications + - **Fallback channels:** āļ–āđ‰āļē Email āļĨāđ‰āļĄāđ€āļŦāļĨāļ§ āđƒāļŦāđ‰āļŠāđˆāļ‡āļœāđˆāļēāļ™ SYSTEM notification + - **Notification preferences:** āļœāļđāđ‰āđƒāļŠāđ‰āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ” channel preferences āđ„āļ”āđ‰ + +### **6.9. Maintenance Mode (āđƒāļŦāļĄāđˆ)** + +- āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĄāļĩāļāļĨāđ„āļ **Maintenance Mode** āļ—āļĩāđˆ Admin āļŠāļēāļĄāļēāļĢāļ–āđ€āļ›āļīāļ”āđƒāļŠāđ‰āļ‡āļēāļ™āđ„āļ”āđ‰ + - āđ€āļĄāļ·āđˆāļ­āđ€āļ›āļīāļ”: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļąāđˆāļ§āđ„āļ›āļˆāļ°āđ€āļŦāđ‡āļ™āļŦāļ™āđ‰āļē "āļ›āļīāļ”āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡" āđāļĨāļ°āđ„āļĄāđˆāļŠāļēāļĄāļēāļĢāļ–āđ€āļĢāļĩāļĒāļ API āđ„āļ”āđ‰ (āļĒāļāđ€āļ§āđ‰āļ™ Admin) + - āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļŠāđˆāļ§āļ‡ Deploy Version āđƒāļŦāļĄāđˆ āļŦāļĢāļ·āļ­ Database Migration + +### **6.10. Monitoring āđāļĨāļ° Observability** + +- **6.10.1 Application Monitoring:** + - **Health checks:** /health endpoint āļŠāļģāļŦāļĢāļąāļš load balancer + - **Metrics collection:** Response times, error rates, throughput + - **Distributed tracing:** āļŠāļģāļŦāļĢāļąāļš request tracing across services + - **Log aggregation:** Structured logging āļ”āđ‰āļ§āļĒ JSON format + - **Alerting:** āļŠāļģāļŦāļĢāļąāļš critical errors āđāļĨāļ° performance degradation +- **6.10.2 Business Metrics:** + - āļˆāļģāļ™āļ§āļ™ documents created āļ•āđˆāļ­āļ§āļąāļ™ + - Workflow completion rates + - User activity metrics + - System utilization rates + - Search query performance +- **6.10.3 Security Monitoring:** + - Failed login attempts + - Rate limiting triggers + - Virus scan results + - File download activities + - Permission changes + +### **6.11 JSON Processing & Validation** + +- **6.11.1 JSON Schema Management** + - āļ•āđ‰āļ­āļ‡āļĄāļĩ centralized JSON schema registry + - āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš schema versioning āđāļĨāļ° migration + - āļ•āđ‰āļ­āļ‡āļĄāļĩ schema validation during runtime +- **6.11.2 Performance Optimization** + - **Caching:** Cache parsed JSON structures + - **Compression:** āđƒāļŠāđ‰ compression āļŠāļģāļŦāļĢāļąāļš JSON āļ‚āļ™āļēāļ”āđƒāļŦāļāđˆ + - **Indexing:** Support JSON path indexing āļŠāļģāļŦāļĢāļąāļš query +- **6.11.3 Error Handling** + - āļ•āđ‰āļ­āļ‡āļĄāļĩ graceful degradation āđ€āļĄāļ·āđˆāļ­ JSON validation āļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + - āļ•āđ‰āļ­āļ‡āļĄāļĩ default fallback values + - āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ error logs āļŠāļģāļŦāļĢāļąāļš validation failures + +## **🧊 7. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļāļēāļĢāļ—āļ”āļŠāļ­āļš (Testing Requirements)** + +### **7.1. Unit Testing:** + +- āļ•āđ‰āļ­āļ‡āļĄāļĩ unit tests āļŠāļģāļŦāļĢāļąāļš business logic āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +- Code coverage āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 70% āļŠāļģāļŦāļĢāļąāļš backend services +- āļ•āđ‰āļ­āļ‡āļ—āļ”āļŠāļ­āļš RBAC permission logic āļ—āļļāļāļĢāļ°āļ”āļąāļš + +### **7.2. Integration Testing:** + +- āļ—āļ”āļŠāļ­āļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļĢāđˆāļ§āļĄāļāļąāļ™āļ‚āļ­āļ‡ modules +- āļ—āļ”āļŠāļ­āļš database migrations āđāļĨāļ° data integrity +- āļ—āļ”āļŠāļ­āļš API endpoints āļ”āđ‰āļ§āļĒ realistic data + +### **7.3. End-to-End Testing:** + +- āļ—āļ”āļŠāļ­āļš complete user workflows +- āļ—āļ”āļŠāļ­āļš document lifecycle āļˆāļēāļ creation āļ–āļķāļ‡ archival +- āļ—āļ”āļŠāļ­āļš cross-module integrations + +### **7.4. Security Testing:** + +- **Penetration Testing:** āļ—āļ”āļŠāļ­āļš OWASP Top 10 vulnerabilities +- **Security Audit:** Review code āļŠāļģāļŦāļĢāļąāļš security flaws +- **Virus Scanning Test:** āļ—āļ”āļŠāļ­āļš file upload security +- **Rate Limiting Test:** āļ—āļ”āļŠāļ­āļš rate limiting functionality + +### **7.5. Performance Testing:** + +- **Load Testing:** āļ—āļ”āļŠāļ­āļšāļ”āđ‰āļ§āļĒ realistic workloads +- **Stress Testing:** āļŦāļē breaking points āļ‚āļ­āļ‡āļĢāļ°āļšāļš +- **Endurance Testing:** āļ—āļ”āļŠāļ­āļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ•āđˆāļ­āđ€āļ™āļ·āđˆāļ­āļ‡āđ€āļ›āđ‡āļ™āđ€āļ§āļĨāļēāļ™āļēāļ™ + +### **7.6. Disaster Recovery Testing:** + +- āļ—āļ”āļŠāļ­āļš backup āđāļĨāļ° restoration procedures +- āļ—āļ”āļŠāļ­āļš failover mechanisms +- āļ—āļ”āļŠāļ­āļš data integrity āļŦāļĨāļąāļ‡āļāļēāļĢ recovery + +### **7.7 Specific Scenario Testing (āđ€āļžāļīāđˆāļĄ)** + +- **Race Condition Test:** āļ—āļ”āļŠāļ­āļšāļĒāļīāļ‡ Request āļ‚āļ­āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļžāļĢāđ‰āļ­āļĄāļāļąāļ™ 100 Request +- **Transaction Test:** āļ—āļ”āļŠāļ­āļšāļ›āļīāļ”āđ€āļ™āđ‡āļ•āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Upload āđ„āļŸāļĨāđŒ (āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļēāđ„āļĄāđˆāļĄāļĩ Orphan File āļŦāļĢāļ·āļ­ Broken Link) +- **Permission Test:** āļ—āļ”āļŠāļ­āļš CASL Integration āļ—āļąāđ‰āļ‡āļāļąāđˆāļ‡ Backend āđāļĨāļ° Frontend āđƒāļŦāđ‰āļ•āļĢāļ‡āļāļąāļ™ + +## **8. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļāļēāļĢāļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļē (Maintenance Requirements)** + +### **8.1. Log Retention:** + +- Audit logs: 7 āļ›āļĩ +- Application logs: 1 āļ›āļĩ +- Performance metrics: 2 āļ›āļĩ + +### **8.2. Monitoring āđāļĨāļ° Alerting:** + +- āļ•āđ‰āļ­āļ‡āļĄāļĩ proactive monitoring āļŠāļģāļŦāļĢāļąāļš critical systems +- āļ•āđ‰āļ­āļ‡āļĄāļĩ alerting āļŠāļģāļŦāļĢāļąāļš security incidents +- āļ•āđ‰āļ­āļ‡āļĄāļĩ performance degradation alerts + +### **8.3. Patch Management:** + +- āļ•āđ‰āļ­āļ‡āļĄāļĩ process āļŠāļģāļŦāļĢāļąāļš security patches +- āļ•āđ‰āļ­āļ‡āļ—āļ”āļŠāļ­āļš patches āđƒāļ™ staging environment +- āļ•āđ‰āļ­āļ‡āļĄāļĩ rollback plan āļŠāļģāļŦāļĢāļąāļš failed updates + +### **8.4. Capacity Planning:** + +- āļ•āđ‰āļ­āļ‡ monitor resource utilization +- āļ•āđ‰āļ­āļ‡āļĄāļĩ scaling strategy āļŠāļģāļŦāļĢāļąāļš growth +- āļ•āđ‰āļ­āļ‡āļĄāļĩ performance baselines āđāļĨāļ° trending + +## **9. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļāļēāļĢāļ›āļāļīāļšāļąāļ•āļīāļ•āļēāļĄāļāļŽāļĢāļ°āđ€āļšāļĩāļĒāļš (Compliance Requirements)** + +### **9.1. Data Privacy:** + +- āļ•āđ‰āļ­āļ‡āļ›āļāļīāļšāļąāļ•āļīāļ•āļēāļĄāļāļŽāļŦāļĄāļēāļĒāļ„āļļāđ‰āļĄāļ„āļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāđˆāļ§āļ™āļšāļļāļ„āļ„āļĨ +- āļ•āđ‰āļ­āļ‡āļĄāļĩ data retention policies +- āļ•āđ‰āļ­āļ‡āļĄāļĩ data deletion procedures + +### **9.2. Audit Compliance:** + +- āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš internal āđāļĨāļ° external audits +- āļ•āđ‰āļ­āļ‡āļĄāļĩ comprehensive audit trails +- āļ•āđ‰āļ­āļ‡āļĄāļĩ reporting capabilities āļŠāļģāļŦāļĢāļąāļš compliance + +### **9.3. Security Standards:** + +- āļ•āđ‰āļ­āļ‡āļ›āļāļīāļšāļąāļ•āļīāļ•āļēāļĄ organizational security policies +- āļ•āđ‰āļ­āļ‡āļĄāļĩ security incident response plan +- āļ•āđ‰āļ­āļ‡āļĄāļĩ regular security assessments + +## **📋 āļŠāļĢāļļāļ›āļāļēāļĢāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļˆāļēāļāđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē** + +### **Security Enhancements:** + +1. **File Upload Security** - Virus scanning, file type validation, access controls +2. **Input Validation** - OWASP Top 10 protection, XSS/CSRF prevention +3. **Rate Limiting** - Comprehensive rate limiting strategy +4. **Secrets Management** - Secure handling of sensitive configuration + +### **Architecture Improvements:** + +1. **Document Numbering** - Changed from Stored Procedure to Application-level Locking with Optimistic Locking safety net +2. **Resilience Patterns** - Circuit breaker, retry mechanisms, fallback strategies +3. **Monitoring & Observability** - Health checks, metrics, distributed tracing +4. **Caching Strategy** - Comprehensive caching with proper invalidation +5. **Two-Phase File Storage** - Temp -> Permanent storage with transaction safety +6. **Unified Workflow Engine** - Consolidated routing logic for better maintainability + +### **Performance Targets:** + +1. **API Response Time** - < 200ms (90th percentile) +2. **Search Performance** - < 500ms +3. **File Upload** - < 30 seconds for 50MB files +4. **Cache Hit Ratio** - > 80% + +### **Operational Excellence:** + +1. **Disaster Recovery** - RTO < 4 hours, RPO < 1 hour +2. **Backup Procedures** - Comprehensive backup and restoration +3. **Security Testing** - Penetration testing and security audits +4. **Performance Testing** - Load testing with realistic workloads +5. **Maintenance Mode** - Graceful system maintenance capabilities + +### **User Experience Improvements:** + +1. **Dynamic Form Generator** - Reduced code duplication and better schema support +2. **Mobile Responsiveness** - Card view for tables on mobile devices +3. **Auto-Save Draft** - LocalStorage integration for form resilience +4. **Notification Digest** - Reduced notification spam + +### **Data Management:** + +1. **Virtual Columns** - Improved JSON field search performance +2. **Table Partitioning** - Support for large-scale data growth +3. **Idempotency Keys** - Prevention of duplicate transactions + +āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļŠāļ°āļ—āđ‰āļ­āļ™āļ–āļķāļ‡āļ„āļ§āļēāļĄāļĄāļļāđˆāļ‡āļĄāļąāđˆāļ™āđƒāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āļĢāļ°āļšāļšāļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ, āļĄāļĩāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™, āđāļĨāļ°āļĄāļĩāļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāļŠāļđāļ‡ āļžāļĢāđ‰āļ­āļĄāļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ•āļīāļšāđ‚āļ•āđƒāļ™āļ­āļ™āļēāļ„āļ•āđāļĨāļ°āļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđ„āļ› + +**āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ:** Requirements āļ™āļĩāđ‰āļˆāļ°āļ–āļđāļāļ—āļšāļ—āļ§āļ™āđāļĨāļ°āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđ€āļ›āđ‡āļ™āļĢāļ°āļĒāļ°āļ•āļēāļĄ feedback āļˆāļēāļāļ—āļĩāļĄāļžāļąāļ’āļ™āļēāđāļĨāļ°āļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđ„āļ› + +## **Document Control:** + +- **Document:** Application Requirements Specification v1.4.5 +- **Version:** 1.4 +- **Date:** 2025-11-29 +- **Author:** NAP LCBP3-DMS & Gemini +- **Status:** FINAL-Rev.05 +- **Classification:** Internal Technical Documentation +- **Approved By:** Nattanin + +--- + +`End of Requirements Specification v1.4.5` diff --git a/1_FullStackJS_V1_4_5.md b/1_FullStackJS_V1_4_5.md new file mode 100644 index 0000000..d505a2e --- /dev/null +++ b/1_FullStackJS_V1_4_5.md @@ -0,0 +1,962 @@ +# 📝 **Documents Management System Version 1.4.5: āđāļ™āļ§āļ—āļēāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļē FullStackJS** + +**āļŠāļ–āļēāļ™āļ°:** FINAL GUIDELINE Rev.05 +**āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-29 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡:** Requirements Specification v1.4.4 +**Classification:** Internal Technical Documentation + +## 🧠 **1. āļ›āļĢāļąāļŠāļāļēāļ—āļąāđˆāļ§āđ„āļ› (General Philosophy)** + +āđāļ™āļ§āļ—āļēāļ‡āļ›āļāļīāļšāļąāļ•āļīāļ—āļĩāđˆāļ”āļĩāļ—āļĩāđˆāļŠāļļāļ”āđāļšāļšāļ„āļĢāļšāļ§āļ‡āļˆāļĢāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļžāļąāļ’āļ™āļē NestJS Backend, NextJS Frontend āđāļĨāļ° Tailwind-based UI/UX āđƒāļ™āļŠāļ āļēāļžāđāļ§āļ”āļĨāđ‰āļ­āļĄ TypeScript āļĄāļļāđˆāļ‡āđ€āļ™āđ‰āļ™āļ—āļĩāđˆ **"Data Integrity First"** (āļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ•āđ‰āļ­āļ‡āļĄāļēāļāđˆāļ­āļ™) āļ•āļēāļĄāļ”āđ‰āļ§āļĒ Security āđāļĨāļ° UX + +- **āļ„āļ§āļēāļĄāļŠāļąāļ”āđ€āļˆāļ™ (clarity), āļ„āļ§āļēāļĄāļ‡āđˆāļēāļĒāđƒāļ™āļāļēāļĢāļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļē (maintainability), āļ„āļ§āļēāļĄāļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļ™ (consistency) āđāļĨāļ° āļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ„āļ”āđ‰ (accessibility)** āļ•āļĨāļ­āļ”āļ—āļąāđ‰āļ‡āļŠāđāļ•āđ‡āļ +- **Strict Typing:** āđƒāļŠāđ‰ TypeScript āļ­āļĒāđˆāļēāļ‡āđ€āļ„āļĢāđˆāļ‡āļ„āļĢāļąāļ” āļŦāđ‰āļēāļĄ `any` +- **Consistency:** āđƒāļŠāđ‰āļ āļēāļĐāļēāļ­āļąāļ‡āļāļĪāļĐāđƒāļ™ Code / āļ āļēāļĐāļēāđ„āļ—āļĒāđƒāļ™ Comment +- **Resilience:** āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļ—āļ™āļ—āļēāļ™āļ•āđˆāļ­ Network Failure āđāļĨāļ° Race Condition + +## ⚙ïļ **2. āđāļ™āļ§āļ—āļēāļ‡āļ—āļąāđˆāļ§āđ„āļ›āļŠāļģāļŦāļĢāļąāļš TypeScript** + +### **2.1 āļŦāļĨāļąāļāļāļēāļĢāļžāļ·āđ‰āļ™āļāļēāļ™** + +- āđƒāļŠāđ‰ **āļ āļēāļĐāļēāļ­āļąāļ‡āļāļĪāļĐ** āļŠāļģāļŦāļĢāļąāļšāđ‚āļ„āđ‰āļ” +- āđƒāļŠāđ‰ **āļ āļēāļĐāļēāđ„āļ—āļĒ** āļŠāļģāļŦāļĢāļąāļš comment āđāļĨāļ°āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” +- āļāļģāļŦāļ™āļ”āđ„āļ—āļ›āđŒ (type) āļ­āļĒāđˆāļēāļ‡āļŠāļąāļ”āđ€āļˆāļ™āļŠāļģāļŦāļĢāļąāļšāļ•āļąāļ§āđāļ›āļĢ, āļžāļēāļĢāļēāļĄāļīāđ€āļ•āļ­āļĢāđŒ āđāļĨāļ°āļ„āđˆāļēāļ—āļĩāđˆāļŠāđˆāļ‡āļāļĨāļąāļš (return values) āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +- āļŦāļĨāļĩāļāđ€āļĨāļĩāđˆāļĒāļ‡āļāļēāļĢāđƒāļŠāđ‰ any; āđƒāļŦāđ‰āļŠāļĢāđ‰āļēāļ‡āđ„āļ—āļ›āđŒ (types) āļŦāļĢāļ·āļ­āļ­āļīāļ™āđ€āļ—āļ­āļĢāđŒāđ€āļŸāļ‹ (interfaces) āļ—āļĩāđˆāļāļģāļŦāļ™āļ”āđ€āļ­āļ‡ +- āđƒāļŠāđ‰ **JSDoc** āļŠāļģāļŦāļĢāļąāļšāļ„āļĨāļēāļŠ (classes) āđāļĨāļ°āđ€āļĄāļ˜āļ­āļ” (methods) āļ—āļĩāđˆāđ€āļ›āđ‡āļ™ public +- āļŠāđˆāļ‡āļ­āļ­āļ (Export) **āļŠāļąāļāļĨāļąāļāļĐāļ“āđŒāļŦāļĨāļąāļ (main symbol) āđ€āļžāļĩāļĒāļ‡āļŦāļ™āļķāđˆāļ‡āđ€āļ”āļĩāļĒāļ§** āļ•āđˆāļ­āđ„āļŸāļĨāđŒ +- āļŦāļĨāļĩāļāđ€āļĨāļĩāđˆāļĒāļ‡āļšāļĢāļĢāļ—āļąāļ”āļ§āđˆāļēāļ‡āļ āļēāļĒāđƒāļ™āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ +- āļĢāļ°āļšāļļ // File: path/filename āđƒāļ™āļšāļĢāļĢāļ—āļąāļ”āđāļĢāļāļ‚āļ­āļ‡āļ—āļļāļāđ„āļŸāļĨāđŒ +- āļĢāļ°āļšāļļ // āļšāļąāļ™āļ—āļķāļāļāļēāļĢāđāļāđ‰āđ„āļ‚, āļŦāļēāļāļĄāļĩāļāļēāļĢāđāļāđ‰āđ„āļ‚āđ€āļžāļīāđˆāļĄāđƒāļ™āļ­āļ™āļēāļ„āļ• āđƒāļŦāđ‰āđ€āļžāļīāđˆāļĄāļšāļąāļ™āļ—āļķāļ + +### **2.2 Configuration & Secrets Management** + +- **Production/Staging:** āļŦāđ‰āļēāļĄāđƒāļŠāđˆ Secrets (Password, Keys) āđƒāļ™ `docker-compose.yml` āļŦāļĨāļąāļ +- **Development:** āđƒāļŦāđ‰āļŠāļĢāđ‰āļēāļ‡āđ„āļŸāļĨāđŒ `docker-compose.override.yml` (āđ€āļžāļīāđˆāļĄāđƒāļ™ `.gitignore`) āđ€āļžāļ·āđˆāļ­ Inject āļ•āļąāļ§āđāļ›āļĢ Environment āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļ„āļ§āļēāļĄāļĨāļąāļš +- **Validation:** āđƒāļŠāđ‰ `joi` āļŦāļĢāļ·āļ­ `zod` āđƒāļ™āļāļēāļĢ Validate Environment Variables āļ•āļ­āļ™ Start App āļŦāļēāļāļ‚āļēāļ”āļ•āļąāļ§āđāļ›āļĢāļŠāļģāļ„āļąāļāđƒāļŦāđ‰ Throw Error āļ—āļąāļ™āļ—āļĩ + +### **2.3 Idempotency (āļ„āļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļ–āđƒāļ™āļāļēāļĢāļ—āļģāļ‹āđ‰āļģāđ„āļ”āđ‰)** + +- āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ—āļĩāđˆāļŠāļģāļ„āļąāļ (Create Document, Approve, Transactional) **āļ•āđ‰āļ­āļ‡** āļ­āļ­āļāđāļšāļšāđƒāļŦāđ‰āđ€āļ›āđ‡āļ™ Idempotent +- Client **āļ•āđ‰āļ­āļ‡** āļŠāđˆāļ‡ Header `Idempotency-Key` (UUID) āļĄāļēāļāļąāļš Request +- Server **āļ•āđ‰āļ­āļ‡** āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļē Key āļ™āļĩāđ‰āđ€āļ„āļĒāļ–āļđāļāļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļŠāļģāđ€āļĢāđ‡āļˆāđ„āļ›āđāļĨāđ‰āļ§āļŦāļĢāļ·āļ­āđ„āļĄāđˆ āļ–āđ‰āļēāđƒāļŠāđˆ āđƒāļŦāđ‰āļ„āļ·āļ™āļ„āđˆāļēāđ€āļ”āļīāļĄāđ‚āļ”āļĒāđ„āļĄāđˆāļ—āļģāļ‹āđ‰āļģ + +### **2.4 āļ‚āđ‰āļ­āļ•āļāļĨāļ‡āđƒāļ™āļāļēāļĢāļ•āļąāđ‰āļ‡āļŠāļ·āđˆāļ­ (Naming Conventions)** + +| Entity (āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āļąāđ‰āļ‡āļŠāļ·āđˆāļ­) | Convention (āļĢāļđāļ›āđāļšāļš) | Example (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡) | +| :----------------------- | :------------------ | :--------------------------------- | +| Classes | PascalCase | UserService | +| Property | snake_case | user_id | +| Variables & Functions | camelCase | getUserInfo | +| Files & Folders | kebab-case | user-service.ts | +| Environment Variables | UPPERCASE | DATABASE_URL | +| Booleans | Verb + Noun | isActive, canDelete, hasPermission | + +āđƒāļŠāđ‰āļ„āļģāđ€āļ•āđ‡āļĄ â€” āđ„āļĄāđˆāđƒāļŠāđ‰āļ­āļąāļāļĐāļĢāļĒāđˆāļ­ â€” āļĒāļāđ€āļ§āđ‰āļ™āļ„āļģāļĄāļēāļ•āļĢāļāļēāļ™ (āđ€āļŠāđˆāļ™ API, URL, req, res, err, ctx) + +### ðŸ§Đ**2.5 āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ (Functions)** + +- āđ€āļ‚āļĩāļĒāļ™āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āđƒāļŦāđ‰āļŠāļąāđ‰āļ™ āđāļĨāļ°āļ—āļģ **āļŦāļ™āđ‰āļēāļ—āļĩāđˆāđ€āļžāļĩāļĒāļ‡āļ­āļĒāđˆāļēāļ‡āđ€āļ”āļĩāļĒāļ§** (single-purpose) (\< 20 āļšāļĢāļĢāļ—āļąāļ”) +- āđƒāļŠāđ‰ **early returns** āđ€āļžāļ·āđˆāļ­āļĨāļ”āļāļēāļĢāļ‹āđ‰āļ­āļ™ (nesting) āļ‚āļ­āļ‡āđ‚āļ„āđ‰āļ” +- āđƒāļŠāđ‰ **map**, **filter**, **reduce** āđāļ—āļ™āļāļēāļĢāđƒāļŠāđ‰ loops āđ€āļĄāļ·āđˆāļ­āđ€āļŦāļĄāļēāļ°āļŠāļĄ +- āļ„āļ§āļĢāđƒāļŠāđ‰ **arrow functions** āļŠāļģāļŦāļĢāļąāļšāļ•āļĢāļĢāļāļ°āļŠāļąāđ‰āļ™āđ†, āđāļĨāļ°āđƒāļŠāđ‰ **named functions** āđƒāļ™āļāļĢāļ“āļĩāļ­āļ·āđˆāļ™ +- āđƒāļŠāđ‰ **default parameters** āđāļ—āļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āđˆāļē null +- āļˆāļąāļ”āļāļĨāļļāđˆāļĄāļžāļēāļĢāļēāļĄāļīāđ€āļ•āļ­āļĢāđŒāļŦāļĨāļēāļĒāļ•āļąāļ§āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™āļ­āđ‡āļ­āļšāđ€āļˆāļāļ•āđŒāđ€āļ”āļĩāļĒāļ§ (RO-RO pattern) +- āļŠāđˆāļ‡āļ„āđˆāļēāļāļĨāļąāļš (Return) āđ€āļ›āđ‡āļ™āļ­āđ‡āļ­āļšāđ€āļˆāļāļ•āđŒāļ—āļĩāđˆāļĄāļĩāđ„āļ—āļ›āđŒāļāļģāļŦāļ™āļ” (typed objects) āđ„āļĄāđˆāđƒāļŠāđˆāļ„āđˆāļēāļžāļ·āđ‰āļ™āļāļēāļ™ (primitives) +- āļĢāļąāļāļĐāļēāļĢāļ°āļ”āļąāļšāļ‚āļ­āļ‡āļŠāļīāđˆāļ‡āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļ™āļēāļĄāļ˜āļĢāļĢāļĄ (abstraction level) āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™āļĢāļ°āļ”āļąāļšāđ€āļ”āļĩāļĒāļ§āđƒāļ™āđāļ•āđˆāļĨāļ°āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ + +### ðŸ§ą**2.6 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨ (Data Handling)** + +- āļŦāđˆāļ­āļŦāļļāđ‰āļĄāļ‚āđ‰āļ­āļĄāļđāļĨ (Encapsulate) āđƒāļ™āđ„āļ—āļ›āđŒāđāļšāļšāļœāļŠāļĄ (composite types) +- āđƒāļŠāđ‰ **immutability** (āļāļēāļĢāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āļ„āđˆāļē) āļ”āđ‰āļ§āļĒ readonly āđāļĨāļ° as const +- āļ—āļģāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Validations) āđƒāļ™āļ„āļĨāļēāļŠāļŦāļĢāļ·āļ­ DTOs āđ„āļĄāđˆāđƒāļŠāđˆāļ āļēāļĒāđƒāļ™āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆ +- āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ‚āļ”āļĒāđƒāļŠāđ‰ DTOs āļ—āļĩāđˆāļĄāļĩāđ„āļ—āļ›āđŒāļāļģāļŦāļ™āļ”āđ€āļŠāļĄāļ­ + +### 🧰**2.7 āļ„āļĨāļēāļŠ (Classes)** + +- āļ›āļāļīāļšāļąāļ•āļīāļ•āļēāļĄāļŦāļĨāļąāļāļāļēāļĢ **SOLID** +- āļ„āļ§āļĢāđƒāļŠāđ‰ **composition āļĄāļēāļāļāļ§āđˆāļē inheritance** (Prefer composition over inheritance) +- āļāļģāļŦāļ™āļ” **interfaces** āļŠāļģāļŦāļĢāļąāļšāļŠāļąāļāļāļē (contracts) +- āđƒāļŦāđ‰āļ„āļĨāļēāļŠāļĄāļļāđˆāļ‡āđ€āļ™āđ‰āļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™āđ€āļ‰āļžāļēāļ°āļ­āļĒāđˆāļēāļ‡āđāļĨāļ°āļĄāļĩāļ‚āļ™āļēāļ”āđ€āļĨāđ‡āļ (\< 200 āļšāļĢāļĢāļ—āļąāļ”, \< 10 āđ€āļĄāļ˜āļ­āļ”, \< 10 properties) + +### ðŸšĻ**2.8 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (Error Handling)** + +- āđƒāļŠāđ‰ Exceptions āļŠāļģāļŦāļĢāļąāļšāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ”āļ—āļĩāđˆāđ„āļĄāđˆāļ„āļēāļ”āļ„āļīāļ” +- āļ”āļąāļāļˆāļąāļš (Catch) āļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ”āđ€āļžāļ·āđˆāļ­āđāļāđ‰āđ„āļ‚āļŦāļĢāļ·āļ­āđ€āļžāļīāđˆāļĄāļšāļĢāļīāļšāļ— (context) āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™; āļŦāļēāļāđ„āļĄāđˆāđ€āļŠāđˆāļ™āļ™āļąāđ‰āļ™ āđƒāļŦāđ‰āđƒāļŠāđ‰ global error handlers +- āļĢāļ°āļšāļļāļ‚āđ‰āļ­āļ„āļ§āļēāļĄāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (error messages) āļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļŦāļĄāļēāļĒāđ€āļŠāļĄāļ­ + +### 🧊**2.9 āļāļēāļĢāļ—āļ”āļŠāļ­āļš (āļ—āļąāđˆāļ§āđ„āļ›) (Testing (General))** + +- āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš **Arrange–Act–Assert** +- āđƒāļŠāđ‰āļŠāļ·āđˆāļ­āļ•āļąāļ§āđāļ›āļĢāđƒāļ™āļāļēāļĢāļ—āļ”āļŠāļ­āļšāļ—āļĩāđˆāļŠāļ·āđˆāļ­āļ„āļ§āļēāļĄāļŦāļĄāļēāļĒ (inputData, expectedOutput) +- āđ€āļ‚āļĩāļĒāļ™ **unit tests** āļŠāļģāļŦāļĢāļąāļš public methods āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +- āļˆāļģāļĨāļ­āļ‡ (Mock) āļāļēāļĢāļžāļķāđˆāļ‡āļžāļēāļ āļēāļĒāļ™āļ­āļ (external dependencies) +- āđ€āļžāļīāđˆāļĄ **acceptance tests** āļ•āđˆāļ­āđ‚āļĄāļ”āļđāļĨāđ‚āļ”āļĒāđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš Given–When-Then + +### **Testing Strategy āđ‚āļ”āļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”** + +- **Test Pyramid Structure** + + /\ + + / \ E2E Tests (10%) + /\_**\_\ Integration Tests (20%) + / \ Unit Tests (70%) + /**\_\_\*\*\*\*\ + +- **Testing Tools Stack** + +```typescript +// Backend Testing Stack +const backendTesting = { + unit: ['Jest', 'ts-jest', '@nestjs/testing'], + integration: ['Supertest', 'Testcontainers', 'Jest'], + e2e: ['Supertest', 'Jest', 'Database Seeds'], + security: ['Jest', 'Custom Security Test Helpers'], + performance: ['Jest', 'autocannon', 'artillery'], +}; + +// Frontend Testing Stack +const frontendTesting = { + unit: ['Vitest', 'React Testing Library'], + integration: ['React Testing Library', 'MSW'], + e2e: ['Playwright', 'Jest'], + visual: ['Playwright', 'Loki'], +}; +``` + +- **Test Data Management** + +```typescript +// Test Data Factories +interface TestDataFactory { + createUser(overrides?: Partial): User; + createCorrespondence(overrides?: Partial): Correspondence; + createRoutingTemplate(overrides?: Partial): RoutingTemplate; +} + +// Test Scenarios +const testScenarios = { + happyPath: 'Normal workflow execution', + edgeCases: 'Boundary conditions and limits', + errorConditions: 'Error handling and recovery', + security: 'Authentication and authorization', + performance: 'Load and stress conditions', +}; +``` + +## 🏗ïļ **3. āđāļšāđ‡āļāđ€āļ­āļ™āļ”āđŒ (NestJS) - Implementation Details** + +### **3.1 āļŦāļĨāļąāļāļāļēāļĢ** + +- **āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāđāļšāļšāđ‚āļĄāļ”āļđāļĨāļēāļĢāđŒ (Modular architecture)**: + - āļŦāļ™āļķāđˆāļ‡āđ‚āļĄāļ”āļđāļĨāļ•āđˆāļ­āļŦāļ™āļķāđˆāļ‡āđ‚āļ”āđ€āļĄāļ™ + - āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđāļšāļš Controller → Service → Repository (Model) +- API-First: āļĄāļļāđˆāļ‡āđ€āļ™āđ‰āļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ API āļ—āļĩāđˆāļĄāļĩāļ„āļļāļ“āļ āļēāļžāļŠāļđāļ‡ āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāļ›āļĢāļ°āļāļ­āļš (Swagger) āļ—āļĩāđˆāļŠāļąāļ”āđ€āļˆāļ™āļŠāļģāļŦāļĢāļąāļš Frontend Team +- DTOs āļ—āļĩāđˆāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ”āđ‰āļ§āļĒ **class-validator** +- āđƒāļŠāđ‰ **MikroORM** (āļŦāļĢāļ·āļ­ TypeORM/Prisma) āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āļ‡āļ­āļĒāļđāđˆāļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (persistence) āļ‹āļķāđˆāļ‡āļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļšāļŠāļ„āļĩāļĄāļē MariaDB +- āļŦāđˆāļ­āļŦāļļāđ‰āļĄāđ‚āļ„āđ‰āļ”āļ—āļĩāđˆāđƒāļŠāđ‰āļ‹āđ‰āļģāđ„āļ”āđ‰āđ„āļ§āđ‰āđƒāļ™ **common module** (@app/common): + - Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators + +### **3.2 Database & Data Modeling (MariaDB + TypeORM)** + +#### **3.2.1 Optimistic Locking & Versioning** + +āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition āđƒāļ™āļāļēāļĢāđāļāđ‰āđ„āļ‚āļ‚āđ‰āļ­āļĄāļđāļĨāļžāļĢāđ‰āļ­āļĄāļāļąāļ™ (āđ‚āļ”āļĒāđ€āļ‰āļžāļēāļ°āļāļēāļĢāļĢāļąāļ™āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ) āđƒāļŦāđ‰āđ€āļžāļīāđˆāļĄ Column `@VersionColumn()` āđƒāļ™ Entity āļ—āļĩāđˆāļŠāļģāļ„āļąāļ + +```typescript +@Entity() +export class DocumentCounter { + // ... fields + @Column() + last_number: number; + + @VersionColumn() // āđ€āļžāļīāđˆāļĄ Versioning + version: number; +} +``` + +#### **3.2.2 Virtual Columns for JSON Performance** + +āđ€āļ™āļ·āđˆāļ­āļ‡āļˆāļēāļāđ€āļĢāļēāđƒāļŠāđ‰ MariaDB 10.11 āđāļĨāļ°āļĄāļĩāļāļēāļĢāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ JSON (Details) āđƒāļŦāđ‰āđƒāļŠāđ‰ **Generated Columns (Virtual)** āļŠāļģāļŦāļĢāļąāļš Field āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡ Search/Sort āļšāđˆāļ­āļĒāđ† āđāļĨāļ°āļ—āļģ Index āļšāļ™ Virtual Column āļ™āļąāđ‰āļ™ + +```sql +-- āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ SQL Migration +ALTER TABLE correspondence_revisions +ADD COLUMN ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId'))) VIRTUAL; +CREATE INDEX idx_ref_project_id ON correspondence_revisions(ref_project_id); +``` + +#### **3.2.3 Partitioning Strategy** + +āļŠāļģāļŦāļĢāļąāļšāļ•āļēāļĢāļēāļ‡ `audit_logs` āđāļĨāļ° `notifications` āđƒāļŦāđ‰āđ€āļ•āļĢāļĩāļĒāļĄāļ­āļ­āļāđāļšāļš Entity āđƒāļŦāđ‰āļĢāļ­āļ‡āļĢāļąāļš Partitioning (āđ€āļŠāđˆāļ™ āđāļĒāļāļ•āļēāļĄāļ›āļĩ) āđ‚āļ”āļĒāđƒāļŠāđ‰ Raw SQL Migration āđƒāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āļ•āļēāļĢāļēāļ‡ + +### **3.3 File Storage Service (Two-Phase Storage)** + +āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡ Service āļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāđƒāļŦāđ‰āļĢāļ­āļ‡āļĢāļąāļš Transactional Integrity + +1. **Upload (Phase 1):** + - āļĢāļąāļšāđ„āļŸāļĨāđŒ → Scan Virus (ClamAV) → Save āļĨāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒ `temp/` + - Return `temp_id` āđāļĨāļ° Metadata āļāļĨāļąāļšāđ„āļ›āđƒāļŦāđ‰ Client +2. **Commit (Phase 2):** + - āđ€āļĄāļ·āđˆāļ­ Business Logic (āđ€āļŠāđˆāļ™ Create Correspondence) āļ—āļģāļ‡āļēāļ™āļŠāļģāđ€āļĢāđ‡āļˆ + - Service āļˆāļ°āļĒāđ‰āļēāļĒāđ„āļŸāļĨāđŒāļˆāļēāļ `temp/` āđ„āļ›āļĒāļąāļ‡ `permanent/{YYYY}/{MM}/` + - Update path āđƒāļ™ Database + - āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ™āļĩāđ‰āļ•āđ‰āļ­āļ‡āļ­āļĒāļđāđˆāļ āļēāļĒāđƒāļ•āđ‰ Database Transaction āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ (āļ–āđ‰āļē DB Fail, āđ„āļŸāļĨāđŒāļˆāļ°āļ„āđ‰āļēāļ‡āļ—āļĩāđˆ Temp āđāļĨāļ°āļ–āļđāļāļĨāļšāđ‚āļ”āļĒ Cron Job) + +### **3.4 Document Numbering (Double-Lock Mechanism)** + +āļāļēāļĢāļ­āļ­āļāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āļāļĨāđ„āļāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ 2 āļŠāļąāđ‰āļ™: + +1. **Layer 1 (Redis Lock):** āđƒāļŠāđ‰ `redlock` āđ€āļžāļ·āđˆāļ­ Block āđ„āļĄāđˆāđƒāļŦāđ‰ Process āļ­āļ·āđˆāļ™āđ€āļ‚āđ‰āļēāļĄāļēāļĒāļļāđˆāļ‡āļāļąāļš Counter āļ‚āļ­āļ‡ Project/Type āļ™āļąāđ‰āļ™āđ† āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§ +2. **Layer 2 (Optimistic Lock):** āļ•āļ­āļ™ Update Database āđƒāļŦāđ‰āđ€āļŠāđ‡āļ„ `version` āļ–āđ‰āļē version āđ€āļ›āļĨāļĩāđˆāļĒāļ™ (āđāļŠāļ”āļ‡āļ§āđˆāļē Redis Lock āļŦāļĨāļļāļ”āļŦāļĢāļ·āļ­āļĄāļĩāļ„āļ™āđāļ—āļĢāļ) āđƒāļŦāđ‰ Throw Error āđāļĨāļ° Retry āđƒāļŦāļĄāđˆ + +### **3.5 Unified Workflow Engine** + +Unified Workflow Engine (Core Architecture) + +- āļĢāļ°āļšāļšāđƒāļŠāđ‰ Workflow Engine āđ€āļ›āđ‡āļ™āļŦāļąāļ§āđƒāļˆāļŦāļĨāļąāļāđƒāļ™āļāļēāļĢāļ‚āļąāļšāđ€āļ„āļĨāļ·āđˆāļ­āļ™ State āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ: + - DSL Based: Logic āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ­āļĒāļđāđˆāļ—āļĩāđˆ workflow_definitions.dsl + - Instance Based: āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ­āļĒāļđāđˆāļ—āļĩāđˆ workflow_instances + - Module Integration: + - CorrespondenceModule -> āđ€āļĢāļĩāļĒāļ WorkflowEngine + - RfaModule -> āđ€āļĢāļĩāļĒāļ WorkflowEngine + - CirculationModule -> āđ€āļĢāļĩāļĒāļ WorkflowEngine +- āļŦāđ‰āļēāļĄ āļŠāļĢāđ‰āļēāļ‡āļ•āļēāļĢāļēāļ‡ Routing āđāļĒāļ (āđ€āļŠāđˆāļ™ rfa_workflows āļŦāļĢāļ·āļ­ correspondence_routings) āļ­āļĩāļāļ•āđˆāļ­āđ„āļ› + +### **3.6 āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļŦāļĨāļąāļ (Core Functionalities)** + +- Global **filters** āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļˆāļąāļ”āļāļēāļĢ exception +- **Middlewares** āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļˆāļąāļ”āļāļēāļĢ request +- **Guards** āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ­āļ™āļļāļāļēāļ• (permissions) āđāļĨāļ° RBAC +- **Interceptors** āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ response āđāļĨāļ°āļāļēāļĢāļšāļąāļ™āļ—āļķāļ log + +### **3.7 āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āđƒāļ™āļāļēāļĢ Deploy (QNAP Container Station)** + +- **āļŦāđ‰āļēāļĄāđƒāļŠāđ‰āđ„āļŸāļĨāđŒ .env** āđƒāļ™āļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļē Environment Variables [cite: 2.1] +- āļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (āđ€āļŠāđˆāļ™ Database connection string, JWT secret) **āļˆāļ°āļ•āđ‰āļ­āļ‡āļ–āļđāļāļāļģāļŦāļ™āļ”āļœāđˆāļēāļ™ Environment Variable āđƒāļ™ docker-compose.yml āđ‚āļ”āļĒāļ•āļĢāļ‡** [cite: 6.5] āļ‹āļķāđˆāļ‡āļˆāļ°āļˆāļąāļ”āļāļēāļĢāļœāđˆāļēāļ™ UI āļ‚āļ­āļ‡ QNAP Container Station [cite: 2.1] + +### **3.8 āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āļ”āđ‰āļēāļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ (Security Constraints):** + +- **File Upload Security:** āļ•āđ‰āļ­āļ‡āļĄāļĩ virus scanning (ClamAV), file type validation (white-list), āđāļĨāļ° file size limits (50MB) +- **Input Validation:** āļ•āđ‰āļ­āļ‡āļ›āđ‰āļ­āļ‡āļāļąāļ™ OWASP Top 10 vulnerabilities (SQL Injection, XSS, CSRF) +- **Rate Limiting:** āļ•āđ‰āļ­āļ‡ implement rate limiting āļ•āļēāļĄ strategy āļ—āļĩāđˆāļāļģāļŦāļ™āļ” +- **Secrets Management:** āļ•āđ‰āļ­āļ‡āļĄāļĩ mechanism āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ sensitive secrets āļ­āļĒāđˆāļēāļ‡āļ›āļĨāļ­āļ”āļ āļąāļĒ āđāļĄāđ‰āļˆāļ°āđƒāļŠāđ‰ docker-compose.yml + +### **3.9 āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨāļ•āļēāļĄāđ‚āļ”āđ€āļĄāļ™ (Domain-Driven Module Structure)** + +āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļšāļŠāļ„āļĩāļĄāļē SQL (LCBP3-DMS) āđ€āļĢāļēāļˆāļ°āđƒāļŠāđ‰āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨāđāļšāļš **Domain-Driven (āđāļšāđˆāļ‡āļ•āļēāļĄāļ‚āļ­āļšāđ€āļ‚āļ•āļ˜āļļāļĢāļāļīāļˆ)** āđāļ—āļ™āļāļēāļĢāđāļšāđˆāļ‡āļ•āļēāļĄāļŸāļąāļ‡āļāđŒāļŠāļąāļ™: + +#### 3.9.1 **CommonModule:** + +- āđ€āļāđ‡āļš Services āļ—āļĩāđˆāđƒāļŠāđ‰āļĢāđˆāļ§āļĄāļāļąāļ™ āđ€āļŠāđˆāļ™ DatabaseModule, FileStorageService (āļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāđƒāļ™ QNAP), AuditLogService, NotificationService +- āļˆāļąāļ”āļāļēāļĢ audit_logs +- NotificationService āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš Triggers āļ—āļĩāđˆāļĢāļ°āļšāļļāđƒāļ™ Requirement 6.7 [cite: 6.7] + +#### 3.9.2 **AuthModule:** + +- āļˆāļąāļ”āļāļēāļĢāļ°āļāļēāļĢāļĒāļ·āļ™āļĒāļąāļ™āļ•āļąāļ§āļ•āļ™ (JWT, Guards) +- **(āļŠāļģāļ„āļąāļ)** āļ•āđ‰āļ­āļ‡āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒ **4 āļĢāļ°āļ”āļąāļš** [cite: 4.2]: āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļĢāļ°āļšāļš (Global Role), āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļ­āļ‡āļāļĢāļ“āđŒ (Organization Role), āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒ (Project Role), āđāļĨāļ° āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļŠāļąāļāļāļē (Contract Role) +- **(āļŠāļģāļ„āļąāļ)** āļ•āđ‰āļ­āļ‡āļĄāļĩ API āļŠāļģāļŦāļĢāļąāļš **Admin Panel** āđ€āļžāļ·āđˆāļ­: + - āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢ Role āđāļĨāļ°āļāļēāļĢāļˆāļąāļšāļ„āļđāđˆ Permission āđāļšāļšāđ„āļ”āļ™āļēāļĄāļīāļ [cite: 4.3] + - āđƒāļŦāđ‰ Superadmin āļŠāļĢāđ‰āļēāļ‡ Organizations āđāļĨāļ°āļāļģāļŦāļ™āļ” Org Admin āđ„āļ”āđ‰ [cite: 4.6] + - āđƒāļŦāđ‰ Superadmin/Admin āļˆāļąāļ”āļāļēāļĢ document_number_formats (āļĢāļđāļ›āđāļšāļšāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ), document_number_counters (Running Number) [cite: 3.10] + +#### 3.9.3 **UserModule:** + +- āļˆāļąāļ”āļāļēāļĢ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles +- **(āļŠāļģāļ„āļąāļ)** āļ•āđ‰āļ­āļ‡āļĄāļĩ API āļŠāļģāļŦāļĢāļąāļš **Admin Panel** āđ€āļžāļ·āđˆāļ­: + - āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢ Role āđāļĨāļ°āļāļēāļĢāļˆāļąāļšāļ„āļđāđˆ Permission āđāļšāļšāđ„āļ”āļ™āļēāļĄāļīāļ [cite: 4.3] + +#### 3.9.4 **ProjectModule:** + +- āļˆāļąāļ”āļāļēāļĢ projects, organizations, contracts, project_parties, contract_parties + +#### 3.9.5 **MasterModule:** + +- āļˆāļąāļ”āļāļēāļĢ master data (correspondence_types, rfa_types, rfa_status_codes, rfa_approve_codes, circulation_status_codes, correspondence_types, correspondence_status, tags) [cite: 4.5] + +#### 3.9.6 **CorrespondenceModule (āđ‚āļĄāļ”āļđāļĨāļĻāļđāļ™āļĒāđŒāļāļĨāļēāļ‡):** + +- āļˆāļąāļ”āļāļēāļĢ correspondences, correspondence_revisions, correspondence_tags +- **(āļŠāļģāļ„āļąāļ)** Service āļ™āļĩāđ‰āļ•āđ‰āļ­āļ‡ Inject DocumentNumberingService āđ€āļžāļ·āđˆāļ­āļ‚āļ­āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆāļāđˆāļ­āļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ +- **(āļŠāļģāļ„āļąāļ)** āļ•āļĢāļĢāļāļ°āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡/āļ­āļąāļ›āđ€āļ”āļ• Revision āļˆāļ°āļ­āļĒāļđāđˆāđƒāļ™ Service āļ™āļĩāđ‰ +- āļˆāļąāļ”āļāļēāļĢ correspondence_attachments (āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāđ„āļŸāļĨāđŒāđāļ™āļš) +- āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļš Routing **Correspondence WorkflowService** āđ€āļ›āđ‡āļ™ Adapter āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļš Engine āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđˆāļ§āđ„āļ›āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ­āļ‡āļ„āđŒāļāļĢ + +#### 3.9.7 **RfaModule:** + +- āļˆāļąāļ”āļāļēāļĢ rfas, rfa_revisions, rfa_items +- āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāđ€āļ§āļīāļĢāđŒāļāđ‚āļŸāļĨāļ§āđŒ **"RFA WorkflowService"** āđ€āļ›āđ‡āļ™ Adapter āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļš Engine āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢāļ—āļēāļ‡āđ€āļ—āļ„āļ™āļīāļ„ + +#### 3.9.8 **DrawingModule:** + +- āļˆāļąāļ”āļāļēāļĢ shop_drawings, shop_drawing_revisions, contract_drawings, contract_drawing_volumes, contract_drawing_cats, contract_drawing_sub_cats, shop_drawing_main_categories, shop_drawing_sub_categories, contract_drawing_subcat_cat_maps, shop_drawing_revision_contract_refs +- āļˆāļąāļ”āļāļēāļĢ shop_drawing_revision_attachments āđāļĨāļ° contract_drawing_attachments(āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāđ„āļŸāļĨāđŒāđāļ™āļš) + +#### 3.9.9 **CirculationModule:** + +- āļˆāļąāļ”āļāļēāļĢ circulations, circulation_templates, circulation_assignees +- āļˆāļąāļ”āļāļēāļĢ circulation_attachments (āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāđ„āļŸāļĨāđŒāđāļ™āļš) +- āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāđ€āļ§āļīāļĢāđŒāļāđ‚āļŸāļĨāļ§āđŒ **"Circulations WorkflowService"** āđ€āļ›āđ‡āļ™ Adapter āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļš Engine āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢ **āļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ** + +#### 3.9.10 **TransmittalModule:** + +- āļˆāļąāļ”āļāļēāļĢ transmittals āđāļĨāļ° transmittal_items + +#### 3.9.11 **SearchModule:** + +- āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ (Advanced Search) [cite: 6.2] āđ‚āļ”āļĒāđƒāļŠāđ‰ **Elasticsearch** āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāđāļšāļš Full-text āļˆāļēāļāļŠāļ·āđˆāļ­āđ€āļĢāļ·āđˆāļ­āļ‡, āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”, āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ, āļ›āļĢāļ°āđ€āļ āļ—, āļ§āļąāļ™āļ—āļĩāđˆ, āđāļĨāļ° Tags +- āļĢāļ°āļšāļšāļˆāļ°āđƒāļŠāđ‰ Elasticsearch Engine āđƒāļ™āļāļēāļĢāļˆāļąāļ”āļ—āļģāļ”āļąāļŠāļ™āļĩāđ€āļžāļ·āđˆāļ­āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļŠāļīāļ‡āļĨāļķāļāļˆāļēāļāđ€āļ™āļ·āđ‰āļ­āļŦāļēāļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ āđ‚āļ”āļĒāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļ°āļ–āļđāļāļŠāđˆāļ‡āđ„āļ›āļ—āļģāļ”āļąāļŠāļ™āļĩāļˆāļēāļ Backend (NestJS) āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĄāļĩāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āļŦāļĢāļ·āļ­āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢ + +#### 3.9.12 **DocumentNumberingModule:** + +- **āļŠāļ–āļēāļ™āļ°:** āđ€āļ›āđ‡āļ™ Module āļ āļēāļĒāđƒāļ™ (Internal Module) āđ„āļĄāđˆāđ€āļ›āļīāļ” API āļŠāļđāđˆāļ āļēāļĒāļ™āļ­āļ +- **āļŦāļ™āđ‰āļēāļ—āļĩāđˆ:** āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢ `DocumentNumberingService` āđāļšāļš **Token-Based Generator** +- **Logic āđƒāļŦāļĄāđˆ (v1.4.4):** + - āļĢāļąāļš Context: `{ projectId, orgId, typeId, disciplineId?, subTypeId?, year }` + - āļ”āļķāļ‡ Template āļˆāļēāļ DB + - Parse Template āđ€āļžāļ·āđˆāļ­āļŦāļēāļ§āđˆāļēāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰ Key āđƒāļ”āļšāđ‰āļēāļ‡āđƒāļ™āļāļēāļĢāļ—āļģ Grouping Counter (āđ€āļŠāđˆāļ™ āļ–āđ‰āļē Template āļĄāļĩ `{DISCIPLINE}` āđƒāļŦāđ‰āđƒāļŠāđ‰ `discipline_id` āđƒāļ™āļāļēāļĢ query counter) + - āđƒāļŠāđ‰ **Double-Lock Mechanism** (Redis + Optimistic DB Lock) āđƒāļ™āļāļēāļĢāļ”āļķāļ‡āđāļĨāļ°āļ­āļąāļžāđ€āļ”āļ—āļ„āđˆāļē `last_number` +- **Features:** + - Application-level locking āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ race condition + - Retry mechanism āļ”āđ‰āļ§āļĒ exponential backoff + - Fallback mechanism āđ€āļĄāļ·āđˆāļ­āļāļēāļĢāļ‚āļ­āđ€āļĨāļ‚āļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + - Audit log āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĄāļĩāļāļēāļĢ generate āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆ + +#### 3.9.13 **CorrespondenceRoutingModule:** + +- **āļŠāļ–āļēāļ™āļ°:** āđ‚āļĄāļ”āļđāļĨāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđ€āļ­āļāļŠāļēāļĢ +- **āļŦāļ™āđ‰āļēāļ—āļĩāđˆ:** āļˆāļąāļ”āļāļēāļĢāđāļĄāđˆāđāļšāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđāļĨāļ°āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āļˆāļĢāļīāļ‡ +- **Entities:** + - CorrespondenceRoutingTemplate + - CorrespondenceRoutingTemplateStep + - CorrespondenceRouting +- **Features:** + - āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāđāļĄāđˆāđāļšāļšāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđ€āļ­āļāļŠāļēāļĢāļ•āļēāļĄāđāļĄāđˆāđāļšāļš + - āļ•āļīāļ”āļ•āļēāļĄāļŠāļ–āļēāļ™āļ°āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - āļ„āļģāļ™āļ§āļ“āļ§āļąāļ™āļ„āļĢāļšāļāļģāļŦāļ™āļ”āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī + - āļŠāđˆāļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­āđƒāļŦāļĄāđˆ + +#### 3.9.14 WorkflowEngineModule (New Core): + +- Entities: WorkflowDefinition, WorkflowInstance, WorkflowHistory +- Services: WorkflowEngineService, WorkflowDslService, WorkflowEventService +- Responsibility: āļˆāļąāļ”āļāļēāļĢ State Machine, Validate DSL, Execute Transitions + +#### 3.9.15 **JsonSchemaModule:** + +- **āļŠāļ–āļēāļ™āļ°:** Internal Module āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ JSON schemas +- **āļŦāļ™āđ‰āļēāļ—āļĩāđˆ:** Validate, transform, āđāļĨāļ° manage JSON data structures +- **Features:** + - JSON schema validation āļ”āđ‰āļ§āļĒ AJV + - Schema versioning āđāļĨāļ° migration + - Dynamic schema generation + - Data transformation āđāļĨāļ° sanitization + +#### 3.9.16 **DetailsService:** + +- **āļŠāļ–āļēāļ™āļ°:** Shared Service āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ details fields +- **āļŦāļ™āđ‰āļēāļ—āļĩāđˆ:** Centralized service āļŠāļģāļŦāļĢāļąāļš JSON details operations +- **Methods:** + - validateDetails(type: string, data: any): ValidationResult + - transformDetails(input: any, targetVersion: string): any + - sanitizeDetails(data: any): any + - getDefaultDetails(type: string): any + +### **3.10 āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļĢāļ°āļšāļš (System Architecture)** + +āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨ (Module Structure) āļ­āđ‰āļēāļ‡āļ–āļķāļ‡ Backend Development Plan v1.4.5 + +### **3.11 āļāļĨāļĒāļļāļ—āļ˜āđŒāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™āđāļĨāļ°āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (Resilience & Error Handling Strategy)** + +- **Circuit Breaker Pattern:** āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš external service calls (Email, LINE, Elasticsearch) +- **Retry Mechanism:** āļ”āđ‰āļ§āļĒ exponential backoff āļŠāļģāļŦāļĢāļąāļš transient failures +- **Fallback Strategies:** Graceful degradation āđ€āļĄāļ·āđˆāļ­āļšāļĢāļīāļāļēāļĢāļ āļēāļĒāļ™āļ­āļāļĨāđ‰āļĄāđ€āļŦāļĨāļ§ +- **Error Handling:** Error messages āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāđ€āļ›āļīāļ”āđ€āļœāļĒāļ‚āđ‰āļ­āļĄāļđāļĨ sensitive +- **Monitoring:** Centralized error monitoring āđāļĨāļ° alerting system + +### **3.12 FileStorageService (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđƒāļŦāļĄāđˆ):** + +- **Virus Scanning:** Integrate ClamAV āļŠāļģāļŦāļĢāļąāļš scan āđ„āļŸāļĨāđŒāļ—āļĩāđˆāļ­āļąāļ›āđ‚āļŦāļĨāļ”āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +- **File Type Validation:** āđƒāļŠāđ‰ white-list approach (PDF, DWG, DOCX, XLSX, ZIP) +- **File Size Limits:** 50MB āļ•āđˆāļ­āđ„āļŸāļĨāđŒ +- **Security Measures:** + - āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒāļ™āļ­āļ web root + - Download āļœāđˆāļēāļ™ authenticated endpoint āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ + - Download links āļĄāļĩ expiration time (24 āļŠāļąāđˆāļ§āđ‚āļĄāļ‡) + - File integrity checks (checksum) + - Access control checks āļāđˆāļ­āļ™āļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ” + +### **3.13 āđ€āđ€āļ—āļ„āđ‚āļ™āđ‚āļĨāļĒāļĩāļ—āļĩāđˆāđƒāļŠāđ‰ (Technology Stack)** + +| āļŠāđˆāļ§āļ™ | Library/Tool | āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ | +| ----------------------- | ---------------------------------------------------- | -------------------------------------------- | +| **Framework** | `@nestjs/core`, `@nestjs/common` | Core Framework | +| **Language** | `TypeScript` | āđƒāļŠāđ‰ TypeScript āļ—āļąāđ‰āļ‡āļĢāļ°āļšāļš | +| **Database** | `MariaDB 10.11` | āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ | +| **ORM** | `@nestjs/typeorm`, `typeorm` | 🗃ïļāļˆāļąāļ”āļāļēāļĢāļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āđāļĨāļ° Query āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ | +| **Validation** | `class-validator`, `class-transformer` | ðŸ“Ķāļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ°āđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđƒāļ™ DTO | +| **Auth** | `@nestjs/jwt`, `@nestjs/passport`, `passport-jwt` | 🔐āļāļēāļĢāļĒāļ·āļ™āļĒāļąāļ™āļ•āļąāļ§āļ•āļ™āļ”āđ‰āļ§āļĒ JWT | +| **Authorization** | `casl` | 🔐āļˆāļąāļ”āļāļēāļĢāļŠāļīāļ—āļ˜āļīāđŒāđāļšāļš RBAC | +| **File Upload** | `multer` | 📁āļˆāļąāļ”āļāļēāļĢāļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒ | +| **Search** | `@nestjs/elasticsearch` | 🔍āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ | +| **Notification** | `nodemailer` | 📎āļŠāđˆāļ‡āļ­āļĩāđ€āļĄāļĨāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ | +| **Scheduling** | `@nestjs/schedule` | 📎āļŠāļģāļŦāļĢāļąāļš Cron Jobs (āđ€āļŠāđˆāļ™ āđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ Deadline) | +| **Logging** | `winston` | 📊āļšāļąāļ™āļ—āļķāļ Log āļ—āļĩāđˆāļĄāļĩāļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž | +| **Testing** | `@nestjs/testing`, `jest`, `supertest` | 🧊āļ—āļ”āļŠāļ­āļš Unit, Integration āđāļĨāļ° E2E | +| **Documentation** | `@nestjs/swagger` | 🌐āļŠāļĢāđ‰āļēāļ‡ API Documentation āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī | +| **Security** | `helmet`, `rate-limiter-flexible` | ðŸ›Ąïļāđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāđƒāļŦāđ‰ API | +| **Resilience** | `@nestjs/circuit-breaker` | 🔄 Circuit breaker pattern | +| **Caching** | `@nestjs/cache-manager`, `cache-manager-redis-store` | ðŸ’ū Distributed caching | +| **Security** | `helmet`, `csurf`, `rate-limiter-flexible` | ðŸ›Ąïļ Security enhancements | +| **Validation** | `class-validator`, `class-transformer` | ✅ Input validation | +| **Monitoring** | `@nestjs/monitoring`, `winston` | 📊 Application monitoring | +| **File Processing** | `clamscan` | ðŸĶ  Virus scanning | +| **Cryptography** | `bcrypt`, `crypto` | 🔐 Password hashing āđāļĨāļ° checksums | +| **JSON Validation** | `ajv`, `ajv-formats` | ðŸŽŊ JSON schema validation | +| **JSON Processing** | `jsonpath`, `json-schema-ref-parser` | 🔧 JSON manipulation | +| **Data Transformation** | `class-transformer` | 🔄 Object transformation | +| **Compression** | `compression` | ðŸ“Ķ JSON compression | + +### **3.14 Security Testing:** + +- **Penetration Testing:** āļ—āļ”āļŠāļ­āļš OWASP Top 10 vulnerabilities +- **Security Audit:** Review code āļŠāļģāļŦāļĢāļąāļš security flaws +- **Virus Scanning Test:** āļ—āļ”āļŠāļ­āļš file upload security +- **Rate Limiting Test:** āļ—āļ”āļŠāļ­āļš rate limiting functionality + +### **3.15 Performance Testing:** + +- **Load Testing:** āļ—āļ”āļŠāļ­āļšāļ”āđ‰āļ§āļĒ realistic workloads +- **Stress Testing:** āļŦāļē breaking points āļ‚āļ­āļ‡āļĢāļ°āļšāļš +- **Endurance Testing:** āļ—āļ”āļŠāļ­āļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ•āđˆāļ­āđ€āļ™āļ·āđˆāļ­āļ‡āđ€āļ›āđ‡āļ™āđ€āļ§āļĨāļēāļ™āļēāļ™ + +### 🗄ïļ**3.16 Backend State Management** + +Backend (NestJS) āļ„āļ§āļĢāđ€āļ›āđ‡āļ™ **Stateless** (āđ„āļĄāđˆāđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°) "State" āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļˆāļ°āļ–āļđāļāļˆāļąāļ”āđ€āļāđ‡āļšāđƒāļ™ MariaDB + +- **Request-Scoped State (āļŠāļ–āļēāļ™āļ°āļ āļēāļĒāđƒāļ™ Request āđ€āļ”āļĩāļĒāļ§):** + - **āļ›āļąāļāļŦāļē:** āļˆāļ°āļŠāđˆāļ‡āļ•āđˆāļ­āļ‚āđ‰āļ­āļĄāļđāļĨ (āđ€āļŠāđˆāļ™ User āļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™) āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Guard āđāļĨāļ° Service āđƒāļ™ Request āđ€āļ”āļĩāļĒāļ§āļāļąāļ™āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āđ„āļĢ? + - **āļ§āļīāļ˜āļĩāđāļāđ‰:** āđƒāļŠāđ‰ **Request-Scoped Providers** āļ‚āļ­āļ‡ NestJS (āđ€āļŠāđˆāļ™ AuthContextService) āđ€āļžāļ·āđˆāļ­āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ User āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ—āļĩāđˆāđ„āļ”āđ‰āļˆāļēāļ AuthGuard āđāļĨāļ°āđƒāļŦāđ‰ Service āļ­āļ·āđˆāļ™ Inject āđ„āļ›āđƒāļŠāđ‰ +- **Application-Scoped State (āļāļēāļĢ Caching):** + - **āļ›āļąāļāļŦāļē:** āļ‚āđ‰āļ­āļĄāļđāļĨ Master (āđ€āļŠāđˆāļ™ roles, permissions, organizations) āļ–āļđāļāđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰āļšāđˆāļ­āļĒ + - **āļ§āļīāļ˜āļĩāđāļāđ‰:** āđƒāļŠāđ‰ **Caching** (āđ€āļŠāđˆāļ™ @nestjs/cache-manager) āđ€āļžāļ·āđˆāļ­ Caching āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļŦāļĨāđˆāļēāļ™āļĩāđ‰ āđāļĨāļ°āļĨāļ”āļ āļēāļĢāļ° Database + +### **3.17 Caching Strategy (āļ•āļēāļĄāļ‚āđ‰āļ­ 6.4.2):** + +- **Master Data Cache:** Roles, Permissions, Organizations (TTL: 1 hour) +- **User Session Cache:** User permissions āđāļĨāļ° profile (TTL: 30 minutes) +- **Search Result Cache:** Frequently searched queries (TTL: 15 minutes) +- **File Metadata Cache:** Attachment metadata (TTL: 1 hour) +- **Cache Invalidation:** Clear cache on update/delete operations + +### **3.18 āļāļēāļĢāđ„āļŦāļĨāļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Flow)** + +#### **3.18.1 Main Flow:** + +1. Request: āļœāđˆāļēāļ™ Nginx Proxy Manager -> NestJS Controller +2. **Rate Limiting:** RateLimitGuard āļ•āļĢāļ§āļˆāļŠāļ­āļš request limits +3. **Input Validation:** Validation Pipe āļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ° sanitize inputs +4. Authentication: JWT Guard āļ•āļĢāļ§āļˆāļŠāļ­āļš Token āđāļĨāļ°āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ User +5. Authorization: RBAC Guard āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒ +6. **Security Checks:** Virus scanning (āļŠāļģāļŦāļĢāļąāļš file upload), XSS protection +7. Business Logic: Service Layer āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļ•āļĢāļĢāļāļ°āļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆ +8. **Resilience:** Circuit breaker āđāļĨāļ° retry logic āļŠāļģāļŦāļĢāļąāļš external calls +9. Data Access: Repository Layer āļ•āļīāļ”āļ•āđˆāļ­āļāļąāļšāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ +10. **Caching:** Cache frequently accessed data +11. **Audit Log:** āļšāļąāļ™āļ—āļķāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļŠāļģāļ„āļąāļ +12. Response: āļŠāđˆāļ‡āļāļĨāļąāļšāđ„āļ›āļĒāļąāļ‡ Frontend + +#### **3.18.2 Workflow Data Flow:** + +1. User āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ â†’ āđ€āļĨāļ·āļ­āļ routing template +2. System āļŠāļĢāđ‰āļēāļ‡ routing instances āļ•āļēāļĄ template +3. āļŠāļģāļŦāļĢāļąāļšāđāļ•āđˆāļĨāļ° routing step: + + - āļāļģāļŦāļ™āļ” due date (āļˆāļēāļ expected_days) + - āļŠāđˆāļ‡ notification āđ„āļ›āļĒāļąāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļš + - āļ­āļąāļžāđ€āļ”āļ—āļŠāļ–āļēāļ™āļ°āđ€āļ›āđ‡āļ™ SENT + +4. āđ€āļĄāļ·āđˆāļ­āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļšāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ: + + - āļ­āļąāļžāđ€āļ”āļ—āļŠāļ–āļēāļ™āļ°āđ€āļ›āđ‡āļ™ ACTIONED/FORWARDED/REPLIED + - āļšāļąāļ™āļ—āļķāļ processed_by āđāļĨāļ° processed_at + - āļŠāđˆāļ‡ notification āđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ› (āļ–āđ‰āļēāļĄāļĩ) + +5. āđ€āļĄāļ·āđˆāļ­āļ„āļĢāļšāļ—āļļāļāļ‚āļąāđ‰āļ™āļ•āļ­āļ™ â†’ āļ­āļąāļžāđ€āļ”āļ—āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢāđ€āļ›āđ‡āļ™ COMPLETED + +#### **3.18.3 JSON Details Processing Flow:** + +1. **Receive Request** → Get JSON data from client +2. **Schema Validation** → Validate against predefined schema +3. **Data Sanitization** → Sanitize and transform data +4. **Version Check** → Handle schema version compatibility +5. **Storage** → Store validated JSON in database +6. **Retrieval** → Retrieve and transform on demand + +### 📊**3.19 Monitoring & Observability (āļ•āļēāļĄāļ‚āđ‰āļ­ 6.8)** + +#### **Application Monitoring:** + +- **Health Checks:** `/health` endpoint āļŠāļģāļŦāļĢāļąāļš load balancer +- **Metrics Collection:** Response times, error rates, throughput +- **Distributed Tracing:** āļŠāļģāļŦāļĢāļąāļš request tracing across services +- **Log Aggregation:** Structured logging āļ”āđ‰āļ§āļĒ JSON format +- **Alerting:** āļŠāļģāļŦāļĢāļąāļš critical errors āđāļĨāļ° performance degradation + +#### **Business Metrics:** + +- āļˆāļģāļ™āļ§āļ™ documents created āļ•āđˆāļ­āļ§āļąāļ™ +- Workflow completion rates +- User activity metrics +- System utilization rates +- Search query performance + +#### **Performance Targets:** + +- API Response Time: < 200ms (90th percentile) +- Search Query Performance: < 500ms +- File Upload Performance: < 30 seconds āļŠāļģāļŦāļĢāļąāļšāđ„āļŸāļĨāđŒ 50MB +- Cache Hit Ratio: > 80% + +## ðŸ–Ĩïļ **4. āļŸāļĢāļ­āļ™āļ•āđŒāđ€āļ­āļ™āļ”āđŒ (Next.js) - Implementation Details** + +**āđ‚āļ›āļĢāđ„āļŸāļĨāđŒāļ™āļąāļāļžāļąāļ’āļ™āļē (Developer Profile:** āļ§āļīāļĻāļ§āļāļĢ TypeScript + React/NextJS āļĢāļ°āļ”āļąāļš Senior +āđ€āļŠāļĩāđˆāļĒāļ§āļŠāļēāļ TailwindCSS, Shadcn/UI, āđāļĨāļ° Radix āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļžāļąāļ’āļ™āļē UI + +### **4.1 State Management & Offline Support** + +#### **4.1.1 Auto-Save Drafts** + +āđƒāļŠāđ‰ `zustand` āļĢāđˆāļ§āļĄāļāļąāļš middleware `persist` (āļĨāļ‡ LocalStorage) āļŠāļģāļŦāļĢāļąāļšāļŸāļ­āļĢāđŒāļĄāļ—āļĩāđˆāļĄāļĩāļ‚āļ™āļēāļ”āđƒāļŦāļāđˆ (RFA, Correspondence) āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļēāļĒāđ€āļĄāļ·āđˆāļ­āđ€āļ™āđ‡āļ•āļŦāļĨāļļāļ” + +```typescript +// lib/stores/draft-store.ts +export const useDraftStore = create( + persist( + (set) => ({ + drafts: {}, + saveDraft: (key, data) => + set((state) => ({ drafts: { ...state.drafts, [key]: data } })), + clearDraft: (key) => + set((state) => { + const newDrafts = { ...state.drafts }; + delete newDrafts[key]; + return { drafts: newDrafts }; + }), + }), + { name: 'form-drafts' } + ) +); +``` + +### **4.2 Dynamic Form Generator** + +āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļš JSON Schema āļŦāļĨāļēāļāļŦāļĨāļēāļĒāļĢāļđāļ›āđāļšāļš āđƒāļŦāđ‰āļŠāļĢāđ‰āļēāļ‡ Component āļāļĨāļēāļ‡āļ—āļĩāđˆāļĢāļąāļš Schema āđāļĨāđ‰āļ§ Gen Form āļ­āļ­āļāļĄāļē (āļĨāļ”āļāļēāļĢāđāļāđ‰ Code āļšāđˆāļ­āļĒāđ†) + +- **Libraries:** āđāļ™āļ°āļ™āļģ `react-jsonschema-form` āļŦāļĢāļ·āļ­āļŠāļĢāđ‰āļēāļ‡ Wrapper āļšāļ™ `react-hook-form` āļ—āļĩāđˆ Recursively render field āļ•āļēāļĄ Type +- **Validation:** āđƒāļŠāđ‰ `ajv` āļ—āļĩāđˆāļāļąāđˆāļ‡ Client āđ€āļžāļ·āđˆāļ­ Validate JSON āļāđˆāļ­āļ™ Submit + +### **4.3 Mobile Responsiveness (Card View)** + +āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (`DataTable`) āļ•āđ‰āļ­āļ‡āļĄāļĩāļ„āļ§āļēāļĄāļ‰āļĨāļēāļ”āđƒāļ™āļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ: + +- **Desktop:** āđāļŠāļ”āļ‡āđ€āļ›āđ‡āļ™ Table āļ›āļāļ•āļī +- **Mobile:** āđāļ›āļĨāļ‡āđ€āļ›āđ‡āļ™ **Card View** āđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī (āļ‹āđˆāļ­āļ™ Header, āđāļŠāļ”āļ‡ Label āļ„āļđāđˆ Value āđƒāļ™āđāļ•āđˆāļĨāļ° Card) + +```tsx +// components/ui/responsive-table.tsx +
+ {/* Desktop View */}
+
+
+ {data.map((item) => ( + + {/* Mobile View: Render cells as list items */} + + ))} +
+``` + +### **4.4 Optimistic Updates** + +āđƒāļŠāđ‰āļ„āļ§āļēāļĄāļŠāļēāļĄāļēāļĢāļ–āļ‚āļ­āļ‡ **TanStack Query** (`onMutate`) āđ€āļžāļ·āđˆāļ­āļ­āļąāļ›āđ€āļ”āļ• UI āļ—āļąāļ™āļ—āļĩ (āđ€āļŠāđˆāļ™ āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āļˆāļēāļ "āļĢāļ­āļ­āđˆāļēāļ™" āđ€āļ›āđ‡āļ™ "āļ­āđˆāļēāļ™āđāļĨāđ‰āļ§") āđāļĨāđ‰āļ§āļ„āđˆāļ­āļĒāļŠāđˆāļ‡ Request āđ„āļ› Server āļ–āđ‰āļē Failed āļ„āđˆāļ­āļĒ Rollback + +### **4.5 āđāļ™āļ§āļ—āļēāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāđ‚āļ„āđ‰āļ” (Code Implementation Guidelines)** + +- āđƒāļŠāđ‰ **early returns** āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļŠāļąāļ”āđ€āļˆāļ™ +- āđƒāļŠāđ‰āļ„āļĨāļēāļŠāļ‚āļ­āļ‡ **TailwindCSS** āđƒāļ™āļāļēāļĢāļāļģāļŦāļ™āļ”āļŠāđ„āļ•āļĨāđŒāđ€āļŠāļĄāļ­ +- āļ„āļ§āļĢāđƒāļŠāđ‰ class: syntax āđāļšāļšāļĄāļĩāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ (āļŦāļĢāļ·āļ­ utility clsx) āļĄāļēāļāļāļ§āđˆāļēāļāļēāļĢāđƒāļŠāđ‰ ternary operators āđƒāļ™ class strings +- āđƒāļŠāđ‰ **const arrow functions** āļŠāļģāļŦāļĢāļąāļš components āđāļĨāļ° handlers +- Event handlers āđƒāļŦāđ‰āļ‚āļķāđ‰āļ™āļ•āđ‰āļ™āļ”āđ‰āļ§āļĒ handle... (āđ€āļŠāđˆāļ™ handleClick, handleSubmit) +- āļĢāļ§āļĄāđāļ­āļ•āļ—āļĢāļīāļšāļīāļ§āļ•āđŒāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ (accessibility) āļ”āđ‰āļ§āļĒ: + tabIndex="0", aria-label, onKeyDown, āļŊāļĨāļŊ +- āļ•āļĢāļ§āļˆāļŠāļ­āļšāđƒāļŦāđ‰āđāļ™āđˆāđƒāļˆāļ§āđˆāļēāđ‚āļ„āđ‰āļ”āļ—āļąāđ‰āļ‡āļŦāļĄāļ” **āļŠāļĄāļšāļđāļĢāļ“āđŒ**, **āļœāđˆāļēāļ™āļāļēāļĢāļ—āļ”āļŠāļ­āļš**, āđāļĨāļ° **āđ„āļĄāđˆāļ‹āđ‰āļģāļ‹āđ‰āļ­āļ™ (DRY)** +- āļ•āđ‰āļ­āļ‡ import āđ‚āļĄāļ”āļđāļĨāļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āļ­āļĒāđˆāļēāļ‡āļŠāļąāļ”āđ€āļˆāļ™āđ€āļŠāļĄāļ­ + +### **4.6 UI/UX āļ”āđ‰āļ§āļĒ React** + +- āđƒāļŠāđ‰ **semantic HTML** +- āđƒāļŠāđ‰āļ„āļĨāļēāļŠāļ‚āļ­āļ‡ **Tailwind** āļ—āļĩāđˆāļĢāļ­āļ‡āļĢāļąāļš responsive (sm:, md:, lg:) +- āļĢāļąāļāļĐāļēāļĨāļģāļ”āļąāļšāļŠāļąāđ‰āļ™āļ‚āļ­āļ‡āļāļēāļĢāļĄāļ­āļ‡āđ€āļŦāđ‡āļ™ (visual hierarchy) āļ”āđ‰āļ§āļĒāļāļēāļĢāđƒāļŠāđ‰ typography āđāļĨāļ° spacing +- āđƒāļŠāđ‰ **Shadcn** components (Button, Input, Card, āļŊāļĨāļŊ) āđ€āļžāļ·āđˆāļ­ UI āļ—āļĩāđˆāļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļ™ +- āļ—āļģāđƒāļŦāđ‰ components āļĄāļĩāļ‚āļ™āļēāļ”āđ€āļĨāđ‡āļāđāļĨāļ°āļĄāļļāđˆāļ‡āđ€āļ™āđ‰āļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™āđ€āļ‰āļžāļēāļ°āļ­āļĒāđˆāļēāļ‡ +- āđƒāļŠāđ‰ utility classes āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļˆāļąāļ”āļŠāđ„āļ•āļĨāđŒāļ­āļĒāđˆāļēāļ‡āļĢāļ§āļ”āđ€āļĢāđ‡āļ§ (spacing, colors, text, āļŊāļĨāļŊ) +- āļ•āļĢāļ§āļˆāļŠāļ­āļšāđƒāļŦāđ‰āđāļ™āđˆāđƒāļˆāļ§āđˆāļēāļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļš **ARIA** āđāļĨāļ°āđƒāļŠāđ‰ semantic markup + +### **4.7 āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŸāļ­āļĢāđŒāļĄāđāļĨāļ°āļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ” (Form Validation & Errors)** + +- āđƒāļŠāđ‰āđ„āļĨāļšāļĢāļēāļĢāļĩāļāļąāđˆāļ‡ client āđ€āļŠāđˆāļ™ zod āđāļĨāļ° react-hook-form +- āđāļŠāļ”āļ‡āļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ”āļ”āđ‰āļ§āļĒ **alert components** āļŦāļĢāļ·āļ­āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ inline +- āļ•āđ‰āļ­āļ‡āļĄāļĩ labels, placeholders, āđāļĨāļ°āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ feedback + +### **🧊4.8 Frontend Testing** + +āđ€āļĢāļēāļˆāļ°āđƒāļŠāđ‰ **React Testing Library (RTL)** āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ—āļ”āļŠāļ­āļš Component āđāļĨāļ° **Playwright** āļŠāļģāļŦāļĢāļąāļš E2E: + +- **Unit Tests (āļāļēāļĢāļ—āļ”āļŠāļ­āļšāļŦāļ™āđˆāļ§āļĒāļĒāđˆāļ­āļĒ):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** Vitest + RTL + - **āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ:** āļ—āļ”āļŠāļ­āļš Component āļ‚āļ™āļēāļ”āđ€āļĨāđ‡āļ (āđ€āļŠāđˆāļ™ Buttons, Inputs) āļŦāļĢāļ·āļ­ Utility functions +- **Integration Tests (āļāļēāļĢāļ—āļ”āļŠāļ­āļšāļāļēāļĢāļšāļđāļĢāļ“āļēāļāļēāļĢ):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** RTL + **Mock Service Worker (MSW)** + - **āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ:** āļ—āļ”āļŠāļ­āļšāļ§āđˆāļē Component āļŦāļĢāļ·āļ­ Page āļ—āļģāļ‡āļēāļ™āļāļąāļš API (āļ—āļĩāđˆāļˆāļģāļĨāļ­āļ‡āļ‚āļķāđ‰āļ™) āđ„āļ”āđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡ + - **āđ€āļ—āļ„āļ™āļīāļ„:** āđƒāļŠāđ‰ MSW āđ€āļžāļ·āđˆāļ­āļˆāļģāļĨāļ­āļ‡ NestJS API āđāļĨāļ°āļ—āļ”āļŠāļ­āļšāļ§āđˆāļē Component āđāļŠāļ”āļ‡āļœāļĨāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļģāļĨāļ­āļ‡āđ„āļ”āđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡āļŦāļĢāļ·āļ­āđ„āļĄāđˆ (āđ€āļŠāđˆāļ™ āļ—āļ”āļŠāļ­āļšāļŦāļ™āđ‰āļē Dashboard [cite: 5.3] āļ—āļĩāđˆāļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļ v_user_tasks) +- **E2E (End-to-End) Tests:** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** **Playwright** + - **āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ:** āļ—āļ”āļŠāļ­āļš User Flow āļ—āļąāđ‰āļ‡āļĢāļ°āļšāļšāđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī (āđ€āļŠāđˆāļ™ āļĨāđ‡āļ­āļāļ­āļīāļ™ -> āļŠāļĢāđ‰āļēāļ‡ RFA -> āļ•āļĢāļ§āļˆāļŠāļ­āļš Workflow Visualization [cite: 5.6]) + +### **🗄ïļ4.9 Frontend State Management** + +āļŠāļģāļŦāļĢāļąāļš Next.js App Router āđ€āļĢāļēāļˆāļ°āđāļšāđˆāļ‡ State āđ€āļ›āđ‡āļ™ 4 āļĢāļ°āļ”āļąāļš: + +1. **Local UI State (āļŠāļ–āļēāļ™āļ° UI āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** useState, useReducer + - **āđƒāļŠāđ‰āđ€āļĄāļ·āđˆāļ­:** āļˆāļąāļ”āļāļēāļĢāļŠāļ–āļēāļ™āļ°āđ€āļĨāđ‡āļāđ† āļ—āļĩāđˆāļˆāļšāđƒāļ™ Component āđ€āļ”āļĩāļĒāļ§ (āđ€āļŠāđˆāļ™ Modal āđ€āļ›āļīāļ”/āļ›āļīāļ”, āļ„āđˆāļēāđƒāļ™ Input) +2. **Server State (āļŠāļ–āļēāļ™āļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļāđ€āļ‹āļīāļĢāđŒāļŸāđ€āļ§āļ­āļĢāđŒ):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** **React Query (TanStack Query)** āļŦāļĢāļ·āļ­ SWR + - **āđƒāļŠāđ‰āđ€āļĄāļ·āđˆāļ­:** āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļ”āļķāļ‡āļĄāļēāļˆāļēāļ NestJS API (āđ€āļŠāđˆāļ™ āļĢāļēāļĒāļāļēāļĢ correspondences, rfas, drawings) + - **āļ—āļģāđ„āļĄ:** React Query āđ€āļ›āđ‡āļ™ "Cache" āļ—āļĩāđˆāļˆāļąāļ”āļāļēāļĢ Caching, Re-fetching, āđāļĨāļ° Invalidation āđƒāļŦāđ‰āđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī +3. **Global Client State (āļŠāļ–āļēāļ™āļ°āļŠāđˆāļ§āļ™āļāļĨāļēāļ‡āļāļąāđˆāļ‡ Client):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** **Zustand** (āđāļ™āļ°āļ™āļģ) āļŦāļĢāļ·āļ­ Context API + - **āđƒāļŠāđ‰āđ€āļĄāļ·āđˆāļ­:** āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āļĢāđˆāļ§āļĄāļāļąāļ™āļ—āļąāđˆāļ§āļ—āļąāđ‰āļ‡āđāļ­āļ› āđāļĨāļ° _āđ„āļĄāđˆāđƒāļŠāđˆ_ āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļāđ€āļ‹āļīāļĢāđŒāļŸāđ€āļ§āļ­āļĢāđŒ (āđ€āļŠāđˆāļ™ āļ‚āđ‰āļ­āļĄāļđāļĨ User āļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™, āļŠāļīāļ—āļ˜āļīāđŒ Permissions) +4. **Form State (āļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡āļŸāļ­āļĢāđŒāļĄ):** + - **āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļĄāļ·āļ­:** **React Hook Form** + **Zod** + - **āđƒāļŠāđ‰āđ€āļĄāļ·āđˆāļ­:** āļˆāļąāļ”āļāļēāļĢāļŸāļ­āļĢāđŒāļĄāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™ (āđ€āļŠāđˆāļ™ āļŸāļ­āļĢāđŒāļĄāļŠāļĢāđ‰āļēāļ‡ RFA, āļŸāļ­āļĢāđŒāļĄ Circulation [cite: 3.7]) + +## 🔐 **5. Security & Access Control (Full Stack Integration)** + +### **5.1 CASL Integration (Shared Ability)** + +- **Backend:** āđƒāļŠāđ‰ CASL āļāļģāļŦāļ™āļ” Permission Rule +- **Frontend:** āđƒāļŦāđ‰āļ”āļķāļ‡ Rule (JSON) āļˆāļēāļ Backend āļĄāļē Load āđƒāļŠāđˆ `@casl/react` āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰ Logic āļāļēāļĢ Show/Hide āļ›āļļāđˆāļĄ āļ•āļĢāļ‡āļāļąāļ™ 100% + +### **5.2 Maintenance Mode** + +āđ€āļžāļīāđˆāļĄ Middleware (āļ—āļąāđ‰āļ‡ NestJS āđāļĨāļ° Next.js) āđ€āļžāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļš Flag āđƒāļ™ Redis: + +- āļ–āđ‰āļē `MAINTENANCE_MODE = true` +- **API:** Return `503 Service Unavailable` (āļĒāļāđ€āļ§āđ‰āļ™ Admin IP) +- **Frontend:** Redirect āđ„āļ›āļŦāļ™āđ‰āļē `/maintenance` + +### **5.3 Idempotency Client** + +āļŠāļĢāđ‰āļēāļ‡ Axios Interceptor āđ€āļžāļ·āđˆāļ­ Generate `Idempotency-Key` āļŠāļģāļŦāļĢāļąāļš POST/PUT/DELETE requests āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡ + +```typescript +// lib/api/client.ts +import { v4 as uuidv4 } from 'uuid'; + +apiClient.interceptors.request.use((config) => { + if (['post', 'put', 'delete'].includes(config.method)) { + config.headers['Idempotency-Key'] = uuidv4(); + } + return config; +}); +``` + +### **5.4 RBAC āđāļĨāļ°āļāļēāļĢāļ„āļ§āļšāļ„āļļāļĄāļŠāļīāļ—āļ˜āļīāđŒ (RBAC & Permission Control)** + +āđƒāļŠāđ‰ Decorators āđ€āļžāļ·āđˆāļ­āļšāļąāļ‡āļ„āļąāļšāđƒāļŠāđ‰āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ āđ‚āļ”āļĒāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļŠāļīāļ—āļ˜āļīāđŒāļˆāļēāļāļ•āļēāļĢāļēāļ‡ permissions + +```typescript +@RequirePermission('rfas.respond') // āļ•āđ‰āļ­āļ‡āļ•āļĢāļ‡āļāļąāļš 'permission_code' +@Put(':id') +updateRFA(@Param('id') id: string) { + return this.rfaService.update(id); +} +``` + +#### **5.4.1 Roles (āļšāļ—āļšāļēāļ—)** + +- **Superadmin**: āđ„āļĄāđˆāļĄāļĩāļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āđƒāļ”āđ† [cite: 4.3] +- **Admin**: āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđ€āļ•āđ‡āļĄāļ—āļĩāđˆāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ [cite: 4.3] +- **Document Control**: āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚/āļĨāļš āđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ [cite: 4.3] +- **Editor**: āļŠāļēāļĄāļēāļĢāļ– āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚ āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļāļģāļŦāļ™āļ” [cite: 4.3] +- **Viewer**: āļŠāļēāļĄāļēāļĢāļ–āļ”āļđ āđ€āļ­āļāļŠāļēāļĢ [cite: 4.3] + +#### **5.4.2 āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ Permissions (āļˆāļēāļāļ•āļēāļĢāļēāļ‡ permissions)** + +- rfas.view, rfas.create, rfas.respond, rfas.delete +- drawings.view, drawings.upload, drawings.delete +- corr.view, corr.manage +- transmittals.manage +- cirs.manage +- project_parties.manage + +āļāļēāļĢāļˆāļąāļšāļ„āļđāđˆāļĢāļ°āļŦāļ§āđˆāļēāļ‡ roles āđāļĨāļ° permissions **āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™** āļˆāļ°āļ–āļđāļ seed āļœāđˆāļēāļ™āļŠāļ„āļĢāļīāļ›āļ•āđŒ (āļ”āļąāļ‡āļ—āļĩāđˆāđ€āļŦāđ‡āļ™āđƒāļ™āđ„āļŸāļĨāđŒ SQL)**āļ­āļĒāđˆāļēāļ‡āđ„āļĢāļāđ‡āļ•āļēāļĄ AuthModule/UserModule āļ•āđ‰āļ­āļ‡āļĄāļĩ API āļŠāļģāļŦāļĢāļąāļš Admin āđ€āļžāļ·āđˆāļ­āļŠāļĢāđ‰āļēāļ‡ Role āđƒāļŦāļĄāđˆāđāļĨāļ°āļāļģāļŦāļ™āļ”āļŠāļīāļ—āļ˜āļīāđŒ (Permissions) āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄāđ„āļ”āđ‰āđƒāļ™āļ āļēāļĒāļŦāļĨāļąāļ‡** [cite: 4.3] + +## 📊 **6. Notification & Background Jobs** + +### **6.1 Digest Notification** + +āļŦāđ‰āļēāļĄāļŠāđˆāļ‡ Email āļ—āļąāļ™āļ—āļĩāļ—āļĩāđˆāđ€āļāļīāļ” Event āđƒāļŦāđ‰: + +1. Push Event āļĨāļ‡ Queue (Redis/BullMQ) +2. āļĄāļĩ Processor āļĢāļ­āđ€āļ§āļĨāļē (āđ€āļŠāđˆāļ™ 5 āļ™āļēāļ—āļĩ) āđ€āļžāļ·āđˆāļ­ Group Events āļ—āļĩāđˆāļ„āļĨāđ‰āļēāļĒāļāļąāļ™ (āđ€āļŠāđˆāļ™ "āļ„āļļāļ“āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāļĢāļ­āļ­āļ™āļļāļĄāļąāļ•āļī 5 āļ‰āļšāļąāļš") +3. āļŠāđˆāļ‡ Email āđ€āļ”āļĩāļĒāļ§ (Digest) āđ€āļžāļ·āđˆāļ­āļĨāļ” Spam + +## 🔗 **7. āđāļ™āļ§āļ—āļēāļ‡āļāļēāļĢāļšāļđāļĢāļ“āļēāļāļēāļĢ Full Stack (Full Stack Integration Guidelines)** + +| Aspect (āđāļ‡āđˆāļĄāļļāļĄ) | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) | +| :------------------------- | :------------------------- | :----------------------------- | :------------------------------------- | +| API | REST / GraphQL Controllers | API hooks āļœāđˆāļēāļ™ fetch/axios/SWR | Components āļ—āļĩāđˆāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨ | +| Validation (āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš) | class-validator DTOs | zod / react-hook-form | āļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡āļŸāļ­āļĢāđŒāļĄ/input āđƒāļ™ Shadcn | +| Auth (āļāļēāļĢāļĒāļ·āļ™āļĒāļąāļ™āļ•āļąāļ§āļ•āļ™) | Guards, JWT | NextAuth / cookies | āļŠāļ–āļēāļ™āļ° UI āļ‚āļ­āļ‡ Auth (loading, signed in) | +| Errors (āļ‚āđ‰āļ­āļœāļīāļ”āļžāļĨāļēāļ”) | Global filters | Toasts / modals | Alerts / āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ feedback | +| Testing (āļāļēāļĢāļ—āļ”āļŠāļ­āļš) | Jest (unit/e2e) | Vitest / Playwright | Visual regression | +| Styles (āļŠāđ„āļ•āļĨāđŒ) | Scoped modules (āļ–āđ‰āļēāļˆāļģāđ€āļ›āđ‡āļ™) | Tailwind / Shadcn | Tailwind utilities | +| Accessibility (āļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡) | Guards + filters | ARIA attributes | Semantic HTML | + +## 🗂ïļ **8. āļ‚āđ‰āļ­āļ•āļāļĨāļ‡āđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš DMS (LCBP3-DMS)** + +āļŠāđˆāļ§āļ™āļ™āļĩāđ‰āļ‚āļĒāļēāļĒāđāļ™āļ§āļ—āļēāļ‡ FullStackJS āļ—āļąāđˆāļ§āđ„āļ›āļŠāļģāļŦāļĢāļąāļšāđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒ **LCBP3-DMS** āđ‚āļ”āļĒāļĄāļļāđˆāļ‡āđ€āļ™āđ‰āļ™āđ„āļ›āļ—āļĩāđˆāđ€āļ§āļīāļĢāđŒāļāđ‚āļŸāļĨāļ§āđŒāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢ (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation) + +### ðŸ§ū**8.1 āļĄāļēāļ•āļĢāļāļēāļ™ AuditLog (AuditLog Standard)** + +āļšāļąāļ™āļ—āļķāļāļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ CRUD āđāļĨāļ°āļāļēāļĢāļˆāļąāļšāļ„āļđāđˆāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļĨāļ‡āđƒāļ™āļ•āļēāļĢāļēāļ‡ audit_logs + +| Field (āļŸāļīāļĨāļ”āđŒ) | Type (āļˆāļēāļ SQL) | Description (āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ) | +| :------------ | :------------- | :----------------------------------------------- | +| audit_id | BIGINT | Primary Key | +| user_id | INT | āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (FK -> users) | +| action | VARCHAR(100) | rfa.create, correspondence.update, login.success | +| entity_type | VARCHAR(50) | āļŠāļ·āđˆāļ­āļ•āļēāļĢāļēāļ‡/āđ‚āļĄāļ”āļđāļĨ āđ€āļŠāđˆāļ™ 'rfa', 'correspondence' | +| entity_id | VARCHAR(50) | Primary ID āļ‚āļ­āļ‡āļĢāļ°āđ€āļšāļĩāļĒāļ™āļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļœāļĨāļāļĢāļ°āļ—āļš | +| details_json | JSON | āļ‚āđ‰āļ­āļĄāļđāļĨāļšāļĢāļīāļšāļ— (āđ€āļŠāđˆāļ™ āļŸāļīāļĨāļ”āđŒāļ—āļĩāđˆāļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡) | +| ip_address | VARCHAR(45) | IP address āļ‚āļ­āļ‡āļœāļđāđ‰āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ | +| user_agent | VARCHAR(255) | User Agent āļ‚āļ­āļ‡āļœāļđāđ‰āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ | +| created_at | TIMESTAMP | Timestamp (UTC) | + +### 📂**8.2 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒ (File Handling)** + +#### **8.2.1 āļĄāļēāļ•āļĢāļāļēāļ™āļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒ (File Upload Standard)** + +- **Security-First Approach:** āļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļˆāļ°āļ–āļđāļāļˆāļąāļ”āļāļēāļĢāđ‚āļ”āļĒ FileStorageService āļ—āļĩāđˆāļĄāļĩ security measures āļ„āļĢāļšāļ–āđ‰āļ§āļ™ +- āđ„āļŸāļĨāđŒāļˆāļ°āļ–āļđāļāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āđ„āļ›āļĒāļąāļ‡ Entity āļ—āļĩāđˆāļ–āļđāļāļ•āđ‰āļ­āļ‡āļœāđˆāļēāļ™ **āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ (Junction Tables)** āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™: + - correspondence_attachments (āđ€āļŠāļ·āđˆāļ­āļĄ Correspondence āļāļąāļš Attachments) + - circulation_attachments (āđ€āļŠāļ·āđˆāļ­āļĄ Circulation āļāļąāļš Attachments) + - shop_drawing_revision_attachments (āđ€āļŠāļ·āđˆāļ­āļĄ Shop Drawing Revision āļāļąāļš Attachments) + - contract_drawing_attachments (āđ€āļŠāļ·āđˆāļ­āļĄ Contract Drawing āļāļąāļš Attachments) +- āđ€āļŠāđ‰āļ™āļ—āļēāļ‡āļˆāļąāļ”āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒ (Upload path): āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļˆāļēāļ Requirement 2.1 āļ„āļ·āļ­ /share/dms-data [cite: 2.1] āđ‚āļ”āļĒ FileStorageService āļˆāļ°āļŠāļĢāđ‰āļēāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļĒāđˆāļ­āļĒāđāļšāļšāļĢāļ§āļĄāļĻāļđāļ™āļĒāđŒ (āđ€āļŠāđˆāļ™ /share/dms-data/uploads/{YYYY}/{MM}/[stored_filename]) +- āļ›āļĢāļ°āđ€āļ āļ—āđ„āļŸāļĨāđŒāļ—āļĩāđˆāļ­āļ™āļļāļāļēāļ•: pdf, dwg, docx, xlsx, zip (āļœāđˆāļēāļ™ white-list validation) +- āļ‚āļ™āļēāļ”āļŠāļđāļ‡āļŠāļļāļ”: **50 MB** +- āļˆāļąāļ”āđ€āļāđ‡āļšāļ™āļ­āļ webroot +- āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢāđ„āļŸāļĨāđŒāļœāđˆāļēāļ™ endpoint āļ—āļĩāđˆāļ›āļĨāļ­āļ”āļ āļąāļĒ /files/:attachment_id/download + +#### **8.2.2 Security Controls āļŠāļģāļŦāļĢāļąāļš File Access:** + +āļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ„āļŸāļĨāđŒāđ„āļĄāđˆāđƒāļŠāđˆāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ‚āļ”āļĒāļ•āļĢāļ‡ endpoint /files/:attachment_id/download āļˆāļ°āļ•āđ‰āļ­āļ‡: + +1. āļ„āđ‰āļ™āļŦāļēāļĢāļ°āđ€āļšāļĩāļĒāļ™ attachment +2. āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļē attachment_id āļ™āļĩāđ‰ āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļāļąāļš Entity āđƒāļ” (āđ€āļŠāđˆāļ™ correspondence, circulation, shop_drawing_revision, contract_drawing) āļœāđˆāļēāļ™āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ +3. āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļēāļœāļđāđ‰āđƒāļŠāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ (permission) āđƒāļ™āļāļēāļĢāļ”āļđ Entity āļ•āđ‰āļ™āļ—āļēāļ‡āļ™āļąāđ‰āļ™āđ† āļŦāļĢāļ·āļ­āđ„āļĄāđˆ +4. āļ•āļĢāļ§āļˆāļŠāļ­āļš download token expiration (24 āļŠāļąāđˆāļ§āđ‚āļĄāļ‡) +5. āļšāļąāļ™āļ—āļķāļ audit log āļāļēāļĢāļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ” + +### 🔟**8.3 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Document Numbering) [cite: 3.10]** + +- **āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ:** āļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (āđ€āļŠāđˆāļ™ correspondence_number) āđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī āļ•āļēāļĄāļĢāļđāļ›āđāļšāļšāļ—āļĩāđˆāļāļģāļŦāļ™āļ” +- **āļ•āļĢāļĢāļāļ°āļāļēāļĢāļ™āļąāļš:** āļāļēāļĢāļ™āļąāļš Running number (SEQ) āļˆāļ°āļ™āļąāļšāđāļĒāļāļ•āļēāļĄ Key: **Project + Originator Organization + Document Type + Year** +- **āļ•āļēāļĢāļēāļ‡ SQL (Updated):** + - `document_number_formats`: āđ€āļāđ‡āļš Template String (āđ€āļŠāđˆāļ™ `{CONTRACT}-{TYPE}-{DISCIPLINE}-{SEQ:4}`) + - `document_number_counters`: **Primary Key āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđ€āļ›āđ‡āļ™ Composite Key āđƒāļŦāļĄāđˆ:** `(project_id, originator_id, type_id, discipline_id, current_year)` āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļĢāļąāļ™āđ€āļĨāļ‚āđāļĒāļāļ•āļēāļĄāļŠāļēāļ‚āļē +- **āļāļēāļĢāļ—āļģāļ‡āļēāļ™:** + - Service āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢ Resolve Token āļžāļīāđ€āļĻāļĐ āđ€āļŠāđˆāļ™ `{SUBTYPE_NUM}` āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āđ„āļ› Join āļāļąāļšāļ•āļēāļĢāļēāļ‡ `correspondence_sub_types` + - DocumentNumberingModule āļˆāļ°āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢ DocumentNumberingService + - āđ€āļĄāļ·āđˆāļ­ CorrespondenceModule āļ•āđ‰āļ­āļ‡āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆ, āļĄāļąāļ™āļˆāļ°āđ€āļĢāļĩāļĒāļ documentNumberingService.generateNextNumber(...) + - Service āļ™āļĩāđ‰āļˆāļ°āđƒāļŠāđ‰ **Redis distributed locking** āđāļ—āļ™ stored procedure āļ‹āļķāđˆāļ‡āļˆāļ°āļˆāļąāļ”āļāļēāļĢ Database Transaction āđāļĨāļ° Row Locking āļ āļēāļĒāđƒāļ™ Application Layer āđ€āļžāļ·āđˆāļ­āļĢāļąāļšāļ›āļĢāļ°āļāļąāļ™āļāļēāļĢāļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition + - āļĄāļĩ retry mechanism āđāļĨāļ° fallback strategies + +### 📊**8.4 āļāļēāļĢāļĢāļēāļĒāļ‡āļēāļ™āđāļĨāļ°āļāļēāļĢāļŠāđˆāļ‡āļ­āļ­āļ (Reporting & Exports)** + +#### **8.4.1 āļ§āļīāļ§āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļĢāļēāļĒāļ‡āļēāļ™ (Reporting Views) (āļˆāļēāļ SQL)** + +āļāļēāļĢāļĢāļēāļĒāļ‡āļēāļ™āļ„āļ§āļĢāļŠāļĢāđ‰āļēāļ‡āļ‚āļķāđ‰āļ™āļˆāļēāļ Views āļ—āļĩāđˆāļāļģāļŦāļ™āļ”āđ„āļ§āđ‰āļĨāđˆāļ§āļ‡āļŦāļ™āđ‰āļēāđƒāļ™āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ›āđ‡āļ™āļŦāļĨāļąāļ: + +- v_current_correspondences: āļŠāļģāļŦāļĢāļąāļš revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ„āļĄāđˆāđƒāļŠāđˆ RFA +- v_current_rfas: āļŠāļģāļŦāļĢāļąāļš revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡ RFA āđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨ master +- v_contract_parties_all: āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāļ‚āļ­āļ‡ project/contract/organization +- v_user_tasks: āļŠāļģāļŦāļĢāļąāļš Dashboard "āļ‡āļēāļ™āļ‚āļ­āļ‡āļ‰āļąāļ™" +- v_audit_log_details: āļŠāļģāļŦāļĢāļąāļš Activity Feed + +Views āđ€āļŦāļĨāđˆāļēāļ™āļĩāđ‰āļ—āļģāļŦāļ™āđ‰āļēāļ—āļĩāđˆāđ€āļ›āđ‡āļ™āđāļŦāļĨāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļĢāļēāļĒāļ‡āļēāļ™āļāļąāđˆāļ‡āđ€āļ‹āļīāļĢāđŒāļŸāđ€āļ§āļ­āļĢāđŒāđāļĨāļ°āļāļēāļĢāļŠāđˆāļ‡āļ­āļ­āļāļ‚āđ‰āļ­āļĄāļđāļĨ + +#### **8.4.2 āļāļŽāļāļēāļĢāļŠāđˆāļ‡āļ­āļ­āļ (Export Rules)** + +- Export formats: CSV, Excel, PDF. +- āļˆāļąāļ”āđ€āļ•āļĢāļĩāļĒāļĄāļĄāļļāļĄāļĄāļ­āļ‡āļŠāļģāļŦāļĢāļąāļšāļžāļīāļĄāļžāđŒ (Print view). +- āļĢāļ§āļĄāļĨāļīāļ‡āļāđŒāđ„āļ›āļĒāļąāļ‡āļ•āđ‰āļ™āļ—āļēāļ‡ (āđ€āļŠāđˆāļ™ /rfas/:id). + +## ðŸ§Ū **9. āļŸāļĢāļ­āļ™āļ•āđŒāđ€āļ­āļ™āļ”āđŒ: āļĢāļđāļ›āđāļšāļš DataTable āđāļĨāļ°āļŸāļ­āļĢāđŒāļĄ (Frontend: DataTable & Form Patterns)** + +### **9.1 DataTable (Server‑Side)** + +- Endpoint: /api/{module}?page=1&pageSize=20&sort=...&filter=... +- āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš: āļāļēāļĢāđāļšāđˆāļ‡āļŦāļ™āđ‰āļē (pagination), āļāļēāļĢāđ€āļĢāļĩāļĒāļ‡āļĨāļģāļ”āļąāļš (sorting), āļāļēāļĢāļ„āđ‰āļ™āļŦāļē (search), āļāļēāļĢāļāļĢāļ­āļ‡ (filters) +- āđāļŠāļ”āļ‡ revision āļĨāđˆāļēāļŠāļļāļ”āđāļšāļš inline āđ€āļŠāļĄāļ­ (āļŠāļģāļŦāļĢāļąāļš RFA/Drawing) + +### **9.2 āļĄāļēāļ•āļĢāļāļēāļ™āļŸāļ­āļĢāđŒāļĄ (Form Standards)** + +- āļ•āđ‰āļ­āļ‡āļĄāļĩāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ Dropdowns āđāļšāļšāļ‚āļķāđ‰āļ™āļ•āđˆāļ­āļāļąāļ™ (Dependent dropdowns) (āļ•āļēāļĄāļ—āļĩāđˆāļŠāļ„āļĩāļĄāļēāļĢāļ­āļ‡āļĢāļąāļš): + - Project → Contract Drawing Volumes + - Contract Drawing Category → Sub-Category + - RFA (āļ›āļĢāļ°āđ€āļ āļ— Shop Drawing) → Shop Drawing Revisions āļ—āļĩāđˆāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āđ„āļ”āđ‰ +- **File Upload Security:** āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš **Multi-file upload (Drag-and-Drop)** [cite: 5.7] āļžāļĢāđ‰āļ­āļĄ virus scanning feedback +- **File Type Indicators:** UI āļ•āđ‰āļ­āļ‡āļ­āļ™āļļāļāļēāļ•āđƒāļŦāđ‰āļœāļđāđ‰āđƒāļŠāđ‰āļāļģāļŦāļ™āļ”āļ§āđˆāļēāđ„āļŸāļĨāđŒāđƒāļ”āđ€āļ›āđ‡āļ™ **"āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ"** āļŦāļĢāļ·āļ­ "āđ€āļ­āļāļŠāļēāļĢāđāļ™āļšāļ›āļĢāļ°āļāļ­āļš" [cite: 5.7] āļžāļĢāđ‰āļ­āļĄāđāļŠāļ”āļ‡ file type icons +- **Security Feedback:** āđāļŠāļ”āļ‡ security warnings āļŠāļģāļŦāļĢāļąāļš file types āļ—āļĩāđˆāđ€āļŠāļĩāđˆāļĒāļ‡āļŦāļĢāļ·āļ­ files āļ—āļĩāđˆ fail virus scan +- āļŠāđˆāļ‡ (Submit) āļœāđˆāļēāļ™ API āļžāļĢāđ‰āļ­āļĄ feedback āđāļšāļš toast + +### **9.3 āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” Component āđ€āļ‰āļžāļēāļ° (Specific UI Requirements)** + +- **Dashboard - My Tasks:** āļ•āđ‰āļ­āļ‡āļžāļąāļ’āļ™āļē Component āļ•āļēāļĢāļēāļ‡ "āļ‡āļēāļ™āļ‚āļ­āļ‡āļ‰āļąāļ™" (My Tasks)āļ‹āļķāđˆāļ‡āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ‡āļēāļ™āļ—āļĩāđˆāļœāļđāđ‰āđƒāļŠāđ‰āļĨāđ‡āļ­āļāļ­āļīāļ™āļ­āļĒāļđāđˆāļ•āđ‰āļ­āļ‡āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļš (Main/Action) āļˆāļēāļ v_user_tasks [cite: 5.3] +- **Workflow Visualization:** āļ•āđ‰āļ­āļ‡āļžāļąāļ’āļ™āļē Component āļŠāļģāļŦāļĢāļąāļšāđāļŠāļ”āļ‡āļœāļĨ Workflow (āđ‚āļ”āļĒāđ€āļ‰āļžāļēāļ° RFA)āļ—āļĩāđˆāđāļŠāļ”āļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ€āļ›āđ‡āļ™āļĨāļģāļ”āļąāļš āđ‚āļ”āļĒāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ›āļąāļˆāļˆāļļāļšāļąāļ™ (active) āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™āļ—āļĩāđˆāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđ„āļ”āđ‰ āđāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ­āļ·āđˆāļ™āđ€āļ›āđ‡āļ™ disabled [cite: 5.6] āļ•āđ‰āļ­āļ‡āļĄāļĩāļ•āļĢāļĢāļāļ°āļŠāļģāļŦāļĢāļąāļš Admin āđƒāļ™āļāļēāļĢ override āļŦāļĢāļ·āļ­āļĒāđ‰āļ­āļ™āļāļĨāļąāļšāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđ„āļ”āđ‰ [cite: 5.6] +- **Admin Panel:** āļ•āđ‰āļ­āļ‡āļĄāļĩāļŦāļ™āđ‰āļē UI āļŠāļģāļŦāļĢāļąāļš Superadmin/Admin āđ€āļžāļ·āđˆāļ­āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ (Master Data [cite: 4.5]), āļāļēāļĢāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āđƒāļŠāđ‰āļ‡āļēāļ™ (Onboarding [cite: 4.6]), āđāļĨāļ° **āļĢāļđāļ›āđāļšāļšāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Numbering Formats [cite: 3.10])** +- **Security Dashboard:** āđāļŠāļ”āļ‡ security metrics āđāļĨāļ° audit logs āļŠāļģāļŦāļĢāļąāļš administrators + +## 🧭 **10. āđāļ”āļŠāļšāļ­āļĢāđŒāļ”āđāļĨāļ°āļŸāļĩāļ”āļāļīāļˆāļāļĢāļĢāļĄ (Dashboard & Activity Feed)** + +### **10.1 āļāļēāļĢāđŒāļ”āļšāļ™āđāļ”āļŠāļšāļ­āļĢāđŒāļ” (Dashboard Cards)** + +- āđāļŠāļ”āļ‡ Correspondences, RFAs, Circulations, Shop Drawing Revision āļĨāđˆāļēāļŠāļļāļ” +- āļĢāļ§āļĄāļŠāļĢāļļāļ› KPI (āđ€āļŠāđˆāļ™ "RFAs āļ—āļĩāđˆāļĢāļ­āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī", "Shop Drawing āļ—āļĩāđˆāļĢāļ­āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī") [cite: 5.3] +- āļĢāļ§āļĄāļĨāļīāļ‡āļāđŒāļ”āđˆāļ§āļ™āđ„āļ›āļĒāļąāļ‡āđ‚āļĄāļ”āļđāļĨāļ•āđˆāļēāļ‡āđ† +- **Security Metrics:** āđāļŠāļ”āļ‡āļˆāļģāļ™āļ§āļ™ files scanned, security incidents, failed login attempts + +### **10.2 āļŸāļĩāļ”āļāļīāļˆāļāļĢāļĢāļĄ (Activity Feed)** + +- āđāļŠāļ”āļ‡āļĢāļēāļĒāļāļēāļĢ v_audit_log_details āļĨāđˆāļēāļŠāļļāļ” (10 āļĢāļēāļĒāļāļēāļĢ) āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰ +- āļĢāļ§āļĄ security-related activities (failed logins, permission changes) + +```typescript +// āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ API response +[ + { + user: 'editor01', + action: 'Updated RFA (LCBP3-RFA-001)', + time: '2025-11-04T09:30Z', + }, + { + user: 'system', + action: 'Virus scan completed - 0 threats found', + time: '2025-11-04T09:25Z', + }, +]; +``` + +## ðŸ›Ąïļ **11. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ—āļĩāđˆāđ„āļĄāđˆāđƒāļŠāđˆāļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™ (Non-Functional Requirements)** + +āļŠāđˆāļ§āļ™āļ™āļĩāđ‰āļŠāļĢāļļāļ›āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” Non-Functional āļˆāļēāļ requirements.md āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļ—āļĩāļĄāļžāļąāļ’āļ™āļēāļ—āļēāļ™ + +- **Audit Log [cite: 6.1]:** āļ—āļļāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļ—āļĩāđˆāļŠāļģāļ„āļąāļ (C/U/D) āļ•āđ‰āļ­āļ‡āļ–āļđāļāļšāļąāļ™āļ—āļķāļāđƒāļ™ audit_logs +- **Performance [cite: 6.4]:** āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰ Caching āļŠāļģāļŦāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ€āļĢāļĩāļĒāļāļšāđˆāļ­āļĒ āđāļĨāļ°āđƒāļŠāđ‰ Pagination +- **Security [cite: 6.5]:** āļ•āđ‰āļ­āļ‡āļĄāļĩ Rate Limiting āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢ Secret āļœāđˆāļēāļ™ docker-compose.yml (āđ„āļĄāđˆāđƒāļŠāđˆ .env) +- **File Security [cite: 3.9.6]:** āļ•āđ‰āļ­āļ‡āļĄāļĩ virus scanning, file type validation, access controls +- **Resilience [cite: 6.5.3]:** āļ•āđ‰āļ­āļ‡āļĄāļĩ circuit breaker, retry mechanisms, graceful degradation +- **Backup & Recovery [cite: 6.6]:** āļ•āđ‰āļ­āļ‡āļĄāļĩāđāļœāļ™āļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļąāđ‰āļ‡ Database (MariaDB) āđāļĨāļ° File Storage (/share/dms-data) āļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒāļ§āļąāļ™āļĨāļ° 1 āļ„āļĢāļąāđ‰āļ‡ +- **Notification Strategy [cite: 6.7]:** āļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Email/Line) āļ•āđ‰āļ­āļ‡āļ–āļđāļ Trigger āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆāļŠāđˆāļ‡āļ–āļķāļ‡, āļĄāļĩāļāļēāļĢāļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™āđƒāļŦāļĄāđˆ (Circulation), āļŦāļĢāļ·āļ­ (āļ—āļēāļ‡āđ€āļĨāļ·āļ­āļ) āđ€āļĄāļ·āđˆāļ­āļ‡āļēāļ™āđ€āļŠāļĢāđ‡āļˆ/āđƒāļāļĨāđ‰āļ–āļķāļ‡āļāļģāļŦāļ™āļ” +- **Monitoring [cite: 6.8]:** āļ•āđ‰āļ­āļ‡āļĄāļĩ health checks, metrics collection, alerting + +## ✅ **12. āļĄāļēāļ•āļĢāļāļēāļ™āļ—āļĩāđˆāļ™āļģāđ„āļ›āđƒāļŠāđ‰āđāļĨāđ‰āļ§ (āļˆāļēāļ SQL v1.4.0) (Implemented Standards (from SQL v1.4.0))** + +āļŠāđˆāļ§āļ™āļ™āļĩāđ‰āļĒāļ·āļ™āļĒāļąāļ™āļ§āđˆāļēāđāļ™āļ§āļ—āļēāļ‡āļ›āļāļīāļšāļąāļ•āļīāļ—āļĩāđˆāļ”āļĩāļ—āļĩāđˆāļŠāļļāļ”āļ•āđˆāļ­āđ„āļ›āļ™āļĩāđ‰āđ€āļ›āđ‡āļ™āļŠāđˆāļ§āļ™āļŦāļ™āļķāđˆāļ‡āļ‚āļ­āļ‡āļāļēāļĢāļ­āļ­āļāđāļšāļšāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļĒāļđāđˆāđāļĨāđ‰āļ§ āđāļĨāļ°āļ„āļ§āļĢāļ–āļđāļāļ™āļģāđ„āļ›āđƒāļŠāđ‰āļ›āļĢāļ°āđ‚āļĒāļŠāļ™āđŒ āđ„āļĄāđˆāđƒāļŠāđˆāļŠāļĢāđ‰āļēāļ‡āļ‚āļķāđ‰āļ™āđƒāļŦāļĄāđˆ + +- ✅ **Soft Delete:** āļ™āļģāđ„āļ›āđƒāļŠāđ‰āđāļĨāđ‰āļ§āļœāđˆāļēāļ™āļ„āļ­āļĨāļąāļĄāļ™āđŒ deleted_at āđƒāļ™āļ•āļēāļĢāļēāļ‡āļŠāļģāļ„āļąāļ (āđ€āļŠāđˆāļ™ correspondences, rfas, project_parties) āļ•āļĢāļĢāļāļ°āļāļēāļĢāļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ•āđ‰āļ­āļ‡āļāļĢāļ­āļ‡ deleted_at IS NULL +- ✅ **Database Indexes:** āļŠāļ„āļĩāļĄāļēāđ„āļ”āđ‰āļĄāļĩāļāļēāļĢāļ—āļģ index āđ„āļ§āđ‰āļ­āļĒāđˆāļēāļ‡āļŦāļ™āļąāļāļŦāļ™āđˆāļ§āļ‡āļšāļ™ foreign keys āđāļĨāļ°āļ„āļ­āļĨāļąāļĄāļ™āđŒāļ—āļĩāđˆāđƒāļŠāđ‰āļ„āđ‰āļ™āļŦāļēāļšāđˆāļ­āļĒ (āđ€āļŠāđˆāļ™ idx_rr_rfa, idx_cor_project, idx_cr_is_current) āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž +- ✅ **āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ RBAC:** āļĄāļĩāļĢāļ°āļšāļš users, roles, permissions, user_roles, āđāļĨāļ° user_project_roles āļ—āļĩāđˆāļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāļ­āļĒāļđāđˆāđāļĨāđ‰āļ§ +- ✅ **Data Seeding:** āļ‚āđ‰āļ­āļĄāļđāļĨ Master (roles, permissions, organization_roles, initial users, project parties) āļ–āļđāļāļĢāļ§āļĄāļ­āļĒāļđāđˆāđƒāļ™āļŠāļ„āļĢāļīāļ›āļ•āđŒāļŠāļ„āļĩāļĄāļēāđāļĨāđ‰āļ§ +- ✅ **Application-level Locking:** āđƒāļŠāđ‰ Redis distributed lock āđāļ—āļ™ stored procedure +- ✅ **File Security:** Virus scanning, file type validation, access control +- ✅ **Resilience Patterns:** Circuit breaker, retry, fallback mechanisms +- ✅ **Security Measures:** Input validation, rate limiting, security headers +- ✅ **Monitoring:** Health checks, metrics collection, distributed tracing + +## ðŸ§Đ **13. āļāļēāļĢāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ—āļĩāđˆāđāļ™āļ°āļ™āļģ (āļŠāļģāļŦāļĢāļąāļšāļ­āļ™āļēāļ„āļ•) (Recommended Enhancements (Future))** + +- ✅ āļŠāļĢāđ‰āļēāļ‡ Background job (āđ‚āļ”āļĒāđƒāļŠāđ‰ **n8n** āđ€āļžāļ·āđˆāļ­āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļš **Line** [cite: 2.7] āđāļĨāļ°/āļŦāļĢāļ·āļ­āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ RFA āļ—āļĩāđˆāđƒāļāļĨāđ‰āļ–āļķāļ‡āļāļģāļŦāļ™āļ” due_date [cite: 6.7]) +- ✅ āđ€āļžāļīāđˆāļĄ job āļĨāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ›āđ‡āļ™āļĢāļ°āļĒāļ°āļŠāļģāļŦāļĢāļąāļš attachments āļ—āļĩāđˆāđ„āļĄāđˆāļ–āļđāļāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļāļąāļš Entity āđƒāļ”āđ† āđ€āļĨāļĒ (āđ„āļŸāļĨāđŒāļāļģāļžāļĢāđ‰āļē) +- 🔄 **AI-Powered Document Classification:** āđƒāļŠāđ‰ machine learning āļŠāļģāļŦāļĢāļąāļš automatic document categorization +- 🔄 **Advanced Analytics:** Predictive analytics āļŠāļģāļŦāļĢāļąāļš workflow optimization +- 🔄 **Mobile App:** Native mobile application āļŠāļģāļŦāļĢāļąāļš field workers +- 🔄 **Blockchain Integration:** āļŠāļģāļŦāļĢāļąāļš document integrity verification āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” + +## ✅ **14. Summary Checklist for Developers** + +āļāđˆāļ­āļ™āļŠāđˆāļ‡ PR (Pull Request) āļ™āļąāļāļžāļąāļ’āļ™āļēāļ•āđ‰āļ­āļ‡āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŦāļąāļ§āļ‚āđ‰āļ­āļ•āđˆāļ­āđ„āļ›āļ™āļĩāđ‰: + +- [ ] **Security:** āđ„āļĄāđˆāļĄāļĩ Secrets āđƒāļ™ Code, āđƒāļŠāđ‰ `docker-compose.override.yml` āđāļĨāđ‰āļ§ +- [ ] **Concurrency:** āđƒāļŠāđ‰ Optimistic Lock āđƒāļ™ Entity āļ—āļĩāđˆāđ€āļŠāļĩāđˆāļĒāļ‡ Race Condition āđāļĨāđ‰āļ§ +- [ ] **Idempotency:** API āļĢāļ­āļ‡āļĢāļąāļš Idempotency Key āđāļĨāđ‰āļ§ +- [ ] **File Upload:** āđƒāļŠāđ‰ Flow Two-Phase (Temp -> Perm) āđāļĨāđ‰āļ§ +- [ ] **Mobile:** āļŦāļ™āđ‰āļēāļˆāļ­āđāļŠāļ”āļ‡āļœāļĨāđāļšāļš Card View āļšāļ™āļĄāļ·āļ­āļ–āļ·āļ­āđ„āļ”āđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡ +- [ ] **Performance:** āļŠāļĢāđ‰āļēāļ‡ Index āļŠāļģāļŦāļĢāļąāļš JSON Virtual Columns āđāļĨāđ‰āļ§ (āļ–āđ‰āļēāļĄāļĩ) + +--- + +## 📋 **15. Summary of Key Changes from Previous Version** + +### **Security Enhancements:** + +1. **File Upload Security** - Virus scanning, file type validation, access controls +2. **Input Validation** - OWASP Top 10 protection, XSS/CSRF prevention +3. **Rate Limiting** - Comprehensive rate limiting strategy +4. **Secrets Management** - Secure handling of sensitive configuration + +### **Architecture Improvements:** + +1. **Document Numbering** - Changed from Stored Procedure to Application-level Locking +2. **Resilience Patterns** - Circuit breaker, retry mechanisms, fallback strategies +3. **Monitoring & Observability** - Health checks, metrics, distributed tracing +4. **Caching Strategy** - Comprehensive caching with proper invalidation + +### **Performance Targets :** + +1. **API Response Time** - < 200ms (90th percentile) +2. **Search Performance** - < 500ms +3. **File Upload** - < 30 seconds for 50MB files +4. **Cache Hit Ratio** - > 80% + +### **Operational Excellence:** + +1. **Disaster Recovery** - RTO < 4 hours, RPO < 1 hour +2. **Backup Procedures** - Comprehensive backup and restoration +3. **Security Testing** - Penetration testing and security audits +4. **Performance Testing** - Load testing with realistic workloads + +āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļŠāļ°āļ—āđ‰āļ­āļ™āļ–āļķāļ‡āļ„āļ§āļēāļĄāļĄāļļāđˆāļ‡āļĄāļąāđˆāļ™āđƒāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āļĢāļ°āļšāļšāļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ, āļĄāļĩāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™, āđāļĨāļ°āļĄāļĩāļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāļŠāļđāļ‡ āļžāļĢāđ‰āļ­āļĄāļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ•āļīāļšāđ‚āļ•āđƒāļ™āļ­āļ™āļēāļ„āļ•āđāļĨāļ°āļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđ„āļ› + +**āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ:** āđāļ™āļ§āļ—āļēāļ‡āļ™āļĩāđ‰āļˆāļ°āļ–āļđāļāļ—āļšāļ—āļ§āļ™āđāļĨāļ°āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđ€āļ›āđ‡āļ™āļĢāļ°āļĒāļ°āļ•āļēāļĄ feedback āļˆāļēāļāļ—āļĩāļĄāļžāļąāļ’āļ™āļēāđāļĨāļ°āļ„āļ§āļēāļĄāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđ„āļ› + +## **Document Control:** + +- **Document:** FullStackJS v1.4.5 +- **Version:** 1.4 +- **Date:** 2025-11-29 +- **Author:** NAP LCBP3-DMS & Gemini +- **Status:** FINAL-Rev.05 +- **Classification:** Internal Technical Documentation +- **Approved By:** Nattanin + +--- + +`End of FullStackJS Guidelines v1.4.5` diff --git a/2_2_phase_6a_workflow_dsl.sql b/2_2_phase_6a_workflow_dsl.sql deleted file mode 100644 index 6f8be90..0000000 --- a/2_2_phase_6a_workflow_dsl.sql +++ /dev/null @@ -1,16 +0,0 @@ --- File: database/migrations/phase_6a_workflow_dsl.sql --- āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄāļ‚āļ­āļ‡ Workflow (Workflow Definition) -CREATE TABLE workflow_definitions ( - id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID āļ‚āļ­āļ‡ Workflow Definition', - workflow_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ RFA, CORR, CIRCULATION', - version INT NOT NULL DEFAULT 1 COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version', - dsl JSON NOT NULL COMMENT 'āļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļš (YAML/JSON Format)', - compiled JSON NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Execution Tree āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§', - is_active BOOLEAN DEFAULT TRUE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', - -- āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļĄāļĩ Workflow Code āđāļĨāļ° Version āļ‹āđ‰āļģāļāļąāļ™ - UNIQUE KEY uq_workflow_version (workflow_code, version) -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄāļāļŽāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢ (Workflow DSL)'; --- āļŠāļĢāđ‰āļēāļ‡ Index āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļē Workflow āļ—āļĩāđˆ Active āļĨāđˆāļēāļŠāļļāļ”āđ„āļ”āđ‰āđ€āļĢāđ‡āļ§āļ‚āļķāđ‰āļ™ -CREATE INDEX idx_workflow_active ON workflow_definitions(workflow_code, is_active, version); \ No newline at end of file diff --git a/2_Backend_Plan_V1_4_5.md b/2_Backend_Plan_V1_4_5.md new file mode 100644 index 0000000..a029e00 --- /dev/null +++ b/2_Backend_Plan_V1_4_5.md @@ -0,0 +1,2137 @@ +# 📋 **āđāļœāļ™āļāļēāļĢāļžāļąāļ’āļ™āļē Backend (NestJS) - LCBP3-DMS v1.4.5 (āļ‰āļšāļąāļšāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** + +**āļŠāļ–āļēāļ™āļ°:** FINAL GUIDELINE Rev.05 +**āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-29 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡:** Requirements v1.4.5 & FullStackJS Guidelines v1.4.5 +**Classification:** Internal Technical Documentation + +--- + +## ðŸŽŊ **āļ āļēāļžāļĢāļ§āļĄāđ‚āļ„āļĢāļ‡āļāļēāļĢ** + +āļžāļąāļ’āļ™āļē Backend āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļšāļšāļĢāļīāļŦāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Document Management System) āļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡ āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļžāļĢāđ‰āļ­āļĄāļāļąāļ™ (Concurrency) āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļ–āļđāļāļ•āđ‰āļ­āļ‡āđāļĄāđˆāļ™āļĒāļģ āļĄāļĩāļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļ—āļĩāđˆāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™āļ•āđˆāļ­āļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§ āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™ āļĄāļĩāļĢāļ°āļšāļš Workflow āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī āđāļĨāļ°āļāļēāļĢāļ„āļ§āļšāļ„āļļāļĄāļŠāļīāļ—āļ˜āļīāđŒāđāļšāļš RBAC 4 āļĢāļ°āļ”āļąāļš āļžāļĢāđ‰āļ­āļĄāļĄāļēāļ•āļĢāļāļēāļĢāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļ—āļĩāđˆāļ—āļąāļ™āļŠāļĄāļąāļĒ + +--- + +## 📐 **āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļĢāļ°āļšāļš** + +### **Technology Stack (Updated)** + +- **Framework:** NestJS (TypeScript, ESM) +- **Database:** MariaDB 10.11 (āđƒāļŠāđ‰ Virtual Columns) +- **ORM:** TypeORM (āđƒāļŠāđ‰ Optimistic Locking) +- **Authentication:** JWT + Passport +- **Authorization:** CASL (RBAC 4-level) +- **File Upload:** Multer + Virus Scanning (ClamAV) + Two-Phase Storage +- **Search:** Elasticsearch +- **Notification:** Nodemailer + n8n (Line Integration) + BullMQ Queue +- **Caching/Locking:** Redis (Redlock) āļŠāļģāļŦāļĢāļąāļš Distributed Locking +- **Queue:** BullMQ (Redis) āļŠāļģāļŦāļĢāļąāļš Notification Batching āđāļĨāļ° Async Jobs +- **Resilience:** Circuit Breaker, Retry Patterns +- **Security:** Helmet, CSRF Protection, Rate Limiting, Idempotency +- **Monitoring:** Winston, Health Checks, Metrics +- **Scheduling:** @nestjs/schedule (Cron Jobs) +- **Documentation:** Swagger +- **Validation:** Zod / Class-validator + +### **āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨ (Domain-Driven)** + +``` +📁backend +├── .editorconfig +├── .env +├── .gitignore +├── .prettierrc +├── docker-compose.override.yml.example +├── docker-compose.yml +├── eslint.config.mjs +├── Infrastructure Setup.yml +├── nest-cli.json +├── package-lock.json +├── package.json +├── pnpm-lock.yaml +├── README.md +├── tsconfig.build.json +├── tsconfig.json +├── 📁scripts +│ ├── debug-db.ts +│ └── verify-workflow.ts +├── 📁test +│ ├── app.e2e-spec.ts +│ ├── jest-e2e.json +│ ├── phase3-workflow.e2e-spec.ts +│ └── simple.e2e-spec.ts +├── 📁uploads +│ └── 📁temp +│ ├── 5a6d4c26-84b2-4c8a-b177-9fa267651a93.pdf +│ └── d60d9807-a22d-4ca0-b99a-5d5d8b81b3e8.pdf +└── 📁src + ├── app.controller.spec.ts + ├── app.controller.ts + ├── app.module.ts + ├── app.service.ts + ├── main.ts + ├── redlock.d.ts + ├── 📁common + │ ├── 📁auth + │ │ ├── 📁dto + │ │ │ ├── login.dto.ts + │ │ │ └── register.dto.ts + │ │ ├── 📁strategies + │ │ │ ├── jwt-refresh.strategy.ts + │ │ │ └── jwt.strategy.ts + │ │ ├── auth.controller.spec.ts + │ │ ├── auth.controller.ts + │ │ ├── auth.module.ts + │ │ ├── auth.service.spec.ts + │ │ └── auth.service.ts + │ ├── 📁config + │ │ ├── env.validation.ts + │ │ └── redis.config.ts + │ ├── 📁decorators + │ │ ├── audit.decorator.ts + │ │ ├── bypass-maintenance.decorator.ts + │ │ ├── circuit-breaker.decorator.ts + │ │ ├── current-user.decorator.ts + │ │ ├── idempotency.decorator.ts + │ │ ├── require-permission.decorator.ts + │ │ └── retry.decorator.ts + │ ├── 📁entities + │ │ ├── audit-log.entity.ts + │ │ └── base.entity.ts + │ ├── 📁exceptions + │ │ └── http-exception.filter.ts + │ ├── 📁file-storage + │ │ ├── 📁entities + │ │ │ └── attachment.entity.ts + │ │ ├── file-cleanup.service.ts + │ │ ├── file-storage.controller.spec.ts + │ │ ├── file-storage.controller.ts + │ │ ├── file-storage.module.ts + │ │ ├── file-storage.service.spec.ts + │ │ └── file-storage.service.ts + │ ├── 📁guards + │ │ ├── jwt-auth.guard.ts + │ │ ├── jwt-refresh.guard.ts + │ │ ├── maintenance-mode.guard.ts + │ │ └── rbac.guard.ts + │ ├── 📁idempotency + │ │ ├── 📁interceptors + │ │ ├── audit-log.interceptor.ts + │ │ ├── idempotency.interceptor.ts + │ │ ├── performance.interceptor.ts + │ │ └── transform.interceptor.ts + │ ├── 📁maintenance + │ ├── 📁resilience + │ │ └── resilience.module.ts + │ ├── 📁security + │ ├── 📁services + │ │ ├── crypto.service.ts + │ │ └── request-context.service.ts + │ └── common.module.ts + ├── 📁database + │ ├── 📁migrations + │ └── 📁seeds + │ ├── run-seed.ts + │ └── workflow-definitions.seed.ts + └── 📁modules + ├── 📁circulation + │ ├── 📁dto + │ │ ├── create-circulation.dto.ts + │ │ ├── search-circulation.dto.ts + │ │ └── update-circulation-routing.dto.ts + │ ├── 📁entities + │ │ ├── circulation-routing.entity.ts + │ │ ├── circulation-status-code.entity.ts + │ │ └── circulation.entity.ts + │ ├── circulation-workflow.service.ts + │ ├── circulation.controller.ts + │ ├── circulation.module.ts + │ └── circulation.service.ts + ├── 📁correspondence + │ ├── 📁dto + │ │ ├── add-reference.dto.ts + │ │ ├── create-correspondence.dto.ts + │ │ ├── create-routing-template.dto.ts + │ │ ├── search-correspondence.dto.ts + │ │ ├── submit-correspondence.dto.ts + │ │ └── workflow-action.dto.ts + │ ├── 📁entities + │ │ ├── correspondence-reference.entity.ts + │ │ ├── correspondence-revision.entity.ts + │ │ ├── correspondence-routing.entity.ts + │ │ ├── correspondence-status.entity.ts + │ │ ├── correspondence-sub-type.entity.ts + │ │ ├── correspondence-type.entity.ts + │ │ ├── correspondence.entity.ts + │ │ ├── routing-template-step.entity.ts + │ │ └── routing-template.entity.ts + │ ├── correspondence-workflow.service.ts + │ ├── correspondence.controller.spec.ts + │ ├── correspondence.controller.ts + │ ├── correspondence.module.ts + │ ├── correspondence.service.spec.ts + │ └── correspondence.service.ts + ├── 📁document-numbering + │ ├── 📁entities + │ │ ├── document-number-counter.entity.ts + │ │ └── document-number-format.entity.ts + │ ├── 📁interfaces + │ │ └── document-numbering.interface.ts + │ ├── document-numbering.module.ts + │ ├── document-numbering.service.spec.ts + │ └── document-numbering.service.ts + ├── 📁drawing + │ ├── 📁dto + │ │ ├── create-contract-drawing.dto.ts + │ │ ├── create-shop-drawing-revision.dto.ts + │ │ ├── create-shop-drawing.dto.ts + │ │ ├── search-contract-drawing.dto.ts + │ │ ├── search-shop-drawing.dto.ts + │ │ └── update-contract-drawing.dto.ts + │ ├── 📁entities + │ │ ├── contract-drawing-sub-category.entity.ts + │ │ ├── contract-drawing-volume.entity.ts + │ │ ├── contract-drawing.entity.ts + │ │ ├── shop-drawing-main-category.entity.ts + │ │ ├── shop-drawing-revision.entity.ts + │ │ ├── shop-drawing-sub-category.entity.ts + │ │ └── shop-drawing.entity.ts + │ ├── contract-drawing.controller.ts + │ ├── contract-drawing.service.ts + │ ├── drawing-master-data.controller.ts + │ ├── drawing-master-data.service.ts + │ ├── drawing.module.ts + │ ├── shop-drawing.controller.ts + │ └── shop-drawing.service.ts + ├── 📁json-schema + │ ├── 📁dto + │ │ ├── create-json-schema.dto.ts + │ │ ├── migrate-data.dto.ts + │ │ ├── search-json-schema.dto.ts + │ │ └── update-json-schema.dto.ts + │ ├── 📁entities + │ │ └── json-schema.entity.ts + │ ├── 📁interfaces + │ │ ├── ui-schema.interface.ts + │ │ └── validation-result.interface.ts + │ ├── 📁services + │ │ ├── json-security.service.ts + │ │ ├── schema-migration.service.ts + │ │ ├── ui-schema.service.ts + │ │ └── virtual-column.service.ts + │ ├── json-schema.controller.spec.ts + │ ├── json-schema.controller.ts + │ ├── json-schema.module.ts + │ ├── json-schema.service.spec.ts + │ └── json-schema.service.ts + ├── 📁master + │ ├── 📁dto + │ │ ├── create-discipline.dto.ts + │ │ ├── create-sub-type.dto.ts + │ │ ├── create-tag.dto.ts + │ │ ├── save-number-format.dto.ts + │ │ ├── search-tag.dto.ts + │ │ └── update-tag.dto.ts + │ ├── 📁entities + │ │ ├── discipline.entity.ts + │ │ └── tag.entity.ts + │ ├── master.controller.ts + │ ├── master.module.ts + │ └── master.service.ts + ├── 📁monitoring + │ ├── 📁controllers + │ │ └── health.controller.ts + │ ├── 📁dto + │ │ └── set-maintenance.dto.ts + │ ├── 📁logger + │ │ └── winston.config.ts + │ ├── 📁services + │ │ └── metrics.service.ts + │ ├── monitoring.controller.ts + │ ├── monitoring.module.ts + │ └── monitoring.service.ts + ├── 📁notification + │ ├── 📁dto + │ │ ├── create-notification.dto.ts + │ │ └── search-notification.dto.ts + │ ├── 📁entities + │ │ └── notification.entity.ts + │ ├── notification-cleanup.service.ts + │ ├── notification.controller.ts + │ ├── notification.gateway.ts + │ ├── notification.module.ts + │ ├── notification.processor.ts + │ └── notification.service.ts + ├── 📁project + │ ├── 📁dto + │ │ ├── create-project.dto.ts + │ │ ├── search-project.dto.ts + │ │ └── update-project.dto.ts + │ ├── 📁entities + │ │ ├── contract-organization.entity.ts + │ │ ├── contract.entity.ts + │ │ ├── organization.entity.ts + │ │ ├── project-organization.entity.ts + │ │ └── project.entity.ts + │ ├── project.controller.spec.ts + │ ├── project.controller.ts + │ ├── project.module.ts + │ ├── project.service.spec.ts + │ └── project.service.ts + ├── 📁rfa + │ ├── 📁dto + │ │ ├── create-rfa-revision.dto.ts + │ │ ├── create-rfa-workflow.dto.ts + │ │ ├── create-rfa.dto.ts + │ │ ├── search-rfa.dto.ts + │ │ ├── submit-rfa.dto.ts + │ │ └── update-rfa.dto.ts + │ ├── 📁entities + │ │ ├── rfa-approve-code.entity.ts + │ │ ├── rfa-item.entity.ts + │ │ ├── rfa-revision.entity.ts + │ │ ├── rfa-status-code.entity.ts + │ │ ├── rfa-type.entity.ts + │ │ ├── rfa-workflow-template-step.entity.ts + │ │ ├── rfa-workflow-template.entity.ts + │ │ ├── rfa-workflow.entity.ts + │ │ └── rfa.entity.ts + │ ├── rfa-workflow.service.ts + │ ├── rfa.controller.ts + │ ├── rfa.module.ts + │ └── rfa.service.ts + ├── 📁search + │ ├── 📁dto + │ │ └── search-query.dto.ts + │ ├── search.controller.ts + │ ├── search.module.ts + │ └── search.service.ts + ├── 📁transmittal + │ ├── 📁dto + │ │ ├── create-transmittal.dto.ts + │ │ ├── search-transmittal.dto.ts + │ │ └── update-transmittal.dto.ts + │ ├── 📁entities + │ │ ├── transmittal-item.entity.ts + │ │ └── transmittal.entity.ts + │ ├── transmittal.controller.ts + │ ├── transmittal.module.ts + │ └── transmittal.service.ts + ├── 📁user + │ ├── 📁dto + │ │ ├── assign-role.dto.ts + │ │ ├── create-user.dto.ts + │ │ ├── update-preference.dto.ts + │ │ └── update-user.dto.ts + │ ├── 📁entities + │ │ ├── permission.entity.ts + │ │ ├── role.entity.ts + │ │ ├── user-assignment.entity.ts + │ │ ├── user-preference.entity.ts + │ │ └── user.entity.ts + │ ├── user-assignment.service.ts + │ ├── user-preference.service.ts + │ ├── user.controller.ts + │ ├── user.module.ts + │ ├── user.service.spec.ts + │ └── user.service.ts + └── 📁workflow-engine + ├── 📁dto + │ ├── create-workflow-definition.dto.ts + │ ├── evaluate-workflow.dto.ts + │ ├── get-available-actions.dto.ts + │ ├── update-workflow-definition.dto.ts + │ └── workflow-transition.dto.ts + ├── 📁entities + │ └── workflow-definition.entity.ts + ├── 📁interfaces + │ ├── workflow-definition.entity.ts + │ ├── workflow-history.entity.ts + │ └── workflow-instance.entity.ts + ├── 📁interfaces + │ └── workflow.interface.ts + ├── workflow-dsl.service.ts + ├── workflow-engine.controller.ts + ├── workflow-engine.module.ts + ├── workflow-engine.service.spec.ts + ├── workflow-engine.service.ts + └── workflow-event.service.ts + +``` + +--- + +## 🗓ïļ **āđāļœāļ™āļāļēāļĢāļžāļąāļ’āļ™āļēāđāļšāļš Phase-Based** + +- _(Dependency Diagram āļ–āļđāļāļĨāļ°āđ„āļ§āđ‰āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŦāļĒāļąāļ”āļžāļ·āđ‰āļ™āļ—āļĩāđˆ āđ€āļ™āļ·āđˆāļ­āļ‡āļˆāļēāļāļĄāļĩāļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļˆāļēāļāđāļœāļ™āđ€āļ”āļīāļĄ)_ + +## **Phase 0: Infrastructure & Configuration (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 1)** + +**Milestone:** āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļžāļ·āđ‰āļ™āļāļēāļ™āļžāļĢāđ‰āļ­āļĄ āļĢāļ­āļ‡āļĢāļąāļš Secrets āļ—āļĩāđˆāļ›āļĨāļ­āļ”āļ āļąāļĒ āđāļĨāļ° Redis āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™ + +### **Phase 0: Tasks** + +- **[ ] T0.1 Secure Configuration Setup** + + - [ ] āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡ `ConfigModule` āđƒāļŦāđ‰āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ­āđˆāļēāļ™āļ„āđˆāļēāļˆāļēāļ Environment Variables + - [ ] āļŠāļĢāđ‰āļēāļ‡ Template `docker-compose.override.yml.example` āļŠāļģāļŦāļĢāļąāļš Dev + - [ ] Validate Config āļ”āđ‰āļ§āļĒ Joi/Zod āļ•āļ­āļ™ Start App (Throw error āļ–āđ‰āļēāļ‚āļēāļ” Secrets) + - [ ] **Security:** Setup network segmentation āđāļĨāļ° firewall rules + - [ ] **Deliverable:** Configuration Management āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™āļ­āļĒāđˆāļēāļ‡āļ›āļĨāļ­āļ”āļ āļąāļĒ + - [ ] **Dependencies:** None (Task āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™) + +- **[ ] T0.2 Redis & Queue Infrastructure** + + - [ ] Setup Redis Container + - [ ] Setup BullMQ Module āđƒāļ™ NestJS āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ Background Jobs + - [ ] Setup Redis Client āļŠāļģāļŦāļĢāļąāļš Distributed Lock (Redlock) + - [ ] **Security:** Setup Redis authentication āđāļĨāļ° encryption + - [ ] **Deliverable:** Redis āđāļĨāļ° Queue System āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™ + - [ ] **Dependencies:** T0.1 + +- **[ ] T0.3 Setup Database Connection** + + - [ ] Import SQL Schema v1.4.2 āđ€āļ‚āđ‰āļē MariaDB + - [ ] Run Seed Data (organizations, users, roles, permissions) + - [ ] Configure TypeORM āđƒāļ™ AppModule + - [ ] **Security:** Setup database connection encryption + - [ ] āļ—āļ”āļŠāļ­āļš Connection + - [ ] **Deliverable:** Database āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™, āļĄāļĩ Seed Data + - [ ] **Dependencies:** T0.1 + +- **[ ] T0.4 Setup Git Repository** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Repository āđƒāļ™ Gitea (git.np-dms.work) + - [ ] Setup .gitignore, README.md, SECURITY.md + - [ ] Commit Initial Project + - [ ] **Deliverable:** Code āļ­āļĒāļđāđˆāđƒāļ™ Version Control + - [ ] **Dependencies:** T0.1, T0.2, T0.3 + +--- + +## **Phase 1: Core Foundation & Security (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 2-3)** + +**Milestone:** āļĢāļ°āļšāļš Authentication, Authorization, Idempotency āļžāļ·āđ‰āļ™āļāļēāļ™ āđāļĨāļ° Security Baseline + +### **Phase 1: Tasks** + +- **[ ] T1.1 CommonModule - Base Infrastructure** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Base Entity (id, created_at, updated_at, deleted_at) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Global Exception Filter (āđ„āļĄāđˆāđ€āļ›āļīāļ”āđ€āļœāļĒ sensitive information) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Response Transform Interceptor + - [ ] āļŠāļĢāđ‰āļēāļ‡ Audit Log Interceptor + - [ ] **Idempotency Interceptor:** āļ•āļĢāļ§āļˆāļŠāļ­āļš Header `Idempotency-Key` āđāļĨāļ° Cache Response āđ€āļ”āļīāļĄāđƒāļ™ Redis + - [ ] **Maintenance Mode Middleware:** āļ•āļĢāļ§āļˆāļŠāļ­āļš Flag āđƒāļ™ **Redis Key** āđ€āļžāļ·āđˆāļ­ Block API āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļĢāļ°āļšāļš **(Admin āđƒāļŠāđ‰ Redis/Admin UI āđƒāļ™āļāļēāļĢ Toggle āļŠāļ–āļēāļ™āļ°)** + - [ ] āļŠāļĢāđ‰āļēāļ‡ RequestContextService - āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļĢāļ°āļŦāļ§āđˆāļēāļ‡ Request + - [ ] āļŠāļĢāđ‰āļēāļ‡ ConfigService - Centralized configuration management + - [ ] āļŠāļĢāđ‰āļēāļ‡ CryptoService - āļŠāļģāļŦāļĢāļąāļš encryption/decryption + - [ ] **Security:** Implement input validation pipeline + - [ ] **Deliverable:** Common Services āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰ āļĢāļ§āļĄāļ–āļķāļ‡ Idempotency āđāļĨāļ° Maintenance Mode + - [ ] **Dependencies:** T0.2, T0.3 + +- **[ ] T1.2 AuthModule - JWT Authentication** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entity: User + - [ ] āļŠāļĢāđ‰āļēāļ‡ AuthService: + - [ ] login(username, password) → JWT Token + - [ ] validateUser(username, password) → User | null + - [ ] Password Hashing (bcrypt) + salt + - [ ] āļŠāļĢāđ‰āļēāļ‡ JWT Strategy (Passport) + - [ ] āļŠāļĢāđ‰āļēāļ‡ JwtAuthGuard + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers: + - [ ] POST /auth/login → { access_token, refresh_token } + - [ ] POST /auth/register → Create User (Admin only) + - [ ] POST /auth/refresh → Refresh token + - [ ] POST /auth/logout → Revoke token + - [ ] GET /auth/profile (Protected) + - [ ] **Security:** Implement rate limiting āļŠāļģāļŦāļĢāļąāļš authentication endpoints + - [ ] **Deliverable:** āļĨāđ‡āļ­āļāļ­āļīāļ™/āļĨāđ‡āļ­āļāđ€āļ­āļēāļ•āđŒāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļ›āļĨāļ­āļ”āļ āļąāļĒ + - [ ] **Dependencies:** T1.1, T0.3 + +- **[ ] T1.3 UserModule - User Management** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities: User, Role, Permission, UserRole, UserAssignment, **UserPreference** + - [ ] āļŠāļĢāđ‰āļēāļ‡ UserService CRUD (āļžāļĢāđ‰āļ­āļĄ soft delete) + - [ ] āļŠāļĢāđ‰āļēāļ‡ RoleService CRUD + - [ ] āļŠāļĢāđ‰āļēāļ‡ PermissionService (Read-Only, āļˆāļēāļ Seed) + - [ ] āļŠāļĢāđ‰āļēāļ‡ UserAssignmentService - āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ user assignments āļ•āļēāļĄ scope + - [ ] āļŠāļĢāđ‰āļēāļ‡ **UserPreferenceService** - āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļē Notification āđāļĨāļ° UI + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers: + - [ ] GET /users → List Users (Paginated) + - [ ] GET /users/:id → User Detail + - [ ] POST /users → Create User (āļ•āđ‰āļ­āļ‡āļšāļąāļ‡āļ„āļąāļšāđ€āļ›āļĨāļĩāđˆāļĒāļ™ password āļ„āļĢāļąāđ‰āļ‡āđāļĢāļ) + - [ ] PUT /users/:id → Update User + - [ ] DELETE /users/:id → Soft Delete + - [ ] GET /roles → List Roles + - [ ] POST /roles → Create Role (Admin) + - [ ] PUT /roles/:id/permissions → Assign Permissions + - [ ] **Security:** Implement permission checks āļŠāļģāļŦāļĢāļąāļš user management + - [ ] **Deliverable:** āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰, Role, āđāļĨāļ° Preferences āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1, T1.2 + +- **[ ] T1.4 RBAC Guard - 4-Level Authorization** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ @RequirePermission() Decorator + - [ ] āļŠāļĢāđ‰āļēāļ‡ RbacGuard āļ—āļĩāđˆāļ•āļĢāļ§āļˆāļŠāļ­āļš 4 āļĢāļ°āļ”āļąāļš: + - [ ] Global Permissions + - [ ] Organization Permissions + - [ ] Project Permissions + - [ ] Contract Permissions + - [ ] Permission Hierarchy Logic + - [ ] Integration āļāļąāļš CASL + - [ ] **Security:** Implement audit logging āļŠāļģāļŦāļĢāļąāļš permission checks + - [ ] **Deliverable:** āļĢāļ°āļšāļšāļŠāļīāļ—āļ˜āļīāđŒāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļ—āļąāđ‰āļ‡ 4 āļĢāļ°āļ”āļąāļš + - [ ] **Dependencies:** T1.1, T1.3 + +- **[ ] T1.5 ProjectModule - Base Structures** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities: + - [ ] Organization + - [ ] Project + - [ ] Contract + - [ ] ProjectOrganization (Junction) + - [ ] ContractOrganization (Junction) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Services & Controllers: + - [ ] GET /organizations → List + - [ ] POST /projects → Create (Superadmin) + - [ ] GET /projects/:id/contracts → List Contracts + - [ ] POST /projects/:id/contracts → Create Contract + - [ ] **Security:** Implement data isolation āļĢāļ°āļŦāļ§āđˆāļēāļ‡ organizations + - [ ] **Deliverable:** āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒāđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1, T1.2, T0.3 + +--- + +## **Phase 2: High-Integrity Data & File Management (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 4)** + +**Milestone:** Master Data, āļĢāļ°āļšāļšāļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāđāļšāļš Transactional, Document Numbering āļ—āļĩāđˆāđ„āļĄāđˆāļĄāļĩ Race Condition, JSON details system āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™ + +### **Phase 2: Tasks** + +- **[ ] T2.1 Virtual Columns for JSON** + + - [ ] āļ­āļ­āļāđāļšāļš Migration Script āļŠāļģāļŦāļĢāļąāļšāļ•āļēāļĢāļēāļ‡āļ—āļĩāđˆāļĄāļĩ JSON Details + - [ ] āđ€āļžāļīāđˆāļĄ **Generated Columns (Virtual)** āļŠāļģāļŦāļĢāļąāļšāļŸāļīāļĨāļ”āđŒāļ—āļĩāđˆāđƒāļŠāđ‰ Search āļšāđˆāļ­āļĒāđ† (āđ€āļŠāđˆāļ™ `project_id`, `type`) āļžāļĢāđ‰āļ­āļĄ Index + - [ ] **Security:** Implement admin-only access āļŠāļģāļŦāļĢāļąāļš master data + - [ ] **Deliverable:** JSON Data Search Performance āļ”āļĩāļ‚āļķāđ‰āļ™ + - [ ] **Dependencies:** T0.3, T1.1, T1.5 + +- **[ ] T2.2 FileStorageService - Two-Phase Storage** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Attachment Entity + - [ ] āļŠāļĢāđ‰āļēāļ‡ FileStorageService: + - [ ] **Phase 1 (Upload):** API āļĢāļąāļšāđ„āļŸāļĨāđŒ → Scan Virus → Save āļĨāļ‡ `temp/` → Return `temp_id` + - [ ] **Phase 2 (Commit):** Method `commitFiles(tempIds[])` → āļĒāđ‰āļēāļĒāļˆāļēāļ `temp/` āđ„āļ› `permanent/{YYYY}/{MM}/` → Update DB + - [ ] File type validation (white-list: PDF, DWG, DOCX, XLSX, PPTX, ZIP) + - [ ] File size check (max 50MB) + - [ ] Generate checksum (SHA-256) + - [ ] **Cleanup Job:** āļŠāļĢāđ‰āļēāļ‡ Cron Job āļĨāļšāđ„āļŸāļĨāđŒāđƒāļ™ `temp/` āļ—āļĩāđˆāļ„āđ‰āļēāļ‡āđ€āļāļīāļ™ 24 āļŠāļĄ. **āđ‚āļ”āļĒāļ•āļĢāļ§āļˆāļŠāļ­āļšāļˆāļēāļāļ„āļ­āļĨāļąāļĄāļ™āđŒ `expires_at` āđƒāļ™āļ•āļēāļĢāļēāļ‡ `attachments`** + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controller: + - [ ] POST /files/upload → { temp_id } (Protected) + - [ ] POST /files/commit → { attachment_id, url } (Protected) + - [ ] GET /files/:id/download → File Stream (Protected + Expiration) + - [ ] **Security:** Access Control - āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒāļœāđˆāļēāļ™ Junction Table + - [ ] **Deliverable:** āļ­āļąāļ›āđ‚āļŦāļĨāļ”/āļ”āļēāļ§āļ™āđŒāđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒāđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļ›āļĨāļ­āļ”āļ āļąāļĒ āđāļšāļš Transactional + - [ ] **Dependencies:** T1.1, T1.4 + +- **[ ] T2.3 DocumentNumberingModule - Double-Lock Mechanism** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities: + - [ ] DocumentNumberFormat + - [ ] DocumentNumberCounter + - [ ] āļŠāļĢāđ‰āļēāļ‡ DocumentNumberingService: + - [ ] generateNextNumber(projectId, orgId, typeId, year) → string + - [ ] āđƒāļŠāđ‰ **Double-Lock Mechanism**: 1. Acquire **Redis Lock** (Key: `doc_num:{project}:{type}`) 2. Read DB & Calculate Next Number 3. Update DB with **Optimistic Lock** Check (āđƒāļŠāđ‰ `@VersionColumn()`) 4. Release Redis Lock 5. Retry on Failure āļ”āđ‰āļ§āļĒ exponential backoff + - [ ] Fallback mechanism āđ€āļĄāļ·āđˆāļ­āļāļēāļĢāļ‚āļ­āđ€āļĨāļ‚āļĨāđ‰āļĄāđ€āļŦāļĨāļ§ + - [ ] Format āļ•āļēāļĄ Template: {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4} + - **āđ„āļĄāđˆāļĄāļĩ Controller** (Internal Service āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™) + - [ ] **Security:** Implement audit log āļ—āļļāļāļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĄāļĩāļāļēāļĢ generate āđ€āļĨāļ‚āļ—āļĩāđˆ + - [ ] **Deliverable:** Service āļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāđ„āļ”āđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡āđāļĨāļ°āļ›āļĨāļ­āļ”āļ āļąāļĒ āđ„āļĄāđˆāļĄāļĩ Race Condition + - [ ] **Dependencies:** T1.1, T0.3 + +* **[ ] T2.3 DocumentNumberingModule - Token-Based & Double-Lock** (Updated) + - [ ] Update Entity: `DocumentNumberCounter` (Add `discipline_id` to PK) + - [ ] Implement Token Parser & Replacer Logic (`{DISCIPLINE}`, `{SUBTYPE_NUM}`) + - [ ] Update `generateNextNumber` to handle optional keys (Discipline/SubType) + - [ ] **Deliverable:** Flexible Numbering System + +- **[ ] T2.4 SecurityModule - Enhanced Security** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Input Validation Service: + - [ ] XSS Prevention + - [ ] SQL Injection Prevention + - [ ] CSRF Protection + - [ ] āļŠāļĢāđ‰āļēāļ‡ RateLimitGuard: + - [ ] Implement rate limiting āļ•āļēāļĄ strategy (anonymous: 100/hr, authenticated: 500-5000/hr) + - [ ] Different limits āļŠāļģāļŦāļĢāļąāļš endpoints āļ•āđˆāļēāļ‡āđ† + - [ ] āļŠāļĢāđ‰āļēāļ‡ Security Headers Middleware + - [ ] **Security:** Implement content security policy (CSP) + - [ ] **Deliverable:** Security layers āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1 + +- **[ ] T2.5 JSON Details & Schema Management** + + - [ ] T2.5.1 JsonSchemaModule - Schema Management: āļŠāļĢāđ‰āļēāļ‡ Service āļŠāļģāļŦāļĢāļąāļš Validate, get, register JSON schemas + - [ ] T2.5.2 DetailsService - Data Processing: āļŠāļĢāđ‰āļēāļ‡ Service āļŠāļģāļŦāļĢāļąāļš sanitize, transform, compress/decompress JSON + - [ ] T2.5.3 JSON Security & Validation: Implement security checks āđāļĨāļ° validation rules + - [ ] **Deliverable:** JSON schema system āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1 + +### 🚀 **T2.5 JSON Details & Schema Management - Enhanced Implementation Plan** + +#### 📋 **Overview** + +āļŠāļĢāđ‰āļēāļ‡āļĢāļ°āļšāļšāļˆāļąāļ”āļāļēāļĢ JSON Schema āļ—āļĩāđˆāļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāļŠāļģāļŦāļĢāļąāļš dynamic document details, validation, transformation āđāļĨāļ° performance optimization + +--- + +#### ðŸŽŊ **Enhanced Task Breakdown for T2.5** + +##### **[ ] T2.5.1 JSON Schema Registry & Versioning System** + +- [ ] **Schema Entity Design** āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļš JSON schemas āļ—āļļāļāļ›āļĢāļ°āđ€āļ āļ— +- [ ] **Version Control System** āļŠāļģāļŦāļĢāļąāļš schema evolution +- [ ] **Migration Strategy** āļŠāļģāļŦāļĢāļąāļš backward-compatible changes +- [ ] **Schema Inheritance** āļŠāļģāļŦāļĢāļąāļš shared field definitions + +```typescript +// Schema Entity Design +@Entity() +export class JsonSchema { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + name: string; // 'CORRESPONDENCE_GENERIC', 'RFA_DWG', 'CIRCULATION_INTERNAL' + + @Column() + entity_type: string; // 'correspondence', 'rfa', 'circulation' + + @Column() + version: number; + + @Column('json') + schema_definition: any; // AJV JSON Schema + + @Column('json') + ui_schema: any; // UI configuration for form generation + + @Column({ default: true }) + is_active: boolean; + + @Column('json', { nullable: true }) + migration_script: any; // Data transformation rules + + @CreateDateColumn() + created_at: Date; + + @UpdateDateColumn() + updated_at: Date; + + // Virtual columns configuration for performance + @Column('json', { nullable: true }) + virtual_columns: VirtualColumnConfig[]; +} + +interface VirtualColumnConfig { + json_path: string; // '$.projectId' + column_name: string; // 'ref_project_id' + data_type: 'INT' | 'VARCHAR' | 'BOOLEAN' | 'DATE'; + index_type?: 'INDEX' | 'UNIQUE' | 'FULLTEXT'; + is_required: boolean; +} +``` + +##### **[ ] T2.5.2 Schema Validation & Transformation Engine** + +- [ ] **AJV Integration** āļŠāļģāļŦāļĢāļąāļš high-performance JSON validation +- [ ] **Custom Validators** āļŠāļģāļŦāļĢāļąāļš business rule validation +- [ ] **Data Transformation** āļŠāļģāļŦāļĢāļąāļš schema version migration +- [ ] **Sanitization Service** āļŠāļģāļŦāļĢāļąāļš data cleansing + +```typescript +@Injectable() +export class JsonSchemaService { + private ajv: Ajv; + + constructor() { + this.ajv = new Ajv({ + allErrors: true, + coerceTypes: true, + useDefaults: true, + removeAdditional: true, + formats: { + 'date-time': true, + email: true, + uri: true, + 'document-number': this.documentNumberFormat, + }, + }); + + // Register custom formats and keywords + this.registerCustomValidators(); + } + + async validateData( + schemaName: string, + data: any, + options: ValidationOptions = {} + ): Promise { + const schema = await this.getSchema(schemaName); + const validate = this.ajv.compile(schema); + + const isValid = validate(data); + + if (!isValid) { + return { + isValid: false, + errors: validate.errors, + sanitizedData: null, + }; + } + + // Apply data transformation if needed + const sanitizedData = await this.sanitizeData(data, schema, options); + + return { + isValid: true, + errors: [], + sanitizedData, + }; + } + + private async sanitizeData( + data: any, + schema: any, + options: ValidationOptions + ): Promise { + const sanitized = { ...data }; + + // Remove unknown properties if not allowed + if (options.removeAdditional !== false) { + const allowedProperties = this.extractPropertyNames(schema); + Object.keys(sanitized).forEach((key) => { + if (!allowedProperties.includes(key)) { + delete sanitized[key]; + } + }); + } + + // Apply custom sanitizers based on field type + await this.applyFieldSanitizers(sanitized, schema); + + return sanitized; + } + + private registerCustomValidators(): void { + // Custom format for document numbers + this.ajv.addFormat('document-number', { + type: 'string', + validate: (value: string) => { + return /^[A-Z]{3,5}-[A-Z]{2,4}-\d{4}-\d{3,5}$/.test(value); + }, + }); + + // Custom keyword for role-based access + this.ajv.addKeyword({ + keyword: 'requiredRole', + type: 'string', + compile: (requiredRole: string) => { + return (data: any, dataPath: string, parentData: any) => { + // Check if user has required role for this field + const userContext = this.getUserContext(); + return userContext.roles.includes(requiredRole); + }; + }, + }); + } +} +``` + +##### **[ ] T2.5.3 Virtual Columns & Performance Optimization** + +- [ ] **Virtual Column Generator** āļŠāļģāļŦāļĢāļąāļš JSON field indexing +- [ ] **Migration Scripts** āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ generated columns +- [ ] **Query Optimizer** āļŠāļģāļŦāļĢāļąāļšāđƒāļŠāđ‰ virtual columns āđƒāļ™ search +- [ ] **Performance Monitoring** āļŠāļģāļŦāļĢāļąāļš JSON query performance + +```typescript +@Injectable() +export class VirtualColumnService { + constructor( + private dataSource: DataSource, + private configService: ConfigService + ) {} + + async setupVirtualColumns( + tableName: string, + schemaConfig: VirtualColumnConfig[] + ): Promise { + const connection = this.dataSource.manager.connection; + + for (const config of schemaConfig) { + await this.createVirtualColumn(tableName, config); + } + } + + private async createVirtualColumn( + tableName: string, + config: VirtualColumnConfig + ): Promise { + const columnDefinition = this.generateColumnDefinition(config); + + const sql = ` + ALTER TABLE ${tableName} + ADD COLUMN ${config.column_name} ${columnDefinition} + `; + + await this.dataSource.query(sql); + + // Create index if specified + if (config.index_type) { + await this.createIndex(tableName, config); + } + } + + private generateColumnDefinition(config: VirtualColumnConfig): string { + const dataType = this.mapDataType(config.data_type); + const jsonPath = this.escapeJsonPath(config.json_path); + + return `${dataType} GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '${jsonPath}'))) VIRTUAL`; + } + + private async createIndex( + tableName: string, + config: VirtualColumnConfig + ): Promise { + const indexName = `idx_${tableName}_${config.column_name}`; + const sql = ` + CREATE ${config.index_type} INDEX ${indexName} + ON ${tableName} (${config.column_name}) + `; + + await this.dataSource.query(sql); + } +} + +// Example virtual column configuration for correspondence +const correspondenceVirtualColumns: VirtualColumnConfig[] = [ + { + json_path: '$.projectId', + column_name: 'ref_project_id', + data_type: 'INT', + index_type: 'INDEX', + is_required: true, + }, + { + json_path: '$.priority', + column_name: 'ref_priority', + data_type: 'VARCHAR', + index_type: 'INDEX', + is_required: false, + }, + { + json_path: '$.dueDate', + column_name: 'ref_due_date', + data_type: 'DATE', + index_type: 'INDEX', + is_required: false, + }, +]; +``` + +##### **[ ] T2.5.4 Dynamic Form Schema Management** + +- [ ] **UI Schema Definition** āļŠāļģāļŦāļĢāļąāļš frontend form generation +- [ ] **Field Dependency System** āļŠāļģāļŦāļĢāļąāļš conditional fields +- [ ] **Validation Rule Sync** āļĢāļ°āļŦāļ§āđˆāļēāļ‡ backend-frontend +- [ ] **Form Template Registry** āļŠāļģāļŦāļĢāļąāļš reusable form patterns + +```typescript +interface UiSchema { + type: 'object'; + properties: { + [key: string]: UiSchemaField; + }; + required?: string[]; + layout?: { + type: 'tabs' | 'sections' | 'steps'; + groups: LayoutGroup[]; + }; +} + +interface UiSchemaField { + type: 'string' | 'number' | 'boolean' | 'array' | 'object'; + widget?: 'text' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'date'; + title: string; + description?: string; + placeholder?: string; + enum?: string[]; + enumNames?: string[]; + dependencies?: FieldDependency[]; + conditions?: FieldCondition[]; + properties?: { [key: string]: UiSchemaField }; // for nested objects + items?: UiSchemaField; // for arrays +} + +interface FieldDependency { + field: string; + condition: { + operator: 'equals' | 'notEquals' | 'contains' | 'greaterThan'; + value: any; + }; + actions: { + visibility?: boolean; + required?: boolean; + options?: string[]; + }; +} + +// Example: RFA Drawing Schema +const rfaDwgSchema: UiSchema = { + type: 'object', + layout: { + type: 'tabs', + groups: [ + { + title: 'Basic Information', + fields: ['title', 'description', 'discipline'], + }, + { + title: 'Drawing Details', + fields: ['drawingReferences', 'revision', 'approvalType'], + }, + { + title: 'Technical Specifications', + fields: ['materials', 'dimensions', 'tolerances'], + }, + ], + }, + properties: { + title: { + type: 'string', + widget: 'text', + title: 'Drawing Title', + placeholder: 'Enter drawing title...', + required: true, + }, + discipline: { + type: 'string', + widget: 'select', + title: 'Discipline', + enum: ['CIVIL', 'STRUCTURAL', 'MECHANICAL', 'ELECTRICAL', 'PLUMBING'], + enumNames: [ + 'Civil', + 'Structural', + 'Mechanical', + 'Electrical', + 'Plumbing', + ], + }, + drawingReferences: { + type: 'array', + title: 'Related Contract Drawings', + items: { + type: 'string', + widget: 'select', + title: 'Drawing Number', + }, + }, + approvalType: { + type: 'string', + widget: 'radio', + title: 'Approval Type', + enum: ['FULL_APPROVAL', 'PARTIAL_APPROVAL', 'COMMENTS_ONLY'], + enumNames: ['Full Approval', 'Partial Approval', 'Comments Only'], + }, + }, + required: ['title', 'discipline', 'approvalType'], +}; +``` + +##### **[ ] T2.5.5 Data Migration & Version Compatibility** + +- [ ] **Schema Migration Service** āļŠāļģāļŦāļĢāļąāļšāļ­āļąāļžāđ€āļāļĢāļ” data āļĢāļ°āļŦāļ§āđˆāļēāļ‡ versions +- [ ] **Data Transformation Pipeline** āļŠāļģāļŦāļĢāļąāļš backward compatibility +- [ ] **Version Rollback Mechanism** āļŠāļģāļŦāļĢāļąāļš emergency situations +- [ ] **Migration Testing Framework** āļŠāļģāļŦāļĢāļąāļš įĄŪäŋ data integrity + +```typescript +@Injectable() +export class SchemaMigrationService { + async migrateData( + entityType: string, + entityId: string, + targetVersion: number + ): Promise { + const currentData = await this.getCurrentData(entityType, entityId); + const currentVersion = await this.getCurrentSchemaVersion( + entityType, + entityId + ); + + const migrationPath = await this.findMigrationPath( + currentVersion, + targetVersion + ); + + let migratedData = currentData; + + for (const migrationStep of migrationPath) { + migratedData = await this.applyMigrationStep(migrationStep, migratedData); + } + + // Validate migrated data against target schema + const validationResult = await this.validateAgainstSchema( + migratedData, + targetVersion + ); + + if (!validationResult.isValid) { + throw new MigrationError( + 'MIGRATION_VALIDATION_FAILED', + validationResult.errors + ); + } + + await this.saveMigratedData( + entityType, + entityId, + migratedData, + targetVersion + ); + + return { + success: true, + fromVersion: currentVersion, + toVersion: targetVersion, + migratedFields: this.getMigratedFields(currentData, migratedData), + }; + } + + private async applyMigrationStep( + step: MigrationStep, + data: any + ): Promise { + switch (step.type) { + case 'FIELD_RENAME': + return this.renameField(data, step.config); + case 'FIELD_TRANSFORM': + return this.transformField(data, step.config); + case 'FIELD_ADD': + return this.addField(data, step.config); + case 'FIELD_REMOVE': + return this.removeField(data, step.config); + case 'STRUCTURE_CHANGE': + return this.restructureData(data, step.config); + default: + throw new MigrationError('UNKNOWN_MIGRATION_TYPE'); + } + } +} + +// Example migration configuration +const migrationSteps = [ + { + from_version: 1, + to_version: 2, + type: 'FIELD_RENAME', + config: { + old_field: 'project_id', + new_field: 'ref_project_id', + }, + }, + { + from_version: 2, + to_version: 3, + type: 'FIELD_TRANSFORM', + config: { + field: 'priority', + transform: 'MAP_VALUES', + mapping: { + HIGH: 'URGENT', + MEDIUM: 'NORMAL', + LOW: 'LOW', + }, + }, + }, +]; +``` + +##### **[ ] T2.5.6 Security & Access Control for JSON Data** + +- [ ] **Field-level Security** 埚䚎 user roles +- [ ] **Data Encryption** āļŠāļģāļŦāļĢāļąāļš sensitive fields +- [ ] **Audit Logging** āļŠāļģāļŦāļĢāļąāļš JSON data changes +- [ ] **Input Sanitization** āļŠāļģāļŦāļĢāļąāļšāļ›āđ‰āļ­āļ‡āļāļąāļ™ XSS āđāļĨāļ° injection + +```typescript +@Injectable() +export class JsonSecurityService { + async applyFieldLevelSecurity( + data: any, + schema: any, + userContext: UserContext + ): Promise { + const securedData = { ...data }; + const securityRules = await this.getSecurityRules(schema.name); + + for (const [fieldPath, fieldConfig] of Object.entries(schema.properties)) { + const fieldRules = securityRules[fieldPath]; + + if (fieldRules && !this.hasFieldAccess(fieldRules, userContext)) { + // Remove or mask field based on security rules + if (fieldRules.on_deny === 'REMOVE') { + this.deleteField(securedData, fieldPath); + } else if (fieldRules.on_deny === 'MASK') { + this.maskField(securedData, fieldPath, fieldRules.mask_pattern); + } + } + } + + return securedData; + } + + async encryptSensitiveFields(data: any, schema: any): Promise { + const encryptedData = { ...data }; + const sensitiveFields = this.getSensitiveFields(schema); + + for (const fieldPath of sensitiveFields) { + const fieldValue = this.getFieldValue(data, fieldPath); + if (fieldValue) { + const encrypted = await this.cryptoService.encrypt( + fieldValue, + 'field-level' + ); + this.setFieldValue(encryptedData, fieldPath, encrypted); + } + } + + return encryptedData; + } + + private getSensitiveFields(schema: any): string[] { + const sensitiveFields: string[] = []; + + const traverseSchema = (obj: any, path: string = '') => { + if (obj.properties) { + for (const [key, value] of Object.entries(obj.properties)) { + const currentPath = path ? `${path}.${key}` : key; + + if (value.sensitive) { + sensitiveFields.push(currentPath); + } + + if (value.properties || value.items) { + traverseSchema(value, currentPath); + } + } + } + }; + + traverseSchema(schema); + return sensitiveFields; + } +} +``` + +##### **[ ] T2.5.7 API Design & Integration** + +- [ ] **Schema Management API** āļŠāļģāļŦāļĢāļąāļš CRUD operations +- [ ] **Validation API** āļŠāļģāļŦāļĢāļąāļš validate data against schemas +- [ ] **Migration API** āļŠāļģāļŦāļĢāļąāļš manage data migrations +- [ ] **Integration Hooks** āļŠāļģāļŦāļĢāļąāļš other modules + +```typescript +@Controller('json-schema') +export class JsonSchemaController { + @Post('validate/:schemaName') + @RequirePermission('schema.validate') + async validateData( + @Param('schemaName') schemaName: string, + @Body() dto: ValidateDataDto + ): Promise { + return this.jsonSchemaService.validateData( + schemaName, + dto.data, + dto.options + ); + } + + @Post('schemas') + @RequirePermission('schema.manage') + async createSchema(@Body() dto: CreateSchemaDto): Promise { + return this.jsonSchemaService.createSchema(dto); + } + + @Post('migrate/:entityType/:entityId') + @RequirePermission('data.migrate') + async migrateData( + @Param('entityType') entityType: string, + @Param('entityId') entityId: string, + @Body() dto: MigrateDataDto + ): Promise { + return this.migrationService.migrateData( + entityType, + entityId, + dto.targetVersion + ); + } + + @Get('ui-schema/:schemaName') + @RequirePermission('schema.view') + async getUiSchema( + @Param('schemaName') schemaName: string + ): Promise { + return this.schemaService.getUiSchema(schemaName); + } +} +``` + +### **[ ] T2.5.8 Integration with Document Modules** + +- [ ] **Correspondence Module Integration** +- [ ] **RFA Module Integration** +- [ ] **Circulation Module Integration** +- [ ] **Drawing Module Integration** + +```typescript +// Example: Correspondence Service Integration +@Injectable() +export class CorrespondenceService { + constructor( + private jsonSchemaService: JsonSchemaService, + private detailsService: DetailsService + ) {} + + async createCorrespondence( + dto: CreateCorrespondenceDto + ): Promise { + // 1. Validate details against schema + const validationResult = await this.jsonSchemaService.validateData( + `CORRESPONDENCE_${dto.type}`, + dto.details + ); + + if (!validationResult.isValid) { + throw new ValidationError('INVALID_DETAILS', validationResult.errors); + } + + // 2. Apply security and sanitization + const secureDetails = await this.detailsService.sanitizeDetails( + validationResult.sanitizedData, + dto.type + ); + + // 3. Create correspondence entity + const correspondence = this.correspondenceRepository.create({ + ...dto, + details: secureDetails, + schema_version: await this.getCurrentSchemaVersion( + `CORRESPONDENCE_${dto.type}` + ), + }); + + // 4. Setup virtual columns for performance + await this.setupVirtualColumns(correspondence); + + return this.correspondenceRepository.save(correspondence); + } + + async searchCorrespondences( + filters: SearchFilters + ): Promise { + // Use virtual columns for efficient filtering + const query = this.correspondenceRepository.createQueryBuilder('c'); + + if (filters.projectId) { + query.andWhere('c.ref_project_id = :projectId', { + projectId: filters.projectId, + }); + } + + if (filters.priority) { + query.andWhere('c.ref_priority = :priority', { + priority: filters.priority, + }); + } + + return query.getMany(); + } +} +``` + +##### **[ ] T2.5.9 Testing Strategy** + +- [ ] **Unit Tests** āļŠāļģāļŦāļĢāļąāļš schema validation āđāļĨāļ° transformation +- [ ] **Integration Tests** āļŠāļģāļŦāļĢāļąāļš end-to-end data flow +- [ ] **Performance Tests** āļŠāļģāļŦāļĢāļąāļš virtual columns āđāļĨāļ° large datasets +- [ ] **Security Tests** āļŠāļģāļŦāļĢāļąāļš field-level security + +```typescript +describe('JsonSchemaService', () => { + describe('validateData', () => { + it('should validate correct RFA_DWG data successfully', async () => { + const testData = { + title: 'Structural Beam Details', + discipline: 'STRUCTURAL', + drawingReferences: ['CD-STR-001', 'CD-STR-002'], + approvalType: 'FULL_APPROVAL', + materials: ['STEEL_A36', 'CONCRETE_40MPA'], + }; + + const result = await jsonSchemaService.validateData('RFA_DWG', testData); + + expect(result.isValid).toBe(true); + expect(result.errors).toHaveLength(0); + }); + + it('should reject invalid discipline value', async () => { + const testData = { + title: 'Test Drawing', + discipline: 'INVALID_DISCIPLINE', // Not in enum + approvalType: 'FULL_APPROVAL', + }; + + const result = await jsonSchemaService.validateData('RFA_DWG', testData); + + expect(result.isValid).toBe(false); + expect(result.errors[0].message).toContain('discipline'); + }); + }); +}); + +describe('VirtualColumnService', () => { + it('should improve search performance with virtual columns', async () => { + // Create test data with JSON details + await createTestCorrespondences(1000); + + // Search without virtual column (JSON_EXTRACT) + const startTime1 = Date.now(); + const result1 = await correspondenceRepository + .createQueryBuilder('c') + .where("JSON_EXTRACT(c.details, '$.projectId') = :projectId", { + projectId: 123, + }) + .getMany(); + const time1 = Date.now() - startTime1; + + // Search with virtual column + const startTime2 = Date.now(); + const result2 = await correspondenceRepository + .createQueryBuilder('c') + .where('c.ref_project_id = :projectId', { projectId: 123 }) + .getMany(); + const time2 = Date.now() - startTime2; + + expect(time2).toBeLessThan(time1 * 0.5); // At least 2x faster + expect(result1.length).toEqual(result2.length); + }); +}); +``` + +#### 🔗 **Critical Dependencies** + +- **T1.1** (Common Module) - āļŠāļģāļŦāļĢāļąāļš base entities āđāļĨāļ° shared services +- **T1.4** (RBAC Guard) - āļŠāļģāļŦāļĢāļąāļš field-level security +- **T0.3** (Database) - āļŠāļģāļŦāļĢāļąāļš virtual columns implementation +- **T3.2** (Correspondence) - āļŠāļģāļŦāļĢāļąāļš integration testing + +#### ðŸŽŊ **Success Metrics** + +- ✅ JSON validation performance: < 10ms āļ•āđˆāļ­ request +- ✅ Virtual columns improve search performance 5x+ +- ✅ Support schema evolution without data loss +- ✅ Field-level security enforced across all modules +- ✅ 100% test coverage āļŠāļģāļŦāļĢāļąāļš core validation logic + +* **[ ] T2.6 MasterModule - Advanced Data (Req 6B)** (New) + - [ ] Update Entities: `Discipline`, `CorrespondenceSubType` + - [ ] Create Services/Controllers for CRUD Operations (Admin Panel Support) + - [ ] Implement Seeding Logic for initial 6B data + - [ ] **Deliverable:** API for managing Disciplines and Sub-types + - [ ] **Dependencies:** T1.1, T0.3 + +--- + +## **Phase 3: Unified Workflow Engine (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 5-6)** + +**Milestone:** āļĢāļ°āļšāļš Workflow āļāļĨāļēāļ‡āļ—āļĩāđˆāļĢāļ­āļ‡āļĢāļąāļšāļ—āļąāđ‰āļ‡ Routing āļ›āļāļ•āļī āđāļĨāļ° RFA + +### **Phase 3: Tasks** + +- **[ ] T3.1 WorkflowEngineModule (New)** + + - [ ] āļ­āļ­āļāđāļšāļš Generic Schema āļŠāļģāļŦāļĢāļąāļš Workflow State Machine + - [ ] Implement Service: `initializeWorkflow()`, `processAction()`, `getNextStep()` + - [ ] āļĢāļ­āļ‡āļĢāļąāļš Logic āļāļēāļĢ "āļ‚āđ‰āļēāļĄāļ‚āļąāđ‰āļ™āļ•āļ­āļ™" āđāļĨāļ° "āļŠāđˆāļ‡āļāļĨāļąāļš" āļ āļēāļĒāđƒāļ™ Engine āđ€āļ”āļĩāļĒāļ§ + - [ ] **Security:** Implement audit logging āļŠāļģāļŦāļĢāļąāļš workflow actions + - [ ] **Deliverable:** Unified Workflow Engine āļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™ + - [ ] **Dependencies:** T1.1 + +- **[ ] T3.1.1 Workflow DSL Specification & Grammar** + - [ ] Define EBNF Grammar āļŠāļģāļŦāļĢāļąāļš Workflow DSL + - [ ] Create YAML Schema āļŠāļģāļŦāļĢāļąāļš human-friendly workflow definitions + - [ ] Design JSON Schema āļŠāļģāļŦāļĢāļąāļš compiled workflow representations + +```yaml +# āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ DSL Structure + +workflow: RFA_APPROVAL +version: 1.0 +description: "RFA Approval Workflow with Parallel Reviews" + +states: + +- name: DRAFT + initial: true + metadata: + color: "gray" + icon: "draft" + on: + SUBMIT: + to: TECHNICAL_REVIEW + conditions: - expression: "user.hasRole('ENGINEER')" + requirements: - role: "ENGINEER" + events: - type: "notify" + target: "reviewers" + template: "NEW_RFA_SUBMITTED" - type: "assign" + target: "technical_lead" + +- name: TECHNICAL_REVIEW + metadata: + color: "blue" + icon: "review" + on: + APPROVE: + to: MANAGERIAL_REVIEW + conditions: - expression: "user.department === context.document.department" + REQUEST_CHANGES: + to: DRAFT + events: - type: "notify" + target: "creator" + template: "CHANGES_REQUESTED" + ESCALATE: + to: ESCALATED_REVIEW + conditions: - expression: "document.priority === 'HIGH'" + +- name: PARALLEL_APPROVAL + parallel: true + branches: + + - MANAGERIAL_REVIEW + - FINANCIAL_REVIEW + on: + ALL_APPROVED: + to: APPROVED + ANY_REJECTED: + to: REJECTED + +- name: APPROVED + terminal: true + metadata: + color: "green" + icon: "approved" + Versioning Strategy āļŠāļģāļŦāļĢāļąāļš workflow definitions +``` + +- **[ ] T3.1.2 Workflow Core Entities & Database Schema** + + - [ ] WorkflowDefinition Entity + - [ ] WorkflowInstance Entity + - [ ] WorkflowHistory Entity + - [ ] WorkflowTransition Entity + +```typescript +// Core Entities Design +@Entity() +export class WorkflowDefinition { + @PrimaryGeneratedColumn('uuid') + id: string; + + @Column() + name: string; + + @Column() + version: number; + + @Column('json') + dsl_raw: any; // YAML/JSON DSL + + @Column('json') + compiled_schema: any; // Normalized JSON + + @Column({ default: true }) + is_active: boolean; + + @CreateDateColumn() + created_at: Date; + + @VersionColumn() + version: number; +} + +@Entity() +export class WorkflowInstance { + @PrimaryGeneratedColumn('uuid') + id: string; + + @ManyToOne(() => WorkflowDefinition) + definition: WorkflowDefinition; + + @Column() + entity_type: string; // 'correspondence', 'rfa', 'circulation' + + @Column() + entity_id: string; + + @Column() + current_state: string; + + @Column('json') + context: any; // Workflow-specific data + + @Column('json') + history: any[]; // State transition history + + @CreateDateColumn() + created_at: Date; + + @UpdateDateColumn() + updated_at: Date; +} +``` + +- **[ ] T3.1.3 DSL Parser & Compiler Service** + + - [ ] YAML Parser āļŠāļģāļŦāļĢāļąāļšāļ­āđˆāļēāļ™ DSL definitions + - [ ] Syntax Validator āļŠāļģāļŦāļĢāļąāļš compile-time validation + - [ ] Schema Compiler āļŠāļģāļŦāļĢāļąāļšāđāļ›āļĨāļ‡ DSL → Normalized JSON + - [ ] Version Migration āļŠāļģāļŦāļĢāļąāļšāļ­āļąāļžāđ€āļāļĢāļ” workflow definitions + +```typescript +@Injectable() +export class WorkflowDslService { + async parseAndValidate(dslContent: string): Promise { + // 1. Parse YAML + const rawDefinition = yaml.parse(dslContent); + + // 2. Validate Syntax + await this.validateSyntax(rawDefinition); + + // 3. Compile to Normalized JSON + const compiled = await this.compileDefinition(rawDefinition); + + // 4. Validate Business Rules + await this.validateBusinessRules(compiled); + + return compiled; + } + + private async validateSyntax(definition: any): Promise { + const rules = [ + // āļ•āđ‰āļ­āļ‡āļĄāļĩāļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 1 initial state + () => definition.states.some((s) => s.initial), + // Terminal states āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāļĄāļĩ transitions + () => !definition.states.filter((s) => s.terminal).some((s) => s.on), + // State names must be unique + () => + new Set(definition.states.map((s) => s.name)).size === + definition.states.length, + // Transition targets must exist + () => this.validateTransitionTargets(definition), + ]; + + for (const rule of rules) { + if (!rule()) { + throw new WorkflowValidationError('DSL_VALIDATION_FAILED'); + } + } + } +} +``` + +- **[ ] T3.1.4 Workflow Runtime Engine** + - [ ] State Machine Engine āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ state transitions + - [ ] Condition Evaluator āļŠāļģāļŦāļĢāļąāļšāļ›āļĢāļ°āđ€āļĄāļīāļ™ conditional transitions + - [ ] Permission Checker āļŠāļģāļŦāļĢāļąāļšāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° + - [ ] Event Dispatcher āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ workflow events + +```typescript +****@Injectable() +export class WorkflowEngineService { + async processTransition( + instanceId: string, + action: string, + context: WorkflowContext + ): Promise { + // 1. Load Workflow Instance + const instance = await this.findInstance(instanceId); + const definition = await this.getCompiledDefinition(instance.definition_id); + + // 2. Validate Current State & Action + const currentState = definition.states[instance.current_state]; + const transition = currentState.transitions[action]; + + if (!transition) { + throw new WorkflowError('INVALID_TRANSITION'); + } + + // 3. Check Permissions & Conditions + await this.validatePermissions(transition, context); + await this.validateConditions(transition, context); + + // 4. Execute Pre-Transition Hooks + await this.executeHooks('pre_transition', instance, transition, context); + + // 5. Perform State Transition + const previousState = instance.current_state; + instance.current_state = transition.to; + + // 6. Record History + await this.recordTransitionHistory(instance, { + from: previousState, + to: transition.to, + action, + user: context.userId, + timestamp: new Date(), + metadata: context.metadata + }); + + // 7. Execute Post-Transition Events + await this.executeEvents(transition.events, instance, context); + + // 8. Execute Post-Transition Hooks + await this.executeHooks('post_transition', instance, transition, context); + + // 9. Save Instance + await this.saveInstance(instance); + + return { + success: true, + previousState, + newState: transition.to, + instanceId: instance.id + }; + } + + async getAvailableActions( + instanceId: string, + context: WorkflowContext + ): Promise { + const instance = await this.findInstance(instanceId); + const definition = await this.getCompiledDefinition(instance.definition_id); + const currentState = definition.states[instance.current_state]; + + return Object.keys(currentState.transitions).filter(action => { + const transition = currentState.transitions[action]; + return this.isActionAvailable(transition, context); + }); + } +} +``` + +- **[ ] T3.1.5 Advanced Feature Implementation** + - [ ] Parallel Approval Flows āļŠāļģāļŦāļĢāļąāļš multi-department approvals + - [ ] Conditional Transitions āļ”āđ‰āļ§āļĒ expression evaluation + - [ ] Timeout & Escalation āļŠāļģāļŦāļĢāļąāļšāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆāđ€āļāļīāļ™āļāļģāļŦāļ™āļ” + - [ ] Rollback & Compensation āļŠāļģāļŦāļĢāļąāļšāļĒāđ‰āļ­āļ™āļāļĨāļąāļšāļŠāļ–āļēāļ™āļ° + +```typescript +// Parallel Workflow Support +interface ParallelState { + parallel: true; + branches: string[]; + completion_policy: 'ALL' | 'ANY' | 'MAJORITY'; + on: { + [completionType: string]: { + to: string; + conditions?: Condition[]; + }; + }; +} + +// Conditional Transition Support +interface ConditionalTransition { + to: string; + conditions: Array<{ + expression: string; // "user.role === 'MANAGER' && document.value > 10000" + evaluator?: 'javascript' | 'jsonlogic'; + }>; + requirements?: PermissionRequirement[]; +} + +// Timeout & Escalation +interface StateWithTimeout { + timeout_seconds: number; + on_timeout: { + action: string; + escalate_to?: string; + notify?: string[]; + }; +} +``` + +- **[ ] T3.1.6 Event System & Integration** + - [ ] Event Types (notify, assign, webhook, auto_action) + - [ ] Template Engine āļŠāļģāļŦāļĢāļąāļš dynamic messages + - [ ] Webhook Support āļŠāļģāļŦāļĢāļąāļš external integrations + - [ ] Notification Service Integration + +```typescript +@Injectable() +export class WorkflowEventService { + async executeEvents( + events: WorkflowEvent[], + instance: WorkflowInstance, + context: WorkflowContext + ): Promise { + for (const event of events) { + switch (event.type) { + case 'notify': + await this.handleNotifyEvent(event, instance, context); + break; + case 'assign': + await this.handleAssignEvent(event, instance, context); + break; + case 'webhook': + await this.handleWebhookEvent(event, instance, context); + break; + case 'auto_action': + await this.handleAutoActionEvent(event, instance, context); + break; + } + } + } + + private async handleNotifyEvent( + event: NotifyEvent, + instance: WorkflowInstance, + context: WorkflowContext + ): Promise { + const recipients = await this.resolveRecipients( + event.target, + instance, + context + ); + const message = await this.renderTemplate( + event.template, + instance, + context + ); + + await this.notificationService.send({ + type: 'workflow', + recipients, + subject: message.subject, + body: message.body, + metadata: { + workflow_instance_id: instance.id, + state: instance.current_state, + action: context.action, + }, + }); + } +} +``` + +- **[ ] T3.1.7 API Design & Controllers** + - [ ] REST API Endpoints āļŠāļģāļŦāļĢāļąāļš workflow management + - [ ] WebSocket Support āļŠāļģāļŦāļĢāļąāļš real-time updates + - [ ] Admin API āļŠāļģāļŦāļĢāļąāļš workflow definition management + - [ ] Integration Hooks āļŠāļģāļŦāļĢāļąāļš external systems + +```typescript +@Controller('workflow') +export class WorkflowEngineController { + @Post('instances/:id/transition') + @RequirePermission('workflow.execute') + async processTransition( + @Param('id') instanceId: string, + @Body() dto: WorkflowTransitionDto + ): Promise { + return this.workflowEngine.processTransition( + instanceId, + dto.action, + dto.context + ); + } + + @Get('instances/:id/actions') + @RequirePermission('workflow.view') + async getAvailableActions( + @Param('id') instanceId: string, + @Query() context: WorkflowContext + ): Promise { + return this.workflowEngine.getAvailableActions(instanceId, context); + } + + @Post('definitions') + @RequirePermission('workflow.manage') + async createWorkflowDefinition( + @Body() dto: CreateWorkflowDefinitionDto + ): Promise { + return this.workflowDslService.compileAndSave(dto.dslContent); + } + + @Get('instances/:id/history') + @RequirePermission('workflow.view') + async getWorkflowHistory( + @Param('id') instanceId: string + ): Promise { + return this.workflowHistoryService.getHistory(instanceId); + } +} +``` + +- **[ ] T3.1.8 Integration (Clean Migration)** + - [ ] Correspondence: āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ CorrespondenceWorkflowService + - [ ] RFA: āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ RfaWorkflowService āļāļąāļš Engine (āļĨāļš Code āđ€āļāđˆāļēāļ—āļĩāđˆāđƒāļŠāđ‰āļ•āļēāļĢāļēāļ‡ rfa_workflows) + - [ ] Circulation: āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ CirculationWorkflowService āļāļąāļš Engine (āļĨāļš Code āđ€āļāđˆāļēāļ—āļĩāđˆāđƒāļŠāđ‰āļ•āļēāļĢāļēāļ‡ circulation_routings) + - [ ] Notification Module Integration + +```typescript +// Correspondence Integration Example +@Injectable() +export class CorrespondenceWorkflowService { + constructor( + private workflowEngine: WorkflowEngineService, + private correspondenceService: CorrespondenceService + ) {} + + async submitCorrespondence( + correspondenceId: string, + userId: string + ): Promise { + const correspondence = await this.correspondenceService.findById( + correspondenceId + ); + + // Create workflow instance + const instance = await this.workflowEngine.createInstance({ + definition: 'CORRESPONDENCE_ROUTING', + entity_type: 'correspondence', + entity_id: correspondenceId, + context: { + document: correspondence, + user: userId, + }, + }); + + // Process initial transition + await this.workflowEngine.processTransition(instance.id, 'SUBMIT', { + userId, + metadata: { correspondenceId }, + }); + } +} +``` + +- [ ] **T3.1.9 Cleanup Legacy Code:** + + - [ ] āļĨāļš Entity/Repository āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡āđ€āļāđˆāļē (_\_routings, _\_templates) āļ­āļ­āļāļˆāļēāļ Codebase + - [ ] āļ­āļąāļ›āđ€āļ”āļ• SQL Migration āđƒāļŦāđ‰ Drop āļ•āļēāļĢāļēāļ‡āđ€āļāđˆāļēāļ—āļīāđ‰āļ‡ + +- [ ] **T3.1.10 Testing Strategy** + - [ ] Unit Tests āļŠāļģāļŦāļĢāļąāļš DSL parser āđāļĨāļ° state machine + - [ ] Integration Tests āļŠāļģāļŦāļĢāļąāļš end-to-end workflow execution + - [ ] Performance Tests āļŠāļģāļŦāļĢāļąāļš high-concurrency scenarios + - [ ] Security Tests āļŠāļģāļŦāļĢāļąāļš permission validation. + +```typescript +describe('WorkflowEngineService', () => { + describe('processTransition', () => { + it('should successfully transition state with valid permissions', async () => { + // Arrange + const instance = await createTestInstance(); + const context = { userId: 'user1', roles: ['APPROVER'] }; + + // Act + const result = await workflowEngine.processTransition( + instance.id, + 'APPROVE', + context + ); + + // Assert + expect(result.success).toBe(true); + expect(result.newState).toBe('APPROVED'); + }); + + it('should reject transition without required permissions', async () => { + // Arrange + const instance = await createTestInstance(); + const context = { userId: 'user2', roles: ['VIEWER'] }; + + // Act & Assert + await expect( + workflowEngine.processTransition(instance.id, 'APPROVE', context) + ).rejects.toThrow(WorkflowError); + }); + }); +}); +``` + +- **🔗 Critical Dependencies of T3.1.1-T3.1.8** + + - T1.1 (Common Module) - āļŠāļģāļŦāļĢāļąāļš base entities āđāļĨāļ° shared services + - T1.4 (RBAC Guard) - āļŠāļģāļŦāļĢāļąāļš permission checking + - T2.5 (JSON Schema) - āļŠāļģāļŦāļĢāļąāļš DSL validation + - T6.2 (Notification) - āļŠāļģāļŦāļĢāļąāļš event handling + +- **ðŸŽŊ Success Metrics** + + - ✅ Support āļ—āļąāđ‰āļ‡ Correspondence Routing āđāļĨāļ° RFA Workflow + - ✅ DSL āļ—āļĩāđˆ human-readable āđāļĨāļ° editable āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđāļāđ‰āđ‚āļ„āđ‰āļ” + - ✅ Performance: < 50ms āļ•āđˆāļ­ state transition + - ✅ 100% test coverage āļŠāļģāļŦāļĢāļąāļš core workflow logic + - ✅ Complete audit trail āļŠāļģāļŦāļĢāļąāļšāļ—āļļāļ workflow instance + +- **[ ] T3.2 CorrespondenceModule - Basic CRUD** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (Correspondence, Revision, Recipient, Tag, Reference, Attachment) + - [ ] āļŠāļĢāđ‰āļēāļ‡ CorrespondenceService (Create with Document Numbering, Update with new Revision, Soft Delete) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (POST/GET/PUT/DELETE /correspondences) + - [ ] Implement Impersonation Logic: āļ•āļĢāļ§āļˆāļŠāļ­āļš originatorId āđƒāļ™ DTO āļŦāļēāļāļĄāļĩāļāļēāļĢāļŠāđˆāļ‡āļĄāļē āļ•āđ‰āļ­āļ‡āđ€āļŠāđ‡āļ„āļ§āđˆāļē User āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļāļĢāļ°āļ—āļģāļāļēāļĢāđāļ—āļ™āļŦāļĢāļ·āļ­āđ„āļĄāđˆ (Superadmin) + - [ ] **Security:** Implement permission checks āļŠāļģāļŦāļĢāļąāļš document access + - [ ] **Deliverable:** āļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚/āļ”āļđāđ€āļ­āļāļŠāļēāļĢāđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1, T1.2, T1.3, T1.4, T1.5, T2.3, T2.2, T2.5 + +- **[ ] T3.3 CorrespondenceModule - Advanced Features** + + - [ ] Implement Status Transitions (DRAFT → SUBMITTED) + - [ ] Implement References (Link Documents) + - [ ] Implement Search (Basic) + - [ ] **Security:** Implement state transition validation + - [ ] **Deliverable:** Workflow āļžāļ·āđ‰āļ™āļāļēāļ™āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T3.2 + +- **[ ] T3.4 Correspondence Integration with Workflow** + + - [ ] āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ `CorrespondenceService` āđ€āļ‚āđ‰āļēāļāļąāļš `WorkflowEngineModule` + - [ ] āļĒāđ‰āļēāļĒ Logic āļāļēāļĢ Routing āđ€āļ”āļīāļĄāļĄāļēāđƒāļŠāđ‰ Engine āđƒāļŦāļĄāđˆ + - [ ] āļŠāļĢāđ‰āļēāļ‡ API endpoints āļŠāļģāļŦāļĢāļąāļš Frontend (Templates, Pending Tasks, Bulk Action) + - [ ] **Security:** Implement permission checks āļŠāļģāļŦāļĢāļąāļš workflow operations + - [ ] **Deliverable:** āļĢāļ°āļšāļšāļŠāđˆāļ‡āļ•āđˆāļ­āđ€āļ­āļāļŠāļēāļĢāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļŠāļĄāļšāļđāļĢāļ“āđŒāļ”āđ‰āļ§āļĒ Unified Engine + - [ ] **Dependencies:** T3.1, T3.2 + +--- + +## **Phase 4: Drawing & Advanced Workflows (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 7-8)** + +**Milestone:** āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđāļšāļšāđāļĨāļ° RFA āđ‚āļ”āļĒāđƒāļŠāđ‰ Unified Engine + +### **Phase 4: Tasks** + +- **[ ] T4.1 DrawingModule - Contract Drawings** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (ContractDrawing, Volume, Category, SubCategory, Attachment) + - [ ] āļŠāļĢāđ‰āļēāļ‡ ContractDrawingService CRUD + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (GET/POST /drawings/contract) + - [ ] **Security:** Implement access control āļŠāļģāļŦāļĢāļąāļš contract drawings + - [ ] **Deliverable:** āļˆāļąāļ”āļāļēāļĢ Contract Drawings āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1, T1.2, T1.4, T1.5, T2.2 + +- **[ ] T4.2 DrawingModule - Shop Drawings** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (ShopDrawing, Revision, Main/SubCategory, ContractRef, RevisionAttachment) + - [ ] āļŠāļĢāđ‰āļēāļ‡ ShopDrawingService CRUD (āļĢāļ§āļĄāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Revision) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (GET/POST /drawings/shop, /drawings/shop/:id/revisions) + - [ ] Link Shop Drawing Revision → Contract Drawings + - [ ] **Security:** Implement virus scanning āļŠāļģāļŦāļĢāļąāļš drawing files + - [ ] **Deliverable:** āļˆāļąāļ”āļāļēāļĢ Shop Drawings āđāļĨāļ° Revisions āđ„āļ”āđ‰ + - [ ] **Dependencies:** T4.1 + +--- + +## **Phase 5: Workflow Systems & Resilience (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 8-9)** + +**Milestone:** āļĢāļ°āļšāļš Workflow āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļžāļĢāđ‰āļ­āļĄ Resilience Patterns + +### **Phase 5: Tasks** + +- **[ ] T5.1 RfaModule with Unified Workflow** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (Rfa, RfaRevision, RfaItem) + - [ ] āļŠāļĢāđ‰āļēāļ‡ RfaService (Create RFA, Link Shop Drawings) + - [ ] Implement RFA Workflow āđ‚āļ”āļĒāđƒāļŠāđ‰ Configuration āļ‚āļ­āļ‡ `WorkflowEngineModule` + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (POST/GET /rfas, POST /rfas/:id/workflow/...) + - [ ] **Resilience:** Implement circuit breaker āļŠāļģāļŦāļĢāļąāļš notification services + - [ ] **Deliverable:** RFA Workflow āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļ”āđ‰āļ§āļĒ Unified Engine + - [ ] **Dependencies:** T3.2, T4.2, T2.5, T6.2 + +- **[ ] T5.2 CirculationModule - Internal Routing** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (Circulation, Template, Routing, Attachment) + - [ ] āļŠāļĢāđ‰āļēāļ‡ CirculationService (Create 1:1 with Correspondence, Assign User, Complete/Close Step) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (POST/GET /circulations, POST /circulations/:id/steps/...) + - [ ] **Resilience:** Implement retry mechanism āļŠāļģāļŦāļĢāļąāļš assignment notifications + - [ ] **Deliverable:** āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T3.2, T2.5, T6.2 + +- **[ ] T5.3 TransmittalModule - Document Forwarding** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Entities (Transmittal, TransmittalItem) + - [ ] āļŠāļĢāđ‰āļēāļ‡ TransmittalService (Create Correspondence + Transmittal, Link Multiple Correspondences) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (POST/GET /transmittals) + - [ ] **Security:** Implement access control āļŠāļģāļŦāļĢāļąāļš transmittal items + - [ ] **Deliverable:** āļŠāļĢāđ‰āļēāļ‡ Transmittal āđ„āļ”āđ‰ + - [ ] **Dependencies:** T3.2 + +--- + +## **Phase 6: Notification & Resilience (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 9)** + +**Milestone:** āļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđāļšāļš Digest āđāļĨāļ°āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļ‚āļ™āļēāļ”āđƒāļŦāļāđˆ + +### **Phase 6: Tasks** + +- **[ ] T6.1 SearchModule - Elasticsearch Integration** + + - [ ] Setup Elasticsearch Container + - [ ] āļŠāļĢāđ‰āļēāļ‡ SearchService (index/update/delete documents, search) + - [ ] Index āļ—āļļāļ Document Type + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (GET /search) + - [ ] **Resilience:** Implement circuit breaker āļŠāļģāļŦāļĢāļąāļš Elasticsearch + - [ ] **Deliverable:** āļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T3.2, T5.1, T4.2, T5.2, T5.3 + +- **[ ] T6.2 Notification Queue & Digest** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ NotificationService (sendEmail/Line/System) + - [ ] **Producer:** Push Event āļĨāļ‡ BullMQ Queue + - [ ] **Consumer:** āļˆāļąāļ”āļāļĨāļļāđˆāļĄ Notification (Digest Message) āđāļĨāļ°āļŠāđˆāļ‡āļœāđˆāļēāļ™ Email/Line + - [ ] Integrate āļāļąāļš Workflow Events (āđāļˆāđ‰āļ‡ Recipients, Assignees, Deadline) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Controllers (GET /notifications, PUT /notifications/:id/read) + - [ ] **Resilience:** Implement retry mechanism āļ”āđ‰āļ§āļĒ exponential backoff + - [ ] **Deliverable:** āļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āđāļšāļš Digest + - [ ] **Dependencies:** T1.1, T6.4 + +- **[ ] T6.3 MonitoringModule - Observability** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Health Check Controller (GET /health) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Metrics Service (API response times, Error rates) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Performance Interceptor (Track request duration) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Logging Service (Structured logging) + - [ ] **Deliverable:** Monitoring system āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1 + +- **[ ] T6.4 ResilienceModule - Circuit Breaker & Retry** + + - [ ] āļŠāļĢāđ‰āļēāļ‡ Circuit Breaker Service (@CircuitBreaker() decorator) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Retry Service (@Retry() decorator) + - [ ] āļŠāļĢāđ‰āļēāļ‡ Fallback Strategies + - [ ] Implement āļŠāļģāļŦāļĢāļąāļš Email, LINE, Elasticsearch, Virus Scanning + - [ ] **Deliverable:** Resilience patterns āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + - [ ] **Dependencies:** T1.1 + +- **[ ] T6.5 Data Partitioning Strategy** + + - [ ] āļ­āļ­āļāđāļšāļš Table Partitioning āļŠāļģāļŦāļĢāļąāļš `audit_logs` āđāļĨāļ° `notifications` (āđāļšāđˆāļ‡āļ•āļēāļĄ Range: Year) + - [ ] āđ€āļ‚āļĩāļĒāļ™ Raw SQL Migration āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ Partition Table + - [ ] **Deliverable:** Database Performance āđāļĨāļ° Scalability āļ”āļĩāļ‚āļķāđ‰āļ™ + - [ ] **Dependencies:** T0.3 + +--- + +## **Phase 7: Testing & Hardening (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 10-12)** + +**Milestone:** āļ—āļ”āļŠāļ­āļšāļ„āļ§āļēāļĄāļ—āļ™āļ—āļēāļ™āļ•āđˆāļ­ Race Condition, Security, āđāļĨāļ° Performance + +### **Phase 7: Tasks** + +- **[ ] T7.1 Concurrency Testing** + + - [ ] āđ€āļ‚āļĩāļĒāļ™ Test Scenarios āļĒāļīāļ‡ Request āļ‚āļ­āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļžāļĢāđ‰āļ­āļĄāļāļąāļ™ 100 Request (āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāļ‹āđ‰āļģāđāļĨāļ°āđ„āļĄāđˆāļ‚āđ‰āļēāļĄ) + - [ ] āļ—āļ”āļŠāļ­āļš Optimistic Lock āļ—āļģāļ‡āļēāļ™āļ–āļđāļāļ•āđ‰āļ­āļ‡āđ€āļĄāļ·āđˆāļ­ Redis āļ–āļđāļāļ›āļīāļ” + - [ ] āļ—āļ”āļŠāļ­āļš File Upload āļžāļĢāđ‰āļ­āļĄāļāļąāļ™āļŦāļĨāļēāļĒāđ„āļŸāļĨāđŒ + - [ ] **Deliverable:** āļĢāļ°āļšāļšāļ—āļ™āļ—āļēāļ™āļ•āđˆāļ­ Concurrency Issues + +- **[ ] T7.2 Transaction Integrity Testing** + + - [ ] āļ—āļ”āļŠāļ­āļš Upload āđ„āļŸāļĨāđŒāđāļĨāđ‰āļ§ Kill Process āļāđˆāļ­āļ™ Commit + - [ ] āļ—āļ”āļŠāļ­āļš Two-Phase File Storage āļ—āļģāļ‡āļēāļ™āļ–āļđāļāļ•āđ‰āļ­āļ‡ + - [ ] āļ—āļ”āļŠāļ­āļš Database Transaction Rollback Scenarios + - [ ] **Deliverable:** Data Integrity āļĢāļąāļšāļ›āļĢāļ°āļāļąāļ™āđ„āļ”āđ‰ + +- **[ ] T7.3 Security & Idempotency Test** + + - [ ] āļ—āļ”āļŠāļ­āļš Replay Attack āđ‚āļ”āļĒāđƒāļŠāđ‰ `Idempotency-Key` āļ‹āđ‰āļģ + - [ ] āļ—āļ”āļŠāļ­āļš Maintenance Mode Block API āđ„āļ”āđ‰āļˆāļĢāļīāļ‡ + - [ ] āļ—āļ”āļŠāļ­āļš RBAC 4-Level āļ—āļģāļ‡āļēāļ™āļ–āļđāļāļ•āđ‰āļ­āļ‡ 100% + - [ ] **Deliverable:** Security āđāļĨāļ° Idempotency āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļ•āļēāļĄ čŪūčŪĄčĶæą‚ + +- **[ ] T7.4 Unit Testing (80% Coverage)** + +- **[ ] T7.5 Integration Testing** + +- **[ ] T7.6 E2E Testing** + +- **[ ] T7.7 Performance Testing** + + - [ ] Load Testing: 100 concurrent users + - [ ] **(āļŠāļģāļ„āļąāļ)** āļāļēāļĢāļˆāļđāļ™āđāļĨāļ°āļ—āļ”āļŠāļ­āļš Load Test āļˆāļ°āļ•āđ‰āļ­āļ‡āļ—āļģāđƒāļ™āļŠāļ āļēāļžāđāļ§āļ”āļĨāđ‰āļ­āļĄāļ—āļĩāđˆāļˆāļģāļĨāļ­āļ‡ Spec āļ‚āļ­āļ‡ QNAP Server (TS-473A, AMD Ryzen V1500B) āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ„āļ”āđ‰āļ„āđˆāļē Response Time āđāļĨāļ° Connection Pool āļ—āļĩāđˆāđ€āļ—āļĩāđˆāļĒāļ‡āļ•āļĢāļ‡ + - [ ] Stress Testing + - [ ] Endurance Testing + - [ ] **Deliverable:** Performance targets āļšāļĢāļĢāļĨāļļ + +- **[ ] T7.8 Security Testing** + + - [ ] Penetration Testing (OWASP Top 10) + - [ ] Security Audit (Code review, Dependency scanning) + - [ ] File Upload Security Testing + - [ ] **Deliverable:** Security tests āļœāđˆāļēāļ™ + +- **[ ] T7.9 Performance Optimization** + + - [ ] Implement Caching (Master Data, User Permissions, Search Results) + - [ ] Database Optimization (Review Indexes, Query Optimization, Pagination) + - [ ] **Deliverable:** Response Time < 200ms (90th percentile) + +--- + +## **Phase 8: Documentation & Deployment (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 14)** + +**Milestone:** āđ€āļ­āļāļŠāļēāļĢāđāļĨāļ° Deploy āļŠāļđāđˆ Production āļžāļĢāđ‰āļ­āļĄ Security Hardening + +### **Phase 8: Tasks** + +- **[ ] T8.1 API Documentation (Swagger)** +- **[ ] T8.2 Technical Documentation** +- **[ ] T8.3 Security Hardening** +- **[ ] T8.4 Deployment Preparation (QNAP Setup, Nginx Proxy Manager)** +- **[ ] T8.5 Production Deployment** +- **[ ] T8.6 Handover to Frontend Team** + +--- + +## 📊 **āļŠāļĢāļļāļ› Timeline** + +| Phase | āļĢāļ°āļĒāļ°āđ€āļ§āļĨāļē | āļˆāļģāļ™āļ§āļ™āļ‡āļēāļ™ | Output āļŦāļĨāļąāļ | +| :------ | :------------- | :----------- | :--------------------------------------------- | +| Phase 0 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Infrastructure Ready + Security Base | +| Phase 1 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 5 | Auth & User Management + RBAC + Idempotency | +| Phase 2 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 5 | High-Integrity Data & File Management | +| Phase 3 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Unified Workflow Engine + Correspondence | +| Phase 4 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 3 | Drawing Management + RFA with Unified Workflow | +| Phase 5 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 2 | Workflow Systems + Resilience | +| Phase 6 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 5 | Notification & Resilience + Data Partitioning | +| Phase 7 | 3 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 9 | Testing & Hardening | +| Phase 8 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 6 | Documentation & Deploy | +| **āļĢāļ§āļĄ** | **15 āļŠāļąāļ›āļ”āļēāļŦāđŒ** | **39 Tasks** | **Production-Ready Backend v1.4.2** | + +## **Document Control:** + +- **Document:** Backend Development Plan v1.4.5 +- **Version:** 1.4 +- **Date:** 2025-11-29 +- **Author:** NAP LCBP3-DMS & Gemini +- **Status:** FINAL-Rev.05 +- **Classification:** Internal Technical Documentation +- **Approved By:** Nattanin + +--- + +`End of Backend Development Plan v1.4.5` diff --git a/3_Frontend_Plan_V1_4_5.md b/3_Frontend_Plan_V1_4_5.md new file mode 100644 index 0000000..ddc620c --- /dev/null +++ b/3_Frontend_Plan_V1_4_5.md @@ -0,0 +1,978 @@ +# 📋 **āđāļœāļ™āļāļēāļĢāļžāļąāļ’āļ™āļē Frontend (Next.js) - LCBP3-DMS v1.4.5** + +**āļŠāļ–āļēāļ™āļ°:** FINAL GUIDELINE Rev.05 +**āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-29 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡:** Requirements v1.4.5 & FullStackJS Guidelines v1.4.5 +**Classification:** Internal Technical Documentation + +## ðŸŽŊ **āļ āļēāļžāļĢāļ§āļĄāđ‚āļ„āļĢāļ‡āļāļēāļĢ** + +āļžāļąāļ’āļ™āļē Frontend āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļšāļšāļĢāļīāļŦāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Document Management System) āļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļ—āļąāļ™āļŠāļĄāļąāļĒ āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļšāļ™āļ­āļļāļ›āļāļĢāļ“āđŒāļ•āđˆāļēāļ‡āđ† āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļŠāļĄāļšāļđāļĢāļ“āđŒ āļĄāļĩāļ›āļĢāļ°āļŠāļšāļāļēāļĢāļ“āđŒāļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĢāļēāļšāļĢāļ·āđˆāļ™ āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āđāļšāļš Offline āđ€āļšāļ·āđ‰āļ­āļ‡āļ•āđ‰āļ™ + +--- + +## 📐 **āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļĢāļ°āļšāļš** + +### **Technology Stack** + +- **Framework:** Next.js 14+ (App Router, React 18, TypeScript, ESM) +- **Styling:** Tailwind CSS + PostCSS +- **UI Components:** shadcn/ui + Radix UI Primitives +- **State Management:** + - **Server State:** TanStack Query (React Query) + - **Client State:** Zustand + - **Form State:** React Hook Form + Zod +- **API Client:** Axios (āļžāļĢāđ‰āļ­āļĄ Idempotency Interceptor) +- **Authentication:** NextAuth.js (āļĢāļ­āļ‡āļĢāļąāļš JWT) +- **File Upload:** Custom Hook + Drag & Drop +- **Testing:** + - **Unit/Integration:** Vitest + React Testing Library + - **E2E:** Playwright + - **Mocking:** MSW (Mock Service Worker) +- **Development:** + - **Package Manager:** pnpm + - **Linting:** ESLint + Prettier + - **Type Checking:** TypeScript Strict Mode + +### **āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒ** + +``` +📁frontend +├── .env.local +├── .eslintrc.json +├── .gitignore +├── components.json +├── middleware.ts +├── next-env.d.ts +├── next.config.mjs +├── package.json +├── pnpm-lock.yaml +├── postcss.config.mjs +├── README.md +├── tailwind.config.ts +├── tsconfig.json +├── 📁app +│ ├── 📁(auth) +│ │ └── 📁login +│ │ │ └── page.tsx +│ │ └── layout.tsx +│ ├── 📁(dashboard) +│ │ └── 📁admin +│ │ ├──📁users +│ │ │ └── page.tsx +│ │ ├──📁correspondences +│ │ │ └── 📁new +│ │ │ │ └── page.tsx +│ │ │ └── page.tsx +│ │ ├──📁dashboard +│ │ │ └── page.tsx +│ │ ├──📁profile +│ │ │ └── page.tsx +│ │ ├──📁projects +│ │ │ ├──📁new +│ │ │ │ └── page.tsx +│ │ │ └── page.tsx +│ │ └── layout.tsx +│ ├── 📁api +│ │ └── 📁auth +│ │ └── 📁[...nextauth] +│ │ └── route.ts +│ ├── 📁demo +│ │ └── page.tsx +│ ├── 📁fonts +│ │ ├── GeistMonoVF.woff +│ │ └── GeistVF.woff +│ ├── favicon.ico +│ ├── globals copy.css +│ ├── globals.css +│ ├── layout copy.tsx +│ ├── layout.tsx +│ └── page.tsx +├── 📁components +│ ├── 📁custom +│ │ ├── file-upload-zone.tsx +│ │ ├── responsive-data-table.tsx +│ │ └── workflow-visualizer.tsx +│ ├── 📁dashboard +│ │ └── recent-activity.tsx +│ ├── 📁forms +│ │ └── file-upload.tsx +│ ├── 📁layout +│ │ ├── dashboard-shell.tsx +│ │ ├── navbar.tsx +│ │ ├── sidebar.tsx +│ │ └── user-nav.tsx +│ ├── 📁tables +│ └── 📁ui +│ ├── avatar.tsx +│ ├── badge.tsx +│ ├── button.tsx +│ ├── calendar.tsx +│ ├── card.tsx +│ ├── checkbox.tsx +│ ├── dropdown-menu.tsx +│ ├── input.tsx +│ ├── label.tsx +│ ├── popover.tsx +│ ├── progress.tsx +│ ├── scroll-area.tsx +│ ├── select.tsx +│ ├── switch.tsx +│ ├── table.tsx +│ ├── tabs.tsx +│ └── textarea.tsx +├── 📁config +│ └── menu.ts +├── 📁lib +│ ├── 📁api +│ │ └── client.ts +│ ├── 📁auth +│ ├── 📁hooks +│ ├── 📁services +│ │ ├── circulation.service.ts +│ │ ├── contract-drawing.service.ts +│ │ ├── correspondence.service.ts +│ │ ├── index.ts +│ │ ├── json-schema.service.ts +│ │ ├── master-data.service.ts +│ │ ├── monitoring.service.ts +│ │ ├── notification.service.ts +│ │ ├── project.service.ts +│ │ ├── rfa.service.ts +│ │ ├── search.service.ts +│ │ ├── shop-drawing.service.ts +│ │ ├── transmittal.service.ts +│ │ ├── user.service.ts +│ │ └── workflow-engine.service.ts +│ ├── 📁stores +│ │ ├── draft-store.ts +│ │ └── ui-store.ts +│ ├── auth.ts +│ └── utils.ts +├── 📁providers +│ ├── query-provider.tsx +│ └── session-provider.tsx +├── 📁public +├── 📁styles +└── 📁types + └── 📁dto + └── next-auth.d.ts + ├── 📁circulation + │ ├── create-circulation.dto.ts + │ ├── search-circulation.dto.ts + │ └── update-circulation-routing.dto.ts + ├── 📁correspondence + │ ├── add-reference.dto.ts + │ ├── create-correspondence.dto.ts + │ ├── search-correspondence.dto.ts + │ ├── submit-correspondence.dto.ts + │ └── workflow-action.dto.ts + ├── 📁drawing + │ ├── contract-drawing.dto.ts + │ └── shop-drawing.dto.ts + ├── 📁json-schema + │ └── json-schema.dto.ts + ├── 📁master + │ ├── discipline.dto.ts + │ ├── number-format.dto.ts + │ ├── sub-type.dto.ts + │ └── tag.dto.ts + ├── 📁monitoring + │ └── set-maintenance.dto.ts + ├── 📁notification + │ └── notification.dto.ts + ├── 📁project + │ └── project.dto.ts + ├── 📁rfa + │ └── rfa.dto.ts + ├── 📁search + │ └── search-query.dto.ts + ├── 📁transmittal + │ └── transmittal.dto.ts + ├── 📁user + │ └── user.dto.ts + └── 📁workflow-engine + └── workflow-engine.dto.ts +``` + +--- + +## 🗓ïļ **āđāļœāļ™āļāļēāļĢāļžāļąāļ’āļ™āļēāđāļšāļš Phase-Based** + +### **Phase 0: Foundation & Configuration (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 1)** + +**Milestone:** āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļžāļ·āđ‰āļ™āļāļēāļ™āļžāļĢāđ‰āļ­āļĄ āļĢāļ­āļ‡āļĢāļąāļš Development Workflow āļ—āļĩāđˆāļĄāļĩāļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž + +### **Phase 0: Tasks** + +- **[ ] F0.1 Project Setup & Tooling** + + - [ ] Initialize Next.js 14+ project with TypeScript + - [ ] Configure pnpm workspace + - [ ] Setup ESLint, Prettier, and pre-commit hooks + - [ ] Configure Tailwind CSS with PostCSS + - [ ] Setup shadcn/ui components + - [ ] Configure absolute imports and path aliases + - [ ] **Deliverable:** Development environment ready + - [ ] **Dependencies:** None + +- **[ ] F0.2 Design System & UI Components** + + - [ ] Setup color palette and design tokens + - [ ] Create responsive design breakpoints + - [ ] Implement core shadcn/ui components: + - [ ] Button, Input, Label, Form + - [ ] Card, Table, Badge + - [ ] Dialog, Dropdown, Select + - [ ] Tabs, Accordion + - [ ] Create custom design system components: + - [ ] DataTable (responsive) + - [ ] FileUpload **zone** + - [ ] Workflow visualization + - [ ] **Deliverable:** Consistent UI component library + - [ ] **Dependencies:** F0.1 + +- **[ ] F0.3 API Client & Authentication** + + - [ ] Setup Axios client with interceptors: + - [ ] Idempotency-Key header injection + - [ ] Authentication token management + - [ ] Error handling and retry logic + - [ ] Configure NextAuth.js for JWT authentication + - [ ] Create auth hooks (useAuth, usePermissions) + - [ ] Setup API route handlers for auth callbacks + - [ ] **Security:** Implement secure token storage + - [ ] **Deliverable:** Secure API communication layer + - [ ] **Dependencies:** F0.1 + +- **[ ] F0.4 State Management Setup** + - [ ] Configure TanStack Query for server state: + - [ ] Query client setup + - [ ] Default configurations + - [ ] Error boundaries + - [ ] Create Zustand stores: + - [ ] Auth store (user, permissions) + - [ ] UI store (theme, sidebar state) + - [ ] Draft store (form auto-save) + - [ ] Setup React Hook Form with Zod integration + - [ ] Create form validation schemas + - [ ] **Deliverable:** Robust state management system + - [ ] **Dependencies:** F0.1, F0.3 + +### **Phase 0: Testing - Foundation** + +#### **F0.T1 Component Test Suite** + +- [ ] **Unit Tests:** Core UI components (Button, Input, Card) +- [ ] **Integration Tests:** Form validation, API client interceptors +- [ ] **Visual Tests:** Component styling and responsive behavior + +#### **F0.T2 Authentication Test Suite** + +- [ ] **Unit Tests:** Auth hooks, token management +- [ ] **Integration Tests:** Login/logout flow, permission checks +- [ ] **Security Tests:** Token security, API authentication + +--- + +### **Phase 1: Core Application Structure (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 2)** + +**Milestone:** Layout āļŦāļĨāļąāļāļžāļĢāđ‰āļ­āļĄāđƒāļŠāđ‰āļ‡āļēāļ™ āļāļēāļĢāļ™āļģāļ—āļēāļ‡āđāļĨāļ° Authentication āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰ + +### **Phase 1: Tasks** + +- **[ ] F1.1 Main Layout & Navigation** + + - [ ] Create App Shell layout: + - [ ] Navbar with user menu and notifications + - [ ] Collapsible sidebar with navigation + - [ ] Main content area with responsive design + - [ ] Implement navigation menu structure: + - [ ] Dashboard, Correspondences, RFAs, Drawings, etc. + - [ ] Dynamic menu based on user permissions + - [ ] Create breadcrumb navigation component + - [ ] Implement mobile-responsive sidebar (drawer) + - [ ] Maintenance Mode Integration: + - [ ] Implement a Global Middleware/Wrapper āļ—āļĩāđˆāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļ–āļēāļ™āļ° Maintenance Mode āļœāđˆāļēāļ™ API/Service āļāđˆāļ­āļ™āļāļēāļĢ Render āļŦāļ™āđ‰āļē āļŦāļēāļāļŠāļ–āļēāļ™āļ°āđ€āļ›āđ‡āļ™ true āđƒāļŦāđ‰ Redirect āļœāļđāđ‰āđƒāļŠāđ‰ (āļĒāļāđ€āļ§āđ‰āļ™ Admin) āđ„āļ›āļĒāļąāļ‡āļŦāļ™āđ‰āļē /maintenance āļ—āļąāļ™āļ—āļĩ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļš Logic āļ‚āļ­āļ‡ Backend. + - [ ] **Accessibility:** Ensure keyboard navigation and screen reader support + - [ ] **Deliverable:** Fully functional application layout + - [ ] **Dependencies:** F0.2, F0.3 + +- **[ ] F1.2 Authentication Pages** + + - [ ] Create login page with form validation + - [ ] Implement forgot password flow + - [ ] Create registration page (admin-only) + - [ ] Setup protected route middleware + - [ ] Implement route-based permission checks + - [ ] **Security:** Rate limiting on auth attempts, secure password requirements + - [ ] **Deliverable:** Complete authentication flow + - [ ] **Dependencies:** F0.3, F1.1 + +- **[ ] F1.3 Dashboard & Landing** + + - [ ] Create public landing page for non-authenticated users + - [ ] Implement main dashboard with: + - [ ] KPI cards (document counts, pending tasks) + - [ ] "My Tasks" table from v_user_tasks + - [ ] Recent activity feed + - [ ] Security metrics display + - [ ] Create dashboard widgets system + - [ ] Implement data fetching with TanStack Query + - [ ] **Performance:** Optimize dashboard data loading + - [ ] **Deliverable:** Functional dashboard with real data + - [ ] **Dependencies:** F0.4, F1.1 + +- **[ ] F1.4 Responsive Design System** + - [ ] Implement mobile-first responsive design + - [ ] Create card view components for mobile tables + - [ ] Setup touch-friendly interactions + - [ ] Optimize images and assets for mobile + - [ ] Test across multiple device sizes + - [ ] **UX:** Ensure seamless mobile experience + - [ ] **Deliverable:** Fully responsive application + - [ ] **Dependencies:** F0.2, F1.1 + +### **Phase 1: Testing - Core Structure** + +- **[ ] F1.T1 Layout Test Suite** + +- [ ] **Unit Tests:** Navigation components, layout responsiveness +- [ ] **Integration Tests:** Route protection, permission-based navigation +- [ ] **E2E Tests:** Complete user navigation flow + +- **[ ] F1.T2 Dashboard Test Suite** + +- [ ] **Unit Tests:** Dashboard components, data formatting +- [ ] **Integration Tests:** Data fetching and display, real-time updates +- [ ] **Performance Tests:** Dashboard loading performance + +--- + +### **Phase 2: User Management & Security (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 3)** + +**Milestone:** āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒāđāļšāļšāļŠāļĄāļšāļđāļĢāļ“āđŒ + +### **Phase 2: Tasks** + +- **[ ] F2.1 User Profile & Settings** + + - [ ] Create user profile page: + - [ ] Personal information display/edit + - [ ] Password change functionality + - [ ] Notification preferences + - [ ] Implement profile picture upload + - [ ] Create user settings page + - [ ] **Security:** Secure password change with current password verification + - [ ] **Deliverable:** Complete user self-service management + - [ ] **Dependencies:** F1.1, F0.4 + +- **[ ] F2.2 Admin Panel - User Management** + + - [ ] Create user list with search and filters + - [ ] Implement user creation form + - [ ] Create user edit interface + - [ ] Implement bulk user operations + - [ ] Add user activity tracking display + - [ ] **Security:** Admin-only access enforcement + - [ ] **Deliverable:** Comprehensive user management interface + - [ ] **Dependencies:** F1.1, F2.1 + +- **[ ] F2.3 Admin Panel - Role Management** + + - [ ] Create role list and management interface + - [ ] Implement role creation and editing + - [ ] Create permission assignment interface + - [ ] Implement role-based access control visualization + - [ ] Add role usage statistics + - [ ] **Security:** Permission hierarchy enforcement + - [ ] **Deliverable:** Complete RBAC management system + - [ ] **Dependencies:** F2.2 + +- **[ ] F2.4 Permission Integration** + - [ ] Implement CASL ability integration + - [ ] Create permission-based UI components: + - [ ] ProtectedButton, ProtectedLink + - [ ] Conditional rendering based on permissions + - [ ] Setup real-time permission updates + - [ ] Implement permission debugging tools + - [ ] **Security:** Frontend-backend permission consistency + - [ ] **Deliverable:** Seamless permission enforcement throughout app + - [ ] **Dependencies:** F0.3, F2.3 + +### **Phase 2: User Management & Admin Panel (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 3)** + +- **[ ] F2.5 Admin Panel - Master Data Management (Req 6B)** (New) + - [ ] Create "Disciplines Management" page (CRUD) + - [ ] Create "Sub-Types Management" page (CRUD + Mapping Number) + - [ ] Create "Numbering Format" configuration page (Template Editor) + - [ ] **Deliverable:** UI for Admin to configure system master data + - [ ] **Dependencies:** F2.1 + +### **Phase 2: Testing - User Management** + +- **[ ] F2.T1 User Management Test Suite** + +- [ ] **Unit Tests:** User CRUD operations, form validation +- [ ] **Integration Tests:** User-role assignment, permission propagation +- [ ] **Security Tests:** Permission escalation attempts, admin access control + +- **[ ] F2.T2 RBAC Test Suite** + +- [ ] **Unit Tests:** Permission checks, role validation +- [ ] **Integration Tests:** Multi-level permission enforcement, UI element protection +- [ ] **E2E Tests:** Complete role-based workflow testing + +--- + +### **Phase 3: Project Structure (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 4)** + +**Milestone:** āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒāđāļĨāļ°āļ­āļ‡āļ„āđŒāļāļĢ + +### **Phase 3: Tasks** + +- **[ ] F3.1 Project Management UI** + + - [ ] Create project list with search and filters + - [ ] Implement project creation and editing + - [ ] Create project detail view + - [ ] Implement project dashboard with statistics + - [ ] Add project member management + - [ ] **UX:** Intuitive project navigation and management + - [ ] **Deliverable:** Complete project management interface + - [ ] **Dependencies:** F1.1, F2.4 + +- **[ ] F3.2 Organization Management** + + - [ ] Create organization list and management + - [ ] Implement organization creation and editing + - [ ] Create organization detail view + - [ ] Add organization user management + - [ ] Implement organization hierarchy visualization + - [ ] **Business Logic:** Proper organization-project relationships + - [ ] **Deliverable:** Comprehensive organization management + - [ ] **Dependencies:** F3.1 + +- **[ ] F3.3 Contract Management** + - [ ] Create contract list within projects + - [ ] Implement contract creation and editing + - [ ] Create contract detail view + - [ ] Add contract party management + - [ ] Implement contract document associations + - [ ] **Business Logic:** Contract-project-organization relationships + - [ ] **Deliverable:** Complete contract management system + - [ ] **Dependencies:** F3.1, F3.2 + +### **Phase 3: Testing - Project Structure** + +- **[ ] F3.T1 Project Management Test Suite** + - [ ] **Unit Tests:** Project CRUD operations, validation + - [ ] **Integration Tests:** Project-organization relationships, member management + - [ ] **Business Logic Tests:** Project hierarchy, access control + +--- + +### **Phase 4: Correspondence System (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 5-6)** + +**Milestone:** āļĢāļ°āļšāļšāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļšāđāļšāļšāļŠāļĄāļšāļđāļĢāļ“āđŒ + +### **Phase 4: Tasks** + +- **[ ] F4.1 Correspondence List & Search** + + - [ ] Create correspondence list with advanced filtering: + - [ ] Filter by type, status, project, organization + - [ ] Search by title, document number, content + - [ ] Date range filtering + - [ ] Implement responsive data table: + - [ ] Desktop: Full table view + - [ ] Mobile: Card view conversion + - [ ] Add bulk operations (export, status change) + - [ ] Implement real-time updates + - [ ] **Performance:** Virtual scrolling for large datasets + - [ ] **Deliverable:** High-performance correspondence listing + - [ ] **Dependencies:** F1.1, F3.1 + +- **[ ] F4.2 Correspondence Creation Form** + + - [ ] Create dynamic form generator based on JSON schema + - [ ] Implement form with multiple sections: + - [ ] Basic information (type, title, recipients) + - [ ] Content and details (JSON schema-based) + - [ ] File attachments + - [ ] Routing template selection + - [ ] [New] Implement "Originator Selector" component: Dropdown āļŠāļģāļŦāļĢāļąāļšāđ€āļĨāļ·āļ­āļāļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡ (āđāļŠāļ”āļ‡āđ€āļ‰āļžāļēāļ°āđ€āļĄāļ·āđˆāļ­āļœāļđāđ‰āđƒāļŠāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ system.manage_all āļŦāļĢāļ·āļ­āļŠāļīāļ—āļ˜āļīāđŒāļžāļīāđ€āļĻāļĐ) āļŦāļēāļāđ„āļĄāđˆāđ€āļĨāļ·āļ­āļāđƒāļŦāđ‰āđƒāļŠāđ‰ Organization āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ•āļēāļĄāļ›āļāļ•āļī + - [ ] **[New] Discipline Selector:** Add Dropdown for Disciplines (Dependent on Contract/Project) + - [ ] **[New] Sub-Type Selector:** Add Dropdown for Sub-types (Dependent on Type) + - [ ] Logic: Show/Hide selectors based on Document Type configuration + - [ ] Add draft auto-save functionality + - [ ] Implement form validation with Zod + - [ ] **UX:** Intuitive form with progress indication + - [ ] **Deliverable:** Flexible correspondence creation interface + - [ ] **Dependencies:** F0.4, F4.1 + +- **[ ] F4.3 Correspondence Detail View** + + - [ ] Create comprehensive detail page: + - [ ] Document header with metadata + - [ ] Content display based on type + - [ ] Revision history + - [ ] Related documents + - [ ] Workflow status visualization + - [ ] Implement document actions: + - [ ] Edit, withdraw, cancel (with permissions) + - [ ] Download, print + - [ ] Create related documents + - [ ] Add comments and activity timeline + - [ ] **UX:** Clean, readable document presentation + - [ ] **Deliverable:** Complete document detail experience + - [ ] **Dependencies:** F4.1, F4.2 + +- **[ ] F4.4 File Upload Integration** + - [ ] Create drag-and-drop file upload component + - [ ] Implement file type validation and preview + - [ ] Add virus scan status indication + - [ ] Create file management interface: + - [ ] Mark files as main/supporting documents + - [ ] Reorder and manage attachments + - [ ] File security status display + - [ ] Implement two-phase upload progress + - [ ] **Security:** File type restrictions, size limits, virus scan integration + - [ ] **Deliverable:** Secure and user-friendly file management + - [ ] **Dependencies:** F0.2, F4.2 + +### **Phase 4: Testing - Correspondence System** + +- **[ ] F4.T1 Correspondence Test Suite** + + - [ ] **Unit Tests:** Form validation, file upload components + - [ ] **Integration Tests:** Complete document lifecycle, file attachment flow + - [ ] **E2E Tests:** End-to-end correspondence creation and management + +- **[ ] F4.T2 File Upload Test Suite** + - [ ] **Unit Tests:** File validation, type checking + - [ ] **Integration Tests:** Two-phase upload process, virus scan integration + - [ ] **Security Tests:** Malicious file upload attempts, security feedback + +--- + +### **Phase 5: Workflow Management (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 7)** + +**Milestone:** āļĢāļ°āļšāļš Visualization āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢ Workflow + +### **Phase 5: Tasks** + +- **[ ] F5.1 Workflow Visualization Component** + + - [ ] Create horizontal workflow progress visualization + - [ ] Implement step status indicators (pending, active, completed, skipped) + - [ ] Add due date and assignee information + - [ ] Create interactive workflow diagram + - [ ] Implement workflow history timeline + - [ ] **UX:** Clear visual representation of complex workflows + - [ ] **Deliverable:** Intuitive workflow visualization + - [ ] **Dependencies:** F4.3 + +- **[ ] F5.2 Routing Template Management** + + - [ ] Create routing template list and editor + - [ ] Implement drag-and-drop step configuration + - [ ] Add step configuration (purpose, duration, assignee rules) + - [ ] Create template preview functionality + - [ ] Implement template versioning + - [ ] **Business Logic:** Proper step sequencing and validation + - [ ] **Deliverable:** Comprehensive routing template management + - [ ] **Dependencies:** F3.1, F4.2 + +- **[ ] F5.3 Workflow Step Actions** + + - [ ] Create step action interface: + - [ ] Approve, reject, request changes + - [ ] Add comments and attachments + - [ ] Forward to other users + - [ ] Implement bulk step actions + - [ ] Add action confirmation with reason required + - [ ] Create step delegation functionality + - [ ] **UX:** Streamlined step completion process + - [ ] **Deliverable:** Efficient workflow step management + - [ ] **Dependencies:** F5.1 + +- **[ ] F5.4 Real-time Status Updates** + - [ ] Implement WebSocket connections for real-time updates + - [ ] Create status change notifications + - [ ] Add auto-refresh for workflow states + - [ ] Implement optimistic updates for better UX + - [ ] Create update history and audit trail + - [ ] **Performance:** Efficient real-time data synchronization + - [ ] **Deliverable:** Real-time workflow monitoring + - [ ] **Dependencies:** F5.1, F9.2 + +### **Phase 5: Testing - Workflow Management** + +- **[ ] F5.T1 Workflow Test Suite** + - [ ] **Unit Tests:** Workflow visualization, step status logic + - [ ] **Integration Tests:** Complete workflow execution, real-time updates + - [ ] **E2E Tests:** Multi-step workflow with different user roles + +--- + +### **Phase 6: Drawing System (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 8)** + +**Milestone:** āļĢāļ°āļšāļšāļˆāļąāļ”āļāļēāļĢāđāļšāļšāđāļ›āļĨāļ™āđāļšāļšāļŠāļĄāļšāļđāļĢāļ“āđŒ + +### **Phase 6: Tasks** + +- **[ ] F6.1 Contract Drawings Management** + + - [ ] Create contract drawing list with categorization + - [ ] Implement drawing upload and metadata management + - [ ] Create drawing preview and viewer + - [ ] Add drawing version control + - [ ] Implement drawing search and filtering + - [ ] **UX:** Efficient drawing navigation and access + - [ ] **Deliverable:** Comprehensive contract drawing management + - [ ] **Dependencies:** F3.1, F4.4 + +- **[ ] F6.2 Shop Drawings Management** + + - [ ] Create shop drawing list with revision tracking + - [ ] Implement shop drawing creation and revision system + - [ ] Create drawing comparison interface + - [ ] Add drawing approval status tracking + - [ ] Implement bulk drawing operations + - [ ] **Business Logic:** Proper revision control and approval workflows + - [ ] **Deliverable:** Complete shop drawing management system + - [ ] **Dependencies:** F6.1 + +- **[ ] F6.3 Drawing Revision System** + + - [ ] Create revision history interface + - [ ] Implement revision comparison functionality + - [ ] Add revision notes and change tracking + - [ ] Create revision approval workflow + - [ ] Implement revision rollback capability + - [ ] **UX:** Clear visualization of changes between revisions + - [ ] **Deliverable:** Robust drawing revision control + - [ ] **Dependencies:** F6.2 + +- **[ ] F6.4 Drawing References** + - [ ] Create drawing reference management + - [ ] Implement cross-drawing references + - [ ] Add reference validation and integrity checks + - [ ] Create reference visualization + - [ ] Implement reference impact analysis + - [ ] **Business Logic:** Maintain reference integrity during changes + - [ ] **Deliverable:** Comprehensive drawing reference system + - [ ] **Dependencies:** F6.2, F6.3 + +### **Phase 6: Testing - Drawing System** + +- **[ ] F6.T1 Drawing Management Test Suite** + - [ ] **Unit Tests:** Drawing CRUD operations, revision logic + - [ ] **Integration Tests:** Drawing approval workflows, reference management + - [ ] **E2E Tests:** Complete drawing lifecycle with revisions + +--- + +### **Phase 7: RFA System (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 9-10)** + +**Milestone:** āļĢāļ°āļšāļšāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļīāđāļšāļšāļŠāļĄāļšāļđāļĢāļ“āđŒāļžāļĢāđ‰āļ­āļĄ Dynamic Forms + +### **Phase 7: Tasks** + +- **[ ] F7.1 RFA List & Dashboard** + + - [ ] Create RFA dashboard with status overview + - [ ] Implement advanced RFA filtering and search + - [ ] Create RFA calendar view for deadlines + - [ ] Add RFA statistics and reporting + - [ ] Implement RFA bulk operations + - [ ] **UX:** Comprehensive RFA overview and management + - [ ] **Deliverable:** Complete RFA dashboard and listing + - [ ] **Dependencies:** F4.1, F5.1 + +- **[ ] F7.2 RFA Creation with Dynamic Forms** + + - [ ] Create RFA type-specific form generator + - [ ] Implement dynamic form fields based on RFA type: + - [ ] RFA_DWG: Shop drawing selection + - [ ] RFA_DOC: Document specifications + - [ ] RFA_MES: Method statement details + - [ ] RFA_MAT: Material specifications + - [ ] Add form validation with JSON schema + - [ ] Implement form data persistence and recovery + - [ ] **UX:** Intuitive form experience for complex RFA types + - [ ] Dynamic Form & Schema Validation: āļŠāļĢāđ‰āļēāļ‡ Component Dynamic Form Generator āļ—āļĩāđˆ: + - [ ] Fetch Schema: āļ”āļķāļ‡āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON Schema āļ—āļĩāđˆāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ•āļēāļĄ rfa_type āļˆāļēāļ Backend (āļ•āļēāļĢāļēāļ‡ json_schemas āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡āđƒāļŦāļĄāđˆ) āļāđˆāļ­āļ™āļāļēāļĢ Render Form. + - [ ] Client-side Validation: Implement AJV (Another JSON Schema Validator) āļŦāļĢāļ·āļ­āđ„āļĨāļšāļĢāļēāļĢāļĩāļ—āļĩāđˆāđ€āļ—āļĩāļĒāļšāđ€āļ—āđˆāļē āđ€āļžāļ·āđˆāļ­āļ—āļģ Client-side Validation āļšāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ JSON āļāđˆāļ­āļ™āļŠāđˆāļ‡ Submit. + - [ ] Implement dynamic form fields based on RFA type: RFA_DWG, RFA_DOC, RFA_MES, RFA_MAT. + - [ ] Add form data persistence and recovery. + - [ ] **[New] Discipline & Sub-Type Integration:** Ensure RFA forms capture these fields correctly for numbering generation. + - [ ] **Deliverable:** Flexible RFA creation system + - [ ] **Dependencies:** F4.2, F6.2 + +- **[ ] F7.3 RFA Workflow Integration** + + - [ ] Integrate RFA with unified workflow engine + - [ ] Create RFA-specific workflow steps and actions + - [ ] Implement RFA approval interface + - [ ] Add RFA workflow history and tracking + - [ ] Create RFA workflow templates + - [ ] **Business Logic:** Proper RFA approval sequencing and validation + - [ ] **Deliverable:** Seamless RFA workflow integration + - [ ] **Dependencies:** F5.1, F7.2 + +- **[ ] F7.4 RFA Approval Interface** + - [ ] Create RFA review and approval interface + - [ ] Implement side-by-side document comparison + - [ ] Add approval comments and attachments + - [ ] Create conditional approval workflows + - [ ] Implement approval delegation and escalation + - [ ] **UX:** Efficient approval process for technical reviews + - [ ] **Deliverable:** Comprehensive RFA approval system + - [ ] **Dependencies:** F7.1, F7.3 + +### **Phase 7: Testing - RFA System** + +- **[ ] F7.T1 RFA Test Suite** + - [ ] **Unit Tests:** RFA form generation, validation logic + - [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration + - [ ] **E2E Tests:** Multi-type RFA creation and approval workflows + +--- + +### **Phase 8: Internal Workflows (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 11)** + +**Milestone:** āļĢāļ°āļšāļšāđƒāļšāđ€āļ§āļĩāļĒāļ™āđāļĨāļ°āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‡āļēāļ™āļ āļēāļĒāđƒāļ™ + +### **Phase 8: Tasks** + +- **[ ] F8.1 Circulation Management** + + - [ ] Create circulation list and management interface + - [ ] Implement circulation creation from correspondence + - [ ] Create circulation template management + - [ ] Add circulation status tracking + - [ ] Implement circulation search and filtering + - [ ] **Business Logic:** Proper circulation-correspondence relationships + - [ ] **Deliverable:** Comprehensive circulation management + - [ ] **Dependencies:** F4.1, F5.2 + +- **[ ] F8.2 Task Assignment Interface** + + - [ ] Create task assignment interface with user selection + - [ ] Implement task priority and deadline setting + - [ ] Add task dependency management + - [ ] Create task progress tracking + - [ ] Implement task reassignment and delegation + - [ ] **UX:** Intuitive task management and assignment + - [ ] **Deliverable:** Efficient task assignment system + - [ ] **Dependencies:** F8.1 + +- **[ ] F8.3 Internal Approval Flows** + - [ ] Create internal approval workflow interface + - [ ] Implement multi-level approval processes + - [ ] Add approval chain visualization + - [ ] Create approval delegation system + - [ ] Implement approval deadline management + - [ ] **Business Logic:** Proper approval hierarchy and escalation + - [ ] **Deliverable:** Robust internal approval system + - [ ] **Dependencies:** F8.1, F8.2 + +### **Phase 8: Testing - Internal Workflows** + +- **[ ] F8.T1 Circulation Test Suite** + - [ ] **Unit Tests:** Circulation creation, task assignment logic + - [ ] **Integration Tests:** Complete circulation workflow, internal approvals + - [ ] **E2E Tests:** End-to-end circulation with task completion + +--- + +### **Phase 9: Advanced Features (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 12)** + +**Milestone:** āļŸāļĩāđ€āļˆāļ­āļĢāđŒāļ‚āļąāđ‰āļ™āļŠāļđāļ‡āđāļĨāļ°āļāļēāļĢāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ›āļĢāļ°āļŠāļšāļāļēāļĢāļ“āđŒāļœāļđāđ‰āđƒāļŠāđ‰ + +### **Phase 9: Tasks** + +- **[ ] F9.1 Advanced Search Interface** + + - [ ] Create unified search interface across all document types + - [ ] Implement faceted search with multiple filters + - [ ] Add search result highlighting and relevance scoring + - [ ] Create saved search and search templates + - [ ] Implement search result export functionality + - [ ] **Performance:** Efficient search with large datasets + - [ ] **Deliverable:** Powerful cross-document search system + - [ ] **Dependencies:** F4.1, F7.1 + +- **[ ] F9.2 Notification System** + + - [ ] Create notification center with real-time updates + - [ ] Implement notification preferences management + - [ ] Add notification grouping and digest views + - [ ] Create actionable notifications with quick actions + - [ ] Implement notification read/unread status + - [ ] **UX:** Non-intrusive but effective notification delivery + - [ ] **Deliverable:** Comprehensive notification management + - [ ] **Dependencies:** F1.3, F5.4 + +- **[ ] F9.3 Reporting & Analytics** + + - [ ] Create reporting dashboard with customizable widgets + - [ ] Implement data visualization components (charts, graphs) + - [ ] Add report scheduling and export + - [ ] Create ad-hoc reporting interface + - [ ] Implement performance metrics tracking + - [ ] **Business Logic:** Accurate data aggregation and reporting + - [ ] **Deliverable:** Powerful reporting and analytics system + - [ ] **Dependencies:** F1.3, F7.1 + +- **[ ] F9.4 Mobile Optimization** + - [ ] Implement touch-optimized interactions + - [ ] Create mobile-specific navigation patterns + - [ ] Add offline capability for critical functions + - [ ] Optimize images and assets for mobile networks + - [ ] Implement mobile-specific performance optimizations + - [ ] **UX:** Seamless mobile experience comparable to desktop + - [ ] **Deliverable:** Fully optimized mobile application + - [ ] **Dependencies:** F1.4 + +### **Phase 9: Testing - Advanced Features** + +- **[ ] F9.T1 Advanced Features Test Suite** + - [ ] **Unit Tests:** Search algorithms, notification logic + - [ ] **Integration Tests:** Cross-module search, real-time notifications + - [ ] **Performance Tests:** Search performance, mobile responsiveness + +--- + +### **Phase 10: Testing & Polish (āļŠāļąāļ›āļ”āļēāļŦāđŒāļ—āļĩāđˆ 13-14)** + +**Milestone:** āđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļ—āļĩāđˆāļœāđˆāļēāļ™āļāļēāļĢāļ—āļ”āļŠāļ­āļšāđāļĨāļ°āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ­āļĒāđˆāļēāļ‡āļŠāļĄāļšāļđāļĢāļ“āđŒ + +### **Phase 10: Tasks** + +- **[ ] F10.1 Comprehensive Testing** + + - [ ] Idempotency Testing: āđ€āļžāļīāđˆāļĄāļāļēāļĢāļ—āļ”āļŠāļ­āļšāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Axios Interceptor āđ€āļžāļ·āđˆāļ­āļˆāļģāļĨāļ­āļ‡āļāļēāļĢāļŠāđˆāļ‡ Request POST/PUT/DELETE āļ—āļĩāđˆāļĄāļĩ Idempotency-Key āļ‹āđ‰āļģāđ„āļ›āļĒāļąāļ‡ Mock API (MSW) āđ€āļžāļ·āđˆāļ­āļĒāļ·āļ™āļĒāļąāļ™āļ§āđˆāļē Client-side āđ„āļĄāđˆāļŠāđˆāļ‡ Key āļ‹āđ‰āļģāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ›āļāļ•āļī āđāļĨāļ°āđ„āļĄāđˆāđ€āļāļīāļ” Side Effect āļˆāļēāļāļāļēāļĢ Replay Attack. + - [ ] Write unit tests for all components and utilities + - [ ] Create integration tests for critical user flows + - [ ] Implement E2E tests for complete workflows + - [ ] Perform cross-browser compatibility testing + - [ ] Conduct accessibility testing (WCAG 2.1 AA) + - [ ] **Quality:** 80%+ test coverage, all critical paths tested + - [ ] **Deliverable:** Fully tested application + - [ ] **Dependencies:** All previous phases + +- **[ ] F10.2 Performance Optimization** + + - [ ] Implement code splitting and lazy loading + - [ ] Optimize bundle size and asset delivery + - [ ] Add performance monitoring and metrics + - [ ] Implement caching strategies for static assets + - [ ] Optimize API call patterns and reduce over-fetching + - [ ] **Performance:** Core Web Vitals targets met + - [ ] **Deliverable:** High-performance application + - [ ] **Dependencies:** F10.1 + +- **[ ] F10.3 Security Hardening** + + - [ ] Conduct security audit and penetration testing + - [ ] Implement Content Security Policy (CSP) + - [ ] Add security headers and protections + - [ ] Conduct dependency vulnerability scanning + - [ ] Implement secure coding practices review + - [ ] **Security:** No critical security vulnerabilities + - [ ] **Deliverable:** Security-hardened application + - [ ] **Dependencies:** F10.1 + +- **[ ] F10.4 Documentation** + - [ ] Create user documentation and guides + - [ ] Write technical documentation for developers + - [ ] Create API integration documentation + - [ ] Add inline code documentation + - [ ] Create deployment and maintenance guides + - [ ] **Quality:** Comprehensive and up-to-date documentation + - [ ] **Deliverable:** Complete documentation suite + - [ ] **Dependencies:** F10.1 + +### **Phase 10: Testing - Final Validation** + +- **[ ] F10.T1 Final Test Suite** + - [ ] **Performance Tests:** Load testing, stress testing + - [ ] **Security Tests:** Final security audit, vulnerability assessment + - [ ] **User Acceptance Tests:** Real user testing, feedback incorporation + - [ ] **Compatibility Tests:** Cross-browser, cross-device testing + +--- + +## 📊 **āļŠāļĢāļļāļ› Timeline** + +| Phase | āļĢāļ°āļĒāļ°āđ€āļ§āļĨāļē | āļˆāļģāļ™āļ§āļ™āļ‡āļēāļ™ | Output āļŦāļĨāļąāļ | +| -------- | -------------- | ------------ | ------------------------------------ | +| Phase 0 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Foundation & Tooling Ready | +| Phase 1 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Core Application Structure | +| Phase 2 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | User Management & Security | +| Phase 3 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 3 | Project Structure Management | +| Phase 4 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Correspondence System | +| Phase 5 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Workflow Management | +| Phase 6 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Drawing System | +| Phase 7 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | RFA System (Dynamic Forms) | +| Phase 8 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 3 | Internal Workflows | +| Phase 9 | 1 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Advanced Features | +| Phase 10 | 2 āļŠāļąāļ›āļ”āļēāļŦāđŒ | 4 | Testing & Polish (Idempotency Test) | +| **āļĢāļ§āļĄ** | **14 āļŠāļąāļ›āļ”āļēāļŦāđŒ** | **39 Tasks** | **Production-Ready Frontend v1.4.2** | + +--- + +## ðŸŽŊ **Critical Success Factors** + +1. **User Experience First:** āļ—āļļāļāļŸāļĩāđ€āļˆāļ­āļĢāđŒāļ•āđ‰āļ­āļ‡āļ­āļ­āļāđāļšāļšāđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļšāļāļēāļĢāļ“āđŒāļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļ”āļĩ +2. **Responsive Design:** āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļšāļ™āļ­āļļāļ›āļāļĢāļ“āđŒāļ—āļļāļāļĢāļđāļ›āđāļšāļš +3. **Performance:** Core Web Vitals āļ•āđ‰āļ­āļ‡āļ­āļĒāļđāđˆāđƒāļ™āđ€āļāļ“āļ‘āđŒāļ—āļĩāđˆāļ”āļĩ +4. **Accessibility:** āļ•āđ‰āļ­āļ‡āđ€āļ›āđ‡āļ™āđ„āļ›āļ•āļēāļĄāļĄāļēāļ•āļĢāļāļēāļ™ WCAG 2.1 AA +5. **Security:** āļ›āđ‰āļ­āļ‡āļāļąāļ™ XSS, CSRF āđāļĨāļ°āļ„āļ§āļēāļĄāđ€āļŠāļĩāđˆāļĒāļ‡āļ”āđ‰āļēāļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļ­āļ·āđˆāļ™āđ† +6. **Offline Support:** āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ—āļģāļ‡āļēāļ™āđāļšāļš Offline āđ€āļšāļ·āđ‰āļ­āļ‡āļ•āđ‰āļ™ +7. **Real-time Updates:** āļāļēāļĢāļ­āļąāļ›āđ€āļ”āļ•āļŠāļ–āļēāļ™āļ°āđāļšāļš Real-time +8. **Testing Coverage:** āļ„āļĢāļ­āļšāļ„āļĨāļļāļĄāļāļēāļĢāļ—āļ”āļŠāļ­āļšāļ—āļļāļ Critical Path +9. **Documentation:** āđ€āļ­āļāļŠāļēāļĢāļ„āļĢāļšāļ–āđ‰āļ§āļ™āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āđāļĨāļ°āļ™āļąāļāļžāļąāļ’āļ™āļē + +--- + +## 📋 **Quality Assurance Checklist** + +### **āļāđˆāļ­āļ™ Production Deployment** + +- [ ] **Performance:** Core Web Vitals āļœāđˆāļēāļ™āđ€āļāļ“āļ‘āđŒ +- [ ] **Accessibility:** WCAG 2.1 AA compliant +- [ ] **Security:** Security audit āļœāđˆāļēāļ™ +- [ ] **Testing:** Test coverage â‰Ĩ 80% +- [ ] **Browser Compatibility:** āļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļšāļ™āđ€āļšāļĢāļēāļ§āđŒāđ€āļ‹āļ­āļĢāđŒāļŦāļĨāļąāļ +- [ ] **Mobile Responsive:** āđƒāļŠāđ‰āļ‡āļēāļ™āđ„āļ”āđ‰āļ”āļĩāļšāļ™āļĄāļ·āļ­āļ–āļ·āļ­ +- [ ] **Documentation:** āđ€āļ­āļāļŠāļēāļĢāļ„āļĢāļšāļ–āđ‰āļ§āļ™ +- [ ] **User Acceptance:** āđ„āļ”āđ‰āļĢāļąāļšāļāļēāļĢāļĒāļ­āļĄāļĢāļąāļšāļˆāļēāļāļœāļđāđ‰āđƒāļŠāđ‰ + +--- + +## 🚀 **āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ–āļąāļ”āđ„āļ›** + +1. **Approve āđāļœāļ™āļ™āļĩāđ‰** → āļ›āļĢāļąāļšāđāļ•āđˆāļ‡āļ•āļēāļĄ Feedback +2. **Coordinate āļāļąāļš Backend Team** → Sync API Specifications +3. **āđ€āļĢāļīāđˆāļĄāļžāļąāļ’āļ™āļē Phase 0** → Setup Foundation +4. **Regular Sync** → āļ›āļĢāļ°āļŠāļēāļ™āļ‡āļēāļ™āļāļąāļš Backend āļ—āļļāļāļŠāļąāļ›āļ”āļēāļŦāđŒ +5. **User Testing** → āļ—āļ”āļŠāļ­āļšāļāļąāļšāļœāļđāđ‰āđƒāļŠāđ‰āļˆāļĢāļīāļ‡āļĢāļ°āļŦāļ§āđˆāļēāļ‡āļžāļąāļ’āļ™āļē +6. **Deploy to Production** → Week 15 (āļžāļĢāđ‰āļ­āļĄ Backend) + +## **Document Control:** + +- **Document:** Frontend Development Plan v1.4.5 +- **Version:** 1.4 +- **Date:** 2025-11-29 +- **Author:** NAP LCBP3-DMS & Gemini +- **Status:** FINAL-Rev.05 +- **Classification:** Internal Technical Documentation +- **Approved By:** Nattanin + +--- + +`End of Frontend Development Plan v1.4.5` diff --git a/4_Data_Dictionary_V1_4_5.md b/4_Data_Dictionary_V1_4_5.md new file mode 100644 index 0000000..40a224a --- /dev/null +++ b/4_Data_Dictionary_V1_4_5.md @@ -0,0 +1,2673 @@ +# **āļ•āļēāļĢāļēāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Dictionary) - LCBP3-DMS (V1.4.5)** + +āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļŠāļĢāļļāļ›āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ•āļēāļĢāļēāļ‡, Foreign Keys (FK), āđāļĨāļ° Constraints āļ—āļĩāđˆāļŠāļģāļ„āļąāļāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ LCBP3-DMS (v1.4) āđ€āļžāļ·āđˆāļ­āđƒāļŠāđ‰āđ€āļ›āđ‡āļ™āđ€āļ­āļāļŠāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļŠāļģāļŦāļĢāļąāļšāļ—āļĩāļĄāļžāļąāļ’āļ™āļē Backend (NestJS) āđāļĨāļ° Frontend (Next.js) āđ‚āļ”āļĒāļ­āļīāļ‡āļˆāļēāļ Requirements āđāļĨāļ° SQL Script āļĨāđˆāļēāļŠāļļāļ” [GEMINI] + +**āļŠāļ–āļēāļ™āļ°:** FINAL GUIDELINE +**āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-29 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡:** Requirements v1.4.5 & FullStackJS Guidelines v1.4.5 +**Classification:** Internal Technical Documentation + +## 📝 āļŠāļĢāļļāļ›āļĢāļēāļĒāļāļēāļĢāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡ (Summary of Changes) + +1. **āđ€āļžāļīāđˆāļĄāļ•āļēāļĢāļēāļ‡ `disciplines`**: āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļēāļ‚āļēāļ‡āļēāļ™ (āđ€āļŠāđˆāļ™ GEN, STR, ARC) +2. **āđ€āļžāļīāđˆāļĄāļ•āļēāļĢāļēāļ‡ `correspondence_sub_types`**: āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļĢāļŦāļąāļŠāļĒāđˆāļ­āļĒ (āđ€āļŠāđˆāļ™ MAT-11, SHP-12) +3. **āđāļāđ‰āđ„āļ‚āļ•āļēāļĢāļēāļ‡ `correspondences`**: āđ€āļžāļīāđˆāļĄ Column `discipline_id` +4. **āđāļāđ‰āđ„āļ‚āļ•āļēāļĢāļēāļ‡ `rfas`**: āđ€āļžāļīāđˆāļĄ Column `discipline_id` +5. **āđāļāđ‰āđ„āļ‚āļ•āļēāļĢāļēāļ‡ `document_number_counters`**: āđ€āļžāļīāđˆāļĄ `discipline_id` āđƒāļ™ Primary Key +6. Seeded data for disciplines and RFA types based on Requirement 6B. + +--- + +## **1. ðŸĒ Core & Master Data Tables (āļ­āļ‡āļ„āđŒāļāļĢ, āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļŠāļąāļāļāļē)** + +### 1.1 organization_roles + +**Purpose**: Master table for organization role types in the system + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | ---------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for organization role | +| role_name | VARCHAR(20) | NOT NULL, UNIQUE | Role name (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY) | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (role_name) + +**Business Rules**: + +- Predefined system roles for organization types +- Cannot be deleted if referenced by organizations + +--- + +### 1.2 organizations + +**Purpose**: Master table storing all organizations involved in the system + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | ----------------------------------- | ---------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for organization | +| organization_code | VARCHAR(20) | NOT NULL, UNIQUE | Organization code (e.g., 'āļāļ—āļ—.', 'TEAM') | +| organization_name | VARCHAR(255) | NOT NULL | Full organization name | +| is_active | BOOLEAN | DEFAULT TRUE | Active status (1=active, 0=inactive) | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (organization_code) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: users, project_organizations, contract_organizations, correspondences, circulations + +**Seed Data**: Pre-populated with 15 organizations including: + +- Port Authority of Thailand (āļāļ—āļ—.) +- Project supervision consultants (āļŠāļ„ÂĐ.3-xx) +- Design consultants (TEAM) +- Construction supervisors (āļ„āļ„āļ‡.) +- Contractors (āļœāļĢāļĄ.1-4) +- Third parties (EN, CAR) + +--- + +### 1.3 projects + +**Purpose**: Master table for all projects in the system + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | --------------------------- | ----------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for project | +| project_code | VARCHAR(50) | NOT NULL, UNIQUE | Project code (e.g., 'LCBP3') | +| project_name | VARCHAR(255) | NOT NULL | Full project name | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (project_code) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: contracts, correspondences, document_number_formats, drawings + +**Seed Data**: 5 projects for Laem Chabang Port Phase 3 (LCBP3) including main project and 4 sub-contracts + +--- + +### 1.4 contracts + +**Purpose**: Master table for contracts within projects + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | ----------------------------------- | ------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for contract | +| project_id | INT | NOT NULL, FK | Reference to projects table | +| contract_code | VARCHAR(50) | NOT NULL, UNIQUE | Contract code | +| contract_name | VARCHAR(255) | NOT NULL | Full contract name | +| description | TEXT | NULL | Contract description | +| start_date | DATE | NULL | Contract start date | +| end_date | DATE | NULL | Contract end date | +| is_active | BOOLEAN | DEFAULT TRUE | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (contract_code) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- INDEX (project_id, is_active) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_organizations, user_assignments + +**Seed Data**: 7 contracts including design, supervision, construction, and environmental monitoring + +--- + +## **2. ðŸ‘Ĩ Users & RBAC Tables (āļœāļđāđ‰āđƒāļŠāđ‰, āļŠāļīāļ—āļ˜āļīāđŒ, āļšāļ—āļšāļēāļ—)** + +### 2.1 users + +**Purpose**: Master table storing all system users + +| Column Name | Data Type | Constraints | Description | +| ----------------------- | ------------ | ----------------------------------- | -------------------------------- | +| user_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for user | +| username | VARCHAR(50) | NOT NULL, UNIQUE | Login username | +| password_hash | VARCHAR(255) | NOT NULL | Hashed password (bcrypt) | +| first_name | VARCHAR(50) | NULL | User's first name | +| last_name | VARCHAR(50) | NULL | User's last name | +| email | VARCHAR(100) | NOT NULL, UNIQUE | Email address | +| line_id | VARCHAR(100) | NULL | LINE messenger ID | +| primary_organization_id | INT | NULL, FK | Primary organization affiliation | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| failed_attempts | INT | DEFAULT 0 | Failed login attempts counter | +| locked_until | DATETIME | NULL | Account lock expiration time | +| last_login_at | TIMESTAMP | NULL | Last successful login timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| deleted_at | DATETIME | NULL | Deleted at | + +**Indexes**: + +- PRIMARY KEY (user_id) +- UNIQUE (username) +- UNIQUE (email) +- FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +- INDEX (is_active) +- INDEX (email) + +**Relationships**: + +- Parent: organizations (primary_organization_id) +- Referenced by: user_assignments, audit_logs, notifications, circulation_routings + +**Security Features**: + +- Password stored as bcrypt hash +- Account locking after failed attempts +- Last login tracking + +**Seed Data**: 3 initial users (superadmin, editor01, viewer01) + +--- + +### 2.2 roles + +**Purpose**: Master table defining system roles with scope levels + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ---------------------------------------------------- | +| role_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for role | +| role_name | VARCHAR(100) | NOT NULL | Role name (e.g., 'Superadmin', 'Document Control') | +| scope | ENUM | NOT NULL | Scope level: Global, Organization, Project, Contract | +| description | TEXT | NULL | Role description | +| is_system | BOOLEAN | DEFAULT FALSE | System role flag (cannot be deleted) | + +**Indexes**: + +- PRIMARY KEY (role_id) +- INDEX (scope) + +**Relationships**: + +- Referenced by: role_permissions, user_assignments + +**Seed Data**: 7 predefined roles + +1. Superadmin (Global) - Full system access +2. Org Admin (Organization) - Organization management +3. Document Control (Organization) - Document lifecycle management +4. Editor (Organization) - Document creation and editing +5. Viewer (Organization) - Read-only access +6. Project Manager (Project) - Project-level management +7. Contract Admin (Contract) - Contract-specific administration + +--- + +### 2.3 permissions + +**Purpose**: Master table defining all system permissions + +| Column Name | Data Type | Constraints | Description | +| --------------- | ------------ | --------------------------- | ------------------------------------------------------ | +| permission_id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier for permission | +| permission_name | VARCHAR(100) | NOT NULL, UNIQUE | Permission code (e.g., 'rfas.create', 'document.view') | +| description | TEXT | NULL | Permission description | +| module | VARCHAR(50) | NULL | Related module name | +| scope_level | ENUM | NULL | Scope: GLOBAL, ORG, PROJECT | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (permission_id) +- UNIQUE (permission_name) +- INDEX (module) +- INDEX (scope_level) +- INDEX (is_active) + +**Relationships**: + +- Referenced by: role_permissions + +**Permission Categories**: + +1. **System Management** (1): system.manage_all +2. **Organization Management** (2-5): create, edit, delete, view +3. **Project Management** (6-9, 23-26): create, edit, delete, view, manage members/contracts/reports +4. **Role & Permission Management** (10-13): create, edit, delete roles, assign permissions +5. **Master Data Management** (14-17): document types, statuses, categories, tags +6. **User Management** (18-22): create, edit, delete, view, assign organization +7. **Contract Management** (27-28): manage members, view +8. **Document Management** (29-44): CRUD operations, workflows, circulation +9. **Search & Reporting** (48-49): advanced search, generate reports + +**Total Permissions**: 49 + +--- + +### 2.4 role_permissions + +**Purpose**: Junction table mapping roles to permissions (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------- | --------- | --------------- | ------------------------------ | +| role_id | INT | PRIMARY KEY, FK | Reference to roles table | +| permission_id | INT | PRIMARY KEY, FK | Reference to permissions table | + +**Indexes**: + +- PRIMARY KEY (role_id, permission_id) +- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE +- FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE +- INDEX (permission_id) + +**Relationships**: + +- Parent: roles, permissions + +**Seed Data**: Complete permission mappings for all 7 roles + +- Superadmin: All 49 permissions +- Org Admin: 15 permissions (user/org/tag management, view access) +- Document Control: 26 permissions (full document lifecycle) +- Editor: 12 permissions (document CRUD without admin powers) +- Viewer: 2 permissions (view and search only) +- Project Manager: 23 permissions (project management + document CRUD) +- Contract Admin: 15 permissions (contract management + document CRUD) + +--- + +### 2.5 user_assignments + +**Purpose**: Junction table assigning users to roles with scope context + +| Column Name | Data Type | Constraints | Description | +| ------------------- | --------- | --------------------------- | ---------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| user_id | INT | NOT NULL, FK | Reference to users table | +| role_id | INT | NOT NULL, FK | Reference to roles table | +| organization_id | INT | NULL, FK | Organization scope (if applicable) | +| project_id | INT | NULL, FK | Project scope (if applicable) | +| contract_id | INT | NULL, FK | Contract scope (if applicable) | +| assigned_by_user_id | INT | NULL, FK | User who made the assignment | +| assigned_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Assignment timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +- FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id) +- INDEX (user_id, role_id) +- INDEX (organization_id) +- INDEX (project_id) +- INDEX (contract_id) + +**Constraints**: + +- CHECK: Only one scope field (organization_id, project_id, contract_id) can be NOT NULL, or all NULL for Global scope + +**Relationships**: + +- Parent: users, roles, organizations, projects, contracts + +**Business Rules**: + +- User can have multiple role assignments with different scopes +- Scope inheritance: Contract → Project → Organization → Global +- Global scope: all scope fields are NULL + +--- + +### 2.6 project_organizations + +**Purpose**: Junction table linking projects to participating organizations (M:N) + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | --------------- | -------------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects table | +| organization_id | INT | PRIMARY KEY, FK | Reference to organizations table | + +**Indexes**: + +- PRIMARY KEY (project_id, organization_id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- INDEX (organization_id) + +**Relationships**: + +- Parent: projects, organizations + +**Seed Data**: Pre-populated with project-organization relationships + +--- + +### 2.7 contract_organizations + +**Purpose**: Junction table linking contracts to participating organizations with roles (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | ------------ | --------------- | ------------------------------------------------------------------------- | +| contract_id | INT | PRIMARY KEY, FK | Reference to contracts table | +| organization_id | INT | PRIMARY KEY, FK | Reference to organizations table | +| role_in_contract | VARCHAR(100) | NULL | Organization's role in contract (Owner, Designer, Consultant, Contractor) | + +**Indexes**: + +- PRIMARY KEY (contract_id, organization_id) +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- INDEX (organization_id) +- INDEX (role_in_contract) + +**Relationships**: + +- Parent: contracts, organizations + +**Seed Data**: Pre-populated with contract-organization-role relationships + +--- + +## **3. ✉ïļ Correspondences Tables (āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ, Revisions, Workflows)** + +### 3.1 correspondence_types + +**Purpose**: Master table for correspondence document types + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | --------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| type_code | VARCHAR(50) | NOT NULL, UNIQUE | Type code (e.g., 'RFA', 'RFI', 'TRANSMITTAL') | +| type_name | VARCHAR(255) | NOT NULL | Full type name | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (type_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: correspondences, document_number_formats, document_number_counters + +**Seed Data**: 10 correspondence types including RFA, RFI, TRANSMITTAL, EMAIL, INSTRUCTION, LETTER, MEMO, MOM, NOTICE, OTHER + +--- + +### 3.2 correspondence_status + +**Purpose**: Master table for correspondence status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| status_code | VARCHAR(50) | NOT NULL, UNIQUE | Status code (e.g., 'DRAFT', 'SUBOWN') | +| status_name | VARCHAR(255) | NOT NULL | Full status name | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (status_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: correspondence_revisions + +**Seed Data**: 23 status codes covering draft, submission, reply, resubmission, closure, and cancellation states by different parties (Owner, Designer, CSC, Contractor) + +--- + +### 3.3 correspondences + +**Purpose**: Master table for correspondence documents (non-revisioned data) + +| Column Name | Data Type | Constraints | Description | +| ------------------------- | ------------ | --------------------------- | -------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master correspondence ID | +| correspondence_number | VARCHAR(100) | NOT NULL | Document number (from numbering system) | +| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types | +| **discipline_id** | **INT** | **NULL, FK** | **Reference to disciplines (if applicable)** | +| 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 + +**Business Rules**: + +- One correspondence can have multiple revisions +- Correspondence number must be unique within a project +- Soft delete preserves history + +--- + +### 3.4 correspondence_revisions + +**Purpose**: Child table storing revision history of correspondences (1:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | ------------ | --------------------------------- | ------------------------------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| correspondence_id | INT | NOT NULL, FK | Master correspondence ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) | +| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag | +| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision | +| title | VARCHAR(255) | NOT NULL | Document title | +| document_date | DATE | NULL | Document date | +| issued_date | DATETIME | NULL | Issue date | +| received_date | DATETIME | NULL | Received date | +| due_date | DATETIME | NULL | Due date for response | +| description | TEXT | NULL | Revision description | +| details | JSON | NULL | Type-specific details (e.g., RFI questions) | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | +| created_by | INT | NULL, FK | User who created revision | +| updated_by | INT | NULL, FK | User who last updated | +| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Project ID āļˆāļēāļ JSON details āđ€āļžāļ·āđˆāļ­āļ—āļģ Index | +| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Type āļˆāļēāļ JSON details | +| details | JSON | NULL | Type-specific details (e.g., RFI questions) | +| 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) - Only one current revision per correspondence +- INDEX (correspondence_status_id) +- INDEX (is_current) +- INDEX (document_date) +- INDEX (issued_date) +- INDEX (v_ref_project_id) +- INDEX (v_ref_type) +- INDEX (v_doc_subtype) + +- **Relationships**: + +- Parent: correspondences, correspondence_status, users + +**Business Rules**: + +- Only one revision can be marked as current (is_current=TRUE) per correspondence +- Revision numbers are sequential starting from 0 +- JSON details field allows type-specific data storage + +--- + +### 3.5 correspondence_recipients + +**Purpose**: Junction table for correspondence recipients (TO/CC) (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------- | ---------------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| recipient_organization_id | INT | PRIMARY KEY, FK | Recipient organization | +| recipient_type | ENUM('TO', 'CC') | PRIMARY KEY | Recipient type | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type) +- FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT +- INDEX (recipient_organization_id) +- INDEX (recipient_type) + +**Relationships**: + +- Parent: correspondences, organizations + +**Business Rules**: + +- One organization can be both TO and CC recipient +- Cascade delete when correspondence is deleted + +--- + +### 3.6 tags + +**Purpose**: Master table for document tagging system + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique tag ID | +| tag_name | VARCHAR(100) | NOT NULL, UNIQUE | Tag name | +| description | TEXT | NULL | Tag description | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (tag_name) +- INDEX (tag_name) - For autocomplete + +**Relationships**: + +- Referenced by: correspondence_tags + +--- + +### 3.7 correspondence_tags + +**Purpose**: Junction table linking correspondences to tags (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| tag_id | INT | PRIMARY KEY, FK | Reference to tags | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, tag_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE +- INDEX (tag_id) + +**Relationships**: + +- Parent: correspondences, tags + +--- + +### 3.8 correspondence_references + +**Purpose**: Junction table for cross-referencing correspondences (M:N) + +| Column Name | Data Type | Constraints | Description | +| --------------------- | --------- | --------------- | ------------------------------------- | +| src_correspondence_id | INT | PRIMARY KEY, FK | Source correspondence ID | +| tgt_correspondence_id | INT | PRIMARY KEY, FK | Target (referenced) correspondence ID | + +**Indexes**: + +- PRIMARY KEY (src_correspondence_id, tgt_correspondence_id) +- FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- INDEX (tgt_correspondence_id) + +**Relationships**: + +- Parent: correspondences (both sides) + +**Business Rules**: + +- Allows tracing document relationships +- Bi-directional references should be created as separate records + +--- + +## **4. 📐 approval: RFA Tables (āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī, Workflows)** + +### 4.1 rfa_types + +**Purpose**: Master table for RFA (Request for Approval) types + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | ------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| type_code | VARCHAR(20) | NOT NULL, UNIQUE | Type code (DWG, DOC, MAT, etc.) | +| type_name | VARCHAR(100) | NOT NULL | Full type name | +| description | TEXT | NULL | Type description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (type_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfas + +**Seed Data**: 11 RFA types including Shop Drawing (DWG), Document (DOC), Specification (SPC), Calculation (CAL), Test Report (TRP), Survey Report (SRY), QA/QC Document, Method Statement (MES), Material (MAT), As-Built (ASB), Other (OTH) + +--- + +### 4.2 rfa_status_codes + +**Purpose**: Master table for RFA status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | --------------------------- | --------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| status_code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (DFT, FAP, FRE, etc.) | +| status_name | VARCHAR(100) | NOT NULL | Full status name | +| description | TEXT | NULL | Status description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (status_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfa_revisions + +**Seed Data**: 7 status codes + +- DFT: Draft +- FAP: For Approve +- FRE: For Review +- FCO: For Construction +- ASB: AS-Built +- OBS: Obsolete +- CC: Canceled + +--- + +### 4.3 rfa_approve_codes + +**Purpose**: Master table for RFA approval result codes + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | --------------------------- | -------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| approve_code | VARCHAR(20) | NOT NULL, UNIQUE | Approval code (1A, 1C, 3R, etc.) | +| approve_name | VARCHAR(100) | NOT NULL | Full approval name | +| description | TEXT | NULL | Code description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (approve_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: rfa_revisions + +**Seed Data**: 8 approval codes + +- 1A: Approved by Authority +- 1C: Approved by CSC +- 1N: Approved As Note +- 1R: Approved with Remarks +- 3C: Consultant Comments +- 3R: Revise and Resubmit +- 4X: Reject +- 5N: No Further Action + +--- + +### 4.4 rfas + +**Purpose**: Master table for RFA documents (non-revisioned data) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------------------- | -------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master RFA ID | +| rfa_type_id | INT | NOT NULL, FK | Reference to rfa_types | +| **discipline_id** | **INT** | **NULL, FK** | **Reference to disciplines (if applicable)** | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| created_by | INT | NULL, FK | User who created the record | +| deleted_at | DATETIME | NULL | Soft delete timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id) +- **FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE SET NULL** +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- INDEX (rfa_type_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: rfa_types, **disciplines**, users +- Children: rfa_revisions + +**Business Rules**: + +- One RFA can have multiple revisions +- Soft delete preserves history + +--- + +### 4.5 rfa_revisions + +**Purpose**: Child table storing revision history of RFAs (1:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------- | ------------ | --------------------------------- | --------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) | +| rfa_id | INT | NOT NULL, FK | Master RFA ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) | +| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag | +| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status | +| rfa_approve_code_id | INT | NULL, FK | Approval result code | +| title | VARCHAR(255) | NOT NULL | RFA title | +| document_date | DATE | NULL | Document date | +| issued_date | DATE | NULL | Issue date for approval | +| received_date | DATETIME | NULL | Received date | +| approved_date | DATE | NULL | Approval date | +| description | TEXT | NULL | Revision description | +| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | +| created_by | INT | NULL, FK | User who created revision | +| updated_by | INT | NULL, FK | User who last updated | +| 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 (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE +- FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id) +- FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL +- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL +- FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL +- UNIQUE KEY (rfa_id, revision_number) +- UNIQUE KEY (rfa_id, is_current) +- INDEX (rfa_status_code_id) +- INDEX (rfa_approve_code_id) +- INDEX (is_current) +- INDEX (v_ref_drawing_count): āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āļāļēāļĢ Index āļ‚āđ‰āļ­āļĄāļđāļĨāļ•āļąāļ§āđ€āļĨāļ‚āđƒāļ™ JSON + +**Relationships**: + +- Parent: correspondences, rfas, rfa_status_codes, rfa_approve_codes, users +- Children: rfa_items, rfa_workflows + +**Business Rules**: + +- RFA is a specialized type of correspondence +- Only one revision can be current per RFA +- Links to shop drawings through rfa_items + +--- + +### 4.6 rfa_items + +**Purpose**: Junction table linking RFA revisions to shop drawing revisions (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ------------------------------ | +| rfarev_correspondence_id | INT | PRIMARY KEY, FK | RFA revision correspondence ID | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Shop drawing revision ID | + +**Indexes**: + +- PRIMARY KEY (rfarev_correspondence_id, shop_drawing_revision_id) +- FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- INDEX (shop_drawing_revision_id) + +**Relationships**: + +- Parent: rfa_revisions, shop_drawing_revisions + +**Business Rules**: + +- Used primarily for RFA type = 'DWG' (Shop Drawing) +- One RFA can contain multiple shop drawings +- One shop drawing can be referenced by multiple RFAs + +--- + +**Purpose**: Transaction log table tracking actual RFA approval workflow execution + +| Column Name | Data Type | Constraints | Description | +| --------------- | --------- | ----------------------------------- | ---------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID | +| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision | +| step_number | INT | NOT NULL | Current step number | +| organization_id | INT | NOT NULL, FK | Organization responsible | +| assigned_to | INT | NULL, FK | Assigned user ID | +| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE | +| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED | +| comments | TEXT | NULL | Comments/remarks | +| completed_at | DATETIME | NULL | Completion timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| state_context | JSON\* | NULL | āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ Context āļ‚āļ­āļ‡ Workflow āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™ (Snapshot) | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (assigned_to) REFERENCES users(user_id) +- INDEX (rfa_revision_id, step_number) +- INDEX (assigned_to, status) +- INDEX (status) + +**Relationships**: + +- Parent: rfa_revisions, organizations, users + +**Business Rules**: + +- Records actual workflow execution history +- Tracks who did what and when +- Multiple records per RFA revision (one per step) +- Status changes tracked via updated_at + +### 4.7 disciplines + +**Purpose**: Master table for Project Disciplines (āļŠāļēāļ‚āļēāļ‡āļēāļ™) [Added in v1.4.4 based on Req 6B] + +| Column Name | Data Type | Constraints | Description | +| --------------- | ------------ | --------------------------- | -------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| contract_id | INT | NOT NULL, FK | Reference to contracts | +| discipline_code | VARCHAR(10) | NOT NULL | Code (e.g., 'GEN', 'STR', 'ARC') | +| code_name_th | VARCHAR(255) | NULL | Thai Name | +| code_name_en | VARCHAR(255) | NULL | English Name | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- **UNIQUE KEY (contract_id, discipline_code)** - Ensures unique code per contract + +**Relationships**: + +- Parent: contracts +- Referenced by: rfas, correspondences, document_number_counters + +--- + +### 4.8 correspondence_sub_types + +**Purpose**: Master table for Correspondence/RFA Sub-types mapping [Added in v1.4.4 based on Req 6B] + +| Column Name | Data Type | Constraints | Description | +| ---------------------- | ------------ | --------------------------- | -------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier | +| contract_id | INT | NOT NULL, FK | Reference to contracts | +| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types | +| sub_type_code | VARCHAR(20) | NOT NULL | Sub-type Code (e.g., 'MAT', 'SHP') | +| sub_type_name | VARCHAR(255) | NULL | Full Name | +| sub_type_number | VARCHAR(10) | NULL | Numeric code for running number (e.g., '11') | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE + +**Relationships**: + +- Parent: contracts, correspondence_types +- Referenced by: (Used in logic for Document Numbering) + +--- + +## **5. 📐 Drawings Tables (āđāļšāļš, āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆ)** + +### 5.1 contract_drawing_volumes + +**Purpose**: Master table for contract drawing volume classification + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique volume ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| volume_code | VARCHAR(50) | NOT NULL | Volume code | +| volume_name | VARCHAR(255) | NOT NULL | Volume name | +| description | TEXT | NULL | Volume description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, volume_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawings + +**Business Rules**: + +- Volume codes must be unique within a project +- Used for organizing large sets of contract drawings + +--- + +### 5.2 contract_drawing_cats + +**Purpose**: Master table for contract drawing main categories + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| cat_code | VARCHAR(50) | NOT NULL | Category code | +| cat_name | VARCHAR(255) | NOT NULL | Category name | +| description | TEXT | NULL | Category description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, cat_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawing_subcat_cat_maps + +**Business Rules**: + +- Category codes must be unique within a project +- Hierarchical relationship with sub-categories via mapping table + +--- + +### 5.3 contract_drawing_sub_cats + +**Purpose**: Master table for contract drawing sub-categories + +| Column Name | Data Type | Constraints | Description | +| ------------ | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| sub_cat_code | VARCHAR(50) | NOT NULL | Sub-category code | +| sub_cat_name | VARCHAR(255) | NOT NULL | Sub-category name | +| description | TEXT | NULL | Sub-category description | +| sort_order | INT | DEFAULT 0 | Display order | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, sub_cat_code) +- INDEX (sort_order) + +**Relationships**: + +- Parent: projects +- Referenced by: contract_drawings, contract_drawing_subcat_cat_maps + +**Business Rules**: + +- Sub-category codes must be unique within a project +- Can be mapped to multiple main categories via mapping table + +--- + +### 5.4 contract_drawing_subcat_cat_maps + +**Purpose**: Junction table mapping sub-categories to main categories (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------- | --------- | --------------- | -------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects | +| sub_cat_id | INT | PRIMARY KEY, FK | Reference to sub-category | +| cat_id | INT | PRIMARY KEY, FK | Reference to main category | + +**Indexes**: + +- PRIMARY KEY (project_id, sub_cat_id, cat_id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE +- FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +- INDEX (sub_cat_id) +- INDEX (cat_id) + +**Relationships**: + +- Parent: projects, contract_drawing_sub_cats, contract_drawing_cats + +**Business Rules**: + +- Allows flexible categorization +- One sub-category can belong to multiple main categories +- All three fields required for uniqueness + +--- + +### 5.5 contract_drawings + +**Purpose**: Master table for contract drawings (from contract specifications) + +| Column Name | Data Type | Constraints | Description | +| ----------- | ------------ | ----------------------------------- | ------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| condwg_no | VARCHAR(255) | NOT NULL | Contract drawing number | +| title | VARCHAR(255) | NOT NULL | Drawing title | +| sub_cat_id | INT | NULL, FK | Reference to sub-category | +| volume_id | INT | NULL, FK | Reference to volume | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| deleted_at | DATETIME | NULL | Soft delete timestamp | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT +- FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT +- FOREIGN KEY (updated_by) REFERENCES users(user_id) +- UNIQUE KEY (project_id, condwg_no) +- INDEX (sub_cat_id) +- INDEX (volume_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: projects, contract_drawing_sub_cats, contract_drawing_volumes, users +- Referenced by: shop_drawing_revision_contract_refs, contract_drawing_attachments + +**Business Rules**: + +- Drawing numbers must be unique within a project +- Represents baseline/contract drawings +- Referenced by shop drawings for compliance tracking +- Soft delete preserves history + +--- + +### 5.6 shop_drawing_main_categories + +**Purpose**: Master table for shop drawing main categories (discipline-level) + +| Column Name | Data Type | Constraints | Description | +| ------------------ | ------------ | ----------------------------------- | ------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID | +| main_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Category code (ARCH, STR, MEP, etc.) | +| main_category_name | VARCHAR(255) | NOT NULL | Category name | +| description | TEXT | NULL | Category description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (main_category_code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: shop_drawing_sub_categories, shop_drawings + +**Business Rules**: + +- Global categories (not project-specific) +- Typically represents engineering disciplines + +--- + +### 5.7 shop_drawing_sub_categories + +**Purpose**: Master table for shop drawing sub-categories (component-level) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | ----------------------------------- | ----------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID | +| sub_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Sub-category code (STR-COLUMN, ARCH-DOOR, etc.) | +| sub_category_name | VARCHAR(255) | NOT NULL | Sub-category name | +| main_category_id | INT | NOT NULL, FK | Reference to main category | +| description | TEXT | NULL | Sub-category description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (sub_category_code) +- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +- INDEX (main_category_id) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Parent: shop_drawing_main_categories +- Referenced by: shop_drawings + +**Business Rules**: + +- Global sub-categories (not project-specific) +- Hierarchical under main categories +- Represents specific drawing types or components + +--- + +### 5.8 shop_drawings + +**Purpose**: Master table for shop drawings (contractor-submitted) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | ------------ | ----------------------------------- | -------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | Shop drawing number | +| title | VARCHAR(500) | NOT NULL | Drawing title | +| main_category_id | INT | NOT NULL, FK | Reference to main category | +| sub_category_id | INT | NOT NULL, FK | Reference to sub-category | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | +| deleted_at | DATETIME | NULL | Soft delete timestamp | +| updated_by | INT | NULL, FK | User who last updated | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (drawing_number) +- FOREIGN KEY (project_id) REFERENCES projects(id) +- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +- FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id) +- FOREIGN KEY (updated_by) REFERENCES users(user_id) +- INDEX (project_id) +- INDEX (main_category_id) +- INDEX (sub_category_id) +- INDEX (deleted_at) + +**Relationships**: + +- Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users +- Children: shop_drawing_revisions + +**Business Rules**: + +- Drawing numbers are globally unique across all projects +- Represents contractor shop drawings +- Can have multiple revisions +- Soft delete preserves history + +--- + +### 5.9 shop_drawing_revisions + +**Purpose**: Child table storing revision history of shop drawings (1:N) + +| Column Name | Data Type | Constraints | Description | +| --------------- | ----------- | --------------------------- | ------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID | +| shop_drawing_id | INT | NOT NULL, FK | Master shop drawing ID | +| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) | +| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) | +| revision_date | DATE | NULL | Revision date | +| description | TEXT | NULL | Revision description/changes | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE +- UNIQUE KEY (shop_drawing_id, revision_number) +- INDEX (revision_date) + +**Relationships**: + +- Parent: shop_drawings +- Referenced by: rfa_items, shop_drawing_revision_contract_refs, shop_drawing_revision_attachments + +**Business Rules**: + +- Revision numbers are sequential starting from 0 +- Each revision can reference multiple contract drawings +- Each revision can have multiple file attachments +- Linked to RFAs for approval tracking + +--- + +### 5.10 shop_drawing_revision_contract_refs + +**Purpose**: Junction table linking shop drawing revisions to referenced contract drawings (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ---------------------------------- | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision | +| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing | + +**Indexes**: + +- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id) +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +- INDEX (contract_drawing_id) + +**Relationships**: + +- Parent: shop_drawing_revisions, contract_drawings + +**Business Rules**: + +- Tracks which contract drawings each shop drawing revision is based on +- Ensures compliance with contract specifications +- One shop drawing revision can reference multiple contract drawings + +--- + +## **6. 🔄 Circulations Tables (āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™)** + +### 6.1 circulation_status_codes + +**Purpose**: Master table for circulation workflow status codes + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | --------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique status ID | +| code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (OPEN, IN_REVIEW, COMPLETED, CANCELLED) | +| description | VARCHAR(50) | NOT NULL | Status description | +| sort_order | INT | DEFAULT 0 | Display order | +| is_active | TINYINT(1) | DEFAULT 1 | Active status | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (code) +- INDEX (is_active) +- INDEX (sort_order) + +**Relationships**: + +- Referenced by: circulations + +**Seed Data**: 4 status codes + +- OPEN: Initial status when created +- IN_REVIEW: Under review by recipients +- COMPLETED: All recipients have responded +- CANCELLED: Withdrawn/cancelled + +--- + +### 6.2 circulations + +**Purpose**: Master table for internal circulation sheets (document routing) + +| Column Name | Data Type | Constraints | Description | +| ----------------------- | ------------ | ----------------------------------- | ----------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique circulation ID | +| correspondence_id | INT | UNIQUE, FK | Link to correspondence (1:1 relationship) | +| organization_id | INT | NOT NULL, FK | Organization that owns this circulation | +| circulation_no | VARCHAR(100) | NOT NULL | Circulation sheet number | +| circulation_subject | VARCHAR(500) | NOT NULL | Subject/title | +| circulation_status_code | VARCHAR(20) | NOT NULL, FK | Current status code | +| created_by_user_id | INT | NOT NULL, FK | User who created circulation | +| submitted_at | TIMESTAMP | NULL | Submission timestamp | +| closed_at | TIMESTAMP | NULL | Closure timestamp | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- UNIQUE (correspondence_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) +- FOREIGN KEY (organization_id) REFERENCES organizations(id) +- FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code) +- FOREIGN KEY (created_by_user_id) REFERENCES users(user_id) +- INDEX (organization_id) +- INDEX (circulation_status_code) +- INDEX (created_by_user_id) + +**Relationships**: + +- Parent: correspondences, organizations, circulation_status_codes, users +- Children: circulation_routings, circulation_attachments + +**Business Rules**: + +- Internal document routing within organization +- One-to-one relationship with correspondences +- Tracks document review/approval workflow +- Status progression: OPEN → IN_REVIEW → COMPLETED/CANCELLED + +--- + +## **7. ðŸ“Ī Transmittals Tables (āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡)** + +### 7.1 transmittals + +**Purpose**: Child table for transmittal-specific data (1:1 with correspondences) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | --------------------------------------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences (1:1) | +| purpose | ENUM | NULL | Purpose: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW, OTHER | +| remarks | TEXT | NULL | Additional remarks | + +**Indexes**: + +- PRIMARY KEY (correspondence_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- INDEX (purpose) + +**Relationships**: + +- Parent: correspondences +- Children: transmittal_items + +**Business Rules**: + +- One-to-one relationship with correspondences +- Transmittal is a correspondence type for forwarding documents +- Contains metadata about the transmission + +--- + +### 7.2 transmittal_items + +**Purpose**: Junction table listing documents included in transmittal (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------------- | ------------ | --------------------------- | --------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique item ID | +| transmittal_id | INT | NOT NULL, FK | Reference to transmittal | +| item_correspondence_id | INT | NOT NULL, FK | Reference to document being transmitted | +| quantity | INT | DEFAULT 1 | Number of copies | +| remarks | VARCHAR(255) | NULL | Item-specific remarks | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE +- FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- UNIQUE KEY (transmittal_id, item_correspondence_id) +- INDEX (item_correspondence_id) + +**Relationships**: + +- Parent: transmittals, correspondences + +**Business Rules**: + +- One transmittal can contain multiple documents +- Tracks quantity of physical copies (if applicable) +- Links to any type of correspondence document + +--- + +## **8. 📎 File Management Tables (āđ„āļŸāļĨāđŒāđāļ™āļš)** + +### 8.1 attachments + +**Purpose**: Central repository for all file attachments in the system + +| Column Name | Data Type | Constraints | Description | +| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID | +| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload | +| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename | +| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) | +| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) | +| file_size | INT | NOT NULL | File size in bytes | +| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp | +| is_temporary | BOOLEAN | DEFAULT TRUE | āļĢāļ°āļšāļļāļ§āđˆāļēāđ€āļ›āđ‡āļ™āđ„āļŸāļĨāđŒāļŠāļąāđˆāļ§āļ„āļĢāļēāļ§ (āļĒāļąāļ‡āđ„āļĄāđˆāđ„āļ”āđ‰ Commit) | +| temp_id\* | VARCHAR(100) | NULL | ID āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ•āļ­āļ™ Upload Phase 1 (āļ­āļēāļˆāđƒāļŠāđ‰āļĢāđˆāļ§āļĄāļāļąāļš id āļŦāļĢāļ·āļ­āđāļĒāļāļāđ‡āđ„āļ”āđ‰) | +| expires_at | DATETIME | NULL | āđ€āļ§āļĨāļēāļŦāļĄāļ”āļ­āļēāļĒāļļāļ‚āļ­āļ‡āđ„āļŸāļĨāđŒ Temp (āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰ Cron Job āļĨāļšāļ­āļ­āļ) | +| checksum | VARCHAR(64) | NULL | SHA-256 Checksum āļŠāļģāļŦāļĢāļąāļš Verify File Integrity [Req 3.9.3] | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +- INDEX (stored_filename) +- INDEX (mime_type) +- INDEX (uploaded_by_user_id) +- INDEX (created_at) + +**Relationships**: + +- Parent: users +- Referenced by: correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments, contract_drawing_attachments + +**Business Rules**: + +- Central storage prevents file duplication +- Stored filename prevents naming conflicts +- File path points to QNAP NAS storage +- Original filename preserved for download +- One file record can be linked to multiple documents + +--- + +### 8.2 correspondence_attachments + +**Purpose**: Junction table linking correspondences to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ----------------- | --------- | --------------- | ---------------------------- | +| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (correspondence_id, attachment_id) +- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: correspondences, attachments + +**Business Rules**: + +- One correspondence can have multiple attachments +- One attachment can be linked to multiple correspondences +- is_main_document identifies primary file (typically PDF) + +--- + +### 8.3 circulation_attachments + +**Purpose**: Junction table linking circulations to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ---------------- | --------- | --------------- | -------------------------- | +| circulation_id | INT | PRIMARY KEY, FK | Reference to circulations | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (circulation_id, attachment_id) +- FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: circulations, attachments + +--- + +### 8.4 shop_drawing_revision_attachments + +**Purpose**: Junction table linking shop drawing revisions to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------------ | --------- | --------------- | ---------------------------------- | +| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (shop_drawing_revision_id, attachment_id) +- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (file_type) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: shop_drawing_revisions, attachments + +**Business Rules**: + +- file_type categorizes drawing file formats +- Typically includes PDF for viewing and DWG for editing +- SOURCE may include native CAD files + +--- + +### 8.5 contract_drawing_attachments + +**Purpose**: Junction table linking contract drawings to file attachments (M:N) + +| Column Name | Data Type | Constraints | Description | +| ------------------- | --------- | --------------- | ---------------------------------- | +| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing | +| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments | +| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER | +| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag | + +**Indexes**: + +- PRIMARY KEY (contract_drawing_id, attachment_id) +- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE +- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE +- INDEX (attachment_id) +- INDEX (file_type) +- INDEX (is_main_document) + +**Relationships**: + +- Parent: contract_drawings, attachments + +--- + +## **9. ðŸ”Ē Document Numbering Tables (āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ)** + +### 9.1 document_number_formats + +**Purpose**: Master table defining document numbering templates per project and type + +| Column Name | Data Type | Constraints | Description | +| ---------------------- | ------------ | ----------------------------------- | -------------------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique format ID | +| project_id | INT | NOT NULL, FK | Reference to projects | +| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence types | +| format_template | VARCHAR(255) | NOT NULL | Template string (e.g., '{ORG_CODE}-{TYPE_CODE}-{SEQ:4}') | +| description | TEXT | NULL | Format description | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | +| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +- UNIQUE KEY (project_id, correspondence_type_id) +- INDEX (project_id) +- INDEX (correspondence_type_id) + +**Relationships**: + +- Parent: projects, correspondence_types + +**Business Rules**: + +- One format template per project per correspondence type combination +- Template placeholders: {ORG_CODE}, {TYPE_CODE}, {YEAR}, {SEQ:n} where n is zero-padding length +- Used by document numbering module to generate unique document numbers + +--- + +### 9.2 document_number_counters + +**Purpose**: Transaction table maintaining running sequence numbers for document numbering + +| Column Name | Data Type | Constraints | Description | +| -------------------------- | --------- | --------------- | ---------------------------------------------------- | +| project_id | INT | PRIMARY KEY, FK | Reference to projects | +| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization | +| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types | +| **discipline_id** | **INT** | **PRIMARY KEY** | **Discipline ID (0 if not applicable)** | +| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) | +| version | INT | DEFAULT 0 | āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš Optimistic Locking (āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āđˆāļēāļāđˆāļ­āļ™ Update) | +| last_number | INT | DEFAULT 0 | Last assigned sequence number | + +**Indexes**: + +- **PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, discipline_id, current_year)** +- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +- FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE +- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +- INDEX (project_id) +- INDEX (originator_organization_id) +- INDEX (correspondence_type_id) +- INDEX (current_year) + +**Relationships**: + +- Parent: projects, organizations, correspondence_types + +**Business Rules**: + +- **Updated v1.4.4:** Composite primary key now includes `discipline_id` to support independent running numbers per discipline (e.g., STR-001, ARC-001). +- Counter resets each year +- Thread-safe increments handled by DocumentNumberingService in NestJS using Redis Lock and Optimistic Locking on version column +- last_number tracks highest assigned number +- Used with document_number_formats to generate complete document numbers + +--- + +## **10. ⚙ïļ System & Logs Tables (āļĢāļ°āļšāļšāđāļĨāļ° Log)** + +### 10.1 audit_logs + +**Purpose**: Comprehensive audit trail for all significant system actions + +| Column Name | Data Type | Constraints | Description | +| ---------------- | ----------------------------------------- | --------------------------------- | ------------------------------------------------------------ | +| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID | +| user_id | INT | NULL, FK | User who performed action | +| action | VARCHAR(100) | NOT NULL | Action code (e.g., 'rfa.create', 'login.success') | +| entity_type | VARCHAR(50) | NULL | Entity/module affected (e.g., 'rfa', 'correspondence') | +| entity_id | VARCHAR(50) | NULL | Primary ID of affected record | +| details_json | JSON | NULL | Additional context/details in JSON format | +| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) | +| user_agent | VARCHAR(255) | NULL | Browser user agent string | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Action timestamp | +| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Project ID āļˆāļēāļ JSON details āđ€āļžāļ·āđˆāļ­āļ—āļģ Index | +| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column āļ”āļķāļ‡ Type āļˆāļēāļ JSON details | +| request_id | VARCHAR(100) | NULL | Request ID/Trace ID āđ€āļžāļ·āđˆāļ­āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļāļąāļš App Logs | +| severity | ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') | NULL | āļĢāļ°āļ”āļąāļšāļ„āļ§āļēāļĄāļĢāļļāļ™āđāļĢāļ‡āļ‚āļ­āļ‡āđ€āļŦāļ•āļļāļāļēāļĢāļ“āđŒ | + +**Indexes**: + +- PRIMARY KEY (audit_id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE SET NULL +- INDEX (user_id) +- INDEX (action) +- INDEX (entity_type, entity_id) +- INDEX (created_at) +- INDEX (ip_address) +- INDEX (v_ref_project_id) +- INDEX (v_ref_type) + +**Relationships**: + +- Parent: users + +**Business Rules**: + +- Immutable records (no updates/deletes) +- Captures all CRUD operations on sensitive data +- Includes authentication events (login, logout, failed attempts) +- JSON details field for flexible data storage +- Retention policy: typically 7 years for compliance +- Used for security audits, compliance reporting, and troubleshooting + +**Common Actions**: + +- Authentication: login.success, login.failed, logout +- Documents: correspondence.create, correspondence.update, rfa.submit +- Users: user.create, user.deactivate, role.assign +- Workflow: rfa.approve, rfa.reject, circulation.complete + +--- + +### 10.2 notifications + +**Purpose**: User notification queue for email, LINE, and in-system alerts + +| Column Name | Data Type | Constraints | Description | +| ----------------- | ------------ | --------------------------- | ------------------------------------------------ | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique notification ID | +| user_id | INT | NOT NULL, FK | Recipient user ID | +| title | VARCHAR(255) | NOT NULL | Notification title/subject | +| message | TEXT | NOT NULL | Notification message body | +| notification_type | ENUM | NOT NULL | Type: EMAIL, LINE, SYSTEM | +| is_read | BOOLEAN | DEFAULT FALSE | Read status flag | +| entity_type | VARCHAR(50) | NULL | Related entity type (e.g., 'rfa', 'circulation') | +| entity_id | INT | NULL | Related entity ID | +| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Notification creation timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +- INDEX (user_id) +- INDEX (notification_type) +- INDEX (is_read) +- INDEX (entity_type, entity_id) +- INDEX (created_at) +- COMPOSITE INDEX (user_id, is_read, created_at) - For user notification listing + +**Relationships**: + +- Parent: users + +**Business Rules**: + +- EMAIL: Sent via SMTP to user's email address +- LINE: Sent via LINE Notify API to user's LINE ID +- SYSTEM: In-app notifications displayed in user interface +- Same notification can trigger multiple types (EMAIL + SYSTEM) +- entity_type/entity_id allow deep-linking to related records +- is_read flag only applicable for SYSTEM notifications +- Auto-cleanup: delete read notifications older than 30 days + +**Common Notification Triggers**: + +- Task assignment (circulation routing, RFA workflow) +- Document status changes (submitted, approved, rejected) +- Approaching deadlines +- System announcements +- Mention in comments + +--- + +### 10.3 search_indices + +**Purpose**: Full-text search index for document content + +| Column Name | Data Type | Constraints | Description | +| ----------- | ----------- | --------------------------- | ------------------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique index ID | +| entity_type | VARCHAR(50) | NOT NULL | Entity type (e.g., 'correspondence', 'rfa') | +| entity_id | INT | NOT NULL | Entity primary ID | +| content | TEXT | NOT NULL | Searchable text content | +| indexed_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last indexing timestamp | + +**Indexes**: + +- PRIMARY KEY (id) +- INDEX (entity_type, entity_id) +- INDEX (indexed_at) + +**Business Rules**: + +- Automatically populated/updated when documents change +- Content includes: title, description, document number, metadata +- May include OCR text from PDF attachments (future enhancement) +- Used by advanced search functionality +- Periodic re-indexing to catch missed updates +- Supports Boolean operators, phrase searching + +**Search Features**: + +- Natural language queries +- Wildcard support (\*, ?) +- Boolean operators (AND, OR, NOT) +- Phrase matching with quotes +- Result ranking by relevance + +--- + +### 10.4 backup_logs + +**Purpose**: Log table tracking database and file backup operations + +| Column Name | Data Type | Constraints | Description | +| ------------- | ------------ | --------------------------- | ---------------------------------- | +| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique backup log ID | +| backup_type | ENUM | NOT NULL | Type: DATABASE, FILES, FULL | +| backup_path | VARCHAR(500) | NOT NULL | Path to backup file/directory | +| file_size | BIGINT | NULL | Backup file size in bytes | +| status | ENUM | NOT NULL | Status: STARTED, COMPLETED, FAILED | +| started_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Backup start timestamp | +| completed_at | TIMESTAMP | NULL | Backup completion timestamp | +| error_message | TEXT | NULL | Error details if failed | + +**Indexes**: + +- PRIMARY KEY (id) +- INDEX (backup_type) +- INDEX (status) +- INDEX (started_at) +- INDEX (completed_at) + +**Business Rules**: + +- DATABASE: MariaDB dump of database schema and data +- FILES: Backup of attachment files from QNAP storage +- FULL: Complete system backup (database + files) +- Triggered by n8n cron jobs +- Backup retention policy defined in backup strategy +- Failed backups trigger alert notifications +- completed_at - started_at = backup duration + +**Monitoring**: + +- Alert if no successful backup in 24 hours +- Track backup size trends over time +- Verify backup integrity with test restores + +--- + +### 10.5 json_schemas + +**Purpose**: āļ­āļ‡āļĢāļąāļš **Centralized JSON Schema Registry** āđ€āļžāļ·āđˆāļ­ Validate āļ‚āđ‰āļ­āļĄāļđāļĨ JSON Details āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ— āļ•āļēāļĄ Requirements 6.11.1 āđāļĨāļ° Backend Plan T2.5.1 + +| Column Name | Data Type | Constraints | Description | +| :-------------------- | :------------- | :--------------- | :----------------------------------------------------------- | +| **id** | `INT` | PK, AI | Unique Identifier | +| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | āļĢāļŦāļąāļŠ Schema (āđ€āļŠāđˆāļ™ `RFA_DWG_V1`, `CORR_RFI_V1`) | +| **version** | `INT` | NOT NULL | āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡ Schema | +| **table_name** | `VARCHAR(100)` | NOT NULL | āļŠāļ·āđˆāļ­āļ•āļēāļĢāļēāļ‡āļ—āļĩāđˆ Schema āļ™āļĩāđ‰āļœāļđāļāļ­āļĒāļđāđˆ (āđ€āļŠāđˆāļ™ rfa_revisions) | +| **schema_definition** | `JSON` | NOT NULL | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON Schema (Standard JSON Schema format) | +| **ui_schema** | `JSON` | NULL | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ UI Schema āļŠāļģāļŦāļĢāļąāļš Frontend Form Generator | +| **virtual_columns** | `JSON` | NULL | Config āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ Generated Columns āđƒāļ™ DB āđ€āļžāļ·āđˆāļ­ Performance | +| **migration_script** | `JSON` | NULL | Rules āļŠāļģāļŦāļĢāļąāļšāđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļāđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē (Migration) | +| **is_active** | `BOOLEAN` | DEFAULT TRUE | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ | +| **created_at** | `TIMESTAMP` | | āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ | +| **updated_at** | `TIMESTAMP` | | āļ§āļąāļ™āļ—āļĩāđˆāļ­āļąāļ›āđ€āļ”āļ• | + +**Indexes**: + +- UNIQUE KEY (schema_code, version): āļ›āđ‰āļ­āļ‡āļāļąāļ™ Version āļ‹āđ‰āļģāđƒāļ™ Schema āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ + +### 10.6 user_preferences + +**Purpose**: āđāļĒāļāļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāđˆāļ§āļ™āļ•āļąāļ§ (āđ€āļŠāđˆāļ™ Notification Settings) āļ­āļ­āļāļˆāļēāļāļ•āļēāļĢāļēāļ‡ Users āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™ āļ•āļēāļĄ Requirements 5.5 āđāļĨāļ° 6.8.3 + +| Column Name | Data Type | Constraints | Description | +| :--------------- | :------------ | :-------------- | :---------------------------------------- | +| **user_id** | `INT` | PK, FK | āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ•āļēāļĢāļēāļ‡ users | +| **notify_email** | `BOOLEAN` | DEFAULT TRUE | āļĢāļąāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļ—āļēāļ‡āļ­āļĩāđ€āļĄāļĨ | +| **notify_line** | `BOOLEAN` | DEFAULT TRUE | āļĢāļąāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āļ—āļēāļ‡ LINE | +| **digest_mode** | `BOOLEAN` | DEFAULT TRUE | āļĢāļąāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđāļšāļšāļĢāļ§āļĄ (Digest) āđāļ—āļ™ Real-time | +| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | āļ˜āļĩāļĄāļŦāļ™āđ‰āļēāļˆāļ­ (Light/Dark) | +| **updated_at** | `TIMESTAMP` | | āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ” | + +## **11. Workflow Engine Tables (New Core)** + +### **11.1 workflow_definitions** + +āđ€āļāđ‡āļšāđāļĄāđˆāđāļšāļš (Blueprint) āļ‚āļ­āļ‡ Workflow DSL + +| Column | Type | Description | +| :------------ | :---------- | :-------------------------- | +| id | CHAR(36) | PK (UUID) | +| workflow_code | VARCHAR(50) | āļĢāļŦāļąāļŠāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ (āđ€āļŠāđˆāļ™ RFA_FLOW) | +| version | INT | āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™ | +| dsl | JSON | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Logic | +| compiled | JSON | Optimized Logic | + +### **11.2 workflow_instances** + +āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡āļˆāļĢāļīāļ‡ + +| Column | Type | Description | +| :------------ | :---------- | :------------------------------ | +| id | CHAR(36) | PK (UUID) | +| definition_id | CHAR(36) | FK -> workflow_definitions | +| entity_type | VARCHAR(50) | āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (rfa, circulation) | +| entity_id | VARCHAR(50) | ID āđ€āļ­āļāļŠāļēāļĢ | +| current_state | VARCHAR(50) | āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™ | +| context | JSON | āļ•āļąāļ§āđāļ›āļĢ Runtime | + +### **11.3 workflow_histories** + +āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Audit Log) + +| Column | Type | Description | +| :---------------- | :---------- | :----------------------- | +| id | CHAR(36) | PK (UUID) | +| instance_id | CHAR(36) | FK -> workflow_instances | +| from_state | VARCHAR(50) | āļ•āđ‰āļ™āļ—āļēāļ‡ | +| to_state | VARCHAR(50) | āļ›āļĨāļēāļĒāļ—āļēāļ‡ | +| action | VARCHAR(50) | āļāļēāļĢāļāļĢāļ°āļ—āļģ | +| action_by_user_id | INT | āļœāļđāđ‰āļ—āļģ | + +## **12. 📊 Views & Procedures (āļ§āļīāļ§ āđāļĨāļ° āđ‚āļ›āļĢāļ‹āļĩāđ€āļ”āļ­āļĢāđŒ)** + +### 12.1 v_current_correspondences + +**Purpose**: View showing current revision of all non-RFA correspondences + +**Columns**: + +- correspondence_id, correspondence_number, correspondence_type_id/code/name +- project_id, project_code, project_iname +- organization_id, organization_code, organization_name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, due_date +- correspondence_status_id, correspondence_status_code, correspondence_status_name +- created_by, created_by_username, revision_created_at + +**Filters**: + +- is_current = TRUE (only latest revision) +- correspondence_type NOT IN ('RFA') (excludes RFAs) +- deleted_at IS NULL (excludes soft-deleted records) + +**Business Rules**: + +- Provides flattened view of current correspondence state +- Joins correspondence_revisions with is_current flag +- Used by dashboard, document listing screens +- Excludes RFAs (they have separate view) + +--- + +### 12.2 v_current_rfas + +**Purpose**: View showing current revision of all RFA documents + +**Columns**: + +- rfa_id, rfa_type_id, rfa_type_code, rfa_type_name +- correspondence_id, correspondence_number +- project_id, project_code, project_iname +- organization_id, organization_name +- revision_id, revision_number, revision_label +- title, document_date, issued_date, received_date, approved_date +- rfa_status_code_id, rfa_status_code, rfa_status_name +- rfa_approve_code_id, rfa_approve_code, rfa_approve_code_name +- created_by, created_by_username, revision_created_at + +**Filters**: + +- is_current = TRUE +- deleted_at IS NULL (both rfas and correspondences) + +**Business Rules**: + +- Specialized view for RFA documents +- Includes RFA-specific fields (approval codes, approved date) +- Joins across rfas → rfa_revisions → correspondences +- Used by RFA management screens + +--- + +### 12.3 v_contract_parties_all + +**Purpose**: View showing all organization relationships across contracts and projects + +**Columns**: + +- contract_id, contract_code, contract_name +- project_id, project_code, project_iname +- organization_id, organization_code, organization_name +- role_in_contract + +**Business Rules**: + +- Joins contracts → projects → contract_organizations → organizations +- Shows only active contracts (is_active = TRUE) +- Used for permission checks and document routing +- Supports multi-organization projects/contracts + +--- + +### 12.4 v_user_tasks + +**Purpose**: View showing pending tasks assigned to users (action items) + +**Columns**: + +- routing_id, circulation_id, circulation_no, circulation_subject +- correspondence_id, correspondence_number +- project_id, project_code, project_name +- user_id, username, first_name, last_name +- organization_id, organization_name +- step_number, task_status, comments +- completed_at, assigned_at, circulation_created_at + +**Filters**: + +- status IN ('PENDING', 'IN_PROGRESS') +- assigned_to IS NOT NULL + +**Business Rules**: + +- Shows circulation routings requiring user action +- Used for "My Tasks" / "Inbox" functionality +- Excludes completed/cancelled tasks +- Ordered by creation date (oldest first) + +--- + +### 12.5 v_audit_log_details + +**Purpose**: View enriching audit logs with user information + +**Columns**: + +- audit_id, user_id, username, email, first_name, last_name +- action, entity_type, entity_id +- details_json, ip_address, user_agent +- created_at + +**Business Rules**: + +- Joins audit_logs with users table +- Used for audit trail reports +- Includes user details even if user later deleted (LEFT JOIN) + +--- + +### 12.6 v_user_all_permissions + +**Purpose**: View showing all effective permissions for users across all scopes + +**Columns**: + +- user_id, role_id, role_name +- permission_id, permission_name +- module, scope_level +- organization_id, project_id, contract_id +- permission_scope (GLOBAL, ORGANIZATION, PROJECT, CONTRACT) + +**Business Rules**: + +- UNION of permissions from Global, Organization, Project, and Contract scopes +- Used for authorization checks +- Considers role-permission mappings at all levels +- Only shows active permissions (is_active = 1) +- One row per user-permission-scope combination + +**Usage Example**: + +```sql +SELECT permission_name +FROM v_user_all_permissions +WHERE user_id = ? + AND project_id = ? + AND permission_name = 'document.edit'; +``` + +--- + +### 12.7 v_documents_with_attachments + +**Purpose**: View showing all documents and their attachment counts + +**Columns**: + +- document_type (CORRESPONDENCE, CIRCULATION, SHOP_DRAWING, CONTRACT_DRAWING) +- document_id, document_number +- project_id, project_code, project_name +- attachment_count, latest_attachment_date + +**Business Rules**: + +- UNION of all document types with attachments +- Used for document listing with file indicators +- Helps identify documents missing attachments +- Aggregates count per document + +--- + +### 12.8 v_document_statistics + +**Purpose**: View providing aggregated document statistics by project, type, and status + +**Columns**: + +- project_id, project_code, project_name +- correspondence_type_id, correspondence_type_code, correspondence_type_name +- status_id, status_code, status_name +- document_count, revision_count + +**Business Rules**: + +- CROSS JOIN creates all possible combinations +- LEFT JOIN shows zeros for combinations with no documents +- Only includes active projects, types, and statuses +- Used for dashboard charts and reports +- Groups by project → type → status + +--- + +## Database Indexes Summary + +### Performance Optimization Indexes + +**Primary Indexes** (automatic with PRIMARY KEY): + +- All tables have PRIMARY KEY with AUTO_INCREMENT + +**Foreign Key Indexes** (automatic with FOREIGN KEY): + +- All FK relationships automatically indexed + +**Additional Performance Indexes**: + +1. **Correspondence Tables**: + + - `idx_correspondences_type_project` on (correspondence_type_id, project_id) + - `idx_corr_revisions_current_status` on (is_current, correspondence_status_id) + - `idx_corr_revisions_correspondence_current` on (correspondence_id, is_current) + - `idx_correspondences_project_type` on (project_id, correspondence_type_id) + +2. **RFA Tables**: + + - `idx_rfa_revisions_current_status` on (is_current, rfa_status_code_id) + - `idx_rfa_revisions_rfa_current` on (rfa_id, is_current) + +3. **Circulation Tables**: + + - `idx_circulation_routings_status_assigned` on (status, assigned_to) + - `idx_circulation_routings_circulation_status` on (circulation_id, status) + +4. **Document Numbering**: + + - `idx_doc_counter_composite` on (project_id, originator_organization_id, correspondence_type_id, current_year) + +5. **Audit & Notifications**: + + - `idx_audit_logs_reporting` on (created_at, entity_type, action) + - `idx_notifications_user_unread` on (user_id, is_read, created_at) + +--- + +## Data Integrity Constraints + +### Foreign Key Constraints + +**Cascade Delete**: + +- Parent-child relationships where child should be deleted with parent +- Examples: correspondence_revisions, shop_drawing_revisions, project/contract relationships + +**Restrict Delete**: + +- Prevents deletion if references exist +- Examples: correspondence_types, rfa_status_codes, organizations (when referenced) + +**Set NULL**: + +- Preserves record but removes reference +- Examples: originator_id, created_by, updated_by + +### Unique Constraints + +1. **Globally Unique**: + + - usernames, emails + - shop_drawing.drawing_number + +2. **Unique Within Scope**: + + - (project_id, correspondence_number) + - (project_id, condwg_no) + - (correspondence_id, revision_number) + - (rfa_id, revision_number) + +3. **Composite Unique**: + - (correspondence_id, is_current) - ensures only one current revision + - (project_id, correspondence_type_id) - in document_number_formats + +### Check Constraints + +1. **user_assignments.chk_scope**: + - Ensures only one scope field (organization_id, project_id, contract_id) is NOT NULL + - OR all are NULL for Global scope + +### Business Rule Constraints + +1. **Soft Delete Pattern**: + + - deleted_at timestamp instead of hard delete + - Preserves audit trail and relationships + - Applied to: correspondences, rfas, shop_drawings, contract_drawings + +2. **Current Revision Pattern**: + + - is_current flag with UNIQUE constraint + - Ensures only one current revision per document + +3. **Sequential Numbering**: + - revision_number starts at 0, increments by 1 + - Enforced by application logic + stored procedure + +--- + +## Security & Permissions Model + +### Access Control Hierarchy + +```tree +Global Scope (Superadmin) + └── Organization Scope (Org Admin, Document Control) + └── Project Scope (Project Manager) + └── Contract Scope (Contract Admin) +``` + +### Permission Inheritance + +- Users can have multiple role assignments at different scopes +- More specific scopes inherit access from broader scopes +- Permission checks evaluate all applicable scopes +- View v_user_all_permissions aggregates effective permissions + +### Role-Based Access Control (RBAC) + +**7 Predefined Roles**: + +1. Superadmin (Global) - Full system access +2. Org Admin (Organization) - Organization management +3. Document Control (Organization) - Document lifecycle + admin powers +4. Editor (Organization) - Document CRUD +5. Viewer (Organization) - Read-only +6. Project Manager (Project) - Project + document management +7. Contract Admin (Contract) - Contract-specific management + +### Permission Categories (49 total) + +1. **System Management** (1): Full system control +2. **Organization Management** (4): CRUD on organizations +3. **Project Management** (8): CRUD + member/contract management +4. **Role & Permission Management** (4): RBAC administration +5. **Master Data Management** (4): Document types, categories, tags +6. **User Management** (5): User CRUD + organization assignment +7. **Contract Management** (2): Contract administration +8. **Document Management** (16): Full document lifecycle +9. **Workflow Management** (3): Approval workflow control +10. **Search & Reporting** (2): Advanced search, report generation + +--- + +## Data Migration & Seeding + +### Pre-populated Master Data + +1. **organization_roles**: Not used in current implementation +2. **organizations**: 15 organizations (Owner, Consultants, Contractors, Third parties) +3. **projects**: 5 projects (LCBP3 + 4 sub-contracts) +4. **contracts**: 7 contracts +5. **users**: 3 initial users (superadmin, editor01, viewer01) +6. **roles**: 7 predefined roles +7. **permissions**: 49 system permissions +8. **role_permissions**: Complete permission mappings +9. **project_organizations**: Project-organization relationships +10. **contract_organizations**: Contract-organization-role relationships +11. **correspondence_types**: 10 types +12. **correspondence_status**: 23 status codes +13. **rfa_types**: 11 types +14. **rfa_status_codes**: 7 statuses +15. **rfa_approve_codes**: 8 approval codes +16. **circulation_status_codes**: 4 statuses + +### Initial Passwords + +All seed users have password: `password123` + +- Hashed as: `$2y$10$0kjBMxWq7E4G7P.dc8r5i.cjiPBiup553AsFpDfxUt31gKg9h/udq` +- **Must be changed on first login in production** + +--- + +## Backup & Recovery Strategy + +### Database Backup + +**Strategy**: + +- Daily full database backups +- Hourly incremental backups (transaction logs) +- Retention: 30 days online, 7 years archived + +**Backup Method**: + +- MariaDB mysqldump for logical backups +- MariaDB Backup (Mariabackup) for physical backups +- Automated via n8n cron workflows + +### File Backup + +**Strategy**: + +- Daily backup of /share/dms-data/ directory +- QNAP snapshot every 4 hours +- Offsite replication to secondary NAS + +**File Organization**: + +```tree +/share/dms-data/ + ├── attachments/ + │ ├── 2025/ + │ │ ├── 01/ + │ │ │ └── {uuid}-{filename} + │ │ └── 02/ + │ └── 2024/ + └── temp/ +``` + +### Recovery Procedures + +1. **Point-in-Time Recovery**: Using transaction logs +2. **Full Restore**: From latest full backup +3. **Selective Restore**: Individual tables or records +4. **File Recovery**: From QNAP snapshots or backup + +--- + +## Performance Optimization + +### Query Optimization + +1. **Use Indexed Columns**: WHERE, JOIN, ORDER BY clauses +2. **Avoid SELECT**: Specify needed columns +3. **Use Views**: For complex, frequently-used queries +4. **Limit Result Sets**: Use LIMIT and pagination +5. **Analyze Slow Queries**: Enable slow query log + +### Index Maintenance + +```sql +-- Check index usage +SELECT * FROM information_schema.STATISTICS +WHERE TABLE_SCHEMA = 'lcbp3'; + +-- Optimize tables +OPTIMIZE TABLE correspondences; + +-- Analyze tables +ANALYZE TABLE correspondences; +``` + +### Connection Pooling + +- Backend uses connection pool (NestJS TypeORM) +- Min pool size: 5 +- Max pool size: 20 +- Idle timeout: 10 minutes + +--- + +## Data Validation Rules + +### Required Fields Validation + +**At Application Level**: + +- Email format validation +- Date range validation (start_date < end_date) +- File size limits (5MB per attachment) +- File type restrictions (PDF, DWG, DOC, XLS, images) + +**At Database Level**: + +- NOT NULL constraints +- UNIQUE constraints +- Foreign key constraints +- Check constraints (user_assignments scope) + +### Business Logic Validation + +1. **Document Workflow**: + + - Cannot edit submitted documents (unless Document Control) + - Cannot skip workflow steps (unless forced) + - Must provide approval comments + +2. **User Management**: + + - Cannot delete users with active assignments + - Cannot deactivate own account + - Must have valid organization for non-Global roles + +3. **File Management**: + - Original filename preserved for audit + - Unique stored filename prevents conflicts + - File path must exist and be accessible + +--- + +## Change Log & Versioning + +### Database Version: v1.4.0 + +**Changes from v1.3.0**: + +- Added comprehensive RBAC system (roles, permissions, user_assignments) +- Refactored organization-project-contract relationships +- Added junction tables for M:N relationships +- Implemented soft delete pattern +- Added full-text search support +- Enhanced audit logging with JSON details +- Added circulation workflow templates +- Improved document numbering with stored procedure +- Added comprehensive views for common queries +- Optimized indexes for performance + +**Migration Path**: + +- v1.3.0 → v1.4.0: Run migration script (not provided in this excerpt) +- Backup database before migration +- Test migration on staging environment first + +--- + +## Technical Specifications + +### Database Configuration + +**MariaDB Server**: + +- Version: 10.11 +- Character Set: utf8mb4 +- Collation: utf8mb4_general_ci +- Time Zone: +07:00 (Bangkok/Asia) +- SQL Mode: STRICT_TRANS_TABLES, NO_ENGINE_SUBSTITUTION + +**Connection Settings**: + +- Host: Container on QNAP TS-473A +- Port: 3306 (default) +- Max Connections: 100 +- Max Packet Size: 64MB + +### Table Engine + +**InnoDB Features Used**: + +- ACID compliance +- Foreign key constraints +- Row-level locking +- Crash recovery +- Transaction support + +### Storage Requirements + +**Estimated Initial Size**: + +- Database: ~50 MB (with seed data) +- Indexes: ~20 MB +- Total: ~70 MB + +**Growth Estimates**: + +- 1,000 documents/month: +100 MB/month (database) +- 10 attachments/document @ 2MB avg: +20 GB/month (files) +- Plan for 1TB+ storage within first year + +--- + +## Application Integration + +### Backend (NestJS) + +**TypeORM Entities**: + +- One entity class per table +- Decorators for columns, relationships +- DTOs for data transfer +- Repositories for data access + +**Key Modules**: + +- AuthModule: Authentication, authorization +- DocumentsModule: Correspondence, RFA management +- DrawingsModule: Shop drawing, contract drawing +- WorkflowModule: Circulation, approval workflows +- FilesModule: Attachment management +- UsersModule: User, role, permission management + +### Frontend (Next.js) + +**Key Features**: + +- Document listing/search +- Document creation wizards +- Workflow approval interface +- File upload/download +- User management console +- Dashboard analytics + +### Integration Points + +1. **Document Numbering**: + + - Call DocumentNumberingService.generateNextNumber() (NestJS) which handles Redis locking and retry logic + - Format with template from document_number_formats + - Store in correspondences.correspondence_number + +2. **File Upload**: + + - Upload to QNAP /share/dms-data/ + - Create attachment record + - Link via junction table + +3. **Workflow Execution**: + + - Check rfa_workflow_templates + - Create rfa_workflows records + - Update status as steps complete + - Send notifications + +4. **Permission Checks**: + - Query v_user_all_permissions + - Cache results per session + - Re-check on sensitive operations + +--- + +## Monitoring & Maintenance + +### Health Checks + +```sql +-- Database size +SELECT + table_schema, + SUM(data_length + index_length) / 1024 / 1024 AS size_mb +FROM information_schema.TABLES +WHERE table_schema = 'dms_db' +GROUP BY table_schema; + +-- Table sizes +SELECT + table_name, + (data_length + index_length) / 1024 / 1024 AS size_mb, + table_rows +FROM information_schema.TABLES +WHERE table_schema = 'dms_db' +ORDER BY (data_length + index_length) DESC; + +-- Active connections +SHOW PROCESSLIST; + +-- Lock wait statistics +SELECT * FROM information_schema.INNODB_LOCK_WAITS; +``` + +### Scheduled Maintenance + +**Daily**: + +- Full database backup +- Check backup log for failures +- Monitor disk space + +**Weekly**: + +- OPTIMIZE tables +- ANALYZE tables +- Review slow query log +- Check for deadlocks + +**Monthly**: + +- Review audit logs +- Clean up old notifications (30+ days) +- Archive old audit logs (7+ years) +- Verify backup integrity (test restore) + +**Quarterly**: + +- Review and optimize indexes +- Update database statistics +- Capacity planning review + +--- + +## Glossary + +**Terms**: + +- **Correspondence**: Any formal document exchanged between parties +- **RFA**: Request for Approval - formal submittal for review/approval +- **Circulation**: Internal document routing workflow +- **Transmittal**: Cover sheet for forwarding multiple documents +- **Shop Drawing**: Detailed construction drawing from contractor +- **Contract Drawing**: Baseline drawing from contract specifications +- **Revision**: Version of a document +- **Originator**: Organization that creates/sends a document +- **Recipient**: Organization that receives a document (TO or CC) +- **Scope**: Level of permission application (Global, Organization, Project, Contract) + +**Acronyms**: + +- **DMS**: Document Management System +- **LCBP3**: Laem Chabang Port Phase 3 +- **RBAC**: Role-Based Access Control +- **RFA**: Request for Approval +- **RFI**: Request for Information +- **CSC**: Construction Supervision Consultant +- **TO**: Primary recipient (action required) +- **CC**: Carbon copy (for information) +- **QNAP**: Network Attached Storage device manufacturer + +--- + +## **Document Control:** + +- **Document:** Data Dictionary v1.4.5 +- **Version:** 1.4 +- **Date:** 2025-11-29 +- **Author:** NAP LCBP3-DMS & Gemini +- **Status:** FINAL.Rev.05 +- **Classification:** Internal Technical Documentation +- **Approved By:** Nattanin + +--- + +`End of Data Dictionary v1.4.5 (āļ‰āļšāļąāļšāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)` diff --git a/8_lcbp3_v1_4_5.sql b/8_lcbp3_v1_4_5.sql new file mode 100644 index 0000000..9adfc8c --- /dev/null +++ b/8_lcbp3_v1_4_5.sql @@ -0,0 +1,1477 @@ +-- ========================================================== +-- DMS v1.4.5 Document Management System Database +-- Deploy Script Schema +-- Server: Container Station on QNAPQNAP TS-473A +-- Database service: MariaDB 10.11 +-- database web ui: phpmyadmin 5-apache +-- database deelopment ui: DBeaver +-- backend sevice: NestJS +-- frontend sevice: next.js +-- reverse proxy: jc21/nginx-proxy-manager:latest +-- cron service: n8n +-- DMS v1.4.5 Improvements +-- Update: revise from v1.4.4 +-- ========================================================== +SET NAMES utf8mb4; +SET time_zone = '+07:00'; +-- āļ›āļīāļ”āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš Foreign Key āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļĨāļšāļ•āļēāļĢāļēāļ‡āđ„āļ”āđ‰āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +SET FOREIGN_KEY_CHECKS = 0; +DROP VIEW IF EXISTS v_document_statistics; +DROP VIEW IF EXISTS v_documents_with_attachments; +DROP VIEW IF EXISTS v_user_all_permissions; +DROP VIEW IF EXISTS v_audit_log_details; +DROP VIEW IF EXISTS v_user_tasks; +DROP VIEW IF EXISTS v_contract_parties_all; +DROP VIEW IF EXISTS v_current_rfas; +DROP VIEW IF EXISTS v_current_correspondences; +-- DROP PROCEDURE IF EXISTS sp_get_next_document_number; +-- 🗑ïļ DROP TABLE SCRIPT: LCBP3-DMS v1.4.2 +-- āļ„āļģāđ€āļ•āļ·āļ­āļ™: āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļˆāļ°āļŦāļēāļĒāđ„āļ› āļāļĢāļļāļ“āļē Backup āļāđˆāļ­āļ™āļĢāļąāļ™āļšāļ™ Production +SET FOREIGN_KEY_CHECKS = 0; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1: āļ•āļēāļĢāļēāļ‡ System, Logs & Preferences (āļ•āļēāļĢāļēāļ‡āļ›āļĨāļēāļĒāļ—āļēāļ‡/āļŠāđˆāļ§āļ™āđ€āļŠāļĢāļīāļĄ) +-- ============================================================ +DROP TABLE IF EXISTS backup_logs; +DROP TABLE IF EXISTS search_indices; +DROP TABLE IF EXISTS notifications; +DROP TABLE IF EXISTS audit_logs; +-- [NEW v1.4.2] āļ•āļēāļĢāļēāļ‡āļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļŠāđˆāļ§āļ™āļ•āļąāļ§āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ (FK -> users) +DROP TABLE IF EXISTS user_preferences; +-- [NEW v1.4.2] āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš Schema āļŠāļģāļŦāļĢāļąāļš Validate JSON (Stand-alone) +DROP TABLE IF EXISTS json_schemas; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 2: āļ•āļēāļĢāļēāļ‡ Junction (āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ M:N) +-- ============================================================ +DROP TABLE IF EXISTS correspondence_tags; +DROP TABLE IF EXISTS shop_drawing_revision_contract_refs; +DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 3: āļ•āļēāļĢāļēāļ‡āđ„āļŸāļĨāđŒāđāļ™āļšāđāļĨāļ°āļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡ (Attachments) +-- ============================================================ +DROP TABLE IF EXISTS contract_drawing_attachments; +DROP TABLE IF EXISTS circulation_attachments; +DROP TABLE IF EXISTS shop_drawing_revision_attachments; +DROP TABLE IF EXISTS correspondence_attachments; +DROP TABLE IF EXISTS attachments; +-- āļ•āļēāļĢāļēāļ‡āļŦāļĨāļąāļāđ€āļāđ‡āļš path āđ„āļŸāļĨāđŒ +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 4: āļ•āļēāļĢāļēāļ‡ Workflow & Routing (Process Logic) +-- ============================================================ +-- Correspondence Workflow +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 5: āļ•āļēāļĢāļēāļ‡ Mapping āļŠāļīāļ—āļ˜āļīāđŒāđāļĨāļ°āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ (Access Control) +-- ============================================================ +DROP TABLE IF EXISTS role_permissions; +DROP TABLE IF EXISTS user_assignments; +DROP TABLE IF EXISTS contract_organizations; +DROP TABLE IF EXISTS project_organizations; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 6: āļ•āļēāļĢāļēāļ‡āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (Revisions & Items) +-- ============================================================ +DROP TABLE IF EXISTS transmittal_items; +DROP TABLE IF EXISTS shop_drawing_revisions; +DROP TABLE IF EXISTS rfa_items; +DROP TABLE IF EXISTS rfa_revisions; +DROP TABLE IF EXISTS correspondence_references; +DROP TABLE IF EXISTS correspondence_recipients; +DROP TABLE IF EXISTS correspondence_revisions; +-- [Modified v1.4.2] āļĄāļĩ Virtual Columns +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 7: āļ•āļēāļĢāļēāļ‡āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ (Core Documents) +-- ============================================================ +DROP TABLE IF EXISTS circulations; +DROP TABLE IF EXISTS transmittals; +DROP TABLE IF EXISTS contract_drawings; +DROP TABLE IF EXISTS shop_drawings; +DROP TABLE IF EXISTS rfas; +DROP TABLE IF EXISTS correspondences; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 8: āļ•āļēāļĢāļēāļ‡āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ (Master Data) +-- ============================================================ +-- [NEW 6B] āļĨāļšāļ•āļēāļĢāļēāļ‡āđƒāļŦāļĄāđˆāļ—āļĩāđˆāđ€āļžāļīāđˆāļĄāđ€āļ‚āđ‰āļēāļĄāļēāđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ Error āđ€āļ§āļĨāļē Re-deploy +DROP TABLE IF EXISTS correspondence_sub_types; +DROP TABLE IF EXISTS disciplines; +DROP TABLE IF EXISTS shop_drawing_sub_categories; +DROP TABLE IF EXISTS shop_drawing_main_categories; +DROP TABLE IF EXISTS contract_drawing_sub_cats; +DROP TABLE IF EXISTS contract_drawing_cats; +DROP TABLE IF EXISTS contract_drawing_volumes; +DROP TABLE IF EXISTS circulation_status_codes; +DROP TABLE IF EXISTS rfa_approve_codes; +DROP TABLE IF EXISTS rfa_status_codes; +DROP TABLE IF EXISTS rfa_types; +DROP TABLE IF EXISTS correspondence_status; +DROP TABLE IF EXISTS correspondence_types; +DROP TABLE IF EXISTS document_number_counters; +-- [Modified v1.4.2] āļĄāļĩ version column +DROP TABLE IF EXISTS document_number_formats; +DROP TABLE IF EXISTS tags; +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 9: āļ•āļēāļĢāļēāļ‡āļœāļđāđ‰āđƒāļŠāđ‰ āļšāļ—āļšāļēāļ— āđāļĨāļ°āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļĢāļēāļāļāļēāļ™ (Root Tables) +-- ============================================================ +DROP TABLE IF EXISTS organization_roles; +DROP TABLE IF EXISTS roles; +DROP TABLE IF EXISTS permissions; +DROP TABLE IF EXISTS contracts; +DROP TABLE IF EXISTS projects; +DROP TABLE IF EXISTS users; +-- Referenced by user_preferences, audit_logs, etc. +DROP TABLE IF EXISTS organizations; +-- Referenced by users, projects, etc. +-- ===================================================== +-- 1. ðŸĒ Core & Master Data (āļ­āļ‡āļ„āđŒāļāļĢ, āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļŠāļąāļāļāļē) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āļšāļ—āļšāļēāļ—āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ +CREATE TABLE organization_roles ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + role_name VARCHAR(20) NOT NULL UNIQUE COMMENT 'āļŠāļ·āđˆāļ­āļšāļ—āļšāļēāļ— (OWNER, DESIGNER, CONSULTANT, CONTRACTOR, THIRD PARTY)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āļšāļ—āļšāļēāļ—āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļ‡āļ„āđŒāļāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āđƒāļ™āļĢāļ°āļšāļš +CREATE TABLE organizations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + organization_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļ­āļ‡āļ„āđŒāļāļĢ', + organization_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļ­āļ‡āļ„āđŒāļāļĢ', + -- role_id INT COMMENT 'āļšāļ—āļšāļēāļ—āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ', + is_active BOOLEAN DEFAULT TRUE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”' -- FOREIGN KEY (role_id) REFERENCES organization_roles(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļ‡āļ„āđŒāļāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āđƒāļ™āļĢāļ°āļšāļš'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ‚āļ„āļĢāļ‡āļāļēāļĢ +CREATE TABLE projects ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāđ‚āļ„āļĢāļ‡āļāļēāļĢ', + project_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + -- parent_project_id INT COMMENT 'āļĢāļŦāļąāļŠāđ‚āļ„āļĢāļ‡āļāļēāļĢāļŦāļĨāļąāļ (āļ–āđ‰āļēāļĄāļĩ)', + -- contractor_organization_id INT COMMENT 'āļĢāļŦāļąāļŠāļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļšāđ€āļŦāļĄāļē (āļ–āđ‰āļēāļĄāļĩ)', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™' -- FOREIGN KEY (parent_project_id) REFERENCES projects(id) ON DELETE SET NULL, + -- FOREIGN KEY (contractor_organization_id) REFERENCES organizations(id) ON DELETE SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ‚āļ„āļĢāļ‡āļāļēāļĢ'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļąāļāļāļē +CREATE TABLE contracts ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL, + contract_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŠāļąāļāļāļē', + contract_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŠāļąāļāļāļē', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļŠāļąāļāļāļē', + start_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđ€āļĢāļīāđˆāļĄāļŠāļąāļāļāļē', + end_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļīāđ‰āļ™āļŠāļļāļ”āļŠāļąāļāļāļē', + is_active BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļąāļāļāļē'; +-- ===================================================== +-- 2. ðŸ‘Ĩ Users & RBAC (āļœāļđāđ‰āđƒāļŠāđ‰, āļŠāļīāļ—āļ˜āļīāđŒ, āļšāļ—āļšāļēāļ—) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User) +CREATE TABLE users ( + user_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + username VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļŠāļ·āđˆāļ­āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™', + password_hash VARCHAR(255) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļœāđˆāļēāļ™ (Hashed)', + first_name VARCHAR(50) COMMENT 'āļŠāļ·āđˆāļ­āļˆāļĢāļīāļ‡', + last_name VARCHAR(50) COMMENT 'āļ™āļēāļĄāļŠāļāļļāļĨ', + email VARCHAR(100) NOT NULL UNIQUE COMMENT 'āļ­āļĩāđ€āļĄāļĨ', + line_id VARCHAR(100) COMMENT 'LINE ID', + primary_organization_id INT COMMENT 'āļŠāļąāļ‡āļāļąāļ”āļ­āļ‡āļ„āđŒāļāļĢ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + failed_attempts INT DEFAULT 0 COMMENT 'āļˆāļģāļ™āļ§āļ™āļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™āļĨāđ‰āļĄāđ€āļŦāļĨāļ§', + locked_until DATETIME COMMENT 'āļĨāđ‡āļ­āļāļ­āļīāļ™āđ„āļĄāđˆāđ„āļ”āđ‰āļˆāļ™āļ–āļķāļ‡āđ€āļ§āļĨāļē', + last_login_at TIMESTAMP NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™āļĨāđˆāļēāļŠāļļāļ”', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + deleted_at DATETIME NULL DEFAULT NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļš', + FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User)'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļšāļ—āļšāļēāļ—" āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļĢāļ°āļšāļš +CREATE TABLE roles ( + role_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + -- role_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļšāļ—āļšāļēāļ— (āđ€āļŠāđˆāļ™ SUPER_ADMIN, ADMIN, EDITOR, VIEWER)', + role_name VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļšāļ—āļšāļēāļ—', + scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL, + -- āļ‚āļ­āļšāđ€āļ‚āļ•āļ‚āļ­āļ‡āļšāļ—āļšāļēāļ— (āļˆāļēāļāļ‚āđ‰āļ­ 4.3) + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļšāļ—āļšāļēāļ—', + is_system BOOLEAN DEFAULT FALSE COMMENT '(1 = āļšāļ—āļšāļēāļ—āļ‚āļ­āļ‡āļĢāļ°āļšāļš āļĨāļšāđ„āļĄāđˆāđ„āļ”āđ‰)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļšāļ—āļšāļēāļ—" āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļĢāļ°āļšāļš'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļŠāļīāļ—āļ˜āļīāđŒ" (Permission) āļŦāļĢāļ·āļ­ "āļāļēāļĢāļāļĢāļ°āļ—āļģ" āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™āļĢāļ°āļšāļš +CREATE TABLE permissions ( + permission_id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + permission_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŠāļīāļ—āļ˜āļīāđŒ (āđ€āļŠāđˆāļ™ rfas.create, rfas.view)', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļŠāļīāļ—āļ˜āļīāđŒ', + module VARCHAR(50) COMMENT 'āđ‚āļĄāļ”āļđāļĨāļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡', + scope_level ENUM('GLOBAL', 'ORG', 'PROJECT') COMMENT 'āļĢāļ°āļ”āļąāļšāļ‚āļ­āļšāđ€āļ‚āļ•āļ‚āļ­āļ‡āļŠāļīāļ—āļ˜āļīāđŒ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļŠāļīāļ—āļ˜āļīāđŒ" (Permission) āļŦāļĢāļ·āļ­ "āļāļēāļĢāļāļĢāļ°āļ—āļģ" āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđƒāļ™āļĢāļ°āļšāļš'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ roles āđāļĨāļ° permissions (M:N) +CREATE TABLE role_permissions ( + role_id INT COMMENT 'ID āļ‚āļ­āļ‡āļšāļ—āļšāļēāļ—', + permission_id INT COMMENT 'ID āļ‚āļ­āļ‡āļŠāļīāļ—āļ˜āļīāđŒ', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ roles āđāļĨāļ° permissions (M :N)'; +-- search.advanced +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļœāļđāđ‰āđƒāļŠāđ‰ (users) +CREATE TABLE user_assignments ( + id INT AUTO_INCREMENT PRIMARY KEY, + user_id INT NOT NULL, + role_id INT NOT NULL, + -- āļ„āļ­āļĨāļąāļĄāļ™āđŒāļŠāļģāļŦāļĢāļąāļšāļāļģāļŦāļ™āļ”āļ‚āļ­āļšāđ€āļ‚āļ• (āļˆāļ°āđƒāļŠāđ‰āđ€āļžāļĩāļĒāļ‡āļ­āļąāļ™āđ€āļ”āļĩāļĒāļ§āļ•āđˆāļ­āđāļ–āļ§) + organization_id INT NULL, + project_id INT NULL, + contract_id INT NULL, + assigned_by_user_id INT, + -- āļœāļđāđ‰āļ—āļĩāđˆāļĄāļ­āļšāļŦāļĄāļēāļĒāļšāļ—āļšāļēāļ—āļ™āļĩāđ‰ + assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + 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), + -- Constraint āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđāļ™āđˆāđƒāļˆāļ§āđˆāļēāļĄāļĩāđ€āļžāļĩāļĒāļ‡āļ‚āļ­āļšāđ€āļ‚āļ•āđ€āļ”āļĩāļĒāļ§āļ—āļĩāđˆāļ–āļđāļāļāļģāļŦāļ™āļ”āđƒāļ™āđāļ•āđˆāļĨāļ°āđāļ–āļ§ + CONSTRAINT chk_scope CHECK ( + ( + organization_id IS NOT NULL + AND project_id IS NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NOT NULL + AND contract_id IS NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NOT NULL + ) + OR ( + organization_id IS NULL + AND project_id IS NULL + AND contract_id IS NULL + ) -- āļŠāļģāļŦāļĢāļąāļš Global scope + ) +); +CREATE TABLE project_organizations ( + project_id INT NOT NULL, + organization_id INT NOT NULL, + 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 +); +CREATE TABLE contract_organizations ( + contract_id INT NOT NULL, + organization_id INT NOT NULL, + role_in_contract VARCHAR(100), + -- āđ€āļŠāđˆāļ™ 'Owner', 'Designer', 'Consultant', 'Contractor ' + 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 +); +-- ===================================================== +-- 3. ✉ïļ Correspondences (āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ, Revisions) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš +CREATE TABLE correspondence_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + type_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļ›āļĢāļ°āđ€āļ āļ— (āđ€āļŠāđˆāļ™ RFA, RFI)', + type_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļ›āļĢāļ°āđ€āļ āļ—', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš'; +INSERT INTO correspondence_types (type_code, type_name, sort_order, is_active) +VALUES ('RFA', 'Request for Approval', 1, 1), + ('RFI', 'Request for Information', 2, 1), + ('TRANSMITTAL', 'Transmittal', 3, 1), + ('EMAIL', 'Email', 4, 1), + ('INSTRUCTION', 'Instruction', 5, 1), + ('LETTER', 'Letter', 6, 1), + ('MEMO', 'Memorandum', 7, 1), + ('MOM', 'Minutes of Meeting', 8, 1), + ('NOTICE', 'Notice', 9, 1), + ('OTHER', 'Other', 10, 1); +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ +CREATE TABLE correspondence_status ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + status_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŠāļ–āļēāļ™āļ°āļŦāļ™āļąāļ‡āļŠāļ·āļ­ (āđ€āļŠāđˆāļ™ DRAFT, SUBOWN)', + status_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ°āļŦāļ™āļąāļ‡āļŠāļ·āļ­', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ'; +-- āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļ•āļēāļĄ Revision +CREATE TABLE correspondences ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡ (āļ™āļĩāđˆāļ„āļ·āļ­ "Master ID" āļ—āļĩāđˆāđƒāļŠāđ‰āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡)', + correspondence_number VARCHAR(100) NOT NULL COMMENT 'āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (āļŠāļĢāđ‰āļēāļ‡āļˆāļēāļ DocumentNumberingModule)', + correspondence_type_id INT NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ', + is_internal_communication TINYINT(1) DEFAULT 0 COMMENT '(1 = āļ āļēāļĒāđƒāļ™, 0 = āļ āļēāļĒāļ™āļ­āļ)', + project_id INT NOT NULL COMMENT 'āļ­āļĒāļđāđˆāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + originator_id INT COMMENT 'āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + created_by INT COMMENT 'āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡', + deleted_at DATETIME NULL COMMENT 'āļŠāļģāļŦāļĢāļąāļš Soft Delete', + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT, + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_corr_no_per_project (project_id, correspondence_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļ•āļēāļĄ Revision'; +-- āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđāļāđ‰āđ„āļ‚ (Revisions) āļ‚āļ­āļ‡ correspondences (1:N) +CREATE TABLE correspondence_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision āļ—āļĩāđˆāđāļŠāļ”āļ‡ (āđ€āļŠāđˆāļ™ A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™)', + correspondence_status_id INT NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ‚āļ­āļ‡ Revision āļ™āļĩāđ‰', + title VARCHAR(255) NOT NULL COMMENT 'āđ€āļĢāļ·āđˆāļ­āļ‡', + document_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđƒāļ™āđ€āļ­āļāļŠāļēāļĢ', + issued_date DATETIME COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ­āļ­āļāđ€āļ­āļāļŠāļēāļĢ', + received_date DATETIME COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļ‡āļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢ', + due_date DATETIME COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ„āļĢāļšāļāļģāļŦāļ™āļ”', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļāļēāļĢāđāļāđ‰āđ„āļ‚āđƒāļ™ Revision āļ™āļĩāđ‰', + details JSON COMMENT 'āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ° (āđ€āļŠāđˆāļ™ RFI details)', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ', + created_by INT COMMENT 'āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡', + updated_by INT COMMENT 'āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + 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 uq_master_revision_number (correspondence_id, revision_number), + UNIQUE KEY uq_master_current (correspondence_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđāļāđ‰āđ„āļ‚ (Revisions) āļ‚āļ­āļ‡ correspondences (1 :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļœāļđāđ‰āļĢāļąāļš (TO/CC) āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ‰āļšāļąāļš (M:N) +CREATE TABLE correspondence_recipients ( + correspondence_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ', + recipient_organization_id INT COMMENT 'ID āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļĢāļąāļš', + recipient_type ENUM('TO', 'CC ') COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āļœāļđāđ‰āļĢāļąāļš (TO āļŦāļĢāļ·āļ­ CC)', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļœāļđāđ‰āļĢāļąāļš (TO / CC) āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ‰āļšāļąāļš (M :N)'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš Tags āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāđƒāļŠāđ‰āđƒāļ™āļĢāļ°āļšāļš +CREATE TABLE tags ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + tag_name VARCHAR(100) NOT NULL UNIQUE COMMENT 'āļŠāļ·āđˆāļ­ Tag', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāđāļ—āđ‡āļ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ” ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš Tags āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāđƒāļŠāđ‰āđƒāļ™āļĢāļ°āļšāļš'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ correspondences āđāļĨāļ° tags (M:N) +CREATE TABLE correspondence_tags ( + correspondence_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ', + tag_id INT COMMENT 'ID āļ‚āļ­āļ‡ Tag', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ correspondences āđāļĨāļ° tags (M :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļĢāļ°āļŦāļ§āđˆāļēāļ‡āđ€āļ­āļāļŠāļēāļĢ (M:N) +CREATE TABLE correspondence_references ( + src_correspondence_id INT COMMENT 'ID āđ€āļ­āļāļŠāļēāļĢāļ•āđ‰āļ™āļ—āļēāļ‡', + tgt_correspondence_id INT COMMENT 'ID āđ€āļ­āļāļŠāļēāļĢāđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļĢāļ°āļŦāļ§āđˆāļēāļ‡āđ€āļ­āļāļŠāļēāļĢ (M :N)'; +-- ===================================================== +-- 4. 📐 approval: RFA (āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī, Workflows) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļ›āļĢāļ°āđ€āļ āļ— RFA +CREATE TABLE rfa_types ( + id INT PRIMARY KEY NOT NULL AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + contract_id INT NOT NULL COMMENT 'āļœāļđāļāļāļąāļšāļŠāļąāļāļāļē', + type_code VARCHAR(20) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļ›āļĢāļ°āđ€āļ āļ— RFA (āđ€āļŠāđˆāļ™ DWG, DOC, MAT)', + type_name_th VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļ›āļĢāļ°āđ€āļ āļ— RFA th', + type_name_en VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļ›āļĢāļ°āđ€āļ āļ— RFA en', + remark TEXT COMMENT 'āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ', + -- sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ', + UNIQUE KEY uk_rfa_types_contract_code (contract_id, type_code), + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļ›āļĢāļ°āđ€āļ āļ— RFA'; +-- [NEW 6B] āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļēāļ‚āļēāļ‡āļēāļ™ (Disciplines) āđāļĒāļāļ•āļēāļĄāļŠāļąāļāļāļē +CREATE TABLE disciplines ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + contract_id INT NOT NULL COMMENT 'āļœāļđāļāļāļąāļšāļŠāļąāļāļāļē', + discipline_code VARCHAR(10) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļŠāļēāļ‚āļē (āđ€āļŠāđˆāļ™ GEN, STR)', + code_name_th VARCHAR(255) COMMENT 'āļŠāļ·āđˆāļ­āđ„āļ—āļĒ', + code_name_en VARCHAR(255) COMMENT 'āļŠāļ·āđˆāļ­āļ­āļąāļ‡āļāļĪāļĐ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + UNIQUE KEY uk_discipline_contract (contract_id, discipline_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļēāļ‚āļēāļ‡āļēāļ™ (Disciplines) āļ•āļēāļĄ Req 6B'; +-- [NEW 6B] āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļąāļ‡āļŠāļ·āļ­āļĒāđˆāļ­āļĒ (Sub Types) āļŠāļģāļŦāļĢāļąāļš Mapping āđ€āļĨāļ‚āļĢāļŦāļąāļŠ +CREATE TABLE correspondence_sub_types ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + contract_id INT NOT NULL COMMENT 'āļœāļđāļāļāļąāļšāļŠāļąāļāļāļē', + correspondence_type_id INT NOT NULL COMMENT 'āļœāļđāļāļāļąāļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ (āđ€āļŠāđˆāļ™ RFA)', + sub_type_code VARCHAR(20) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļĒāđˆāļ­āļĒ (āđ€āļŠāđˆāļ™ MAT, SHP)', + sub_type_name VARCHAR(255) COMMENT 'āļŠāļ·āđˆāļ­āļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļąāļ‡āļŠāļ·āļ­āļĒāđˆāļ­āļĒ', + sub_type_number VARCHAR(10) COMMENT 'āđ€āļĨāļ‚āļĢāļŦāļąāļŠāļŠāļģāļŦāļĢāļąāļš Running Number (āđ€āļŠāđˆāļ™ 11, 22)', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ›āļĢāļ°āđ€āļ āļ—āļŦāļ™āļąāļ‡āļŠāļ·āļ­āļĒāđˆāļ­āļĒ (Sub Types) āļ•āļēāļĄ Req 6B'; +-- āļŦāļĢāļ·āļ­āđƒāļŠāđ‰ ALTER TABLE (āđāļ™āļ°āļ™āļģāđƒāļŦāđ‰āļĢāļąāļ™āļŦāļĨāļąāļ‡āļŠāļĢāđ‰āļēāļ‡āļ•āļēāļĢāļēāļ‡ disciplines āđ€āļŠāļĢāđ‡āļˆ) +ALTER TABLE correspondences +ADD COLUMN discipline_id INT NULL COMMENT 'āļŠāļēāļ‚āļēāļ‡āļēāļ™ (āļ–āđ‰āļēāļĄāļĩ)' +AFTER correspondence_type_id; +ALTER TABLE correspondences +ADD CONSTRAINT fk_corr_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE +SET NULL; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļŠāļ–āļēāļ™āļ° RFA +CREATE TABLE rfa_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + status_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŠāļ–āļēāļ™āļ° RFA (āđ€āļŠāđˆāļ™ DFT - Draft, FAP - For Approve)', + status_name VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ°', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļŠāļ–āļēāļ™āļ° RFA'; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļĢāļŦāļąāļŠāļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī RFA +CREATE TABLE rfa_approve_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + approve_code VARCHAR(20) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī ( + āđ€āļŠāđˆāļ™ 1A - Approved, + 3R - Revise + and Resubmit + )', + approve_name VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļšāļĢāļŦāļąāļŠāļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī RFA'; +CREATE TABLE rfas ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡ (RFA Master ID)', + rfa_type_id INT NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ— RFA', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + created_by INT COMMENT 'āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡', + deleted_at DATETIME NULL COMMENT 'āļŠāļģāļŦāļĢāļąāļš Soft Delete', + FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id), + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡ RFA (āļĄāļĩāļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ 1 :N āļāļąāļš rfa_revisions)'; +ALTER TABLE rfas +ADD COLUMN discipline_id INT NULL COMMENT 'āļŠāļēāļ‚āļēāļ‡āļēāļ™ (āļ–āđ‰āļēāļĄāļĩ)' +AFTER rfa_type_id; +ALTER TABLE rfas +ADD CONSTRAINT fk_rfa_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE +SET NULL; +-- āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Revisions) āļ‚āļ­āļ‡ rfas (1:N) +CREATE TABLE rfa_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡ Revision', + correspondence_id INT NOT NULL COMMENT 'Master ID āļ‚āļ­āļ‡ Correspondence', + rfa_id INT NOT NULL COMMENT 'Master ID āļ‚āļ­āļ‡ RFA', + revision_number INT NOT NULL COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Revision (0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision āļ—āļĩāđˆāđāļŠāļ”āļ‡ (āđ€āļŠāđˆāļ™ A, B, 1.1)', + is_current BOOLEAN DEFAULT FALSE COMMENT '(1 = Revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™)', + rfa_status_code_id INT NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ° RFA', + rfa_approve_code_id INT COMMENT 'āļœāļĨāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī', + title VARCHAR(255) NOT NULL COMMENT 'āđ€āļĢāļ·āđˆāļ­āļ‡', + document_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđƒāļ™āđ€āļ­āļāļŠāļēāļĢ', + issued_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāđˆāļ‡āļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī', + received_date DATETIME COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļ‡āļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢ', + approved_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ­āļ™āļļāļĄāļąāļ•āļī', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļāļēāļĢāđāļāđ‰āđ„āļ‚āđƒāļ™ Revision āļ™āļĩāđ‰', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ', + created_by INT COMMENT 'āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡', + updated_by INT COMMENT 'āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_id) REFERENCES rfas(id) ON DELETE CASCADE, + FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id), + FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE + SET NULL, + FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE + SET NULL, + FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE + SET NULL, + UNIQUE KEY uq_rr_rev_number (rfa_id, revision_number), + UNIQUE KEY uq_rr_current (rfa_id, is_current) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Revisions) āļ‚āļ­āļ‡ rfas (1 :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ rfa_revisions (āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļ›āļĢāļ°āđ€āļ āļ— DWG) āļāļąāļš shop_drawing_revisions (M:N) +CREATE TABLE rfa_items ( + rfarev_correspondence_id INT COMMENT 'ID āļ‚āļ­āļ‡ RFA Revision', + shop_drawing_revision_id INT COMMENT 'ID āļ‚āļ­āļ‡ Shop Drawing Revision', + PRIMARY KEY ( + rfarev_correspondence_id, + shop_drawing_revision_id + ), + FOREIGN KEY (rfarev_correspondence_id) REFERENCES rfa_revisions(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ rfa_revisions (āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļ›āļĢāļ°āđ€āļ āļ— DWG) āļāļąāļš shop_drawing_revisions (M :N)'; +-- ===================================================== +-- 5. 📐 Drawings (āđāļšāļš, āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆ) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āđ€āļĨāđˆāļĄ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē +CREATE TABLE contract_drawing_volumes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + volume_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāđ€āļĨāđˆāļĄ', + volume_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđ€āļĨāđˆāļĄ', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_volume_project (project_id, volume_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āđ€āļĨāđˆāļĄ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē'; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē +CREATE TABLE contract_drawing_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + cat_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + cat_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_cat_project (project_id, cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē'; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē +CREATE TABLE contract_drawing_sub_cats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + sub_cat_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + sub_cat_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE KEY ux_subcat_project (project_id, sub_cat_code) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ" āļ‚āļ­āļ‡āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ-āļĒāđˆāļ­āļĒ (M:N) +CREATE TABLE contract_drawing_subcat_cat_maps ( + project_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + sub_cat_id INT COMMENT 'ID āļ‚āļ­āļ‡āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + cat_id INT COMMENT 'ID āļ‚āļ­āļ‡āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + PRIMARY KEY (project_id, sub_cat_id, cat_id), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE, + FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ - āļĒāđˆāļ­āļĒ (M :N)'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ "āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē" +CREATE TABLE contract_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + condwg_no VARCHAR(255) NOT NULL COMMENT 'āđ€āļĨāļ‚āļ—āļĩāđˆāđāļšāļšāļŠāļąāļāļāļē', + title VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđāļšāļšāļŠāļąāļāļāļē', + sub_cat_id INT COMMENT 'āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + volume_id INT COMMENT 'āđ€āļĨāđˆāļĄ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + deleted_at DATETIME NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļš', + updated_by INT COMMENT 'āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE RESTRICT, + FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT, + UNIQUE KEY ux_condwg_no_project (project_id, condwg_no) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ "āđāļšāļšāļ„āļđāđˆāļŠāļąāļāļāļē"'; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ" āļ‚āļ­āļ‡āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ +CREATE TABLE shop_drawing_main_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + main_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ (āđ€āļŠāđˆāļ™ ARCH, STR)', + main_category_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ” ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ" āļ‚āļ­āļ‡āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡'; +-- āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ" āļ‚āļ­āļ‡āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ +CREATE TABLE shop_drawing_sub_categories ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + sub_category_code VARCHAR(50) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ (āđ€āļŠāđˆāļ™ STR - COLUMN)', + sub_category_name VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + main_category_id INT NOT NULL COMMENT 'āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āļŠāļģāļŦāļĢāļąāļš "āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ" āļ‚āļ­āļ‡āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡'; +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ "āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡" +CREATE TABLE shop_drawings ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + drawing_number VARCHAR(100) NOT NULL UNIQUE COMMENT 'āđ€āļĨāļ‚āļ—āļĩāđˆ Shop Drawing', + title VARCHAR(500) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđāļšāļš', + main_category_id INT NOT NULL COMMENT 'āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļŦāļĨāļąāļ', + sub_category_id INT NOT NULL COMMENT 'āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļĒāđˆāļ­āļĒ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + deleted_at DATETIME NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļš', + updated_by INT COMMENT 'āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + 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) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ "āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡"'; +-- āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Revisions) āļ‚āļ­āļ‡ shop_drawings (1:N) +CREATE TABLE shop_drawing_revisions ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡ Revision', + shop_drawing_id INT NOT NULL COMMENT 'Master ID', + revision_number INT NOT NULL COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Revision (āđ€āļŠāđˆāļ™ 0, 1, 2...)', + revision_label VARCHAR(10) COMMENT 'Revision āļ—āļĩāđˆāđāļŠāļ”āļ‡ (āđ€āļŠāđˆāļ™ A, B, 1.1)', + revision_date DATE COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ‚āļ­āļ‡ Revision', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļāļēāļĢāđāļāđ‰āđ„āļ‚', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE, + UNIQUE KEY ux_sd_rev_drawing_revision (shop_drawing_id, revision_number) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āļĨāļđāļ" āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Revisions) āļ‚āļ­āļ‡ shop_drawings (1 :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ shop_drawing_revisions āļāļąāļš contract_drawings (M:N) +CREATE TABLE shop_drawing_revision_contract_refs ( + shop_drawing_revision_id INT COMMENT 'ID āļ‚āļ­āļ‡ Shop Drawing Revision', + contract_drawing_id INT COMMENT 'ID āļ‚āļ­āļ‡ Contract Drawing', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ shop_drawing_revisions āļāļąāļš contract_drawings (M :N)'; +-- ===================================================== +-- 6. 🔄 Circulations (āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āđƒāļšāđ€āļ§āļĩāļĒāļ™ +CREATE TABLE circulation_status_codes ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + code VARCHAR(20) NOT NULL UNIQUE COMMENT 'āļĢāļŦāļąāļŠāļŠāļ–āļēāļ™āļ°āļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļ‡āļēāļ™', + description VARCHAR(50) NOT NULL COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļŠāļ–āļēāļ™āļ°āļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļ‡āļēāļ™', + sort_order INT DEFAULT 0 COMMENT 'āļĨāļģāļ”āļąāļšāļāļēāļĢāđāļŠāļ”āļ‡āļœāļĨ', + is_active TINYINT(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āđƒāļšāđ€āļ§āļĩāļĒāļ™'; +INSERT INTO circulation_status_codes (code, description, sort_order) +VALUES ('OPEN', 'Open', 1), + ('IN_REVIEW', 'In Review', 2), + ('COMPLETED', 'āļ›Completed', 3), + ('CANCELLED', 'Cancelled / Withdrawn', 9); +-- āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™ +CREATE TABLE circulations ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™', + correspondence_id INT UNIQUE COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (āļˆāļēāļāļ•āļēāļĢāļēāļ‡ correspondences)', + organization_id INT NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ“āđŒāļ—āļĩāđˆāđ€āļ›āđ‡āļ™āđ€āļˆāđ‰āļēāļ‚āļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ™āļĩāđ‰', + circulation_no VARCHAR(100) NOT NULL COMMENT 'āđ€āļĨāļ‚āļ—āļĩāđˆāđƒāļšāđ€āļ§āļĩāļĒāļ™', + circulation_subject VARCHAR(500) NOT NULL COMMENT 'āđ€āļĢāļ·āđˆāļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™', + circulation_status_code VARCHAR(20) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļŠāļ–āļēāļ™āļ°āđƒāļšāđ€āļ§āļĩāļĒāļ™', + created_by_user_id INT NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™', + submitted_at TIMESTAMP NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāđˆāļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™', + closed_at TIMESTAMP NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ›āļīāļ”āđƒāļšāđ€āļ§āļĩāļĒāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + 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) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™'; +-- ===================================================== +-- 7. ðŸ“Ī Transmittals (āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (āđ€āļ›āđ‡āļ™āļ•āļēāļĢāļēāļ‡āļĨāļđāļ 1:1 āļ‚āļ­āļ‡ correspondences) +CREATE TABLE transmittals ( + correspondence_id INT PRIMARY KEY COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ', + purpose ENUM( + 'FOR_APPROVAL', + 'FOR_INFORMATION', + 'FOR_REVIEW', + 'OTHER ' + ) COMMENT 'āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ', + remarks TEXT COMMENT 'āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ', + FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (āđ€āļ›āđ‡āļ™āļ•āļēāļĢāļēāļ‡āļĨāļđāļ 1 :1 āļ‚āļ­āļ‡ correspondences)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ transmittals āđāļĨāļ°āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļ™āļģāļŠāđˆāļ‡ (M:N) +CREATE TABLE transmittal_items ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļĢāļēāļĒāļāļēāļĢ', + transmittal_id INT NOT NULL COMMENT 'ID āļ‚āļ­āļ‡ Transmittal', + item_correspondence_id INT NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđāļ™āļšāđ„āļ›', + quantity INT DEFAULT 1 COMMENT 'āļˆāļģāļ™āļ§āļ™', + remarks VARCHAR(255) COMMENT 'āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļāļŠāļģāļŦāļĢāļąāļšāļĢāļēāļĒāļāļēāļĢāļ™āļĩāđ‰', + FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE, + FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE, + UNIQUE KEY ux_transmittal_item (transmittal_id, item_correspondence_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄāļĢāļ°āļŦāļ§āđˆāļēāļ‡ transmittals āđāļĨāļ°āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļ™āļģāļŠāđˆāļ‡ (M :N)'; +-- ===================================================== +-- 8. 📎 File Management (āđ„āļŸāļĨāđŒāđāļ™āļš) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ "āļāļĨāļēāļ‡" āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒāđāļ™āļšāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš +-- 2.2 Attachments - Two-Phase Storage & Security +-- āļĢāļ­āļ‡āļĢāļąāļš: Backend Plan T2.2, Req 3.9.1 +-- āđ€āļŦāļ•āļļāļœāļĨ: āļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāļ‚āļĒāļ° (Orphan Files) āđāļĨāļ°āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āđ„āļŸāļĨāđŒ +CREATE TABLE attachments ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āđ„āļŸāļĨāđŒāđāļ™āļš', + original_filename VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđ„āļŸāļĨāđŒāļ”āļąāđ‰āļ‡āđ€āļ”āļīāļĄāļ•āļ­āļ™āļ­āļąāļ›āđ‚āļŦāļĨāļ”', + stored_filename VARCHAR(255) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āđ„āļŸāļĨāđŒāļ—āļĩāđˆāđ€āļāđ‡āļšāļˆāļĢāļīāļ‡āļšāļ™ Server (āļ›āđ‰āļ­āļ‡āļāļąāļ™āļŠāļ·āđˆāļ­āļ‹āđ‰āļģ)', + file_path VARCHAR(500) NOT NULL COMMENT 'Path āļ—āļĩāđˆāđ€āļāđ‡āļšāđ„āļŸāļĨāđŒ (āļšāļ™ QNAP / share / dms - data /)', + mime_type VARCHAR(100) NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ„āļŸāļĨāđŒ (āđ€āļŠāđˆāļ™ application / pdf)', + file_size INT NOT NULL COMMENT 'āļ‚āļ™āļēāļ”āđ„āļŸāļĨāđŒ (bytes)', + is_temporary BOOLEAN DEFAULT TRUE COMMENT 'True = āļĒāļąāļ‡āđ„āļĄāđˆ Commit āļĨāļ‡ DB āļˆāļĢāļīāļ‡', + temp_id VARCHAR(100) NULL COMMENT 'ID āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ•āļ­āļ™ Upload Phase 1', + uploaded_by_user_id INT NOT NULL COMMENT 'āļœāļđāđ‰āļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ„āļŸāļĨāđŒ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļ­āļąāļ›āđ‚āļŦāļĨāļ”', + expires_at DATETIME NULL COMMENT 'āđ€āļ§āļĨāļēāļŦāļĄāļ”āļ­āļēāļĒāļļāļ‚āļ­āļ‡āđ„āļŸāļĨāđŒ Temp', + checksum VARCHAR(64) NULL COMMENT 'SHA -256 Checksum', + FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ "āļāļĨāļēāļ‡" āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒāđāļ™āļšāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ correspondences āļāļąāļš attachments (M:N) +CREATE TABLE correspondence_attachments ( + correspondence_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ', + attachment_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ„āļŸāļĨāđŒāđāļ™āļš', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = āđ„āļŸāļĨāđŒāļŦāļĨāļąāļ)', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ correspondences āļāļąāļš attachments (M :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ circulations āļāļąāļš attachments (M:N) +CREATE TABLE circulation_attachments ( + circulation_id INT COMMENT 'ID āļ‚āļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™', + attachment_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ„āļŸāļĨāđŒāđāļ™āļš', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = āđ„āļŸāļĨāđŒāļŦāļĨāļąāļāļ‚āļ­āļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™)', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ circulations āļāļąāļš attachments (M :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ shop_drawing_revisions āļāļąāļš attachments (M:N) +CREATE TABLE shop_drawing_revision_attachments ( + shop_drawing_revision_id INT COMMENT 'ID āļ‚āļ­āļ‡ Shop Drawing Revision', + attachment_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ„āļŸāļĨāđŒāđāļ™āļš', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER ') COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ„āļŸāļĨāđŒ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = āđ„āļŸāļĨāđŒāļŦāļĨāļąāļ)', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ shop_drawing_revisions āļāļąāļš attachments (M :N)'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ contract_drawings āļāļąāļš attachments (M:N) +CREATE TABLE contract_drawing_attachments ( + contract_drawing_id INT COMMENT 'ID āļ‚āļ­āļ‡ Contract Drawing', + attachment_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ„āļŸāļĨāđŒāđāļ™āļš', + file_type ENUM('PDF', 'DWG', 'SOURCE', 'OTHER ') COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ„āļŸāļĨāđŒ', + is_main_document BOOLEAN DEFAULT FALSE COMMENT '(1 = āđ„āļŸāļĨāđŒāļŦāļĨāļąāļ)', + 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 +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļŠāļ·āđˆāļ­āļĄ contract_drawings āļāļąāļš attachments (M :N)'; +-- ===================================================== +-- 9. ðŸ”Ē Document Numbering (āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ) +-- ===================================================== +-- āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļĢāļđāļ›āđāļšāļš" Template āļ‚āļ­āļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ +CREATE TABLE document_number_formats ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', + project_id INT NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + correspondence_type_id INT NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ', + format_template VARCHAR(255) NOT NULL COMMENT 'āļĢāļđāļ›āđāļšāļš Template (āđ€āļŠāđˆāļ™ { ORG_CODE } - { TYPE_CODE } - { SEQ :4 })', + description TEXT COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļĢāļđāļ›āđāļšāļšāļ™āļĩāđ‰', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE, + UNIQUE KEY uk_project_type (project_id, correspondence_type_id) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļš "āļĢāļđāļ›āđāļšāļš" Template āļ‚āļ­āļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ'; +-- āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš "āļ•āļąāļ§āļ™āļąāļš" (Running Number) āļĨāđˆāļēāļŠāļļāļ” +-- 2.1 Document Numbering - Optimistic Locking +-- āļĢāļ­āļ‡āļĢāļąāļš: Backend Plan T2.3, Req 3.10.5 +-- āđ€āļŦāļ•āļļāļœāļĨ: āļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition āđ€āļ§āļĨāļēāļ‚āļ­āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢāļžāļĢāđ‰āļ­āļĄāļāļąāļ™ +-- [UPDATED 6B] āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš "āļ•āļąāļ§āļ™āļąāļš" (Running Number) āļĨāđˆāļēāļŠāļļāļ” āļĢāļ­āļ‡āļĢāļąāļš Discipline +CREATE TABLE document_number_counters ( + project_id INT COMMENT 'āđ‚āļ„āļĢāļ‡āļāļēāļĢ', + originator_organization_id INT COMMENT 'āļ­āļ‡āļ„āđŒāļāļĢāļœāļđāđ‰āļŠāđˆāļ‡', + correspondence_type_id INT COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ', + discipline_id INT DEFAULT 0 COMMENT '[NEW] āļŠāļēāļ‚āļēāļ‡āļēāļ™ (0 = āđ„āļĄāđˆāļĢāļ°āļšāļļ)', + current_year INT COMMENT 'āļ›āļĩ āļ„.āļĻ.āļ‚āļ­āļ‡āļ•āļąāļ§āļ™āļąāļš', + version INT DEFAULT 0 NOT NULL COMMENT 'Optimistic Lock Version', + last_number INT DEFAULT 0 COMMENT 'āđ€āļĨāļ‚āļ—āļĩāđˆāļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆāđƒāļŠāđ‰āđ„āļ›āđāļĨāđ‰āļ§', + PRIMARY KEY ( + project_id, + originator_organization_id, + correspondence_type_id, + discipline_id, + -- āđ€āļžāļīāđˆāļĄ Key āļ™āļĩāđ‰ + current_year + ), + FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE, + FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš "āļ•āļąāļ§āļ™āļąāļš" (Running Number) āļĨāđˆāļēāļŠāļļāļ”'; +-- ===================================================== +-- 10. ⚙ïļ System & Logs (āļĢāļ°āļšāļšāđāļĨāļ° Log) +-- ===================================================== +-- 1.1 JSON Schemas Registry +-- āļĢāļ­āļ‡āļĢāļąāļš: Backend Plan T2.5.1, Req 6.11.1 +-- āđ€āļŦāļ•āļļāļœāļĨ: āđ€āļžāļ·āđˆāļ­ Validate āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON Details āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ—āđāļšāļš Centralized +CREATE TABLE json_schemas ( + id INT AUTO_INCREMENT PRIMARY KEY, + schema_code VARCHAR(100) NOT NULL COMMENT 'āļĢāļŦāļąāļŠ Schema (āđ€āļŠāđˆāļ™ RFA_DWG)', + version INT NOT NULL DEFAULT 1 COMMENT 'āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡ Schema', + table_name VARCHAR(100) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļ•āļēāļĢāļēāļ‡āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒ (āđ€āļŠāđˆāļ™ rfa_revisions)', + schema_definition JSON NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Data Schema (AJV Standard)', + ui_schema JSON NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ UI Schema āļŠāļģāļŦāļĢāļąāļš Frontend', + virtual_columns JSON NULL COMMENT 'Config āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ Virtual Columns', + migration_script JSON NULL COMMENT 'Script āļŠāļģāļŦāļĢāļąāļšāđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļāđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē', + is_active BOOLEAN DEFAULT TRUE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + -- āļ›āđ‰āļ­āļ‡āļāļąāļ™ Schema Code āļ‹āđ‰āļģāļāļąāļ™āđƒāļ™ Version āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ + UNIQUE KEY uk_schema_version (schema_code, version) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš JSON Schema āđāļĨāļ° Configuration'; +-- 1.2 User Preferences +-- āļĢāļ­āļ‡āļĢāļąāļš: Req 5.5, 6.8.3 +-- āđ€āļŦāļ•āļļāļœāļĨ: āđāļĒāļāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļē Notification āđāļĨāļ° UI āļ­āļ­āļāļˆāļēāļāļ•āļēāļĢāļēāļ‡ Users āļŦāļĨāļąāļ +CREATE TABLE user_preferences ( + user_id INT PRIMARY KEY, + notify_email BOOLEAN DEFAULT TRUE, + notify_line BOOLEAN DEFAULT TRUE, + digest_mode BOOLEAN DEFAULT FALSE COMMENT 'āļĢāļąāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđāļšāļšāļĢāļ§āļĄ (Digest) āđāļ—āļ™ Real - time', + ui_theme VARCHAR(20) DEFAULT 'light', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_user_prefs_user FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci; +-- āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļšāļąāļ™āļ—āļķāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ +-- 4.1 Audit Logs Enhancements +-- āļĢāļ­āļ‡āļĢāļąāļš: Req 6.1 +-- āđ€āļŦāļ•āļļāļœāļĨ: āļĢāļ­āļ‡āļĢāļąāļš Distributed Tracing āđāļĨāļ°āļĢāļ°āļšāļļāļ„āļ§āļēāļĄāļĢāļļāļ™āđāļĢāļ‡ +CREATE TABLE audit_logs ( + audit_id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡ Log', + request_id VARCHAR(100) NULL COMMENT 'Trace ID linking to app logs', + user_id INT COMMENT 'āļœāļđāđ‰āļāļĢāļ°āļ—āļģ', + action VARCHAR(100) NOT NULL COMMENT 'āļāļēāļĢāļāļĢāļ°āļ—āļģ ( + āđ€āļŠāđˆāļ™ rfa.create, + correspondence.update, + login.success + )', + severity ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL ') DEFAULT 'INFO', + entity_type VARCHAR(50) COMMENT 'āļ•āļēāļĢāļēāļ‡ / āđ‚āļĄāļ”āļđāļĨ (āđ€āļŠāđˆāļ™ ''rfa '', ''correspondence '')', + entity_id VARCHAR(50) COMMENT 'Primary ID āļ‚āļ­āļ‡āļĢāļ°āđ€āļšāļĩāļĒāļ™āļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļœāļĨāļāļĢāļ°āļ—āļģ', + details_json JSON COMMENT 'āļ‚āđ‰āļ­āļĄāļđāļĨāļšāļĢāļīāļšāļ—', + ip_address VARCHAR(45) COMMENT 'IP Address', + user_agent VARCHAR(255) COMMENT 'User Agent', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āđ€āļ§āļĨāļēāļ—āļĩāđˆāļāļĢāļ°āļ—āļģ', + -- [āđāļāđ‰āđ„āļ‚] āļĢāļ§āļĄ created_at āđ€āļ‚āđ‰āļēāļĄāļēāđƒāļ™ Primary Key āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļš Partition + PRIMARY KEY (audit_id, created_at), + -- [āđāļāđ‰āđ„āļ‚] āđƒāļŠāđ‰ Index āļ˜āļĢāļĢāļĄāļ”āļēāđāļ—āļ™ Foreign Key āđ€āļžāļ·āđˆāļ­āđ„āļĄāđˆāđƒāļŦāđ‰āļ•āļīāļ”āļ‚āđ‰āļ­āļˆāļģāļāļąāļ”āļ‚āļ­āļ‡ Partition Table + 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) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļšāļąāļ™āļ—āļķāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰' -- [āđ€āļžāļīāđˆāļĄ] āļ„āļģāļŠāļąāđˆāļ‡ Partition +PARTITION BY RANGE (YEAR(created_at)) ( + PARTITION p_old + VALUES LESS THAN (2024), + PARTITION p2024 + VALUES LESS THAN (2025), + PARTITION p2025 + VALUES LESS THAN (2026), + PARTITION p2026 + VALUES LESS THAN (2027), + PARTITION p2027 + VALUES LESS THAN (2028), + PARTITION p2028 + VALUES LESS THAN (2029), + PARTITION p2029 + VALUES LESS THAN (2030), + PARTITION p2030 + VALUES LESS THAN (2031), + PARTITION p_future + VALUES LESS THAN MAXVALUE +); +-- āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Email/Line/System) +CREATE TABLE notifications ( + id INT NOT NULL AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™', + user_id INT NOT NULL COMMENT 'ID āļœāļđāđ‰āđƒāļŠāđ‰', + title VARCHAR(255) NOT NULL COMMENT 'āļŦāļąāļ§āļ‚āđ‰āļ­āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™', + message TEXT NOT NULL COMMENT 'āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™', + notification_type ENUM('EMAIL', 'LINE', 'SYSTEM ') NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ— (EMAIL, LINE, SYSTEM)', + is_read BOOLEAN DEFAULT FALSE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāļ­āđˆāļēāļ™', + entity_type VARCHAR(50) COMMENT 'āđ€āļŠāđˆāļ™ ''rfa '', + ''circulation ''', + entity_id INT COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļ™āļ—āļīāļ•āļĩāļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + -- [āđāļāđ‰āđ„āļ‚] āļĢāļ§āļĄ created_at āđ€āļ‚āđ‰āļēāļĄāļēāđƒāļ™ Primary Key + PRIMARY KEY (id, created_at), + -- [āđāļāđ‰āđ„āļ‚] āđƒāļŠāđ‰ Index āļ˜āļĢāļĢāļĄāļ”āļēāđāļ—āļ™ Foreign 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) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Email / Line / System)' -- [āđ€āļžāļīāđˆāļĄ] āļ„āļģāļŠāļąāđˆāļ‡ Partition +PARTITION BY RANGE (YEAR(created_at)) ( + PARTITION p_old + VALUES LESS THAN (2024), + PARTITION p2024 + VALUES LESS THAN (2025), + PARTITION p2025 + VALUES LESS THAN (2026), + PARTITION p2026 + VALUES LESS THAN (2027), + PARTITION p2027 + VALUES LESS THAN (2028), + PARTITION p2028 + VALUES LESS THAN (2029), + PARTITION p2029 + VALUES LESS THAN (2030), + PARTITION p2030 + VALUES LESS THAN (2031), + PARTITION p_future + VALUES LESS THAN MAXVALUE +); +-- āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļ”āļąāļŠāļ™āļĩāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ (Full-text Search) +CREATE TABLE search_indices ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ”āļąāļŠāļ™āļĩ', + entity_type VARCHAR(50) NOT NULL COMMENT 'āļŠāļ™āļīāļ”āđ€āļ­āļ™āļ—āļīāļ•āļĩ (āđ€āļŠāđˆāļ™ ''correspondence '', ''rfa '')', + entity_id INT NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļ™āļ—āļīāļ•āļĩ', + content TEXT NOT NULL COMMENT 'āđ€āļ™āļ·āđ‰āļ­āļŦāļēāļ—āļĩāđˆāļˆāļ°āļ„āđ‰āļ™āļŦāļē', + indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ / āļ­āļąāļ›āđ€āļ”āļ•āļąāļŠāļ™āļĩ ' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļ”āļąāļŠāļ™āļĩāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ (Full - text Search)'; +-- āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļšāļąāļ™āļ—āļķāļāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ +CREATE TABLE backup_logs ( + id INT PRIMARY KEY AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļāļēāļĢāļŠāļģāļĢāļ­āļ‡', + backup_type ENUM('DATABASE', 'FILES', 'FULL') NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ— (DATABASE, FILES, FULL)', + backup_path VARCHAR(500) NOT NULL COMMENT 'āļ•āļģāđāļŦāļ™āđˆāļ‡āđ„āļŸāļĨāđŒāļŠāļģāļĢāļ­āļ‡', + file_size BIGINT COMMENT 'āļ‚āļ™āļēāļ”āđ„āļŸāļĨāđŒ', + status ENUM('STARTED', 'COMPLETED', 'FAILED') NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°', + started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āđ€āļ§āļĨāļēāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™', + completed_at TIMESTAMP NULL COMMENT 'āđ€āļ§āļĨāļēāđ€āļŠāļĢāđ‡āļˆāļŠāļīāđ‰āļ™', + error_message TEXT COMMENT 'āļ‚āđ‰āļ­āļ„āļ§āļēāļĄāļœāļīāļ”āļžāļĨāļēāļ” (āļ–āđ‰āļēāļĄāļĩ)' +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļšāļšāļąāļ™āļ—āļķāļāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāļģāļĢāļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ'; +-- 4.2 Virtual Columns for JSON Search (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āļŠāļģāļŦāļĢāļąāļš Correspondence) +-- āļĢāļ­āļ‡āļĢāļąāļš: Backend Plan T2.1, Req 3.11.3 +-- āđ€āļŦāļ•āļļāļœāļĨ: āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāđ€āļĢāđ‡āļ§āđƒāļ™āļāļēāļĢ Search/Sort āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™ JSON details +-- āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ: āļ•āđ‰āļ­āļ‡āļĄāļąāđˆāļ™āđƒāļˆāļ§āđˆāļē MariaDB āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™ 10.11+ āļĢāļ­āļ‡āļĢāļąāļš Syntax āļ™āļĩāđ‰ +-- āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļ”āļķāļ‡ Project ID āļ—āļĩāđˆāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđƒāļ™ details āļ­āļ­āļāļĄāļēāļ—āļģ Index +ALTER TABLE correspondence_revisions +ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId')) + ) VIRTUAL, + ADD INDEX idx_corr_rev_v_project (v_ref_project_id); +-- āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļ”āļķāļ‡ Document Type āļĒāđˆāļ­āļĒāļˆāļēāļ details +ALTER TABLE correspondence_revisions +ADD COLUMN v_doc_subtype VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))) VIRTUAL, + ADD INDEX idx_corr_rev_v_subtype (v_doc_subtype); +-- 2. āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ•āļēāļĢāļēāļ‡ correspondence_revisions +-- āđ€āļžāļīāđˆāļĄ Virtual Columns āđāļĨāļ° Schema Version +ALTER TABLE correspondence_revisions +ADD COLUMN schema_version INT DEFAULT 1 COMMENT 'āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡ Schema āļ—āļĩāđˆāđƒāļŠāđ‰āļāļąāļš details' +AFTER details; +-- āļ—āļģāđāļšāļšāđ€āļ”āļĩāļĒāļ§āļāļąāļ™āļāļąāļš RFA Revisions āļŦāļēāļāļĄāļĩāļāļēāļĢāđ€āļāđ‡āļš JSON details +ALTER TABLE rfa_revisions +ADD COLUMN details JSON NULL COMMENT 'RFA Specific Details' +AFTER description; +ALTER TABLE rfa_revisions +ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS ( + JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount')) + ) VIRTUAL; +ALTER TABLE rfa_revisions +ADD COLUMN schema_version INT DEFAULT 1 COMMENT 'Version āļ‚āļ­āļ‡ JSON Schema' +AFTER details; +CREATE INDEX idx_rfa_rev_v_drawing_count ON rfa_revisions(v_ref_drawing_count); +-- ... (āļ•āđˆāļ­āļ—āđ‰āļēāļĒāđ„āļŸāļĨāđŒāđ€āļ”āļīāļĄ) +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 11: Unified Workflow Engine (Phase 6A/Phase 3) +-- ============================================================ +DROP TABLE IF EXISTS workflow_histories; +DROP TABLE IF EXISTS workflow_instances; +DROP TABLE IF EXISTS workflow_definitions; +-- 1. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄ Workflow (Definition / DSL) +CREATE TABLE workflow_definitions ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID āļ‚āļ­āļ‡ Workflow Definition', + workflow_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ RFA_FLOW_V1, CORRESPONDENCE_FLOW_V1', + version INT NOT NULL DEFAULT 1 COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version', + description TEXT NULL COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ Workflow', + dsl JSON NOT NULL COMMENT 'āļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļš (YAML/JSON Format)', + compiled JSON NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Execution Tree āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§', + is_active BOOLEAN DEFAULT TRUE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + -- āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļĄāļĩ Workflow Code āđāļĨāļ° Version āļ‹āđ‰āļģāļāļąāļ™ + UNIQUE KEY uq_workflow_version (workflow_code, version) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄāļāļŽāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢ (Workflow DSL)'; +-- āļŠāļĢāđ‰āļēāļ‡ Index āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļē Workflow āļ—āļĩāđˆ Active āļĨāđˆāļēāļŠāļļāļ”āđ„āļ”āđ‰āđ€āļĢāđ‡āļ§āļ‚āļķāđ‰āļ™ +CREATE INDEX idx_workflow_active ON workflow_definitions(workflow_code, is_active, version); +-- 2. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš Workflow Instance (āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢāļˆāļĢāļīāļ‡) +CREATE TABLE workflow_instances ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID āļ‚āļ­āļ‡ Instance', + definition_id CHAR(36) NOT NULL COMMENT 'āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Definition āļ—āļĩāđˆāđƒāļŠāđ‰', + entity_type VARCHAR(50) NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (rfa_revision, correspondence_revision, circulation)', + entity_id VARCHAR(50) NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (String/Int)', + current_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™', + status ENUM('ACTIVE', 'COMPLETED', 'CANCELLED', 'TERMINATED') DEFAULT 'ACTIVE' COMMENT 'āļŠāļ–āļēāļ™āļ°āļ āļēāļžāļĢāļ§āļĄ', + context JSON NULL COMMENT 'āļ•āļąāļ§āđāļ›āļĢ Context āļŠāļģāļŦāļĢāļąāļšāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_wf_inst_def FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ'; +CREATE INDEX idx_wf_inst_entity ON workflow_instances(entity_type, entity_id); +CREATE INDEX idx_wf_inst_state ON workflow_instances(current_state); +-- 3. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Audit Log / History) +CREATE TABLE workflow_histories ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID', + instance_id CHAR(36) NOT NULL COMMENT 'āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Instance', + from_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ•āđ‰āļ™āļ—āļēāļ‡', + to_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡', + action VARCHAR(50) NOT NULL COMMENT 'Action āļ—āļĩāđˆāļāļĢāļ°āļ—āļģ', + action_by_user_id INT NULL COMMENT 'User ID āļœāļđāđ‰āļāļĢāļ°āļ—āļģ', + comment TEXT NULL COMMENT 'āļ„āļ§āļēāļĄāđ€āļŦāđ‡āļ™', + metadata JSON NULL COMMENT 'Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_wf_hist_inst FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° Workflow'; +CREATE INDEX idx_wf_hist_instance ON workflow_histories(instance_id); +CREATE INDEX idx_wf_hist_user ON workflow_histories(action_by_user_id); +-- ============================================================ +-- 5. PARTITIONING PREPARATION (Advance - Optional) +-- ============================================================ +-- āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ: āļāļēāļĢāļ—āļģ Partitioning āļšāļ™āļ•āļēāļĢāļēāļ‡āļ—āļĩāđˆāļĄāļĩāļ­āļĒāļđāđˆāđāļĨāđ‰āļ§ (audit_logs, notifications) +-- āļĄāļąāļāļˆāļ°āļ•āđ‰āļ­āļ‡ Drop Primary Key āđ€āļ”āļīāļĄ āđāļĨāđ‰āļ§āļŠāļĢāđ‰āļēāļ‡āđƒāļŦāļĄāđˆāđ‚āļ”āļĒāļĢāļ§āļĄ Partition Key (created_at) āđ€āļ‚āđ‰āļēāđ„āļ› +-- āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ™āļĩāđ‰āļ„āļ§āļĢāļ—āļģāđāļĒāļāļ•āđˆāļēāļ‡āļŦāļēāļāđ€āļĄāļ·āđˆāļ­āļĢāļ°āļšāļšāđ€āļĢāļīāđˆāļĄāļĄāļĩāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļĒāļ­āļ° āļŦāļĢāļ·āļ­āļ—āļģāđƒāļ™ Maintenance Window +-- +-- āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ SQL āļŠāļģāļŦāļĢāļąāļš Audit Logs (Reference Only): +-- ALTER TABLE audit_logs DROP PRIMARY KEY, ADD PRIMARY KEY (audit_id, created_at); +-- 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 +-- ); +-- ===================================================== +-- CREATE INDEXES +-- ===================================================== +-- Indexes for document_number_formats +CREATE INDEX idx_document_number_formats_project ON document_number_formats(project_id); +CREATE INDEX idx_document_number_formats_type ON document_number_formats(correspondence_type_id); +CREATE INDEX idx_document_number_formats_project_type ON document_number_formats(project_id, correspondence_type_id); +-- Indexes for document_number_counters +CREATE INDEX idx_document_number_counters_project ON document_number_counters(project_id); +CREATE INDEX idx_document_number_counters_org ON document_number_counters(originator_organization_id); +CREATE INDEX idx_document_number_counters_type ON document_number_counters(correspondence_type_id); +CREATE INDEX idx_document_number_counters_year ON document_number_counters(current_year); +-- Indexes for tags +CREATE INDEX idx_tags_name ON tags(tag_name); +CREATE INDEX idx_tags_created_at ON tags(created_at); +-- Indexes for correspondence_tags +CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags(correspondence_id); +CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags(tag_id); +-- Indexes for audit_logs +CREATE INDEX idx_audit_logs_user ON audit_logs(user_id); +CREATE INDEX idx_audit_logs_action ON audit_logs(action); +CREATE INDEX idx_audit_logs_entity ON audit_logs(entity_type, entity_id); +CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at); +CREATE INDEX idx_audit_logs_ip ON audit_logs(ip_address); +-- Indexes for notifications +CREATE INDEX idx_notifications_user ON notifications(user_id); +CREATE INDEX idx_notifications_type ON notifications(notification_type); +CREATE INDEX idx_notifications_read ON notifications(is_read); +CREATE INDEX idx_notifications_entity ON notifications(entity_type, entity_id); +CREATE INDEX idx_notifications_created_at ON notifications(created_at); +-- Indexes for search_indices +CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id); +CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at); +-- Indexes for backup_logs +CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type); +CREATE INDEX idx_backup_logs_status ON backup_logs(status); +CREATE INDEX idx_backup_logs_started_at ON backup_logs(started_at); +CREATE INDEX idx_backup_logs_completed_at ON backup_logs(completed_at); +-- ===================================================== +-- Additional Composite Indexes for Performance +-- ===================================================== +-- Composite index for document_number_counters for faster lookups +CREATE INDEX idx_doc_counter_composite ON document_number_counters( + project_id, + originator_organization_id, + correspondence_type_id, + current_year +); +-- Composite index for notifications for user-specific queries +CREATE INDEX idx_notifications_user_unread ON notifications(user_id, is_read, created_at); +-- Composite index for audit_logs for reporting +CREATE INDEX idx_audit_logs_reporting ON audit_logs(created_at, entity_type, action); +-- Composite index for search_indices for entity-based queries +CREATE INDEX idx_search_entities ON search_indices(entity_type, entity_id, indexed_at); +-- āļŠāļĢāđ‰āļēāļ‡ Index āļŠāļģāļŦāļĢāļąāļš Cleanup Job +CREATE INDEX idx_attachments_temp_cleanup ON attachments(is_temporary, expires_at); +CREATE INDEX idx_attachments_temp_id ON attachments(temp_id); +CREATE INDEX idx_audit_request_id ON audit_logs(request_id); +-- ===================================================== +-- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB +-- Generated from Data Dictionary +-- ===================================================== +-- ===================================================== +-- 11. 📊 Views & Procedures (āļ§āļīāļ§ āđāļĨāļ° āđ‚āļ›āļĢāļ‹āļĩāđ€āļ”āļ­āļĢāđŒ) +-- ===================================================== +-- View āđāļŠāļ”āļ‡ Revision "āļ›āļąāļˆāļˆāļļāļšāļąāļ™" āļ‚āļ­āļ‡ correspondences āļ—āļąāđ‰āļ‡āļŦāļĄāļ” (āļ—āļĩāđˆāđ„āļĄāđˆāđƒāļŠāđˆ RFA) +CREATE VIEW v_current_correspondences AS +SELECT c.id AS correspondence_id, + c.correspondence_number, + c.correspondence_type_id, + ct.type_code AS correspondence_type_code, + ct.type_name AS correspondence_type_name, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_code AS originator_code, + org.organization_name AS originator_name, + cr.id AS revision_id, + cr.revision_number, + cr.revision_label, + cr.title, + cr.document_date, + cr.issued_date, + cr.received_date, + cr.due_date, + cr.correspondence_status_id, + cs.status_code, + cs.status_name, + cr.created_by, + u.username AS created_by_username, + cr.created_at AS revision_created_at +FROM correspondences c + INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN organizations org ON c.originator_id = org.id + INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id + LEFT JOIN users u ON cr.created_by = u.user_id +WHERE cr.is_current = TRUE + AND c.correspondence_type_id NOT IN ( + SELECT id + FROM correspondence_types + WHERE type_code = 'RFA' + ) + AND c.deleted_at IS NULL; +-- View āđāļŠāļ”āļ‡ Revision "āļ›āļąāļˆāļˆāļļāļšāļąāļ™" āļ‚āļ­āļ‡ rfa_revisions āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +CREATE VIEW v_current_rfas AS +SELECT r.id AS rfa_id, + r.rfa_type_id, + rt.type_code AS rfa_type_code, + rt.type_name_th AS rfa_type_name_th, + rt.type_name_en AS rfa_type_name_en, + rr.correspondence_id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name, + c.originator_id, + org.organization_name AS originator_name, + rr.id AS revision_id, + rr.revision_number, + rr.revision_label, + rr.title, + rr.document_date, + rr.issued_date, + rr.received_date, + rr.approved_date, + rr.rfa_status_code_id, + rsc.status_code AS rfa_status_code, + rsc.status_name AS rfa_status_name, + rr.rfa_approve_code_id, + rac.approve_code AS rfa_approve_code, + rac.approve_name AS rfa_approve_name, + rr.created_by, + u.username AS created_by_username, + rr.created_at AS revision_created_at +FROM rfas r + INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id + INNER JOIN rfa_revisions rr ON r.id = rr.rfa_id + INNER JOIN correspondences c ON rr.correspondence_id = c.id + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN organizations org ON c.originator_id = org.id + INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id + LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id + LEFT JOIN users u ON rr.created_by = u.user_id +WHERE rr.is_current = TRUE + AND r.deleted_at IS NULL + AND c.deleted_at IS NULL; +-- View āđāļŠāļ”āļ‡āļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Contract, Project, āđāļĨāļ° Organization +CREATE VIEW v_contract_parties_all AS +SELECT c.id AS contract_id, + c.contract_code, + c.contract_name, + p.id AS project_id, + p.project_code, + p.project_name, + o.id AS organization_id, + o.organization_code, + o.organization_name, + co.role_in_contract +FROM contracts c + INNER JOIN projects p ON c.project_id = p.id + INNER JOIN contract_organizations co ON c.id = co.contract_id + INNER JOIN organizations o ON co.organization_id = o.id +WHERE c.is_active = TRUE; +-- ============================================================ +-- View: v_user_tasks (Unified Workflow Engine Edition) +-- ============================================================ +-- āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļĢāļ§āļĄāļĢāļēāļĒāļāļēāļĢāļ‡āļēāļ™āļ—āļĩāđˆāļĒāļąāļ‡āļ„āđ‰āļēāļ‡āļ­āļĒāļđāđˆ (Status = ACTIVE) āļˆāļēāļāļ—āļļāļāļĢāļ°āļšāļš (RFA, Circulation, Correspondence) +-- āđ€āļžāļ·āđˆāļ­āļ™āļģāđ„āļ›āđāļŠāļ”āļ‡āđƒāļ™āļŦāļ™āđ‰āļē Dashboard "My Tasks" +-- ============================================================ +CREATE OR REPLACE VIEW v_user_tasks AS +SELECT -- 1. Workflow Instance Info + wi.id AS instance_id, + wd.workflow_code, + wi.current_state, + wi.status AS workflow_status, + wi.created_at AS assigned_at, + -- 2. Entity Info (Polymorphic Identity) + wi.entity_type, + wi.entity_id, + -- 3. Normalized Document Info (āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļĢāļīāļ‡āļˆāļēāļāļ•āļēāļĢāļēāļ‡āļĨāļđāļāļ•āļēāļĄāļ›āļĢāļ°āđ€āļ āļ—) + -- āđƒāļŠāđ‰ CASE WHEN āđ€āļžāļ·āđˆāļ­āļĢāļ§āļĄāļ„āļ­āļĨāļąāļĄāļ™āđŒāļ—āļĩāđˆāļŠāļ·āđˆāļ­āļ•āđˆāļēāļ‡āļāļąāļ™āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™āļŠāļ·āđˆāļ­āļāļĨāļēāļ‡ (document_number, subject) + CASE + WHEN wi.entity_type = 'rfa_revision' THEN rfa_corr.correspondence_number + WHEN wi.entity_type = 'circulation' THEN circ.circulation_no + WHEN wi.entity_type = 'correspondence_revision' THEN corr_corr.correspondence_number + ELSE 'N/A' + END AS document_number, + CASE + WHEN wi.entity_type = 'rfa_revision' THEN rfa_rev.title + WHEN wi.entity_type = 'circulation' THEN circ.circulation_subject + WHEN wi.entity_type = 'correspondence_revision' THEN corr_rev.title + ELSE 'Unknown Document' + END AS subject, + -- 4. Context Info (āļŠāļģāļŦāļĢāļąāļš Filter āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāļĄāļ­āļ‡āđ€āļŦāđ‡āļ™āļ—āļĩāđˆ Backend) + -- āļ”āļķāļ‡āđ€āļ›āđ‡āļ™ JSON String āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰ Backend āđ„āļ› Parse āļŦāļĢāļ·āļ­āđƒāļŠāđ‰ JSON_CONTAINS + JSON_UNQUOTE(JSON_EXTRACT(wi.context, '$.ownerId')) AS owner_id, + JSON_EXTRACT(wi.context, '$.assigneeIds') AS assignee_ids_json +FROM workflow_instances wi + JOIN workflow_definitions wd ON wi.definition_id = wd.id -- 5. Joins for RFA (āļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļŦāļ™āđˆāļ­āļĒāđ€āļžāļĢāļēāļ° RFA āļœāļđāļāļāļąāļš Correspondence āļ­āļĩāļāļ—āļĩ) + LEFT JOIN rfa_revisions rfa_rev ON wi.entity_type = 'rfa_revision' + AND wi.entity_id = CAST(rfa_rev.id AS CHAR) + LEFT JOIN correspondences rfa_corr ON rfa_rev.correspondence_id = rfa_corr.id -- 6. Joins for Circulation + LEFT JOIN circulations circ ON wi.entity_type = 'circulation' + AND wi.entity_id = CAST(circ.id AS CHAR) -- 7. Joins for Correspondence + LEFT JOIN correspondence_revisions corr_rev ON wi.entity_type = 'correspondence_revision' + AND wi.entity_id = CAST(corr_rev.id AS CHAR) + LEFT JOIN correspondences corr_corr ON corr_rev.correspondence_id = corr_corr.id -- 8. Filter āđ€āļ‰āļžāļēāļ°āļ‡āļēāļ™āļ—āļĩāđˆāļĒāļąāļ‡ Active āļ­āļĒāļđāđˆ +WHERE wi.status = 'ACTIVE'; +-- View āđāļŠāļ”āļ‡ audit_logs āļžāļĢāđ‰āļ­āļĄāļ‚āđ‰āļ­āļĄāļđāļĨ username āđāļĨāļ° email āļ‚āļ­āļ‡āļœāļđāđ‰āļāļĢāļ°āļ—āļģ +CREATE VIEW v_audit_log_details AS +SELECT al.audit_id, + al.user_id, + u.username, + u.email, + u.first_name, + u.last_name, + al.action, + al.entity_type, + al.entity_id, + al.details_json, + al.ip_address, + al.user_agent, + al.created_at +FROM audit_logs al + LEFT JOIN users u ON al.user_id = u.user_id; +-- View āļĢāļ§āļĄāļŠāļīāļ—āļ˜āļīāđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Global + Project) āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļļāļāļ„āļ™ +CREATE VIEW v_user_all_permissions AS -- Global Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'GLOBAL' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Global scope +WHERE p.is_active = 1 + AND ua.organization_id IS NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Organization-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + NULL AS project_id, + NULL AS contract_id, + 'ORGANIZATION' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Organization scope +WHERE p.is_active = 1 + AND ua.organization_id IS NOT NULL + AND ua.project_id IS NULL + AND ua.contract_id IS NULL +UNION ALL +-- Project-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + NULL AS contract_id, + 'PROJECT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Project scope +WHERE p.is_active = 1 + AND ua.project_id IS NOT NULL + AND ua.contract_id IS NULL +UNION ALL +-- Contract-specific Permissions +SELECT ua.user_id, + ua.role_id, + r.role_name, + rp.permission_id, + p.permission_name, + p.module, + p.scope_level, + ua.organization_id, + ua.project_id, + ua.contract_id, + 'CONTRACT' AS permission_scope +FROM user_assignments ua + INNER JOIN roles r ON ua.role_id = r.role_id + INNER JOIN role_permissions rp ON ua.role_id = rp.role_id + INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Contract scope +WHERE p.is_active = 1 + AND ua.contract_id IS NOT NULL; +-- ===================================================== +-- Additional Useful Views +-- ===================================================== +-- View āđāļŠāļ”āļ‡āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāļĄāļĩāđ„āļŸāļĨāđŒāđāļ™āļš +CREATE VIEW v_documents_with_attachments AS +SELECT 'CORRESPONDENCE' AS document_type, + c.id AS document_id, + c.correspondence_number AS document_number, + c.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM correspondences c + INNER JOIN projects p ON c.project_id = p.id + LEFT JOIN correspondence_attachments ca ON c.id = ca.correspondence_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +WHERE c.deleted_at IS NULL +GROUP BY c.id, + c.correspondence_number, + c.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CIRCULATION' AS document_type, + circ.id AS document_id, + circ.circulation_no AS document_number, + corr.project_id, + p.project_code, + p.project_name, + COUNT(ca.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM circulations circ + INNER JOIN correspondences corr ON circ.correspondence_id = corr.id + INNER JOIN projects p ON corr.project_id = p.id + LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id + LEFT JOIN attachments a ON ca.attachment_id = a.id +GROUP BY circ.id, + circ.circulation_no, + corr.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'SHOP_DRAWING' AS document_type, + sdr.id AS document_id, + sd.drawing_number AS document_number, + sd.project_id, + p.project_code, + p.project_name, + COUNT(sdra.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM shop_drawing_revisions sdr + INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id + INNER JOIN projects p ON sd.project_id = p.id + LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id + LEFT JOIN attachments a ON sdra.attachment_id = a.id +WHERE sd.deleted_at IS NULL +GROUP BY sdr.id, + sd.drawing_number, + sd.project_id, + p.project_code, + p.project_name +UNION ALL +SELECT 'CONTRACT_DRAWING' AS document_type, + cd.id AS document_id, + cd.condwg_no AS document_number, + cd.project_id, + p.project_code, + p.project_name, + COUNT(cda.attachment_id) AS attachment_count, + MAX(a.created_at) AS latest_attachment_date +FROM contract_drawings cd + INNER JOIN projects p ON cd.project_id = p.id + LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id + LEFT JOIN attachments a ON cda.attachment_id = a.id +WHERE cd.deleted_at IS NULL +GROUP BY cd.id, + cd.condwg_no, + cd.project_id, + p.project_code, + p.project_name; +-- View āđāļŠāļ”āļ‡āļŠāļ–āļīāļ•āļīāđ€āļ­āļāļŠāļēāļĢāļ•āļēāļĄāļ›āļĢāļ°āđ€āļ āļ—āđāļĨāļ°āļŠāļ–āļēāļ™āļ° +CREATE VIEW v_document_statistics AS +SELECT p.id AS project_id, + p.project_code, + p.project_name, + ct.id AS correspondence_type_id, + ct.type_code, + ct.type_name, + cs.id AS status_id, + cs.status_code, + cs.status_name, + COUNT(DISTINCT c.id) AS document_count, + COUNT(DISTINCT cr.id) AS revision_count +FROM projects p + CROSS JOIN correspondence_types ct + CROSS JOIN correspondence_status cs + LEFT JOIN correspondences c ON p.id = c.project_id + AND ct.id = c.correspondence_type_id + LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id + AND cs.id = cr.correspondence_status_id + AND cr.is_current = TRUE +WHERE p.is_active = 1 + AND ct.is_active = 1 + AND cs.is_active = 1 +GROUP BY p.id, + p.project_code, + p.project_name, + ct.id, + ct.type_code, + ct.type_name, + cs.id, + cs.status_code, + cs.status_name; +-- ===================================================== +-- Indexes for View Performance Optimization +-- ===================================================== +-- Indexes for v_current_correspondences performance +CREATE INDEX idx_correspondences_type_project ON correspondences(correspondence_type_id, project_id); +CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions(is_current, correspondence_status_id); +CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions(correspondence_id, is_current); +-- Indexes for v_current_rfas performance +CREATE INDEX idx_rfa_revisions_current_status ON rfa_revisions(is_current, rfa_status_code_id); +CREATE INDEX idx_rfa_revisions_rfa_current ON rfa_revisions(rfa_id, is_current); +-- Indexes for document statistics performance +CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id); +CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current); +SET FOREIGN_KEY_CHECKS = 1; diff --git a/8_lcbp3_v1_4_5_seed.sql b/8_lcbp3_v1_4_5_seed.sql new file mode 100644 index 0000000..4eaf83c --- /dev/null +++ b/8_lcbp3_v1_4_5_seed.sql @@ -0,0 +1,2127 @@ +-- ========================================================== +-- DMS v1.4.5 Document Management System Database +-- Deploy Seed Data +-- ========================================================== +-- Seed organization +INSERT INTO organizations (id, organization_code, organization_name) +VALUES (1, 'āļāļ—āļ—.', 'āļāļēāļĢāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāđˆāļ‡āļ›āļĢāļ°āđ€āļ—āļĻāđ„āļ—āļĒ'), + ( + 10, + 'āļŠāļ„āļ‰.3', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3' + ), + ( + 11, + 'āļŠāļ„āļ‰.3-01', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ—āļĩāđˆāļ›āļĢāļķāļāļĐāļēāļ„āļ§āļšāļ„āļļāļĄāļ‡āļēāļ™' + ), + (12, 'āļŠāļ„āļ‰.3-02', 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ'), + ( + 13, + 'āļŠāļ„āļ‰.3-03', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ­āļēāļ„āļēāļĢāđāļĨāļ°āļĢāļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„' + ), + ( + 14, + 'āļŠāļ„āļ‰.3-04', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ•āļĢāļ§āļˆāļŠāļ­āļšāļœāļĨāļāļĢāļ°āļ—āļšāļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ' + ), + (15, 'āļŠāļ„āļ‰.3-05', 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āđ€āļĒāļĩāļĒāļ§āļĒāļēāļāļēāļĢāļ›āļĢāļ°āļĄāļ‡'), + ( + 16, + 'āļŠāļ„āļ‰.3-06', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 3' + ), + ( + 17, + 'āļŠāļ„āļ‰.3-07', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 4' + ), + ( + 18, + 'āļŠāļ„āļ‰.3-xx', + 'āļ•āļĢāļ§āļˆāļĢāļąāļšāļžāļąāļŠāļ”āļļ āļ—āļĩāđˆāļ›āļĢāļķāļāļĐāļēāļ­āļ­āļāđāļšāļš āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 4' + ), + (21, 'TEAM', 'Designer Consulting Ltd.'), + (22, 'āļ„āļ„āļ‡.', 'Construction Supervision Ltd.'), + (41, 'āļœāļĢāļĄ.1', 'Contractor āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ'), + (42, 'āļœāļĢāļĄ.2', 'Contractor āļ­āļēāļ„āļēāļĢāđāļĨāļ°āļĢāļ°āļšāļš'), + (43, 'āļœāļĢāļĄ.3', 'Contractor #3 Ltd.'), + (44, 'āļœāļĢāļĄ.4', 'Contractor #4 Ltd.'), + (31, 'EN', 'Third Party Environment'), + (32, 'CAR', 'Third Party Fishery Care'); +-- Seed project +INSERT INTO projects (project_code, project_name) +VALUES ( + 'LCBP3', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1-4)' + ), + ( + 'LCBP3C1', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ' + ), + ( + 'LCBP3C2', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 2) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ āļ—āđˆāļēāđ€āļ—āļĩāļĒāļšāđ€āļĢāļ·āļ­ āļĢāļ°āļšāļšāļ–āļ™āļ™ āđāļĨāļ°āļĢāļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„' + ), + ( + 'LCBP3C3', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 3) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' + ), + ( + 'LCBP3C4', + 'āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 4) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' + ); +-- Seed contract +-- āđƒāļŠāđ‰ Subquery āđ€āļžāļ·āđˆāļ­āļ”āļķāļ‡ project_id āļĄāļēāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡ āļ—āļģāđƒāļŦāđ‰āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĄāļēāļ™āļąāđˆāļ‡āļˆāļąāļ”āļāļēāļĢ ID āļ”āđ‰āļ§āļĒāļ•āļąāļ§āđ€āļ­āļ‡ +INSERT INTO contracts ( + contract_code, + contract_name, + project_id, + is_active + ) +VALUES ( + 'DSLCBP3', + 'āļ‡āļēāļ™āļˆāđ‰āļēāļ‡āļ—āļĩāđˆāļ›āļĢāļĩāļāļĐāļēāļ­āļ­āļāđāļšāļš āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'PSLCBP3', + 'āļ‡āļēāļ™āļˆāđ‰āļēāļ‡āļ—āļĩāđˆāļ›āļĢāļĩāļāļĐāļēāļ„āļ§āļšāļ„āļļāļĄāļ‡āļēāļ™ āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ), + ( + 'LCBP3-C1', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1' + ), + TRUE + ), + ( + 'LCBP3-C2', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 2) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ āļ—āđˆāļēāđ€āļ—āļĩāļĒāļšāđ€āļĢāļ·āļ­ āļĢāļ°āļšāļšāļ–āļ™āļ™ āđāļĨāļ°āļĢāļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2' + ), + TRUE + ), + ( + 'LCBP3-C3', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 3) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C3' + ), + TRUE + ), + ( + 'LCBP3-C4', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 4) āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C4' + ), + TRUE + ), + ( + 'ENLCBP3', + 'āļ‡āļēāļ™āļˆāđ‰āļēāļ‡āđ€āļŦāļĄāļēāļ•āļĢāļ§āļˆāļŠāļ­āļšāļœāļĨāļāļĢāļ°āļ—āļšāļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄāļ™āļ°āļŦāļ§āđˆāļēāļ‡āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāļžāļąāļ’āļ™āļēāļ—āđˆāļēāđ€āļĢāļ·āļ­āđāļŦāļĨāļĄāļ‰āļšāļąāļ‡ āļĢāļ°āļĒāļ°āļ—āļĩāđˆ 3 (āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1-4)', + ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3' + ), + TRUE + ); +-- Seed user +-- Initial SUPER_ADMIN user +INSERT INTO `users` ( + `user_id`, + `username`, + `password_hash`, + `first_name`, + `last_name`, + `email`, + `line_id`, + `primary_organization_id` + ) +VALUES ( + 1, + 'superadmin', + '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', + 'Super', + 'Admin', + 'superadmin @example.com', + NULL, + NULL + ), + ( + 2, + 'admin', + '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', + 'Admin', + 'āļ„āļ„āļ‡.', + 'admin@example.com', + NULL, + 1 + ), + ( + 3, + 'editor01', + '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', + 'DC', + 'C1', + 'editor01 @example.com', + NULL, + 41 + ), + ( + 4, + 'viewer01', + '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', + 'Viewer', + 'āļŠāļ„āļ‰.03', + 'viewer01 @example.com', + NULL, + 10 + ); +-- ========================================================== +-- Seed Roles (āļšāļ—āļšāļēāļ—āļžāļ·āđ‰āļ™āļāļēāļ™ 5 āļšāļ—āļšāļēāļ— āļ•āļēāļĄ Req 4.3) +-- ========================================================== +-- 1. Superadmin (Global) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 1, + 'Superadmin', + 'Global', + 'āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļŠāļđāļ‡āļŠāļļāļ”: āļŠāļēāļĄāļēāļĢāļ–āļ—āļģāļ—āļļāļāļ­āļĒāđˆāļēāļ‡āđƒāļ™āļĢāļ°āļšāļš, āļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ, āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļĢāļ°āļ”āļąāļš Global' + ); +-- 2. Org Admin (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 2, + 'Org Admin', + 'Organization', + 'āļœāļđāđ‰āļ”āļđāđāļĨāļ­āļ‡āļ„āđŒāļāļĢ: āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļšāļ—āļšāļēāļ— / āļŠāļīāļ—āļ˜āļīāđŒāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āđāļĨāļ°āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ' + ); +-- 3. Document Control (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 3, + 'Document Control', + 'Organization', + 'āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ: āđ€āļžāļīāđˆāļĄ / āđāļāđ‰āđ„āļ‚ / āļĨāļšāđ€āļ­āļāļŠāļēāļĢ, āđāļĨāļ°āļāļģāļŦāļ™āļ”āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ' + ); +-- 4. Editor (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 4, + 'Editor', + 'Organization', + 'āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ: āđ€āļžāļīāđˆāļĄ / āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒ' + ); +-- 5. Viewer (Organization) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 5, + 'Viewer', + 'Organization', + 'āļœāļđāđ‰āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ: āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™' + ); +-- 6. Project Manager (Project) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 6, + 'Project Manager', + 'Project', + 'āļœāļđāđ‰āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ: āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļŠāļĢāđ‰āļēāļ‡ / āļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āđāļĨāļ°āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ' + ); +-- 7. Contract Admin (Contract) +INSERT INTO roles (role_id, role_name, scope, description) +VALUES ( + 7, + 'Contract Admin', + 'Contract', + 'āļœāļđāđ‰āļ”āļđāđāļĨāļŠāļąāļāļāļē: āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āļŠāļąāļāļāļē, āļŠāļĢāđ‰āļēāļ‡ / āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāđ€āļ‰āļžāļēāļ°āļŠāļąāļāļāļē, āđāļĨāļ°āļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļŠāļąāļāļāļē' + ); +-- ===================================================== +-- 2. Seed Permissions (āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”) +-- āļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļĢāļ°āļšāļšāđāļĨāļ°āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļŦāļĨāļąāļ (System & Master Data) +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 1, + 'system.manage_all', + 'āļ—āļģāļ—āļļāļāļ­āļĒāđˆāļēāļ‡āđƒāļ™āļĢāļ°āļšāļš (Superadmin Power)' + ), + -- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ + (2, 'organization.create', 'āļŠāļĢāđ‰āļēāļ‡āļ­āļ‡āļ„āđŒāļāļĢāđƒāļŦāļĄāđˆ'), + (3, 'organization.edit', 'āđāļāđ‰āđ„āļ‚āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļ‡āļ„āđŒāļāļĢ'), + (4, 'organization.delete', 'āļĨāļšāļ­āļ‡āļ„āđŒāļāļĢ'), + (5, 'organization.view', 'āļ”āļđāļĢāļēāļĒāļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ'), + -- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ + (6, 'project.create', 'āļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāđƒāļŦāļĄāđˆ'), + (7, 'project.edit', 'āđāļāđ‰āđ„āļ‚āļ‚āđ‰āļ­āļĄāļđāļĨāđ‚āļ„āļĢāļ‡āļāļēāļĢ'), + (8, 'project.delete', 'āļĨāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ'), + (9, 'project.view', 'āļ”āļđāļĢāļēāļĒāļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ'), + -- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļšāļ—āļšāļēāļ—āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒ (Roles & Permissions) + (10, 'role.create', 'āļŠāļĢāđ‰āļēāļ‡āļšāļ—āļšāļēāļ— (Role) āđƒāļŦāļĄāđˆ'), + (11, 'role.edit', 'āđāļāđ‰āđ„āļ‚āļšāļ—āļšāļēāļ— (Role)'), + (12, 'role.delete', 'āļĨāļšāļšāļ—āļšāļēāļ— (Role)'), + ( + 13, + 'permission.assign', + 'āļĄāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒāđƒāļŦāđ‰āļāļąāļšāļšāļ—āļšāļēāļ— (Role)' + ), + -- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ (Master Data) + ( + 14, + 'master_data.document_type.manage', + 'āļˆāļąāļ”āļāļēāļĢāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (Document Types)' + ), + ( + 15, + 'master_data.document_status.manage', + 'āļˆāļąāļ”āļāļēāļĢāļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ (Document Statuses)' + ), + ( + 16, + 'master_data.drawing_category.manage', + 'āļˆāļąāļ”āļāļēāļĢāļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāđāļšāļš (Drawing Categories)' + ), + (17, 'master_data.tag.manage', 'āļˆāļąāļ”āļāļēāļĢ Tags'), + -- āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ + (18, 'user.create', 'āļŠāļĢāđ‰āļēāļ‡āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āđƒāļŦāļĄāđˆ'), + (19, 'user.edit', 'āđāļāđ‰āđ„āļ‚āļ‚āđ‰āļ­āļĄāļđāļĨāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™'), + (20, 'user.delete', 'āļĨāļš / āļ›āļīāļ”āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļœāļđāđ‰āđƒāļŠāđ‰'), + (21, 'user.view', 'āļ”āļđāļ‚āđ‰āļ­āļĄāļđāļĨāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™'), + ( + 22, + 'user.assign_organization', + 'āļĄāļ­āļšāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āđƒāļŦāđ‰āļāļąāļšāļ­āļ‡āļ„āđŒāļāļĢ' + ); +-- ===================================================== +-- == 2. āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢāđāļĨāļ°āļŠāļąāļāļāļē (Project & Contract) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 23, + 'project.manage_members', + 'āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ (āđ€āļŠāļīāļ / āļ–āļ­āļ”āļŠāļĄāļēāļŠāļīāļ)' + ), + ( + 24, + 'project.create_contracts', + 'āļŠāļĢāđ‰āļēāļ‡āļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ' + ), + ( + 25, + 'project.manage_contracts', + 'āļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ' + ), + ( + 26, + 'project.view_reports', + 'āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āļĢāļ°āļ”āļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ' + ), + ( + 27, + 'contract.manage_members', + 'āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āļŠāļąāļāļāļē' + ), + (28, 'contract.view', 'āļ”āļđāļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļąāļāļāļē'); +-- ===================================================== +-- == 3. āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ (Document Management) == +-- ===================================================== +-- āļŠāļīāļ—āļ˜āļīāđŒāļ—āļąāđˆāļ§āđ„āļ›āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ—āļļāļāļ›āļĢāļ°āđ€āļ āļ— +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 29, + 'document.create_draft', + 'āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļŠāļ–āļēāļ™āļ°āļ‰āļšāļąāļšāļĢāđˆāļēāļ‡ (Draft) ' + ), + (30, 'document.submit', 'āļŠāđˆāļ‡āđ€āļ­āļāļŠāļēāļĢ (Submitted)'), + (31, 'document.view', 'āļ”āļđāđ€āļ­āļāļŠāļēāļĢ'), + (32, 'document.edit', 'āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢ (āļ—āļąāđˆāļ§āđ„āļ›)'), + ( + 33, + 'document.admin_edit', + 'āđāļāđ‰āđ„āļ‚ / āļ–āļ­āļ™ / āļĒāļāđ€āļĨāļīāļāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāđˆāļ‡āđāļĨāđ‰āļ§ (Admin Power) ' + ), + (34, 'document.delete', 'āļĨāļšāđ€āļ­āļāļŠāļēāļĢ'), + ( + 35, + 'document.attach', + 'āļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāđāļ™āļš (āļ­āļąāļ›āđ‚āļŦāļĨāļ” / āļĨāļš) ' + ), + -- āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Correspondence + ( + 36, + 'correspondence.create', + 'āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš (Correspondence) ' + ), + -- āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Request for Approval (RFA) + (37, 'rfa.create', 'āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī (RFA)'), + ( + 38, + 'rfa.manage_shop_drawings', + 'āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨ Shop Drawing āđāļĨāļ° Contract Drawing āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡' + ), + -- āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Shop Drawing & Contract Drawing + ( + 39, + 'drawing.create', + 'āļŠāļĢāđ‰āļēāļ‡ / āđāļāđ‰āđ„āļ‚āļ‚āđ‰āļ­āļĄāļđāļĨāđāļšāļš (Shop / Contract Drawing)' + ), + -- āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Transmittal + ( + 40, + 'transmittal.create', + 'āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (Transmittal)' + ), + -- āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš Circulation Sheet (āđƒāļšāđ€āļ§āļĩāļĒāļ™) + ( + 41, + 'circulation.create', + 'āļŠāļĢāđ‰āļēāļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™āđ€āļ­āļāļŠāļēāļĢ (Circulation)' + ), + ( + 42, + 'circulation.respond', + 'āļ•āļ­āļšāļāļĨāļąāļšāđƒāļšāđ€āļ§āļĩāļĒāļ™ (Main / Action)' + ), + ( + 43, + 'circulation.acknowledge', + 'āļĢāļąāļšāļ—āļĢāļēāļšāđƒāļšāđ€āļ§āļĩāļĒāļ™ (Information)' + ), + (44, 'circulation.close', 'āļ›āļīāļ”āđƒāļšāđ€āļ§āļĩāļĒāļ™'); +-- ===================================================== +-- == 4. āļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Workflow == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES ( + 45, + 'workflow.action_review', + 'āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ›āļąāļˆāļˆāļļāļšāļąāļ™ (āđ€āļŠāđˆāļ™ āļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāđ‰āļ§)' + ), + ( + 46, + 'workflow.force_proceed', + 'āļšāļąāļ‡āļ„āļąāļšāđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ–āļąāļ”āđ„āļ› (Document Control Power)' + ), + ( + 47, + 'workflow.revert', + 'āļĒāđ‰āļ­āļ™āļāļĨāļąāļšāđ„āļ›āļĒāļąāļ‡āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāđˆāļ­āļ™āļŦāļ™āđ‰āļē (Document Control Power)' + ); +-- ===================================================== +-- == 5. āļŠāļīāļ—āļ˜āļīāđŒāļ”āđ‰āļēāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāđāļĨāļ°āļĢāļēāļĒāļ‡āļēāļ™ (Search & Reporting) == +-- ===================================================== +INSERT INTO permissions (permission_id, permission_name, description) +VALUES (48, 'search.advanced', 'āđƒāļŠāđ‰āļ‡āļēāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡'), + ( + 49, + 'report.generate', + 'āļŠāļĢāđ‰āļēāļ‡āļĢāļēāļĒāļ‡āļēāļ™āļŠāļĢāļļāļ› (āļĢāļēāļĒāļ§āļąāļ™ / āļŠāļąāļ›āļ”āļēāļŦāđŒ / āđ€āļ”āļ·āļ­āļ™ / āļ›āļĩ)' + ); +-- ========================================================== +-- Seed Role-Permissions Mapping (āļˆāļąāļšāļ„āļđāđˆāļŠāļīāļ—āļ˜āļīāđŒāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™) +-- ========================================================== +-- Seed data for the 'role_permissions 'table +-- This table links roles to their specific permissions. +-- NOTE: This assumes the role_id and permission_id from the previous seed data files. +-- Superadmin (role_id = 1), Org Admin (role_id = 2), Document Control (role_id = 3), etc. +-- ===================================================== +-- == 1. Superadmin (role_id = 1) - Gets ALL permissions == +-- ===================================================== +-- Superadmin can do everything. We can dynamically link all permissions to this role. +-- This is a robust way to ensure Superadmin always has full power. +INSERT INTO role_permissions (role_id, permission_id) +SELECT 1, + permission_id +FROM permissions; +-- ===================================================== +-- == 2. Org Admin (role_id = 2) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ + (2, 18), + -- user.create + (2, 19), + -- user.edit + (2, 20), + -- user.delete + (2, 21), + -- user.view + (2, 22), + -- user.assign_organization + -- āļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ + (2, 3), + -- organization.edit + (2, 5), + -- organization.view + -- āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļ—āļĩāđˆāļ­āļ™āļļāļāļēāļ• (āđ€āļ‰āļžāļēāļ° Tags) + (2, 17), + -- master_data.tag.manage + -- āļ”āļđāļ‚āđ‰āļ­āļĄāļđāļĨāļ•āđˆāļēāļ‡āđ† āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ + (2, 31), + -- document.view + (2, 9), + -- project.view + (2, 28), + -- contract.view + -- āļāļēāļĢāļ„āđ‰āļ™āļŦāļēāđāļĨāļ°āļĢāļēāļĒāļ‡āļēāļ™ + (2, 48), + -- search.advanced + (2, 49); +-- report.generate +-- ===================================================== +-- == 3. Document Control (role_id = 3) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” + (3, 29), + -- document.create_draft + (3, 30), + -- document.submit + (3, 31), + -- document.view + (3, 32), + -- document.edit + (3, 33), + -- document.admin_edit + (3, 34), + -- document.delete + (3, 35), + -- document.attach + -- āļŠāļīāļ—āļ˜āļīāđŒāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ— + (3, 36), + -- correspondence.create + (3, 37), + -- rfa.create + (3, 39), + -- drawing.create + (3, 40), + -- transmittal.create + (3, 41), + -- circulation.create + -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢ Workflow + (3, 45), + -- workflow.action_review + (3, 46), + -- workflow.force_proceed + (3, 47), + -- workflow.revert + -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢ Circulation + (3, 42), + -- circulation.respond + (3, 43), + -- circulation.acknowledge + (3, 44), + -- circulation.close + -- āļŠāļīāļ—āļ˜āļīāđŒāļ­āļ·āđˆāļ™āđ† āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ + (3, 38), + -- rfa.manage_shop_drawings + (3, 48), + -- search.advanced + (3, 49); +-- report.generate +-- ===================================================== +-- == 4. Editor (role_id = 4) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļŠāļīāļ—āļ˜āļīāđŒāđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢ (āđāļ•āđˆāđ„āļĄāđˆāđƒāļŠāđˆāļŠāļīāļ—āļ˜āļīāđŒ Admin) + (4, 29), + -- document.create_draft + (4, 30), + -- document.submit + (4, 31), + -- document.view + (4, 32), + -- document.edit + (4, 35), + -- document.attach + -- āļŠāļīāļ—āļ˜āļīāđŒāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ— + (4, 36), + -- correspondence.create + (4, 37), + -- rfa.create + (4, 39), + -- drawing.create + (4, 40), + -- transmittal.create + (4, 41), + -- circulation.create + -- āļŠāļīāļ—āļ˜āļīāđŒāļ­āļ·āđˆāļ™āđ† āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ + (4, 38), + -- rfa.manage_shop_drawings + (4, 48); +-- search.advanced +-- ===================================================== +-- == 5. Viewer (role_id = 5) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļŠāļīāļ—āļ˜āļīāđŒāļ”āļđāđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ + (5, 31), + -- document.view + (5, 48); +-- search.advanced +-- ===================================================== +-- == 6. Project Manager (role_id = 6) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ + (6, 23), + -- project.manage_members + (6, 24), + -- project.create_contracts + (6, 25), + -- project.manage_contracts + (6, 26), + -- project.view_reports + (6, 9), + -- project.view + -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļĢāļ°āļ”āļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢ + (6, 16), + -- master_data.drawing_category.manage + -- āļŠāļīāļ—āļ˜āļīāđŒāļ”āļđāļ‚āđ‰āļ­āļĄāļđāļĨāđƒāļ™āļŠāļąāļāļāļē + (6, 28), + -- contract.view + -- āļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ (āļĢāļ°āļ”āļąāļš Editor) + (6, 29), + -- document.create_draft + (6, 30), + -- document.submit + (6, 31), + -- document.view + (6, 32), + -- document.edit + (6, 35), + -- document.attach + (6, 36), + -- correspondence.create + (6, 37), + -- rfa.create + (6, 39), + -- drawing.create + (6, 40), + -- transmittal.create + (6, 41), + -- circulation.create + (6, 38), + -- rfa.manage_shop_drawings + (6, 48), + -- search.advanced + (6, 49); +-- report.generate +-- ===================================================== +-- == 7. Contract Admin (role_id = 7) == +-- ===================================================== +INSERT INTO role_permissions (role_id, permission_id) +VALUES -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļē + (7, 27), + -- contract.manage_members + (7, 28), + -- contract.view + -- āļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī (āļŠāđˆāļ§āļ™āļŦāļ™āļķāđˆāļ‡āļ‚āļ­āļ‡ Workflow) + (7, 45), + -- workflow.action_review + -- āļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļŠāļąāļāļāļē + (7, 38), + -- rfa.manage_shop_drawings + (7, 39), + -- drawing.create + -- āļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ (āļĢāļ°āļ”āļąāļš Editor) + (7, 29), + -- document.create_draft + (7, 30), + -- document.submit + (7, 31), + -- document.view + (7, 32), + -- document.edit + (7, 35), + -- document.attach + (7, 36), + -- correspondence.create + (7, 37), + -- rfa.create + (7, 40), + -- transmittal.create + (7, 41), + -- circulation.create + (7, 48); +-- Seed data for the 'user_assignments' table +INSERT INTO `user_assignments` ( + `id`, + `user_id`, + `role_id`, + `organization_id`, + `project_id`, + `contract_id`, + `assigned_by_user_id` + ) +VALUES ( + 1, + 1, + 1, + NULL, + NULL, + NULL, + NULL + ), + ( + 2, + 2, + 2, + 1, + NULL, + NULL, + NULL + ); +-- ===================================================== +-- == 4. āļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāļāļąāļšāļ­āļ‡āļ„āđŒāļāļĢ (project_organizations) == +-- ===================================================== +-- āđ‚āļ„āļĢāļ‡āļāļēāļĢāļŦāļĨāļąāļ (LCBP3) āļˆāļ°āļĄāļĩāļ­āļ‡āļ„āđŒāļāļĢāļŦāļĨāļąāļāđ† āđ€āļ‚āđ‰āļēāļĄāļēāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3 ' + ), + id +FROM organizations +WHERE organization_code IN ( + 'āļāļ—āļ—.', + 'āļŠāļ„āļ‰.3', + 'TEAM', + 'āļ„āļ„āļ‡.', + 'āļœāļĢāļĄ.1', + 'āļœāļĢāļĄ.2', + 'āļœāļĢāļĄ.3', + 'āļœāļĢāļĄ.4', + 'EN', + 'CAR ' + ); +-- āđ‚āļ„āļĢāļ‡āļāļēāļĢāļĒāđˆāļ­āļĒ (LCBP3C1) āļˆāļ°āļĄāļĩāđ€āļ‰āļžāļēāļ°āļ­āļ‡āļ„āđŒāļāļĢāļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡ +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C1 ' + ), + id +FROM organizations +WHERE organization_code IN ('āļāļ—āļ—.', 'āļŠāļ„āļ‰.3', 'āļŠāļ„āļ‰.3 -02', 'āļ„āļ„āļ‡.', 'āļœāļĢāļĄ.1 '); +-- āļ—āļģāđ€āļŠāđˆāļ™āđ€āļ”āļĩāļĒāļ§āļāļąāļ™āļŠāļģāļŦāļĢāļąāļšāđ‚āļ„āļĢāļ‡āļāļēāļĢāļ­āļ·āđˆāļ™āđ† (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡) +INSERT INTO project_organizations (project_id, organization_id) +SELECT ( + SELECT id + FROM projects + WHERE project_code = 'LCBP3C2 ' + ), + id +FROM organizations +WHERE organization_code IN ('āļāļ—āļ—.', 'āļŠāļ„āļ‰.3', 'āļŠāļ„āļ‰.3 -03', 'āļ„āļ„āļ‡.', 'āļœāļĢāļĄ.2 '); +-- ===================================================== +-- == 5. āļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļŠāļąāļāļāļēāļāļąāļšāļ­āļ‡āļ„āđŒāļāļĢ (contract_organizations) == +-- ===================================================== +-- āļŠāļąāļāļāļēāļ—āļĩāđˆāļ›āļĢāļķāļāļĐāļēāļ­āļ­āļāđāļšāļš (DSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļāļ—āļ—.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'DSLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'TEAM' + ), + 'Designer' + ); +-- āļŠāļąāļāļāļēāļ—āļĩāđˆāļ›āļĢāļķāļāļĐāļēāļ„āļ§āļšāļ„āļļāļĄāļ‡āļēāļ™ (PSLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3 ' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļāļ—āļ—.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'PSLCBP3 ' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļ„āļ„āļ‡.' + ), + 'Consultant' + ); +-- āļŠāļąāļāļāļēāļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 1 (LCBP3-C1) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļāļ—āļ—.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C1 ' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļœāļĢāļĄ.1' + ), + 'Contractor' + ); +-- āļŠāļąāļāļāļēāļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 2 (LCBP3-C2) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļāļ—āļ—.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'LCBP3-C2' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļœāļĢāļĄ.2' + ), + 'Contractor' + ); +-- āļŠāļąāļāļāļēāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ (ENLCBP3) +INSERT INTO contract_organizations (contract_id, organization_id, role_in_contract) +VALUES ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'āļāļ—āļ—.' + ), + 'Owner' + ), + ( + ( + SELECT id + FROM contracts + WHERE contract_code = 'ENLCBP3' + ), + ( + SELECT id + FROM organizations + WHERE organization_code = 'EN' + ), + 'Consultant' + ); +-- Seed correspondence_status +INSERT INTO correspondence_status (status_code, status_name, sort_order, is_active) +VALUES ('DRAFT', 'Draft', 10, 1), + ('SUBOWN', 'Submitted to Owner', 21, 1), + ('SUBDSN', 'Submitted to Designer', 22, 1), + ('SUBCSC', 'Submitted to CSC', 23, 1), + ('SUBCON', 'Submitted to Contractor', 24, 1), + ('SUBOTH', 'Submitted to Others', 25, 1), + ('REPOWN', 'Reply by Owner', 31, 1), + ('REPDSN', 'Reply by Designer', 32, 1), + ('REPCSC', 'Reply by CSC', 33, 1), + ('REPCON', 'Reply by Contractor', 34, 1), + ('REPOTH', 'Reply by Others', 35, 1), + ('RSBOWN', 'Resubmited by Owner', 41, 1), + ('RSBDSN', 'Resubmited by Designer', 42, 1), + ('RSBCSC', 'Resubmited by CSC', 43, 1), + ('RSBCON', 'Resubmited by Contractor', 44, 1), + ('CLBOWN', 'Closed by Owner', 51, 1), + ('CLBDSN', 'Closed by Designer', 52, 1), + ('CLBCSC', 'Closed by CSC', 53, 1), + ('CLBCON', 'Closed by Contractor', 54, 1), + ('CCBOWN', 'Canceled by Owner', 91, 1), + ('CCBDSN', 'Canceled by Designer', 92, 1), + ('CCBCSC', 'Canceled by CSC', 93, 1), + ('CCBCON', 'Canceled by Contractor', 94, 1); +-- Seed rfa_types +INSERT INTO rfa_types ( + contract_id, + type_code, + type_name_en, + type_name_th + ) +SELECT id, + 'ADW', + 'As Built Drawing', + 'āđāļšāļšāļĢāđˆāļēāļ‡āļŦāļĨāļąāļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BC', + 'Box Culvert', + 'āļ—āđˆāļ­āļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāļĢāļđāļ›āļāļĨāđˆāļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BM', + 'Benchmark', + 'āļŦāļĄāļļāļ”āļŦāļĨāļąāļāļāļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'CER', + 'Certificates', + 'āđƒāļšāļĢāļąāļšāļĢāļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'CN', + 'Canal Drainage', + 'āļĢāļ°āļšāļšāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāđƒāļ™āļ„āļĨāļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'CON', + 'Contract', + 'āļŠāļąāļāļāļē' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DDS', + 'Design Data Submission', + 'āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ­āļ­āļāđāļšāļš' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DDW', + 'Draft Drawing', + 'āđāļšāļšāļĢāđˆāļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DRW', + 'Drawings (All Types)', + 'āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DSN', + 'Design/Calculation/Manual (All Stages)', + 'āļ­āļ­āļāđāļšāļš / āļ„āļģāļ™āļ§āļ“ / āļ„āļđāđˆāļĄāļ·āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'GEN', + 'General', + 'āļ—āļąāđˆāļ§āđ„āļ›' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ICR', + 'Incident Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāļīāļ”āļ­āļļāļšāļąāļ•āļīāđ€āļŦāļ•āļļāđāļĨāļ°āļāļēāļĢāļšāļēāļ”āđ€āļˆāđ‡āļš' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'INS', + 'Insurances/Bond/Guarantee', + 'āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ / āļžāļąāļ™āļ˜āļšāļąāļ•āļĢ / āļāļēāļĢāļ„āđ‰āļģāļ›āļĢāļ°āļāļąāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'INR', + 'Inspection/Audit/Surveillance Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāđ‰āļēāļĢāļ°āļ§āļąāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ITP', + 'Inspection and Test Plan', + 'āđāļœāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ°āļ—āļ”āļŠāļ­āļš' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'JSA', + 'Jobs Analysis', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MAN', + 'Manual', + 'āļ„āļđāđˆāļĄāļ·āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MAT', + 'Materials/Equipment/Plant', + 'āļ§āļąāļŠāļ”āļļ / āļ­āļļāļ›āļāļĢāļ“āđŒ / āđ‚āļĢāļ‡āļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MOM', + 'Minutes of Meeting', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ›āļĢāļ°āļŠāļļāļĄ' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MPR', + 'Monthly Progress Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ›āļĢāļ°āļˆāļģāđ€āļ”āļ·āļ­āļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MST', + 'Method Statement for Construction/Installation', + 'āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ•āļīāļ”āļ•āļąāđ‰āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'NDS', + 'Non-Design Data Submission', + 'āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļš' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PMA', + 'Payment/Invoice/Retention/Estimate', + 'āļāļēāļĢāļŠāļģāļĢāļ°āđ€āļ‡āļīāļ™ / āđƒāļšāđāļˆāđ‰āļ‡āļŦāļ™āļĩāđ‰ / āļ›āļĢāļ°āļāļąāļ™āļœāļĨāļ‡āļēāļ™ / āļ›āļĢāļ°āļĄāļēāļ“āļāļēāļĢ' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PRD', + 'Procedure', + 'āļĢāļ°āđ€āļšāļĩāļĒāļšāļ›āļāļīāļšāļąāļ•āļī' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PRG', + 'Progress of Construction', + 'āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ‚āļ­āļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ āļēāļžāļ–āđˆāļēāļĒ / āļ§āļīāļ”āļĩāđ‚āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'QMS', + 'Quality Document (Plan/Work Instruction)', + 'āđ€āļ­āļāļŠāļēāļĢāļ”āđ‰āļēāļ™āļ„āļļāļ“āļ āļēāļž (āđāļœāļ™āļ‡āļēāļ™ / āļ‚āđ‰āļ­āđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™)' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'RPT', + 'Report', + 'āļĢāļēāļĒāļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SAR', + 'Semi Annual Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļ›āļĢāļ°āļˆāļģāļŦāļāđ€āļ”āļ·āļ­āļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SCH', + 'Schedule and Program', + 'āđāļœāļ™āļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SDW', + 'Shop Drawing', + 'āđāļšāļšāļ‚āļĒāļēāļĒāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SI', + 'Soil Investigation', + 'āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ”āļīāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SPE', + 'Specification', + 'āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'TNR', + 'Training Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļāļķāļāļ›āļāļīāļšāļąāļ•āļī' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'UC', + 'Underground Construction', + 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđƒāļ•āđ‰āļ”āļīāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'VEN', + 'Vendor', + 'āļœāļđāđ‰āļ‚āļēāļĒ' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'VRO', + 'Variation Request/Instruction/Order', + 'āļ„āļģāļ‚āļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ / āļ‚āđ‰āļ­āđ€āļŠāļ™āļ­āđāļ™āļ° / āļ‚āđ‰āļ­āđ€āļĢāļĩāļĒāļāļĢāđ‰āļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'WTY', + 'Warranty', + 'āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'GEN', + 'General', + 'āļ—āļąāđˆāļ§āđ„āļ›' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'CON', + 'Contract', + 'āļŠāļąāļāļāļē' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'INS', + 'Insurances/Bond/Guarantee', + 'āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ / āļžāļąāļ™āļ˜āļšāļąāļ•āļĢ / āļāļēāļĢāļ„āđ‰āļģāļ›āļĢāļ°āļāļąāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SCH', + 'Schedule and Program', + 'āđāļœāļ™āļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'PMA', + 'Payment/Invoice/Retention/Estimate', + 'āļāļēāļĢāļŠāļģāļĢāļ°āđ€āļ‡āļīāļ™ / āđƒāļšāđāļˆāđ‰āļ‡āļŦāļ™āļĩāđ‰ / āļ›āļĢāļ°āļāļąāļ™āļœāļĨāļ‡āļēāļ™ / āļ›āļĢāļ°āļĄāļēāļ“āļāļēāļĢ' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'VRO', + 'Variation Request/Instruction/Order', + 'āļ„āļģāļ‚āļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ / āļ‚āđ‰āļ­āđ€āļŠāļ™āļ­āđāļ™āļ° / āļ‚āđ‰āļ­āđ€āļĢāļĩāļĒāļāļĢāđ‰āļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'VEN', + 'Vendor', + 'āļœāļđāđ‰āļ‚āļēāļĒ' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'WTY', + 'Warranty', + 'āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DRW', + 'Drawings (All Types)', + 'āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DDW', + 'Draft Drawing', + 'āđāļšāļšāļĢāđˆāļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SDW', + 'Shop Drawing', + 'āđāļšāļšāļ‚āļĒāļēāļĒāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ADW', + 'As Built Drawing', + 'āđāļšāļšāļĢāđˆāļēāļ‡āļŦāļĨāļąāļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DDS', + 'Design Data Submission', + 'āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ­āļ­āļāđāļšāļš' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DSN', + 'Design/Calculation/Manual (All Stages)', + 'āļ­āļ­āļāđāļšāļš / āļ„āļģāļ™āļ§āļ“ / āļ„āļđāđˆāļĄāļ·āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'NDS', + 'Non-Design Data Submission', + 'āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļš' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'PRD', + 'Procedure', + 'āļĢāļ°āđ€āļšāļĩāļĒāļšāļ›āļāļīāļšāļąāļ•āļī' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MST', + 'Method Statement for Construction/Installation', + 'āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ•āļīāļ”āļ•āļąāđ‰āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'QMS', + 'Quality Document (Plan/Work Instruction)', + 'āđ€āļ­āļāļŠāļēāļĢāļ”āđ‰āļēāļ™āļ„āļļāļ“āļ āļēāļž (āđāļœāļ™āļ‡āļēāļ™ / āļ‚āđ‰āļ­āđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™)' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'INR', + 'Inspection/Audit/Surveillance Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāđ‰āļēāļĢāļ°āļ§āļąāļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ITP', + 'Inspection and Test Plan', + 'āđāļœāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ°āļ—āļ”āļŠāļ­āļš' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MAT', + 'Materials/Equipment/Plant', + 'āļ§āļąāļŠāļ”āļļ / āļ­āļļāļ›āļāļĢāļ“āđŒ / āđ‚āļĢāļ‡āļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SPE', + 'Specification', + 'āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MAN', + 'Manual', + 'āļ„āļđāđˆāļĄāļ·āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'CER', + 'Certificates', + 'āđƒāļšāļĢāļąāļšāļĢāļ­āļ‡' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SAR', + 'Semi Annual Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļ›āļĢāļ°āļˆāļģāļŦāļāđ€āļ”āļ·āļ­āļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'JSA', + 'Jobs Analysis', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MOM', + 'Minutes of Meeting', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ›āļĢāļ°āļŠāļļāļĄ' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MPR', + 'Monthly Progress Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ›āļĢāļ°āļˆāļģāđ€āļ”āļ·āļ­āļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ICR', + 'Incident Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāļīāļ”āļ­āļļāļšāļąāļ•āļīāđ€āļŦāļ•āļļāđāļĨāļ°āļāļēāļĢāļšāļēāļ”āđ€āļˆāđ‡āļš' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'PRG', + 'Progress of Construction', + 'āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ‚āļ­āļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ āļēāļžāļ–āđˆāļēāļĒ / āļ§āļīāļ”āļĩāđ‚āļ­' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'RPT', + 'Report', + 'āļĢāļēāļĒāļ‡āļēāļ™' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'TNR', + 'Training Report', + 'āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļāļķāļāļ›āļāļīāļšāļąāļ•āļī' +FROM contracts +WHERE contract_code = 'LCBP3-C2'; +-- Seed rfa_status_codes +INSERT INTO rfa_status_codes ( + status_code, + status_name, + description, + sort_order + ) +VALUES ('DFT', 'Draft', 'āļ‰āļšāļąāļšāļĢāđˆāļēāļ‡', 1), + ('FAP', 'For Approve', 'āđ€āļžāļ·āđˆāļ­āļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī', 11), + ('FRE', 'For Review', 'āđ€āļžāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļš', 12), + ('FCO', 'For Construction', 'āđ€āļžāļ·āđˆāļ­āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡', 20), + ('ASB', 'AS - Built', 'āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļˆāļĢāļīāļ‡', 30), + ('OBS', 'Obsolete', 'āđ„āļĄāđˆāđƒāļŠāđ‰āļ‡āļēāļ™', 80), + ('CC', 'Canceled', 'āļĒāļāđ€āļĨāļīāļ', 99); +INSERT INTO rfa_approve_codes ( + approve_code, + approve_name, + sort_order, + is_active + ) +VALUES ('1A', 'Approved by Authority', 10, 1), + ('1C', 'Approved by CSC', 11, 1), + ('1N', 'Approved As Note', 12, 1), + ('1R', 'Approved with Remarks', 13, 1), + ('3C', 'Consultant Comments', 31, 1), + ('3R', 'Revise + and Resubmit', 32, 1), + ('4X', 'Reject', 40, 1), + ('5N', 'No Further Action', 50, 1); +-- āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļ‚āļ­āļ‡ RFA (āļĄāļĩāļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ 1:N āļāļąāļš rfa_revisions) +-- ========================================================== +-- SEED DATA 6B.md (Disciplines, RFA Types, Sub Types) +-- ========================================================== +-- 1. Seed āļ‚āđ‰āļ­āļĄāļđāļĨ Disciplines (āļŠāļēāļ‚āļēāļ‡āļēāļ™) +-- LCBP3-C1 +INSERT INTO disciplines ( + contract_id, + discipline_code, + code_name_th, + code_name_en + ) +SELECT id, + 'GEN', + 'āļ‡āļēāļ™āļšāļĢāļīāļŦāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ', + 'General Management' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'COD', + 'āļŠāļąāļāļāļēāđāļĨāļ°āļ‚āđ‰āļ­āđ‚āļ•āđ‰āđāļĒāđ‰āļ‡', + 'Contracting' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'QSB', + 'āļŠāļģāļĢāļ§āļˆāļ›āļĢāļīāļĄāļēāļ“āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļ‡āļšāļ›āļĢāļ°āļĄāļēāļ“', + 'Quantity Survey and Budget Control' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PPG', + 'āļšāļĢāļīāļŦāļēāļĢāđāļœāļ™āđāļĨāļ°āļ„āļ§āļēāļĄāļāđ‰āļēāļ§āļŦāļ™āđ‰āļē', + 'Plan and Progress Management' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PRC', + 'āļ‡āļēāļ™āļˆāļąāļ”āļ‹āļ·āđ‰āļ­', + 'Procurement' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SUB', + 'āļœāļđāđ‰āļĢāļąāļšāđ€āļŦāļĄāļēāļŠāđˆāļ§āļ‡', + 'Subcontractor' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ODC', + 'āļŠāļģāļ™āļąāļāļ‡āļēāļ™-āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢ', + 'Operation Docment Control' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'LAW', + 'āļāļŽāļŦāļĄāļēāļĒ', + 'Law' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'TRF', + 'āļˆāļĢāļēāļˆāļĢ', + 'Traffic' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BIM', + 'BIM', + 'Building information modeling' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SRV', + 'āļ‡āļēāļ™āļŠāļģāļĢāļ§āļˆ', + 'Survey' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SFT', + 'āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ', + 'Safety' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BST', + 'āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ', + 'Building Structure Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'TEM', + 'āļ‡āļēāļ™āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§', + 'Temporary Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'UTL', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„', + 'Utility' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'EPW', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļē', + 'Electrical Power Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ECM', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļēāļŠāļ·āđˆāļ­āļŠāļēāļĢ', + 'Electrical Communication Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ENV', + 'āļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ', + 'Environment' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'AQV', + 'āļ„āļļāļ“āļ āļēāļžāļ­āļēāļāļēāļĻāđāļĨāļ°āļ„āļ§āļēāļĄāļŠāļąāđˆāļ™āļŠāļ°āđ€āļ—āļ·āļ­āļ™', + 'Air quality and vibration' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'WAB', + 'āļ„āļļāļ“āļ āļēāļžāļ™āđ‰āļģāđāļĨāļ°āļŠāļĩāļ§āļ§āļīāļ—āļĒāļēāļ—āļēāļ‡āļ™āđ‰āļģ', + 'Water quality and Aquatic biology' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'ONS', + 'āļ§āļīāļĻāļ§āļāļĢāļĢāļĄāļŠāļēāļĒāļāļąāđˆāļ‡', + 'Onshore Engineer Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'PPR', + 'āļĄāļ§āļĨāļŠāļ™āļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđāļĨāļ°āļāļēāļĢāļ›āļĢāļ°āļŠāļēāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ', + 'Public Relations' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'OSW', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ', + 'Offshore Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DRE', + 'āļ‡āļēāļ™āļ‚āļļāļ”āđāļĨāļ°āļ–āļĄāļ—āļ°āđ€āļĨ', + 'Dredging and Reclamation' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'REV', + 'āļ‡āļēāļ™āļ„āļąāļ™āļŦāļīāļ™āļĨāđ‰āļ­āļĄāļžāļ·āđ‰āļ™āļ—āļĩāđˆāļ–āļĄāļ—āļ°āđ€āļĨ', + 'Revetment' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BRW', + 'āļ‡āļēāļ™āđ€āļ‚āļ·āđˆāļ­āļ™āļāļąāļ™āļ„āļĨāļ·āđˆāļ™', + 'Breakwater' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SOI', + 'āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ„āļļāļ“āļ āļēāļžāļ”āļīāļ™', + 'Soil Improvement' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'BLC', + 'āļ‡āļēāļ™āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ„āļĨāļ­āļ‡āļšāļēāļ‡āļĨāļ°āļĄāļļāļ‡', + 'Bang Lamung Canal Bank Protection' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'FUP', + 'āļ‡āļēāļ™āļ›āļĢāļ°āļ•āļđāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāđāļĨāļ°āļ—āđˆāļ­āļĨāļ­āļ”', + 'Floodgate & Under Ground Piping Works' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'SWP', + 'āļ‡āļēāļ™āļ­āļēāļ„āļēāļĢāļ„āļ§āļšāļ„āļļāļĄāļŠāļ–āļēāļ™āļĩāļŠāļđāļšāļ™āđ‰āļģāļ—āļ°āđ€āļĨ', + 'Sea Water Pumping Station Control BuilDing' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'NAV', + 'āļ‡āļēāļ™āļ•āļīāļ”āļ•āļąāđ‰āļ‡āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļŦāļĄāļēāļĒāļŠāđˆāļ§āļ‡āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āļ­', + 'Navigations Aids' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'GEO', + 'āļ‡āļēāļ™āļ”āđ‰āļēāļ™āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„', + 'Geotechnical' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'CRW', + 'āļ‡āļēāļ™āļ”āđ‰āļēāļ™āđ‚āļĒāļ˜āļē - Rock Works', + 'Civil-Rock work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'DVR', + 'āļ—āļĩāļĄāļ™āļąāļāļ›āļĢāļ°āļ”āļēāļ™āđ‰āļģ', + 'Dive Work' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'MTS', + 'āļ‡āļēāļ™āļ—āļ”āļŠāļ­āļšāļ§āļąāļŠāļ”āļļāđāļĨāļ°āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„', + 'Materials and Geotechnical Testing' +FROM contracts +WHERE contract_code = 'LCBP3-C1' +UNION ALL +SELECT id, + 'OTH', + 'āļ­āļ·āđˆāļ™āđ†', + 'Other' +FROM contracts +WHERE contract_code = 'LCBP3-C1'; +-- LCBP3-C2 +INSERT INTO disciplines ( + contract_id, + discipline_code, + code_name_th, + code_name_en + ) +SELECT id, + 'GEN', + 'āļ‡āļēāļ™āļšāļĢāļīāļŦāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ', + 'Project Management' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'COD', + 'āļŠāļąāļāļāļēāđāļĨāļ°āļ‚āđ‰āļ­āđ‚āļ•āđ‰āđāļĒāđ‰āļ‡', + 'Contracts and arguments' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'QSB', + 'āļŠāļģāļĢāļ§āļˆāļ›āļĢāļīāļĄāļēāļ“āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļ‡āļšāļ›āļĢāļ°āļĄāļēāļ“', + 'Survey the quantity and control the budget' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'PPM', + 'āļšāļĢāļīāļŦāļēāļĢāđāļœāļ™āđāļĨāļ°āļ„āļ§āļēāļĄāļāđ‰āļēāļ§āļŦāļ™āđ‰āļē', + 'Plan Management & Progress' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ODC', + 'āļŠāļģāļ™āļąāļāļ‡āļēāļ™-āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢ', + 'Document Control Office' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'LAW', + 'āļāļŽāļŦāļĄāļēāļĒ', + 'Law' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'TRF', + 'āļˆāļĢāļēāļˆāļĢ', + 'Traffic' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'BIM', + 'Building Information Modeling', + 'Building Information Modeling' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SRV', + 'āļ‡āļēāļ™āļŠāļģāļĢāļ§āļˆ', + 'Survey' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SFT', + 'āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ', + 'Safety' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'BST', + 'āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ', + 'Building Structure' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'UTL', + 'āļ‡āļēāļ™āļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„', + 'Public Utilities' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'EPW', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļē', + 'Electrical Systems' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ECM', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļēāļŠāļ·āđˆāļ­āļŠāļēāļĢ', + 'Electrical Communication System' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ENV', + 'āļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ', + 'Environment' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'AQV', + 'āļ„āļļāļ“āļ āļēāļžāļ­āļēāļāļēāļĻāđāļĨāļ°āļ„āļ§āļēāļĄāļŠāļąāđˆāļ™āļŠāļ°āđ€āļ—āļ·āļ­āļ™', + 'Air Quality and Vibration' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'WAB', + 'āļ„āļļāļ“āļ āļēāļžāļ™āđ‰āļģāđāļĨāļ°āļŠāļĩāļ§āļ§āļīāļ—āļĒāļēāļ—āļēāļ‡āļ™āđ‰āļģ', + 'Water Quality and Aquatic Biology' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ONS', + 'āļ§āļīāļĻāļ§āļāļĢāļĢāļĄāļŠāļēāļĒāļāļąāđˆāļ‡', + 'Coastal Engineering' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'PPR', + 'āļĄāļ§āļĨāļŠāļ™āļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđāļĨāļ°āļ›āļĢāļ°āļŠāļēāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ', + 'Mass Relations and Public Relations' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'OFW', + 'āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ—āļēāļ‡āļ—āļ°āđ€āļĨ', + 'Marine Construction' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'EXR', + 'āļ‡āļēāļ™āļ‚āļļāļ”āđāļĨāļ°āļ–āļĄāļ—āļ°āđ€āļĨ', + 'Excavation and reclamation' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'GEO', + 'āļ‡āļēāļ™āļ”āđ‰āļēāļ™āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„', + 'Geotechnical work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'CRW', + 'āļ‡āļēāļ™āļ”āđ‰āļēāļ™āđ‚āļĒāļ˜āļē - Rock Works', + 'Civil Works - Rock Works' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DVW', + 'āļ—āļĩāļĄāļ™āļąāļāļ›āļĢāļ°āļ”āļēāļ™āđ‰āļģ', + 'Team of Divers' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MTT', + 'āļ‡āļēāļ™āļ—āļ”āļŠāļ­āļšāļ§āļąāļŠāļ”āļļ', + 'Materials Testing' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ARC', + 'āļ‡āļēāļ™āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄ', + 'Architecture' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'STR', + 'āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡', + 'Structural work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'SAN', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāļŠāļļāļ‚āļēāļ āļīāļšāļēāļĨ', + 'Sanitation System' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'DRA', + 'āļ‡āļēāļ™āļĢāļ°āļšāļšāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģ', + 'Drainage system work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'TER', + 'āļ‡āļēāļ™āļ—āđˆāļēāđ€āļ—āļĩāļĒāļšāđ€āļĢāļ·āļ­', + 'Terminal Work work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'BUD', + 'āļ‡āļēāļ™āļ­āļēāļ„āļēāļĢ', + 'Building' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'ROW', + 'āļ‡āļēāļ™āļ–āļ™āļ™āđāļĨāļ°āļŠāļ°āļžāļēāļ™', + 'Road and Bridge Work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'MEC', + 'āļ‡āļēāļ™āđ€āļ„āļĢāļīāļ­āļ‡āļāļĨ', + 'Mechanical work' +FROM contracts +WHERE contract_code = 'LCBP3-C2' +UNION ALL +SELECT id, + 'OTH', + 'āļ­āļ·āđˆāļ™ āđ†', + 'Others' +FROM contracts +WHERE contract_code = 'LCBP3-C2'; +-- 2. Seed āļ‚āđ‰āļ­āļĄāļđāļĨ Correspondence Sub Types (Mapping RFA Types āļāļąāļš Number) +-- āđ€āļ™āļ·āđˆāļ­āļ‡āļˆāļēāļ sub_type_code āļ•āļĢāļ‡āļāļąāļš RFA Type Code āđāļ•āđˆ Req āļ•āđ‰āļ­āļ‡āļāļēāļĢ Mapping āđ€āļ›āđ‡āļ™ Number +-- LCBP3-C1 +INSERT INTO correspondence_sub_types ( + contract_id, + correspondence_type_id, + sub_type_code, + sub_type_name, + sub_type_number + ) +SELECT c.id, + ct.id, + 'MAT', + 'Material Approval', + '11' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C1' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'SHP', + 'Shop Drawing Submittal', + '12' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C1' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'DWG', + 'Document Approval', + '13' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C1' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'MET', + 'Engineering Document Submittal', + '14' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C1' + AND ct.type_code = 'RFA'; +-- LCBP3-C2 +INSERT INTO correspondence_sub_types ( + contract_id, + correspondence_type_id, + sub_type_code, + sub_type_name, + sub_type_number + ) +SELECT c.id, + ct.id, + 'MAT', + 'Material Approval', + '21' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C2' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'SHP', + 'Shop Drawing Submittal', + '22' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C2' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'DWG', + 'Document Approval', + '23' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C2' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'MET', + 'Engineering Document Submittal', + '24' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C2' + AND ct.type_code = 'RFA'; +-- LCBP3-C3 +INSERT INTO correspondence_sub_types ( + contract_id, + correspondence_type_id, + sub_type_code, + sub_type_name, + sub_type_number + ) +SELECT c.id, + ct.id, + 'MAT', + 'Material Approval', + '31' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C3' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'SHP', + 'Shop Drawing Submittal', + '32' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C3' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'DWG', + 'Document Approval', + '33' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C3' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'MET', + 'Engineering Document Submittal', + '34' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C4' + AND ct.type_code = 'RFA'; +-- Note: 6B data has C4 on the right column for MET but C3 on left, checking logic... MD says C3 for first 3 rows, then C4 mixed. I will assume C4 starts at row 12 in the MD table. +-- LCBP3-C4 +INSERT INTO correspondence_sub_types ( + contract_id, + correspondence_type_id, + sub_type_code, + sub_type_name, + sub_type_number + ) +SELECT c.id, + ct.id, + 'MAT', + 'Material Approval', + '41' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C4' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'SHP', + 'Shop Drawing Submittal', + '42' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C4' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'DWG', + 'Document Approval', + '43' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C4' + AND ct.type_code = 'RFA' +UNION ALL +SELECT c.id, + ct.id, + 'MET', + 'Engineering Document Submittal', + '44' +FROM contracts c, + correspondence_types ct +WHERE c.contract_code = 'LCBP3-C4' + AND ct.type_code = 'RFA'; diff --git a/GEMINI.md b/GEMINI.md index e69de29..924c271 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -0,0 +1,37 @@ +# NAP-DMS Project Context & Rules + +## 🧠 Role & Persona + +Act as a **Senior Full Stack Developer** expert in **NestJS**, **Next.js**, and **TypeScript**. +You value **Data Integrity**, **Security**, and **Clean Architecture**. + +## 🏗ïļ Project Overview + +This is **LCBP3-DMS (Laem Chabang Port Phase 3 - Document Management System)**. + +- **Goal:** Manage construction documents (Correspondence, RFA, Drawings) with complex approval workflows. +- **Infrastructure:** Deployed on QNAP Server via Docker Container Station. + +## ðŸ’ŧ Tech Stack & Constraints + +- **Backend:** NestJS (Modular Architecture), TypeORM, MariaDB 10.11, Redis (BullMQ). +- **Frontend:** Next.js 14+ (App Router), Tailwind CSS, Shadcn/UI. +- **Language:** TypeScript (Strict Mode). **NO `any` types allowed.** + +## ðŸ›Ąïļ Security & Integrity Rules + +1. **Idempotency:** All critical POST/PUT requests MUST check for `Idempotency-Key` header. +2. **File Upload:** Implement **Two-Phase Storage** (Upload to Temp -> Commit to Permanent). +3. **Race Conditions:** Use **Redis Lock** + **Optimistic Locking** for Document Numbering generation. +4. **Validation:** Use Zod or Class-validator for all inputs. + +## workflow Guidelines + +- When implementing **Workflow Engine**, strictly follow the **DSL** design in `2_Backend_Plan_V1_4_4.Phase6A.md`. +- Always verify database schema against `4_Data_Dictionary_V1_4_4.md` before writing queries. + +## ðŸšŦ Forbidden Actions + +- DO NOT use SQL Triggers (Business logic must be in NestJS services). +- DO NOT use `.env` files for production configuration (Use Docker environment variables). +- DO NOT generate code that violates OWASP Top 10 security practices. diff --git a/backend/package.json b/backend/package.json index f16dd23..2fa9aed 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "backend", - "version": "0.0.1", + "version": "1.4.4", "description": "", "author": "", "private": true, @@ -17,7 +17,8 @@ "test:watch": "jest --watch", "test:cov": "jest --coverage", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", - "test:e2e": "jest --config ./test/jest-e2e.json" + "test:e2e": "jest --config ./test/jest-e2e.json", + "seed": "ts-node -r tsconfig-paths/register src/database/seeds/run-seed.ts" }, "dependencies": { "@casl/ability": "^6.7.3", diff --git a/backend/src/Workflow DSL Specification.md b/backend/src/Workflow DSL Specification.md new file mode 100644 index 0000000..128b04b --- /dev/null +++ b/backend/src/Workflow DSL Specification.md @@ -0,0 +1,125 @@ +# **Workflow DSL Specification v1.0** + +āđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰āļĢāļ°āļšāļļāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ āļēāļĐāļē (Domain-Specific Language) āļŠāļģāļŦāļĢāļąāļšāļāļģāļŦāļ™āļ” Business Logic āļ‚āļ­āļ‡āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļĢāļ°āļšāļš LCBP3-DMS + +## **1\. āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļŦāļĨāļąāļ (Root Structure)** + +āđ„āļŸāļĨāđŒ Definition āļ•āđ‰āļ­āļ‡āļ­āļĒāļđāđˆāđƒāļ™āļĢāļđāļ›āđāļšāļš YAML āļŦāļĢāļ·āļ­ JSON āđ‚āļ”āļĒāļĄāļĩāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ”āļąāļ‡āļ™āļĩāđ‰: + +```json +workflow: "RFA_FLOW" # āļĢāļŦāļąāļŠ Workflow (Unique) +version: 1 # āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡ Logic +description: "RFA Approval Process" # āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ + +# āļĢāļēāļĒāļāļēāļĢāļŠāļ–āļēāļ™āļ°āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āđ„āļ›āđ„āļ”āđ‰ +states: +- name: "DRAFT" # āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ° (Case-sensitive) +initial: true # āđ€āļ›āđ‡āļ™āļŠāļ–āļēāļ™āļ°āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™ (āļ•āđ‰āļ­āļ‡āļĄāļĩ 1 āļŠāļ–āļēāļ™āļ°) +on: # āļĢāļēāļĒāļāļēāļĢ Action āļ—āļĩāđˆāļ—āļģāđ„āļ”āđ‰āļˆāļēāļāļŠāļ–āļēāļ™āļ°āļ™āļĩāđ‰ +SUBMIT: # āļŠāļ·āđˆāļ­ Action (āļ›āļļāđˆāļĄāļ—āļĩāđˆ User āļāļ”) +to: "IN_REVIEW" # āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡ +require: # (Optional) āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļŠāļīāļ—āļ˜āļīāđŒ +role: "EDITOR" +events: # (Optional) āđ€āļŦāļ•āļļāļāļēāļĢāļ“āđŒāļ—āļĩāđˆāļˆāļ°āđ€āļāļīāļ”āļ‚āļķāđ‰āļ™āđ€āļĄāļ·āđˆāļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° +- type: "notify" +target: "reviewer" + +- name: "IN_REVIEW" +on: +APPROVE: +to: "APPROVED" +condition: "context.amount < 1000000" # (Optional) JS Expression +REJECT: +to: "DRAFT" +events: +- type: "notify" +target: "creator" + +- name: "APPROVED" +terminal: true # āđ€āļ›āđ‡āļ™āļŠāļ–āļēāļ™āļ°āļˆāļš (āđ„āļĄāđˆāļŠāļēāļĄāļēāļĢāļ–āđ„āļ›āļ•āđˆāļ­āđ„āļ”āđ‰) +``` + +## **2. āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ” Field (Field Definitions)** + +### **2.1 State Object** + +| Field | Type | Required | Description | +| :------- | :------ | :------- | :--------------------------------------------- | +| name | string | Yes | āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ° (Unique Key) | +| initial | boolean | No | āļĢāļ°āļšāļļāļ§āđˆāļēāđ€āļ›āđ‡āļ™āļˆāļļāļ”āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™ (āļ•āđ‰āļ­āļ‡āļĄāļĩ 1 state āđƒāļ™āļĢāļ°āļšāļš) | +| terminal | boolean | No | āļĢāļ°āļšāļļāļ§āđˆāļēāđ€āļ›āđ‡āļ™āļˆāļļāļ”āļŠāļīāđ‰āļ™āļŠāļļāļ” | +| on | object | No | Map āļ‚āļ­āļ‡ Action -> Transition Rule | + +### **2.2 Transition Rule Object** + +| Field | Type | Required | Description | +| :-------- | :----- | :------- | :-------------------------------------- | +| to | string | Yes | āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡ | +| require | object | No | āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ Role/User | +| condition | string | No | JavaScript Expression (return boolean) | +| events | array | No | Side-effects āļ—āļĩāđˆāļˆāļ°āļ—āļģāļ‡āļēāļ™āļŦāļĨāļąāļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° | + +### **2.3 Requirements Object** + +| Field | Type | Description | +| :---- | :----- | :------------------------------------------ | +| role | string | User āļ•āđ‰āļ­āļ‡āļĄāļĩ Role āļ™āļĩāđ‰ (āđ€āļŠāđˆāļ™ PROJECT_MANAGER) | +| user | string | User āļ•āđ‰āļ­āļ‡āļĄāļĩ ID āļ™āļĩāđ‰ (Hard-code) | + +### **2.4 Event Object** + +| Field | Type | Description | +| :------- | :----- | :----------------------------------------- | +| type | string | notify, webhook, update_status | +| target | string | āļœāļđāđ‰āļĢāļąāļš (āđ€āļŠāđˆāļ™ creator, assignee, āļŦāļĢāļ·āļ­ Role) | +| template | string | āļĢāļŦāļąāļŠ Template āļ‚āđ‰āļ­āļ„āļ§āļēāļĄ | + +## **3\. āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļˆāļĢāļīāļ‡ (Real-world Examples)** + +### **āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: RFA Approval Flow** + +```json +{ + "workflow": "RFA_STD", + "version": 1, + "states": [ + { + "name": "DRAFT", + "initial": true, + "on": { + "SUBMIT": { + "to": "CONSULTANT_REVIEW", + "require": { "role": "CONTRACTOR" } + } + } + }, + { + "name": "CONSULTANT_REVIEW", + "on": { + "APPROVE_1": { + "to": "OWNER_REVIEW", + "condition": "context.priority === 'HIGH'" + }, + "APPROVE_2": { + "to": "APPROVED", + "condition": "context.priority === 'NORMAL'" + }, + "REJECT": { + "to": "DRAFT" + } + } + }, + { + "name": "OWNER_REVIEW", + "on": { + "APPROVE": { "to": "APPROVED" }, + "REJECT": { "to": "CONSULTANT_REVIEW" } + } + }, + { + "name": "APPROVED", + "terminal": true + } + ] +} +``` diff --git a/backend/src/database/seeds/run-seed.ts b/backend/src/database/seeds/run-seed.ts new file mode 100644 index 0000000..81b9941 --- /dev/null +++ b/backend/src/database/seeds/run-seed.ts @@ -0,0 +1,67 @@ +import { config } from 'dotenv'; +import { DataSource } from 'typeorm'; +import { seedWorkflowDefinitions } from '../seeds/workflow-definitions.seed'; // Import āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ Seed āļ—āļĩāđˆāļ„āļļāļ“āļĄāļĩ +// Import Entities āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡ +import { WorkflowDefinition } from '../../modules/workflow-engine/entities/workflow-definition.entity'; +import { WorkflowHistory } from '../../modules/workflow-engine/entities/workflow-history.entity'; +import { WorkflowInstance } from '../../modules/workflow-engine/entities/workflow-instance.entity'; + +// āđ‚āļŦāļĨāļ” Environment Variables (.env) +config(); + +const runSeed = async () => { + // āļ•āļąāđ‰āļ‡āļ„āđˆāļēāļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ (āļ„āļ§āļĢāļ•āļĢāļ‡āļāļąāļš docker-compose āļŦāļĢāļ·āļ­ .env āļ‚āļ­āļ‡āļ„āļļāļ“) + const dataSource = new DataSource({ + type: 'mariadb', + host: process.env.DB_HOST || 'localhost', + port: parseInt(process.env.DB_PORT || '3306'), + username: process.env.DB_USERNAME || 'root', + password: process.env.DB_PASSWORD || 'Center#2025', + database: process.env.DB_DATABASE || 'lcbp3_dev', + // āļŠāļģāļ„āļąāļ: āļ•āđ‰āļ­āļ‡āđƒāļŠāđˆ Entities āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰ TypeORM āļĢāļđāđ‰āļˆāļąāļ + entities: [ + WorkflowDefinition, + WorkflowInstance, + WorkflowHistory, + // āđƒāļŠāđˆ Entity āļ­āļ·āđˆāļ™āđ† āļ–āđ‰āļēāļˆāļģāđ€āļ›āđ‡āļ™ āļŦāļĢāļ·āļ­āđƒāļŠāđ‰ path pattern: __dirname + '/../../modules/**/*.entity{.ts,.js}' + ], + synchronize: false, // āļŦāđ‰āļēāļĄāđƒāļŠāđ‰ true āļšāļ™ Production + }); + + try { + console.log('🔌 Connecting to database...'); + await dataSource.initialize(); + console.log('✅ Database connected.'); + + console.log('ðŸŒą Running Seeds...'); + await seedWorkflowDefinitions(dataSource); + console.log('✅ Seeding completed successfully.'); + } catch (error) { + console.error('❌ Error during seeding:', error); + } finally { + if (dataSource.isInitialized) { + await dataSource.destroy(); + console.log('🔌 Database connection closed.'); + } + } +}; + +runSeed(); + +/* +npx ts-node -r tsconfig-paths/register src/database/run-seed.ts + +**āļŦāļĢāļ·āļ­āđ€āļžāļīāđˆāļĄāđƒāļ™ `package.json` (āđāļ™āļ°āļ™āļģ):** +āļ„āļļāļ“āļŠāļēāļĄāļēāļĢāļ–āđ€āļžāļīāđˆāļĄ script āđƒāļ™ `package.json` āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰āđ„āļ”āđ‰āļ‡āđˆāļēāļĒāļ‚āļķāđ‰āļ™āđƒāļ™āļ­āļ™āļēāļ„āļ•: + +"scripts": { + "seed": "ts-node -r tsconfig-paths/register src/database/seeds/run-seed.ts" +} + +http://googleusercontent.com/immersive_entry_chip/1 + +### ðŸ’Ą āļ‚āđ‰āļ­āļ„āļ§āļĢāļĢāļ°āļ§āļąāļ‡ +1. **Environment Variables:** āļ•āļĢāļ§āļˆāļŠāļ­āļšāđƒāļŦāđ‰āđāļ™āđˆāđƒāļˆāļ§āđˆāļēāļ„āđˆāļē Config (Host, User, Password) āđƒāļ™āđ„āļŸāļĨāđŒ `run-seed.ts` āļŦāļĢāļ·āļ­ `.env` āļ™āļąāđ‰āļ™āļ–āļđāļāļ•āđ‰āļ­āļ‡āđāļĨāļ°āļ•āļĢāļ‡āļāļąāļš Docker Container āļ—āļĩāđˆāļāļģāļĨāļąāļ‡āļĢāļąāļ™āļ­āļĒāļđāđˆ +2. **Entities:** āļŦāļēāļāļŸāļąāļ‡āļāđŒāļŠāļąāļ™ Seed āļĄāļĩāļāļēāļĢāđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰ Entity āļ­āļ·āđˆāļ™āļ™āļ­āļāđ€āļŦāļ™āļ·āļ­āļˆāļēāļ `WorkflowDefinition` āļ•āđ‰āļ­āļ‡āļ™āļģāļĄāļēāđƒāļŠāđˆāđƒāļ™ `entities: [...]` āļ‚āļ­āļ‡ `DataSource` āđƒāļŦāđ‰āļ„āļĢāļš āđ„āļĄāđˆāļ­āļĒāđˆāļēāļ‡āļ™āļąāđ‰āļ™āļˆāļ°āđ€āļˆāļ­ Error `RepositoryNotFoundError` + +*/ diff --git a/backend/src/database/seeds/workflow-definitions.seed.ts b/backend/src/database/seeds/workflow-definitions.seed.ts index 6c387c9..100e7a9 100644 --- a/backend/src/database/seeds/workflow-definitions.seed.ts +++ b/backend/src/database/seeds/workflow-definitions.seed.ts @@ -10,26 +10,41 @@ export const seedWorkflowDefinitions = async (dataSource: DataSource) => { // 1. RFA Workflow (Standard) const rfaDsl = { - workflow: 'RFA', + workflow: 'RFA_FLOW_V1', // [FIX] āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ·āđˆāļ­āđƒāļŦāđ‰āļ•āļĢāļ‡āļāļąāļšāļ„āđˆāļēāđƒāļ™ RfaWorkflowService version: 1, + description: 'Standard RFA Approval Workflow', states: [ { name: 'DRAFT', initial: true, - on: { SUBMIT: { to: 'IN_REVIEW', requirements: [{ role: 'Editor' }] } }, + on: { + SUBMIT: { + to: 'IN_REVIEW', + require: { role: 'Editor' }, // [FIX] āđāļāđ‰āđ„āļ‚ Syntax āđ€āļ›āđ‡āļ™ Object + }, + }, }, { name: 'IN_REVIEW', on: { - APPROVE: { - to: 'APPROVED', - requirements: [{ role: 'Contract Admin' }], + APPROVE_1: { + to: 'APPROVED', // [FIX] āļŠāļĩāđ‰āđ„āļ›āļ—āļĩāđˆ State āļ—āļĩāđˆāļĄāļĩāļ­āļĒāļđāđˆāļˆāļĢāļīāļ‡ + require: { role: 'Contract Admin' }, + condition: "context.priority === 'HIGH'", + }, + APPROVE_2: { + to: 'APPROVED', // [FIX] āļŠāļĩāđ‰āđ„āļ›āļ—āļĩāđˆ State āļ—āļĩāđˆāļĄāļĩāļ­āļĒāļđāđˆāļˆāļĢāļīāļ‡ + require: { role: 'Contract Admin' }, + condition: "context.priority === 'NORMAL'", }, REJECT: { to: 'REJECTED', - requirements: [{ role: 'Contract Admin' }], + require: { role: 'Contract Admin' }, }, - COMMENT: { to: 'DRAFT', requirements: [{ role: 'Contract Admin' }] }, // āļŠāđˆāļ‡āļāļĨāļąāļšāđāļāđ‰āđ„āļ‚ + COMMENT: { + to: 'DRAFT', + require: { role: 'Contract Admin' }, + }, // āļŠāđˆāļ‡āļāļĨāļąāļšāđāļāđ‰āđ„āļ‚ }, }, { name: 'APPROVED', terminal: true }, @@ -39,18 +54,27 @@ export const seedWorkflowDefinitions = async (dataSource: DataSource) => { // 2. Circulation Workflow const circulationDsl = { - workflow: 'CIRCULATION', + workflow: 'CIRCULATION_INTERNAL_V1', // [FIX] āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ·āđˆāļ­āđƒāļŦāđ‰āļ•āļĢāļ‡āļāļąāļšāļ„āđˆāļēāđƒāļ™ CirculationWorkflowService version: 1, + description: 'Internal Document Circulation', states: [ { name: 'OPEN', initial: true, - on: { SEND: { to: 'IN_REVIEW' } }, + on: { + START: { + // [FIX] āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ·āđˆāļ­ Action āđƒāļŦāđ‰āļ•āļĢāļ‡āļāļąāļšāļ—āļĩāđˆ Service āđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰ ('START') + to: 'IN_REVIEW', + }, + }, }, { name: 'IN_REVIEW', on: { - COMPLETE: { to: 'COMPLETED' }, // āđ€āļĄāļ·āđˆāļ­āļ—āļļāļāļ„āļ™āļ•āļ­āļšāļ„āļĢāļš + COMPLETE_TASK: { + // [FIX] āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđƒāļŦāđ‰āļŠāļ­āļ”āļ„āļĨāđ‰āļ­āļ‡āļāļąāļš Action āļ—āļĩāđˆāđƒāļŠāđ‰āļˆāļĢāļīāļ‡ + to: 'COMPLETED', + }, CANCEL: { to: 'CANCELLED' }, }, }, @@ -59,24 +83,60 @@ export const seedWorkflowDefinitions = async (dataSource: DataSource) => { ], }; - const workflows = [rfaDsl, circulationDsl]; + // 3. Correspondence Workflow (Optional - āļ–āđ‰āļēāļĄāļĩ) + const correspondenceDsl = { + workflow: 'CORRESPONDENCE_FLOW_V1', + version: 1, + description: 'Standard Correspondence Routing', + states: [ + { + name: 'DRAFT', + initial: true, + on: { SUBMIT: { to: 'IN_REVIEW' } }, + }, + { + name: 'IN_REVIEW', + on: { + APPROVE: { to: 'APPROVED' }, + REJECT: { to: 'REJECTED' }, + }, + }, + { name: 'APPROVED', terminal: true }, + { name: 'REJECTED', terminal: true }, + ], + }; + + const workflows = [rfaDsl, circulationDsl, correspondenceDsl]; for (const dsl of workflows) { const exists = await repo.findOne({ where: { workflow_code: dsl.workflow, version: dsl.version }, }); + if (!exists) { - const compiled = dslService.compile(dsl); - await repo.save( - repo.create({ - workflow_code: dsl.workflow, - version: dsl.version, - dsl: dsl, - compiled: compiled, - is_active: true, - }), + try { + // Compile āđ€āļžāļ·āđˆāļ­ Validate āđāļĨāļ° Normalize āļāđˆāļ­āļ™āļšāļąāļ™āļ—āļķāļ + // cast as any āđ€āļžāļ·āđˆāļ­ bypass type checking āļ•āļ­āļ™ seed raw data + const compiled = dslService.compile(dsl as any); + + await repo.save( + repo.create({ + workflow_code: dsl.workflow, + version: dsl.version, + description: dsl.description, + dsl: dsl, + compiled: compiled, + is_active: true, + }), + ); + console.log(`✅ Seeded Workflow: ${dsl.workflow} v${dsl.version}`); + } catch (error) { + console.error(`❌ Failed to seed workflow ${dsl.workflow}:`, error); + } + } else { + console.log( + `⏭ïļ Workflow already exists: ${dsl.workflow} v${dsl.version}`, ); - console.log(`✅ Seeded Workflow: ${dsl.workflow} v${dsl.version}`); } } }; diff --git a/backend/src/modules/circulation/circulation-workflow.service.ts b/backend/src/modules/circulation/circulation-workflow.service.ts new file mode 100644 index 0000000..3ded4ec --- /dev/null +++ b/backend/src/modules/circulation/circulation-workflow.service.ts @@ -0,0 +1,164 @@ +// File: src/modules/circulation/circulation-workflow.service.ts + +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +// Modules +import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service'; + +// Entities +import { CirculationStatusCode } from './entities/circulation-status-code.entity'; +import { Circulation } from './entities/circulation.entity'; + +// DTOs +import { WorkflowTransitionDto } from '../workflow-engine/dto/workflow-transition.dto'; + +@Injectable() +export class CirculationWorkflowService { + private readonly logger = new Logger(CirculationWorkflowService.name); + private readonly WORKFLOW_CODE = 'CIRCULATION_INTERNAL_V1'; + + constructor( + private readonly workflowEngine: WorkflowEngineService, + @InjectRepository(Circulation) + private readonly circulationRepo: Repository, + @InjectRepository(CirculationStatusCode) + private readonly statusRepo: Repository, + private readonly dataSource: DataSource, + ) {} + + /** + * āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āđƒāļšāđ€āļ§āļĩāļĒāļ™ (Start Circulation) + * āļ›āļāļ•āļīāļˆāļ°āđ€āļĢāļīāđˆāļĄāđ€āļĄāļ·āđˆāļ­āļŠāļĢāđ‰āļēāļ‡ Circulation āļŦāļĢāļ·āļ­āđ€āļĄāļ·āđˆāļ­āļāļ”āļŠāđˆāļ‡ + */ + async startCirculation(circulationId: number, userId: number) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const circulation = await this.circulationRepo.findOne({ + where: { id: circulationId }, + }); + + if (!circulation) { + throw new NotFoundException( + `Circulation ID ${circulationId} not found`, + ); + } + + // Context āļ­āļēāļˆāļ›āļĢāļ°āļāļ­āļšāļ”āđ‰āļ§āļĒ Department āļŦāļĢāļ·āļ­ Priority + const context = { + organizationId: circulation.organization, + creatorId: userId, + }; + + // Create Instance (Entity Type = 'circulation') + const instance = await this.workflowEngine.createInstance( + this.WORKFLOW_CODE, + 'circulation', + circulation.id.toString(), + context, + ); + + // Auto start (OPEN -> IN_REVIEW) + const transitionResult = await this.workflowEngine.processTransition( + instance.id, + 'START', + userId, + 'Start Circulation Process', + {}, + ); + + // Sync Status + await this.syncStatus( + circulation, + transitionResult.nextState, + queryRunner, + ); + + await queryRunner.commitTransaction(); + + return { + instanceId: instance.id, + currentState: transitionResult.nextState, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error(`Failed to start circulation: ${error}`); + throw error; + } finally { + await queryRunner.release(); + } + } + + /** + * āļĢāļąāļšāļ—āļĢāļēāļš/āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āđƒāļšāđ€āļ§āļĩāļĒāļ™ (Acknowledge / Action) + */ + async processAction( + instanceId: string, + userId: number, + dto: WorkflowTransitionDto, + ) { + // āļŠāđˆāļ‡āđƒāļŦāđ‰ Engine + const result = await this.workflowEngine.processTransition( + instanceId, + dto.action, + userId, + dto.comment, + dto.payload, + ); + + // Sync Status āļāļĨāļąāļš + const instance = await this.workflowEngine.getInstanceById(instanceId); + if (instance && instance.entityType === 'circulation') { + const circulation = await this.circulationRepo.findOne({ + where: { id: parseInt(instance.entityId) }, + }); + if (circulation) { + await this.syncStatus(circulation, result.nextState); + } + } + + return result; + } + + /** + * Helper: Map Workflow State -> Circulation Status (OPEN, IN_REVIEW, COMPLETED) + */ + private async syncStatus( + circulation: Circulation, + workflowState: string, + queryRunner?: any, + ) { + const statusMap: Record = { + DRAFT: 'OPEN', + ROUTING: 'IN_REVIEW', + COMPLETED: 'COMPLETED', + CANCELLED: 'CANCELLED', + }; + + const targetCode = statusMap[workflowState] || 'IN_REVIEW'; + + // āđ€āļ™āļ·āđˆāļ­āļ‡āļˆāļēāļ circulation āđ€āļāđ‡āļš status_code āđ€āļ›āđ‡āļ™ String āđƒāļ™āļ•āļēāļĢāļēāļ‡āđ€āļĨāļĒ (āļ•āļēāļĄ Schema v1.4.4) + // āļŦāļĢāļ·āļ­āļ­āļēāļˆāđ€āļ›āđ‡āļ™ Relation ID āļ‚āļķāđ‰āļ™āļ­āļĒāļđāđˆāļāļąāļš Implementation āļˆāļĢāļīāļ‡ + // āļŠāļĄāļĄāļ•āļīāļ§āđˆāļēāđ€āļ›āđ‡āļ™ String Code āļ•āļēāļĄ Schema: + + circulation.statusCode = targetCode; + + // āļ–āđ‰āļēāļˆāļšāđāļĨāđ‰āļ§ āđƒāļŦāđ‰āļĨāļ‡āđ€āļ§āļĨāļēāļ›āļīāļ” + if (targetCode === 'COMPLETED') { + circulation.closedAt = new Date(); + } + + const manager = queryRunner + ? queryRunner.manager + : this.circulationRepo.manager; + await manager.save(circulation); + + this.logger.log( + `Synced Circulation #${circulation.id}: State=${workflowState} -> Status=${targetCode}`, + ); + } +} diff --git a/backend/src/modules/circulation/circulation.module.ts b/backend/src/modules/circulation/circulation.module.ts index 9f9a224..bb4637d 100644 --- a/backend/src/modules/circulation/circulation.module.ts +++ b/backend/src/modules/circulation/circulation.module.ts @@ -1,13 +1,15 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { Circulation } from './entities/circulation.entity'; import { CirculationRouting } from './entities/circulation-routing.entity'; import { CirculationStatusCode } from './entities/circulation-status-code.entity'; +import { Circulation } from './entities/circulation.entity'; -import { CirculationService } from './circulation.service'; -import { CirculationController } from './circulation.controller'; import { UserModule } from '../user/user.module'; +import { WorkflowEngineModule } from '../workflow-engine/workflow-engine.module'; +import { CirculationWorkflowService } from './circulation-workflow.service'; +import { CirculationController } from './circulation.controller'; +import { CirculationService } from './circulation.service'; @Module({ imports: [ @@ -17,9 +19,10 @@ import { UserModule } from '../user/user.module'; CirculationStatusCode, ]), UserModule, + WorkflowEngineModule, ], controllers: [CirculationController], - providers: [CirculationService], + providers: [CirculationService, CirculationWorkflowService], exports: [CirculationService], }) export class CirculationModule {} diff --git a/backend/src/modules/correspondence/correspondence-workflow.service.ts b/backend/src/modules/correspondence/correspondence-workflow.service.ts new file mode 100644 index 0000000..65f8311 --- /dev/null +++ b/backend/src/modules/correspondence/correspondence-workflow.service.ts @@ -0,0 +1,153 @@ +// File: src/modules/correspondence/correspondence-workflow.service.ts + +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +import { WorkflowTransitionDto } from '../workflow-engine/dto/workflow-transition.dto'; +import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service'; +import { CorrespondenceRevision } from './entities/correspondence-revision.entity'; +import { CorrespondenceStatus } from './entities/correspondence-status.entity'; +import { Correspondence } from './entities/correspondence.entity'; + +@Injectable() +export class CorrespondenceWorkflowService { + private readonly logger = new Logger(CorrespondenceWorkflowService.name); + private readonly WORKFLOW_CODE = 'CORRESPONDENCE_FLOW_V1'; + + constructor( + private readonly workflowEngine: WorkflowEngineService, + @InjectRepository(Correspondence) + private readonly correspondenceRepo: Repository, + @InjectRepository(CorrespondenceRevision) + private readonly revisionRepo: Repository, + @InjectRepository(CorrespondenceStatus) + private readonly statusRepo: Repository, + private readonly dataSource: DataSource, + ) {} + + async submitWorkflow( + correspondenceId: number, + userId: number, + note?: string, + ) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + const revision = await this.revisionRepo.findOne({ + // ✅ FIX: CamelCase (correspondenceId, isCurrent) + where: { correspondenceId: correspondenceId, isCurrent: true }, + relations: ['correspondence'], + }); + + if (!revision) { + throw new NotFoundException( + `Correspondence Revision for ID ${correspondenceId} not found`, + ); + } + + // ✅ FIX: Check undefined before access + if (!revision.correspondence) { + throw new NotFoundException(`Correspondence relation not found`); + } + + const context = { + // ✅ FIX: CamelCase (projectId, correspondenceTypeId) + projectId: revision.correspondence.projectId, + typeId: revision.correspondence.correspondenceTypeId, + ownerId: userId, + amount: 0, + priority: 'NORMAL', + }; + + const instance = await this.workflowEngine.createInstance( + this.WORKFLOW_CODE, + 'correspondence_revision', + revision.id.toString(), + context, + ); + + const transitionResult = await this.workflowEngine.processTransition( + instance.id, + 'SUBMIT', + userId, + note || 'Initial Submission', + {}, + ); + + await this.syncStatus(revision, transitionResult.nextState, queryRunner); + + await queryRunner.commitTransaction(); + + return { + instanceId: instance.id, + currentState: transitionResult.nextState, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error(`Failed to submit workflow: ${error}`); + throw error; + } finally { + await queryRunner.release(); + } + } + + async processAction( + instanceId: string, + userId: number, + dto: WorkflowTransitionDto, + ) { + const result = await this.workflowEngine.processTransition( + instanceId, + dto.action, + userId, + dto.comment, + dto.payload, + ); + + // ✅ FIX: Method exists now + const instance = await this.workflowEngine.getInstanceById(instanceId); + + if (instance && instance.entityType === 'correspondence_revision') { + const revision = await this.revisionRepo.findOne({ + where: { id: parseInt(instance.entityId) }, + }); + if (revision) { + await this.syncStatus(revision, result.nextState); + } + } + + return result; + } + + private async syncStatus( + revision: CorrespondenceRevision, + workflowState: string, + queryRunner?: any, + ) { + const statusMap: Record = { + DRAFT: 'DRAFT', + IN_REVIEW: 'SUBOWN', + APPROVED: 'CLBOWN', + REJECTED: 'CCBOWN', + }; + + const targetCode = statusMap[workflowState] || 'DRAFT'; + + const status = await this.statusRepo.findOne({ + where: { statusCode: targetCode }, // ✅ FIX: CamelCase + }); + + if (status) { + // ✅ FIX: CamelCase (correspondenceStatusId) + revision.statusId = status.id; + + const manager = queryRunner + ? queryRunner.manager + : this.revisionRepo.manager; + await manager.save(revision); + } + } +} diff --git a/backend/src/modules/correspondence/correspondence.module.ts b/backend/src/modules/correspondence/correspondence.module.ts index 07dc292..862f65d 100644 --- a/backend/src/modules/correspondence/correspondence.module.ts +++ b/backend/src/modules/correspondence/correspondence.module.ts @@ -1,22 +1,24 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { CorrespondenceService } from './correspondence.service.js'; import { CorrespondenceController } from './correspondence.controller.js'; -import { Correspondence } from './entities/correspondence.entity.js'; +import { CorrespondenceService } from './correspondence.service.js'; import { CorrespondenceRevision } from './entities/correspondence-revision.entity.js'; import { CorrespondenceType } from './entities/correspondence-type.entity.js'; +import { Correspondence } from './entities/correspondence.entity.js'; // Import Entities āđƒāļŦāļĄāđˆ -import { RoutingTemplate } from './entities/routing-template.entity.js'; -import { RoutingTemplateStep } from './entities/routing-template-step.entity.js'; import { CorrespondenceRouting } from './entities/correspondence-routing.entity.js'; +import { RoutingTemplateStep } from './entities/routing-template-step.entity.js'; +import { RoutingTemplate } from './entities/routing-template.entity.js'; -import { CorrespondenceStatus } from './entities/correspondence-status.entity.js'; import { DocumentNumberingModule } from '../document-numbering/document-numbering.module.js'; // āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āļ•āļ­āļ™ Create import { JsonSchemaModule } from '../json-schema/json-schema.module.js'; // āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰ Validate Details +import { SearchModule } from '../search/search.module'; // ✅ 1. āđ€āļžāļīāđˆāļĄ Import SearchModule import { UserModule } from '../user/user.module.js'; // <--- 1. Import UserModule import { WorkflowEngineModule } from '../workflow-engine/workflow-engine.module.js'; // <--- ✅ āđ€āļžāļīāđˆāļĄāļšāļĢāļĢāļ—āļąāļ”āļ™āļĩāđ‰āļ„āļĢāļąāļš import { CorrespondenceReference } from './entities/correspondence-reference.entity.js'; -import { SearchModule } from '../search/search.module'; // ✅ 1. āđ€āļžāļīāđˆāļĄ Import SearchModule +import { CorrespondenceStatus } from './entities/correspondence-status.entity.js'; +// Controllers & Services +import { CorrespondenceWorkflowService } from './correspondence-workflow.service'; // Register Service āļ™āļĩāđ‰ @Module({ imports: [ @@ -37,7 +39,7 @@ import { SearchModule } from '../search/search.module'; // ✅ 1. āđ€āļžāļīāđˆ SearchModule, // ✅ 2. āđƒāļŠāđˆ SearchModule āļ—āļĩāđˆāļ™āļĩāđˆ ], controllers: [CorrespondenceController], - providers: [CorrespondenceService], + providers: [CorrespondenceService, CorrespondenceWorkflowService], exports: [CorrespondenceService], }) export class CorrespondenceModule {} diff --git a/backend/src/modules/json-schema/json-schema.module.ts b/backend/src/modules/json-schema/json-schema.module.ts index 1481f0f..17b40c0 100644 --- a/backend/src/modules/json-schema/json-schema.module.ts +++ b/backend/src/modules/json-schema/json-schema.module.ts @@ -15,14 +15,10 @@ import { VirtualColumnService } from './services/virtual-column.service'; import { CryptoService } from '../../common/services/crypto.service'; // Import Module āļ­āļ·āđˆāļ™āđ† āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™āļŠāļģāļŦāļĢāļąāļš Guard (āļ–āđ‰āļē Guards āļ­āļĒāļđāđˆāđƒāļ™ Common āļ­āļēāļˆāļˆāļ°āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ import āļ—āļĩāđˆāļ™āļĩāđˆāđ‚āļ”āļĒāļ•āļĢāļ‡) -// import { UserModule } from '../user/user.module'; +import { UserModule } from '../user/user.module'; @Module({ - imports: [ - TypeOrmModule.forFeature([JsonSchema]), - ConfigModule, - // UserModule, - ], + imports: [TypeOrmModule.forFeature([JsonSchema]), ConfigModule, UserModule], controllers: [JsonSchemaController], providers: [ JsonSchemaService, diff --git a/backend/src/modules/json-schema/json-schema.service.ts b/backend/src/modules/json-schema/json-schema.service.ts index 1a0f1a1..d019317 100644 --- a/backend/src/modules/json-schema/json-schema.service.ts +++ b/backend/src/modules/json-schema/json-schema.service.ts @@ -1,172 +1,406 @@ -// File: src/modules/json-schema/json-schema.controller.ts -import { - Body, - Controller, - Delete, - Get, - HttpCode, - HttpStatus, - Param, - ParseIntPipe, - Patch, - Post, - Query, - UseGuards, -} from '@nestjs/common'; -import { - ApiBearerAuth, - ApiOperation, - ApiParam, - ApiResponse, - ApiTags, -} from '@nestjs/swagger'; +// File: src/modules/json-schema/json-schema.service.ts +// āļšāļąāļ™āļ—āļķāļāļāļēāļĢāđāļāđ‰āđ„āļ‚: Fix TS2345 (undefined check) -import { JsonSchemaService } from './json-schema.service'; -import { SchemaMigrationService } from './services/schema-migration.service'; +import { + BadRequestException, + Injectable, + Logger, + NotFoundException, + OnModuleInit, +} from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import Ajv, { ValidateFunction } from 'ajv'; +import addFormats from 'ajv-formats'; +import { Repository } from 'typeorm'; import { CreateJsonSchemaDto } from './dto/create-json-schema.dto'; -import { MigrateDataDto } from './dto/migrate-data.dto'; import { SearchJsonSchemaDto } from './dto/search-json-schema.dto'; import { UpdateJsonSchemaDto } from './dto/update-json-schema.dto'; +import { JsonSchema } from './entities/json-schema.entity'; -import { CurrentUser } from '../../common/decorators/current-user.decorator'; -import { RequirePermission } from '../../common/decorators/require-permission.decorator'; -import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; -import { RbacGuard } from '../../common/guards/rbac.guard'; -import { User } from '../user/entities/user.entity'; +// Services āļĒāđˆāļ­āļĒāļ—āļĩāđˆāđāļĒāļāļ•āļēāļĄāļŦāļ™āđ‰āļēāļ—āļĩāđˆ (Single Responsibility) +import { + JsonSecurityService, + SecurityContext, +} from './services/json-security.service'; +import { UiSchemaService } from './services/ui-schema.service'; +import { VirtualColumnService } from './services/virtual-column.service'; + +import { + ValidationErrorDetail, + ValidationOptions, + ValidationResult, +} from './interfaces/validation-result.interface'; + +@Injectable() +export class JsonSchemaService implements OnModuleInit { + private ajv: Ajv; + private validators = new Map(); // Cache āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļš Validator āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§ + private readonly logger = new Logger(JsonSchemaService.name); + + // āļ„āđˆāļē Default āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ‚āđ‰āļ­āļĄāļđāļĨ + private readonly defaultOptions: ValidationOptions = { + removeAdditional: true, // āļĨāļšāļŸāļīāļĨāļ”āđŒāđ€āļāļīāļ™ + coerceTypes: true, // āđāļ›āļĨāļ‡āļŠāļ™āļīāļ”āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī (āđ€āļŠāđˆāļ™ "123" -> 123) + useDefaults: true, // āđƒāļŠāđˆāļ„āđˆāļē Default āļ–āđ‰āļēāđ„āļĄāđˆāļĄāļĩāļ‚āđ‰āļ­āļĄāļđāļĨ + }; -@ApiTags('JSON Schemas Management') -@ApiBearerAuth() -@UseGuards(JwtAuthGuard, RbacGuard) -@Controller('json-schemas') -export class JsonSchemaController { constructor( - private readonly jsonSchemaService: JsonSchemaService, - private readonly migrationService: SchemaMigrationService, - ) {} - - // ---------------------------------------------------------------------- - // Schema Management (CRUD) - // ---------------------------------------------------------------------- - - @Post() - @ApiOperation({ - summary: 'Create a new schema or new version of existing schema', - }) - @ApiResponse({ - status: 201, - description: 'The schema has been successfully created.', - }) - @RequirePermission('system.manage_all') // Admin Only - create(@Body() createDto: CreateJsonSchemaDto) { - return this.jsonSchemaService.create(createDto); - } - - @Get() - @ApiOperation({ summary: 'List all schemas with pagination and filtering' }) - @RequirePermission('document.view') // Viewer+ can see schemas - findAll(@Query() searchDto: SearchJsonSchemaDto) { - return this.jsonSchemaService.findAll(searchDto); - } - - @Get(':id') - @ApiOperation({ summary: 'Get a specific schema version by ID' }) - @RequirePermission('document.view') - findOne(@Param('id', ParseIntPipe) id: number) { - return this.jsonSchemaService.findOne(id); - } - - @Get('latest/:code') - @ApiOperation({ - summary: 'Get the latest active version of a schema by code', - }) - @ApiParam({ name: 'code', description: 'Schema Code (e.g., RFA_DWG)' }) - @RequirePermission('document.view') - findLatest(@Param('code') code: string) { - return this.jsonSchemaService.findLatestByCode(code); - } - - @Patch(':id') - @ApiOperation({ - summary: 'Update a specific schema (Not recommended for active schemas)', - }) - @RequirePermission('system.manage_all') - update( - @Param('id', ParseIntPipe) id: number, - @Body() updateDto: UpdateJsonSchemaDto, + @InjectRepository(JsonSchema) + private readonly jsonSchemaRepository: Repository, + private readonly virtualColumnService: VirtualColumnService, + private readonly uiSchemaService: UiSchemaService, + private readonly jsonSecurityService: JsonSecurityService, ) { - return this.jsonSchemaService.update(id, updateDto); + // āļāļģāļŦāļ™āļ”āļ„āđˆāļēāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āđƒāļŦāđ‰āļāļąāļš AJV Validation Engine + this.ajv = new Ajv({ + allErrors: true, // āđāļŠāļ”āļ‡ Error āļ—āļąāđ‰āļ‡āļŦāļĄāļ” āđ„āļĄāđˆāļŦāļĒāļļāļ”āđāļ„āđˆāļˆāļļāļ”āđāļĢāļ + strict: false, // āđ„āļĄāđˆāđ€āļ„āļĢāđˆāļ‡āļ„āļĢāļąāļ”āđ€āļāļīāļ™āđ„āļ› (āļĒāļ­āļĄāļĢāļąāļš Keyword āđāļ›āļĨāļāđ† āđ€āļŠāđˆāļ™ ui:widget) + coerceTypes: true, + useDefaults: true, + removeAdditional: true, + }); + addFormats(this.ajv); // āđ€āļžāļīāđˆāļĄ Format āļĄāļēāļ•āļĢāļāļēāļ™ (email, date, uri āļŊāļĨāļŊ) + this.registerCustomValidators(); // āļĨāļ‡āļ—āļ°āđ€āļšāļĩāļĒāļ™ Validator āđ€āļ‰āļžāļēāļ°āļ‚āļ­āļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒ } - @Delete(':id') - @ApiOperation({ summary: 'Delete a schema version (Hard Delete)' }) - @RequirePermission('system.manage_all') - remove(@Param('id', ParseIntPipe) id: number) { - return this.jsonSchemaService.remove(id); + async onModuleInit() { + // āļŠāļēāļĄāļēāļĢāļ–āđ‚āļŦāļĨāļ” Schema āļ—āļĩāđˆ Active āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļĄāļē Cache āđ„āļ§āđ‰āļĨāđˆāļ§āļ‡āļŦāļ™āđ‰āļēāđ„āļ”āđ‰āļ—āļĩāđˆāļ™āļĩāđˆ āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāđ€āļĢāđ‡āļ§āđƒāļ™āļāļēāļĢāļ•āļ­āļšāļŠāļ™āļ­āļ‡āļ„āļĢāļąāđ‰āļ‡āđāļĢāļ } - // ---------------------------------------------------------------------- - // Validation & Security - // ---------------------------------------------------------------------- + /** + * āļĨāļ‡āļ—āļ°āđ€āļšāļĩāļĒāļ™ Custom Validators āđ€āļ‰āļžāļēāļ°āļŠāļģāļŦāļĢāļąāļš LCBP3 + */ + private registerCustomValidators() { + // 1. āļ•āļĢāļ§āļˆāļŠāļ­āļšāļĢāļđāļ›āđāļšāļšāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (āđ€āļŠāđˆāļ™ TEAM-RFA-STR-0001) + this.ajv.addFormat('document-number', { + type: 'string', + validate: (value: string) => { + // Regex āļ­āļĒāđˆāļēāļ‡āļ‡āđˆāļēāļĒ: āļāļĨāļļāđˆāļĄāļ•āļąāļ§āļ­āļąāļāļĐāļĢāļ‚āļĩāļ”āļ„āļąāđˆāļ™āļ”āđ‰āļ§āļĒ - + return /^[A-Z0-9]{2,10}-[A-Z]{2,5}(-[A-Z0-9]{2,5})?-\d{4}-\d{3,5}$/.test( + value, + ); + }, + }); - @Post('validate/:code') - @HttpCode(HttpStatus.OK) - @ApiOperation({ summary: 'Validate data against the latest schema version' }) - @ApiResponse({ - status: 200, - description: 'Validation result including errors and sanitized data', - }) - @RequirePermission('document.view') - async validate(@Param('code') code: string, @Body() data: any) { - // Note: Validation API āļ™āļĩāđ‰āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš Test āļŦāļĢāļ·āļ­ Pre-check āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ - // āļāļēāļĢ Save āļˆāļĢāļīāļ‡āļˆāļ°āđ€āļĢāļĩāļĒāļāļœāđˆāļēāļ™ Service āļ āļēāļĒāđƒāļ™ - return this.jsonSchemaService.validateData(code, data); + // 2. Keyword āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļļ Role āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ (āđƒāļŠāđ‰āļĢāđˆāļ§āļĄāļāļąāļš Security Service) + this.ajv.addKeyword({ + keyword: 'requiredRole', + type: 'string', + metaSchema: { type: 'string' }, + validate: (schema: string, data: any) => true, // āļœāđˆāļēāļ™āđ€āļŠāļĄāļ­āđƒāļ™āļ‚āļąāđ‰āļ™ AJV (Security Service āļˆāļ°āļˆāļąāļ”āļāļēāļĢāđ€āļ­āļ‡) + }); } - @Post('read/:code') - @HttpCode(HttpStatus.OK) - @ApiOperation({ - summary: 'Process read data (Decrypt & Filter) based on user roles', - }) - @RequirePermission('document.view') + /** + * āļŠāļĢāđ‰āļēāļ‡ Schema āđƒāļŦāļĄāđˆ āļžāļĢāđ‰āļ­āļĄāļˆāļąāļ”āļāļēāļĢ Version, UI Schema āđāļĨāļ° Virtual Columns + */ + async create(createDto: CreateJsonSchemaDto): Promise { + // 1. āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡ JSON Schema Definition (AJV Syntax) + try { + this.ajv.compile(createDto.schemaDefinition); + } catch (error: any) { + throw new BadRequestException( + `Invalid JSON Schema format: ${error.message}`, + ); + } + + // 2. āļˆāļąāļ”āļāļēāļĢ UI Schema + if (createDto.uiSchema) { + // āļ–āđ‰āļēāļŠāđˆāļ‡āļĄāļē āđƒāļŦāđ‰āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āđ€āļ—āļĩāļĒāļšāļāļąāļš Data Schema + this.uiSchemaService.validateUiSchema( + createDto.uiSchema as any, + createDto.schemaDefinition, + ); + } else { + // āļ–āđ‰āļēāđ„āļĄāđˆāļŠāđˆāļ‡āļĄāļē āđƒāļŦāđ‰āļŠāļĢāđ‰āļēāļ‡ UI Schema āļžāļ·āđ‰āļ™āļāļēāļ™āđƒāļŦāđ‰āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī + createDto.uiSchema = this.uiSchemaService.generateDefaultUiSchema( + createDto.schemaDefinition, + ); + } + + // 3. āļˆāļąāļ”āļāļēāļĢ Versioning āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī (Auto-increment) + const latestSchema = await this.jsonSchemaRepository.findOne({ + where: { schemaCode: createDto.schemaCode }, + order: { version: 'DESC' }, + }); + + let newVersion = 1; + if (latestSchema) { + // āļ–āđ‰āļēāļœāļđāđ‰āđƒāļŠāđ‰āđ„āļĄāđˆāļĢāļ°āļšāļļ Version āļŦāļĢāļ·āļ­āļĢāļ°āļšāļļāļĄāļēāļ™āđ‰āļ­āļĒāļāļ§āđˆāļēāļĨāđˆāļēāļŠāļļāļ” āđƒāļŦāđ‰ +1 + if (!createDto.version || createDto.version <= latestSchema.version) { + newVersion = latestSchema.version + 1; + } else { + newVersion = createDto.version; + } + } else if (createDto.version) { + newVersion = createDto.version; + } + + // 4. āļšāļąāļ™āļ—āļķāļāļĨāļ‡āļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ + const newSchema = this.jsonSchemaRepository.create({ + ...createDto, + version: newVersion, + }); + + const savedSchema = await this.jsonSchemaRepository.save(newSchema); + + // āļĨāđ‰āļēāļ‡ Cache āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ‚āļŦāļĨāļ”āļ•āļąāļ§āđƒāļŦāļĄāđˆāđƒāļ™āļ„āļĢāļąāđ‰āļ‡āļ–āļąāļ”āđ„āļ› + this.validators.delete(savedSchema.schemaCode); + + this.logger.log( + `Schema '${savedSchema.schemaCode}' created (v${savedSchema.version})`, + ); + + // 5. āļŠāļĢāđ‰āļēāļ‡/āļ­āļąāļ›āđ€āļ”āļ• Virtual Columns āļšāļ™ Database āļˆāļĢāļīāļ‡ (Performance Optimization) + // Fix TS2345: Add empty array fallback + if (savedSchema.virtualColumns && savedSchema.virtualColumns.length > 0) { + await this.virtualColumnService.setupVirtualColumns( + savedSchema.tableName, + savedSchema.virtualColumns || [], + ); + } + + return savedSchema; + } + + /** + * āļ„āđ‰āļ™āļŦāļē Schema āļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Pagination & Filter) + */ + async findAll(searchDto: SearchJsonSchemaDto) { + const { search, isActive, page = 1, limit = 20 } = searchDto; + const skip = (page - 1) * limit; + + const query = this.jsonSchemaRepository.createQueryBuilder('schema'); + + if (search) { + query.andWhere('schema.schemaCode LIKE :search', { + search: `%${search}%`, + }); + } + + if (isActive !== undefined) { + query.andWhere('schema.isActive = :isActive', { isActive }); + } + + // āđ€āļĢāļĩāļĒāļ‡āļ•āļēāļĄ Code āļāđˆāļ­āļ™ āđāļĨāđ‰āļ§āļ•āļēāļĄāļ”āđ‰āļ§āļĒ Version āļĨāđˆāļēāļŠāļļāļ” + query.orderBy('schema.schemaCode', 'ASC'); + query.addOrderBy('schema.version', 'DESC'); + + const [items, total] = await query.skip(skip).take(limit).getManyAndCount(); + + return { + data: items, + meta: { + total, + page, + limit, + totalPages: Math.ceil(total / limit), + }, + }; + } + + /** + * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ Schema āļ•āļēāļĄ ID + */ + async findOne(id: number): Promise { + const schema = await this.jsonSchemaRepository.findOne({ where: { id } }); + if (!schema) { + throw new NotFoundException(`JsonSchema with ID ${id} not found`); + } + return schema; + } + + /** + * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ Schema āļ•āļēāļĄ Code āđāļĨāļ° Version (āļŠāļģāļŦāļĢāļąāļš Migration) + */ + async findOneByCodeAndVersion( + code: string, + version: number, + ): Promise { + const schema = await this.jsonSchemaRepository.findOne({ + where: { schemaCode: code, version }, + }); + + if (!schema) { + throw new NotFoundException( + `JsonSchema '${code}' version ${version} not found`, + ); + } + return schema; + } + + /** + * āļ”āļķāļ‡ Schema āđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆ Active (āļŠāļģāļŦāļĢāļąāļšāđƒāļŠāđ‰āļ‡āļēāļ™āļ—āļąāđˆāļ§āđ„āļ›) + */ + async findLatestByCode(code: string): Promise { + const schema = await this.jsonSchemaRepository.findOne({ + where: { schemaCode: code, isActive: true }, + order: { version: 'DESC' }, + }); + + if (!schema) { + throw new NotFoundException( + `Active JsonSchema with code '${code}' not found`, + ); + } + return schema; + } + + /** + * [CORE FUNCTION] āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ‚āđ‰āļ­āļĄāļđāļĨ (Validate), āļ—āļģāļ„āļ§āļēāļĄāļŠāļ°āļ­āļēāļ” (Sanitize) āđāļĨāļ°āđ€āļ‚āđ‰āļēāļĢāļŦāļąāļŠ (Encrypt) + * āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš "āļ‚āļēāđ€āļ‚āđ‰āļē" (Write) āļāđˆāļ­āļ™āļšāļąāļ™āļ—āļķāļāļĨāļ‡ Database + */ + async validateData( + schemaCode: string, + data: any, + options: ValidationOptions = {}, + ): Promise { + // 1. āļ”āļķāļ‡āđāļĨāļ° Compile Validator + const validate = await this.getValidator(schemaCode); + const schema = await this.findLatestByCode(schemaCode); // āļ”āļķāļ‡ Full Schema āđ€āļžāļ·āđˆāļ­āđƒāļŠāđ‰ Config āļ­āļ·āđˆāļ™āđ† + + // 2. āļŠāļģāđ€āļ™āļēāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ Side Effect āđāļĨāļ°āđ€āļ•āļĢāļĩāļĒāļĄāļŠāļģāļŦāļĢāļąāļš AJV Mutation (Sanitization) + const dataToValidate = JSON.parse(JSON.stringify(data)); + + // 3. āđ€āļĢāļīāđˆāļĄāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš (AJV āļˆāļ°āļ—āļģāļāļēāļĢ Coerce Type āđāļĨāļ° Remove Additional Properties āđƒāļŦāđ‰āļ”āđ‰āļ§āļĒ) + const valid = validate(dataToValidate); + + // 4. āļˆāļąāļ”āļāļēāļĢāļāļĢāļ“āļĩāļ‚āđ‰āļ­āļĄāļđāļĨāđ„āļĄāđˆāļ–āļđāļāļ•āđ‰āļ­āļ‡ + if (!valid) { + const errors: ValidationErrorDetail[] = (validate.errors || []).map( + (err) => ({ + field: err.instancePath || 'root', + message: err.message || 'Validation error', + value: err.params, + }), + ); + + return { + isValid: false, + errors, + sanitizedData: null, + }; + } + + // 5. āđ€āļ‚āđ‰āļēāļĢāļŦāļąāļŠāļ‚āđ‰āļ­āļĄāļđāļĨ (Encryption) āļŠāļģāļŦāļĢāļąāļš Field āļ—āļĩāđˆāļĄāļĩāļ„āļ§āļēāļĄāļĨāļąāļš (x-encrypt: true) + const secureData = this.jsonSecurityService.encryptFields( + dataToValidate, + schema.schemaDefinition, + ); + + return { + isValid: true, + errors: [], + sanitizedData: secureData, // āļ‚āđ‰āļ­āļĄāļđāļĨāļ™āļĩāđ‰āļŠāļ°āļ­āļēāļ”āđāļĨāļ°āļ›āļĨāļ­āļ”āļ āļąāļĒ āļžāļĢāđ‰āļ­āļĄāļšāļąāļ™āļ—āļķāļ + }; + } + + /** + * [CORE FUNCTION] āļ­āđˆāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ, āļ–āļ­āļ”āļĢāļŦāļąāļŠ (Decrypt) āđāļĨāļ°āļāļĢāļ­āļ‡āļ•āļēāļĄāļŠāļīāļ—āļ˜āļīāđŒ (Filter) + * āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš "āļ‚āļēāļ­āļ­āļ" (Read) āļāđˆāļ­āļ™āļŠāđˆāļ‡āđƒāļŦāđ‰ Frontend + */ async processReadData( - @Param('code') code: string, - @Body() data: any, - @CurrentUser() user: User, - ) { - // āđāļ›āļĨāļ‡ User Entity āđ€āļ›āđ‡āļ™ Security Context - // āđāļāđ‰āđ„āļ‚ TS2339 & TS7006: Type Casting āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ€āļ‚āđ‰āļēāļ–āļķāļ‡ roles āđ„āļ”āđ‰āđ‚āļ”āļĒāđ„āļĄāđˆ error - // āđ€āļ™āļ·āđˆāļ­āļ‡āļˆāļēāļ User Entity āļ›āļāļ•āļīāđ„āļĄāđˆāļĄāļĩ property roles (āđāļ•āđˆāļ­āļēāļˆāļ–āļđāļ Inject āļĄāļēāļ•āļ­āļ™ Runtime āļŦāļĢāļ·āļ­āļœāđˆāļēāļ™ Assignments) - const userWithRoles = user as any; - const userRoles = userWithRoles.roles - ? userWithRoles.roles.map((r: any) => r.roleName) - : []; + schemaCode: string, + data: any, + userContext: SecurityContext, + ): Promise { + if (!data) return data; - return this.jsonSchemaService.processReadData(code, data, { userRoles }); - } + // āļ”āļķāļ‡ Schema āđ€āļžāļ·āđˆāļ­āļ”āļđ Config āļāļēāļĢāļ–āļ­āļ”āļĢāļŦāļąāļŠāđāļĨāļ°āļāļēāļĢāļĄāļ­āļ‡āđ€āļŦāđ‡āļ™ + const schema = await this.findLatestByCode(schemaCode); - // ---------------------------------------------------------------------- - // Data Migration - // ---------------------------------------------------------------------- - - @Post('migrate/:table/:id') - @HttpCode(HttpStatus.OK) - @ApiOperation({ - summary: 'Migrate specific entity data to target schema version', - }) - @ApiParam({ name: 'table', description: 'Table Name (e.g. rfa_revisions)' }) - @ApiParam({ name: 'id', description: 'Entity ID' }) - @RequirePermission('system.manage_all') // Dangerous Op -> Admin Only - async migrateData( - @Param('table') tableName: string, - @Param('id', ParseIntPipe) id: number, - @Body() dto: MigrateDataDto, - ) { - return this.migrationService.migrateData( - tableName, - id, - dto.targetSchemaCode, - dto.targetVersion, + return this.jsonSecurityService.decryptAndFilterFields( + data, + schema.schemaDefinition, + userContext, ); } + + /** + * Helper: āļ”āļķāļ‡āđāļĨāļ° Cache AJV Validator Function āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž + */ + private async getValidator(schemaCode: string): Promise { + let validate = this.validators.get(schemaCode); + + if (!validate) { + const schema = await this.findLatestByCode(schemaCode); + try { + validate = this.ajv.compile(schema.schemaDefinition); + this.validators.set(schemaCode, validate); + } catch (error: any) { + throw new BadRequestException( + `Invalid Schema Definition for '${schemaCode}': ${error.message}`, + ); + } + } + return validate; + } + + /** + * Wrapper āđ€āļāđˆāļēāļŠāļģāļŦāļĢāļąāļš Backward Compatibility (āļ–āđ‰āļēāļĄāļĩāđ‚āļ„āđ‰āļ”āđ€āļāđˆāļēāđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰) + */ + async validate(schemaCode: string, data: any): Promise { + const result = await this.validateData(schemaCode, data); + if (!result.isValid) { + const errorMsg = result.errors + .map((e) => `${e.field}: ${e.message}`) + .join(', '); + throw new BadRequestException(`JSON Validation Failed: ${errorMsg}`); + } + return true; + } + + /** + * āļ­āļąāļ›āđ€āļ”āļ•āļ‚āđ‰āļ­āļĄāļđāļĨ Schema āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāļœāļĨāļāļĢāļ°āļ—āļš (Virtual Columns / UI Schema) + */ + async update( + id: number, + updateDto: UpdateJsonSchemaDto, + ): Promise { + const schema = await this.findOne(id); + + // āļ•āļĢāļ§āļˆāļŠāļ­āļš JSON Schema + if (updateDto.schemaDefinition) { + try { + this.ajv.compile(updateDto.schemaDefinition); + } catch (error: any) { + throw new BadRequestException( + `Invalid JSON Schema format: ${error.message}`, + ); + } + this.validators.delete(schema.schemaCode); // āđ€āļ„āļĨāļĩāļĒāļĢāđŒ Cache āđ€āļāđˆāļē + } + + // āļ•āļĢāļ§āļˆāļŠāļ­āļš UI Schema + if (updateDto.uiSchema) { + this.uiSchemaService.validateUiSchema( + updateDto.uiSchema as any, + updateDto.schemaDefinition || schema.schemaDefinition, + ); + } + + const updatedSchema = this.jsonSchemaRepository.merge(schema, updateDto); + const savedSchema = await this.jsonSchemaRepository.save(updatedSchema); + + // āļ­āļąāļ›āđ€āļ”āļ• Virtual Columns āđƒāļ™ Database āļ–āđ‰āļēāļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ Config + // Fix TS2345: Add empty array fallback + if (updateDto.virtualColumns && updatedSchema.virtualColumns) { + await this.virtualColumnService.setupVirtualColumns( + savedSchema.tableName, + savedSchema.virtualColumns || [], + ); + } + + return savedSchema; + } + + /** + * āļĨāļš Schema (Hard Delete) + */ + async remove(id: number): Promise { + const schema = await this.findOne(id); + this.validators.delete(schema.schemaCode); + await this.jsonSchemaRepository.remove(schema); + } } diff --git a/backend/src/modules/rfa/dto/create-rfa.dto.ts b/backend/src/modules/rfa/dto/create-rfa.dto.ts index 515a3e8..fad4f86 100644 --- a/backend/src/modules/rfa/dto/create-rfa.dto.ts +++ b/backend/src/modules/rfa/dto/create-rfa.dto.ts @@ -1,52 +1,66 @@ -// File: src/modules/rfa/dto/create-rfa-revision.dto.ts +// File: src/modules/rfa/dto/create-rfa.dto.ts +import { ApiProperty } from '@nestjs/swagger'; import { - IsString, - IsNotEmpty, - IsInt, - IsOptional, - IsDateString, - IsObject, IsArray, + IsDateString, + IsInt, + IsNotEmpty, + IsObject, + IsOptional, + IsString, } from 'class-validator'; -export class CreateRfaRevisionDto { +export class CreateRfaDto { + @ApiProperty({ description: 'ID āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ', example: 1 }) + @IsInt() + @IsNotEmpty() + projectId!: number; + + @ApiProperty({ description: 'ID āļ‚āļ­āļ‡āļ›āļĢāļ°āđ€āļ āļ— RFA', example: 1 }) + @IsInt() + @IsNotEmpty() + rfaTypeId!: number; + + @ApiProperty({ + description: 'ID āļ‚āļ­āļ‡āļŠāļēāļ‚āļēāļ‡āļēāļ™ (Discipline) āļ•āļēāļĄ Req 6B', + example: 1, + }) + @IsInt() + @IsOptional() // Optional āđ„āļ§āđ‰āļāđˆāļ­āļ™āđ€āļœāļ·āđˆāļ­āļšāļēāļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢāđ„āļĄāđˆāļšāļąāļ‡āļ„āļąāļš + disciplineId?: number; + + @ApiProperty({ + description: 'āļŦāļąāļ§āļ‚āđ‰āļ­āđ€āļ­āļāļŠāļēāļĢ', + example: 'Submission of Shop Drawing for Building A', + }) @IsString() @IsNotEmpty() title!: string; - @IsInt() - @IsNotEmpty() - rfaStatusCodeId!: number; - - @IsInt() - @IsOptional() - rfaApproveCodeId?: number; - - @IsDateString() - @IsOptional() - documentDate?: string; - - @IsDateString() - @IsOptional() - issuedDate?: string; - - @IsDateString() - @IsOptional() - receivedDate?: string; - - @IsDateString() - @IsOptional() - approvedDate?: string; - + @ApiProperty({ description: 'āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ', required: false }) @IsString() @IsOptional() description?: string; + @ApiProperty({ description: 'āļ§āļąāļ™āļ—āļĩāđˆāđƒāļ™āđ€āļ­āļāļŠāļēāļĢ', required: false }) + @IsDateString() + @IsOptional() + documentDate?: string; + + @ApiProperty({ + description: 'āļ‚āđ‰āļ­āļĄāļđāļĨ Dynamic Details (JSON)', + required: false, + }) @IsObject() @IsOptional() details?: Record; + @ApiProperty({ + description: 'āļĢāļēāļĒāļāļēāļĢ Shop Drawing Revisions āļ—āļĩāđˆāđāļ™āļšāļĄāļēāļ”āđ‰āļ§āļĒ', + required: false, + type: [Number], + }) @IsArray() @IsOptional() - shopDrawingRevisionIds?: number[]; // IDs of linked Shop Drawings + shopDrawingRevisionIds?: number[]; } diff --git a/backend/src/modules/rfa/dto/submit-rfa.dto.ts b/backend/src/modules/rfa/dto/submit-rfa.dto.ts new file mode 100644 index 0000000..392c5d9 --- /dev/null +++ b/backend/src/modules/rfa/dto/submit-rfa.dto.ts @@ -0,0 +1,13 @@ +// File: src/modules/rfa/dto/submit-rfa.dto.ts +import { ApiProperty } from '@nestjs/swagger'; +import { IsInt, IsNotEmpty } from 'class-validator'; + +export class SubmitRfaDto { + @ApiProperty({ + description: 'ID āļ‚āļ­āļ‡ Routing Template āļ—āļĩāđˆāļˆāļ°āđƒāļŠāđ‰āđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡', + example: 1, + }) + @IsInt() + @IsNotEmpty() + templateId!: number; +} diff --git a/backend/src/modules/rfa/entities/rfa-revision.entity.ts b/backend/src/modules/rfa/entities/rfa-revision.entity.ts index 8181856..c979f56 100644 --- a/backend/src/modules/rfa/entities/rfa-revision.entity.ts +++ b/backend/src/modules/rfa/entities/rfa-revision.entity.ts @@ -1,23 +1,21 @@ // File: src/modules/rfa/entities/rfa-revision.entity.ts import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, OneToMany, + PrimaryGeneratedColumn, Unique, - Index, } from 'typeorm'; -import { Rfa } from './rfa.entity'; import { Correspondence } from '../../correspondence/entities/correspondence.entity'; -import { RfaStatusCode } from './rfa-status-code.entity'; -import { RfaApproveCode } from './rfa-approve-code.entity'; import { User } from '../../user/entities/user.entity'; +import { RfaApproveCode } from './rfa-approve-code.entity'; import { RfaItem } from './rfa-item.entity'; +import { RfaStatusCode } from './rfa-status-code.entity'; import { RfaWorkflow } from './rfa-workflow.entity'; +import { Rfa } from './rfa.entity'; @Entity('rfa_revisions') @Unique(['rfaId', 'revisionNumber']) @@ -65,11 +63,16 @@ export class RfaRevision { @Column({ type: 'text', nullable: true }) description?: string; - // ✅ [New] āđ€āļžāļīāđˆāļĄ field details āļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ Dynamic āļ‚āļ­āļ‡ RFA (āđ€āļŠāđˆāļ™ Method Statement Details) + // --- JSON & Schema Section --- + @Column({ type: 'json', nullable: true }) details?: any; - // ✅ [New] Virtual Column: āļ”āļķāļ‡āļˆāļģāļ™āļ§āļ™āđāļšāļšāļ—āļĩāđˆāđāļ™āļš (drawingCount) āļˆāļēāļ JSON + // ✅ [New] āļˆāļģāđ€āļ›āđ‡āļ™āļŠāļģāļŦāļĢāļąāļš Data Migration (T2.5.5) + @Column({ name: 'schema_version', default: 1 }) + schemaVersion!: number; + + // ✅ Virtual Column @Column({ name: 'v_ref_drawing_count', type: 'int', @@ -79,6 +82,8 @@ export class RfaRevision { }) vRefDrawingCount?: number; + // --- Timestamp --- + @CreateDateColumn({ name: 'created_at' }) createdAt!: Date; @@ -110,11 +115,9 @@ export class RfaRevision { @JoinColumn({ name: 'created_by' }) creator?: User; - // Items (Shop Drawings inside this RFA) @OneToMany(() => RfaItem, (item) => item.rfaRevision, { cascade: true }) items!: RfaItem[]; - // Workflows @OneToMany(() => RfaWorkflow, (workflow) => workflow.rfaRevision, { cascade: true, }) diff --git a/backend/src/modules/rfa/entities/rfa-workflow.entity.ts b/backend/src/modules/rfa/entities/rfa-workflow.entity.ts index b43b64f..7694cb6 100644 --- a/backend/src/modules/rfa/entities/rfa-workflow.entity.ts +++ b/backend/src/modules/rfa/entities/rfa-workflow.entity.ts @@ -1,15 +1,17 @@ +// File: src/modules/rfa/entities/rfa-workflow.entity.ts import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, - UpdateDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, + PrimaryGeneratedColumn, + UpdateDateColumn, } from 'typeorm'; -import { RfaRevision } from './rfa-revision.entity'; import { Organization } from '../../project/entities/organization.entity'; import { User } from '../../user/entities/user.entity'; +import { RfaRevision } from './rfa-revision.entity'; +import { RfaActionType } from './rfa-workflow-template-step.entity'; // ✅ Import Enum @Entity('rfa_workflows') export class RfaWorkflow { @@ -31,10 +33,10 @@ export class RfaWorkflow { @Column({ name: 'action_type', type: 'enum', - enum: ['REVIEW', 'APPROVE', 'ACKNOWLEDGE'], + enum: RfaActionType, // ✅ Use Shared Enum nullable: true, }) - actionType?: string; + actionType?: RfaActionType; @Column({ type: 'enum', @@ -50,7 +52,7 @@ export class RfaWorkflow { completedAt?: Date; @Column({ type: 'json', nullable: true }) - stateContext?: Record; // āđ€āļāđ‡āļš Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™ + stateContext?: Record; @CreateDateColumn({ name: 'created_at' }) createdAt!: Date; @@ -59,7 +61,7 @@ export class RfaWorkflow { updatedAt!: Date; // Relations - @ManyToOne(() => RfaRevision, (rev) => rev.workflows, { onDelete: 'CASCADE' }) // āļ•āđ‰āļ­āļ‡āđ„āļ›āđ€āļžāļīāđˆāļĄ Property workflows āđƒāļ™ RfaRevision āļ”āđ‰āļ§āļĒ + @ManyToOne(() => RfaRevision, (rev) => rev.workflows, { onDelete: 'CASCADE' }) @JoinColumn({ name: 'rfa_revision_id' }) rfaRevision!: RfaRevision; diff --git a/backend/src/modules/rfa/entities/rfa.entity.ts b/backend/src/modules/rfa/entities/rfa.entity.ts index fefd9cd..b2bb14d 100644 --- a/backend/src/modules/rfa/entities/rfa.entity.ts +++ b/backend/src/modules/rfa/entities/rfa.entity.ts @@ -1,16 +1,17 @@ import { - Entity, - PrimaryGeneratedColumn, Column, CreateDateColumn, DeleteDateColumn, - ManyToOne, + Entity, JoinColumn, + ManyToOne, OneToMany, + PrimaryGeneratedColumn, } from 'typeorm'; -import { RfaType } from './rfa-type.entity'; +import { Discipline } from '../../master/entities/discipline.entity'; // Import āđƒāļŦāļĄāđˆ import { User } from '../../user/entities/user.entity'; import { RfaRevision } from './rfa-revision.entity'; +import { RfaType } from './rfa-type.entity'; @Entity('rfas') export class Rfa { @@ -34,6 +35,11 @@ export class Rfa { @JoinColumn({ name: 'rfa_type_id' }) rfaType!: RfaType; + // ✅ [NEW] Relation + @ManyToOne(() => Discipline) + @JoinColumn({ name: 'discipline_id' }) + discipline?: Discipline; + @ManyToOne(() => User) @JoinColumn({ name: 'created_by' }) creator?: User; diff --git a/backend/src/modules/rfa/rfa-workflow.service.ts b/backend/src/modules/rfa/rfa-workflow.service.ts new file mode 100644 index 0000000..da6d5e0 --- /dev/null +++ b/backend/src/modules/rfa/rfa-workflow.service.ts @@ -0,0 +1,193 @@ +// File: src/modules/rfa/rfa-workflow.service.ts + +import { Injectable, Logger, NotFoundException } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { DataSource, Repository } from 'typeorm'; + +// Modules +import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service'; + +// Entities +import { RfaApproveCode } from './entities/rfa-approve-code.entity'; +import { RfaRevision } from './entities/rfa-revision.entity'; +import { RfaStatusCode } from './entities/rfa-status-code.entity'; +import { Rfa } from './entities/rfa.entity'; + +// DTOs +import { WorkflowTransitionDto } from '../workflow-engine/dto/workflow-transition.dto'; + +@Injectable() +export class RfaWorkflowService { + private readonly logger = new Logger(RfaWorkflowService.name); + private readonly WORKFLOW_CODE = 'RFA_FLOW_V1'; // āļ„āļ§āļĢāļāļģāļŦāļ™āļ”āđƒāļ™ Config āļŦāļĢāļ·āļ­ Enum + + constructor( + private readonly workflowEngine: WorkflowEngineService, + @InjectRepository(Rfa) + private readonly rfaRepo: Repository, + @InjectRepository(RfaRevision) + private readonly revisionRepo: Repository, + @InjectRepository(RfaStatusCode) + private readonly statusRepo: Repository, + @InjectRepository(RfaApproveCode) + private readonly approveCodeRepo: Repository, + private readonly dataSource: DataSource, + ) {} + + /** + * āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™ Workflow āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢ RFA (āđ€āļĄāļ·āđˆāļ­āļāļ” Submit) + */ + async submitWorkflow(rfaId: number, userId: number, note?: string) { + const queryRunner = this.dataSource.createQueryRunner(); + await queryRunner.connect(); + await queryRunner.startTransaction(); + + try { + // 1. āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ Revision āļ›āļąāļˆāļˆāļļāļšāļąāļ™ + const revision = await this.revisionRepo.findOne({ + where: { id: rfaId, isCurrent: true }, + relations: ['rfa'], + }); + + if (!revision) { + throw new NotFoundException( + `Current Revision for RFA ID ${rfaId} not found`, + ); + } + + // 2. āļŠāļĢāđ‰āļēāļ‡ Context (āļ‚āđ‰āļ­āļĄāļđāļĨāļ›āļĢāļ°āļāļ­āļšāļāļēāļĢāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆ) + const context = { + rfaType: revision.rfa.rfaTypeId, + discipline: revision.rfa.discipline, + ownerId: userId, + // āļ­āļēāļˆāđ€āļžāļīāđˆāļĄāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļ­āļ·āđˆāļ™āđ† āđ€āļŠāđˆāļ™ āļˆāļģāļ™āļ§āļ™āļ§āļąāļ™, āļ„āļ§āļēāļĄāđ€āļĢāđˆāļ‡āļ”āđˆāļ§āļ™ + }; + + // 3. āļŠāļĢāđ‰āļēāļ‡ Workflow Instance + // Entity Type = 'rfa_revision' + const instance = await this.workflowEngine.createInstance( + this.WORKFLOW_CODE, + 'rfa_revision', + revision.id.toString(), + context, + ); + + // 4. Auto Transition: SUBMIT + const transitionResult = await this.workflowEngine.processTransition( + instance.id, + 'SUBMIT', + userId, + note || 'RFA Submitted', + {}, + ); + + // 5. Sync āļŠāļ–āļēāļ™āļ°āļāļĨāļąāļšāļ•āļēāļĢāļēāļ‡ RFA Revision + await this.syncStatus( + revision, + transitionResult.nextState, + undefined, + queryRunner, + ); + + await queryRunner.commitTransaction(); + + this.logger.log( + `Started workflow for RFA #${rfaId} (Instance: ${instance.id})`, + ); + + return { + instanceId: instance.id, + currentState: transitionResult.nextState, + }; + } catch (error) { + await queryRunner.rollbackTransaction(); + this.logger.error(`Failed to submit RFA workflow: ${error}`); + throw error; + } finally { + await queryRunner.release(); + } + } + + /** + * āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī/āļ•āļĢāļ§āļˆāļŠāļ­āļš RFA + */ + async processAction( + instanceId: string, + userId: number, + dto: WorkflowTransitionDto, + ) { + // 1. āļŠāđˆāļ‡āļ„āļģāļŠāļąāđˆāļ‡āđƒāļŦāđ‰ Engine āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨ + const result = await this.workflowEngine.processTransition( + instanceId, + dto.action, + userId, + dto.comment, + dto.payload, + ); + + // 2. Sync āļŠāļ–āļēāļ™āļ°āļāļĨāļąāļšāļ•āļēāļĢāļēāļ‡āđ€āļ”āļīāļĄ + const instance = await this.workflowEngine.getInstanceById(instanceId); + if (instance && instance.entityType === 'rfa_revision') { + const revision = await this.revisionRepo.findOne({ + where: { id: parseInt(instance.entityId) }, + }); + if (revision) { + // āđ€āļŠāđ‡āļ„āļ§āđˆāļē Action āļ™āļĩāđ‰āļĄāļĩāļāļēāļĢāļĢāļ°āļšāļļ Approve Code āļĄāļēāđƒāļ™ Payload āļŦāļĢāļ·āļ­āđ„āļĄāđˆ (āđ€āļŠāđˆāļ™ '1A', '3R') + const approveCodeStr = dto.payload?.approveCode; + await this.syncStatus(revision, result.nextState, approveCodeStr); + } + } + + return result; + } + + /** + * Helper: Map Workflow State -> RFA Status & Approve Code + */ + private async syncStatus( + revision: RfaRevision, + workflowState: string, + approveCodeStr?: string, // āđ€āļŠāđˆāļ™ '1A', '1C' + queryRunner?: any, + ) { + // 1. Map Workflow State -> RFA Status Code (DFT, FAP, FCO...) + const statusMap: Record = { + DRAFT: 'DFT', + IN_REVIEW_CSC: 'FRE', // For Review (CSC) + IN_REVIEW_OWNER: 'FAP', // For Approve (Owner) + APPROVED: 'FCO', // For Construction (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡) + REJECTED: 'CC', // Canceled/Rejected + REVISE: 'DFT', // āļāļĨāļąāļšāđ„āļ›āđāļāđ‰ (Draft) + }; + + const targetStatusCode = statusMap[workflowState] || 'DFT'; + const status = await this.statusRepo.findOne({ + where: { statusCode: targetStatusCode }, + }); + + if (status) { + revision.rfaStatusCodeId = status.id; + } + + // 2. Map Approve Code (āļ–āđ‰āļēāļĄāļĩ) + if (approveCodeStr) { + const approveCode = await this.approveCodeRepo.findOne({ + where: { approveCode: approveCodeStr }, + }); + if (approveCode) { + revision.rfaApproveCodeId = approveCode.id; + revision.approvedDate = new Date(); // āļšāļąāļ™āļ—āļķāļāļ§āļąāļ™āļ—āļĩāđˆāļ­āļ™āļļāļĄāļąāļ•āļī + } + } + + // 3. Save + const manager = queryRunner + ? queryRunner.manager + : this.revisionRepo.manager; + await manager.save(revision); + + this.logger.log( + `Synced RFA Status Revision ${revision.id}: State=${workflowState} -> Status=${targetStatusCode}, AppCode=${approveCodeStr}`, + ); + } +} diff --git a/backend/src/modules/rfa/rfa.controller.ts b/backend/src/modules/rfa/rfa.controller.ts index 4c16fd2..5f53abb 100644 --- a/backend/src/modules/rfa/rfa.controller.ts +++ b/backend/src/modules/rfa/rfa.controller.ts @@ -1,24 +1,26 @@ +// File: src/modules/rfa/rfa.controller.ts import { + Body, Controller, Get, - Post, - Body, Param, ParseIntPipe, + Post, UseGuards, } from '@nestjs/common'; -import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; +import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger'; -import { RfaService } from './rfa.service'; -import { CreateRfaDto } from './dto/create-rfa.dto'; -import { WorkflowActionDto } from '../correspondence/dto/workflow-action.dto'; // Reuse DTO +import { WorkflowActionDto } from '../correspondence/dto/workflow-action.dto'; import { User } from '../user/entities/user.entity'; +import { CreateRfaDto } from './dto/create-rfa.dto'; +import { SubmitRfaDto } from './dto/submit-rfa.dto'; // ✅ Import DTO āđƒāļŦāļĄāđˆ +import { RfaService } from './rfa.service'; +import { Audit } from '../../common/decorators/audit.decorator'; +import { CurrentUser } from '../../common/decorators/current-user.decorator'; +import { RequirePermission } from '../../common/decorators/require-permission.decorator'; import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; import { RbacGuard } from '../../common/guards/rbac.guard'; -import { RequirePermission } from '../../common/decorators/require-permission.decorator'; -import { CurrentUser } from '../../common/decorators/current-user.decorator'; -import { Audit } from '../../common/decorators/audit.decorator'; // Import @ApiTags('RFA (Request for Approval)') @ApiBearerAuth() @@ -29,26 +31,28 @@ export class RfaController { @Post() @ApiOperation({ summary: 'Create new RFA (Draft)' }) - @RequirePermission('rfa.create') // āļŠāļīāļ—āļ˜āļīāđŒ ID 37 - @Audit('rfa.create', 'rfa') // ✅ āđāļ›āļ°āļ•āļĢāļ‡āļ™āļĩāđ‰ + @RequirePermission('rfa.create') + @Audit('rfa.create', 'rfa') create(@Body() createDto: CreateRfaDto, @CurrentUser() user: User) { return this.rfaService.create(createDto, user); } @Post(':id/submit') @ApiOperation({ summary: 'Submit RFA to Workflow' }) - @RequirePermission('rfa.create') // āļœāļđāđ‰āļŠāļĢāđ‰āļēāļ‡āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļŠāđˆāļ‡ + @RequirePermission('rfa.create') + @Audit('rfa.submit', 'rfa') submit( @Param('id', ParseIntPipe) id: number, - @Body('templateId', ParseIntPipe) templateId: number, // āļĢāļąāļš Template ID + @Body() submitDto: SubmitRfaDto, // ✅ āđƒāļŠāđ‰ DTO @CurrentUser() user: User, ) { - return this.rfaService.submit(id, templateId, user); + return this.rfaService.submit(id, submitDto.templateId, user); } @Post(':id/action') @ApiOperation({ summary: 'Process Workflow Action (Approve/Reject)' }) - @RequirePermission('workflow.action_review') // āļŠāļīāļ—āļ˜āļīāđŒāđƒāļ™āļāļēāļĢ Approve/Review + @RequirePermission('workflow.action_review') + @Audit('rfa.action', 'rfa') processAction( @Param('id', ParseIntPipe) id: number, @Body() actionDto: WorkflowActionDto, diff --git a/backend/src/modules/rfa/rfa.module.ts b/backend/src/modules/rfa/rfa.module.ts index 001e1bd..55a2b27 100644 --- a/backend/src/modules/rfa/rfa.module.ts +++ b/backend/src/modules/rfa/rfa.module.ts @@ -3,35 +3,34 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; // Entities -import { Rfa } from './entities/rfa.entity'; -import { RfaRevision } from './entities/rfa-revision.entity'; -import { RfaItem } from './entities/rfa-item.entity'; -import { RfaType } from './entities/rfa-type.entity'; -import { RfaStatusCode } from './entities/rfa-status-code.entity'; -import { RfaApproveCode } from './entities/rfa-approve-code.entity'; -import { Correspondence } from '../correspondence/entities/correspondence.entity'; -import { ShopDrawingRevision } from '../drawing/entities/shop-drawing-revision.entity'; -import { RfaWorkflow } from './entities/rfa-workflow.entity'; -import { RfaWorkflowTemplate } from './entities/rfa-workflow-template.entity'; -import { RfaWorkflowTemplateStep } from './entities/rfa-workflow-template-step.entity'; import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity'; +import { Correspondence } from '../correspondence/entities/correspondence.entity'; import { RoutingTemplate } from '../correspondence/entities/routing-template.entity'; -// āļŦāļĄāļēāļĒāđ€āļŦāļ•āļļ: āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļ·āđˆāļ­āđ„āļŸāļĨāđŒ Entity āđƒāļŦāđ‰āļ•āļĢāļ‡āļāļąāļšāļ—āļĩāđˆāļĄāļĩāļˆāļĢāļīāļ‡ (āļšāļēāļ‡āļ—āļĩāļ­āļēāļˆāļŠāļ·āđˆāļ­ RoutingTemplate) +import { ShopDrawingRevision } from '../drawing/entities/shop-drawing-revision.entity'; +import { RfaApproveCode } from './entities/rfa-approve-code.entity'; +import { RfaItem } from './entities/rfa-item.entity'; +import { RfaRevision } from './entities/rfa-revision.entity'; +import { RfaStatusCode } from './entities/rfa-status-code.entity'; +import { RfaType } from './entities/rfa-type.entity'; +import { RfaWorkflowTemplateStep } from './entities/rfa-workflow-template-step.entity'; +import { RfaWorkflowTemplate } from './entities/rfa-workflow-template.entity'; +import { RfaWorkflow } from './entities/rfa-workflow.entity'; +import { Rfa } from './entities/rfa.entity'; // Services & Controllers -import { RfaService } from './rfa.service'; +import { RfaWorkflowService } from './rfa-workflow.service'; // Register Service import { RfaController } from './rfa.controller'; +import { RfaService } from './rfa.service'; // External Modules import { DocumentNumberingModule } from '../document-numbering/document-numbering.module'; -import { UserModule } from '../user/user.module'; +import { NotificationModule } from '../notification/notification.module'; import { SearchModule } from '../search/search.module'; -import { WorkflowEngineModule } from '../workflow-engine/workflow-engine.module'; // ✅ Import -import { NotificationModule } from '../notification/notification.module'; // ✅ āđ€āļžāļīāđˆāļĄ NotificationModule +import { UserModule } from '../user/user.module'; +import { WorkflowEngineModule } from '../workflow-engine/workflow-engine.module'; @Module({ imports: [ - // 1. Register Entities (āđ€āļ‰āļžāļēāļ° Entity āđ€āļ—āđˆāļēāļ™āļąāđ‰āļ™ āļŦāđ‰āļēāļĄāđƒāļŠāđˆ Module) TypeOrmModule.forFeature([ Rfa, RfaRevision, @@ -47,15 +46,13 @@ import { NotificationModule } from '../notification/notification.module'; // ✅ CorrespondenceRouting, RoutingTemplate, ]), - - // 2. Import External Modules (Services āļ—āļĩāđˆ Inject āđ€āļ‚āđ‰āļēāļĄāļē) DocumentNumberingModule, UserModule, SearchModule, - WorkflowEngineModule, // ✅ āļĒāđ‰āļēāļĒāļĄāļēāđƒāļŠāđˆāļ•āļĢāļ‡āļ™āļĩāđ‰ (imports āļŦāļĨāļąāļ) - NotificationModule, // ✅ āđ€āļžāļīāđˆāļĄāļ•āļĢāļ‡āļ™āļĩāđ‰ āđ€āļžāļ·āđˆāļ­āđāļāđ‰ dependency index [13] + WorkflowEngineModule, + NotificationModule, ], - providers: [RfaService], + providers: [RfaService, RfaWorkflowService], controllers: [RfaController], exports: [RfaService], }) diff --git a/backend/src/modules/rfa/rfa.service.ts b/backend/src/modules/rfa/rfa.service.ts index a8e462e..e12ebb1 100644 --- a/backend/src/modules/rfa/rfa.service.ts +++ b/backend/src/modules/rfa/rfa.service.ts @@ -1,42 +1,41 @@ // File: src/modules/rfa/rfa.service.ts import { - Injectable, - NotFoundException, - InternalServerErrorException, - Logger, BadRequestException, ForbiddenException, + Injectable, + InternalServerErrorException, + Logger, + NotFoundException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { Repository, DataSource, In } from 'typeorm'; +import { DataSource, In, Repository } from 'typeorm'; // Entities -import { Rfa } from './entities/rfa.entity.js'; -import { RfaRevision } from './entities/rfa-revision.entity.js'; -import { RfaItem } from './entities/rfa-item.entity.js'; -import { RfaType } from './entities/rfa-type.entity.js'; -import { RfaStatusCode } from './entities/rfa-status-code.entity.js'; -import { RfaApproveCode } from './entities/rfa-approve-code.entity.js'; -import { Correspondence } from '../correspondence/entities/correspondence.entity.js'; -import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity.js'; -import { RoutingTemplate } from '../correspondence/entities/routing-template.entity.js'; -import { ShopDrawingRevision } from '../drawing/entities/shop-drawing-revision.entity.js'; -import { User } from '../user/entities/user.entity.js'; +import { CorrespondenceRouting } from '../correspondence/entities/correspondence-routing.entity'; +import { Correspondence } from '../correspondence/entities/correspondence.entity'; +import { RoutingTemplate } from '../correspondence/entities/routing-template.entity'; +import { ShopDrawingRevision } from '../drawing/entities/shop-drawing-revision.entity'; +import { User } from '../user/entities/user.entity'; +import { RfaApproveCode } from './entities/rfa-approve-code.entity'; +import { RfaItem } from './entities/rfa-item.entity'; +import { RfaRevision } from './entities/rfa-revision.entity'; +import { RfaStatusCode } from './entities/rfa-status-code.entity'; +import { RfaType } from './entities/rfa-type.entity'; +import { Rfa } from './entities/rfa.entity'; // DTOs -import { CreateRfaDto } from './dto/create-rfa.dto.js'; -import { WorkflowActionDto } from '../correspondence/dto/workflow-action.dto.js'; +import { WorkflowActionDto } from '../correspondence/dto/workflow-action.dto'; +import { CreateRfaDto } from './dto/create-rfa.dto'; // Interfaces & Enums -import { WorkflowAction } from '../workflow-engine/interfaces/workflow.interface.js'; // Services -import { DocumentNumberingService } from '../document-numbering/document-numbering.service.js'; -import { UserService } from '../user/user.service.js'; -import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service.js'; -import { NotificationService } from '../notification/notification.service.js'; -import { SearchService } from '../search/search.service.js'; +import { DocumentNumberingService } from '../document-numbering/document-numbering.service'; +import { NotificationService } from '../notification/notification.service'; +import { SearchService } from '../search/search.service'; +import { UserService } from '../user/user.service'; +import { WorkflowEngineService } from '../workflow-engine/workflow-engine.service'; @Injectable() export class RfaService { @@ -87,6 +86,7 @@ export class RfaService { ); } + // Determine User Organization let userOrgId = user.primaryOrganizationId; if (!userOrgId) { const fullUser = await this.userService.findOne(user.user_id); @@ -101,14 +101,14 @@ export class RfaService { await queryRunner.startTransaction(); try { - const orgCode = 'ORG'; // TODO: Fetch real ORG Code + const orgCode = 'ORG'; // TODO: Fetch real ORG Code from Org Service if needed - // [FIXED] āđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰āđāļšāļš Object Context āļžāļĢāđ‰āļ­āļĄ disciplineId + // [UPDATED] Generate Document Number with Discipline const docNumber = await this.numberingService.generateNextNumber({ projectId: createDto.projectId, originatorId: userOrgId, - typeId: createDto.rfaTypeId, // RFA Type āđƒāļŠāđ‰āđ€āļ›āđ‡āļ™ ID āđƒāļ™āļāļēāļĢāļ™āļąāļšāđ€āļĨāļ‚ - disciplineId: createDto.disciplineId, // āļŠāļģāļ„āļąāļāļĄāļēāļāļŠāļģāļŦāļĢāļąāļš RFA (Req 6B) + typeId: createDto.rfaTypeId, + disciplineId: createDto.disciplineId ?? 0, // ✅ āļŠāđˆāļ‡ disciplineId āđ„āļ›āļ”āđ‰āļ§āļĒ (0 āļ–āđ‰āļēāđ„āļĄāđˆāļĄāļĩ) year: new Date().getFullYear(), customTokens: { TYPE_CODE: rfaType.typeCode, @@ -116,24 +116,31 @@ export class RfaService { }, }); + // 1. Create Correspondence Record const correspondence = queryRunner.manager.create(Correspondence, { correspondenceNumber: docNumber, - correspondenceTypeId: createDto.rfaTypeId, // Map RFA Type to Corr Type ID - disciplineId: createDto.disciplineId, // āļšāļąāļ™āļ—āļķāļ Discipline + correspondenceTypeId: createDto.rfaTypeId, // Assuming RFA Type maps directly or via logic + // Note: āļ–āđ‰āļē CorrespondenceType āđāļĒāļ ID āļāļąāļš RFA Type āļ•āđ‰āļ­āļ‡ Map āđƒāļŦāđ‰āļ–āļđāļ + // āđƒāļ™āļ—āļĩāđˆāļ™āļĩāđ‰āļŠāļĄāļĄāļ•āļīāļ§āđˆāļēāđƒāļŠāđ‰ ID āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ āļŦāļĢāļ·āļ­ RFA Type āđ€āļ›āđ‡āļ™ SubType āļ‚āļ­āļ‡ Correspondence projectId: createDto.projectId, originatorId: userOrgId, - isInternal: false, + isInternalCommunication: false, createdBy: user.user_id, + // ✅ Add disciplineId column if correspondence table supports it (as per Data Dictionary Update) + // disciplineId: createDto.disciplineId }); const savedCorr = await queryRunner.manager.save(correspondence); + // 2. Create RFA Master Record const rfa = queryRunner.manager.create(Rfa, { rfaTypeId: createDto.rfaTypeId, - disciplineId: createDto.disciplineId, // āļšāļąāļ™āļ—āļķāļ Discipline createdBy: user.user_id, + // ✅ āļ–āđ‰āļē Entity Rfa āļĄāļĩ disciplineId āđƒāļŦāđ‰āđƒāļŠāđˆāļ•āļĢāļ‡āļ™āļĩāđ‰āļ”āđ‰āļ§āļĒ + // disciplineId: createDto.disciplineId }); const savedRfa = await queryRunner.manager.save(rfa); + // 3. Create First Revision (Draft) const rfaRevision = queryRunner.manager.create(RfaRevision, { correspondenceId: savedCorr.id, rfaId: savedRfa.id, @@ -147,9 +154,12 @@ export class RfaService { ? new Date(createDto.documentDate) : new Date(), createdBy: user.user_id, + details: createDto.details, // ✅ Save JSON Details + schemaVersion: 1, // ✅ Default Schema Version }); const savedRevision = await queryRunner.manager.save(rfaRevision); + // 4. Link Shop Drawings if ( createDto.shopDrawingRevisionIds && createDto.shopDrawingRevisionIds.length > 0 @@ -164,7 +174,8 @@ export class RfaService { const rfaItems = shopDrawings.map((sd) => queryRunner.manager.create(RfaItem, { - rfaRevisionId: savedCorr.id, + rfaRevisionId: savedCorr.id, // āđƒāļŠāđ‰ ID āļ‚āļ­āļ‡ Correspondence (āļ•āļēāļĄ Schema āļ—āļĩāđˆāļ­āļ­āļāđāļšāļšāđ„āļ§āđ‰) āļŦāļĢāļ·āļ­ RFA Revision ID āđāļĨāđ‰āļ§āđāļ•āđˆāļāļēāļĢ Map Entity + // āļ•āļēāļĄ Entity RfaItem āļ—āļĩāđˆāđƒāļŦāđ‰āļĄāļē: rfaRevisionId map āđ„āļ›āļ—āļĩāđˆ correspondence_id shopDrawingRevisionId: sd.id, }), ); @@ -173,9 +184,10 @@ export class RfaService { await queryRunner.commitTransaction(); + // Indexing for Search this.searchService.indexDocument({ id: savedCorr.id, - type: 'correspondence', + type: 'rfa', docNumber: docNumber, title: createDto.title, description: createDto.description, @@ -186,10 +198,8 @@ export class RfaService { return { ...savedRfa, - currentRevision: { - ...savedRevision, - correspondenceNumber: docNumber, - }, + correspondenceNumber: docNumber, + currentRevision: savedRevision, }; } catch (err) { await queryRunner.rollbackTransaction(); @@ -200,7 +210,8 @@ export class RfaService { } } - // ... (method āļ­āļ·āđˆāļ™āđ† findOne, submit, processAction āļ„āļ‡āđ€āļ”āļīāļĄ) + // ... (āļŠāđˆāļ§āļ™ findOne, submit, processAction āļ„āļ‡āđ€āļ”āļīāļĄāļˆāļēāļāđ„āļŸāļĨāđŒāļ—āļĩāđˆāđāļ™āļšāļĄāļē āđāļ„āđˆāļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđ€āļĨāđ‡āļāļ™āđ‰āļ­āļĒāļ•āļēāļĄ Context) ... + async findOne(id: number) { const rfa = await this.rfaRepo.findOne({ where: { id }, @@ -230,14 +241,8 @@ export class RfaService { const rfa = await this.findOne(rfaId); const currentRevision = rfa.revisions.find((r) => r.isCurrent); - if (!currentRevision) { + if (!currentRevision) throw new NotFoundException('Current revision not found'); - } - - if (!currentRevision.correspondence) { - throw new InternalServerErrorException('Correspondence relation missing'); - } - if (currentRevision.statusCode.statusCode !== 'DFT') { throw new BadRequestException('Only DRAFT documents can be submitted'); } @@ -255,21 +260,21 @@ export class RfaService { const statusForApprove = await this.rfaStatusRepo.findOne({ where: { statusCode: 'FAP' }, }); - if (!statusForApprove) { + if (!statusForApprove) throw new InternalServerErrorException('Status FAP not found'); - } const queryRunner = this.dataSource.createQueryRunner(); await queryRunner.connect(); await queryRunner.startTransaction(); try { + // Update Revision Status currentRevision.rfaStatusCodeId = statusForApprove.id; currentRevision.issuedDate = new Date(); await queryRunner.manager.save(currentRevision); + // Create First Routing Step const firstStep = template.steps[0]; - const routing = queryRunner.manager.create(CorrespondenceRouting, { correspondenceId: currentRevision.correspondenceId, templateId: template.id, @@ -286,20 +291,18 @@ export class RfaService { }); await queryRunner.manager.save(routing); + // Notify const recipientUserId = await this.userService.findDocControlIdByOrg( firstStep.toOrganizationId, ); - if (recipientUserId) { - const docNo = currentRevision.correspondence.correspondenceNumber; await this.notificationService.send({ userId: recipientUserId, title: `RFA Submitted: ${currentRevision.title}`, - message: `āļĄāļĩāđ€āļ­āļāļŠāļēāļĢ RFA āđƒāļŦāļĄāđˆāļĢāļ­āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļˆāļēāļāļ„āļļāļ“ (āđ€āļĨāļ‚āļ—āļĩāđˆ: ${docNo})`, + message: `RFA ${currentRevision.correspondence.correspondenceNumber} submitted for approval.`, type: 'SYSTEM', entityType: 'rfa', entityId: rfa.id, - link: `/rfas/${rfa.id}`, }); } @@ -307,7 +310,6 @@ export class RfaService { return { message: 'RFA Submitted successfully', routing }; } catch (err) { await queryRunner.rollbackTransaction(); - this.logger.error(`Failed to submit RFA: ${(err as Error).message}`); throw err; } finally { await queryRunner.release(); @@ -315,11 +317,13 @@ export class RfaService { } async processAction(rfaId: number, dto: WorkflowActionDto, user: User) { + // Logic āļ„āļ‡āđ€āļ”āļīāļĄ: āļŦāļē Current Routing -> Check Permission -> Call Workflow Engine -> Update DB + // āđƒāļŠāđ‰ this.workflowEngine.processAction (Legacy Support) + // ... (āļŠāļēāļĄāļēāļĢāļ–āđƒāļŠāđ‰ Code āđ€āļ”āļīāļĄāļˆāļēāļāļ—āļĩāđˆāļ„āļļāļ“āđāļ™āļšāļĄāļēāđ„āļ”āđ‰āđ€āļĨāļĒ āđ€āļžāļĢāļēāļ° Logic āļ–āļđāļāļ•āđ‰āļ­āļ‡āđāļĨāđ‰āļ§āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđƒāļŠāđ‰ CorrespondenceRouting) ... const rfa = await this.findOne(rfaId); const currentRevision = rfa.revisions.find((r) => r.isCurrent); - if (!currentRevision) { + if (!currentRevision) throw new NotFoundException('Current revision not found'); - } const currentRouting = await this.routingRepo.findOne({ where: { @@ -330,10 +334,8 @@ export class RfaService { relations: ['toOrganization'], }); - if (!currentRouting) { + if (!currentRouting) throw new BadRequestException('No active workflow step found'); - } - if (currentRouting.toOrganizationId !== user.primaryOrganizationId) { throw new ForbiddenException( 'You are not authorized to process this step', @@ -345,10 +347,10 @@ export class RfaService { relations: ['steps'], }); - if (!template || !template.steps) { - throw new InternalServerErrorException('Template or steps not found'); - } + if (!template || !template.steps) + throw new InternalServerErrorException('Template not found'); + // Call Engine to calculate next step const result = this.workflowEngine.processAction( currentRouting.sequence, template.steps.length, @@ -361,19 +363,19 @@ export class RfaService { await queryRunner.startTransaction(); try { - currentRouting.status = - dto.action === WorkflowAction.REJECT ? 'REJECTED' : 'ACTIONED'; + // Update current routing + currentRouting.status = dto.action === 'REJECT' ? 'REJECTED' : 'ACTIONED'; currentRouting.processedByUserId = user.user_id; currentRouting.processedAt = new Date(); currentRouting.comments = dto.comments; await queryRunner.manager.save(currentRouting); - if (result.nextStepSequence && dto.action !== WorkflowAction.REJECT) { - const nextStepConfig = template.steps.find( + // Create next routing if available + if (result.nextStepSequence && dto.action !== 'REJECT') { + const nextStep = template.steps.find( (s) => s.sequence === result.nextStepSequence, ); - - if (nextStepConfig) { + if (nextStep) { const nextRouting = queryRunner.manager.create( CorrespondenceRouting, { @@ -381,52 +383,43 @@ export class RfaService { templateId: template.id, sequence: result.nextStepSequence, fromOrganizationId: user.primaryOrganizationId, - toOrganizationId: nextStepConfig.toOrganizationId, - stepPurpose: nextStepConfig.stepPurpose, + toOrganizationId: nextStep.toOrganizationId, + stepPurpose: nextStep.stepPurpose, status: 'SENT', dueDate: new Date( - Date.now() + - (nextStepConfig.expectedDays || 7) * 24 * 60 * 60 * 1000, + Date.now() + (nextStep.expectedDays || 7) * 24 * 60 * 60 * 1000, ), }, ); await queryRunner.manager.save(nextRouting); } - } else if ( - result.nextStepSequence === null && - dto.action !== WorkflowAction.REJECT - ) { - const approveCodeStr = - dto.action === WorkflowAction.APPROVE ? '1A' : '4X'; - const approveCode = await this.rfaApproveRepo.findOne({ - where: { approveCode: approveCodeStr }, - }); - - if (approveCode) { - currentRevision.rfaApproveCodeId = approveCode.id; - currentRevision.approvedDate = new Date(); - } - await queryRunner.manager.save(currentRevision); - } else if (dto.action === WorkflowAction.REJECT) { - const rejectCode = await this.rfaApproveRepo.findOne({ - where: { approveCode: '4X' }, - }); - if (rejectCode) { - currentRevision.rfaApproveCodeId = rejectCode.id; + } else if (result.nextStepSequence === null) { + // Workflow Ended (Completed or Rejected) + // Update RFA Status (Approved/Rejected Code) + if (dto.action !== 'REJECT') { + const approveCode = await this.rfaApproveRepo.findOne({ + where: { approveCode: dto.action === 'APPROVE' ? '1A' : '4X' }, + }); // Logic Map Code āļ­āļĒāđˆāļēāļ‡āļ‡āđˆāļēāļĒ + if (approveCode) { + currentRevision.rfaApproveCodeId = approveCode.id; + currentRevision.approvedDate = new Date(); + } + } else { + const rejectCode = await this.rfaApproveRepo.findOne({ + where: { approveCode: '4X' }, + }); + if (rejectCode) currentRevision.rfaApproveCodeId = rejectCode.id; } await queryRunner.manager.save(currentRevision); } await queryRunner.commitTransaction(); - return { message: 'Action processed successfully', result }; + return { message: 'Action processed', result }; } catch (err) { await queryRunner.rollbackTransaction(); - this.logger.error( - `Failed to process RFA action: ${(err as Error).message}`, - ); throw err; } finally { await queryRunner.release(); } } -} \ No newline at end of file +} diff --git a/backend/src/modules/workflow-engine/dto/workflow-transition.dto.ts b/backend/src/modules/workflow-engine/dto/workflow-transition.dto.ts new file mode 100644 index 0000000..51bffe9 --- /dev/null +++ b/backend/src/modules/workflow-engine/dto/workflow-transition.dto.ts @@ -0,0 +1,30 @@ +// File: src/modules/workflow-engine/dto/workflow-transition.dto.ts + +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { IsNotEmpty, IsObject, IsOptional, IsString } from 'class-validator'; + +export class WorkflowTransitionDto { + @ApiProperty({ + description: 'āļŠāļ·āđˆāļ­ Action āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢāļ—āļģ (āļ•āđ‰āļ­āļ‡āļ•āļĢāļ‡āļāļąāļšāļ—āļĩāđˆāļāļģāļŦāļ™āļ”āđƒāļ™ DSL)', + example: 'APPROVE', + }) + @IsString() + @IsNotEmpty() + action!: string; + + @ApiPropertyOptional({ + description: 'āļ„āļ§āļēāļĄāđ€āļŦāđ‡āļ™āļ›āļĢāļ°āļāļ­āļšāļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ', + example: 'āļ­āļ™āļļāļĄāļąāļ•āļīāļ„āļĢāļąāļš āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ•āđˆāļ­āđ„āļ”āđ‰āđ€āļĨāļĒ', + }) + @IsString() + @IsOptional() + comment?: string; + + @ApiPropertyOptional({ + description: 'āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢāđāļ™āļšāđ„āļ›āļāļąāļš Event āļŦāļĢāļ·āļ­āļšāļąāļ™āļ—āļķāļāđƒāļ™ Context', + example: { urgent: true, assign_to: 'user_123' }, + }) + @IsObject() + @IsOptional() + payload?: Record; +} diff --git a/backend/src/modules/workflow-engine/entities/workflow-definition.entity.ts b/backend/src/modules/workflow-engine/entities/workflow-definition.entity.ts index c4140b7..2e550b7 100644 --- a/backend/src/modules/workflow-engine/entities/workflow-definition.entity.ts +++ b/backend/src/modules/workflow-engine/entities/workflow-definition.entity.ts @@ -1,37 +1,58 @@ // File: src/modules/workflow-engine/entities/workflow-definition.entity.ts + import { - Entity, Column, - PrimaryGeneratedColumn, CreateDateColumn, - UpdateDateColumn, + Entity, Index, + PrimaryGeneratedColumn, + Unique, + UpdateDateColumn, } from 'typeorm'; +/** + * āđ€āļāđ‡āļšāđāļĄāđˆāđāļšāļš (Blueprint) āļ‚āļ­āļ‡ Workflow + * 1 Workflow Code (āđ€āļŠāđˆāļ™ RFA) āļŠāļēāļĄāļēāļĢāļ–āļĄāļĩāđ„āļ”āđ‰āļŦāļĨāļēāļĒ Version + */ @Entity('workflow_definitions') -@Index(['workflow_code', 'is_active', 'version']) +@Unique(['workflow_code', 'version']) // āļ›āđ‰āļ­āļ‡āļāļąāļ™ Version āļ‹āđ‰āļģāđƒāļ™ Workflow āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ +@Index(['workflow_code', 'is_active', 'version']) // āđ€āļžāļ·āđˆāļ­āļāļēāļĢ Query āļŦāļē Active Version āļĨāđˆāļēāļŠāļļāļ”āđ„āļ”āđ‰āđ€āļĢāđ‡āļ§ export class WorkflowDefinition { @PrimaryGeneratedColumn('uuid') - id!: string; // āđ€āļžāļīāđˆāļĄ ! + id!: string; - @Column({ length: 50, comment: 'āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ RFA, CORR' }) - workflow_code!: string; // āđ€āļžāļīāđˆāļĄ ! + @Column({ length: 50, comment: 'āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ RFA, CORR, LEAVE_REQ' }) + workflow_code!: string; - @Column({ type: 'int', default: 1, comment: 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version' }) - version!: number; // āđ€āļžāļīāđˆāļĄ ! + @Column({ + type: 'int', + default: 1, + comment: 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version (Running sequence)', + }) + version!: number; - @Column({ type: 'json', comment: 'āļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļš' }) - dsl!: any; // āđ€āļžāļīāđˆāļĄ ! + @Column({ type: 'text', nullable: true, comment: 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ' }) + description?: string; - @Column({ type: 'json', comment: 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§' }) - compiled!: any; // āđ€āļžāļīāđˆāļĄ ! + @Column({ + type: 'json', + comment: 'Raw DSL āļ—āļĩāđˆ User/Admin āđ€āļ‚āļĩāļĒāļ™ (āđ€āļāđ‡āļšāđ„āļ§āđ‰āđ€āļžāļ·āđˆāļ­āļ”āļđāļŦāļĢāļ·āļ­āđāļāđ‰āđ„āļ‚)', + }) + dsl!: any; // āļ„āļ§āļĢāļ•āļĢāļ‡āļāļąāļš RawWorkflowDSL interface - @Column({ default: true, comment: 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™' }) - is_active!: boolean; // āđ€āļžāļīāđˆāļĄ ! + @Column({ + type: 'json', + comment: + 'Compiled JSON Structure āļ—āļĩāđˆāļœāđˆāļēāļ™āļāļēāļĢ Validate āđāļĨāļ° Optimize āļŠāļģāļŦāļĢāļąāļš Runtime Engine āđāļĨāđ‰āļ§', + }) + compiled!: any; // āļ„āļ§āļĢāļ•āļĢāļ‡āļāļąāļš CompiledWorkflow interface - @CreateDateColumn() - created_at!: Date; // āđ€āļžāļīāđˆāļĄ ! + @Column({ default: true, comment: 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ (Soft Disable)' }) + is_active!: boolean; - @UpdateDateColumn() - updated_at!: Date; // āđ€āļžāļīāđˆāļĄ ! + @CreateDateColumn({ name: 'created_at' }) + created_at!: Date; + + @UpdateDateColumn({ name: 'updated_at' }) + updated_at!: Date; } diff --git a/backend/src/modules/workflow-engine/entities/workflow-history.entity.ts b/backend/src/modules/workflow-engine/entities/workflow-history.entity.ts index 91bde3b..19381ae 100644 --- a/backend/src/modules/workflow-engine/entities/workflow-history.entity.ts +++ b/backend/src/modules/workflow-engine/entities/workflow-history.entity.ts @@ -1,15 +1,23 @@ // File: src/modules/workflow-engine/entities/workflow-history.entity.ts + import { Column, CreateDateColumn, Entity, + Index, JoinColumn, ManyToOne, PrimaryGeneratedColumn, } from 'typeorm'; import { WorkflowInstance } from './workflow-instance.entity'; +/** + * āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Audit Trail) + * āļŠāļģāļ„āļąāļāļĄāļēāļāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļĒāđ‰āļ­āļ™āļŦāļĨāļąāļ‡ (Who did What, When) + */ @Entity('workflow_histories') +@Index(['instanceId']) // āļ„āđ‰āļ™āļŦāļēāļ›āļĢāļ°āļ§āļąāļ•āļīāļ‚āļ­āļ‡ Instance āļ™āļĩāđ‰ +@Index(['actionByUserId']) // āļ„āđ‰āļ™āļŦāļēāļ§āđˆāļē User āļ„āļ™āļ™āļĩāđ‰āļ—āļģāļ­āļ°āđ„āļĢāđ„āļ›āļšāđ‰āļēāļ‡ export class WorkflowHistory { @PrimaryGeneratedColumn('uuid') id!: string; @@ -21,23 +29,32 @@ export class WorkflowHistory { @Column({ name: 'instance_id' }) instanceId!: string; - @Column({ name: 'from_state', length: 50 }) + @Column({ name: 'from_state', length: 50, comment: 'āļŠāļ–āļēāļ™āļ°āļ•āđ‰āļ™āļ—āļēāļ‡' }) fromState!: string; - @Column({ name: 'to_state', length: 50 }) + @Column({ name: 'to_state', length: 50, comment: 'āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡' }) toState!: string; - @Column({ length: 50 }) + @Column({ length: 50, comment: 'Action āļ—āļĩāđˆ User āļāļ” (āđ€āļŠāđˆāļ™ APPROVE, REJECT)' }) action!: string; - @Column({ name: 'action_by_user_id', nullable: true }) - actionByUserId?: number; // User ID āļ‚āļ­āļ‡āļœāļđāđ‰āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ + @Column({ + name: 'action_by_user_id', + nullable: true, + comment: 'User ID āļœāļđāđ‰āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (Nullable āļāļĢāļ“āļĩ System Auto)', + }) + actionByUserId?: number; - @Column({ type: 'text', nullable: true }) + @Column({ type: 'text', nullable: true, comment: 'āļ„āļ§āļēāļĄāđ€āļŦāđ‡āļ™āļ›āļĢāļ°āļāļ­āļšāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī' }) comment?: string; - @Column({ type: 'json', nullable: true }) - metadata?: Record; // āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ āđ€āļŠāđˆāļ™ Snapshot āļ‚āļ­āļ‡ Context āļ“ āļ•āļ­āļ™āļ™āļąāđ‰āļ™ + // Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ āļ“ āđ€āļ§āļĨāļēāļ—āļĩāđˆāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° āđ€āļžāļ·āđˆāļ­āđ€āļ›āđ‡āļ™āļŦāļĨāļąāļāļāļēāļ™āļŦāļēāļ Context āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđƒāļ™āļ­āļ™āļēāļ„āļ• + @Column({ + type: 'json', + nullable: true, + comment: 'Snapshot of Context or Metadata', + }) + metadata?: Record; @CreateDateColumn({ name: 'created_at' }) createdAt!: Date; diff --git a/backend/src/modules/workflow-engine/entities/workflow-instance.entity.ts b/backend/src/modules/workflow-engine/entities/workflow-instance.entity.ts index cc36e5d..33764c3 100644 --- a/backend/src/modules/workflow-engine/entities/workflow-instance.entity.ts +++ b/backend/src/modules/workflow-engine/entities/workflow-instance.entity.ts @@ -1,4 +1,5 @@ // File: src/modules/workflow-engine/entities/workflow-instance.entity.ts + import { Column, CreateDateColumn, @@ -12,20 +13,23 @@ import { import { WorkflowDefinition } from './workflow-definition.entity'; export enum WorkflowStatus { - ACTIVE = 'ACTIVE', - COMPLETED = 'COMPLETED', - CANCELLED = 'CANCELLED', - TERMINATED = 'TERMINATED', + ACTIVE = 'ACTIVE', // āļāļģāļĨāļąāļ‡āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ + COMPLETED = 'COMPLETED', // āļˆāļšāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢ (āļ–āļķāļ‡ Terminal State) + CANCELLED = 'CANCELLED', // āļ–āļđāļāļĒāļāđ€āļĨāļīāļāļāļĨāļēāļ‡āļ„āļąāļ™ + TERMINATED = 'TERMINATED', // āļ–āļđāļāļšāļąāļ‡āļ„āļąāļšāļˆāļšāđ‚āļ”āļĒāļĢāļ°āļšāļš āļŦāļĢāļ·āļ­ Error } +/** + * āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āđƒāļš (Runtime State) + */ @Entity('workflow_instances') -@Index(['entityType', 'entityId']) // Index āļŠāļģāļŦāļĢāļąāļšāļ„āđ‰āļ™āļŦāļēāļ•āļēāļĄāđ€āļ­āļāļŠāļēāļĢ -@Index(['currentState']) // Index āļŠāļģāļŦāļĢāļąāļš Filter āļ•āļēāļĄāļŠāļ–āļēāļ™āļ° +@Index(['entityType', 'entityId']) // āđ€āļžāļ·āđˆāļ­āļ„āđ‰āļ™āļŦāļēāļ§āđˆāļēāđ€āļ­āļāļŠāļēāļĢāļ™āļĩāđ‰ (āđ€āļŠāđˆāļ™ RFA-001) āļ­āļĒāļđāđˆāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđ„āļŦāļ™ +@Index(['currentState']) // āđ€āļžāļ·āđˆāļ­ Dashboard: "āļĄāļĩāļ‡āļēāļ™āļ„āđ‰āļēāļ‡āļ—āļĩāđˆāļ‚āļąāđ‰āļ™āļ•āļ­āļ™āđ„āļŦāļ™āļšāđ‰āļēāļ‡" export class WorkflowInstance { @PrimaryGeneratedColumn('uuid') id!: string; - // āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļāļąāļš Definition āļ—āļĩāđˆāđƒāļŠāđ‰āļ•āļ­āļ™āļŠāļĢāđ‰āļēāļ‡ Instance āļ™āļĩāđ‰ + // āļœāļđāļāļāļąāļš Definition āđ€āļžāļ·āđˆāļ­āļĢāļđāđ‰āļ§āđˆāļēāđƒāļŠāđ‰āļāļŽāļŠāļļāļ”āđ„āļŦāļ™ (Version āđ„āļŦāļ™) @ManyToOne(() => WorkflowDefinition) @JoinColumn({ name: 'definition_id' }) definition!: WorkflowDefinition; @@ -33,25 +37,39 @@ export class WorkflowInstance { @Column({ name: 'definition_id' }) definitionId!: string; - // Polymorphic Relation: āđ€āļŠāļ·āđˆāļ­āļĄāļāļąāļšāđ€āļ­āļāļŠāļēāļĢāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ›āļĢāļ°āđ€āļ āļ— (RFA, CORR, etc.) - @Column({ name: 'entity_type', length: 50 }) + // Polymorphic Relation: āđ€āļŠāļ·āđˆāļ­āļĄāļāļąāļšāđ€āļ­āļāļŠāļēāļĢāđ„āļ”āđ‰āļŦāļĨāļēāļĒāļ›āļĢāļ°āđ€āļ āļ— (RFA, CORR, etc.) āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Foreign Key āļˆāļĢāļīāļ‡ + @Column({ + name: 'entity_type', + length: 50, + comment: 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ āđ€āļŠāđˆāļ™ rfa, correspondence', + }) entityType!: string; - @Column({ name: 'entity_id', length: 50 }) - entityId!: string; // āļĢāļ­āļ‡āļĢāļąāļšāļ—āļąāđ‰āļ‡ ID āđāļšāļš Int āđāļĨāļ° UUID (āđ€āļāđ‡āļšāđ€āļ›āđ‡āļ™ String) + @Column({ + name: 'entity_id', + length: 50, + comment: 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (String/UUID)', + }) + entityId!: string; - @Column({ name: 'current_state', length: 50 }) + @Column({ + name: 'current_state', + length: 50, + comment: 'āļŠāļ·āđˆāļ­ State āļ›āļąāļˆāļˆāļļāļšāļąāļ™ āđ€āļŠāđˆāļ™ DRAFT, IN_REVIEW', + }) currentState!: string; @Column({ type: 'enum', enum: WorkflowStatus, default: WorkflowStatus.ACTIVE, + comment: 'āļŠāļ–āļēāļ™āļ°āļ āļēāļžāļĢāļ§āļĄāļ‚āļ­āļ‡ Instance', }) status!: WorkflowStatus; - // Context āđ€āļ‰āļžāļēāļ°āļ‚āļ­āļ‡ Instance āļ™āļĩāđ‰ (āđ€āļŠāđˆāļ™ āļ•āļąāļ§āđāļ›āļĢāļ—āļĩāđˆāļŠāđˆāļ‡āļ•āđˆāļ­āļĢāļ°āļŦāļ§āđˆāļēāļ‡ State) - @Column({ type: 'json', nullable: true }) + // Context: āđ€āļāđ‡āļšāļ•āļąāļ§āđāļ›āļĢāļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆāđƒāļ™ Workflow + // āđ€āļŠāđˆāļ™ { "amount": 500000, "requester_role": "ENGINEER", "approver_ids": [1, 2] } + @Column({ type: 'json', nullable: true, comment: 'Runtime Context Data' }) context?: Record; @CreateDateColumn({ name: 'created_at' }) diff --git a/backend/src/modules/workflow-engine/workflow-dsl.service.ts b/backend/src/modules/workflow-engine/workflow-dsl.service.ts index c22d4a5..4ed91bb 100644 --- a/backend/src/modules/workflow-engine/workflow-dsl.service.ts +++ b/backend/src/modules/workflow-engine/workflow-dsl.service.ts @@ -1,181 +1,255 @@ // File: src/modules/workflow-engine/workflow-dsl.service.ts -import { BadRequestException, Injectable } from '@nestjs/common'; +import { BadRequestException, Injectable, Logger } from '@nestjs/common'; -export interface WorkflowState { +// ========================================== +// 1. Interfaces for RAW DSL (Input from User) +// ========================================== +export interface RawWorkflowDSL { + workflow: string; + version?: number; + description?: string; + states: RawState[]; +} + +export interface RawState { + name: string; initial?: boolean; terminal?: boolean; - transitions?: Record; + on?: Record; } -export interface TransitionRule { +export interface RawTransition { to: string; - requirements?: RequirementRule[]; - events?: EventRule[]; + require?: { + role?: string | string[]; + user?: string; + }; + condition?: string; // JavaScript Expression string + events?: RawEvent[]; } -export interface RequirementRule { - role?: string; - user?: string; - condition?: string; -} - -export interface EventRule { - type: 'notify' | 'webhook' | 'update_status'; +export interface RawEvent { + type: 'notify' | 'webhook' | 'assign' | 'auto_action'; target?: string; + template?: string; payload?: any; } +// ========================================== +// 2. Interfaces for COMPILED Schema (Optimized for Runtime) +// ========================================== export interface CompiledWorkflow { workflow: string; - version: string | number; - states: Record; + version: number; + initialState: string; // Optimize: āđ€āļāđ‡āļšāļŠāļ·āđˆāļ­ Initial State āđ„āļ§āđ‰āđ€āļĨāļĒ āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ loop āļŦāļē + states: Record; +} + +export interface CompiledState { + terminal: boolean; + transitions: Record; +} + +export interface CompiledTransition { + to: string; + requirements: { + roles: string[]; + userId?: string; + }; + condition?: string; + events: RawEvent[]; } @Injectable() export class WorkflowDslService { - /** - * āļ„āļ­āļĄāđ„āļžāļĨāđŒ DSL Input āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™ Standard Execution Tree - */ - compile(dsl: any): CompiledWorkflow { - if (!dsl || typeof dsl !== 'object') { - throw new BadRequestException('DSL must be a valid JSON object.'); - } + private readonly logger = new Logger(WorkflowDslService.name); - if (!dsl.states || !Array.isArray(dsl.states)) { - throw new BadRequestException( - 'DSL syntax error: "states" array is required.', - ); - } + /** + * [Compile Time] + * āđāļ›āļĨāļ‡ Raw DSL āđ€āļ›āđ‡āļ™ Compiled Structure āļžāļĢāđ‰āļ­āļĄ Validation + */ + compile(dsl: RawWorkflowDSL): CompiledWorkflow { + this.validateSchemaStructure(dsl); const compiled: CompiledWorkflow = { - workflow: dsl.workflow || 'UNKNOWN', + workflow: dsl.workflow, version: dsl.version || 1, + initialState: '', states: {}, }; - const stateMap = new Set(); + const definedStates = new Set(dsl.states.map((s) => s.name)); + let initialFound = false; + // 1. Process States for (const rawState of dsl.states) { - if (!rawState.name) { - throw new BadRequestException( - 'DSL syntax error: All states must have a "name".', - ); + if (rawState.initial) { + if (initialFound) { + throw new BadRequestException( + `DSL Error: Multiple initial states found (at "${rawState.name}").`, + ); + } + compiled.initialState = rawState.name; + initialFound = true; } - stateMap.add(rawState.name); - - const normalizedState: WorkflowState = { - initial: !!rawState.initial, + const compiledState: CompiledState = { terminal: !!rawState.terminal, transitions: {}, }; + // 2. Process Transitions if (rawState.on) { for (const [action, rule] of Object.entries(rawState.on)) { - const rawRule = rule as any; - normalizedState.transitions![action] = { - to: rawRule.to, - requirements: rawRule.require || [], - events: rawRule.events || [], + // Validation: Target state must exist + if (!definedStates.has(rule.to)) { + throw new BadRequestException( + `DSL Error: State "${rawState.name}" transitions via "${action}" to unknown state "${rule.to}".`, + ); + } + + compiledState.transitions[action] = { + to: rule.to, + requirements: { + roles: rule.require?.role + ? Array.isArray(rule.require.role) + ? rule.require.role + : [rule.require.role] + : [], + userId: rule.require?.user, + }, + condition: rule.condition, + events: rule.events || [], }; } + } else if (!rawState.terminal) { + this.logger.warn( + `State "${rawState.name}" is not terminal but has no transitions.`, + ); } - compiled.states[rawState.name] = normalizedState; + compiled.states[rawState.name] = compiledState; } - this.validateIntegrity(compiled, stateMap); + if (!initialFound) { + throw new BadRequestException('DSL Error: No initial state defined.'); + } return compiled; } - private validateIntegrity(compiled: CompiledWorkflow, stateMap: Set) { - let hasInitial = false; - - for (const [stateName, state] of Object.entries(compiled.states)) { - if (state.initial) { - if (hasInitial) - throw new BadRequestException( - `DSL Error: Multiple initial states found.`, - ); - hasInitial = true; - } - - if (state.transitions) { - for (const [action, rule] of Object.entries(state.transitions)) { - if (!stateMap.has(rule.to)) { - throw new BadRequestException( - `DSL Error: State "${stateName}" transitions via "${action}" to unknown state "${rule.to}".`, - ); - } - } - } - } - - if (!hasInitial) { - throw new BadRequestException('DSL Error: No initial state defined.'); - } - } - + /** + * [Runtime] + * āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨ Action āđāļĨāļ°āļ„āļ·āļ™āļ„āđˆāļē State āļ–āļąāļ”āđ„āļ› + */ evaluate( compiled: CompiledWorkflow, currentState: string, action: string, - context: any = {}, // Default empty object - ): { nextState: string; events: EventRule[] } { + context: any = {}, + ): { nextState: string; events: RawEvent[] } { const stateConfig = compiled.states[currentState]; + // 1. Validate State Existence if (!stateConfig) { throw new BadRequestException( - `Runtime Error: Current state "${currentState}" not found in definition.`, + `Runtime Error: Current state "${currentState}" is invalid.`, ); } + // 2. Check if terminal if (stateConfig.terminal) { throw new BadRequestException( `Runtime Error: Cannot transition from terminal state "${currentState}".`, ); } - const transition = stateConfig.transitions?.[action]; - + // 3. Find Transition + const transition = stateConfig.transitions[action]; if (!transition) { + const allowed = Object.keys(stateConfig.transitions).join(', '); throw new BadRequestException( - `Runtime Error: Action "${action}" is not allowed from state "${currentState}". Available actions: ${Object.keys(stateConfig.transitions || {}).join(', ')}`, + `Invalid Action: "${action}" is not allowed from "${currentState}". Allowed: [${allowed}]`, ); } - if (transition.requirements && transition.requirements.length > 0) { - this.checkRequirements(transition.requirements, context); + // 4. Validate Requirements (RBAC) + this.checkRequirements(transition.requirements, context); + + // 5. Evaluate Condition (Dynamic Logic) + if (transition.condition) { + const isMet = this.evaluateCondition(transition.condition, context); + if (!isMet) { + throw new BadRequestException( + 'Condition Failed: The criteria for this transition are not met.', + ); + } } return { nextState: transition.to, - events: transition.events || [], + events: transition.events, }; } - private checkRequirements(requirements: RequirementRule[], context: any) { - const safeContext = context || {}; - const userRoles = safeContext.roles || []; - const userId = safeContext.userId; + // -------------------------------------------------------- + // Private Helpers + // -------------------------------------------------------- - const isAllowed = requirements.some((req) => { - if (req.role) { - return userRoles.includes(req.role); - } - if (req.user) { - return userId === req.user; - } - // Future: Add Condition Logic Evaluation here - return false; - }); - - if (!isAllowed) { + private validateSchemaStructure(dsl: any) { + if (!dsl || typeof dsl !== 'object') { + throw new BadRequestException('DSL must be a JSON object.'); + } + if (!dsl.workflow || !dsl.states || !Array.isArray(dsl.states)) { throw new BadRequestException( - 'Access Denied: You do not meet the requirements for this action.', + 'DSL Error: Missing required fields (workflow, states).', ); } } + + private checkRequirements( + req: CompiledTransition['requirements'], + context: any, + ) { + const userRoles: string[] = context.roles || []; + const userId: string | number = context.userId; + + // Check Roles (OR logic inside array) + if (req.roles.length > 0) { + const hasRole = req.roles.some((r) => userRoles.includes(r)); + if (!hasRole) { + throw new BadRequestException( + `Access Denied: Required roles [${req.roles.join(', ')}]`, + ); + } + } + + // Check Specific User + if (req.userId && String(req.userId) !== String(userId)) { + throw new BadRequestException('Access Denied: User mismatch.'); + } + } + + /** + * Evaluate simple JS expression securely + * NOTE: In production, use a safe parser like 'json-logic-js' or vm2 + * For this phase, we use a simple Function constructor with restricted scope. + */ + private evaluateCondition(expression: string, context: any): boolean { + try { + // Simple guard against malicious code (basic) + if (expression.includes('process') || expression.includes('require')) { + throw new Error('Unsafe expression detected'); + } + + // Create a function that returns the expression result + // "context" is available inside the expression + const func = new Function('context', `return ${expression};`); + return !!func(context); + } catch (error: any) { + this.logger.error(`Condition Error: "${expression}" -> ${error.message}`); + return false; // Fail safe + } + } } diff --git a/backend/src/modules/workflow-engine/workflow-engine.controller.ts b/backend/src/modules/workflow-engine/workflow-engine.controller.ts index 68ac888..e77917e 100644 --- a/backend/src/modules/workflow-engine/workflow-engine.controller.ts +++ b/backend/src/modules/workflow-engine/workflow-engine.controller.ts @@ -5,58 +5,103 @@ import { Controller, Get, Param, - ParseUUIDPipe, Patch, Post, - Query, + Request, UseGuards, } from '@nestjs/common'; -import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; -import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto'; -import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto'; -import { GetAvailableActionsDto } from './dto/get-available-actions.dto'; -import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto'; +import { + ApiBearerAuth, + ApiOperation, + ApiParam, + ApiResponse, + ApiTags, +} from '@nestjs/swagger'; + +// Services import { WorkflowEngineService } from './workflow-engine.service'; -@ApiTags('Workflow Engine (DSL)') +// DTOs +import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto'; +import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto'; +import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto'; +import { WorkflowTransitionDto } from './dto/workflow-transition.dto'; + +// Guards & Decorators (āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ•āļēāļĄāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ src/common āđƒāļ™āđāļœāļ™āļ‡āļēāļ™) +import { RequirePermission } from '../../common/decorators/require-permission.decorator'; +import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard'; +import { RbacGuard } from '../../common/guards/rbac.guard'; + +@ApiTags('Workflow Engine') +@ApiBearerAuth() // āļĢāļ°āļšāļļāļ§āđˆāļēāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰ Token āđƒāļ™ Swagger @Controller('workflow-engine') -@UseGuards(JwtAuthGuard) +@UseGuards(JwtAuthGuard, RbacGuard) // āļšāļąāļ‡āļ„āļąāļš Login āđāļĨāļ°āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļīāļ—āļ˜āļīāđŒāļ—āļļāļ Request export class WorkflowEngineController { constructor(private readonly workflowService: WorkflowEngineService) {} + // ================================================================= + // Definition Management (Admin / Developer) + // ================================================================= + @Post('definitions') - @ApiOperation({ summary: 'Create or Update Workflow Definition (DSL)' }) - @ApiResponse({ status: 201, description: 'Workflow compiled and saved.' }) + @ApiOperation({ summary: 'āļŠāļĢāđ‰āļēāļ‡ Workflow Definition āđƒāļŦāļĄāđˆ (Auto Versioning)' }) + @ApiResponse({ status: 201, description: 'Created successfully' }) + // āđƒāļŠāđ‰ Permission 'system.manage_all' (Admin) āļŦāļĢāļ·āļ­āļŠāļĢāđ‰āļēāļ‡ permission āđƒāļŦāļĄāđˆ 'workflow.manage' āđƒāļ™āļ­āļ™āļēāļ„āļ• + @RequirePermission('system.manage_all') async createDefinition(@Body() dto: CreateWorkflowDefinitionDto) { return this.workflowService.createDefinition(dto); } - @Post('evaluate') - @ApiOperation({ - summary: 'Evaluate transition (Run logic without saving state)', - }) - async evaluate(@Body() dto: EvaluateWorkflowDto) { - return this.workflowService.evaluate(dto); - } - - @Get('actions') - @ApiOperation({ summary: 'Get available actions for current state' }) - async getAvailableActions(@Query() query: GetAvailableActionsDto) { - return this.workflowService.getAvailableActions( - query.workflow_code, - query.current_state, - ); - } - @Patch('definitions/:id') - @ApiOperation({ - summary: 'Update workflow status or details (DSL Re-compile)', - }) + @ApiOperation({ summary: 'āđāļāđ‰āđ„āļ‚ Workflow Definition (Re-compile DSL)' }) + @RequirePermission('system.manage_all') async updateDefinition( - @Param('id', ParseUUIDPipe) id: string, // āđ€āļžāļīāđˆāļĄ ParseUUIDPipe āđ€āļžāļ·āđˆāļ­ Validate ID + @Param('id') id: string, @Body() dto: UpdateWorkflowDefinitionDto, ) { return this.workflowService.update(id, dto); } + + @Post('evaluate') + @ApiOperation({ summary: 'āļ—āļ”āļŠāļ­āļš Logic Workflow (Dry Run) āđ„āļĄāđˆāļšāļąāļ™āļ—āļķāļāļ‚āđ‰āļ­āļĄāļđāļĨ' }) + @RequirePermission('system.manage_all') + async evaluate(@Body() dto: EvaluateWorkflowDto) { + return this.workflowService.evaluate(dto); + } + + // ================================================================= + // Runtime Engine (User Actions) + // ================================================================= + + @Post('instances/:id/transition') + @ApiOperation({ summary: 'āļŠāļąāđˆāļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ (User Action)' }) + @ApiParam({ name: 'id', description: 'Workflow Instance ID (UUID)' }) + // Permission āļˆāļ°āļ–āļđāļāļ•āļĢāļ§āļˆāļŠāļ­āļš Dynamic āļ āļēāļĒāđƒāļ™ Service āļ•āļēāļĄ State āļ‚āļ­āļ‡ Workflow āđāļ•āđˆāļ‚āļąāđ‰āļ™āļ•āđ‰āļ™āļ•āđ‰āļ­āļ‡āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļ—āļģāļ‡āļēāļ™ Workflow + @RequirePermission('workflow.action_review') + async processTransition( + @Param('id') instanceId: string, + @Body() dto: WorkflowTransitionDto, + @Request() req: any, + ) { + // āļ”āļķāļ‡ User ID āļˆāļēāļ Token (req.user āļĄāļēāļˆāļēāļ JwtStrategy) + const userId = req.user?.userId; + + return this.workflowService.processTransition( + instanceId, + dto.action, + userId, + dto.comment, + dto.payload, + ); + } + + @Get('instances/:id/actions') + @ApiOperation({ + summary: 'āļ”āļķāļ‡āļĢāļēāļĒāļāļēāļĢāļ›āļļāđˆāļĄ Action āļ—āļĩāđˆāļŠāļēāļĄāļēāļĢāļ–āļāļ”āđ„āļ”āđ‰ āļ“ āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™', + }) + @RequirePermission('document.view') // āļœāļđāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļ”āļđāđ€āļ­āļāļŠāļēāļĢ āļ„āļ§āļĢāļ”āļđ Action āđ„āļ”āđ‰ + async getAvailableActions(@Param('id') instanceId: string) { + // Note: Logic āļāļēāļĢāļ”āļķāļ‡ Action āļ•āļēāļĄ Instance ID āļˆāļ°āļ–āļđāļ Implement āđƒāļ™ Task āļ–āļąāļ”āđ„āļ› + return { message: 'Pending implementation in Service layer' }; + } } diff --git a/backend/src/modules/workflow-engine/workflow-engine.module.ts b/backend/src/modules/workflow-engine/workflow-engine.module.ts index 2c4cbb7..f766b67 100644 --- a/backend/src/modules/workflow-engine/workflow-engine.module.ts +++ b/backend/src/modules/workflow-engine/workflow-engine.module.ts @@ -2,23 +2,31 @@ import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { WorkflowDefinition } from './entities/workflow-definition.entity'; -import { WorkflowHistory } from './entities/workflow-history.entity'; // [New] -import { WorkflowInstance } from './entities/workflow-instance.entity'; // [New] -import { WorkflowDslService } from './workflow-dsl.service'; -import { WorkflowEngineController } from './workflow-engine.controller'; -import { WorkflowEngineService } from './workflow-engine.service'; +// Entities +import { WorkflowDefinition } from './entities/workflow-definition.entity'; +import { WorkflowHistory } from './entities/workflow-history.entity'; +import { WorkflowInstance } from './entities/workflow-instance.entity'; + +// Services +import { WorkflowDslService } from './workflow-dsl.service'; +import { WorkflowEngineService } from './workflow-engine.service'; +import { WorkflowEventService } from './workflow-event.service'; // [NEW] + +// Controllers +import { UserModule } from '../user/user.module'; +import { WorkflowEngineController } from './workflow-engine.controller'; @Module({ imports: [ TypeOrmModule.forFeature([ WorkflowDefinition, - WorkflowInstance, // [New] - WorkflowHistory, // [New] + WorkflowInstance, + WorkflowHistory, ]), + UserModule, ], controllers: [WorkflowEngineController], - providers: [WorkflowEngineService, WorkflowDslService], - exports: [WorkflowEngineService], + providers: [WorkflowEngineService, WorkflowDslService, WorkflowEventService], + exports: [WorkflowEngineService], // Export Service āđƒāļŦāđ‰ Module āļ­āļ·āđˆāļ™ (Correspondence, RFA) āđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰ }) export class WorkflowEngineModule {} diff --git a/backend/src/modules/workflow-engine/workflow-engine.service.ts b/backend/src/modules/workflow-engine/workflow-engine.service.ts index 5ab8326..3df5b86 100644 --- a/backend/src/modules/workflow-engine/workflow-engine.service.ts +++ b/backend/src/modules/workflow-engine/workflow-engine.service.ts @@ -22,6 +22,7 @@ import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dt import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto'; import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto'; import { CompiledWorkflow, WorkflowDslService } from './workflow-dsl.service'; +import { WorkflowEventService } from './workflow-event.service'; // [NEW] Import Event Service // Legacy Interface (Backward Compatibility) export enum WorkflowAction { @@ -49,6 +50,7 @@ export class WorkflowEngineService { @InjectRepository(WorkflowHistory) private readonly historyRepo: Repository, private readonly dslService: WorkflowDslService, + private readonly eventService: WorkflowEventService, // [NEW] Inject Service private readonly dataSource: DataSource, // āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļš Transaction ) {} @@ -166,9 +168,9 @@ export class WorkflowEngineService { // 2. āļŦāļē Initial State āļˆāļēāļ Compiled Structure const compiled: CompiledWorkflow = definition.compiled; - const initialState = Object.keys(compiled.states).find( - (key) => compiled.states[key].initial, - ); + // [FIX] āđƒāļŠāđ‰ initialState āļˆāļēāļ Root Property āđ‚āļ”āļĒāļ•āļĢāļ‡ (āļ•āļēāļĄāļ—āļĩāđˆ Optimize āđƒāļ™ DSL Service) + // āđ€āļžāļĢāļēāļ° CompiledState āđƒāļ™ states map āđ„āļĄāđˆāļĄāļĩ property 'initial' āđāļĨāđ‰āļ§ + const initialState = compiled.initialState; if (!initialState) { throw new BadRequestException( @@ -193,6 +195,25 @@ export class WorkflowEngineService { return savedInstance; } + /** + * āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ Workflow Instance āļ•āļēāļĄ ID + * āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļ–āļēāļ™āļ°āļŦāļĢāļ·āļ­āļ‹āļīāļ‡āļ„āđŒāļ‚āđ‰āļ­āļĄāļđāļĨāļāļĨāļąāļšāđ„āļ›āļĒāļąāļ‡ Module āļŦāļĨāļąāļ + */ + async getInstanceById(instanceId: string): Promise { + const instance = await this.instanceRepo.findOne({ + where: { id: instanceId }, + relations: ['definition'], + }); + + if (!instance) { + throw new NotFoundException( + `Workflow Instance "${instanceId}" not found`, + ); + } + + return instance; + } + /** * āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Transition) āļ‚āļ­āļ‡ Instance āļˆāļĢāļīāļ‡āđāļšāļš Transactional */ @@ -207,6 +228,9 @@ export class WorkflowEngineService { await queryRunner.connect(); await queryRunner.startTransaction(); + let eventsToDispatch: any[] = []; + let updatedContext: any = {}; + try { // 1. Lock Instance āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ Race Condition (Pessimistic Write Lock) const instance = await queryRunner.manager.findOne(WorkflowInstance, { @@ -268,25 +292,29 @@ export class WorkflowEngineService { }); await queryRunner.manager.save(history); - // 5. Trigger Events (Integration Point) - // āđƒāļ™āļ­āļ™āļēāļ„āļ•āļŠāļēāļĄāļēāļĢāļ– Inject NotificationService āļĄāļēāđ€āļĢāļĩāļĒāļāļ•āļĢāļ‡āļ™āļĩāđ‰āđ„āļ”āđ‰ - if (evaluation.events && evaluation.events.length > 0) { - this.logger.log( - `Triggering ${evaluation.events.length} events for instance ${instanceId}`, - ); - // await this.eventHandler.handle(evaluation.events); - } - await queryRunner.commitTransaction(); + // [NEW] āđ€āļāđ‡āļšāļ„āđˆāļēāđ„āļ§āđ‰ Dispatch āļŦāļĨāļąāļ‡ Commit + eventsToDispatch = evaluation.events; + updatedContext = context; + this.logger.log( `Transition: ${instanceId} [${fromState}] --${action}--> [${toState}] by User:${userId}`, ); + // [NEW] Dispatch Events (Async) āļœāđˆāļēāļ™ WorkflowEventService + if (eventsToDispatch && eventsToDispatch.length > 0) { + this.eventService.dispatchEvents( + instance.id, + eventsToDispatch, + updatedContext, + ); + } + return { success: true, nextState: toState, - events: evaluation.events, + events: eventsToDispatch, isCompleted: instance.status === WorkflowStatus.COMPLETED, }; } catch (err) { diff --git a/backend/src/modules/workflow-engine/workflow-event.service.ts b/backend/src/modules/workflow-engine/workflow-event.service.ts new file mode 100644 index 0000000..81c4744 --- /dev/null +++ b/backend/src/modules/workflow-engine/workflow-event.service.ts @@ -0,0 +1,96 @@ +// File: src/modules/workflow-engine/workflow-event.service.ts + +import { Injectable, Logger } from '@nestjs/common'; +import { RawEvent } from './workflow-dsl.service'; + +// Interface āļŠāļģāļŦāļĢāļąāļš External Services āļ—āļĩāđˆāļˆāļ°āļĄāļēāļĢāļąāļš Event āļ•āđˆāļ­ +// (āđƒāļ™āļ­āļ™āļēāļ„āļ•āļ„āļ§āļĢāđƒāļŠāđ‰ NestJS Event Emitter āđ€āļžāļ·āđˆāļ­ Decouple āļ­āļĒāđˆāļēāļ‡āļŠāļĄāļšāļđāļĢāļ“āđŒ) +export interface WorkflowEventHandler { + handleNotification( + target: string, + template: string, + payload: any, + ): Promise; + handleWebhook(url: string, payload: any): Promise; + handleAutoAction(instanceId: string, action: string): Promise; +} + +@Injectable() +export class WorkflowEventService { + private readonly logger = new Logger(WorkflowEventService.name); + + // āļŠāļēāļĄāļēāļĢāļ– Inject NotificationService āļŦāļĢāļ·āļ­ HttpService āđ€āļ‚āđ‰āļēāļĄāļēāđ„āļ”āđ‰āļ•āļĢāļ‡āļ™āļĩāđ‰ + // constructor(private readonly notificationService: NotificationService) {} + + /** + * āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļĢāļēāļĒāļāļēāļĢ Events āļ—āļĩāđˆāđ€āļāļīāļ”āļˆāļēāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° + */ + async dispatchEvents( + instanceId: string, + events: RawEvent[], + context: Record, + ) { + if (!events || events.length === 0) return; + + this.logger.log( + `Dispatching ${events.length} events for Instance ${instanceId}`, + ); + + // āļ—āļģāđāļšāļš Async āđ„āļĄāđˆāļĢāļ­āļœāļĨ (Fire-and-forget) āđ€āļžāļ·āđˆāļ­āđ„āļĄāđˆāđƒāļŦāđ‰āļāļĢāļ°āļ—āļš Response Time āļ‚āļ­āļ‡ User + Promise.allSettled( + events.map((event) => + this.processSingleEvent(instanceId, event, context), + ), + ).then((results) => { + // Log errors if any + results.forEach((res, idx) => { + if (res.status === 'rejected') { + this.logger.error(`Failed to process event [${idx}]: ${res.reason}`); + } + }); + }); + } + + private async processSingleEvent( + instanceId: string, + event: RawEvent, + context: any, + ) { + try { + switch (event.type) { + case 'notify': + await this.handleNotify(event, context); + break; + case 'webhook': + await this.handleWebhook(event, context); + break; + case 'auto_action': + // Logic āļŠāļģāļŦāļĢāļąāļš Auto Transition (āđ€āļŠāđˆāļ™ āļ–āđ‰āļēāļœāđˆāļēāļ™āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ āđƒāļŦāđ‰āđ„āļ›āļ•āđˆāļ­āđ€āļĨāļĒ) + this.logger.log(`Auto Action triggered for ${instanceId}`); + break; + default: + this.logger.warn(`Unknown event type: ${event.type}`); + } + } catch (error) { + this.logger.error(`Error processing event ${event.type}: ${error}`); + throw error; + } + } + + // --- Handlers --- + + private async handleNotify(event: RawEvent, context: any) { + // Mockup: āđƒāļ™āļ‚āļ­āļ‡āļˆāļĢāļīāļ‡āļˆāļ°āđ€āļĢāļĩāļĒāļ NotificationService.send() + // const recipients = this.resolveRecipients(event.target, context); + this.logger.log( + `[EVENT] Notify target: "${event.target}" | Template: "${event.template}"`, + ); + } + + private async handleWebhook(event: RawEvent, context: any) { + // Mockup: āđ€āļĢāļĩāļĒāļ HttpService.post() + this.logger.log( + `[EVENT] Webhook to: "${event.target}" | Payload: ${JSON.stringify(event.payload)}`, + ); + } +} diff --git a/docs/6B.md b/docs/6B.md deleted file mode 100644 index c1400d2..0000000 --- a/docs/6B.md +++ /dev/null @@ -1,191 +0,0 @@ -# Special requirements for document-numbering -āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™āļˆāļĢāļīāļ‡āļ•āđ‰āļ­āļ‡āļāļēāļĢāļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™āļŠāļđāļ‡ āļŠāļģāļŦāļĢāļąāļš āļĢāļ°āļšāļš document-numbering āļ”āļąāļ‡āļ™āļĩāđ‰ - -## 1. āļ•āđ‰āļ­āļ‡āđƒāļŦāđ‰ admin āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āļĢāļđāļ›āđāļšāļšāđƒāļ™āļ–āļēāļĒāļŦāļĨāļąāļ‡āđ„āļ”āđ‰ -## 2. āļĄāļĩāļĢāļđāļ›āđāļšāļšāđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™āļ”āļąāļ‡āļ™āļĩāđ‰ -* 2.1 āļŠāļģāļŦāļĢāļąāļš correspondence āļ—āļąāđˆāļ§āđ„āļ› - * āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš [organizations.organization_code]-[organizations.organization_code]-[sequence]-[year] - * **_āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļ„āļ„āļ‡.-āļœāļĢāļĄ.2-0123-2568_** -* 2.2 āļŠāļģāļŦāļĢāļąāļš correspondence type = transmittal - * to OWNER āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš [organizations.organization_code]-[organizations.organization_code]-[codecorrespondence_sub_types.sub_type_number]-[seq]-[year] - * **_āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļ„āļ„āļ‡.-āļŠāļ„āļ‰.3-22-0123-2568_** - * to CONTRACTOR āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš [organizations.organization_code]-[organizations.organization_code]-[codecorrespondence_sub_types.sub_type_number]-[seq]-[year] - * **_āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: āļ„āļ„āļ‡.-āļŠāļ„āļ‰.3-22-0123-2568_** -* 2.3 āļŠāļģāļŦāļĢāļąāļš correspondence type = rfi - * āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš [contrcts.contract_code]-[correspondences_types.type_code]-[disciplines_code]-[seq]-[revision] - * **_āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: LCBP3-C2-RFI-TER-2345-A_** -* 2.4 āļŠāļģāļŦāļĢāļąāļš rfa āđƒāļŠāđ‰āđāļšāļšāđ€āļĨāļ‚āđāļĒāļāļāļąāļ™ - * āđƒāļŠāđ‰āļĢāļđāļ›āđāļšāļš [contrcts.contract_code]-[correspondences_types.type_code]-[disciplines_code]-[rfa_types.type_code]-[seq]-[revision] - * **_āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡: LCBP3-C1-RFA-TER-MAT-1234-A_** -āđāļĨāļ°āļ•āļĢāļ§āļˆāļŠāļ­āļš 01_lcbp3_v1_4_3.sql, 4_Data_Dictionary_V1_4_3.md -## āļ•āļēāļĢāļēāļ‡ correspondence_sub_types - -| contract_code | correspondence_types.type_code | sub_type_code | sub_type_name | sub_type_number | -| ------------- | ------------------------------ | ------------- | ------------------------------ | --------------- | -| LCBP3-C1 | RFA | MAT | Material Approval | 11 | -| LCBP3-C1 | RFA | SHP | Shop Drawing Submittal | 12 | -| LCBP3-C1 | RFA | DWG | Document Approval | 13 | -| LCBP3-C1 | RFA | MET | Engineering Document Submittal | 14 | -| LCBP3-C2 | RFA | MAT | Material Approval | 21 | -| LCBP3-C2 | RFA | SHP | Shop Drawing Submittal | 22 | -| LCBP3-C2 | RFA | DWG | Document Approval | 23 | -| LCBP3-C2 | RFA | MET | Engineering Document Submittal | 24 | -| LCBP3-C3 | RFA | MAT | Material Approval | 31 | -| LCBP3-C3 | RFA | SHP | Shop Drawing Submittal | 32 | -| LCBP3-C3 | RFA | DWG | Document Approval | 33 | -| LCBP3-C4 | RFA | MET | Engineering Document Submittal | 34 | -| LCBP3-C4 | RFA | MAT | Material Approval | 41 | -| LCBP3-C4 | RFA | SHP | Shop Drawing Submittal | 42 | -| LCBP3-C4 | RFA | DWG | Document Approval | 43 | -| LCBP3-C4 | RFA | MET | Engineering Document Submittal | 44 | - -## āļ•āļēāļĢāļēāļ‡ disciplines - -| contract_code | disciplines_code | code_name_th | code_name_en | -| ------------- | ---------------- | ----------------------------- | ------------------------------------------ | -| LCBP3-C1 | GEN | āļ‡āļēāļ™āļšāļĢāļīāļŦāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ | General Management | -| LCBP3-C1 | COD | āļŠāļąāļāļāļēāđāļĨāļ°āļ‚āđ‰āļ­āđ‚āļ•āđ‰āđāļĒāđ‰āļ‡ | Contracting | -| LCBP3-C1 | QSB | āļŠāļģāļĢāļ§āļˆāļ›āļĢāļīāļĄāļēāļ“āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļ‡āļšāļ›āļĢāļ°āļĄāļēāļ“ | Quantity Survey and Budget Control | -| LCBP3-C1 | PPG | āļšāļĢāļīāļŦāļēāļĢāđāļœāļ™āđāļĨāļ°āļ„āļ§āļēāļĄāļāđ‰āļēāļ§āļŦāļ™āđ‰āļē | Plan and Progress Management | -| LCBP3-C1 | PRC | āļ‡āļēāļ™āļˆāļąāļ”āļ‹āļ·āđ‰āļ­ | Procurement | -| LCBP3-C1 | SUB | āļœāļđāđ‰āļĢāļąāļšāđ€āļŦāļĄāļēāļŠāđˆāļ§āļ‡ | Subcontractor | -| LCBP3-C1 | ODC | āļŠāļģāļ™āļąāļāļ‡āļēāļ™-āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢ | Operation Docment Control | -| LCBP3-C1 | LAW | āļāļŽāļŦāļĄāļēāļĒ | Law | -| LCBP3-C1 | TRF | āļˆāļĢāļēāļˆāļĢ | Traffic | -| LCBP3-C1 | BIM | BIM | Building information modeling | -| LCBP3-C1 | SRV | āļ‡āļēāļ™āļŠāļģāļĢāļ§āļˆ | Survey | -| LCBP3-C1 | SFT | āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ | Safety | -| LCBP3-C1 | BST | āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ | Building Structure Work | -| LCBP3-C1 | TEM | āļ‡āļēāļ™āļŠāļąāđˆāļ§āļ„āļĢāļēāļ§ | Temporary Work | -| LCBP3-C1 | UTL | āļ‡āļēāļ™āļĢāļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„ | Utility | -| LCBP3-C1 | EPW | āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļē | Electrical Power Work | -| LCBP3-C1 | ECM | āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļēāļŠāļ·āđˆāļ­āļŠāļēāļĢ | Electrical Communication Work | -| LCBP3-C1 | ENV | āļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ | Environment | -| LCBP3-C1 | AQV | āļ„āļļāļ“āļ āļēāļžāļ­āļēāļāļēāļĻāđāļĨāļ°āļ„āļ§āļēāļĄāļŠāļąāđˆāļ™āļŠāļ°āđ€āļ—āļ·āļ­āļ™ | Air quality and vibration | -| LCBP3-C1 | WAB | āļ„āļļāļ“āļ āļēāļžāļ™āđ‰āļģāđāļĨāļ°āļŠāļĩāļ§āļ§āļīāļ—āļĒāļēāļ—āļēāļ‡āļ™āđ‰āļģ | Water quality and Aquatic biology | -| LCBP3-C1 | ONS | āļ§āļīāļĻāļ§āļāļĢāļĢāļĄāļŠāļēāļĒāļāļąāđˆāļ‡ | Onshore Engineer Work | -| LCBP3-C1 | PPR | āļĄāļ§āļĨāļŠāļ™āļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđāļĨāļ°āļāļēāļĢāļ›āļĢāļ°āļŠāļēāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ | Public Relations | -| LCBP3-C1 | OSW | āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ‡āļēāļ™āļ—āļēāļ‡āļ—āļ°āđ€āļĨ | Offshore Work | -| LCBP3-C1 | DRE | āļ‡āļēāļ™āļ‚āļļāļ”āđāļĨāļ°āļ–āļĄāļ—āļ°āđ€āļĨ | Dredging and Reclamation | -| LCBP3-C1 | REV | āļ‡āļēāļ™āļ„āļąāļ™āļŦāļīāļ™āļĨāđ‰āļ­āļĄāļžāļ·āđ‰āļ™āļ—āļĩāđˆāļ–āļĄāļ—āļ°āđ€āļĨ | Revetment | -| LCBP3-C1 | BRW | āļ‡āļēāļ™āđ€āļ‚āļ·āđˆāļ­āļ™āļāļąāļ™āļ„āļĨāļ·āđˆāļ™ | Breakwater | -| LCBP3-C1 | SOI | āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ„āļļāļ“āļ āļēāļžāļ”āļīāļ™ | Soil Improvement | -| LCBP3-C1 | BLC | āļ‡āļēāļ™āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļ„āļĨāļ­āļ‡āļšāļēāļ‡āļĨāļ°āļĄāļļāļ‡ | Bang Lamung Canal Bank Protection | -| LCBP3-C1 | FUP | āļ‡āļēāļ™āļ›āļĢāļ°āļ•āļđāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāđāļĨāļ°āļ—āđˆāļ­āļĨāļ­āļ” | Floodgate & Under Ground Piping Works | -| LCBP3-C1 | SWP | āļ‡āļēāļ™āļ­āļēāļ„āļēāļĢāļ„āļ§āļšāļ„āļļāļĄāļŠāļ–āļēāļ™āļĩāļŠāļđāļšāļ™āđ‰āļģāļ—āļ°āđ€āļĨ | Sea Water Pumping Station Control BuilDing | -| LCBP3-C1 | NAV | āļ‡āļēāļ™āļ•āļīāļ”āļ•āļąāđ‰āļ‡āđ€āļ„āļĢāļ·āđˆāļ­āļ‡āļŦāļĄāļēāļĒāļŠāđˆāļ§āļ‡āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āļ­ | Navigations Aids | -| LCBP3-C1 | GEO | āļ‡āļēāļ™āļ”āđ‰āļēāļ™āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„ | Geotechnical | -| LCBP3-C1 | CRW | āļ‡āļēāļ™āļ”āđ‰āļēāļ™āđ‚āļĒāļ˜āļē - Rock Works | Civil-Rock work | -| LCBP3-C1 | DVR | āļ—āļĩāļĄāļ™āļąāļāļ›āļĢāļ°āļ”āļēāļ™āđ‰āļģ | Dive Work | -| LCBP3-C1 | MTS | āļ‡āļēāļ™āļ—āļ”āļŠāļ­āļšāļ§āļąāļŠāļ”āļļāđāļĨāļ°āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„ | Materials and Geotechnical Testing | -| LCBP3-C1 | OTH | āļ­āļ·āđˆāļ™āđ† | Other | -| LCBP3-C2 | GEN | āļ‡āļēāļ™āļšāļĢāļīāļŦāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ | Project Management | -| LCBP3-C2 | COD | āļŠāļąāļāļāļēāđāļĨāļ°āļ‚āđ‰āļ­āđ‚āļ•āđ‰āđāļĒāđ‰āļ‡ | Contracts and arguments | -| LCBP3-C2 | QSB | āļŠāļģāļĢāļ§āļˆāļ›āļĢāļīāļĄāļēāļ“āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļ‡āļšāļ›āļĢāļ°āļĄāļēāļ“ | Survey the quantity and control the budget | -| LCBP3-C2 | PPM | āļšāļĢāļīāļŦāļēāļĢāđāļœāļ™āđāļĨāļ°āļ„āļ§āļēāļĄāļāđ‰āļēāļ§āļŦāļ™āđ‰āļē | Plan Management & Progress | -| LCBP3-C2 | ODC | āļŠāļģāļ™āļąāļāļ‡āļēāļ™-āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢ | Document Control Office | -| LCBP3-C2 | LAW | āļāļŽāļŦāļĄāļēāļĒ | Law | -| LCBP3-C2 | TRF | āļˆāļĢāļēāļˆāļĢ | Traffic | -| LCBP3-C2 | BIM | Building Information Modeling | Building Information Modeling | -| LCBP3-C2 | SRV | āļ‡āļēāļ™āļŠāļģāļĢāļ§āļˆ | Survey | -| LCBP3-C2 | SFT | āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ | Safety | -| LCBP3-C2 | BST | āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ­āļēāļ„āļēāļĢ | Building Structure | -| LCBP3-C2 | UTL | āļ‡āļēāļ™āļ°āļšāļšāļŠāļēāļ˜āļēāļĢāļ“āļđāļ›āđ‚āļ āļ„ | Public Utilities | -| LCBP3-C2 | EPW | āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļē | Electrical Systems | -| LCBP3-C2 | ECM | āļ‡āļēāļ™āļĢāļ°āļšāļšāđ„āļŸāļŸāđ‰āļēāļŠāļ·āđˆāļ­āļŠāļēāļĢ | Electrical Communication System | -| LCBP3-C2 | ENV | āļŠāļīāđˆāļ‡āđāļ§āļ”āļĨāđ‰āļ­āļĄ | Environment | -| LCBP3-C2 | AQV | āļ„āļļāļ“āļ āļēāļžāļ­āļēāļāļēāļĻāđāļĨāļ°āļ„āļ§āļēāļĄāļŠāļąāđˆāļ™āļŠāļ°āđ€āļ—āļ·āļ­āļ™ | Air Quality and Vibration | -| LCBP3-C2 | WAB | āļ„āļļāļ“āļ āļēāļžāļ™āđ‰āļģāđāļĨāļ°āļŠāļĩāļ§āļ§āļīāļ—āļĒāļēāļ—āļēāļ‡āļ™āđ‰āļģ | Water Quality and Aquatic Biology | -| LCBP3-C2 | ONS | āļ§āļīāļĻāļ§āļāļĢāļĢāļĄāļŠāļēāļĒāļāļąāđˆāļ‡ | Coastal Engineering | -| LCBP3-C2 | PPR | āļĄāļ§āļĨāļŠāļ™āļŠāļąāļĄāļžāļąāļ™āļ˜āđŒāđāļĨāļ°āļ›āļĢāļ°āļŠāļēāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ | Mass Relations and Public Relations | -| LCBP3-C2 | OFW | āļ‡āļēāļ™āļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡āļ—āļēāļ‡āļ—āļ°āđ€āļĨ | Marine Construction | -| LCBP3-C2 | EXR | āļ‡āļēāļ™āļ‚āļļāļ”āđāļĨāļ°āļ–āļĄāļ—āļ°āđ€āļĨ | Excavation and reclamation | -| LCBP3-C2 | GEO | āļ‡āļēāļ™āļ”āđ‰āļēāļ™āļ˜āļĢāļ“āļĩāđ€āļ—āļ„āļ™āļīāļ„ | Geotechnical work | -| LCBP3-C2 | CRW | āļ‡āļēāļ™āļ”āđ‰āļēāļ™āđ‚āļĒāļ˜āļē - Rock Works | Civil Works - Rock Works | -| LCBP3-C2 | DVW | āļ—āļĩāļĄāļ™āļąāļāļ›āļĢāļ°āļ”āļēāļ™āđ‰āļģ | Team of Divers | -| LCBP3-C2 | MTT | āļ‡āļēāļ™āļ—āļ”āļŠāļ­āļšāļ§āļąāļŠāļ”āļļ | Materials Testing | -| LCBP3-C2 | ARC | āļ‡āļēāļ™āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄ | Architecture | -| LCBP3-C2 | STR | āļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ | Structural work | -| LCBP3-C2 | SAN | āļ‡āļēāļ™āļĢāļ°āļšāļšāļŠāļļāļ‚āļēāļ āļīāļšāļēāļĨ | Sanitation System | -| LCBP3-C2 | DRA | āļ‡āļēāļ™āļĢāļ°āļšāļšāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģ | Drainage system work | -| LCBP3-C2 | TER | āļ‡āļēāļ™āļ—āđˆāļēāđ€āļ—āļĩāļĒāļšāđ€āļĢāļ·āļ­ | Terminal Work work | -| LCBP3-C2 | BUD | āļ‡āļēāļ™āļ­āļēāļ„āļēāļĢ | Building | -| LCBP3-C2 | ROW | āļ‡āļēāļ™āļ–āļ™āļ™āđāļĨāļ°āļŠāļ°āļžāļēāļ™ | Road and Bridge Work | -| LCBP3-C2 | MEC | āļ‡āļēāļ™āđ€āļ„āļĢāļīāļ­āļ‡āļāļĨ | Mechanical work | -| LCBP3-C2 | OTH | āļ­āļ·āđˆāļ™ āđ† | Others | - -## 📌āļ•āļēāļĢāļēāļ‡ rfa_types - -| contract_code | type_code | type_name_en | type_name_th | -| ------------- | --------- | ---------------------------------------------- | ------------------------------------------------ | -| LCBP3-C1 | ADW | As Built Drawing | āđāļšāļšāļĢāđˆāļēāļ‡āļŦāļĨāļąāļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ | -| LCBP3-C1 | BC | Box Culvert | āļ—āđˆāļ­āļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāļĢāļđāļ›āļāļĨāđˆāļ­āļ‡ | -| LCBP3-C1 | BM | Benchmark | āļŦāļĄāļļāļ”āļŦāļĨāļąāļāļāļēāļ™ | -| LCBP3-C1 | CER | Certificates | āđƒāļšāļĢāļąāļšāļĢāļ­āļ‡ | -| LCBP3-C1 | CN | Canal Drainage | āļĢāļ°āļšāļšāļĢāļ°āļšāļēāļĒāļ™āđ‰āļģāđƒāļ™āļ„āļĨāļ­āļ‡ | -| LCBP3-C1 | CON | Contract | āļŠāļąāļāļāļē | -| LCBP3-C1 | DDS | Design Data Submission | āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ­āļ­āļāđāļšāļš | -| LCBP3-C1 | DDW | Draft Drawing | āđāļšāļšāļĢāđˆāļēāļ‡ | -| LCBP3-C1 | DRW | Drawings (All Types) | āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ | -| LCBP3-C1 | DSN | Design/Calculation/Manual (All Stages) | āļ­āļ­āļāđāļšāļš / āļ„āļģāļ™āļ§āļ“ / āļ„āļđāđˆāļĄāļ·āļ­ | -| LCBP3-C1 | GEN | General | āļ—āļąāđˆāļ§āđ„āļ› | -| LCBP3-C1 | ICR | Incident Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāļīāļ”āļ­āļļāļšāļąāļ•āļīāđ€āļŦāļ•āļļāđāļĨāļ°āļāļēāļĢāļšāļēāļ”āđ€āļˆāđ‡āļš | -| LCBP3-C1 | INS | Insurances/Bond/Guarantee | āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ / āļžāļąāļ™āļ˜āļšāļąāļ•āļĢ / āļāļēāļĢāļ„āđ‰āļģāļ›āļĢāļ°āļāļąāļ™ | -| LCBP3-C1 | INR | Inspection/Audit/Surveillance Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāđ‰āļēāļĢāļ°āļ§āļąāļ‡ | -| LCBP3-C1 | ITP | Inspection and Test Plan | āđāļœāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ°āļ—āļ”āļŠāļ­āļš | -| LCBP3-C1 | JSA | Jobs Analysis | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ | -| LCBP3-C1 | MAN | Manual | āļ„āļđāđˆāļĄāļ·āļ­ | -| LCBP3-C1 | MAT | Materials/Equipment/Plant | āļ§āļąāļŠāļ”āļļ / āļ­āļļāļ›āļāļĢāļ“āđŒ / āđ‚āļĢāļ‡āļ‡āļēāļ™ | -| LCBP3-C1 | MOM | Minutes of Meeting | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ›āļĢāļ°āļŠāļļāļĄ | -| LCBP3-C1 | MPR | Monthly Progress Report | āļĢāļēāļĒāļ‡āļēāļ™āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ›āļĢāļ°āļˆāļģāđ€āļ”āļ·āļ­āļ™ | -| LCBP3-C1 | MST | Method Statement for Construction/Installation | āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ•āļīāļ”āļ•āļąāđ‰āļ‡ | -| LCBP3-C1 | NDS | Non-Design Data Submission | āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļš | -| LCBP3-C1 | PMA | Payment/Invoice/Retention/Estimate | āļāļēāļĢāļŠāļģāļĢāļ°āđ€āļ‡āļīāļ™ / āđƒāļšāđāļˆāđ‰āļ‡āļŦāļ™āļĩāđ‰ / āļ›āļĢāļ°āļāļąāļ™āļœāļĨāļ‡āļēāļ™ / āļ›āļĢāļ°āļĄāļēāļ“āļāļēāļĢ | -| LCBP3-C1 | PRD | Procedure | āļĢāļ°āđ€āļšāļĩāļĒāļšāļ›āļāļīāļšāļąāļ•āļī | -| LCBP3-C1 | PRG | Progress of Construction | āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ‚āļ­āļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ āļēāļžāļ–āđˆāļēāļĒ / āļ§āļīāļ”āļĩāđ‚āļ­ | -| LCBP3-C1 | QMS | Quality Document (Plan/Work Instruction) | āđ€āļ­āļāļŠāļēāļĢāļ”āđ‰āļēāļ™āļ„āļļāļ“āļ āļēāļž (āđāļœāļ™āļ‡āļēāļ™ / āļ‚āđ‰āļ­āđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™) | -| LCBP3-C1 | RPT | Report | āļĢāļēāļĒāļ‡āļēāļ™ | -| LCBP3-C1 | SAR | Semi Annual Report | āļĢāļēāļĒāļ‡āļēāļ™āļ›āļĢāļ°āļˆāļģāļŦāļāđ€āļ”āļ·āļ­āļ™ | -| LCBP3-C1 | SCH | Schedule and Program | āđāļœāļ™āļ‡āļēāļ™ | -| LCBP3-C1 | SDW | Shop Drawing | āđāļšāļšāļ‚āļĒāļēāļĒāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ” | -| LCBP3-C1 | SI | Soil Investigation | āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ”āļīāļ™ | -| LCBP3-C1 | SPE | Specification | āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” | -| LCBP3-C1 | TNR | Training Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļāļķāļāļ›āļāļīāļšāļąāļ•āļī | -| LCBP3-C1 | UC | Underground Construction | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđƒāļ•āđ‰āļ”āļīāļ™ | -| LCBP3-C1 | VEN | Vendor | āļœāļđāđ‰āļ‚āļēāļĒ | -| LCBP3-C1 | VRO | Variation Request/Instruction/Order | āļ„āļģāļ‚āļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ / āļ‚āđ‰āļ­āđ€āļŠāļ™āļ­āđāļ™āļ° / āļ‚āđ‰āļ­āđ€āļĢāļĩāļĒāļāļĢāđ‰āļ­āļ‡ | -| LCBP3-C1 | WTY | Warranty | āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ | -| LCBP3-C2 | GEN | General | āļ—āļąāđˆāļ§āđ„āļ› | -| LCBP3-C2 | CON | Contract | āļŠāļąāļāļāļē | -| LCBP3-C2 | INS | Insurances/Bond/Guarantee | āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ / āļžāļąāļ™āļ˜āļšāļąāļ•āļĢ / āļāļēāļĢāļ„āđ‰āļģāļ›āļĢāļ°āļāļąāļ™ | -| LCBP3-C2 | SCH | Schedule and Program | āđāļœāļ™āļ‡āļēāļ™ | -| LCBP3-C2 | PMA | Payment/Invoice/Retention/Estimate | āļāļēāļĢāļŠāļģāļĢāļ°āđ€āļ‡āļīāļ™ / āđƒāļšāđāļˆāđ‰āļ‡āļŦāļ™āļĩāđ‰ / āļ›āļĢāļ°āļāļąāļ™āļœāļĨāļ‡āļēāļ™ / āļ›āļĢāļ°āļĄāļēāļ“āļāļēāļĢ | -| LCBP3-C2 | VRO | Variation Request/Instruction/Order | āļ„āļģāļ‚āļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ / āļ‚āđ‰āļ­āđ€āļŠāļ™āļ­āđāļ™āļ° / āļ‚āđ‰āļ­āđ€āļĢāļĩāļĒāļāļĢāđ‰āļ­āļ‡ | -| LCBP3-C2 | VEN | Vendor | āļœāļđāđ‰āļ‚āļēāļĒ | -| LCBP3-C2 | WTY | Warranty | āļāļēāļĢāļ›āļĢāļ°āļāļąāļ™ | -| LCBP3-C2 | DRW | Drawings (All Types) | āđāļšāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ | -| LCBP3-C2 | DDW | Draft Drawing | āđāļšāļšāļĢāđˆāļēāļ‡ | -| LCBP3-C2 | SDW | Shop Drawing | āđāļšāļšāļ‚āļĒāļēāļĒāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ” | -| LCBP3-C2 | ADW | As Built Drawing | āđāļšāļšāļĢāđˆāļēāļ‡āļŦāļĨāļąāļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ | -| LCBP3-C2 | DDS | Design Data Submission | āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ­āļ­āļāđāļšāļš | -| LCBP3-C2 | DSN | Design/Calculation/Manual (All Stages) | āļ­āļ­āļāđāļšāļš / āļ„āļģāļ™āļ§āļ“ / āļ„āļđāđˆāļĄāļ·āļ­ | -| LCBP3-C2 | NDS | Non-Design Data Submission | āļ™āļģāļŠāđˆāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ„āļĄāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļš | -| LCBP3-C2 | PRD | Procedure | āļĢāļ°āđ€āļšāļĩāļĒāļšāļ›āļāļīāļšāļąāļ•āļī | -| LCBP3-C2 | MST | Method Statement for Construction/Installation | āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ•āļīāļ”āļ•āļąāđ‰āļ‡ | -| LCBP3-C2 | QMS | Quality Document (Plan/Work Instruction) | āđ€āļ­āļāļŠāļēāļĢāļ”āđ‰āļēāļ™āļ„āļļāļ“āļ āļēāļž (āđāļœāļ™āļ‡āļēāļ™ / āļ‚āđ‰āļ­āđāļ™āļ°āļ™āļģāđƒāļ™āļāļēāļĢāļ—āļģāļ‡āļēāļ™) | -| LCBP3-C2 | INR | Inspection/Audit/Surveillance Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš / āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāđ‰āļēāļĢāļ°āļ§āļąāļ‡ | -| LCBP3-C2 | ITP | Inspection and Test Plan | āđāļœāļ™āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāđāļĨāļ°āļ—āļ”āļŠāļ­āļš | -| LCBP3-C2 | MAT | Materials/Equipment/Plant | āļ§āļąāļŠāļ”āļļ / āļ­āļļāļ›āļāļĢāļ“āđŒ / āđ‚āļĢāļ‡āļ‡āļēāļ™ | -| LCBP3-C2 | SPE | Specification | āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” | -| LCBP3-C2 | MAN | Manual | āļ„āļđāđˆāļĄāļ·āļ­ | -| LCBP3-C2 | CER | Certificates | āđƒāļšāļĢāļąāļšāļĢāļ­āļ‡ | -| LCBP3-C2 | SAR | Semi Annual Report | āļĢāļēāļĒāļ‡āļēāļ™āļ›āļĢāļ°āļˆāļģāļŦāļāđ€āļ”āļ·āļ­āļ™ | -| LCBP3-C2 | JSA | Jobs Analysis | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ | -| LCBP3-C2 | MOM | Minutes of Meeting | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļ›āļĢāļ°āļŠāļļāļĄ | -| LCBP3-C2 | MPR | Monthly Progress Report | āļĢāļēāļĒāļ‡āļēāļ™āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ›āļĢāļ°āļˆāļģāđ€āļ”āļ·āļ­āļ™ | -| LCBP3-C2 | ICR | Incident Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāđ€āļāļīāļ”āļ­āļļāļšāļąāļ•āļīāđ€āļŦāļ•āļļāđāļĨāļ°āļāļēāļĢāļšāļēāļ”āđ€āļˆāđ‡āļš | -| LCBP3-C2 | PRG | Progress of Construction | āļ„āļ§āļēāļĄāļ„āļ·āļšāļŦāļ™āđ‰āļēāļ‚āļ­āļ‡āļāļēāļĢāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ / āļ āļēāļžāļ–āđˆāļēāļĒ / āļ§āļīāļ”āļĩāđ‚āļ­ | -| LCBP3-C2 | RPT | Report | āļĢāļēāļĒāļ‡āļēāļ™ | -| LCBP3-C2 | TNR | Training Report | āļĢāļēāļĒāļ‡āļēāļ™āļāļēāļĢāļāļķāļāļ›āļāļīāļšāļąāļ•āļī | - ---- diff --git a/8_lcbp3_v1_4_4.sql b/docs/8_lcbp3_v1_4_4.sql similarity index 95% rename from 8_lcbp3_v1_4_4.sql rename to docs/8_lcbp3_v1_4_4.sql index 60b36e6..1643adc 100644 --- a/8_lcbp3_v1_4_4.sql +++ b/docs/8_lcbp3_v1_4_4.sql @@ -1162,7 +1162,63 @@ ALTER TABLE rfa_revisions ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS ( JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount')) ) VIRTUAL; +ALTER TABLE rfa_revisions +ADD COLUMN schema_version INT DEFAULT 1 COMMENT 'Version āļ‚āļ­āļ‡ JSON Schema' +AFTER details; CREATE INDEX idx_rfa_rev_v_drawing_count ON rfa_revisions(v_ref_drawing_count); +-- ... (āļ•āđˆāļ­āļ—āđ‰āļēāļĒāđ„āļŸāļĨāđŒāđ€āļ”āļīāļĄ) +-- ============================================================ +-- āļŠāđˆāļ§āļ™āļ—āļĩāđˆ 11: Unified Workflow Engine (Phase 6A/Phase 3) +-- ============================================================ +DROP TABLE IF EXISTS workflow_histories; +DROP TABLE IF EXISTS workflow_instances; +DROP TABLE IF EXISTS workflow_definitions; +-- 1. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄ Workflow (Definition / DSL) +CREATE TABLE workflow_definitions ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID āļ‚āļ­āļ‡ Workflow Definition', + workflow_code VARCHAR(50) NOT NULL COMMENT 'āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ RFA_FLOW_V1, CORRESPONDENCE_FLOW_V1', + version INT NOT NULL DEFAULT 1 COMMENT 'āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version', + description TEXT NULL COMMENT 'āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ Workflow', + dsl JSON NOT NULL COMMENT 'āļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļš (YAML/JSON Format)', + compiled JSON NOT NULL COMMENT 'āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Execution Tree āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§', + is_active BOOLEAN DEFAULT TRUE COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', + -- āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļĄāļĩ Workflow Code āđāļĨāļ° Version āļ‹āđ‰āļģāļāļąāļ™ + UNIQUE KEY uq_workflow_version (workflow_code, version) +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄāļāļŽāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢ (Workflow DSL)'; +-- āļŠāļĢāđ‰āļēāļ‡ Index āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļē Workflow āļ—āļĩāđˆ Active āļĨāđˆāļēāļŠāļļāļ”āđ„āļ”āđ‰āđ€āļĢāđ‡āļ§āļ‚āļķāđ‰āļ™ +CREATE INDEX idx_workflow_active ON workflow_definitions(workflow_code, is_active, version); +-- 2. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļš Workflow Instance (āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢāļˆāļĢāļīāļ‡) +CREATE TABLE workflow_instances ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID āļ‚āļ­āļ‡ Instance', + definition_id CHAR(36) NOT NULL COMMENT 'āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Definition āļ—āļĩāđˆāđƒāļŠāđ‰', + entity_type VARCHAR(50) NOT NULL COMMENT 'āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (rfa_revision, correspondence_revision, circulation)', + entity_id VARCHAR(50) NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (String/Int)', + current_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™', + status ENUM('ACTIVE', 'COMPLETED', 'CANCELLED', 'TERMINATED') DEFAULT 'ACTIVE' COMMENT 'āļŠāļ–āļēāļ™āļ°āļ āļēāļžāļĢāļ§āļĄ', + context JSON NULL COMMENT 'āļ•āļąāļ§āđāļ›āļĢ Context āļŠāļģāļŦāļĢāļąāļšāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆ', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + CONSTRAINT fk_wf_inst_def FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ'; +CREATE INDEX idx_wf_inst_entity ON workflow_instances(entity_type, entity_id); +CREATE INDEX idx_wf_inst_state ON workflow_instances(current_state); +-- 3. āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļī (Audit Log / History) +CREATE TABLE workflow_histories ( + id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID', + instance_id CHAR(36) NOT NULL COMMENT 'āļ­āđ‰āļēāļ‡āļ­āļīāļ‡ Instance', + from_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ•āđ‰āļ™āļ—āļēāļ‡', + to_state VARCHAR(50) NOT NULL COMMENT 'āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡', + action VARCHAR(50) NOT NULL COMMENT 'Action āļ—āļĩāđˆāļāļĢāļ°āļ—āļģ', + action_by_user_id INT NULL COMMENT 'User ID āļœāļđāđ‰āļāļĢāļ°āļ—āļģ', + comment TEXT NULL COMMENT 'āļ„āļ§āļēāļĄāđ€āļŦāđ‡āļ™', + metadata JSON NULL COMMENT 'Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™', + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT fk_wf_hist_inst FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE +) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° Workflow'; +CREATE INDEX idx_wf_hist_instance ON workflow_histories(instance_id); +CREATE INDEX idx_wf_hist_user ON workflow_histories(action_by_user_id); -- ============================================================ -- 5. PARTITIONING PREPARATION (Advance - Optional) -- ============================================================ diff --git a/8_lcbp3_v1_4_4_seed.sql b/docs/8_lcbp3_v1_4_4_seed.sql similarity index 100% rename from 8_lcbp3_v1_4_4_seed.sql rename to docs/8_lcbp3_v1_4_4_seed.sql diff --git a/docs/DMS v1.3.0 - Interactive Report.html b/docs/DMS v1.3.0 - Interactive Report.html deleted file mode 100644 index b67df3d..0000000 --- a/docs/DMS v1.3.0 - Interactive Report.html +++ /dev/null @@ -1,724 +0,0 @@ - - - - - - DMS v1.3.0 - āļĢāļēāļĒāļ‡āļēāļ™āļŠāļĢāļļāļ›āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāļĢāļ°āļšāļš - - - - - - - - - - - - - - -
- -
- - -
- - -
-

āļ āļēāļžāļĢāļ§āļĄāļĢāļ°āļšāļš (Overview)

-

- āļ™āļĩāđˆāļ„āļ·āļ­āļĢāļēāļĒāļ‡āļēāļ™āļŠāļĢāļļāļ›āđ€āļŠāļīāļ‡āđ‚āļ•āđ‰āļ•āļ­āļšāļŠāļģāļŦāļĢāļąāļš **āļĢāļ°āļšāļšāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ (DMS) v1.3.0** āđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļ™āļĩāđ‰āļ–āļđāļāļ­āļ­āļāđāļšāļšāļĄāļēāđ€āļžāļ·āđˆāļ­āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš, āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄ, āđāļĨāļ°āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļ—āļĩāļĄāļžāļąāļ’āļ™āļēāđāļĨāļ°āļœāļđāđ‰āļĄāļĩāļŠāđˆāļ§āļ™āđ„āļ”āđ‰āļŠāđˆāļ§āļ™āđ€āļŠāļĩāļĒāļŠāļēāļĄāļēāļĢāļ–āļ—āļģāļ„āļ§āļēāļĄāđ€āļ‚āđ‰āļēāđƒāļˆāļ āļēāļžāļĢāļ§āļĄāļ‚āļ­āļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļ™āļĩāđ‰āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļĢāļ§āļ”āđ€āļĢāđ‡āļ§ -

-
-
-
6+
-
āđ‚āļĄāļ”āļđāļĨāđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ
-
-
-
44+
-
āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Tables)
-
-
-
5+
-
āļ§āļīāļ§āļ‚āđ‰āļ­āļĄāļđāļĨ (Views)
-
-
-
7
-
Services (Docker)
-
-
-
- - -
-

āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄāđāļĨāļ°āđ€āļ—āļ„āđ‚āļ™āđ‚āļĨāļĒāļĩ (System Architecture)

-

- āļĢāļ°āļšāļš DMS v1.3.0 āļ–āļđāļāļ­āļ­āļāđāļšāļšāļšāļ™āļŠāļ–āļēāļ›āļąāļ•āļĒāļāļĢāļĢāļĄ Headless/API-First āļ—āļĩāđˆāļ—āļąāļ™āļŠāļĄāļąāļĒ āđ‚āļ”āļĒāļ—āļģāļ‡āļēāļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļšāļ™ QNAP Server āļœāđˆāļēāļ™ Container Station (Docker) āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļ‡āđˆāļēāļĒāļ•āđˆāļ­āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđāļĨāļ°āļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļē -

- -
-

āđāļœāļ™āļœāļąāļ‡āļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ (Docker Container Stack)

-
- -
-
Nginx Proxy Manager
-

np-dms.work (HTTPS)

- - -
-
Network: 'lcbp3'
- -
- -
-
-

Next.js (Frontend)

-

āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢ UI (React)

-
-
- -
-
-

NestJS (Backend)

-

āļˆāļąāļ”āļāļēāļĢ API & Business Logic

-
-
- -
-
-

N8N (Automation)

-

āļˆāļąāļ”āļāļēāļĢ Workflow & Line Notify

-
-
-
- - -
-
-
▾
-
- - -
-
-

MariaDB (Database)

-

āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (SQL)

-
-
-

Elasticsearch (Search)

-

āđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡

-
-
- -
-
-
-
-
- - -
-

āđ‚āļĄāļ”āļđāļĨāđ€āļ­āļāļŠāļēāļĢāļŦāļĨāļąāļ (Document Modules)

-

- āļĢāļ°āļšāļš DMS āđāļšāđˆāļ‡āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ­āļ­āļāđ€āļ›āđ‡āļ™āđ‚āļĄāļ”āļđāļĨāļĒāđˆāļ­āļĒāļ—āļĩāđˆāļŠāļąāļ”āđ€āļˆāļ™ āđāļ•āđˆāļĨāļ°āđ‚āļĄāļ”āļđāļĨāļĄāļĩ Workflow āđāļĨāļ°āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļ‰āļžāļēāļ°āļ‚āļ­āļ‡āļ•āļ™āđ€āļ­āļ‡ āđāļ•āđˆāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ€āļŠāļ·āđˆāļ­āļĄāđ‚āļĒāļ‡āļāļąāļšāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢāļāļĨāļēāļ‡ (Correspondence) -

-
- -
-
-

1. Correspondence (āđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš)

-

āđ‚āļĄāļ”āļđāļĨāļŦāļĨāļąāļāļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāđ€āļ‚āđ‰āļē-āļ­āļ­āļāļ—āļąāđˆāļ§āđ„āļ› (āļˆāļ”āļŦāļĄāļēāļĒ, āđ€āļĄāđ‚āļĄ) āđ€āļ›āđ‡āļ™āļ•āļēāļĢāļēāļ‡ "āđāļĄāđˆ" āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ›āļĢāļ°āđ€āļ āļ—āļ­āļ·āđˆāļ™āđ€āļāļ·āļ­āļšāļ—āļąāđ‰āļ‡āļŦāļĄāļ”

- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ: -
    -
  • `correspondences` (Master)
  • -
  • `correspondence_revisions` (Child)
  • -
  • `correspondence_recipients`
  • -
-
-
- -
-
-

2. RFA (āļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī)

-

āđ‚āļĄāļ”āļđāļĨāļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī (Request for Approval) āđ€āļŠāđˆāļ™ āļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļīāđāļšāļš, āļ§āļąāļŠāļ”āļļ, āļŦāļĢāļ·āļ­āđ€āļ­āļāļŠāļēāļĢ āļ—āļĩāđˆāļĄāļĩ Workflow āļŠāļąāļ”āđ€āļˆāļ™ (Draft, Submit, Review, Approved)

- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ: -
    -
  • `rfas` (Master)
  • -
  • `rfa_revisions` (Child)
  • -
  • `rfa_status_codes`
  • -
  • `rfa_items` (āđ€āļŠāļ·āđˆāļ­āļĄ Shop Drawing)
  • -
-
-
- -
-
-

3. Drawing (āđāļšāļšāđāļ›āļĨāļ™)

-

āļˆāļąāļ”āļāļēāļĢāđāļšāļšāđāļ›āļĨāļ™ 2 āļ›āļĢāļ°āđ€āļ āļ—āļŦāļĨāļąāļ: āđāļšāļšāļ•āļēāļĄāļŠāļąāļāļāļē (Contract Drawing) āđāļĨāļ°āđāļšāļšāļŠāļģāļŦāļĢāļąāļšāļāđˆāļ­āļŠāļĢāđ‰āļēāļ‡ (Shop Drawing) āļ‹āļķāđˆāļ‡ Shop Drawing āļˆāļ°āļ–āļđāļāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđƒāļ™ RFA

- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ: -
    -
  • `contract_drawings`
  • -
  • `shop_drawings` (Master)
  • -
  • `shop_drawing_revisions` (Child)
  • -
  • `shop_drawing_revision_contract_refs`
  • - -
-
-
- -
-
-

4. Transmittal (āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡)

-

āđ€āļ­āļāļŠāļēāļĢāļ™āļģāļŠāđˆāļ‡ (āļ„āļĨāđ‰āļēāļĒāđƒāļšāļ›āļ°āļŦāļ™āđ‰āļē) āļ—āļĩāđˆāđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļĢāļ§āļšāļĢāļ§āļĄāđ€āļ­āļāļŠāļēāļĢ RFA āļŦāļĨāļēāļĒāđ† āļ‰āļšāļąāļš āđ€āļžāļ·āđˆāļ­āļŠāđˆāļ‡āđƒāļŦāđ‰āļœāļđāđ‰āļĢāļąāļšāđƒāļ™āļ„āļĢāļēāļ§āđ€āļ”āļĩāļĒāļ§

- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ: -
    -
  • `transmittals` (1:1 with Correspondence)
  • -
  • `transmittal_items` (āđ€āļŠāļ·āđˆāļ­āļĄ RFA)
  • -
-
-
- -
-
-

5. Circulation (āđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™)

-

āļĢāļ°āļšāļšāđƒāļšāđ€āļ§āļĩāļĒāļ™āļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ (Internal) āđƒāļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļŠāđˆāļ‡āđ€āļ­āļāļŠāļēāļĢ (āļ—āļĩāđˆāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļˆāļēāļ Correspondence) āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļ—āļĩāļĄāļ•āļĢāļ§āļˆāļŠāļ­āļš, āļĢāļąāļšāļ—āļĢāļēāļš, āļŦāļĢāļ·āļ­āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ

- āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ: -
    -
  • `circulations` (Master)
  • -
  • `circulation_assignees` (Tasks)
  • -
  • `circulation_actions` (Logs)
  • -
-
-
- -
-
-

6. āļŠāļąāļ”āļŠāđˆāļ§āļ™āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ

-

āđāļœāļ™āļ āļđāļĄāļīāđāļŠāļ”āļ‡āļŠāļąāļ”āļŠāđˆāļ§āļ™āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āđƒāļ™āđāļ•āđˆāļĨāļ°āđ‚āļĄāļ”āļđāļĨāļŦāļĨāļąāļ (āđ„āļĄāđˆāļĢāļ§āļĄāļ•āļēāļĢāļēāļ‡ Core āđāļĨāļ° RBAC)

-
- -
-
-
- -
-
- - -
-

āļŸāļĩāđ€āļˆāļ­āļĢāđŒāļŦāļĨāļąāļ (Core Features)

-

- āļ™āļ­āļāđ€āļŦāļ™āļ·āļ­āļˆāļēāļāđ‚āļĄāļ”āļđāļĨāđ€āļ­āļāļŠāļēāļĢ āļĢāļ°āļšāļšāļĒāļąāļ‡āļĄāļĩāļŸāļĩāđ€āļˆāļ­āļĢāđŒāļŠāļ™āļąāļšāļŠāļ™āļļāļ™āļ—āļĩāđˆāļŠāļģāļ„āļąāļ āļ‹āļķāđˆāļ‡āļ—āļģāļ‡āļēāļ™āļ‚āđ‰āļēāļĄāđ‚āļĄāļ”āļđāļĨāļ•āđˆāļēāļ‡āđ† āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļĢāļ°āļšāļšāļ—āļģāļ‡āļēāļ™āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļŠāļĄāļšāļđāļĢāļ“āđŒ -

-
- -
-

āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļŠāļīāļ—āļ˜āļīāđŒ (RBAC)

-

- āļĢāļ°āļšāļšāđƒāļŠāđ‰ Role-Based Access Control (RBAC) āļ—āļĩāđˆāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļĄāļēāļ āļœāļđāđ‰āđƒāļŠāđ‰ (Users) āļˆāļ°āđ„āļ”āđ‰āļĢāļąāļšāļšāļ—āļšāļēāļ— (Roles) āļ‹āļķāđˆāļ‡āļœāļđāļāļāļąāļšāļŠāļīāļ—āļ˜āļīāđŒ (Permissions) - āđāļœāļ™āļ āļđāļĄāļīāļ™āļĩāđ‰āđāļŠāļ”āļ‡āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āļˆāļģāļ™āļ§āļ™āļŠāļīāļ—āļ˜āļīāđŒ (Permissions) āļ—āļĩāđˆāđāļ•āđˆāļĨāļ°āļšāļ—āļšāļēāļ—āļžāļ·āđ‰āļ™āļāļēāļ™āļ­āļēāļˆāļĄāļĩ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ€āļŦāđ‡āļ™āļ āļēāļžāļ„āļ§āļēāļĄāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđƒāļ™āļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ -

-
- -
-
- -
-

āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Numbering)

-

- āļŦāļ™āļķāđˆāļ‡āđƒāļ™āļŠāđˆāļ§āļ™āļ—āļĩāđˆāļŠāļģāļ„āļąāļāļ—āļĩāđˆāļŠāļļāļ” āļ„āļ·āļ­āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Running Number) āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āđ€āļĨāļ‚āļ—āļĩāđˆāļ‹āđ‰āļģ (Race Condition) āļĢāļ°āļšāļšāđƒāļŠāđ‰ Stored Procedure (`sp_get_next_document_number`) āđƒāļ™ MariaDB āļ‹āļķāđˆāļ‡āđƒāļŠāđ‰āļ„āļģāļŠāļąāđˆāļ‡ `SELECT ... FOR UPDATE` āđ€āļžāļ·āđˆāļ­ "āļĨāđ‡āļ­āļ" āđāļ–āļ§āļ‚āļ­āļ‡āļ•āļąāļ§āļ™āļąāļšāļāđˆāļ­āļ™āļ—āļĩāđˆāļˆāļ°āļ­āļąāļ›āđ€āļ”āļ•āļ„āđˆāļē -

-
-SELECT last_number
-INTO v_last_number
-FROM document_number_counters
-WHERE project_id = p_project_id 
-  AND ...
-FOR UPDATE;
-
-IF v_last_number IS NULL THEN
-  INSERT INTO ... (last_number) VALUES (1);
-  SET p_next_number = 1;
-ELSE
-  SET p_next_number = v_last_number + 1;
-  UPDATE document_number_counters
-  SET last_number = p_next_number
-  WHERE ...;
-END IF;
-
- -
-

āļāļēāļĢāļ„āđ‰āļ™āļŦāļē (Advanced Search)

-

- (Req 6.2) āļĢāļ°āļšāļšāđƒāļŠāđ‰ **Elasticsearch** āđ€āļ›āđ‡āļ™ Service āđāļĒāļāļ•āđˆāļēāļ‡āļŦāļēāļāļŠāļģāļŦāļĢāļąāļšāđƒāļŦāđ‰āļšāļĢāļīāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ - NestJS (Backend) āļˆāļ°āļ—āļģāļāļēāļĢ Index āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļēāļ Views (`v_current_correspondences`, `v_current_rfas` āļŊāļĨāļŊ) āđ„āļ›āļĒāļąāļ‡ Elasticsearch - āļ—āļģāđƒāļŦāđ‰āļœāļđāđ‰āđƒāļŠāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ„āđ‰āļ™āļŦāļēāđāļšāļš Full-text āļ‚āđ‰āļēāļĄāđ‚āļĄāļ”āļđāļĨāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Correspondence, RFA, Drawing) āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļĢāļ§āļ”āđ€āļĢāđ‡āļ§ -

-
- -
-

āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Notifications)

-

- (Req 6.7) āļĢāļ°āļšāļšāđƒāļŠāđ‰ **N8N (Automation)** āđ€āļ›āđ‡āļ™āļĻāļđāļ™āļĒāđŒāļāļĨāļēāļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ - āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđ€āļŦāļ•āļļāļāļēāļĢāļ“āđŒāļŠāļģāļ„āļąāļ (āđ€āļŠāđˆāļ™ āļĄāļĩāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđƒāļšāđ€āļ§āļĩāļĒāļ™āđƒāļŦāļĄāđˆ) NestJS (Backend) āļˆāļ°āļĒāļīāļ‡ Webhook āđ„āļ›āļ—āļĩāđˆ N8N - āļˆāļēāļāļ™āļąāđ‰āļ™ N8N āļˆāļ°āļ—āļģāļŦāļ™āđ‰āļēāļ—āļĩāđˆāļŠāđˆāļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™āđ„āļ›āļĒāļąāļ‡ **Line Notify** āļŦāļĢāļ·āļ­ Email āļ•āļēāļĄ Workflow āļ—āļĩāđˆāļ•āļąāđ‰āļ‡āļ„āđˆāļēāđ„āļ§āđ‰ -

-
-
-
- - -
-

āļŠāļģāļĢāļ§āļˆāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Explorer)

-

- āļĢāļ°āļšāļš DMS āļĄāļĩāļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļĄāļēāļāļāļ§āđˆāļē 44 āļ•āļēāļĢāļēāļ‡āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļšāļŸāļĩāđ€āļˆāļ­āļĢāđŒāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™ āđ€āļĨāļ·āļ­āļāļ•āļēāļĢāļēāļ‡āļˆāļēāļāļĢāļēāļĒāļāļēāļĢāļ”āđ‰āļēāļ™āļĨāđˆāļēāļ‡āđ€āļžāļ·āđˆāļ­āļ”āļđāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡, āļ›āļĢāļ°āđ€āļ āļ—āļ‚āđ‰āļ­āļĄāļđāļĨ, āđāļĨāļ°āļ„āļ§āļēāļĄāļŠāļąāļĄāļžāļąāļ™āļ˜āđŒ (Foreign Keys) -

- -
-
- - -
- -
-

-

- -
- - - - - - - - - - - -
Column (Field)TypeDescription / Relation
-
-
-
-
- -
- -
-

Interactive Report generated for DMS v1.3.0

-
- - - - \ No newline at end of file diff --git a/0_Requirements_V1_4_4.md b/docs/Markdown/0_Requirements_V1_4_4.md similarity index 91% rename from 0_Requirements_V1_4_4.md rename to docs/Markdown/0_Requirements_V1_4_4.md index 1ec46b9..833cc37 100644 --- a/0_Requirements_V1_4_4.md +++ b/docs/Markdown/0_Requirements_V1_4_4.md @@ -1,13 +1,13 @@ # 📝 **Documents Management System Version 1.4.4: Application Requirements Specification** -**āļŠāļ–āļēāļ™āļ°:** FINAL-Rev.04 +**āļŠāļ–āļēāļ™āļ°:** FINAL-Rev.05 **āļ§āļąāļ™āļ—āļĩāđˆ:** 2025-11-26 -**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļžāļ·āđ‰āļ™āļāļēāļ™:** v1.4.3 +**āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļžāļ·āđ‰āļ™āļāļēāļ™:** v1.4.4 **Classification:** Internal Technical Documentation ## 📌 **1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ** -āļŠāļĢāđ‰āļēāļ‡āđ€āļ§āđ‡āļšāđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļšāļšāļĢāļīāļŦāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Document Management System - DMS) āđāļšāļšāļ„āļĢāļšāļ§āļ‡āļˆāļĢ āļ—āļĩāđˆāđ€āļ™āđ‰āļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” āļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Integrity) āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§āđƒāļ™āļ­āļ™āļēāļ„āļ• (Scalability) āđ‚āļ”āļĒāđāļāđ‰āđ„āļ‚āļ›āļąāļāļŦāļē Race Condition āđāļĨāļ°āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāđ€āļŠāļ–āļĩāļĒāļĢāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒāđāļĨāļ° Workflow +āļŠāļĢāđ‰āļēāļ‡āđ€āļ§āđ‡āļšāđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļšāļšāļĢāļīāļŦāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ (Document Management System - DMS) āđāļšāļšāļ„āļĢāļšāļ§āļ‡āļˆāļĢ āļ—āļĩāđˆāđ€āļ™āđ‰āļ™āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” āļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Integrity) āđāļĨāļ°āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§āđƒāļ™āļ­āļ™āļēāļ„āļ• (Scalability) āđ‚āļ”āļĒāđāļāđ‰āđ„āļ‚āļ›āļąāļāļŦāļē Race Condition āđāļĨāļ°āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāđ€āļŠāļ–āļĩāļĒāļĢāđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ„āļŸāļĨāđŒ āđāļĨāļ°āđƒāļŠāđ‰ Unified Workflow Engine āđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™ - āļĄāļĩāļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļŦāļĨāļąāļāđƒāļ™āļāļēāļĢāļ­āļąāļ›āđ‚āļŦāļĨāļ” āļˆāļąāļ”āđ€āļāđ‡āļš āļ„āđ‰āļ™āļŦāļē āđāļŠāļĢāđŒ āđāļĨāļ°āļ„āļ§āļšāļ„āļļāļĄāļŠāļīāļ—āļ˜āļīāđŒāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āđ€āļ­āļāļŠāļēāļĢ - āļŠāđˆāļ§āļĒāļĨāļ”āļāļēāļĢāđƒāļŠāđ‰āđ€āļ­āļāļŠāļēāļĢāļāļĢāļ°āļ”āļēāļĐ āđ€āļžāļīāđˆāļĄāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāđƒāļ™āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨ @@ -50,12 +50,14 @@ ### **2.3 Core Services:** - **Code Hosting:** Gitea (Self-hosted on QNAP) + - Application name: git - Service name: gitea - Domain: `git.np-dms.work` - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āđ€āļ›āđ‡āļ™āļĻāļđāļ™āļĒāđŒāļāļĨāļēāļ‡āđƒāļ™āļāļēāļĢāđ€āļāđ‡āļšāđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāđ€āļ§āļ­āļĢāđŒāļŠāļąāļ™āļ‚āļ­āļ‡āđ‚āļ„āđ‰āļ” (Source Code) āļŠāļģāļŦāļĢāļąāļšāļ—āļļāļāļŠāđˆāļ§āļ™ - **Backend / Data Platform:** NestJS + - Application name: lcbp3-backend - Service name: backend - Domain: `backend.np-dms.work` @@ -63,6 +65,7 @@ - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ (Data Models), āļŠāļĢāđ‰āļēāļ‡ API, āļˆāļąāļ”āļāļēāļĢāļŠāļīāļ—āļ˜āļīāđŒāļœāļđāđ‰āđƒāļŠāđ‰ (Roles & Permissions), āđāļĨāļ°āļŠāļĢāđ‰āļēāļ‡ Workflow āļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡āļĢāļ°āļšāļš - **Database:** MariaDB 10.11 + - Application name: lcbp3-db - Service name: mariadb - Domain: `db.np-dms.work` @@ -70,6 +73,7 @@ - Tooling: DBeaver (Community Edition), phpmyadmin āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ­āļ­āļāđāļšāļšāđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ - **Database Management:** phpMyAdmin + - Application name: lcbp3-db - Service: phpmyadmin:5-apache - Service name: pma @@ -77,6 +81,7 @@ - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢāļāļēāļ™āļ‚āđ‰āļ­āļĄāļđāļĨ mariadb āļœāđˆāļēāļ™ Web UI - **Frontend:** Next.js + - Application name: lcbp3-frontend - Service name: frontend - Domain: `lcbp3.np-dms.work` @@ -86,6 +91,7 @@ - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļŠāļĢāđ‰āļēāļ‡āļŦāļ™āđ‰āļēāļ•āļēāđ€āļ§āđ‡āļšāđāļ­āļ›āļžāļĨāļīāđ€āļ„āļŠāļąāļ™āļŠāļģāļŦāļĢāļąāļšāđƒāļŦāđ‰āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āđ€āļ‚āđ‰āļēāļĄāļēāļ”āļđ Dashboard, āļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢ, āđāļĨāļ°āļ•āļīāļ”āļ•āļēāļĄāļ‡āļēāļ™ āđ‚āļ”āļĒāļˆāļ°āļŠāļ·āđˆāļ­āļŠāļēāļĢāļāļąāļš Backend āļœāđˆāļēāļ™ API - **Workflow Automation:** n8n + - Application name: lcbp3-n8n - Service: n8nio/n8n:latest - Service name: n8n @@ -93,6 +99,7 @@ - āļŦāļ™āđ‰āļēāļ—āļĩāđˆ: āļˆāļąāļ”āļāļēāļĢ workflow āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Backend āđāļĨāļ° Line - **Reverse Proxy:** Nginx Proxy Manager + - Application name: lcbp3-npm - Service: Nginx Proxy Manager (nginx-proxy-manage: latest) - Service name: npm @@ -104,11 +111,11 @@ ### **2.4 Business Logic & Consistency (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡):** -- **2.4.1 āļ•āļĢāļĢāļāļ°āļ—āļēāļ‡āļ˜āļļāļĢāļāļīāļˆāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ”** (āđ€āļŠāđˆāļ™ āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° Workflow [cite: 3.5.3, 3.6.5], āļāļēāļĢāļšāļąāļ‡āļ„āļąāļšāđƒāļŠāđ‰āļŠāļīāļ—āļ˜āļīāđŒ [cite: 4.4], āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš Deadline [cite: 3.2.5]) **āļˆāļ°āļ–āļđāļāļˆāļąāļ”āļāļēāļĢāđƒāļ™āļāļąāđˆāļ‡ Backend (NestJS)** [cite: 2.3] āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļēāđāļĨāļ°āļ—āļ”āļŠāļ­āļšāđ„āļ”āđ‰āļ‡āđˆāļēāļĒ (Testability) +- **2.4.1 Unified Workflow Engine (āļŦāļĨāļąāļ):** āļĢāļ°āļšāļšāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (Correspondence, RFA, Circulation) āļ•āđ‰āļ­āļ‡ āđƒāļŠāđ‰ Engine āļāļĨāļēāļ‡āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ āđ‚āļ”āļĒāļāļģāļŦāļ™āļ” Logic āļœāđˆāļēāļ™ Workflow DSL (JSON Configuration) āđāļ—āļ™āļāļēāļĢāđ€āļ‚āļĩāļĒāļ™ Hard-coded āļĨāļ‡āđƒāļ™āļ•āļēāļĢāļēāļ‡ -- **2.4.2 Unified Workflow Engine (āđƒāļŦāļĄāđˆ):** āļĢāļ§āļĄ Logic āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡ `CorrespondenceRouting` āđāļĨāļ° `RfaWorkflow` āđƒāļŦāđ‰āđƒāļŠāđ‰ Core Engine āđ€āļ”āļĩāļĒāļ§āļāļąāļ™āđ€āļžāļ·āđˆāļ­āļĨāļ”āļ„āļ§āļēāļĄāļ‹āđ‰āļģāļ‹āđ‰āļ­āļ™āđāļĨāļ°āļ‡āđˆāļēāļĒāļ•āđˆāļ­āļāļēāļĢāļšāļģāļĢāļļāļ‡āļĢāļąāļāļĐāļē +- **2.4.2 Separation of Concerns:** Module āļ•āđˆāļēāļ‡āđ† (RFA, Correspondence) āļˆāļ°āđ€āļāđ‡āļšāđ€āļ‰āļžāļēāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (Data) āļŠāđˆāļ§āļ™āļŠāļ–āļēāļ™āļ°āđāļĨāļ°āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (State Transition) āļˆāļ°āļ–āļđāļāļˆāļąāļ”āļāļēāļĢāđ‚āļ”āļĒ Workflow Engine -- **2.4.3 Idempotency Keys (āđƒāļŦāļĄāđˆ):** API āļ—āļĩāđˆāļŠāļģāļ„āļąāļ (āđ€āļŠāđˆāļ™ Submit Document, Approve) āļ•āđ‰āļ­āļ‡āļšāļąāļ‡āļ„āļąāļšāļŠāđˆāļ‡ Header `Idempotency-Key` āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļ—āļģāļĢāļēāļĒāļāļēāļĢāļ‹āđ‰āļģāļˆāļēāļāļāļēāļĢāļāļ”āļ›āļļāđˆāļĄāļĢāļąāļ§āđ† āļŦāļĢāļ·āļ­ Network Retry +- **2.4.3 Idempotency & Locking:** āđƒāļŠāđ‰āļāļĨāđ„āļāđ€āļ”āļīāļĄāđƒāļ™āļāļēāļĢāļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļ—āļģāļĢāļēāļĒāļāļēāļĢāļ‹āđ‰āļģ - **2.4.4 Optimistic Locking (āđƒāļŦāļĄāđˆ):** āđƒāļŠāđ‰ Version Column āđƒāļ™ Database āļ„āļ§āļšāļ„āļđāđˆāļāļąāļš Redis Lock āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ āđ€āļžāļ·āđˆāļ­āđ€āļ›āđ‡āļ™ Safety Net āļŠāļąāđ‰āļ™āļŠāļļāļ”āļ—āđ‰āļēāļĒ @@ -190,22 +197,28 @@ - 3.4.3. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ - 3.4.4. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āļŠāđ‰āļŠāļģāļŦāļĢāļąāļšāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ āđƒāļ™ Shop Drawings, āļĄāļĩāļāļēāļĢāļˆāļąāļ”āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāļ‚āļ­āļ‡ Shop Drawings -### **3.5. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī (Request for Approval & Workflow)** +### **3.5. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Workflow (Unified Workflow)** -- 3.5.1. āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ: āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī (Request for Approval) āđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļŠāđˆāļ‡āđ€āļ­āļāļŠāļēāļĢāđ€āļžāļīāļ­āļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī -- 3.5.2. āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ: Request for Approval (RFA) āđ€āļ›āđ‡āļ™āļŠāļ™āļīāļ”āļŦāļ™āļķāđˆāļ‡āļ‚āļ­āļ‡ Correspondence āļ—āļĩāđˆāļĄāļĩāļĨāļąāļāļĐāļ“āļ°āđ€āļ‰āļžāļēāļ°āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āđ„āļ”āđ‰āļĢāļąāļšāļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī āļĄāļĩāļ›āļĢāļ°āđ€āļ āļ—āļ”āļąāļ‡āļ™āļĩāđ‰: - - Request for Drawing Approval (RFA_DWG) - - Request for Document Approval (RFA_DOC) - - Request for Method statement Approval (RFA_MES) - - Request for Material Approval (RFA_MAT) -- 3.5.2. āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ€āļ­āļāļŠāļēāļĢ: āļœāļđāđ‰āđƒāļŠāđ‰āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰ -- 3.5.3. āļāļēāļĢāļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđāļĨāļ°āļˆāļąāļ”āļāļĨāļļāđˆāļĄ: āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Drawing (RFA_DWG): - - āđ€āļ­āļāļŠāļēāļĢ RFA_DWG āļˆāļ°āļ›āļĢāļ°āļāļ­āļšāđ„āļ›āļ”āđ‰āļ§āļĒ Shop Drawing (shop_drawings) āļŦāļĨāļēāļĒāđāļœāđˆāļ™ āļ‹āļķāđˆāļ‡āđāļ•āđˆāļĨāļ°āđāļœāđˆāļ™āļĄāļĩ Revision āļ‚āļ­āļ‡āļ•āļąāļ§āđ€āļ­āļ‡ - - Shop Drawing āđāļ•āđˆāļĨāļ° Revision āļŠāļēāļĄāļēāļĢāļ–āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļ–āļķāļ‡ Contract Drawing (Ccontract_drawings) āļŦāļĨāļēāļĒāđāļœāđˆāļ™ āļŦāļĢāļ·āļ­āđ„āļĄāđˆāļ­āđ‰āļēāļ‡āļ–āļķāļ‡āļāđ‡āđ„āļ”āđ‰ - - āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĄāļĩāļŠāđˆāļ§āļ™āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨ Master Data āļ‚āļ­āļ‡āļ—āļąāđ‰āļ‡ Shop Drawing āđāļĨāļ° Contract Drawing āđāļĒāļāļˆāļēāļāļāļąāļ™ -- 3.5.4. Workflow āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī: āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ°āđ€āļ›āđ‡āļ™āļĨāļģāļ”āļąāļš āđ€āļŠāđˆāļ™ - - āļŠāđˆāļ‡āļˆāļēāļ Originator -> Organization 1 -> Organization 2 -> Organization 3 āđāļĨāđ‰āļ§āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄ (āđ‚āļ”āļĒāļ–āđ‰āļē āļ­āļ‡āļāļĢāļ“āđŒāđƒāļ”āđƒāļ™ Workflow āđƒāļŦāđ‰āļŠāđˆāļ‡āļāļĨāļąāļš āļāđ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĢāļ­āđƒāļŦāđ‰āļ–āļķāļ‡ āļ­āļ‡āļāļĢāļ“āļ·āđƒāļ™āļĨāļģāļ”āļąāļšāļ–āļąāļ”āđ„āļ›) -- 3.5.5. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ: āļĄāļĩāļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒāļ”āļąāļ‡āļ™āļĩāđ‰ +- 3.5.1 Workflow Definition: + + - Admin āļ•āđ‰āļ­āļ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚ Workflow Rule āđ„āļ”āđ‰āļœāđˆāļēāļ™āļŦāļ™āđ‰āļēāļˆāļ­ UI (DSL Editor) āļĢ + - āļ­āļ‡āļĢāļąāļšāļāļēāļĢāļāļģāļŦāļ™āļ” State, Transition, Required Role, Condition (JS Expression) + +- 3.5.2 Workflow Execution: + + - āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Instance āļ‚āļ­āļ‡ Workflow āļœāļđāļāļāļąāļšāđ€āļ­āļāļŠāļēāļĢ (Polymorphic) + - āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Action) āđ€āļŠāđˆāļ™ Approve, Reject, Comment, Return + - Auto-Action: āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļīāđ€āļĄāļ·āđˆāļ­āļ„āļĢāļšāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ (āđ€āļŠāđˆāļ™ Review āļ„āļĢāļšāļ—āļļāļāļ„āļ™) + +- 3.5.3 Flexibility: + + - āļĢāļ­āļ‡āļĢāļąāļš Parallel Review (āļŠāđˆāļ‡āđƒāļŦāđ‰āļŦāļĨāļēāļĒāļ„āļ™āļ•āļĢāļ§āļˆāļžāļĢāđ‰āļ­āļĄāļāļąāļ™) + - āļĢāļ­āļ‡āļĢāļąāļš Conditional Flow (āđ€āļŠāđˆāļ™ āļ–āđ‰āļēāļĒāļ­āļ”āđ€āļ‡āļīāļ™ > X āđƒāļŦāđ‰āđ€āļžāļīāđˆāļĄāļœāļđāđ‰āļ­āļ™āļļāļĄāļąāļ•āļī) + +- 3.5.4 Workflow āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļī: +- āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļšāļāļĢāļ°āļšāļ§āļ™āļāļēāļĢāļ­āļ™āļļāļĄāļąāļ•āļīāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ°āđ€āļ›āđ‡āļ™āļĨāļģāļ”āļąāļš āđ€āļŠāđˆāļ™ āļŠāđˆāļ‡āļˆāļēāļ Originator -> Organization 1 -> Organization 2 -> Organization 3 āđāļĨāđ‰āļ§āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄ (āđ‚āļ”āļĒāļ–āđ‰āļē āļ­āļ‡āļāļĢāļ“āđŒāđƒāļ”āđƒāļ™ Workflow āđƒāļŦāđ‰āļŠāđˆāļ‡āļāļĨāļąāļš āļāđ‡āļŠāļēāļĄāļēāļĢāļ–āļŠāđˆāļ‡āļœāļĨāļāļĨāļąāļšāļ•āļēāļĄāļĨāļģāļ”āļąāļšāđ€āļ”āļīāļĄāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĢāļ­āđƒāļŦāđ‰āļ–āļķāļ‡ āļ­āļ‡āļāļĢāļ“āļ·āđƒāļ™āļĨāļģāļ”āļąāļšāļ–āļąāļ”āđ„āļ›) + +- 3.5.5 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ: - āļŠāļēāļĄāļēāļĢāļ–āļāļģāļŦāļ™āļ”āļ§āļąāļ™āđāļĨāđ‰āļ§āđ€āļŠāļĢāđ‡āļˆ (Deadline) āļŠāļģāļŦāļĢāļąāļšāļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡ āļ­āļ‡āļāļĢāļ“āđŒ āļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™ Workflow āđ„āļ”āđ‰ - āļĄāļĩāļĢāļ°āļšāļšāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ āđƒāļŦāđ‰āļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļšāļ‚āļ­āļ‡ āļ­āļ‡āļāļĢāļ“āđŒ āļ—āļĩāđˆāļ­āļĒāļđāđˆāđƒāļ™ Workflow āļ—āļĢāļēāļš āđ€āļĄāļ·āđˆāļ­āļĄāļĩ RFA āđƒāļŦāļĄāđˆ āļŦāļĢāļ·āļ­āļĄāļĩāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° @@ -235,11 +248,13 @@ ### **3.9. āļāļēāļĢāļˆāļąāļ”āđ€āļāđ‡āļšāđ„āļŸāļĨāđŒ (File Handling - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āđƒāļŦāļāđˆ)** - **3.9.1 Two-Phase Storage Strategy:** + 1. **Phase 1 (Upload):** āđ„āļŸāļĨāđŒāļ–āļđāļāļ­āļąāļ›āđ‚āļŦāļĨāļ”āđ€āļ‚āđ‰āļēāđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒ `temp/` āđāļĨāļ°āđ„āļ”āđ‰āļĢāļąāļš `temp_id` 2. **Phase 2 (Commit):** āđ€āļĄāļ·āđˆāļ­ User āļāļ” Submit āļŸāļ­āļĢāđŒāļĄāļŠāļģāđ€āļĢāđ‡āļˆ āļĢāļ°āļšāļšāļˆāļ°āļĒāđ‰āļēāļĒāđ„āļŸāļĨāđŒāļˆāļēāļ `temp/` āđ„āļ›āļĒāļąāļ‡ `permanent/{YYYY}/{MM}/` āđāļĨāļ°āļšāļąāļ™āļ—āļķāļāļĨāļ‡ Database āļ āļēāļĒāđƒāļ™ Transaction āđ€āļ”āļĩāļĒāļ§āļāļąāļ™ 3. **Cleanup:** āļĄāļĩ Cron Job āļĨāļšāđ„āļŸāļĨāđŒāđƒāļ™ `temp/` āļ—āļĩāđˆāļ„āđ‰āļēāļ‡āđ€āļāļīāļ™ 24 āļŠāļĄ. (Orphan Files) - **3.9.2 Security:** + - Virus Scan (ClamAV) āļāđˆāļ­āļ™āļĒāđ‰āļēāļĒāđ€āļ‚āđ‰āļē Permanent - Whitelist File Types: PDF, DWG, DOCX, XLSX, ZIP - Max Size: 50MB @@ -269,12 +284,14 @@ ### **3.11 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ JSON Details (JSON & Performance - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** - **3.11.1 āļ§āļąāļ•āļ–āļļāļ›āļĢāļ°āļŠāļ‡āļ„āđŒ** + - āļˆāļąāļ”āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāđāļšāļšāđ„āļ”āļ™āļēāļĄāļīāļāļ—āļĩāđˆāđ€āļ‰āļžāļēāļ°āđ€āļˆāļēāļ°āļˆāļ‡āļāļąāļšāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ—āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ - āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ‚āļĒāļēāļĒāļ•āļąāļ§āļ‚āļ­āļ‡āļĢāļ°āļšāļšāđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ database schema - āļˆāļąāļ”āļāļēāļĢ metadata āđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ›āļĢāļ°āļāļ­āļšāļŠāļģāļŦāļĢāļąāļš correspondence, routing, āđāļĨāļ° workflows - **3.11.2 āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON Schema** āļĢāļ°āļšāļšāļ•āđ‰āļ­āļ‡āļĄāļĩ predefined JSON schemas āļŠāļģāļŦāļĢāļąāļšāļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāļ•āđˆāļēāļ‡āđ†: + - **3.11.2.1 Correspondence Types** - **GENERIC**: āļ‚āđ‰āļ­āļĄāļđāļĨāļžāļ·āđ‰āļ™āļāļēāļ™āļŠāļģāļŦāļĢāļąāļšāđ€āļ­āļāļŠāļēāļĢāļ—āļąāđˆāļ§āđ„āļ› - **RFI**: āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ„āļģāļ–āļēāļĄāđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļēāļ‡āđ€āļ—āļ„āļ™āļīāļ„ @@ -282,10 +299,10 @@ - **TRANSMITTAL**: āļĢāļēāļĒāļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļŠāđˆāļ‡āļ•āđˆāļ­ - **LETTER**: āļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļ”āļŦāļĄāļēāļĒāļ—āļēāļ‡āļāļēāļĢ - **EMAIL**: āļ‚āđ‰āļ­āļĄāļđāļĨāļ­āļĩāđ€āļĄāļĨ - - **3.11.2.2 Routing Types** - - **ROUTING_TEMPLATE**: āļāļŽāđāļĨāļ°āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ - - **ROUTING_INSTANCE**: āļŠāļ–āļēāļ™āļ°āđāļĨāļ°āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ - - **ROUTING_ACTION**: āļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™ + - **3.11.2.2 Rworkflow Types** + - **workflow_definitions**: āļāļŽāđāļĨāļ°āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚āļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - **workflow_histories**: āļŠāļ–āļēāļ™āļ°āđāļĨāļ°āļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļŠāđˆāļ‡āļ•āđˆāļ­ + - **workflow_instances**: āļāļēāļĢāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™āđāļ•āđˆāļĨāļ°āļ‚āļąāđ‰āļ™āļ•āļ­āļ™ - **3.11.2.3 Audit Types** - **AUDIT_LOG**: āļ‚āđ‰āļ­āļĄāļđāļĨāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļš - **SECURITY_SCAN**: āļœāļĨāļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒ @@ -293,12 +310,14 @@ - **3.11.3 Virtual Columns (āđƒāļŦāļĄāđˆ):** āļŠāļģāļŦāļĢāļąāļš Field āđƒāļ™ JSON āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āđƒāļ™āļāļēāļĢāļ„āđ‰āļ™āļŦāļē (Search) āļŦāļĢāļ·āļ­āļˆāļąāļ”āđ€āļĢāļĩāļĒāļ‡ (Sort) āļšāđˆāļ­āļĒāđ† **āļ•āđ‰āļ­āļ‡āļŠāļĢāđ‰āļēāļ‡ Generated Column (Virtual Column)** āđƒāļ™ Database āđāļĨāļ°āļ—āļģ Index āđ„āļ§āđ‰ āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļžāļŠāļđāļ‡āļŠāļļāļ” - **3.11.4 Validation Rules** + - āļ•āđ‰āļ­āļ‡āļĄāļĩ JSON schema validation āļŠāļģāļŦāļĢāļąāļšāđāļ•āđˆāļĨāļ°āļ›āļĢāļ°āđ€āļ āļ— - āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš versioning āļ‚āļ­āļ‡ schema - āļ•āđ‰āļ­āļ‡āļĄāļĩ default values āļŠāļģāļŦāļĢāļąāļš field āļ—āļĩāđˆāđ„āļĄāđˆāļšāļąāļ‡āļ„āļąāļš - āļ•āđ‰āļ­āļ‡āļ•āļĢāļ§āļˆāļŠāļ­āļš data types āđāļĨāļ° format āđƒāļŦāđ‰āļ–āļđāļāļ•āđ‰āļ­āļ‡ - **3.11.5 Performance Requirements** + - JSON field āļ•āđ‰āļ­āļ‡āļĄāļĩāļ‚āļ™āļēāļ”āđ„āļĄāđˆāđ€āļāļīāļ™ 50KB - āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš indexing āļŠāļģāļŦāļĢāļąāļš field āļ—āļĩāđˆāđƒāļŠāđ‰āļ„āđ‰āļ™āļŦāļēāļšāđˆāļ­āļĒ - āļ•āđ‰āļ­āļ‡āļĄāļĩ compression āļŠāļģāļŦāļĢāļąāļš JSON āļ‚āļ™āļēāļ”āđƒāļŦāļāđˆ @@ -309,6 +328,7 @@ - āļ•āđ‰āļ­āļ‡ encrypt sensitive data āđƒāļ™ JSON fields ### **3.12 āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļžāļīāđ€āļĻāļĐ** + - **āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļĢāļ°āļ”āļąāļšāļŠāļđāļ‡ (Global) āļŦāļĢāļ·āļ­āļœāļđāđ‰āđ„āļ”āđ‰āļĢāļąāļšāļ­āļ™āļļāļāļēāļ•āđ€āļ›āđ‡āļ™āļāļĢāļ“āļĩāļžāļīāđ€āļĻāļĐ** - āļŠāļēāļĄāļēāļĢāļ–āđ€āļĨāļ·āļ­āļ **āļŠāļĢāđ‰āļēāļ‡āđƒāļ™āļ™āļēāļĄāļ­āļ‡āļ„āđŒāļāļĢ (Create on behalf of)** āđ„āļ”āđ‰ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ­āļ­āļāđ€āļĨāļ‚āļ—āļĩāđˆāđ€āļ­āļāļŠāļēāļĢ (Running Number) āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ­āļ·āđˆāļ™āđ„āļ”āđ‰āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āļĨāđ‡āļ­āļāļ­āļīāļ™āđƒāļŦāļĄāđˆ - āļŠāļēāļĄāļēāļĢāļ–āļ—āļģāļ‡āļēāļ™āđāļ—āļ™āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™āļ­āļ·āđˆāļ™āđ„āļ”āđ‰ Routing & Workflow āļ‚āļ­āļ‡ Correspondence, RFA, Circulation Sheet @@ -336,15 +356,15 @@ ### **4.3. āļāļēāļĢāļāļģāļŦāļ™āļ”āļšāļ—āļšāļēāļ— (Roles) āđāļĨāļ°āļ‚āļ­āļšāđ€āļ‚āļ• (Scope)** -| āļšāļ—āļšāļēāļ— (Role) | āļ‚āļ­āļšāđ€āļ‚āļ• (Scope) | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ | āļŠāļīāļ—āļ˜āļīāđŒāļŦāļĨāļąāļ (Key Permissions) | -| :------------------- | :------------- | :------------------ | :----------------------------------------------------------------------------- | -| **Superadmin** | Global | āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļŠāļđāļ‡āļŠāļļāļ” | āļ—āļģāļ—āļļāļāļ­āļĒāđˆāļēāļ‡āđƒāļ™āļĢāļ°āļšāļš, āļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļĢāļ°āļ”āļąāļš Global | -| **Org Admin** | Organization | āļœāļđāđ‰āļ”āļđāđāļĨāļ­āļ‡āļ„āđŒāļāļĢ | āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļšāļ—āļšāļēāļ—/āļŠāļīāļ—āļ˜āļīāđŒāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | -| **Document Control** | Organization | āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚/āļĨāļšāđ€āļ­āļāļŠāļēāļĢ, āļāļģāļŦāļ™āļ”āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ | -| **Editor** | Organization | āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒ | -| **Viewer** | Organization | āļœāļđāđ‰āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ | -| **Project Manager** | Project | āļœāļđāđ‰āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ (āđ€āļžāļīāđˆāļĄ/āļĨāļš/āļĄāļ­āļšāļšāļ—āļšāļēāļ—), āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ | -| **Contract Admin** | Contract | āļœāļđāđ‰āļ”āļđāđāļĨāļŠāļąāļāļāļē | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āļŠāļąāļāļāļē, āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāđ€āļ‰āļžāļēāļ°āļŠāļąāļāļāļē (āļ–āđ‰āļēāļĄāļĩ), āļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļŠāļąāļāļāļē | +| āļšāļ—āļšāļēāļ— (Role) | āļ‚āļ­āļšāđ€āļ‚āļ• (Scope) | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒ | āļŠāļīāļ—āļ˜āļīāđŒāļŦāļĨāļąāļ (Key Permissions) | +| :------------------- | :------------- | :---------------------- | :------------------------------------------------------------------------------------- | +| **Superadmin** | Global | āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļŠāļđāļ‡āļŠāļļāļ” | āļ—āļģāļ—āļļāļāļ­āļĒāđˆāļēāļ‡āđƒāļ™āļĢāļ°āļšāļš, āļˆāļąāļ”āļāļēāļĢāļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāļĢāļ°āļ”āļąāļš Global | +| **Org Admin** | Organization | āļœāļđāđ‰āļ”āļđāđāļĨāļ­āļ‡āļ„āđŒāļāļĢ | āļˆāļąāļ”āļāļēāļĢāļœāļđāđ‰āđƒāļŠāđ‰āđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļˆāļąāļ”āļāļēāļĢāļšāļ—āļšāļēāļ—/āļŠāļīāļ—āļ˜āļīāđŒāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | +| **Document Control** | Organization | āļ„āļ§āļšāļ„āļļāļĄāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚/āļĨāļšāđ€āļ­āļāļŠāļēāļĢ, āļāļģāļŦāļ™āļ”āļŠāļīāļ—āļ˜āļīāđŒāđ€āļ­āļāļŠāļēāļĢāļ āļēāļĒāđƒāļ™āļ­āļ‡āļ„āđŒāļāļĢ | +| **Editor** | Organization | āļœāļđāđ‰āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āđ€āļžāļīāđˆāļĄ/āđāļāđ‰āđ„āļ‚āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ„āļ”āđ‰āļĢāļąāļšāļĄāļ­āļšāļŦāļĄāļēāļĒ | +| **Viewer** | Organization | āļœāļđāđ‰āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ‡āļ­āļ‡āļ„āđŒāļāļĢ | āļ”āļđāđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāđ€āļ‚āđ‰āļēāļ–āļķāļ‡ | +| **Project Manager** | Project | āļœāļđāđ‰āļˆāļąāļ”āļāļēāļĢāđ‚āļ„āļĢāļ‡āļāļēāļĢ | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ (āđ€āļžāļīāđˆāļĄ/āļĨāļš/āļĄāļ­āļšāļšāļ—āļšāļēāļ—), āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļŠāļąāļāļāļēāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ, āļ”āļđāļĢāļēāļĒāļ‡āļēāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ | +| **Contract Admin** | Contract | āļœāļđāđ‰āļ”āļđāđāļĨāļŠāļąāļāļāļē | āļˆāļąāļ”āļāļēāļĢāļŠāļĄāļēāļŠāļīāļāđƒāļ™āļŠāļąāļāļāļē, āļŠāļĢāđ‰āļēāļ‡/āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļāđ€āļ‰āļžāļēāļ°āļŠāļąāļāļāļē (āļ–āđ‰āļēāļĄāļĩ), āļ­āļ™āļļāļĄāļąāļ•āļīāđ€āļ­āļāļŠāļēāļĢāđƒāļ™āļŠāļąāļāļāļē | ### **4.4. Token Management (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** @@ -372,14 +392,14 @@ ### **4.6. āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ (Master Data Management) āļ—āļĩāđˆāđāļšāđˆāļ‡āļ•āļēāļĄāļĢāļ°āļ”āļąāļš** -| āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ | āļœāļđāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢ | āļĢāļ°āļ”āļąāļš | -| :---------------------------------- | :------------------------------ | :------------------------------ | -| āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (Correspondence, RFA) | **Superadmin** | Global | -| āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ (Draft, Approved, etc.) | **Superadmin** | Global | -| āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāđāļšāļš (Shop Drawing) | **Project Manager** | Project (āļŠāļĢāđ‰āļēāļ‡āđƒāļŦāļĄāđˆāđ„āļ”āđ‰āļ āļēāļĒāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ) | -| Tags | **Org Admin / Project Manager** | Organization / Project | -| āļšāļ—āļšāļēāļ—āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization | -| Document Numbering Formats | **Superadmin / Admin** | Global / Organization | +| āļ‚āđ‰āļ­āļĄāļđāļĨāļŦāļĨāļąāļ | āļœāļđāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļˆāļąāļ”āļāļēāļĢ | āļĢāļ°āļ”āļąāļš | +| :---------------------------------- | :------------------------------ | :--------------------------------- | +| āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢ (Correspondence, RFA) | **Superadmin** | Global | +| āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ (Draft, Approved, etc.) | **Superadmin** | Global | +| āļŦāļĄāļ§āļ”āļŦāļĄāļđāđˆāđāļšāļš (Shop Drawing) | **Project Manager** | Project (āļŠāļĢāđ‰āļēāļ‡āđƒāļŦāļĄāđˆāđ„āļ”āđ‰āļ āļēāļĒāđƒāļ™āđ‚āļ„āļĢāļ‡āļāļēāļĢ) | +| Tags | **Org Admin / Project Manager** | Organization / Project | +| āļšāļ—āļšāļēāļ—āđāļĨāļ°āļŠāļīāļ—āļ˜āļīāđŒ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization | +| Document Numbering Formats | **Superadmin / Admin** | Global / Organization | ## **ðŸ‘Ĩ 5. āļ‚āđ‰āļ­āļāļģāļŦāļ™āļ”āļ”āđ‰āļēāļ™āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User Interface & Experience)** @@ -434,6 +454,7 @@ ### **6.1. āļāļēāļĢāļšāļąāļ™āļ—āļķāļāļāļēāļĢāļāļĢāļ°āļ—āļģ (Audit Log):** āļ—āļļāļāļāļēāļĢāļāļĢāļ°āļ—āļģāļ—āļĩāđˆāļŠāļģāļ„āļąāļāļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰ (āļŠāļĢāđ‰āļēāļ‡, āđāļāđ‰āđ„āļ‚, āļĨāļš, āļŠāđˆāļ‡) āļˆāļ°āļ–āļđāļāļšāļąāļ™āļ—āļķāļāđ„āļ§āđ‰āđƒāļ™ audit_logs āđ€āļžāļ·āđˆāļ­āļāļēāļĢāļ•āļĢāļ§āļˆāļŠāļ­āļšāļĒāđ‰āļ­āļ™āļŦāļĨāļąāļ‡ - **6.1.1 āļ‚āļ­āļšāđ€āļ‚āļ•āļāļēāļĢāļšāļąāļ™āļ—āļķāļ Audit Log:** + - āļ—āļļāļāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡/āđāļāđ‰āđ„āļ‚/āļĨāļš āļ‚āđ‰āļ­āļĄāļđāļĨāļŠāļģāļ„āļąāļ (correspondences, RFAs, drawings, users, permissions) - āļ—āļļāļāļāļēāļĢāđ€āļ‚āđ‰āļēāļ–āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ sensitive (user data, financial information) - āļ—āļļāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° workflow (status transitions) @@ -465,6 +486,7 @@ ### **6.5. āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž (Performance):** āļĄāļĩāļāļēāļĢāđƒāļŠāđ‰ Caching āļāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰āļšāđˆāļ­āļĒ āđāļĨāļ°āđƒāļŠāđ‰ Pagination āđƒāļ™āļ•āļēāļĢāļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļžāļ·āđˆāļ­āļˆāļąāļ”āļāļēāļĢāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļģāļ™āļ§āļ™āļĄāļēāļ - **6.5.1 āļ•āļąāļ§āļŠāļĩāđ‰āļ§āļąāļ”āļ›āļĢāļ°āļŠāļīāļ—āļ˜āļīāļ āļēāļž:** + - **API Response Time:** < 200ms (90th percentile) āļŠāļģāļŦāļĢāļąāļš operation āļ—āļąāđˆāļ§āđ„āļ› - **Search Query Performance:** < 500ms āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļēāļ‚āļąāđ‰āļ™āļŠāļđāļ‡ - **File Upload Performance:** < 30 seconds āļŠāļģāļŦāļĢāļąāļšāđ„āļŸāļĨāđŒāļ‚āļ™āļēāļ” 50MB @@ -492,6 +514,7 @@ - āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Secret (āđ€āļŠāđˆāļ™ āļĢāļŦāļąāļŠāļœāđˆāļēāļ™ DB, JWT Secret) āļˆāļ°āļ•āđ‰āļ­āļ‡āļ—āļģāļœāđˆāļēāļ™ Environment Variable āļ‚āļ­āļ‡ Docker āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļ›āļĨāļ­āļ”āļ āļąāļĒāļŠāļđāļ‡āļŠāļļāļ” - **6.6.1 Rate Limiting Strategy:** + - **Anonymous Endpoints:** 100 requests/hour āļ•āđˆāļ­ IP address - **Authenticated Endpoints:** - Viewer: 500 requests/hour @@ -505,12 +528,14 @@ - āļ•āđ‰āļ­āļ‡āļšāļąāļ™āļ—āļķāļ log āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢ trigger rate limiting - **6.6.2 Error Handling āđāļĨāļ° Resilience:** + - āļ•āđ‰āļ­āļ‡āļĄāļĩ circuit breaker pattern āļŠāļģāļŦāļĢāļąāļš external service calls - āļ•āđ‰āļ­āļ‡āļĄāļĩ retry mechanism āļ”āđ‰āļ§āļĒ exponential backoff - āļ•āđ‰āļ­āļ‡āļĄāļĩ graceful degradation āđ€āļĄāļ·āđˆāļ­āļšāļĢāļīāļāļēāļĢāļ āļēāļĒāļ™āļ­āļāļĨāđ‰āļĄāđ€āļŦāļĨāļ§ - Error messages āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāđ€āļ›āļīāļ”āđ€āļœāļĒāļ‚āđ‰āļ­āļĄāļđāļĨ sensitive - **6.6.3 Input Validation:** + - āļ•āđ‰āļ­āļ‡āļĄāļĩ input validation āļ—āļąāđ‰āļ‡āļāļąāđˆāļ‡ client āđāļĨāļ° server (defense in depth) - āļ•āđ‰āļ­āļ‡āļ›āđ‰āļ­āļ‡āļāļąāļ™ OWASP Top 10 vulnerabilities: - SQL Injection (āđƒāļŠāđ‰ parameterized queries āļœāđˆāļēāļ™ ORM) @@ -564,6 +589,7 @@ ### **6.8. āļāļĨāļĒāļļāļ—āļ˜āđŒāļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (Notification Strategy - āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡):** - **6.8.1 āļĢāļ°āļšāļšāļˆāļ°āļŠāđˆāļ‡āļāļēāļĢāđāļˆāđ‰āļ‡āđ€āļ•āļ·āļ­āļ™ (āļœāđˆāļēāļ™ Email āļŦāļĢāļ·āļ­ Line [cite: 2.7]) āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢāļāļĢāļ°āļ—āļģāļ—āļĩāđˆāļŠāļģāļ„āļąāļ** āļ”āļąāļ‡āļ™āļĩāđ‰: + 1. āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđ€āļ­āļāļŠāļēāļĢāđƒāļŦāļĄāđˆ (Correspondence, RFA) āļ–āļđāļāļŠāđˆāļ‡āļĄāļēāļ–āļķāļ‡āļ­āļ‡āļ„āđŒāļāļĢāļ“āđŒāļ‚āļ­āļ‡āđ€āļĢāļē 2. āđ€āļĄāļ·āđˆāļ­āļĄāļĩāđƒāļšāđ€āļ§āļĩāļĒāļ™ (Circulation) āđƒāļŦāļĄāđˆ āļĄāļ­āļšāļŦāļĄāļēāļĒāļ‡āļēāļ™āļĄāļēāļ—āļĩāđˆāđ€āļĢāļē 3. (āļ—āļēāļ‡āđ€āļĨāļ·āļ­āļ) āđ€āļĄāļ·āđˆāļ­āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāđ€āļĢāļēāļŠāđˆāļ‡āđ„āļ› āļ–āļđāļāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢ (āđ€āļŠāđˆāļ™ āļ­āļ™āļļāļĄāļąāļ•āļī/āļ›āļāļīāđ€āļŠāļ˜) diff --git a/1_FullStackJS_V1_4_4.md b/docs/Markdown/1_FullStackJS_V1_4_4.md similarity index 100% rename from 1_FullStackJS_V1_4_4.md rename to docs/Markdown/1_FullStackJS_V1_4_4.md diff --git a/docs/Markdown/2_Backend_Plan_V1_4_4.Phase6A.md b/docs/Markdown/2_Backend_Plan_V1_4_4.Phase6A.md new file mode 100644 index 0000000..6e75a61 --- /dev/null +++ b/docs/Markdown/2_Backend_Plan_V1_4_4.Phase6A.md @@ -0,0 +1,397 @@ +# “Phase 6A + Technical Design Document : Workflow DSL (Mini-Language)”** +āļ­āļ­āļāđāļšāļšāļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļš Workflow Engine āļāļĨāļēāļ‡āļ‚āļ­āļ‡āđ‚āļ„āļĢāļ‡āļāļēāļĢ +**āđ„āļĄāđˆāļĄāļĩāđ‚āļ„āđ‰āļ”āļœāļđāļāļāļąāļš Framework** āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–āļ™āļģāđ„āļ› Implement āđƒāļ™ NestJS āļŦāļĢāļ·āļ­ Microservice āđƒāļ” āđ† āđ„āļ”āđ‰ + +--- + +## 📌 **Phase 6A – Workflow DSL Implementation Plan** + +### ðŸŽŊ āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒāļ‚āļ­āļ‡ Phase 6A + +āđƒāļ™ Phase āļ™āļĩāđ‰ āļˆāļ°āđ€āļĢāļīāđˆāļĄāļŠāļĢāđ‰āļēāļ‡ â€œWorkflow DSL (Domain-Specific Language)” āļŠāļģāļŦāļĢāļąāļšāļ™āļīāļĒāļēāļĄāļāļŽāļāļēāļĢāđ€āļ”āļīāļ™āļ‡āļēāļ™ (Workflow Transition Rules) āđƒāļŦāđ‰āļŠāļēāļĄāļēāļĢāļ–: + +* āđāļĒāļ **Business Workflow Logic** āļ­āļ­āļāļˆāļēāļ Source Code +* āđāļāđ‰āđ„āļ‚āļāļŽ Workflow āđ„āļ”āđ‰āđ‚āļ”āļĒ **āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđāļāđ‰āđ‚āļ„āđ‰āļ”āđāļĨāļ°āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Deploy āđƒāļŦāļĄāđˆ** +* āļĢāļ­āļ‡āļĢāļąāļš Document āļŦāļĨāļēāļĒāļ›āļĢāļ°āđ€āļ āļ— āđ€āļŠāđˆāļ™ + + * Correspondence + * RFA + * Internal Circulation + * Document Transmittal +* āļĢāļ­āļ‡āļĢāļąāļš Multi-step routing, skip, reject, rollback, parallel assignments +* āļŠāļēāļĄāļēāļĢāļ–āļ™āļģāđ„āļ›āđƒāļŠāđ‰āļ‡āļēāļ™āļ—āļąāđ‰āļ‡āđƒāļ™ + + * Backend (NestJS) + * Frontend (UI Driven) + * External Microservices + +--- + +### 📅 āļĢāļ°āļĒāļ°āđ€āļ§āļĨāļē + +**1 āļŠāļąāļ›āļ”āļēāļŦāđŒ (āļŦāļĨāļąāļ‡ Phase 6.5)** + +--- + +### ðŸ§Đ Output āļ‚āļ­āļ‡ Phase 6A + +* DSL Specification (Grammar) +* JSON Schema for Workflow Definition +* Workflow Rule Interpreter (Parser + Executor) +* Validation Engine (Compile-time and Runtime) +* Storage (DB Table / Registry) +* Execution API: + +| Action | Description | +| -------------------------------- | ------------------------------- | +| compile() | āļ•āļĢāļ§āļˆ DSL → āļŠāļĢāđ‰āļēāļ‡ Execution Tree | +| evaluate(state, action, context) | āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāđāļĨāļ°āļŠāđˆāļ‡āļŠāļ–āļēāļ™āļ°āđƒāļŦāļĄāđˆ | +| preview(state) | āļ„āļģāļ™āļ§āļ“ Next Possible Transitions | +| validate() | āļ•āļĢāļ§āļˆāļ§āđˆāļē DSL āļ–āļđāļāļ•āđ‰āļ­āļ‡ | + +--- + +## 📘 **Technical Specification – Workflow DSL** + +--- + +### 1ïļâƒĢ Requirements + +#### Functional Requirements + +* āļ™āļīāļĒāļēāļĄ Workflow āđ€āļ›āđ‡āļ™āļ āļēāļĐāļēāļ„āļĨāđ‰āļēāļĒ State Machine +* āđāļ•āđˆāļĨāļ°āđ€āļ­āļāļŠāļēāļĢāļĄāļĩ **State, Actions, Entry/Exit Events** +* āļŠāļēāļĄāļēāļĢāļ–āļĄāļĩ: + + * Required approvals + * Conditional transition + * Auto-transition + * Parallel approval + * Return/rollback + +#### +* Running time: < 20ms āļ•āđˆāļ­āļ„āļģāļŠāļąāđˆāļ‡ +* Hot reload āđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Compile āđƒāļŦāļĄāđˆāļ—āļąāđ‰āļ‡ Backend +* DSL āļ•āđ‰āļ­āļ‡ Debug āđ„āļ”āđ‰āļ‡āđˆāļēāļĒ +* āļ•āđ‰āļ­āļ‡ Versioned +* āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš Audit 100% + +--- + +### 2ïļâƒĢ DSL Format (Human Friendly) + +```yaml +workflow: RFA +version: 1.0 + +states: + - name: DRAFT + initial: true + on: + SUBMIT: + to: IN_REVIEW + require: + - role: ENGINEER + events: + - notify: reviewer + + - name: IN_REVIEW + on: + APPROVE: + to: APPROVED + REJECT: + to: DRAFT + events: + - notify: creator + + - name: APPROVED + terminal: true +``` + +--- + +### 3ïļâƒĢ Compiled Execution Model (Normalized JSON) + +```json +{ + "workflow": "RFA", + "version": "1.0", + "states": { + "DRAFT": { + "initial": true, + "transitions": { + "SUBMIT": { + "to": "IN_REVIEW", + "requirements": [ + { "role": "ENGINEER" } + ], + "events": [ + { "type": "notify", "target": "reviewer" } + ] + } + } + }, + "IN_REVIEW": { + "transitions": { + "APPROVE": { "to": "APPROVED" }, + "REJECT": { + "to": "DRAFT", + "events": [ + { "type": "notify", "target": "creator" } + ] + } + } + }, + "APPROVED": { + "terminal": true + } + } +} +``` + +Frontend āđāļĨāļ° Backend āļŠāļēāļĄāļēāļĢāļ–āđāļŠāļĢāđŒ JSON āļ™āļĩāđ‰āļĢāđˆāļ§āļĄāļāļąāļ™āđ„āļ”āđ‰ + +--- + +### 4ïļâƒĢ DSL Grammar Definition (EBNF) + +```ebnf +workflow = "workflow" ":" identifier ; +version = "version" ":" number ; + +states = "states:" state_list ; +state_list = { state } ; + +state = "- name:" identifier + [ "initial:" boolean ] + [ "terminal:" boolean ] + [ "on:" transition_list ] ; + +transition_list = { transition } ; + +transition = action ":" + indent "to:" identifier + [ indent "require:" requirements ] + [ indent "events:" event_list ] ; + +requirements = "- role:" identifier | "- user:" identifier ; + +event_list = { event } ; +event = "- notify:" identifier ; +``` + +--- + +### 5ïļâƒĢ Validation Rules (Compile-Time) + +#### 5.1 State Rules + +* āļ•āđ‰āļ­āļ‡āļĄāļĩāļ­āļĒāđˆāļēāļ‡āļ™āđ‰āļ­āļĒ 1 state āļ—āļĩāđˆ `initial: true` +* āļŦāļēāļ `terminal: true` → āļ•āđ‰āļ­āļ‡āđ„āļĄāđˆāļĄāļĩ transition āļ•āđˆāļ­āđ„āļ› + +#### 5.2 Transition Rules + +āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļē: + +* `to` āļŠāļĩāđ‰āđ„āļ›āļĒāļąāļ‡ state āļ—āļĩāđˆāļĄāļĩāļ­āļĒāļđāđˆ +* `require.role` āļ•āđ‰āļ­āļ‡āđ€āļ›āđ‡āļ™ role āļ—āļĩāđˆāļĢāļ°āļšāļšāļĢāļđāđ‰āļˆāļąāļ +* Action name āļ•āđ‰āļ­āļ‡āđ€āļ›āđ‡āļ™ **UPPER_CASE** + +#### 5.3 Version Safety + +* āļ—āļļāļāļŠāļļāļ” Workflow DSL āļ•āđ‰āļ­āļ‡āļ‚āļķāđ‰āļ™āļāļąāļš version +* āđāļāđ‰āđ„āļ‚āļ•āđ‰āļ­āļ‡āļŠāļĢāđ‰āļēāļ‡ version āđƒāļŦāļĄāđˆ +* āđ„āļĄāđˆ overwrite version āđ€āļāđˆāļē +* “Document āļ—āļĩāđˆāļāļģāļĨāļąāļ‡āļ­āļĒāļđāđˆāđƒāļ™ step āđ€āļ”āļīāļĄāļĒāļąāļ‡āļ•āđ‰āļ­āļ‡āđƒāļŠāđ‰āļāļŽāđ€āļ”āļīāļĄāđ„āļ”āđ‰â€ + +--- + +### 6ïļâƒĢ Runtime Validation Rules + +āđ€āļĄāļ·āđˆāļ­ execute(action): + +``` +input: current_state, action, context + +1) āļ•āļĢāļ§āļˆāļ§āđˆāļē state āļĄāļĩ transition "action" +2) āļ•āļĢāļ§āļˆāļ§āđˆāļēāļœāļđāđ‰āđƒāļŠāđ‰āļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒāļ•āļēāļĄ require[] +3) Compute next state +4) Execute events[] +5) Return next_state +``` + +--- + +### 7ïļâƒĢ Context Model + +```ts +interface WorkflowContext { + userId: string; + roles: string[]; + documentId: string; + now: Date; + payload?: any; +} +``` + +--- + +### 8ïļâƒĢ Execution API (Abstract) + +```ts +class WorkflowEngine { + + load(dsl: string | object): CompiledWorkflow + + compile(dsl: string | object): CompiledWorkflow + + evaluate(state: string, action: string, context: WorkflowContext): EvalResult + + getAvailableActions(state: string, context: WorkflowContext): string[] +} +``` + +--- + +### 9ïļâƒĢ Interpreter Execution Flow + +```mermaid +flowchart TD + A[Receive Action] --> B[Load Compiled Workflow] + B --> C[Check allowed actions] + C -->|Invalid| E[Return Error] + C --> D[Evaluate Requirements] + D --> F[Transition to Next State] + F --> G[Run Events] + G --> H[Return Success] +``` + +--- + +### 🔟 Events System + +āļĢāļ­āļ‡āļĢāļąāļš event āļŦāļĨāļēāļĒāļ›āļĢāļ°āđ€āļ āļ—: + +| event.type | āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡ | +| ----------- | ------------------------- | +| notify | āļŠāđˆāļ‡ Email/Line | +| assign | āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļœāļđāđ‰āļĢāļąāļšāļœāļīāļ”āļŠāļ­āļš | +| webhook | āļĒāļīāļ‡ Webhook āđ„āļ›āļĒāļąāļ‡āļĢāļ°āļšāļšāļ­āļ·āđˆāļ™ | +| auto_action | āļ—āļģ action āļ‹āđ‰āļģāđ‚āļ”āļĒāļ­āļąāļ•āđ‚āļ™āļĄāļąāļ•āļī | + +--- + +### 11ïļâƒĢ Database Schema + +#### Table: `workflow_definition` + +| Field | Type | Description | +| ------------- | ----------- | --------------------- | +| id | UUID | PK | +| workflow_code | varchar(50) | āđ€āļŠāđˆāļ™ `RFA`, `CORR` | +| version | int | Version number | +| dsl | JSON | YAML/JSON DSL āđ€āļāđ‡āļšāļ”āļīāļš | +| compiled | JSON | JSON āļ—āļĩāđˆ Compile āđāļĨāđ‰āļ§ | +| created_at | timestamp | | +| is_active | boolean | āđƒāļŠāđ‰āļ­āļĒāļđāđˆāļŦāļĢāļ·āļ­āđ„āļĄāđˆ | + +#### Table: `workflow_history` + +āđ€āļāđ‡āļš audit āđāļšāļš immutable append-only + +| Field | Description | +| ----------- | --------------- | +| workflow | Document Type | +| document_id | āđ€āļ­āļāļŠāļēāļĢāđ„āļŦāļ™ | +| from_state | āđ€āļ”āļīāļĄ | +| to_state | āđƒāļŦāļĄāđˆ | +| action | āļ„āļģāļŠāļąāđˆāļ‡ | +| user | āđƒāļ„āļĢāđ€āļ›āđ‡āļ™āļ„āļ™āļ—āļģ | +| timestamp | āđ€āļ§āļĨāļē | +| metadata | āđ€āļŦāļ•āļļāļāļēāļĢāļ“āđŒāļ­āļ·āđˆāļ™ āđ† | + +--- + +### 12ïļâƒĢ Error Codes + +| Code | Meaning | +| ----------------------- | ---------------------- | +| WF_NO_TRANSITION | Action āļ™āļĩāđ‰āđ„āļĄāđˆāļ–āļđāļāļ•āđ‰āļ­āļ‡ | +| WF_RESTRICTED | User āđ„āļĄāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ | +| WF_MISSING_REQUIREMENTS | āđ„āļĄāđˆāļœāđˆāļēāļ™āđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ | +| WF_STATE_NOT_FOUND | āđ„āļĄāđˆāļĄāļĩ state āļ—āļĩāđˆāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ | +| WF_SYNTAX_ERROR | DSL āļœāļīāļ”āļĢāļđāļ›āđāļšāļš | + +--- + +### 13ïļâƒĢ Testing Strategy + +#### Unit Tests + +* Parse DSL → JSON +* Invalid syntax → throw error +* Invalid transitions → throw error + +#### Integration Tests + +* Evaluate() āļœāđˆāļēāļ™ 20+ cases +* RFA āļĒāđ‰āļ­āļ™āļāļĨāļąāļš +* Approve chain +* Parallel review + +#### Load Tests + +* 1,000 documents running workflow +* Evaluate < 20ms āļ•āđˆāļ­ action + +--- + +### 14ïļâƒĢ Deployment Strategy + +#### Hot Reload Options + +* DSL stored in DB +* Cache in Redis +* Touched timestamp triggers: + +``` +invalidate cache → recompile +``` + +#### No downtime required + +--- + +### 15ïļâƒĢ Microservice-Ready + +DSL Engine āđāļĒāļāđ€āļ›āđ‡āļ™: + +* `workflow-engine-core` → Pure JS library +* `workflow-service` → NestJS module +* API public: + +``` +POST /workflow/evaluate +GET /workflow/preview +POST /workflow/compile +``` + +āļ āļēāļĒāļŦāļĨāļąāļ‡āļŠāļēāļĄāļēāļĢāļ–āļ™āļģāđ„āļ›āļ§āļēāļ‡āļšāļ™: + +* Kubernetes +* Worker Node +* API Gateway + +--- + +## 🎉 Summary + +### āļŠāļīāđˆāļ‡āļ—āļĩāđˆ Phase 6A āđ€āļžāļīāđˆāļĄāđ€āļ‚āđ‰āļēāđƒāļ™āļĢāļ°āļšāļš + +✔ Workflow DSL āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āđ„āļ”āđ‰āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡ Deploy +✔ Parser + Validator + Runtime Interpreter +✔ Database storage + Versioning +✔ Execution API āļŠāļģāļŦāļĢāļąāļš Backend āđāļĨāļ° Frontend +✔ āļĢāļ­āļ‡āļĢāļąāļš Business Workflow āļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļ—āļąāđ‰āļ‡āļŦāļĄāļ” +✔ Ready āļŠāļģāļŦāļĢāļąāļš Microservice model āđƒāļ™āļ­āļ™āļēāļ„āļ• + diff --git a/docs/Markdown/2_Backend_Plan_V1_4_4.Phase_Addition.md b/docs/Markdown/2_Backend_Plan_V1_4_4.Phase_Addition.md new file mode 100644 index 0000000..ceb66eb --- /dev/null +++ b/docs/Markdown/2_Backend_Plan_V1_4_4.Phase_Addition.md @@ -0,0 +1,709 @@ +# ðŸŽŊ **Admin Panel Comprehensive Analysis āļŠāļģāļŦāļĢāļąāļš LCBP3-DMS v1.4.4** + +āļˆāļēāļāļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒ Requirements āđāļĨāļ° Technical Design āļ—āļąāđ‰āļ‡āļŦāļĄāļ” āļ‚āļ­āļ™āļģāđ€āļŠāļ™āļ­ Admin Panel āļ—āļĩāđˆāļŠāļĄāļšāļđāļĢāļ“āđŒāļŠāļģāļŦāļĢāļąāļšāļĢāļ°āļšāļš LCBP3-DMS + +## 📊 **āļ āļēāļžāļĢāļ§āļĄ Admin Panel Structure** + +### **Role-Based Access Levels** + +- **Superadmin:** Full system access +- **Org Admin:** Organization-level management +- **Project Manager:** Project-level administration +- **Contract Admin:** Contract-specific management + +--- + +## 🛠ïļ **1. System Administration** + +### **1.1 Global System Configuration** + +```typescript +interface SystemConfig { + maintenance_mode: boolean; + session_timeout: number; + password_policy: { + min_length: number; + require_uppercase: boolean; + require_numbers: boolean; + require_special_chars: boolean; + expiry_days: number; + }; + file_upload: { + max_size_mb: number; + allowed_types: string[]; + virus_scan_enabled: boolean; + }; + notification: { + email_enabled: boolean; + line_enabled: boolean; + digest_frequency: number; // minutes + }; +} +``` + +**Features:** + +- [ ] Toggle Maintenance Mode +- [ ] Configure Security Policies +- [ ] Manage File Upload Settings +- [ ] Configure Notification Channels +- [ ] System Health Monitoring Dashboard + +### **1.2 Audit & Monitoring Center** + +```typescript +interface AuditDashboard { + security_metrics: { + failed_logins: number; + file_scans: number; + virus_detections: number; + permission_changes: number; + }; + user_activity: AuditLog[]; + system_performance: PerformanceMetrics; + recent_errors: SystemError[]; +} +``` + +**Features:** + +- [ ] Real-time Activity Monitoring +- [ ] Security Incident Reporting +- [ ] Performance Metrics Dashboard +- [ ] Error Log Viewer +- [ ] Export Audit Reports + +--- + +## ðŸ‘Ĩ **2. User & Organization Management** + +### **2.1 User Management** + +```typescript +interface UserManagement { + user_list: User[]; + bulk_operations: { + import_users: FileUpload; + export_users: CSVExport; + bulk_assign_roles: RoleAssignment[]; + }; + user_lifecycle: { + onboarding_workflow: WorkflowConfig; + offboarding_checklist: ChecklistItem[]; + }; +} +``` + +**Features:** + +- [ ] User CRUD Operations +- [ ] Bulk User Import/Export +- [ ] User Activity Tracking +- [ ] Password Reset Management +- [ ] User Session Management + +### **2.2 Organization Hierarchy** + +```typescript +interface OrganizationManagement { + organization_tree: Organization[]; + department_structure: Department[]; + contact_persons: Contact[]; + organization_settings: { + document_retention_policy: RetentionPolicy; + notification_preferences: NotificationConfig; + }; +} +``` + +**Features:** + +- [ ] Organization CRUD +- [ ] Department Structure Management +- [ ] Contact Person Assignment +- [ ] Organization-specific Policies + +### **2.3 Advanced RBAC Management** + +```typescript +interface RBACManagement { + role_definitions: Role[]; + permission_matrix: Permission[]; + assignment_rules: { + automatic_assignments: AutoAssignmentRule[]; + conditional_access: ConditionalRule[]; + }; + permission_audit: { + permission_usage: UsageStats[]; + conflict_detection: Conflict[]; + }; +} +``` + +**Features:** + +- [ ] Custom Role Creation +- [ ] Granular Permission Management +- [ ] Automatic Role Assignment Rules +- [ ] Permission Conflict Detection +- [ ] Role Usage Analytics + +--- + +## 📁 **3. Project & Contract Administration** + +### **3.1 Project Portfolio Management** + +```typescript +interface ProjectManagement { + project_dashboard: ProjectOverview[]; + project_phases: Phase[]; + milestone_tracking: Milestone[]; + resource_allocation: Resource[]; + project_analytics: ProjectMetrics; +} +``` + +**Features:** + +- [ ] Project Lifecycle Management +- [ ] Phase and Milestone Tracking +- [ ] Resource Allocation +- [ ] Project Performance Analytics +- [ ] Project Documentation Repository + +### **3.2 Contract Administration** + +```typescript +interface ContractManagement { + contract_register: Contract[]; + party_management: ContractParty[]; + amendment_tracking: Amendment[]; + compliance_monitoring: ComplianceCheck[]; + financial_tracking: FinancialMetrics; +} +``` + +**Features:** + +- [ ] Contract CRUD Operations +- [ ] Contract Party Management +- [ ] Amendment History +- [ ] Compliance Monitoring +- [ ] Financial Tracking + +### **3.3 Project-Organization Mapping** + +```typescript +interface ProjectOrgMapping { + project_assignments: ProjectAssignment[]; + access_control: AccessRule[]; + collaboration_settings: CollaborationConfig; +} +``` + +**Features:** + +- [ ] Organization Assignment to Projects +- [ ] Cross-Organization Collaboration Settings +- [ ] Access Control Configuration + +--- + +## 📋 **4. Master Data Management** + +### **4.1 Document Type Ecosystem** + +```typescript +interface DocumentTypeManagement { + correspondence_types: DocumentType[]; + rfa_types: RFAType[]; + circulation_types: CirculationType[]; + drawing_categories: DrawingCategory[]; + type_hierarchy: TypeHierarchy; +} +``` + +**Features:** + +- [ ] Document Type CRUD +- [ ] Type-Specific Workflow Configuration +- [ ] Category Hierarchy Management +- [ ] Template Association + +### **4.2 Discipline & Classification System** + +```typescript +interface DisciplineManagement { + disciplines: Discipline[]; + sub_types: SubType[]; + classification_rules: ClassificationRule[]; + mapping_configurations: MappingConfig[]; +} +``` + +**Features:** + +- [ ] Discipline CRUD (āļ•āļēāļĄ Requirement 6B) +- [ ] Sub-Type Management with Number Mapping +- [ ] Automatic Classification Rules +- [ ] Cross-Reference Mapping + +### **4.3 Status & Code Management** + +```typescript +interface StatusManagement { + status_codes: StatusCode[]; + transition_rules: TransitionRule[]; + status_workflows: StatusWorkflow[]; + automated_status_changes: AutoStatusChange[]; +} +``` + +**Features:** + +- [ ] Status Code Configuration +- [ ] State Transition Rules +- [ ] Automated Status Updates +- [ ] Status Change Analytics + +--- + +## ðŸ”Ē **5. Document Numbering System Administration** + +### **5.1 Numbering Format Configuration** + +```typescript +interface NumberingFormatManagement { + format_templates: NumberingTemplate[]; + token_library: TokenDefinition[]; + format_preview: FormatPreview; + validation_rules: ValidationRule[]; +} +``` + +**Features:** + +- [ ] Template Editor with Live Preview +- [ ] Custom Token Definition +- [ ] Format Validation +- [ ] Bulk Template Application + +### **5.2 Counter Management** + +```typescript +interface CounterManagement { + counter_groups: CounterGroup[]; + reset_schedules: ResetSchedule[]; + counter_audit: CounterHistory[]; + conflict_resolution: ConflictResolutionRule[]; +} +``` + +**Features:** + +- [ ] Counter Group Configuration +- [ ] Scheduled Reset Management +- [ ] Counter Audit Trail +- [ ] Conflict Resolution Rules + +### **5.3 Numbering Rule Engine** + +```typescript +interface NumberingRuleEngine { + conditional_rules: ConditionalRule[]; + context_resolvers: ContextResolver[]; + fallback_strategies: FallbackStrategy[]; + performance_monitoring: PerformanceMetrics; +} +``` + +**Features:** + +- [ ] Conditional Numbering Rules +- [ ] Context Variable Management +- [ ] Fallback Strategy Configuration +- [ ] Performance Optimization + +--- + +## ⚙ïļ **6. Workflow & Routing Administration** + +### **6.1 Workflow DSL Management** + +```typescript +interface WorkflowDSLManagement { + workflow_library: WorkflowDefinition[]; + dsl_editor: DSLEditor; + version_control: VersionHistory[]; + deployment_pipeline: DeploymentConfig[]; +} +``` + +**Features:** + +- [ ] Visual Workflow Designer +- [ ] DSL Code Editor with Syntax Highlighting +- [ ] Version Control and Rollback +- [ ] Testing and Deployment Pipeline + +### **6.2 Routing Template Administration** + +```typescript +interface RoutingTemplateManagement { + template_library: RoutingTemplate[]; + step_configurations: StepConfig[]; + approval_chains: ApprovalChain[]; + escalation_rules: EscalationRule[]; +} +``` + +**Features:** + +- [ ] Template CRUD Operations +- [ ] Drag-and-Drop Step Configuration +- [ ] Approval Chain Management +- [ ] Escalation Rule Setup + +### **6.3 Workflow Analytics** + +```typescript +interface WorkflowAnalytics { + performance_metrics: WorkflowMetrics[]; + bottleneck_analysis: Bottleneck[]; + compliance_reporting: ComplianceReport[]; + optimization_recommendations: Recommendation[]; +} +``` + +**Features:** + +- [ ] Workflow Performance Dashboard +- [ ] Bottleneck Identification +- [ ] Compliance Reporting +- [ ] Optimization Suggestions + +--- + +## 📊 **7. Reporting & Analytics Center** + +### **7.1 Custom Report Builder** + +```typescript +interface ReportBuilder { + data_sources: DataSource[]; + visualization_types: VisualizationType[]; + report_templates: ReportTemplate[]; + scheduling: ScheduleConfig[]; +} +``` + +**Features:** + +- [ ] Drag-and-Drop Report Designer +- [ ] Multiple Visualization Options +- [ ] Template Library +- [ ] Automated Report Scheduling + +### **7.2 Business Intelligence** + +```typescript +interface BusinessIntelligence { + kpi_dashboard: KPIMetric[]; + trend_analysis: TrendData[]; + predictive_analytics: PredictiveModel[]; + data_export: ExportConfig[]; +} +``` + +**Features:** + +- [ ] Real-time KPI Dashboard +- [ ] Trend Analysis Tools +- [ ] Predictive Analytics +- [ ] Data Export and Integration + +### **7.3 Compliance Reporting** + +```typescript +interface ComplianceReporting { + regulatory_reports: RegulatoryReport[]; + audit_trails: AuditTrail[]; + compliance_dashboard: ComplianceMetric[]; + certification_tracking: Certification[]; +} +``` + +**Features:** + +- [ ] Pre-built Regulatory Reports +- [ ] Comprehensive Audit Trails +- [ ] Compliance Dashboard +- [ ] Certification Management + +--- + +## 🔐 **8. Security & Compliance Center** + +### **8.1 Security Policy Management** + +```typescript +interface SecurityPolicyManagement { + access_policies: AccessPolicy[]; + data_classification: DataClassification[]; + encryption_settings: EncryptionConfig[]; + security_incidents: SecurityIncident[]; +} +``` + +**Features:** + +- [ ] Access Policy Configuration +- [ ] Data Classification Scheme +- [ ] Encryption Management +- [ ] Security Incident Tracking + +### **8.2 Compliance Framework** + +```typescript +interface ComplianceFramework { + compliance_rules: ComplianceRule[]; + control_testing: ControlTest[]; + evidence_management: Evidence[]; + compliance_calendar: ComplianceEvent[]; +} +``` + +**Features:** + +- [ ] Compliance Rule Engine +- [ ] Control Testing Framework +- [ ] Evidence Collection +- [ ] Compliance Calendar + +### **8.3 Risk Management** + +```typescript +interface RiskManagement { + risk_register: Risk[]; + risk_assessments: RiskAssessment[]; + mitigation_plans: MitigationPlan[]; + risk_dashboard: RiskMetrics; +} +``` + +**Features:** + +- [ ] Risk Identification and Registration +- [ ] Risk Assessment Tools +- [ ] Mitigation Planning +- [ ] Risk Monitoring Dashboard + +--- + +## 📧 **9. Notification & Communication Management** + +### **9.1 Notification Template System** + +```typescript +interface NotificationTemplateManagement { + email_templates: EmailTemplate[]; + line_templates: LineTemplate[]; + system_notifications: SystemTemplate[]; + template_variables: TemplateVariable[]; +} +``` + +**Features:** + +- [ ] Multi-channel Template Management +- [ ] Variable Substitution System +- [ ] Template Testing and Preview +- [ ] Bulk Template Operations + +### **9.2 Subscription Management** + +```typescript +interface SubscriptionManagement { + user_preferences: UserPreference[]; + group_subscriptions: GroupSubscription[]; + escalation_policies: EscalationPolicy[]; + delivery_reports: DeliveryReport[]; +} +``` + +**Features:** + +- [ ] User Preference Management +- [ ] Group Subscription Configuration +- [ ] Escalation Policy Setup +- [ ] Delivery Monitoring + +### **9.3 Digest Configuration** + +```typescript +interface DigestConfiguration { + digest_rules: DigestRule[]; + grouping_criteria: GroupingCriteria[]; + timing_configurations: TimingConfig[]; + content_prioritization: PriorityRule[]; +} +``` + +**Features:** + +- [ ] Digest Rule Engine +- [ ] Content Grouping Configuration +- [ ] Timing and Frequency Settings +- [ ] Content Prioritization Rules + +--- + +## 🗃ïļ **10. Data Management & Maintenance** + +### **10.1 Data Lifecycle Management** + +```typescript +interface DataLifecycleManagement { + retention_policies: RetentionPolicy[]; + archival_rules: ArchivalRule[]; + purge_schedules: PurgeSchedule[]; + data_governance: GovernancePolicy[]; +} +``` + +**Features:** + +- [ ] Retention Policy Configuration +- [ ] Automated Archival Rules +- [ ] Scheduled Data Purge +- [ ] Data Governance Framework + +### **10.2 Backup & Recovery** + +```typescript +interface BackupRecoveryManagement { + backup_configurations: BackupConfig[]; + recovery_procedures: RecoveryProcedure[]; + disaster_recovery: DisasterRecoveryPlan[]; + backup_monitoring: BackupMonitor[]; +} +``` + +**Features:** + +- [ ] Backup Schedule Management +- [ ] Recovery Procedure Documentation +- [ ] Disaster Recovery Planning +- [ ] Backup Status Monitoring + +### **10.3 System Maintenance** + +```typescript +interface SystemMaintenance { + maintenance_windows: MaintenanceWindow[]; + update_management: UpdateConfig[]; + performance_tuning: TuningParameter[]; + cleanup_jobs: CleanupJob[]; +} +``` + +**Features:** + +- [ ] Maintenance Window Scheduling +- [ ] Update Management +- [ ] Performance Tuning Parameters +- [ ] Automated Cleanup Jobs + +--- + +## ðŸŽŊ **11. Dashboard & Overview** + +### **11.1 Executive Dashboard** + +```typescript +interface ExecutiveDashboard { + system_health: HealthMetric[]; + business_metrics: BusinessMetric[]; + user_engagement: EngagementMetric[]; + security_posture: SecurityMetric[]; +} +``` + +**Features:** + +- [ ] Real-time System Health Monitoring +- [ ] Business Performance Metrics +- [ ] User Engagement Analytics +- [ ] Security Posture Assessment + +### **11.2 Operational Dashboard** + +```typescript +interface OperationalDashboard { + workflow_monitoring: WorkflowStatus[]; + document_metrics: DocumentMetric[]; + user_productivity: ProductivityMetric[]; + system_utilization: UtilizationMetric[]; +} +``` + +**Features:** + +- [ ] Workflow Status Monitoring +- [ ] Document Processing Metrics +- [ ] User Productivity Analytics +- [ ] System Resource Utilization + +--- + +## 🔄 **12. Integration & API Management** + +### **12.1 API Gateway Administration** + +```typescript +interface APIManagement { + api_endpoints: APIEndpoint[]; + rate_limiting: RateLimitConfig[]; + authentication_settings: AuthConfig[]; + api_analytics: APIAnalytics[]; +} +``` + +**Features:** + +- [ ] API Endpoint Management +- [ ] Rate Limiting Configuration +- [ ] Authentication Settings +- [ ] API Usage Analytics + +### **12.2 External Integration** + +```typescript +interface ExternalIntegration { + webhook_configurations: WebhookConfig[]; + third_party_connectors: Connector[]; + data_sync_rules: SyncRule[]; + integration_monitoring: IntegrationMonitor[]; +} +``` + +**Features:** + +- [ ] Webhook Management +- [ ] Third-party Connector Configuration +- [ ] Data Synchronization Rules +- [ ] Integration Health Monitoring + +--- + +## ðŸŽŊ **Critical Success Factors** + +1. **Unified Administration Experience** - Single pane of glass āļŠāļģāļŦāļĢāļąāļšāļ—āļļāļāļāļēāļĢāļˆāļąāļ”āļāļēāļĢ +2. **Role-Based Access Control** - āđāļ•āđˆāļĨāļ°āļĢāļ°āļ”āļąāļšāđ€āļŦāđ‡āļ™āđāļĨāļ°āļˆāļąāļ”āļāļēāļĢāđ„āļ”āđ‰āđ€āļ‰āļžāļēāļ°āļŠāđˆāļ§āļ™āļ‚āļ­āļ‡āļ•āļąāļ§āđ€āļ­āļ‡ +3. **Real-time Monitoring** - āļĢāļ°āļšāļš monitoring āđāļšāļš real-time āļ—āļļāļāļŠāđˆāļ§āļ™ +4. **Audit Trail** - āļ—āļļāļāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡āđƒāļ™ Admin Panel āļ–āļđāļāļšāļąāļ™āļ—āļķāļāđ„āļ§āđ‰ +5. **Performance** - Admin operations āļ•āđ‰āļ­āļ‡āļĢāļ§āļ”āđ€āļĢāđ‡āļ§āđāļĄāđ‰āļĄāļĩāļ‚āđ‰āļ­āļĄāļđāļĨāļˆāļģāļ™āļ§āļ™āļĄāļēāļ +6. **User Experience** - Interface āđƒāļŠāđ‰āļ‡āļēāļ™āļ‡āđˆāļēāļĒ āđāļĄāđ‰āļŠāļģāļŦāļĢāļąāļšāļŸāļĩāđ€āļˆāļ­āļĢāđŒāļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™ + +Admin Panel āļ™āļĩāđ‰āļˆāļ°āļ—āļģāđƒāļŦāđ‰āļœāļđāđ‰āļ”āļđāđāļĨāļĢāļ°āļšāļšāļŠāļēāļĄāļēāļĢāļ–āļˆāļąāļ”āļāļēāļĢ LCBP3-DMS āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļŠāļĄāļšāļđāļĢāļ“āđŒāđāļšāļš āļ•āļąāđ‰āļ‡āđāļ•āđˆāļāļēāļĢāļ•āļąāđ‰āļ‡āļ„āđˆāļēāļĢāļ°āļ”āļąāļšāļžāļ·āđ‰āļ™āļāļēāļ™āđ„āļ›āļˆāļ™āļ–āļķāļ‡āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ workflow āļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āđāļĨāļ°āļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļŠāļīāļ‡āļĨāļķāļ diff --git a/2_Backend_Plan_V1_4_4.md b/docs/Markdown/2_Backend_Plan_V1_4_4.md similarity index 100% rename from 2_Backend_Plan_V1_4_4.md rename to docs/Markdown/2_Backend_Plan_V1_4_4.md diff --git a/3_Frontend_Plan_V1_4_4.md b/docs/Markdown/3_Frontend_Plan_V1_4_4.md similarity index 100% rename from 3_Frontend_Plan_V1_4_4.md rename to docs/Markdown/3_Frontend_Plan_V1_4_4.md diff --git a/4_Data_Dictionary_V1_4_4.md b/docs/Markdown/4_Data_Dictionary_V1_4_4.md similarity index 100% rename from 4_Data_Dictionary_V1_4_4.md rename to docs/Markdown/4_Data_Dictionary_V1_4_4.md diff --git a/docs/frontend.md b/docs/frontend.md deleted file mode 100644 index 70980a8..0000000 --- a/docs/frontend.md +++ /dev/null @@ -1,94 +0,0 @@ - -xxx npx shadcn@latest init - -✔ npx shadcn@latest init - -✔ Would you like to use TypeScript (recommended)? Yes -✔ Which style would you like to use? Default -✔ Which base color would you like to use? Slate -✔ Where is your global CSS file? src/app/globals.css -✔ Would you like to use CSS variables...? Yes -✔ Where is your tailwind.config.ts...? tailwind.config.ts -✔ Configure the import alias for components: @/components -✔ Configure the import alias for utils: @/lib/utils -✔ Are you using React Server Components? Yes - -npm install @tanstack/react-query zustand react-hook-form zod axios - -npm install msw --save-dev - -LCBP3 DMS V1.2.0 -Custom Gem -āļĢāļąāļšāļ—āļĢāļēāļšāļ„āļĢāļąāļš āđ€āļĢāļēāļˆāļ°āđ„āļ›āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ•āđˆāļ­āđ„āļ› āļ„āļ·āļ­āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ App Shell Layout (āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļŦāļ™āđ‰āļēāđ€āļ§āđ‡āļšāļŦāļĨāļąāļ) āļ•āļēāļĄāļ‚āđ‰āļ­āļāļģāļŦāļ™āļ” 5.1 - -āđ€āļĢāļēāļˆāļ°āļŠāļĢāđ‰āļēāļ‡ 3 āļŠāđˆāļ§āļ™āļŦāļĨāļąāļ: - -Sidebar (āđ€āļĄāļ™āļđāļ”āđ‰āļēāļ™āļ‚āđ‰āļēāļ‡) - -Navbar/Header (āđāļ–āļšāļ”āđ‰āļēāļ™āļšāļ™) - -Main Content Area (āļžāļ·āđ‰āļ™āļ—āļĩāđˆāđ€āļ™āļ·āđ‰āļ­āļŦāļē) - -2.1 āļ•āļīāļ”āļ•āļąāđ‰āļ‡ Icons -npm install lucide-react - -3.1 āļ•āļīāļ”āļ•āļąāđ‰āļ‡ Shadcn Components āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ -npx shadcn@latest add card -npx shadcn@latest add table -npx shadcn@latest add button -npx shadcn@latest - -4.1 āļ•āļąāđ‰āļ‡āļ„āđˆāļē React Query (TanStack Query) Provider -npm install @tanstack/react-query-devtools - -4.2 āļ•āļąāđ‰āļ‡āļ„āđˆāļē Mock Service Worker (MSW) -npx msw init public/ --save - -4.4 āļ­āļąāļ›āđ€āļ”āļ• MyTasksTable (āđƒāļŦāđ‰āđ€āļĢāļĩāļĒāļ API āļˆāļĢāļīāļ‡) -npm install axios - -5.1 āļ•āļīāļ”āļ•āļąāđ‰āļ‡ Shadcn Components -npx shadcn@latest add badge - - -5.4 (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡) āļāļēāļĢāļ™āļģāđ„āļ›āđƒāļŠāđ‰āđƒāļ™āļŦāļ™āđ‰āļē RFA -āđ€āļĢāļēāļĒāļąāļ‡āđ„āļĄāđˆāđ„āļ”āđ‰āļŠāļĢāđ‰āļēāļ‡āļŦāļ™āđ‰āļē /rfa/[id]/page.tsx āđāļ•āđˆāđ€āļĄāļ·āđˆāļ­āļŠāļĢāđ‰āļēāļ‡āđ€āļŠāļĢāđ‡āļˆ āđ€āļĢāļēāļˆāļ°āđ€āļĢāļĩāļĒāļāđƒāļŠāđ‰ Component āļ™āļĩāđ‰āđ‚āļ”āļĒāļŠāđˆāļ‡ props āđ„āļ›: - -// src/app/rfa/[id]/page.tsx (āļ•āļąāļ§āļ­āļĒāđˆāļēāļ‡āđƒāļ™āļ­āļ™āļēāļ„āļ•) - -// import { mockWorkflow } from "@/mocks/data/mock-workflow"; -// import { WorkflowVisualizer } from "@/components/workflow/workflow-visualizer"; - -// export default function RfaDetailPage({ params }: { params: { id: string } }) { -// // 1. (āđƒāļ™āļ­āļ™āļēāļ„āļ•) āļ”āļķāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨ RFA āđāļĨāļ° Workflow steps āļˆāļēāļ API -// // const { data: rfaData } = useQuery(...); -// // const steps = rfaData.workflowSteps; - -// // 2. (āđƒāļ™āļ­āļ™āļēāļ„āļ•) āļ”āļķāļ‡āļŠāļīāļ—āļ˜āļīāđŒāļœāļđāđ‰āđƒāļŠāđ‰āļˆāļēāļ Zustand (Global State) -// // const { user } = useAuthStore(); -// // const isAdmin = user.role === 'ADMIN' || user.role === 'SUPER_ADMIN'; - -// // 3. āļŠāđˆāļ‡ Props āđ„āļ›āļĒāļąāļ‡ Component -// return ( -//
-// {/* (āđāļŠāļ”āļ‡āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ” RFA āļ—āļĩāđˆāļ™āļĩāđˆ) */} - -// -//
-// ); -// } - -6.1 āļ•āļīāļ”āļ•āļąāđ‰āļ‡ Shadcn Components āļ—āļĩāđˆāļˆāļģāđ€āļ›āđ‡āļ™ -npx shadcn@latest add avatar -npx shadcn@latest add dialog -npx shadcn@latest add form -npx shadcn@latest add input -npx shadcn@latest add label -npx shadcn@latest add dropdown-menu -npx shadcn@latest add checkbox -npx shadcn@latest add pagination - -x 6.2 āļ­āļąāļ›āđ€āļ”āļ• Mock API (MSW) x \ No newline at end of file diff --git a/docs/prompt.md b/docs/prompt.md index 11ee9c3..2be174d 100644 --- a/docs/prompt.md +++ b/docs/prompt.md @@ -1,123 +1,5 @@ # **PROMPT** -## Gemini - -## VSCode Shortcut - -Markdown preview Ctrl+Shift+V - -## āļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļŠāļģāļŦāļĢāļąāļš lcbp3-backend - -```bash -# āļŠāļĢāđ‰āļēāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļŦāļĨāļąāļ -$rootFolder = "backend" -New-Item -ItemType Directory -Path $rootFolder -Force -Set-Location $rootFolder - -# āļĢāļēāļĒāļāļēāļĢāđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āļ—āļąāđ‰āļ‡āļŦāļĄāļ” - $folders = @( - "src", - "database", - "src\common", - "src\modules", - "src\modules\user", - "src\modules\project", - "src\modules\master", - "src\modules\correspondence", - "src\modules\rfa", - "src\modules\drawing", - "src\modules\circulations", - "src\modules\transmittal", - "src\modules\search", - "src\modules\document-numbering", - "src\common\auth", - "src\common\config", - "src\common\decorators", - "src\common\entities", - "src\common\exceptions", - "src\common\file-storage", - "src\common\guards", - "src\common\interceptors", - "src\common\services" -) - -# āļ§āļ™āļĨāļđāļ›āļŠāļĢāđ‰āļēāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ” -foreach ($folder in $folders) { - New-Item -ItemType Directory -Path $folder -Force -} - -Write-Host "āļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļŠāļģāļŦāļĢāļąāļš backend āđ€āļĢāļĩāļĒāļšāļĢāđ‰āļ­āļĒāđāļĨāđ‰āļ§" -ForegroundColor Green - -``` - -## Git Commands - -```bash - -# 1ïļâƒĢ āļ•āļąāđ‰āļ‡āļŠāļ·āđˆāļ­āđāļĨāļ°āļ­āļĩāđ€āļĄāļĨāļ‚āļ­āļ‡āļ„āļļāļ“ (āđƒāļŠāđ‰āļāļąāļšāļ—āļļāļ repo) -git config --global user.name "Pean Charoen" -git config --global user.email "peancharoen.pslcp3@gmail.com" - -# 2ïļâƒĢ āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ§āđˆāļēāđ€āļŠāļ·āđˆāļ­āļĄāļāļąāļš remote āļ–āļđāļāļ•āđ‰āļ­āļ‡ (āđāļāđ‰ URL āđƒāļŦāđ‰āļ•āļĢāļ‡ repo āļˆāļĢāļīāļ‡āļ‚āļ­āļ‡āļ„āļļāļ“) -git remote set-url origin ssh://git@git.np-dms.work:2222/np-dms/lcbp3_v1.git - -# 3ïļâƒĢ āļ•āļĢāļ§āļˆāļŠāļ­āļšāļŠāļ–āļēāļ™āļ°āđ„āļŸāļĨāđŒ -git status - -# 4ïļâƒĢ āđ€āļžāļīāđˆāļĄāđ„āļŸāļĨāđŒāļ—āļąāđ‰āļ‡āļŦāļĄāļ” -git add . - -# 5ïļâƒĢ Commit āļžāļĢāđ‰āļ­āļĄāļ‚āđ‰āļ­āļ„āļ§āļēāļĄ -git commit -m "Update project files" - -# 6ïļâƒĢ āļ”āļķāļ‡ remote āļāđˆāļ­āļ™āđ€āļžāļ·āđˆāļ­āļ›āđ‰āļ­āļ‡āļāļąāļ™ conflict (āļ–āđ‰āļēāļĄāļĩ) -git pull --rebase origin main - -# 7ïļâƒĢ Push āļ‚āļķāđ‰āļ™ Gitea -git push -u origin main - -``` - -## **āļŠāļĢāđ‰āļēāļ‡ NestJS Project āđƒāļŦāļĄāđˆ** - -- āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆ 1: āļ•āļīāļ”āļ•āļąāđ‰āļ‡ NestJS CLI (āļ–āđ‰āļēāļĒāļąāļ‡āđ„āļĄāđˆāđ„āļ”āđ‰āļ•āļīāļ”āļ•āļąāđ‰āļ‡) - - - npm install -g @nestjs/cli - -- āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆ 2: āļŠāļĢāđ‰āļēāļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒāđƒāļŦāļĄāđˆ - - - nest new backend - - nest new . /āļ­āļĒāļđāđˆāđƒāļ™āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡āđ„āļ§āđ‰āđāļĨāđ‰āļ§ āđāļĨāļ°āļ•āđ‰āļ­āļ‡āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡āđ‚āļ›āļĢāđ€āļˆāļāļ•āđŒāļĨāļ‡āđƒāļ™āđ‚āļŸāļĨāđ€āļ”āļ­āļĢāđŒāļ™āļąāđ‰āļ™āđ‚āļ”āļĒāļ•āļĢāļ‡: - -- āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ—āļĩāđˆ 3: āļ•āļīāļ”āļ•āļąāđ‰āļ‡ Dependencies āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄāļŠāļģāļŦāļĢāļąāļš DMS - -```bash -# Core & Database -npm install @nestjs/typeorm typeorm mysql2 -npm install @nestjs/config - -# Validation & Transformation -npm install class-validator class-transformer - -# Authentication & Authorization -npm install @nestjs/jwt @nestjs/passport passport passport-jwt -npm install @nestjs/passport -npm install casl - -# File Upload -npm install @nestjs/platform-express multer - -# Documentation -npm install @nestjs/swagger - -# Security & Performance -npm install helmet rate-limiter-flexible - -# Development Dependencies (āļŠāļģāļŦāļĢāļąāļšāļ—āļ”āļŠāļ­āļš) -npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @types/multer supertest - -``` - āļ‚āļ­āļ­āļ āļąāļĒāļ­āļĒāđˆāļēāļ‡āļĒāļīāđˆāļ‡āđƒāļ™āļ„āļ§āļēāļĄāļœāļīāļ”āļžāļĨāļēāļ”āļ—āļĩāđˆāđ€āļāļīāļ”āļ‚āļķāđ‰āļ™āļ„āļĢāļąāļš āđ€āļ‚āđ‰āļēāđƒāļˆāļ„āļĢāļąāļšāļ§āđˆāļēāļ—āļģāđƒāļŦāđ‰āđ€āļŠāļĩāļĒāđ€āļ§āļĨāļēāļĄāļēāļ āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢ **"āļ•āļąāđ‰āļ‡āļ„āđˆāļē"** āļŦāļĢāļ·āļ­ **"āļāļģāļŦāļ™āļ”āļ„āđˆāļē"** āđƒāļŦāđ‰āļœāļĄāļ•āļĢāļ§āļˆāļŠāļ­āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļāđˆāļ­āļ™āļ™āļąāđ‰āļ™ āđƒāļ™āļĢāļ°āļšāļš AI āđāļšāļšāļ™āļĩāđ‰ **āđ„āļĄāđˆāļĄāļĩāđ€āļĄāļ™āļđ Settings āļŦāļĢāļ·āļ­āļ›āļļāđˆāļĄāļāļ”āđƒāļŦāđ‰āļ„āļļāļ“āđ€āļ‚āđ‰āļēāđ„āļ›āļ•āļąāđ‰āļ‡āļ„āđˆāļēāđ„āļ”āđ‰āđ‚āļ”āļĒāļ•āļĢāļ‡āļ„āļĢāļąāļš** @@ -149,119 +31,243 @@ npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @typ --- -### 📂 1. āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨāļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļŠāļĢāđ‰āļēāļ‡ (Module Structure) +Context: We are entering Phase 3: Unified Workflow Engine of the NAP-DMS project. Please analyze the following documents in detail: -āđ€āļĢāļēāļˆāļģāđ€āļ›āđ‡āļ™āļ•āđ‰āļ­āļ‡āļŠāļĢāđ‰āļēāļ‡ `JsonSchemaModule` āļ‚āļķāđ‰āļ™āļĄāļēāđƒāļŦāļĄāđˆ āđ‚āļ”āļĒāļ āļēāļĒāđƒāļ™āļˆāļ°āļ›āļĢāļ°āļāļ­āļšāļ”āđ‰āļ§āļĒ Services āļĒāđˆāļ­āļĒāļ•āļēāļĄāļŦāļ™āđ‰āļēāļ—āļĩāđˆāļ‡āļēāļ™ āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āđ€āļ›āđ‡āļ™āđ„āļ›āļ•āļēāļĄāļŦāļĨāļąāļ Single Responsibility Principle āļ„āļĢāļąāļš +1. 2_Backend_Plan_V1_4_4.md: Focus on Phase 3 Unified Workflow Engine (Tasks T3.1 - T3.1.9) and āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ‚āļĄāļ”āļđāļĨ (Domain-Driven) -**Path:** `src/modules/json-schema/` +2. 0_Requirements_V1_4_4.md: Focus on Section 2.4 Business Logic & Consistency, Section 3.2 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāđ‚āļ•āđ‰āļ•āļ­āļš (Correspondence Management) and 3.5 āļāļēāļĢāļˆāļąāļ”āļāļēāļĢāđ€āļ­āļāļŠāļēāļĢāļ‚āļ­āļ­āļ™āļļāļĄāļąāļ•āļī (Request for Approval & Workflow). -```text -src/modules/json-schema -├── 📁 dto -│ ├── create-json-schema.dto.ts # DTO āļŠāļģāļŦāļĢāļąāļšāļŠāļĢāđ‰āļēāļ‡ Schema āđƒāļŦāļĄāđˆ -│ ├── update-json-schema.dto.ts # DTO āļŠāļģāļŦāļĢāļąāļšāđāļāđ‰āđ„āļ‚ Schema -│ ├── validate-data.dto.ts # DTO āļŠāļģāļŦāļĢāļąāļšāļĢāļąāļšāļ‚āđ‰āļ­āļĄāļđāļĨāļĄāļē Validate -│ └── migrate-data.dto.ts # DTO āļŠāļģāļŦāļĢāļąāļšāļŠāļąāđˆāļ‡ Migrate Data -├── 📁 entities -│ └── json-schema.entity.ts # Entity āļŦāļĨāļąāļāđ€āļāđ‡āļš Definition (T2.5.1) -├── 📁 services -│ ├── json-schema.service.ts # Core Service (CRUD, Validation wrapper) -│ ├── virtual-column.service.ts # āļˆāļąāļ”āļāļēāļĢ Generated Columns āđƒāļ™ DB (T2.5.3) -│ ├── schema-migration.service.ts # āļˆāļąāļ”āļāļēāļĢ Data Migration āļĢāļ°āļŦāļ§āđˆāļēāļ‡ Version (T2.5.5) -│ └── json-security.service.ts # āļˆāļąāļ”āļāļēāļĢ Security & Encryption (T2.5.6) -├── 📁 interfaces -│ ├── ui-schema.interface.ts # Type definition āļŠāļģāļŦāļĢāļąāļš UI Schema (T2.5.4) -│ └── virtual-column.interface.ts # Type definition āļŠāļģāļŦāļĢāļąāļš Virtual Column Config -├── json-schema.controller.ts # API Endpoints (T2.5.7) -├── json-schema.module.ts # Module Definition -└── json-schema.service.spec.ts # Unit Tests (T2.5.9) +3. 1_FullStackJS_V1_4_4.md: Focus on Section 3.5 Unified Workflow Engine, 3.9.13 CorrespondenceRoutingModule, 3.9.14 WorkflowEngineModule. + +Goal: Prepare to implement coding for Unified Workflow Engine. + +Instruction: Summarize todo task, dto, entities, interfaces, module, controllers, services, files and function for Unified Workflow Engine. Do not write code yet, just confirm your understanding of the todo task. + +āļĢāļąāļšāļ—āļĢāļēāļšāļ„āļĢāļąāļš āļœāļĄāđ„āļ”āđ‰āļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒāđ€āļ­āļāļŠāļēāļĢ `2_Backend_Plan_V1_4_4.md`, `0_Requirements_V1_4_4.md`, āđāļĨāļ° `1_FullStackJS_V1_4_4.md` āđ‚āļ”āļĒāđ€āļ™āđ‰āļ™āđ„āļ›āļ—āļĩāđˆ **Phase 3: Unified Workflow Engine** āđ€āļĢāļĩāļĒāļšāļĢāđ‰āļ­āļĒāđāļĨāđ‰āļ§ + +āļŠāļĢāļļāļ›āļ„āļ§āļēāļĄāđ€āļ‚āđ‰āļēāđƒāļˆāđāļĨāļ°āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‡āļēāļ™āļ—āļĩāđˆāļˆāļ°āļ•āđ‰āļ­āļ‡āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāđƒāļ™ Phase āļ™āļĩāđ‰ āļĄāļĩāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ”āļąāļ‡āļ™āļĩāđ‰āļ„āļĢāļąāļš: + +### ðŸŽŊ āļ āļēāļžāļĢāļ§āļĄ (Overview) + +āđ€āļ›āđ‰āļēāļŦāļĄāļēāļĒāļ„āļ·āļ­āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ **Unified Workflow Engine** āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āļĢāļ°āļšāļšāļāļĨāļēāļ‡āđƒāļ™āļāļēāļĢāļˆāļąāļ”āļāļēāļĢ Business Logic āļ‚āļ­āļ‡āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāļ—āļąāđ‰āļ‡āļŦāļĄāļ” (āļ—āļąāđ‰āļ‡ Correspondence Routing āļ›āļāļ•āļī āđāļĨāļ° RFA Approval Workflow āļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™) āđ‚āļ”āļĒāđ„āļĄāđˆāļœāļđāļāļ•āļīāļ”āļāļąāļš Hard-coded logic āđƒāļ™āđ‚āļ„āđ‰āļ” āđāļ•āđˆāļˆāļ°āļ‚āļąāļšāđ€āļ„āļĨāļ·āđˆāļ­āļ™āļ”āđ‰āļ§āļĒ **DSL (Domain-Specific Language)** āđāļĨāļ° **Configuration** āđ€āļžāļ·āđˆāļ­āļ„āļ§āļēāļĄāļĒāļ·āļ”āļŦāļĒāļļāđˆāļ™āđƒāļ™āļāļēāļĢāļ›āļĢāļąāļšāđ€āļ›āļĨāļĩāđˆāļĒāļ™ Flow, āļāļēāļĢāļ‚āđ‰āļēāļĄāļ‚āļąāđ‰āļ™āļ•āļ­āļ™, āļŦāļĢāļ·āļ­āļāļēāļĢāļŠāđˆāļ‡āļāļĨāļąāļš + +--- + +### 📋 1. āļĢāļēāļĒāļāļēāļĢāļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ (Todo Tasks) + +āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļˆāļēāļ Task T3.1 - T3.1.9 āđƒāļ™āđāļœāļ™ Backend: + +1. **T3.1.1 DSL Specification:** āļāļģāļŦāļ™āļ” Grammar (YAML/JSON) āļŠāļģāļŦāļĢāļąāļšāđ€āļ‚āļĩāļĒāļ™ Workflow (State, Transition, Condition, Event). +2. **T3.1.2 Core Entities:** āļŠāļĢāđ‰āļēāļ‡ Entity āđƒāļŦāļĄāđˆāļŠāļģāļŦāļĢāļąāļšāđ€āļāđ‡āļš Definition āđāļĨāļ° Instance āļ‚āļ­āļ‡ Workflow. +3. **T3.1.3 DSL Parser & Compiler:** āļŠāļĢāđ‰āļēāļ‡ Service āđāļ›āļĨāļ‡ YAML DSL āđ€āļ›āđ‡āļ™ JSON Schema āļ—āļĩāđˆ Engine āđ€āļ‚āđ‰āļēāđƒāļˆ. +4. **T3.1.4 Runtime Engine:** āļŠāļĢāđ‰āļēāļ‡ State Machine Logic (āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°, āļ•āļĢāļ§āļˆāļŠāļīāļ—āļ˜āļīāđŒ, āļ•āļĢāļ§āļˆāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚). +5. **T3.1.5 Advanced Features:** āļĢāļ­āļ‡āļĢāļąāļš Parallel Approval, Conditional Transition (JS Expression), Timeout. +6. **T3.1.6 Event System:** āļˆāļąāļ”āļāļēāļĢ Event āđ€āļĄāļ·āđˆāļ­āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Notify, Assign, Webhook). +7. **T3.1.7 API Implementation:** āļŠāļĢāđ‰āļēāļ‡ Controller āļŠāļģāļŦāļĢāļąāļšāļˆāļąāļ”āļāļēāļĢ Definition āđāļĨāļ°āļŠāļąāđˆāļ‡ Transition. +8. **T3.1.8 Integration:** āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ Engine āđ€āļ‚āđ‰āļēāļāļąāļš Module āđ€āļ”āļīāļĄ (`Correspondence`, `RFA`). +9. **T3.1.9 Testing:** āđ€āļ‚āļĩāļĒāļ™ Unit/Integration Test āļŠāļģāļŦāļĢāļąāļš Engine. + +--- + +### 🗂ïļ 2. āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđ„āļŸāļĨāđŒāđāļĨāļ°āđ‚āļĄāļ”āļđāļĨ (File & Module Structure) + +āđ€āļĢāļēāļˆāļ°āļŠāļĢāđ‰āļēāļ‡ Module āđƒāļŦāļĄāđˆāļŠāļ·āđˆāļ­ `workflow-engine` āđāļĨāļ°āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡ Modules āļ—āļĩāđˆāđ€āļāļĩāđˆāļĒāļ§āļ‚āđ‰āļ­āļ‡āļ•āļēāļĄāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Domain-Driven āļ”āļąāļ‡āļ™āļĩāđ‰: + +#### **Module: `src/modules/workflow-engine`** + +**📂 Entities (`.entity.ts`)** + +- `WorkflowDefinition`: āđ€āļāđ‡āļš Schema/DSL āļ‚āļ­āļ‡ Workflow (Code, Version, Compiled JSON). +- `WorkflowInstance`: āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āđƒāļš (Current State, Context). +- `WorkflowHistory`: āđ€āļāđ‡āļš Audit Log āļ‚āļ­āļ‡āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Who, When, From->To, Action). +- `WorkflowTransition`: (Optional) āļ­āļēāļˆāļĢāļ§āļĄāļ­āļĒāļđāđˆāđƒāļ™ Definition āļŦāļĢāļ·āļ­āđāļĒāļāđ€āļžāļ·āđˆāļ­āđ€āļāđ‡āļšāļāļŽāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°. + +**📂 DTOs (`.dto.ts`)** + +- `CreateWorkflowDefinitionDto`: āļĢāļąāļš YAML/JSON string āļ‚āļ­āļ‡ DSL. +- `UpdateWorkflowDefinitionDto`: āđāļāđ‰āđ„āļ‚ DSL. +- `EvaluateWorkflowDto`: āļĢāļąāļš Action āđāļĨāļ° Context āđ€āļžāļ·āđˆāļ­āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨ. +- `WorkflowActionDto`: āļĢāļąāļšāļ„āļģāļŠāļąāđˆāļ‡ Transition (Approve, Reject, etc.). +- `GetAvailableActionsDto`: āļ‚āļ­āļĢāļēāļĒāļāļēāļĢ Action āļ—āļĩāđˆāļ—āļģāđ„āļ”āđ‰ āļ“ āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™. + +**📂 Interfaces (`.interface.ts`)** + +- `WorkflowContext`: āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļŠāđˆāļ‡āđƒāļŦāđ‰ Engine (User, Role, Document Data). +- `WorkflowState`: āļ™āļīāļĒāļēāļĄāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ State āđƒāļ™ Memory. +- `TransitionResult`: āļœāļĨāļĨāļąāļžāļ˜āđŒāļŦāļĨāļąāļ‡āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨ (Success, NewState, SideEffects). + +**📂 Services (`.service.ts`)** + +- `WorkflowDslService`: + - `parse(yaml)`: āđāļ›āļĨāļ‡ YAML āđ€āļ›āđ‡āļ™ Object. + - `validate(schema)`: āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ„āļ§āļēāļĄāļ–āļđāļāļ•āđ‰āļ­āļ‡āļ‚āļ­āļ‡ Logic. + - `compile(schema)`: āđāļ›āļĨāļ‡āđ€āļ›āđ‡āļ™ Optimized JSON āļŠāļģāļŦāļĢāļąāļš Runtime. +- `WorkflowEngineService` (Core): + - `initialize(definitionId, context)`: āđ€āļĢāļīāđˆāļĄāļ•āđ‰āļ™ Workflow āđƒāļŦāļĄāđˆ. + - `processAction(instanceId, action, context)`: āļ›āļĢāļ°āļĄāļ§āļĨāļœāļĨāļ„āļģāļŠāļąāđˆāļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°. + - `getAvailableActions(instanceId, context)`: āļ„āļ·āļ™āļ„āđˆāļēāļ›āļļāđˆāļĄāļ—āļĩāđˆ user āļāļ”āđ„āļ”āđ‰. + - `validateCondition(rule, context)`: āļ•āļĢāļ§āļˆāļŠāļ­āļšāđ€āļ‡āļ·āđˆāļ­āļ™āđ„āļ‚ (āđ€āļŠāđˆāļ™ āļ•āđ‰āļ­āļ‡āđ€āļ›āđ‡āļ™ Role Engineer). +- `WorkflowEventService`: + - `dispatchEvent(event, context)`: āļĒāļīāļ‡ Event āđ„āļ›āļĒāļąāļ‡ Notification āļŦāļĢāļ·āļ­ External Service. + +**📂 Controller (`.controller.ts`)** + +- `WorkflowEngineController`: + - `POST /definitions`: āļŠāļĢāđ‰āļēāļ‡/āļ­āļąāļžāđ€āļ”āļ— Workflow Rules. + - `POST /instances/:id/transition`: āļŠāļąāđˆāļ‡āđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ°āđ€āļ­āļāļŠāļēāļĢ. + - `GET /instances/:id/actions`: āļ”āļķāļ‡ Action āļ—āļĩāđˆāļ—āļģāđ„āļ”āđ‰. + - `GET /instances/:id/history`: āļ”āļđāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡. + +--- + +### 🔗 3. āļāļēāļĢāđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­āļāļąāļšāļĢāļ°āļšāļšāđ€āļ”āļīāļĄ (Integration) + +**Module: `CorrespondenceRoutingModule` (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡āļˆāļēāļāđ€āļ”āļīāļĄ)** + +- āļ—āļģāļŦāļ™āđ‰āļēāļ—āļĩāđˆāđ€āļ›āđ‡āļ™ "Client" āļ‚āļ­āļ‡ Workflow Engine. +- āđ€āļĄāļ·āđˆāļ­āļĄāļĩāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Correspondence -> āđ€āļĢāļĩāļĒāļ `WorkflowEngineService.initialize()`. +- āđ€āļĄāļ·āđˆāļ­ User āļāļ”āļ›āļļāđˆāļĄāđƒāļ™āļŦāļ™āđ‰āļē Routing -> āđ€āļĢāļĩāļĒāļ `WorkflowEngineService.processAction()`. +- Map āļŠāļ–āļēāļ™āļ°āļˆāļēāļ Engine āļāļĨāļąāļšāļĄāļē update āļ•āļēāļĢāļēāļ‡ `correspondence_routings` āđ€āļžāļ·āđˆāļ­ Backward Compatibility (āļŦāļĢāļ·āļ­ migrate āđ„āļ›āđƒāļŠāđ‰ `WorkflowInstance` āđ€āļ•āđ‡āļĄāļĢāļđāļ›āđāļšāļšāļ•āļēāļĄāđāļœāļ™). + +**Module: `RfaModule` (āļ›āļĢāļąāļšāļ›āļĢāļļāļ‡)** + +- āđƒāļŠāđ‰ Logic āđ€āļ”āļĩāļĒāļ§āļāļąāļš Correspondence āđāļ•āđˆāđƒāļŠāđ‰ Definition āļ—āļĩāđˆāļ‹āļąāļšāļ‹āđ‰āļ­āļ™āļāļ§āđˆāļē (RFA Workflow Definition). +- āđƒāļŠāđ‰ Context āļ—āļĩāđˆāļĄāļĩāļ‚āđ‰āļ­āļĄāļđāļĨ Shop Drawing / Technical data āđƒāļ™āļāļēāļĢāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆ Condition. + +--- + +### ✅ 4. āļŠāļĢāļļāļ›āļ„āļ§āļēāļĄāļžāļĢāđ‰āļ­āļĄ + +āļœāļĄāđ€āļ‚āđ‰āļēāđƒāļˆāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āđāļĨāļ°āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģāļ„āļĢāļšāļ–āđ‰āļ§āļ™āđāļĨāđ‰āļ§āļ„āļĢāļąāļš: + +1. **Logic:** āļĒāđ‰āļēāļĒ Business Logic āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢāļ­āļ­āļāļˆāļēāļ Service āļ›āļāļ•āļī āđ€āļ‚āđ‰āļēāļŠāļđāđˆ Centralized Engine. +2. **Data:** āđƒāļŠāđ‰ `workflow_definitions` āđāļĨāļ° `workflow_instances` āđ€āļ›āđ‡āļ™āļ•āļąāļ§āļ‚āļąāļšāđ€āļ„āļĨāļ·āđˆāļ­āļ™. +3. **Flexibility:** āļĢāļ­āļ‡āļĢāļąāļšāļāļēāļĢāļ›āļĢāļąāļš Flow āļœāđˆāļēāļ™ DSL āđ‚āļ”āļĒāđ„āļĄāđˆāļ•āđ‰āļ­āļ‡āđāļāđ‰āđ‚āļ„āđ‰āļ” (Hot Reload config). + +**āļŠāļ–āļēāļ™āļ°:** āļžāļĢāđ‰āļ­āļĄāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāđ€āļĢāļīāđˆāļĄāđ€āļ‚āļĩāļĒāļ™āđ‚āļ„āđ‰āļ”āđƒāļ™ **Phase 3 (Task T3.1)** āļ„āļĢāļąāļš + +āļ„āļļāļ“āļ•āđ‰āļ­āļ‡āļāļēāļĢāđƒāļŦāđ‰āļœāļĄāđ€āļĢāļīāđˆāļĄāļ—āļĩāđˆ **Task T3.1.1 (DSL Specification)** āļŦāļĢāļ·āļ­āđ€āļĢāļīāđˆāļĄāļŠāļĢāđ‰āļēāļ‡āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ **Module āđāļĨāļ° Entity (T3.1.2)** āļāđˆāļ­āļ™āļ”āļĩāļ„āļĢāļąāļš? + +āļĢāļąāļšāļ—āļĢāļēāļšāļ„āļĢāļąāļš āļœāļĄāļˆāļ°āļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Data Dictionary āđƒāļŦāđ‰āļ•āļēāļĄāļ—āļĩāđˆāļĢāđ‰āļ­āļ‡āļ‚āļ­āļ„āļĢāļąāļš + +āļˆāļēāļāļāļēāļĢāļ§āļīāđ€āļ„āļĢāļēāļ°āļŦāđŒ SQL script `8_lcbp3_v1_4_4.sql` āđāļĨāļ°āļ‚āđ‰āļ­āļĄāļđāļĨāļ—āļĩāđˆāļ„āļļāļ“āđ€āļĨāļ·āļ­āļāļĄāļē āļœāļĄāđ„āļ”āđ‰āļŠāļĢāļļāļ›āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ•āļēāļĢāļēāļ‡āđāļĨāļ°āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡āļŠāļģāļŦāļĢāļąāļš **Unified Workflow Engine** āđƒāļ™ Phase 3 āļĄāļēāđƒāļŦāđ‰āđāļĨāđ‰āļ§āļ„āļĢāļąāļš + +--- + +### **Data Dictionary: Unified Workflow Engine (Phase 3)** + +#### **1. workflow_definitions** + +**Purpose:** āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ™āļīāļĒāļēāļĄāļāļŽāļāļēāļĢāđ€āļ”āļīāļ™āđ€āļ­āļāļŠāļēāļĢ (Workflow DSL) āļ—āļĩāđˆāđ€āļ›āđ‡āļ™āđāļĄāđˆāđāļšāļšāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļŠāļĢāđ‰āļēāļ‡ Workflow Instance + +| Column Name | Data Type | Constraints | Description | +| :-------------- | :------------ | :------------------------------------------------------ | :----------------------------------------------------------------------------------- | +| `id` | `CHAR(36)` | **PK**, `NOT NULL` | āļĢāļŦāļąāļŠ UUID āļ‚āļ­āļ‡ Workflow Definition | +| `workflow_code` | `VARCHAR(50)` | `NOT NULL` | āļĢāļŦāļąāļŠ Workflow āđ€āļŠāđˆāļ™ `RFA_FLOW_V1`, `CORRESPONDENCE_FLOW_V1` | +| `version` | `INT` | `NOT NULL`, `DEFAULT 1` | āļŦāļĄāļēāļĒāđ€āļĨāļ‚ Version āļ‚āļ­āļ‡ Workflow (Running Number) | +| `description` | `TEXT` | `NULL` | āļ„āļģāļ­āļ˜āļīāļšāļēāļĒāļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ‚āļ­āļ‡ Workflow | +| `dsl` | `JSON` | `NOT NULL` | āļ‚āđ‰āļ­āļĄāļđāļĨāļ™āļīāļĒāļēāļĄ Workflow āļ•āđ‰āļ™āļ‰āļšāļąāļšāđƒāļ™āļĢāļđāļ›āđāļšāļš JSON (āļ—āļĩāđˆāđāļ›āļĨāļ‡āļĄāļēāļˆāļēāļ YAML/JSON Format) | +| `compiled` | `JSON` | `NOT NULL` | āđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ Execution Tree āļ—āļĩāđˆāļœāđˆāļēāļ™āļāļēāļĢ Compile āđāļĨāļ° Optimize āđāļĨāđ‰āļ§ āļžāļĢāđ‰āļ­āļĄāļŠāļģāļŦāļĢāļąāļšāļāļēāļĢ Runtime | +| `is_active` | `BOOLEAN` | `DEFAULT TRUE` | āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™ (`TRUE` = āđƒāļŠāđ‰āļ‡āļēāļ™, `FALSE` = āļĒāļāđ€āļĨāļīāļ/āļ›āļīāļ”āđƒāļŠāđ‰āļ‡āļēāļ™) | +| `created_at` | `TIMESTAMP` | `DEFAULT CURRENT_TIMESTAMP` | āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ | +| `updated_at` | `TIMESTAMP` | `DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` | āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ” | + +**Indexes:** + +- `PRIMARY KEY (id)` +- `UNIQUE KEY uq_workflow_version (workflow_code, version)`: āļ›āđ‰āļ­āļ‡āļāļąāļ™āļāļēāļĢāļĄāļĩ Workflow Code āđāļĨāļ° Version āļ‹āđ‰āļģāļāļąāļ™ +- `INDEX idx_workflow_active (workflow_code, is_active, version)`: āļŠāļģāļŦāļĢāļąāļšāļāļēāļĢāļ„āđ‰āļ™āļŦāļē Workflow āļ—āļĩāđˆ Active āļĨāđˆāļēāļŠāļļāļ”āđ„āļ”āđ‰āļ­āļĒāđˆāļēāļ‡āļĢāļ§āļ”āđ€āļĢāđ‡āļ§ + +--- + +#### **2. workflow_instances** + +**Purpose:** āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļŠāļ–āļēāļ™āļ°āļāļēāļĢāđ€āļ”āļīāļ™āđ€āļĢāļ·āđˆāļ­āļ‡āļˆāļĢāļīāļ‡āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢāđāļ•āđˆāļĨāļ°āļ‰āļšāļąāļš (Runtime State) + +| Column Name | Data Type | Constraints | Description | +| :-------------- | :------------ | :------------------------------------------------------ | :----------------------------------------------------------------------------------------------- | +| `id` | `CHAR(36)` | **PK**, `NOT NULL` | āļĢāļŦāļąāļŠ UUID āļ‚āļ­āļ‡ Workflow Instance | +| `definition_id` | `CHAR(36)` | **FK**, `NOT NULL` | āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđ„āļ›āļĒāļąāļ‡ `workflow_definitions.id` āđ€āļžāļ·āđˆāļ­āļĢāļ°āļšāļļāļ§āđˆāļēāđƒāļŠāđ‰āļāļŽāļŠāļļāļ”āđ„āļŦāļ™ | +| `entity_type` | `VARCHAR(50)` | `NOT NULL` | āļ›āļĢāļ°āđ€āļ āļ—āđ€āļ­āļāļŠāļēāļĢāļ—āļĩāđˆāļœāļđāļāļāļąāļš Workflow āļ™āļĩāđ‰ āđ€āļŠāđˆāļ™ `rfa_revision`, `correspondence_revision`, `circulation` | +| `entity_id` | `VARCHAR(50)` | `NOT NULL` | āļĢāļŦāļąāļŠāļ­āđ‰āļēāļ‡āļ­āļīāļ‡ (ID) āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ (āđ€āļāđ‡āļšāđ€āļ›āđ‡āļ™ String āđ€āļžāļ·āđˆāļ­āļĢāļ­āļ‡āļĢāļąāļšāļ—āļąāđ‰āļ‡ Int āđāļĨāļ° UUID) | +| `current_state` | `VARCHAR(50)` | `NOT NULL` | āļŠāļ·āđˆāļ­āļŠāļ–āļēāļ™āļ°āļ›āļąāļˆāļˆāļļāļšāļąāļ™āļ‚āļ­āļ‡āđ€āļ­āļāļŠāļēāļĢ āđ€āļŠāđˆāļ™ `DRAFT`, `IN_REVIEW`, `APPROVED` | +| `status` | `ENUM` | `DEFAULT 'ACTIVE'` | āļŠāļ–āļēāļ™āļ°āļ āļēāļžāļĢāļ§āļĄāļ‚āļ­āļ‡ Workflow Instance: `'ACTIVE'`, `'COMPLETED'`, `'CANCELLED'`, `'TERMINATED'` | +| `context` | `JSON` | `NULL` | āļ•āļąāļ§āđāļ›āļĢ Context āļŠāļģāļŦāļĢāļąāļšāđƒāļŠāđ‰āļ›āļĢāļ°āļāļ­āļšāļāļēāļĢāļ•āļąāļ”āļŠāļīāļ™āđƒāļˆāđƒāļ™ Workflow (Runtime Data) | +| `created_at` | `TIMESTAMP` | `DEFAULT CURRENT_TIMESTAMP` | āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡ Instance | +| `updated_at` | `TIMESTAMP` | `DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP` | āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļ­āļąāļ›āđ€āļ”āļ•āļŠāļ–āļēāļ™āļ°āļĨāđˆāļēāļŠāļļāļ” | + +**Indexes:** + +- `PRIMARY KEY (id)` +- `INDEX idx_wf_inst_entity (entity_type, entity_id)`: āļŠāļģāļŦāļĢāļąāļšāļ„āđ‰āļ™āļŦāļē Instance āļˆāļēāļāđ€āļ­āļāļŠāļēāļĢāļ•āđ‰āļ™āļ—āļēāļ‡ +- `INDEX idx_wf_inst_state (current_state)`: āļŠāļģāļŦāļĢāļąāļšāļ„āđ‰āļ™āļŦāļēāđ€āļ­āļāļŠāļēāļĢāļ•āļēāļĄāļŠāļ–āļēāļ™āļ° Workflow +- `CONSTRAINT fk_wf_inst_def FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE` + +--- + +#### **3. workflow_histories** + +**Purpose:** āļ•āļēāļĢāļēāļ‡āđ€āļāđ‡āļšāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (Audit Log / History) āđ€āļžāļ·āđˆāļ­āļ•āļĢāļ§āļˆāļŠāļ­āļšāļĒāđ‰āļ­āļ™āļŦāļĨāļąāļ‡ + +| Column Name | Data Type | Constraints | Description | +| :------------------ | :------------ | :-------------------------- | :--------------------------------------------------------------------- | +| `id` | `CHAR(36)` | **PK**, `NOT NULL` | āļĢāļŦāļąāļŠ UUID āļ‚āļ­āļ‡ History Record | +| `instance_id` | `CHAR(36)` | **FK**, `NOT NULL` | āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āđ„āļ›āļĒāļąāļ‡ `workflow_instances.id` | +| `from_state` | `VARCHAR(50)` | `NOT NULL` | āļŠāļ–āļēāļ™āļ°āļ•āđ‰āļ™āļ—āļēāļ‡āļāđˆāļ­āļ™āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ | +| `to_state` | `VARCHAR(50)` | `NOT NULL` | āļŠāļ–āļēāļ™āļ°āļ›āļĨāļēāļĒāļ—āļēāļ‡āļŦāļĨāļąāļ‡āļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āđāļ›āļĨāļ‡ | +| `action` | `VARCHAR(50)` | `NOT NULL` | āļŠāļ·āđˆāļ­ Action āļ—āļĩāđˆāļœāļđāđ‰āđƒāļŠāđ‰āļāļĢāļ°āļ—āļģ āđ€āļŠāđˆāļ™ `SUBMIT`, `APPROVE`, `REJECT` | +| `action_by_user_id` | `INT` | `NULL` | āļĢāļŦāļąāļŠāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User ID) āļ—āļĩāđˆāļ—āļģāļāļēāļĢāđ€āļ›āļĨāļĩāđˆāļĒāļ™āļŠāļ–āļēāļ™āļ° (`NULL` āļāļĢāļ“āļĩ System Auto) | +| `comment` | `TEXT` | `NULL` | āļ„āļ§āļēāļĄāļ„āļīāļ”āđ€āļŦāđ‡āļ™āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄāļ›āļĢāļ°āļāļ­āļšāļāļēāļĢāļāļĢāļ°āļ—āļģ | +| `metadata` | `JSON` | `NULL` | Snapshot āļ‚āđ‰āļ­āļĄāļđāļĨ Context āļŦāļĢāļ·āļ­āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄ āļ“ āļ‚āļ“āļ°āļ™āļąāđ‰āļ™ | +| `created_at` | `TIMESTAMP` | `DEFAULT CURRENT_TIMESTAMP` | āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāđ€āļāļīāļ”āđ€āļŦāļ•āļļāļāļēāļĢāļ“āđŒ | + +**Indexes:** + +- `PRIMARY KEY (id)` +- `INDEX idx_wf_hist_instance (instance_id)`: āļŠāļģāļŦāļĢāļąāļšāļ”āļķāļ‡āļ›āļĢāļ°āļ§āļąāļ•āļīāļ—āļąāđ‰āļ‡āļŦāļĄāļ”āļ‚āļ­āļ‡ Instance āļŦāļ™āļķāđˆāļ‡āđ† +- `INDEX idx_wf_hist_user (action_by_user_id)`: āļŠāļģāļŦāļĢāļąāļšāļ”āļđāļ›āļĢāļ°āļ§āļąāļ•āļīāļāļēāļĢāļ—āļģāļ‡āļēāļ™āļ‚āļ­āļ‡āļœāļđāđ‰āđƒāļŠāđ‰āđāļ•āđˆāļĨāļ°āļ„āļ™ +- `CONSTRAINT fk_wf_hist_inst FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE` + +--- + +### **ER Diagram Concept** + +```mermaid +erDiagram + workflow_definitions ||--o{ workflow_instances : "defines" + workflow_instances ||--o{ workflow_histories : "has history" + + workflow_definitions { + char(36) id PK + varchar(50) workflow_code + int version + json dsl + json compiled + boolean is_active + } + + workflow_instances { + char(36) id PK + char(36) definition_id FK + varchar(50) entity_type + varchar(50) entity_id + varchar(50) current_state + enum status + json context + } + + workflow_histories { + char(36) id PK + char(36) instance_id FK + varchar(50) from_state + varchar(50) to_state + varchar(50) action + int action_by_user_id + text comment + json metadata + } ``` --- -### 📝 2. āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”āļ‡āļēāļ™āđƒāļ™āđāļ•āđˆāļĨāļ° Task (Tasks Breakdown) - -#### **[ ] T2.5.1 JSON Schema Registry & Versioning** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļŠāļĢāđ‰āļēāļ‡ `JsonSchema` Entity -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - āđ€āļāđ‡āļš `schema_definition` (AJV format) - - āđ€āļāđ‡āļš `ui_schema` (Form configuration) - - āļĢāļ­āļ‡āļĢāļąāļš `version` āđ€āļžāļ·āđˆāļ­āļ—āļģ Schema Evolution - - āđ€āļāđ‡āļš config āļ‚āļ­āļ‡ `virtual_columns` - -#### **[ ] T2.5.2 Schema Validation & Transformation Engine** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** Implement Logic āđƒāļ™ `JsonSchemaService` -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - āļ•āļīāļ”āļ•āļąāđ‰āļ‡āđāļĨāļ° Config `AJV` library - - āļŠāļĢāđ‰āļēāļ‡ Custom Keywords: `document-number`, `requiredRole` - - āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ `validateData(schemaName, data)` - - āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ `sanitizeData()` āđ€āļžāļ·āđˆāļ­āļĨāļš field āļ—āļĩāđˆāđ„āļĄāđˆāļ­āļĒāļđāđˆāđƒāļ™ schema āļŦāļĢāļ·āļ­ field āļ—āļĩāđˆ user āđ„āļĄāđˆāļĄāļĩāļŠāļīāļ—āļ˜āļīāđŒ - -#### **[ ] T2.5.3 Virtual Columns & Performance Optimization** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļŠāļĢāđ‰āļēāļ‡ `VirtualColumnService` -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ `setupVirtualColumns()`: āļ­āđˆāļēāļ™ Config āļˆāļēāļ Schema āđāļĨāđ‰āļ§āļŠāļąāđˆāļ‡ `ALTER TABLE ... ADD COLUMN ... GENERATED ALWAYS AS ...` - - āļŸāļąāļ‡āļāđŒāļŠāļąāļ™āļŠāļĢāđ‰āļēāļ‡ Index āļšāļ™ Virtual Column āđ€āļžāļ·āđˆāļ­āđƒāļŦāđ‰āļ„āđ‰āļ™āļŦāļēāđ€āļĢāđ‡āļ§āļ‚āļķāđ‰āļ™ - - **āļŠāļģāļ„āļąāļ:** āļ•āđ‰āļ­āļ‡āļĢāļ­āļ‡āļĢāļąāļš MariaDB 10.11 Syntax - -#### **[ ] T2.5.4 Dynamic Form Schema Management** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļāļģāļŦāļ™āļ” Interface āđƒāļ™ `ui-schema.interface.ts` -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - āļ­āļ­āļāđāļšāļšāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡ JSON āļ—āļĩāđˆ Frontend āļˆāļ°āļ™āļģāđ„āļ› Render āđ€āļ›āđ‡āļ™ Form - - āļāļģāļŦāļ™āļ” Logic `dependencies` (āđ€āļŠāđˆāļ™ āļ–āđ‰āļēāđ€āļĨāļ·āļ­āļ Type A āđƒāļŦāđ‰āđāļŠāļ”āļ‡ Field B) - -#### **[ ] T2.5.5 Data Migration & Version Compatibility** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļŠāļĢāđ‰āļēāļ‡ `SchemaMigrationService` -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - Logic āļāļēāļĢāđāļ›āļĨāļ‡āļ‚āđ‰āļ­āļĄāļđāļĨāđ€āļāđˆāļēāđƒāļŦāđ‰āđ€āļ‚āđ‰āļēāļāļąāļš Schema āđƒāļŦāļĄāđˆ (Field Rename, Transform Value) - - āļŸāļąāļ‡āļāđŒāļŠāļąāļ™ `migrateData(entityType, entityId, targetVersion)` - -#### **[ ] T2.5.6 Security & Access Control for JSON Data** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļŠāļĢāđ‰āļēāļ‡ `JsonSecurityService` -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - **Field-level Security:** āļ•āļĢāļ§āļˆāļŠāļ­āļš Role āļ§āđˆāļēāđ€āļŦāđ‡āļ™ Field āļ™āļĩāđ‰āđ„āļ”āđ‰āđ„āļŦāļĄ (Masking/Removal) - - **Encryption:** āđ€āļ‚āđ‰āļēāļĢāļŦāļąāļŠ Field āļ—āļĩāđˆ Sensitive āļāđˆāļ­āļ™āļšāļąāļ™āļ—āļķāļāļĨāļ‡ JSON - -#### **[ ] T2.5.7 API Design & Integration** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āļŠāļĢāđ‰āļēāļ‡ `JsonSchemaController` -- **Endpoints:** - - `POST /json-schema/schemas`: āļŠāļĢāđ‰āļēāļ‡ Schema - - `POST /json-schema/validate/:name`: āļ•āļĢāļ§āļˆāļŠāļ­āļšāļ‚āđ‰āļ­āļĄāļđāļĨ - - `POST /json-schema/migrate/:type/:id`: āļŠāļąāđˆāļ‡ Migrate - - `GET /json-schema/ui-schema/:name`: āļ”āļķāļ‡ UI Config - -#### **[ ] T2.5.8 Integration with Document Modules** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āđāļāđ‰āđ„āļ‚ Service āļ­āļ·āđˆāļ™āđ† (Correspondence, RFA) -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - Inject `JsonSchemaService` āđ€āļ‚āđ‰āļēāđ„āļ›āđƒāļ™ `CorrespondenceService` āđāļĨāļ° `RfaService` - - āđ€āļĢāļĩāļĒāļ `validateData` āļāđˆāļ­āļ™ create/update - - āđ€āļĢāļĩāļĒāļ `setupVirtualColumns` āļŦāļĨāļąāļ‡ create schema āđƒāļŦāļĄāđˆ - -#### **[ ] T2.5.9 Testing Strategy** - -- **āļŠāļīāđˆāļ‡āļ—āļĩāđˆāļ•āđ‰āļ­āļ‡āļ—āļģ:** āđ€āļ‚āļĩāļĒāļ™ Unit Test -- **āļĢāļēāļĒāļĨāļ°āđ€āļ­āļĩāļĒāļ”:** - - āļ—āļ”āļŠāļ­āļš Validation Logic (āļœāđˆāļēāļ™/āđ„āļĄāđˆāļœāđˆāļēāļ™) - - āļ—āļ”āļŠāļ­āļš Virtual Column Generation (SQL āļ–āļđāļāļ•āđ‰āļ­āļ‡āđ„āļŦāļĄ) - - āļ—āļ”āļŠāļ­āļš Security Masking - ---- - -### 🚀 āļ‚āļąāđ‰āļ™āļ•āļ­āļ™āļ–āļąāļ”āđ„āļ› - -āļŦāļēāļāļ—āđˆāļēāļ™āđ€āļŦāđ‡āļ™āļ”āđ‰āļ§āļĒāļāļąāļšāđ‚āļ„āļĢāļ‡āļŠāļĢāđ‰āļēāļ‡āļ™āļĩāđ‰ āļœāļĄāđāļ™āļ°āļ™āļģāđƒāļŦāđ‰āđ€āļĢāļīāđˆāļĄāļ”āļģāđ€āļ™āļīāļ™āļāļēāļĢāļ•āļēāļĄāļĨāļģāļ”āļąāļšāļ”āļąāļ‡āļ™āļĩāđ‰āļ„āļĢāļąāļš: - -1. **āļŠāļĢāđ‰āļēāļ‡ Entity āđāļĨāļ° DTOs** (T2.5.1) -2. **Implement JsonSchemaService Core** (Validation Logic) (T2.5.2) -3. **Implement VirtualColumnService** (T2.5.3) - _āļŠāđˆāļ§āļ™āļ™āļĩāđ‰āļŠāļģāļ„āļąāļāļĄāļēāļāļŠāļģāļŦāļĢāļąāļš Performance_ -4. **āļŠāļĢāđ‰āļēāļ‡ Controller āđāļĨāļ°āđ€āļŠāļ·āđˆāļ­āļĄāļ•āđˆāļ­ Module** (T2.5.7) - -āļ—āđˆāļēāļ™āļ•āđ‰āļ­āļ‡āļāļēāļĢāđƒāļŦāđ‰āļœāļĄāđ€āļĢāļīāđˆāļĄāđ€āļ‚āļĩāļĒāļ™āđ‚āļ„āđ‰āļ”āļŠāđˆāļ§āļ™āđ„āļŦāļ™āļāđˆāļ­āļ™āļŦāļĢāļ·āļ­āđ„āļĄāđˆāļ„āļĢāļąāļš? (āđ€āļŠāđˆāļ™ Entity āļŦāļĢāļ·āļ­ VirtualColumnService) +āļ‚āđ‰āļ­āļĄāļđāļĨāļ™āļĩāđ‰āļ­āđ‰āļēāļ‡āļ­āļīāļ‡āļˆāļēāļāđ„āļŸāļĨāđŒ SQL Script āļĨāđˆāļēāļŠāļļāļ”āļ—āļĩāđˆāļ„āļļāļ“āđ€āļĨāļ·āļ­āļāļĄāļēāļ„āļĢāļąāļš āļŦāļēāļāļ•āđ‰āļ­āļ‡āļāļēāļĢāđāļāđ‰āđ„āļ‚āļŦāļĢāļ·āļ­āđ€āļžāļīāđˆāļĄāđ€āļ•āļīāļĄāļŠāđˆāļ§āļ™āđ„āļŦāļ™ āđāļˆāđ‰āļ‡āđ„āļ”āđ‰āđ€āļĨāļĒāļ„āļĢāļąāļš diff --git a/docs/ux_ui.md b/docs/ux_ui.md deleted file mode 100644 index dc44b48..0000000 --- a/docs/ux_ui.md +++ /dev/null @@ -1,42 +0,0 @@ - - You are an expert in Bootstrap and modern web application development. - - Key Principles - - Write clear, concise, and technical responses with precise Bootstrap examples. - - Utilize Bootstrap's components and utilities to streamline development and ensure responsiveness. - - Prioritize maintainability and readability; adhere to clean coding practices throughout your HTML and CSS. - - Use descriptive class names and structure to promote clarity and collaboration among developers. - - Bootstrap Usage - - Leverage Bootstrap's grid system for responsive layouts; use container, row, and column classes to structure content. - - Utilize Bootstrap components (e.g., buttons, modals, alerts) to enhance user experience without extensive custom CSS. - - Apply Bootstrap's utility classes for quick styling adjustments, such as spacing, typography, and visibility. - - Ensure all components are accessible; use ARIA attributes and semantic HTML where applicable. - - Error Handling and Validation - - Implement form validation using Bootstrap's built-in styles and classes to enhance user feedback. - - Use Bootstrap's alert component to display error messages clearly and informatively. - - Structure forms with appropriate labels, placeholders, and error messages for a better user experience. - - Dependencies - - Bootstrap (latest version, CSS and JS) - - Any JavaScript framework (like jQuery, if required) for interactive components. - - Bootstrap-Specific Guidelines - - Customize Bootstrap's Sass variables and mixins to create a unique theme without overriding default styles. - - Utilize Bootstrap's responsive utilities to control visibility and layout on different screen sizes. - - Keep custom styles to a minimum; use Bootstrap's classes wherever possible for consistency. - - Use the Bootstrap documentation to understand component behavior and customization options. - - Performance Optimization - - Minimize file sizes by including only the necessary Bootstrap components in your build process. - - Use a CDN for Bootstrap resources to improve load times and leverage caching. - - Optimize images and other assets to enhance overall performance, especially for mobile users. - - Key Conventions - 1. Follow Bootstrap's naming conventions and class structures to ensure consistency across your project. - 2. Prioritize responsiveness and accessibility in every stage of development. - 3. Maintain a clear and organized file structure to enhance maintainability and collaboration. - - Refer to the Bootstrap documentation for best practices and detailed examples of usage patterns. - \ No newline at end of file diff --git a/users.sql b/users.sql deleted file mode 100644 index 617ef21..0000000 --- a/users.sql +++ /dev/null @@ -1,173 +0,0 @@ --- phpMyAdmin SQL Dump --- version 5.2.3 --- https://www.phpmyadmin.net/ --- --- Host: mariadb --- Generation Time: Nov 21, 2025 at 03:33 AM --- Server version: 11.8.5-MariaDB-ubu2404 --- PHP Version: 8.3.27 -SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO"; -START TRANSACTION; -SET time_zone = "+00:00"; -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */ -; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */ -; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */ -; -/*!40101 SET NAMES utf8mb4 */ -; --- --- Database: `lcbp3_dev` --- - --- -------------------------------------------------------- --- --- Table structure for table `users` --- - -CREATE TABLE `users` ( - `user_id` int(11) NOT NULL COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', - `username` varchar(50) NOT NULL COMMENT 'āļŠāļ·āđˆāļ­āļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™', - `password_hash` varchar(255) NOT NULL COMMENT 'āļĢāļŦāļąāļŠāļœāđˆāļēāļ™ (Hashed)', - `first_name` varchar(50) DEFAULT NULL COMMENT 'āļŠāļ·āđˆāļ­āļˆāļĢāļīāļ‡', - `last_name` varchar(50) DEFAULT NULL COMMENT 'āļ™āļēāļĄāļŠāļāļļāļĨ', - `email` varchar(100) NOT NULL COMMENT 'āļ­āļĩāđ€āļĄāļĨ', - `line_id` varchar(100) DEFAULT NULL COMMENT 'LINE ID', - `primary_organization_id` int(11) DEFAULT NULL COMMENT 'āļŠāļąāļ‡āļāļąāļ”āļ­āļ‡āļ„āđŒāļāļĢ', - `is_active` tinyint(1) DEFAULT 1 COMMENT 'āļŠāļ–āļēāļ™āļ°āļāļēāļĢāđƒāļŠāđ‰āļ‡āļēāļ™', - `failed_attempts` int(11) DEFAULT 0 COMMENT 'āļˆāļģāļ™āļ§āļ™āļ„āļĢāļąāđ‰āļ‡āļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™āļĨāđ‰āļĄāđ€āļŦāļĨāļ§', - `locked_until` datetime DEFAULT NULL COMMENT 'āļĨāđ‡āļ­āļāļ­āļīāļ™āđ„āļĄāđˆāđ„āļ”āđ‰āļˆāļ™āļ–āļķāļ‡āđ€āļ§āļĨāļē', - `last_login_at` timestamp NULL DEFAULT NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļĨāļ°āđ€āļ§āļĨāļēāļ—āļĩāđˆāļĨāđ‡āļ­āļāļ­āļīāļ™āļĨāđˆāļēāļŠāļļāļ”', - `created_at` timestamp NULL DEFAULT current_timestamp() COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļŠāļĢāđ‰āļēāļ‡', - `updated_at` timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāđāļāđ‰āđ„āļ‚āļĨāđˆāļēāļŠāļļāļ”', - `deleted_at` datetime DEFAULT NULL COMMENT 'āļ§āļąāļ™āļ—āļĩāđˆāļĨāļš' -) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'āļ•āļēāļĢāļēāļ‡ Master āđ€āļāđ‡āļšāļ‚āđ‰āļ­āļĄāļđāļĨāļœāļđāđ‰āđƒāļŠāđ‰āļ‡āļēāļ™ (User)'; --- --- Dumping data for table `users` --- - -INSERT INTO `users` ( - `user_id`, - `username`, - `password_hash`, - `first_name`, - `last_name`, - `email`, - `line_id`, - `primary_organization_id`, - `is_active`, - `failed_attempts`, - `locked_until`, - `last_login_at`, - `created_at`, - `updated_at`, - `deleted_at` - ) -VALUES ( - 1, - 'superadmin', - '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', - 'Super', - 'Admin', - 'superadmin @example.com', - NULL, - NULL, - 1, - 0, - NULL, - NULL, - '2025-11-19 08:47:47', - '2025-11-21 03:02:20', - NULL - ), - ( - 2, - 'editor01', - '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', - 'DC', - 'C1', - 'editor01 @example.com', - NULL, - 41, - 1, - 0, - NULL, - NULL, - '2025-11-19 08:47:47', - '2025-11-20 02:57:04', - NULL - ), - ( - 3, - 'viewer01', - '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', - 'Viewer', - 'āļŠāļ„āļ‰.03', - 'viewer01 @example.com', - NULL, - 10, - 1, - 0, - NULL, - NULL, - '2025-11-19 08:47:47', - '2025-11-20 02:55:50', - NULL - ), - ( - 5, - 'admin', - '$2b$10$E6d5k.f46jr.POGWKHhiQ.X1ZsFrMpZox//sCxeOiLUULGuAHO0NW', - 'Admin', - 'āļ„āļ„āļ‡.', - 'admin@example.com', - NULL, - 1, - 1, - 0, - NULL, - NULL, - '2025-11-19 08:57:20', - '2025-11-21 02:56:02', - NULL - ); --- --- Indexes for dumped tables --- - --- --- Indexes for table `users` --- -ALTER TABLE `users` -ADD PRIMARY KEY (`user_id`), - ADD UNIQUE KEY `username` (`username`), - ADD UNIQUE KEY `email` (`email`), - ADD KEY `primary_organization_id` (`primary_organization_id`); --- --- AUTO_INCREMENT for dumped tables --- - --- --- AUTO_INCREMENT for table `users` --- -ALTER TABLE `users` -MODIFY `user_id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID āļ‚āļ­āļ‡āļ•āļēāļĢāļēāļ‡', - AUTO_INCREMENT = 6; --- --- Constraints for dumped tables --- - --- --- Constraints for table `users` --- -ALTER TABLE `users` -ADD CONSTRAINT `users_ibfk_1` FOREIGN KEY (`primary_organization_id`) REFERENCES `organizations` (`id`) ON DELETE -SET NULL; -COMMIT; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */ -; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */ -; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */ -; \ No newline at end of file