260322:1648 Correct Coresspondence / Doing RFA / Correct CI
This commit is contained in:
@@ -1,25 +1,29 @@
|
||||
# 4. Access Control & RBAC Matrix (V1.8.0)
|
||||
|
||||
---
|
||||
|
||||
title: 'Access Control & RBAC Matrix'
|
||||
version: 1.8.0
|
||||
status: APPROVED
|
||||
owner: Nattanin Peancharoen / Development Team
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/02-architecture/02-01-system-architecture.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-005-redis-usage-strategy.md
|
||||
- specs/05-decisions/ADR-001-unified-workflow-engine.md
|
||||
references:
|
||||
- [RBAC Implementation](../../99-archives/ADR-004-rbac-implementation.md)
|
||||
- [Access Control](../../99-archives/01-04-access-control.md)
|
||||
|
||||
- specs/02-architecture/02-01-system-architecture.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-005-redis-usage-strategy.md
|
||||
- specs/05-decisions/ADR-001-unified-workflow-engine.md
|
||||
references:
|
||||
- [RBAC Implementation](../../99-archives/ADR-004-rbac-implementation.md)
|
||||
- [Access Control](../../99-archives/01-04-access-control.md)
|
||||
|
||||
---
|
||||
|
||||
## 4.1. Overview and Problem Statement
|
||||
|
||||
LCBP3-DMS จัดการสิทธิ์การเข้าถึงข้อมูลที่ซับซ้อน ได้แก่:
|
||||
|
||||
- **Multi-Organization**: หลายองค์กรใช้ระบบร่วมกัน แต่ต้องแยกข้อมูลตามบริบท
|
||||
- **Project-Based**: โครงการสามารถมีหลายสัญญาย่อย (Contracts)
|
||||
- **Hierarchical Permissions**: สิทธิ์ระดับบนสามารถครอบคลุมระดับล่าง
|
||||
@@ -28,6 +32,7 @@ LCBP3-DMS จัดการสิทธิ์การเข้าถึงข
|
||||
Users และ Organizations สามารถเข้าดูหรือแก้ไขเอกสารได้จากสิทธิ์ที่ได้รับ ระบบออกแบบด้วย **4-Level Hierarchical Role-Based Access Control (RBAC)** ដើម្បីรองรับความซับซ้อนนี้.
|
||||
|
||||
### Key Requirements
|
||||
|
||||
1. User หนึ่งคนสามารถมีหลาย Roles ในหลาย Scopes
|
||||
2. Permission Inheritance (Global → Organization → Project → Contract)
|
||||
3. Fine-grained Access Control (เช่น "อนุญาตให้ดู Correspondence เฉพาะใน Project A รวมถึง Contract ภายใต้ Project A เท่านั้น")
|
||||
@@ -56,8 +61,9 @@ Global (ทั้งระบบ)
|
||||
```
|
||||
|
||||
**Permission Enforcement:**
|
||||
|
||||
- เมื่อตรวจสอบสิทธิ์ ระบบจะพิจารณาสิทธิ์จากทุก Level ที่ผู้ใช้มี และใช้สิทธิ์ที่ **"ครอบคลุมที่สุด (Most Permissive)"** ในบริบท (Context) นั้นมาเป็นเกณฑ์.
|
||||
- *Example*: User A เป็น `Viewer` ในองค์กร (ระดับ Organization Level) แต่มอบหมายหน้าที่ให้เป็น `Editor` ในตำแหน่งของ Project X. เมื่อ User A ดำเนินการในบริบท Context ของ Project X (หรือ Contract ที่อยู่ใต้ Project X), User A จะสามารถทำงานด้วยสิทธิ์ `Editor` ทันที.
|
||||
- _Example_: User A เป็น `Viewer` ในองค์กร (ระดับ Organization Level) แต่มอบหมายหน้าที่ให้เป็น `Editor` ในตำแหน่งของ Project X. เมื่อ User A ดำเนินการในบริบท Context ของ Project X (หรือ Contract ที่อยู่ใต้ Project X), User A จะสามารถทำงานด้วยสิทธิ์ `Editor` ทันที.
|
||||
|
||||
---
|
||||
|
||||
@@ -75,14 +81,14 @@ Global (ทั้งระบบ)
|
||||
|
||||
### Master Data Management Authority
|
||||
|
||||
| Master Data | Manager | Scope |
|
||||
| :-------------------------------------- | :------------------------------ | :------------------------------ |
|
||||
| Document Type (Correspondence, RFA) | **Superadmin** | Global |
|
||||
| Document Status (Draft, Approved, etc.) | **Superadmin** | Global |
|
||||
| Master Data | Manager | Scope |
|
||||
| :-------------------------------------- | :------------------------------ | :--------------------------------- |
|
||||
| Document Type (Correspondence, RFA) | **Superadmin** | Global |
|
||||
| Document Status (Draft, Approved, etc.) | **Superadmin** | Global |
|
||||
| Shop Drawing Category | **Project Manager** | Project (สร้างใหม่ได้ภายในโครงการ) |
|
||||
| Tags | **Org Admin / Project Manager** | Organization / Project |
|
||||
| Custom Roles | **Superadmin / Org Admin** | Global / Organization |
|
||||
| Document Numbering Formats | **Superadmin / Admin** | Global / Organization |
|
||||
| Tags | **Org Admin / Project Manager** | Organization / Project |
|
||||
| Custom Roles | **Superadmin / Org Admin** | Global / Organization |
|
||||
| Document Numbering Formats | **Superadmin / Admin** | Global / Organization |
|
||||
|
||||
---
|
||||
|
||||
@@ -272,13 +278,11 @@ Controller Example Usage:
|
||||
@Controller('correspondences')
|
||||
@UseGuards(JwtAuthGuard, PermissionGuard)
|
||||
export class CorrespondenceController {
|
||||
|
||||
@Post()
|
||||
@RequirePermission('correspondence.create')
|
||||
async create(@Body() dto: CreateCorrespondenceDto) {
|
||||
// Accessible only when CASL Evaluate "correspondence.create" successfully fits the parameters limits (Project/Contract scope check)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
@@ -299,15 +303,21 @@ export class CorrespondenceController {
|
||||
ส่วนอ้างอิงและประวัติศาสตร์การตัดสินใจพิจารณาในอดีต (Reference) ก่อนการนำมาปรับใช้ร่าง Architecture ฉบับ V1.8.0.
|
||||
|
||||
### Considered Options Before Version 1.5.0
|
||||
|
||||
1. **Option 1**: Simple Role-Based (No Scope)
|
||||
- *Pros*: Very simple implementation, Easy to understand
|
||||
- *Cons*: ไม่รองรับ Multi-organization, Superadmin เห็นข้อมูลองค์กรอื่นมั่วข้ามกลุ่ม
|
||||
|
||||
- _Pros_: Very simple implementation, Easy to understand
|
||||
- _Cons_: ไม่รองรับ Multi-organization, Superadmin เห็นข้อมูลองค์กรอื่นมั่วข้ามกลุ่ม
|
||||
|
||||
2. **Option 2**: Organization-Only Scope
|
||||
- *Pros*: แยกข้อมูลระหว่าง Organizations ได้ชัดเจน
|
||||
- *Cons*: ไม่รองรับระดับ Sub-level Permissions (Project/Contract) ไม่เพียงพอกับ Business Rule.
|
||||
|
||||
- _Pros_: แยกข้อมูลระหว่าง Organizations ได้ชัดเจน
|
||||
- _Cons_: ไม่รองรับระดับ Sub-level Permissions (Project/Contract) ไม่เพียงพอกับ Business Rule.
|
||||
|
||||
3. **Option 3**: **4-Level Hierarchical RBAC (Selected)**
|
||||
- *Pros*: ครอบคลุม Use Case สูงสุด (Maximum Flexibility), รองรับ Hierarchy สืบทอดสิทธิ์ (Inheritance), ขอบเขตจำกัดข้อมูลอย่างรัดกุมระดับ Contract (Data Isolation).
|
||||
- *Cons*: Complexity ทางด้านการ Implement, Invalidate Caching หางานให้ฝั่ง Operations, Developer Learning Curve.
|
||||
|
||||
- _Pros_: ครอบคลุม Use Case สูงสุด (Maximum Flexibility), รองรับ Hierarchy สืบทอดสิทธิ์ (Inheritance), ขอบเขตจำกัดข้อมูลอย่างรัดกุมระดับ Contract (Data Isolation).
|
||||
- _Cons_: Complexity ทางด้านการ Implement, Invalidate Caching หางานให้ฝั่ง Operations, Developer Learning Curve.
|
||||
|
||||
**Decision Rationale:**
|
||||
เลือกแนวทางที่ 3 รองรับความต้องการของ Construction Projects ที่มีการแบ่งกลุ่มรับเหมาช่วงย่อยระดับ Contracts ต่อเนื่อง (Future proof). แก้ไขปัญหา Performance Cons ด้วยแนวคิดการประจุ Redis Caching ข้ามขั้ว และล้าง Invalidation เฉพาะเมื่อ Triggering Database Updates (อ้างอิงหัวข้อ 4.6).
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
# 3.11 Document Numbering Management & Implementation (V1.8.0)
|
||||
|
||||
---
|
||||
|
||||
title: 'Specifications & Implementation Guide: Document Numbering System'
|
||||
version: 1.8.0
|
||||
status: draft
|
||||
owner: Nattanin Peancharoen / Development Team
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/01-requirements/01-01-objectives.md
|
||||
- specs/02-architecture/README.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/04-operations/04-08-document-numbering-operations.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-002-document-numbering-strategy.md
|
||||
Clean Version v1.8.0 – Scope of Changes:
|
||||
- รวม Functional Requirements เข้ากับ Implementation Guide
|
||||
- เลือกใช้ Single Numbering System (Option A) `document_number_counters` เป็น Authoritative Counter
|
||||
- เพิ่ม Idempotency Key, Reservation (Two-Phase Commit)
|
||||
- Number State Machine, Pattern Validate UTF-8, Cancellation Rule (Void/Replace)
|
||||
references:
|
||||
- [Document Numbering](../../99-archives/01-03.11-document-numbering.md)
|
||||
- [Document Numbering](../../99-archives/03-04-document-numbering.md)
|
||||
|
||||
- specs/01-requirements/01-01-objectives.md
|
||||
- specs/02-architecture/README.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/04-operations/04-08-document-numbering-operations.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-002-document-numbering-strategy.md
|
||||
Clean Version v1.8.0 – Scope of Changes:
|
||||
- รวม Functional Requirements เข้ากับ Implementation Guide
|
||||
- เลือกใช้ Single Numbering System (Option A) `document_number_counters` เป็น Authoritative Counter
|
||||
- เพิ่ม Idempotency Key, Reservation (Two-Phase Commit)
|
||||
- Number State Machine, Pattern Validate UTF-8, Cancellation Rule (Void/Replace)
|
||||
references:
|
||||
- [Document Numbering](../../99-archives/01-03.11-document-numbering.md)
|
||||
- [Document Numbering](../../99-archives/03-04-document-numbering.md)
|
||||
|
||||
---
|
||||
|
||||
> **📖 เอกสารฉบับนี้เป็นการรวมรายละเอียดจาก `01-03.11-document-numbering.md` และ `03-04-document-numbering.md` ให้อยู่ในฉบับเดียวสำหรับใช้อ้างอิงการออกแบบเชิง Functional และการพัฒนา Technology Component**
|
||||
@@ -32,6 +35,7 @@ references:
|
||||
ระบบ Document Numbering สำหรับสร้างเลขที่เอกสารอัตโนมัติที่มีความเป็นเอกลักษณ์ (unique) และสามารถติดตามได้ (traceable) สำหรับเอกสารทุกประเภทในระบบ LCBP3-DMS
|
||||
|
||||
### 1.1 Requirements Summary & Scope
|
||||
|
||||
- **Auto-generation**: สร้างเลขที่อัตโนมัติ ไม่ซ้ำ (Unique) ยืดหยุ่น
|
||||
- **Configurable Templates**: รองรับแบบฟอร์มกำหนดค่า สำหรับโปรเจกต์ ประเภทเอกสาร ฯลฯ
|
||||
- **Uniqueness Guarantee**: การันตี Uniqueness ใน Concurrent Environment (Race Conditions)
|
||||
@@ -40,6 +44,7 @@ references:
|
||||
- **Audit Logging**: บันทึกเหตุการณ์ Operation ทั้งหมดอย่างละเอียดครบถ้วน 7 ปี
|
||||
|
||||
### 1.2 Technology Stack
|
||||
|
||||
| Component | Technology |
|
||||
| ----------------- | -------------------- |
|
||||
| Backend Framework | NestJS 10.x |
|
||||
@@ -50,7 +55,9 @@ references:
|
||||
| Monitoring | Prometheus + Grafana |
|
||||
|
||||
### 1.3 Architectural Decision (AD-DN-001)
|
||||
|
||||
ระบบเลือกใช้ **Option A**:
|
||||
|
||||
- `document_number_counters` เป็น Core / Authoritative Counter System.
|
||||
- `document_numbering_configs` (หรือ `document_number_formats`) ใช้เฉพาะกำหนดระเบียบเลข (Template format) และ Permission.
|
||||
- เหตุผล: ลดความซ้ำซ้อน, ป้องกัน Counter Mismatch, Debug ง่าย, Ops เคลียร์.
|
||||
@@ -62,30 +69,33 @@ references:
|
||||
### 2.1 Counter Logic & Reset Policy
|
||||
|
||||
การนับเลขจะแยกตาม **Counter Key** ที่ประกอบด้วยหลายส่วน ซึ่งขึ้นกับประเภทเอกสาร
|
||||
* `(project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)`
|
||||
|
||||
- `(project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)`
|
||||
|
||||
| Document Type | Reset Policy | Counter Key Format / Details |
|
||||
| ---------------------------------- | ------------------ | ------------------------------------------------------------------------------ |
|
||||
| Correspondence (LETTER, MEMO, RFI) | Yearly reset | `(project_id, originator, recipient, type_id, 0, 0, 0, 'YEAR_2025')` |
|
||||
| Transmittal | Yearly reset | `(project_id, originator, recipient, type_id, sub_type_id, 0, 0, 'YEAR_2025')` |
|
||||
| RFA | No reset | `(project_id, originator, 0, type_id, 0, rfa_type_id, discipline_id, 'NONE')` |
|
||||
| Drawing | Separate Namespace | `DRAWING::<project>::<contract>` (ไม่ได้ใช้ counter rules เดียวกัน) |
|
||||
| Drawing | Separate Namespace | `DRAWING::<project>::<contract>` (ไม่ได้ใช้ counter rules เดียวกัน) |
|
||||
|
||||
### 2.2 Format Templates & Supported Tokens
|
||||
|
||||
**Supported Token Types**:
|
||||
* `{PROJECT}`: รหัสโครงการ (เช่น `LCBP3`)
|
||||
* `{ORIGINATOR}`: รหัสองค์กรส่ง (เช่น `คคง.`)
|
||||
* `{RECIPIENT}`: รหัสองค์กรรับหลัก (เช่น `สคฉ.3`) *ไม่ใช้กับ RFA
|
||||
* `{CORR_TYPE}`: รหัสประเภทเอกสาร (เช่น `RFA`, `LETTER`)
|
||||
* `{SUB_TYPE}`: ประเภทย่อย (สำหรับ Transmittal)
|
||||
* `{RFA_TYPE}`: รหัสประเภท RFA (เช่น `SDW`, `RPT`)
|
||||
* `{DISCIPLINE}`: รหัสสาขาวิชา (เช่น `STR`, `CV`)
|
||||
* `{SEQ:n}`: Running Number ตามจำนวนหลัก `n` ลบด้วยศูนย์นำหน้า
|
||||
* `{YEAR:B.E.}`, `{YEAR:A.D.}`, `{YYYY}`, `{YY}`, `{MM}`: สัญลักษณ์บอกเวลาและปฏิทิน.
|
||||
* `{REV}`: Revision Code (เช่น `A`, `B`)
|
||||
|
||||
- `{PROJECT}`: รหัสโครงการ (เช่น `LCBP3`)
|
||||
- `{ORIGINATOR}`: รหัสองค์กรส่ง (เช่น `คคง.`)
|
||||
- `{RECIPIENT}`: รหัสองค์กรรับหลัก (เช่น `สคฉ.3`) \*ไม่ใช้กับ RFA
|
||||
- `{CORR_TYPE}`: รหัสประเภทเอกสาร (เช่น `RFA`, `LETTER`)
|
||||
- `{SUB_TYPE}`: ประเภทย่อย (สำหรับ Transmittal)
|
||||
- `{RFA_TYPE}`: รหัสประเภท RFA (เช่น `SDW`, `RPT`)
|
||||
- `{DISCIPLINE}`: รหัสสาขาวิชา (เช่น `STR`, `CV`)
|
||||
- `{SEQ:n}`: Running Number ตามจำนวนหลัก `n` ลบด้วยศูนย์นำหน้า
|
||||
- `{YEAR:B.E.}`, `{YEAR:A.D.}`, `{YYYY}`, `{YY}`, `{MM}`: สัญลักษณ์บอกเวลาและปฏิทิน.
|
||||
- `{REV}`: Revision Code (เช่น `A`, `B`)
|
||||
|
||||
**Token Validation Grammar**
|
||||
|
||||
```ebnf
|
||||
TEMPLATE := TOKEN ("-" TOKEN)*
|
||||
TOKEN := SIMPLE | PARAM
|
||||
@@ -95,11 +105,13 @@ DIGIT := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
```
|
||||
|
||||
### 2.3 Character & Format Rules (BR-DN-002, BR-DN-003)
|
||||
|
||||
- Document number **must be printable UTF‑8** (Thai, English, Numbers, `-`, `_`, `.`). ไม่อนุญาต Control characters, newlines.
|
||||
- ต้องยาวระหว่าง 10 ถึง 50 ตัวอักษร
|
||||
- ต้องกำหนด Token `{SEQ:n}` ลำดับที่ exactly once. ห้ามเป็น Unknown token ใดๆ.
|
||||
|
||||
### 2.4 Number State Machine & Idempotency
|
||||
|
||||
1. **States Lifecycle**: `RESERVED` (TTL 5 mins) → `CONFIRMED` → `VOID` / `CANCELLED`. Document ที่ Confirmed แล้วสามารถมีพฤติกรรม VOID ในอนาคตเพื่อแทนที่ด้วยเอกสารใหม่ได้ การ Request จะได้ Document ชุดใหม่ทดแทนต่อเนื่องทันที. ห้าม Reuse เลขเดิมโดยสิ้นเชิง.
|
||||
2. **Idempotency Key Support**: ทุก API ในการ Generator จำเป็นต้องระบุ HTTP Header `Idempotency-Key` ป้องกันระบบสร้างเลขเบิ้ล (Double Submission). ถ้าระบบได้รับคู่ Request + Key ชุดเดิม จะ Response เลขที่เอกสารเดิม.
|
||||
|
||||
@@ -107,22 +119,23 @@ DIGIT := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
|
||||
## 3. Functional Requirements
|
||||
|
||||
* **FR-DN-001 (Sequential Auto-generation)**: ระบบตอบกลับความรวดเร็วที่ระดับ < 100ms โดยที่ทน Concurrent ได้ ทนต่อปัญหา Duplicate
|
||||
* **FR-DN-002 (Configurable)**: สามารถเปลี่ยนรูปแบบเทมเพลตผ่านระบบ Admin ได้ด้วยการ Validation ก่อน حفظ
|
||||
* **FR-DN-003 (Scope-based sequences)**: รองรับ Scope แยกระดับเอกสาร
|
||||
* **FR-DN-004 (Manual Override)**: ระบบรองรับการตั้งเลขด้วยตนเองสำหรับ Admin Level พร้อมระบุเหตุผลผ่าน Audit Trails เป็นหลักฐานสำคัญ (Import Legacy, Correction)
|
||||
* **FR-DN-005 (Bulk Import)**: รับเข้าระบบจากไฟล์ Excel/CSV และแก้ไข Counters Sequence ต่อเนื่องพร้อมเช็ค Duplicates.
|
||||
* **FR-DN-006 (Skip Cancelled)**: ไม่ให้สิทธิ์ดึงเอกสารยกเลิกใช้งานซ้ำ. คงรักษาสภาพ Audit ต่อไป.
|
||||
* **FR-DN-007 (Void & Replace)**: เปลี่ยน Status เลขเป็น VOID ไม่มีการ Delete. Reference Link เอกสารใหม่ที่เกิดขึ้นทดแทนอิงตาม `voided_from_id`.
|
||||
* **FR-DN-008 (Race Condition Prevention)**: จัดการ Race Condition (RedLock + Database Pessimistic Locking) เพื่อ Guarantee zero duplicate numbers ที่ Load 1000 req/s.
|
||||
* **FR-DN-009 (Two-phase Commit)**: แบ่งการออกเลขเป็นช่วง Reserve 5 นาที แล้วค่อยเรียก Confirm หลังจากได้เอกสารที่ Submit เรียบร้อย (ลดอาการเลขหาย/เลขฟันหลอที่ยังไม่ถูกใช้).
|
||||
* **FR-DN-010/011 (Audit / Metrics Alerting)**: Audit ทุกๆ Step / Transaction ไว้ใน DB ให้เสิร์ชได้เกิน 7 ปี. ส่งแจ้งเตือนถ้า Sequence เริ่มเต็ม (เกิน 90%) หรือ Rate error เริ่มสูง.
|
||||
- **FR-DN-001 (Sequential Auto-generation)**: ระบบตอบกลับความรวดเร็วที่ระดับ < 100ms โดยที่ทน Concurrent ได้ ทนต่อปัญหา Duplicate
|
||||
- **FR-DN-002 (Configurable)**: สามารถเปลี่ยนรูปแบบเทมเพลตผ่านระบบ Admin ได้ด้วยการ Validation ก่อน حفظ
|
||||
- **FR-DN-003 (Scope-based sequences)**: รองรับ Scope แยกระดับเอกสาร
|
||||
- **FR-DN-004 (Manual Override)**: ระบบรองรับการตั้งเลขด้วยตนเองสำหรับ Admin Level พร้อมระบุเหตุผลผ่าน Audit Trails เป็นหลักฐานสำคัญ (Import Legacy, Correction)
|
||||
- **FR-DN-005 (Bulk Import)**: รับเข้าระบบจากไฟล์ Excel/CSV และแก้ไข Counters Sequence ต่อเนื่องพร้อมเช็ค Duplicates.
|
||||
- **FR-DN-006 (Skip Cancelled)**: ไม่ให้สิทธิ์ดึงเอกสารยกเลิกใช้งานซ้ำ. คงรักษาสภาพ Audit ต่อไป.
|
||||
- **FR-DN-007 (Void & Replace)**: เปลี่ยน Status เลขเป็น VOID ไม่มีการ Delete. Reference Link เอกสารใหม่ที่เกิดขึ้นทดแทนอิงตาม `voided_from_id`.
|
||||
- **FR-DN-008 (Race Condition Prevention)**: จัดการ Race Condition (RedLock + Database Pessimistic Locking) เพื่อ Guarantee zero duplicate numbers ที่ Load 1000 req/s.
|
||||
- **FR-DN-009 (Two-phase Commit)**: แบ่งการออกเลขเป็นช่วง Reserve 5 นาที แล้วค่อยเรียก Confirm หลังจากได้เอกสารที่ Submit เรียบร้อย (ลดอาการเลขหาย/เลขฟันหลอที่ยังไม่ถูกใช้).
|
||||
- **FR-DN-010/011 (Audit / Metrics Alerting)**: Audit ทุกๆ Step / Transaction ไว้ใน DB ให้เสิร์ชได้เกิน 7 ปี. ส่งแจ้งเตือนถ้า Sequence เริ่มเต็ม (เกิน 90%) หรือ Rate error เริ่มสูง.
|
||||
|
||||
---
|
||||
|
||||
## 4. Module System & Code Architecture
|
||||
|
||||
### 4.1 Folder Structure
|
||||
|
||||
```
|
||||
backend/src/modules/document-numbering/
|
||||
├── document-numbering.module.ts
|
||||
@@ -138,6 +151,7 @@ backend/src/modules/document-numbering/
|
||||
### 4.2 Sequence Process Architecture
|
||||
|
||||
**1. Number Generation Process Diagram**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
@@ -166,6 +180,7 @@ sequenceDiagram
|
||||
```
|
||||
|
||||
**2. Two-Phase Commit Pattern (Reserve / Confirm)**
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
@@ -189,6 +204,7 @@ sequenceDiagram
|
||||
```
|
||||
|
||||
### 4.3 Lock Service (Redis Redlock Example)
|
||||
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class DocumentNumberingLockService {
|
||||
@@ -204,6 +220,7 @@ export class DocumentNumberingLockService {
|
||||
```
|
||||
|
||||
### 4.4 Counter Service & Transaction Strategy (Optimistic Example)
|
||||
|
||||
```typescript
|
||||
async incrementCounter(counterKey: CounterKey): Promise<number> {
|
||||
const MAX_RETRIES = 2;
|
||||
@@ -231,6 +248,7 @@ async incrementCounter(counterKey: CounterKey): Promise<number> {
|
||||
## 5. Database Schema Details
|
||||
|
||||
### 5.1 Format Storage & Counters
|
||||
|
||||
```sql
|
||||
-- Format Template Configuration
|
||||
CREATE TABLE document_number_formats (
|
||||
@@ -261,6 +279,7 @@ CREATE TABLE document_number_counters (
|
||||
```
|
||||
|
||||
### 5.2 Two-Phase Commit Reservations
|
||||
|
||||
```sql
|
||||
CREATE TABLE document_number_reservations (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
@@ -274,6 +293,7 @@ CREATE TABLE document_number_reservations (
|
||||
```
|
||||
|
||||
### 5.3 Audit Trails & Error Logs
|
||||
|
||||
```sql
|
||||
CREATE TABLE document_number_audit (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
@@ -313,23 +333,26 @@ CREATE TABLE document_number_errors (
|
||||
## 7. Security, Error Handling & Concurrency Checklists
|
||||
|
||||
**Fallback Strategy for Database Lock Failures**:
|
||||
|
||||
1. System attempt to acquire `Redlock`.
|
||||
2. Redis Down? → **Fallback DB-only Lock** Transaction Layer.
|
||||
3. Redis Online but Timeout `>3 Times`? → Return HTTP 503 (Exponential Backoff).
|
||||
4. Save Failed via TypeORM Version Confilct? → Retry Loop `2 Times`, otherwise Return 409 Conflict.
|
||||
|
||||
**Rate Limiting Profiles**:
|
||||
* Single User Threshold: `10 requests/minute`.
|
||||
* Specific Request IP: `50 requests/minute`.
|
||||
|
||||
- Single User Threshold: `10 requests/minute`.
|
||||
- Specific Request IP: `50 requests/minute`.
|
||||
|
||||
**Authorization Policies**:
|
||||
* `Super Admin` เท่านั้นที่บังคับสั่ง `Reset Counter` ให้เริ่มนับศูนย์ได้เมื่อฉุกเฉิน.
|
||||
* กฎ Audit Log System ระบุชัดเจนว่าต้อง Retain Information ไม่ต่ำกว่า 7 ปี.
|
||||
|
||||
- `Super Admin` เท่านั้นที่บังคับสั่ง `Reset Counter` ให้เริ่มนับศูนย์ได้เมื่อฉุกเฉิน.
|
||||
- กฎ Audit Log System ระบุชัดเจนว่าต้อง Retain Information ไม่ต่ำกว่า 7 ปี.
|
||||
|
||||
## 8. Monitoring / Observability (Prometheus + Grafana)
|
||||
|
||||
| Condition Event | Prometheus Counter Target | Severity Reaction |
|
||||
| ---------------------- | -------------------------------- | ----------------------------------------------------------------- |
|
||||
| Condition Event | Prometheus Counter Target | Severity Reaction |
|
||||
| ---------------------- | -------------------------------- | ------------------------------------------------------------------ |
|
||||
| Utilization `>95%` | `numbering_sequence_utilization` | 🔴 **CRITICAL** (PagerDuty/Slack). Limit Maximum sequence reached. |
|
||||
| Redis Downtime `>1M` | Health System | 🔴 **CRITICAL** (PagerDuty/Slack) |
|
||||
| Lock Latency p95 `>1s` | `numbering_lock_wait_seconds` | 🟡 **WARNING** (Slack). Redis connection struggling. |
|
||||
@@ -340,11 +363,13 @@ CREATE TABLE document_number_errors (
|
||||
## 9. Testing & Rollout Migration Strategies
|
||||
|
||||
### 9.1 Test Approach Requirements
|
||||
* **Unit Tests**: Template Tokens Validations, Error handling retry, Optimistic locking checks.
|
||||
* **Concurrency Integration Test**: Assert >1000 requests without double generating numbers simultaneously per `project_id`.
|
||||
* **E2E Load Sequence Flow**: Mocking bulk API loads over 500 requests per seconds via CI/CD load pipeline.
|
||||
|
||||
- **Unit Tests**: Template Tokens Validations, Error handling retry, Optimistic locking checks.
|
||||
- **Concurrency Integration Test**: Assert >1000 requests without double generating numbers simultaneously per `project_id`.
|
||||
- **E2E Load Sequence Flow**: Mocking bulk API loads over 500 requests per seconds via CI/CD load pipeline.
|
||||
|
||||
### 9.2 Data Rollout Plan (Legacy Legacy Import)
|
||||
|
||||
1. Dump out existing Sequence numbers (Extracted Document Strings).
|
||||
2. Write validation script for Sequence Max Counts.
|
||||
3. Import to Table `document_number_counters` as Manual Override API Method context (`FR-DN-004`).
|
||||
@@ -353,6 +378,7 @@ CREATE TABLE document_number_errors (
|
||||
---
|
||||
|
||||
**Best Practices Checklist**
|
||||
|
||||
- ✅ **DO**: Two-Phase Commit (`Reserve` + `Confirm`) ให้เป็น Routine System Concept.
|
||||
- ✅ **DO**: DB Fallback เมื่อ Redis ดาวน์. ให้ Availability สูงสุด ไม่หยุดทำงาน.
|
||||
- ✅ **DO**: ข้ามเลขที่ยกเลิกทั้งหมดห้ามมีการ Re-Use เด็ดขาด ไม่ว่าเจตนาใดๆ.
|
||||
@@ -361,11 +387,11 @@ CREATE TABLE document_number_errors (
|
||||
- ❌ **DON'T**: ลืมเขียน Idempotency-Key สำหรับ Request.
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.8.0
|
||||
**Created By**: Development Team
|
||||
**End of Document**
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 10. Operations & Infrastructure Guidelines
|
||||
@@ -374,18 +400,18 @@ CREATE TABLE document_number_errors (
|
||||
|
||||
### 1.1. Response Time Targets
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
| ---------------- | -------- | ------------------------ |
|
||||
| Metric | Target | Measurement |
|
||||
| ---------------- | ---------- | ---------------------------- |
|
||||
| 95th percentile | ≤ 2 วินาที | ตั้งแต่ request ถึง response |
|
||||
| 99th percentile | ≤ 5 วินาที | ตั้งแต่ request ถึง response |
|
||||
| Normal operation | ≤ 500ms | ไม่มี retry |
|
||||
| Normal operation | ≤ 500ms | ไม่มี retry |
|
||||
|
||||
### 1.2. Throughput Targets
|
||||
|
||||
| Load Level | Target | Notes |
|
||||
| -------------- | ----------- | ------------------------ |
|
||||
| Normal load | ≥ 50 req/s | ใช้งานปกติ |
|
||||
| Peak load | ≥ 100 req/s | ช่วงเร่งงาน |
|
||||
| Normal load | ≥ 50 req/s | ใช้งานปกติ |
|
||||
| Peak load | ≥ 100 req/s | ช่วงเร่งงาน |
|
||||
| Burst capacity | ≥ 200 req/s | Short duration (< 1 min) |
|
||||
|
||||
### 1.3. Availability SLA
|
||||
@@ -395,7 +421,6 @@ CREATE TABLE document_number_errors (
|
||||
- **Recovery Time Objective (RTO)**: ≤ 30 นาที
|
||||
- **Recovery Point Objective (RPO)**: ≤ 5 นาที
|
||||
|
||||
|
||||
### 2. Infrastructure Setup
|
||||
|
||||
### 2.1. Database Configuration
|
||||
@@ -567,12 +592,12 @@ services:
|
||||
- backend
|
||||
```
|
||||
|
||||
|
||||
### 4. Troubleshooting Runbooks
|
||||
|
||||
### 4.1. Scenario: Redis Unavailable
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Alert: `RedisUnavailable`
|
||||
- System falls back to DB-only locking
|
||||
- Performance degraded 30-50%
|
||||
@@ -580,22 +605,26 @@ services:
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check Redis status:**
|
||||
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli ping
|
||||
# Expected: PONG
|
||||
```
|
||||
|
||||
2. **Check Redis logs:**
|
||||
|
||||
```bash
|
||||
docker logs lcbp3-redis --tail=100
|
||||
```
|
||||
|
||||
3. **Restart Redis (if needed):**
|
||||
|
||||
```bash
|
||||
docker restart lcbp3-redis
|
||||
```
|
||||
|
||||
4. **Verify failover (if using Sentinel):**
|
||||
|
||||
```bash
|
||||
docker exec lcbp3-redis-sentinel redis-cli -p 26379 SENTINEL masters
|
||||
```
|
||||
@@ -607,29 +636,34 @@ services:
|
||||
### 4.2. Scenario: High Lock Failure Rate
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Alert: `HighLockFailureRate` (> 10%)
|
||||
- Users report "ระบบกำลังยุ่ง" errors
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check concurrent load:**
|
||||
|
||||
```bash
|
||||
# Check current request rate
|
||||
curl http://prometheus:9090/api/v1/query?query=rate(docnum_generation_duration_ms_count[1m])
|
||||
```
|
||||
|
||||
2. **Check database connections:**
|
||||
|
||||
```sql
|
||||
SHOW PROCESSLIST;
|
||||
-- Look for waiting/locked queries
|
||||
```
|
||||
|
||||
3. **Check Redis memory:**
|
||||
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli INFO memory
|
||||
```
|
||||
|
||||
4. **Scale up if needed:**
|
||||
|
||||
```bash
|
||||
# Increase backend replicas
|
||||
docker-compose up -d --scale backend=5
|
||||
@@ -644,12 +678,14 @@ services:
|
||||
### 4.3. Scenario: Slow Performance
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- Alert: `SlowDocumentNumberGeneration`
|
||||
- P95 > 2 seconds
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check database query performance:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM document_number_counters USE INDEX (idx_counter_lookup)
|
||||
WHERE project_id = 2 AND correspondence_type_id = 6 AND current_year = 2025;
|
||||
@@ -659,16 +695,19 @@ services:
|
||||
```
|
||||
|
||||
2. **Check for missing indexes:**
|
||||
|
||||
```sql
|
||||
SHOW INDEX FROM document_number_counters;
|
||||
```
|
||||
|
||||
3. **Check Redis latency:**
|
||||
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli --latency
|
||||
```
|
||||
|
||||
4. **Check network latency:**
|
||||
|
||||
```bash
|
||||
ping mariadb-master
|
||||
ping redis-master
|
||||
@@ -682,12 +721,14 @@ services:
|
||||
### 4.4. Scenario: Version Conflicts
|
||||
|
||||
**Symptoms:**
|
||||
|
||||
- High retry count
|
||||
- Users report "เลขที่เอกสารถูกเปลี่ยน" errors
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check concurrent requests to same counter:**
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
project_id,
|
||||
@@ -701,6 +742,7 @@ services:
|
||||
```
|
||||
|
||||
2. **Investigate specific counter:**
|
||||
|
||||
```sql
|
||||
SELECT * FROM document_number_counters
|
||||
WHERE project_id = X AND correspondence_type_id = Y;
|
||||
@@ -720,7 +762,6 @@ services:
|
||||
- Increase retry count in application config
|
||||
- Consider manual counter adjustment (last resort)
|
||||
|
||||
|
||||
### 5. Maintenance Procedures
|
||||
|
||||
### 5.1. Counter Reset (Manual)
|
||||
@@ -730,6 +771,7 @@ services:
|
||||
**Steps:**
|
||||
|
||||
1. **Request approval via API:**
|
||||
|
||||
```bash
|
||||
POST /api/v1/document-numbering/configs/{configId}/reset-counter
|
||||
{
|
||||
@@ -757,6 +799,7 @@ services:
|
||||
4. Template changes do NOT affect existing documents
|
||||
|
||||
**API Call:**
|
||||
|
||||
```bash
|
||||
PUT /api/v1/document-numbering/configs/{configId}
|
||||
{
|
||||
@@ -768,6 +811,7 @@ PUT /api/v1/document-numbering/configs/{configId}
|
||||
### 5.3. Database Maintenance
|
||||
|
||||
**Weekly Tasks:**
|
||||
|
||||
- Check slow query log
|
||||
- Optimize tables if needed:
|
||||
```sql
|
||||
@@ -776,11 +820,10 @@ PUT /api/v1/document-numbering/configs/{configId}
|
||||
```
|
||||
|
||||
**Monthly Tasks:**
|
||||
|
||||
- Review and archive old audit logs (> 2 years)
|
||||
- Check index usage:
|
||||
```sql
|
||||
SELECT * FROM sys.schema_unused_indexes
|
||||
WHERE object_schema = 'lcbp3_db';
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -54,7 +54,6 @@ related:
|
||||
- มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก
|
||||
|
||||
- ตัวชี้วัดประสิทธิภาพ:
|
||||
|
||||
- **API Response Time:** < 200ms (90th percentile) สำหรับ operation ทั่วไป
|
||||
- **Search Query Performance:** < 500ms สำหรับการค้นหาขั้นสูง
|
||||
- **File Upload Performance:** < 30 seconds สำหรับไฟล์ขนาด 50MB
|
||||
@@ -64,7 +63,6 @@ related:
|
||||
- **Application Startup Time:** < 30 seconds
|
||||
|
||||
- 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)
|
||||
|
||||
@@ -13,31 +13,41 @@ last_updated: 2026-02-23
|
||||
หน้านี้รวบรวมข้อกำหนดการใช้งาน (Functional Requirements) ที่แบ่งตามระบบย่อย (Modules) ทั้งหมด
|
||||
|
||||
## 1. การจัดการโครงการและองค์กร (Project Management)
|
||||
|
||||
[01-02-01-project-management.md](01-02-01-project-management.md)
|
||||
|
||||
## 2. การจัดการจดหมายรับ-ส่ง (Correspondence)
|
||||
|
||||
[01-02-02-correspondence.md](01-02-02-correspondence.md)
|
||||
|
||||
## 3. การจัดการขออนุมัติวัสดุอุปกรณ์ (RFA)
|
||||
|
||||
[01-02-03-rfa.md](01-02-03-rfa.md)
|
||||
|
||||
## 4. การจัดการแบบคู่สัญญา (Contract Drawing)
|
||||
|
||||
[01-02-04-contract-drawing.md](01-02-04-contract-drawing.md)
|
||||
|
||||
## 5. การจัดการแบบก่อสร้าง (Shop Drawing)
|
||||
|
||||
[01-02-05-shop-drawing.md](01-02-05-shop-drawing.md)
|
||||
|
||||
## 6. กระบวนการอนุมัติ (Unified Workflow)
|
||||
|
||||
[01-02-06-unified-workflow.md](01-02-06-unified-workflow.md)
|
||||
|
||||
## 7. เอกสารนำส่ง (Transmittals)
|
||||
|
||||
[01-02-07-transmittals.md](01-02-07-transmittals.md)
|
||||
|
||||
## 8. ใบเวียนเอกสาร (Circulation Sheet)
|
||||
|
||||
[01-02-08-circulation-sheet.md](01-02-08-circulation-sheet.md)
|
||||
|
||||
## 9. ประวัติการแก้ไข (Logs / Revisions)
|
||||
|
||||
[01-02-09-logs.md](01-02-09-logs.md)
|
||||
|
||||
## 10. รายละเอียด JSON (JSON Details)
|
||||
|
||||
[01-02-10-json-details.md](01-02-10-json-details.md)
|
||||
|
||||
@@ -24,7 +24,6 @@ related:
|
||||
## 3.12.2 โครงสร้าง JSON Schema
|
||||
|
||||
- ระบบต้องมี predefined JSON schemas สำหรับประเภทเอกสารต่างๆ:
|
||||
|
||||
- 3.12.2.1 Correspondence Types
|
||||
- GENERIC: ข้อมูลพื้นฐานสำหรับเอกสารทั่วไป
|
||||
- RFI: รายละเอียดคำถามและข้อมูลทางเทคนิค
|
||||
@@ -67,7 +66,6 @@ related:
|
||||
## 3.12.7 JSON Schema Migration Strategy (เพิ่มเติม)
|
||||
|
||||
- สำหรับ Schema Breaking Changes:
|
||||
|
||||
- Phase 1 - Add New Column
|
||||
ALTER TABLE correspondence_revisions
|
||||
ADD COLUMN ref_project_id_v2 INT GENERATED ALWAYS AS
|
||||
@@ -79,7 +77,6 @@ related:
|
||||
- Phase 3 - Switch Application Code
|
||||
- Deploy code ที่ใช้ path ใหม่
|
||||
- Phase 4 - Remove Old Column
|
||||
|
||||
- หลังจาก verify แล้วว่าไม่มี error
|
||||
- Drop old virtual column
|
||||
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# 📖 User Stories — LCBP3-DMS v1.8.0
|
||||
|
||||
---
|
||||
|
||||
title: 'User Stories — All Modules'
|
||||
version: 1.0.0
|
||||
status: DRAFT
|
||||
owner: Nattanin Peancharoen (Product Owner)
|
||||
last_updated: 2026-03-11
|
||||
related:
|
||||
- specs/01-Requirements/01-01-objectives.md
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
- specs/01-Requirements/01-03-modules/
|
||||
|
||||
- specs/01-Requirements/01-01-objectives.md
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
- specs/01-Requirements/01-03-modules/
|
||||
|
||||
---
|
||||
|
||||
## 📖 วิธีอ่าน
|
||||
@@ -24,6 +27,7 @@ related:
|
||||
## 👥 Epic 1: Authentication & User Management
|
||||
|
||||
### US-001 — Login เข้าระบบ
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-AUTH-001, AC-AUTH-002
|
||||
|
||||
```
|
||||
@@ -31,7 +35,9 @@ As a ผู้ใช้งานทุกประเภท
|
||||
I want to login ด้วย Username + Password
|
||||
So that ฉันเข้าถึงระบบตามบทบาทของตนได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Login สำเร็จ → Dashboard | Password ผิด → Error กลางๆ
|
||||
- ผิดเกิน 5 ครั้งใน 1 นาที → 429 Rate Limit
|
||||
- Audit Log: `LOGIN_SUCCESS` / `LOGIN_FAILED` + IP
|
||||
@@ -39,6 +45,7 @@ So that ฉันเข้าถึงระบบตามบทบาทขอ
|
||||
---
|
||||
|
||||
### US-002 — เปลี่ยนรหัสผ่านครั้งแรก
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-AUTH-003
|
||||
|
||||
```
|
||||
@@ -46,13 +53,16 @@ As a ผู้ใช้งานใหม่ที่เพิ่งถูกส
|
||||
I want to ถูกบังคับเปลี่ยนรหัสผ่านใน Login ครั้งแรก
|
||||
So that ฉันมีรหัสผ่านที่ฉันรู้คนเดียว
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Login ครั้งแรก → Redirect หน้าเปลี่ยน Password ทันที (ผ่านไม่ได้)
|
||||
- Password ใหม่ต้องผ่าน Policy (8+ ตัว, อักษรใหญ่-เล็ก-ตัวเลข)
|
||||
|
||||
---
|
||||
|
||||
### US-003 — จัดการ Profile ส่วนตัว
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 2
|
||||
|
||||
```
|
||||
@@ -60,13 +70,16 @@ As a ผู้ใช้งานทุกประเภท
|
||||
I want to แก้ไขข้อมูลส่วนตัวและเปลี่ยนรหัสผ่าน
|
||||
So that ฉันดูแล Account ของตัวเองได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- แก้ไข ชื่อ, Email, ช่องทาง Notification ได้
|
||||
- เปลี่ยน Password ต้องยืนยัน Password เก่า
|
||||
|
||||
---
|
||||
|
||||
### US-004 — Superadmin สร้างองค์กร + Project + Contract
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-ADMIN-001, AC-ADMIN-002
|
||||
|
||||
```
|
||||
@@ -74,7 +87,9 @@ As a Superadmin
|
||||
I want to สร้าง Organization, Project, Contract และผูกความสัมพันธ์กัน
|
||||
So that ระบบพร้อมให้แต่ละองค์กรเริ่มทำงานได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- สร้าง Org → แต่งตั้ง Org Admin ได้ทันที
|
||||
- สร้าง Project → ผูก Organizations (หลายองค์กร)
|
||||
- สร้าง Contract → 1 Contractor ต่อ 1 Contract
|
||||
@@ -83,6 +98,7 @@ So that ระบบพร้อมให้แต่ละองค์กรเ
|
||||
---
|
||||
|
||||
### US-005 — Org Admin จัดการ User ในองค์กร
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 3 | **AC:** AC-ADMIN-003
|
||||
|
||||
```
|
||||
@@ -90,7 +106,9 @@ As a Org Admin
|
||||
I want to เพิ่ม/แก้ไข/Deactivate User และกำหนด Role
|
||||
So that ทีมงานเข้าถึงระบบได้ตามหน้าที่
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- เพิ่ม User → Email แจ้ง Credentials อัตโนมัติ
|
||||
- Deactivate ได้ (ไม่ Delete — Audit ยังอยู่)
|
||||
- เห็นเฉพาะ User ในองค์กรตัวเอง
|
||||
@@ -98,6 +116,7 @@ So that ทีมงานเข้าถึงระบบได้ตามห
|
||||
---
|
||||
|
||||
### US-006 — Permission Isolation ระหว่าง Contractor
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-ADMIN-004
|
||||
|
||||
```
|
||||
@@ -105,7 +124,9 @@ As a ระบบ
|
||||
I want to ป้องกัน Contractor A เห็นข้อมูล Contractor B
|
||||
So that ข้อมูลทางธุรกิจของแต่ละ Contractor เป็นความลับ
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Contractor A→ ไม่เห็น List เอกสาร B | พยายาม Access URL ตรงๆ → 403
|
||||
- Audit Log บันทึก Unauthorized Attempt
|
||||
|
||||
@@ -114,6 +135,7 @@ So that ข้อมูลทางธุรกิจของแต่ละ Co
|
||||
## 📨 Epic 2: Correspondence Management
|
||||
|
||||
### US-007 — สร้าง Correspondence Draft
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-CORR-001
|
||||
|
||||
```
|
||||
@@ -121,7 +143,9 @@ As a Document Control
|
||||
I want to สร้าง Correspondence (Letter/RFI) ในสถานะ Draft พร้อมแนบไฟล์
|
||||
So that เตรียมเอกสารได้ก่อนส่งโดยองค์กรอื่นยังไม่เห็น
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Form: Subject, To (หลายองค์กร), CC, Doc Type, Attachments (PDF/ZIP)
|
||||
- Drag-and-Drop Multi-file | ClamAV Scan ทุกไฟล์
|
||||
- Auto-Save Draft → LocalStorage (กันข้อมูลสูญ)
|
||||
@@ -130,6 +154,7 @@ So that เตรียมเอกสารได้ก่อนส่งโด
|
||||
---
|
||||
|
||||
### US-008 — Submit Correspondence + Auto Number
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-CORR-002
|
||||
|
||||
```
|
||||
@@ -137,7 +162,9 @@ As a Document Control
|
||||
I want to Submit Correspondence และให้ระบบออกเลขอัตโนมัติ
|
||||
So that เอกสารได้รับเลขที่ไม่ซ้ำและส่งถึงผู้รับทันที
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ออกเลขตาม Template (เช่น `LCBP3-สค.-กทท.-LETTER-0001-68`)
|
||||
- เลขไม่ซ้ำแม้ Submit พร้อมกัน (Redis Redlock)
|
||||
- สถานะ → SUBMITTED | Workflow Instance ถูกสร้าง
|
||||
@@ -146,6 +173,7 @@ So that เอกสารได้รับเลขที่ไม่ซ้ำ
|
||||
---
|
||||
|
||||
### US-009 — ดู Correspondence ใน Inbox
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 3 | **AC:** AC-CORR-003
|
||||
|
||||
```
|
||||
@@ -153,13 +181,16 @@ As a Document Control ขององค์กรผู้รับ
|
||||
I want to เห็น Correspondence ที่ส่งมาถึงองค์กรในรายการ
|
||||
So that ดำเนินการได้ทันที (สร้าง Circulation, ตอบกลับ)
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- List แสดง Received/Sent แยก | PDF Viewer ในแอป (Streaming)
|
||||
- ดาวน์โหลดไฟล์แนบได้ (ถ้ามีสิทธิ์)
|
||||
|
||||
---
|
||||
|
||||
### US-010 — อ้างอิง + Tag เอกสาร
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 3 | **AC:** AC-CORR-004
|
||||
|
||||
```
|
||||
@@ -167,13 +198,16 @@ As a Document Control
|
||||
I want to อ้างอิงเอกสารเก่าและกำหนด Tag หลาย Tag
|
||||
So that จัดกลุ่มเอกสารและ Navigate ข้าม Thread ได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ค้นหาและเลือก Reference Documents | Link ระหว่างเอกสาร (คลิก Navigate)
|
||||
- กำหนด Tag หลาย Tag | ค้นหาได้จาก Tag
|
||||
|
||||
---
|
||||
|
||||
### US-011 — ยกเลิก Correspondence (Admin)
|
||||
|
||||
**Priority:** 🟡 C | **SP:** 2 | **AC:** AC-CORR-005
|
||||
|
||||
```
|
||||
@@ -181,7 +215,9 @@ As a Org Admin / Superadmin
|
||||
I want to ยกเลิก Correspondence ที่ Submit แล้ว พร้อมระบุเหตุผล
|
||||
So that เอกสารที่ส่งผิดถูกระงับโดยมีหลักฐาน Audit
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ต้องกรอก cancel_reason | สถานะ → CANCELLED
|
||||
- Editor/Viewer ทำ Cancel ไม่ได้ → 403
|
||||
|
||||
@@ -190,6 +226,7 @@ So that เอกสารที่ส่งผิดถูกระงับโ
|
||||
## 📋 Epic 3: RFA Management
|
||||
|
||||
### US-012 — สร้าง RFA + Shop Drawing
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 8 | **AC:** AC-RFA-001
|
||||
|
||||
```
|
||||
@@ -197,7 +234,9 @@ As a Document Control ของ Contractor
|
||||
I want to สร้าง RFA พร้อม Upload Shop Drawing
|
||||
So that ยื่นขออนุมัติแบบก่อสร้างจากที่ปรึกษาได้อย่างเป็นระบบ
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Form: RFA Type, Discipline, Shop Drawing (PDF/DWG/ZIP)
|
||||
- 1 Shop Drawing Revision = 1 RFA เท่านั้น
|
||||
- ClamAV Scan | Draft (ไม่เห็นข้ามองค์กร)
|
||||
@@ -205,6 +244,7 @@ So that ยื่นขออนุมัติแบบก่อสร้าง
|
||||
---
|
||||
|
||||
### US-013 — Submit RFA ผ่าน Transmittal
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 8 | **AC:** AC-RFA-002, AC-TRM-001
|
||||
|
||||
```
|
||||
@@ -212,7 +252,9 @@ As a Document Control ของ Contractor
|
||||
I want to สร้าง Transmittal รวม RFA หลายฉบับ ส่งไปยังที่ปรึกษา
|
||||
So that ส่งเอกสารเป็นชุด ที่ปรึกษาได้รับทุกฉบับพร้อมกัน
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Transmittal → เลือก RFA หลายฉบับ | ออกเลขเองเป็น Correspondence
|
||||
- RFA แต่ละฉบับ → ออกเลขตาม Format `LCBP3-RFA-{DISCIPLINE}-{SEQ}`
|
||||
- ที่ปรึกษา → Notification
|
||||
@@ -220,6 +262,7 @@ So that ส่งเอกสารเป็นชุด ที่ปรึก
|
||||
---
|
||||
|
||||
### US-014 — Review RFA (Approved / w.Comments / Rejected)
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-RFA-003~005
|
||||
|
||||
```
|
||||
@@ -227,7 +270,9 @@ As a Engineer ของ Supervisor Organization
|
||||
I want to เปิดดู Shop Drawing และให้คำตอบ RFA
|
||||
So that Contractor ได้รับผลพิจารณาและดำเนินการต่อได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- PDF Viewer Streaming | ปุ่ม: Approved / Approved w/Comments / Rejected
|
||||
- Comment บังคับสำหรับ Approved w/Comments และ Rejected
|
||||
- Originator → Notification | Workflow History บันทึกครบ
|
||||
@@ -235,6 +280,7 @@ So that Contractor ได้รับผลพิจารณาและดำ
|
||||
---
|
||||
|
||||
### US-015 — ยื่น RFA Revision ใหม่หลัง Reject
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-RFA-006
|
||||
|
||||
```
|
||||
@@ -242,7 +288,9 @@ As a Document Control ของ Contractor
|
||||
I want to สร้าง RFA Revision ใหม่ (Rev.B) หลัง Rev.A ถูก Rejected
|
||||
So that แก้ไขและยื่น Shop Drawing เวอร์ชันใหม่ได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Rev.B ผูกกับ Shop Drawing Rev.B | Rev.A ยังดูได้
|
||||
- Revision Code เปลี่ยนตาม Sequence (A→B→C)
|
||||
|
||||
@@ -251,6 +299,7 @@ So that แก้ไขและยื่น Shop Drawing เวอร์ชั
|
||||
## 📐 Epic 4: Drawing Management
|
||||
|
||||
### US-016 — Upload Contract Drawing
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-DRW-001
|
||||
|
||||
```
|
||||
@@ -258,13 +307,16 @@ As a Document Control ของ Design Consultant
|
||||
I want to Upload แบบคู่สัญญา (Contract Drawing) พร้อมกำหนดหมวดหมู่
|
||||
So that Contractor ใช้อ้างอิงใน Shop Drawing ได้
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Upload PDF → Drawing Number, Category, Discipline
|
||||
- Shop Drawing Link Reference กับ Contract Drawing ได้
|
||||
|
||||
---
|
||||
|
||||
### US-017 — Upload Shop Drawing + Revision Control
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-DRW-002, AC-DRW-003
|
||||
|
||||
```
|
||||
@@ -272,7 +324,9 @@ As a Document Control ของ Contractor
|
||||
I want to Upload Shop Drawing พร้อม Reference Contract Drawing และจัดการ Revision
|
||||
So that ผู้ Review เห็น Revision ล่าสุด และ History ทุก Revision
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ค้นหาและเลือก Contract Drawing ที่ Reference
|
||||
- Rev.ใหม่ → Rev.เก่า Mark "Superseded" แต่ยังดูได้
|
||||
- 1 Revision = 1 RFA เท่านั้น (Constraint enforced)
|
||||
@@ -282,6 +336,7 @@ So that ผู้ Review เห็น Revision ล่าสุด และ Hist
|
||||
## ⚙️ Epic 5: Workflow Engine
|
||||
|
||||
### US-018 — ดู Workflow Diagram ของเอกสาร
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 3 | **AC:** AC-WF-004
|
||||
|
||||
```
|
||||
@@ -289,7 +344,9 @@ As a ผู้ใช้งานที่มีสิทธิ์ดูเอก
|
||||
I want to เห็น Workflow Diagram แบบ Visual
|
||||
So that รู้ทันทีว่าเอกสารอยู่ขั้นตอนไหน ใครถืออยู่
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Workflow Diagram แสดง State ปัจจุบัน (Highlight)
|
||||
- คลิก Step ที่ผ่านมา → Audit Log ย่อย (ใคร/เมื่อไหร่/Comment)
|
||||
- Step ที่ยังไม่ถึง → Disabled Style
|
||||
@@ -297,6 +354,7 @@ So that รู้ทันทีว่าเอกสารอยู่ขั้
|
||||
---
|
||||
|
||||
### US-019 — Approve / Reject ผ่าน Workflow
|
||||
|
||||
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-WF-002, AC-WF-003
|
||||
|
||||
```
|
||||
@@ -304,7 +362,9 @@ As a Reviewer ที่ถูก Assign ใน Workflow
|
||||
I want to Approve / Reject เอกสารในขั้นตอนที่ฉันรับผิดชอบ
|
||||
So that Workflow เดินหน้าหรือส่งกลับตามการตัดสินใจ
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- เห็นปุ่ม Action เฉพาะ Step ที่เป็นของฉัน
|
||||
- Wrong Role → ปุ่มซ่อน / 403 ถ้าเรียก API ตรงๆ
|
||||
- ทุก Action → Workflow History + Timestamp
|
||||
@@ -312,6 +372,7 @@ So that Workflow เดินหน้าหรือส่งกลับตา
|
||||
---
|
||||
|
||||
### US-020 — Force Proceed + Revert (Document Control)
|
||||
|
||||
**Priority:** 🟡 C | **SP:** 3 | **AC:** AC-WF-005
|
||||
|
||||
```
|
||||
@@ -319,13 +380,16 @@ As a Document Control
|
||||
I want to บังคับข้ามหรือย้อน Workflow Step ในกรณีพิเศษ
|
||||
So that Workflow ที่ติดขัดถูก Unblock ได้อย่างมีหลักฐาน
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ปุ่ม "Force Proceed" / "Revert" เห็นได้เฉพาะ Document Control ขึ้นไป
|
||||
- ต้องกรอก reason | Audit Log: FORCE_PROCEED / REVERT
|
||||
|
||||
---
|
||||
|
||||
### US-021 — กำหนด Deadline ใน Workflow
|
||||
|
||||
**Priority:** 🟡 C | **SP:** 3 | **AC:** AC-WF-006
|
||||
|
||||
```
|
||||
@@ -333,7 +397,9 @@ As a Document Control
|
||||
I want to กำหนด Deadline ให้แต่ละ Organization ใน Workflow
|
||||
So that ดำเนินการทันเวลา ไม่เกิดความล่าช้า
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- กำหนด Due Date ต่อ Org ใน Workflow
|
||||
- เกิน Deadline → Dashboard Overdue Badge + Notification
|
||||
- 2 วันก่อน → Reminder Notification
|
||||
@@ -343,6 +409,7 @@ So that ดำเนินการทันเวลา ไม่เกิด
|
||||
## 📄 Epic 6: Circulation Sheet
|
||||
|
||||
### US-022 — สร้าง Circulation Sheet
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-CIRC-001
|
||||
|
||||
```
|
||||
@@ -350,7 +417,9 @@ As a Document Control ภายในองค์กร
|
||||
I want to สร้างใบเวียนสำหรับ Correspondence พร้อมกำหนดผู้รับผิดชอบ
|
||||
So that งานถูก Assign ชัดเจนและมีหลักฐาน
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- กำหนด Main / Action / Information Assignees (หลายคน)
|
||||
- Assignees → In-App + Email Notification
|
||||
- Internal Only (ไม่เห็นข้ามองค์กร)
|
||||
@@ -358,6 +427,7 @@ So that งานถูก Assign ชัดเจนและมีหลัก
|
||||
---
|
||||
|
||||
### US-023 — ติดตาม Circulation Deadline + ปิด
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 3 | **AC:** AC-CIRC-002, AC-CIRC-003
|
||||
|
||||
```
|
||||
@@ -365,7 +435,9 @@ As a Assignee / Document Control
|
||||
I want to เห็น Deadline งานของฉันและปิด Circulation เมื่อเสร็จ
|
||||
So that ไม่พลาดงาน และ Track สถานะได้ชัดเจน
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Dashboard My Tasks แสดง Deadline + Overdue Badge
|
||||
- ปิด Circulation → สถานะ CLOSED + Timestamp
|
||||
- My Tasks ลบรายการที่ปิดแล้ว
|
||||
@@ -375,6 +447,7 @@ So that ไม่พลาดงาน และ Track สถานะได้
|
||||
## 🔍 Epic 7: Search & Notifications
|
||||
|
||||
### US-024 — ค้นหาเอกสาร Full-text
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-SRCH-001
|
||||
|
||||
```
|
||||
@@ -382,13 +455,16 @@ As a ผู้ใช้งานทุกประเภท
|
||||
I want to ค้นหาเอกสารด้วย Keyword ได้รวดเร็ว
|
||||
So that พบเอกสารที่ต้องการโดยไม่ต้องจำเลขที่แม่นยำ
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- ผล Search < 500ms (Elasticsearch) | ค้นจาก เลขเอกสาร, Subject, Tag
|
||||
- แสดงเฉพาะเอกสารที่มีสิทธิ์เห็น
|
||||
|
||||
---
|
||||
|
||||
### US-025 — รับ Notification (Email + In-App)
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-NOTIF-001, AC-NOTIF-002
|
||||
|
||||
```
|
||||
@@ -396,7 +472,9 @@ As a ผู้ใช้งาน
|
||||
I want to รับ Email และ In-App Notification เมื่อมี Event เกี่ยวข้องกับฉัน
|
||||
So that ไม่พลาดงานโดยไม่ต้องเช็คระบบตลอดเวลา
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Email ถูกส่งภายใน 5 นาที หลัง Event (BullMQ Queue)
|
||||
- Bell Icon Unread Count | คลิก → Navigate ไปเอกสาร
|
||||
- Retry 3 ครั้ง ถ้าส่งไม่ได้ → Dead Letter Queue
|
||||
@@ -406,6 +484,7 @@ So that ไม่พลาดงานโดยไม่ต้องเช็ค
|
||||
## 💾 Epic 8: File & Dashboard
|
||||
|
||||
### US-026 — Upload ไฟล์ + ดู PDF ในแอป
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-STOR-001, AC-STOR-003
|
||||
|
||||
```
|
||||
@@ -413,7 +492,9 @@ As a Document Control / Reviewer
|
||||
I want to Upload ไฟล์หลายไฟล์และดู PDF ในแอปได้เลย
|
||||
So that ทำงานเร็วขึ้นและลดความเสี่ยงข้อมูลรั่วจาก Download
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- Drag-and-Drop Multi-file | ClamAV Scan < 30s
|
||||
- In-App PDF Viewer (Range Requests Streaming)
|
||||
- ปุ่ม Download → Disabled สำหรับ Viewer-only
|
||||
@@ -421,6 +502,7 @@ So that ทำงานเร็วขึ้นและลดความเส
|
||||
---
|
||||
|
||||
### US-027 — Dashboard KPI ส่วนตัว
|
||||
|
||||
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-DASH-001
|
||||
|
||||
```
|
||||
@@ -428,7 +510,9 @@ As a ผู้ใช้งานทุกประเภท
|
||||
I want to เห็น Dashboard สรุปงานของฉันทันทีที่ Login
|
||||
So that รู้สถานะงานโดยไม่ต้องไล่ดูแต่ละ Module
|
||||
```
|
||||
|
||||
**Done When:**
|
||||
|
||||
- KPI Cards: Pending RFA, Pending Correspondence, Overdue Circulation
|
||||
- My Tasks Table: Circulation ที่ค้าง + Deadline
|
||||
- Filter ตาม Permission ของ User
|
||||
@@ -437,17 +521,17 @@ So that รู้สถานะงานโดยไม่ต้องไล่
|
||||
|
||||
## 📊 Story Map Summary
|
||||
|
||||
| Epic | Stories | 🔴 Must | 🟠 Should | 🟡 Could |
|
||||
|------|---------|---------|----------|--------|
|
||||
| Auth & Users | US-001~006 | 4 | 1 | 1 |
|
||||
| Correspondence | US-007~011 | 2 | 2 | 1 |
|
||||
| RFA | US-012~015 | 2 | 2 | 0 |
|
||||
| Drawing | US-016~017 | 0 | 2 | 0 |
|
||||
| Workflow | US-018~021 | 1 | 1 | 2 |
|
||||
| Circulation | US-022~023 | 0 | 2 | 0 |
|
||||
| Search & Notify | US-024~025 | 0 | 2 | 0 |
|
||||
| File & Dashboard | US-026~027 | 0 | 2 | 0 |
|
||||
| **รวม** | **27** | **9** | **14** | **4** |
|
||||
| Epic | Stories | 🔴 Must | 🟠 Should | 🟡 Could |
|
||||
| ---------------- | ---------- | ------- | --------- | -------- |
|
||||
| Auth & Users | US-001~006 | 4 | 1 | 1 |
|
||||
| Correspondence | US-007~011 | 2 | 2 | 1 |
|
||||
| RFA | US-012~015 | 2 | 2 | 0 |
|
||||
| Drawing | US-016~017 | 0 | 2 | 0 |
|
||||
| Workflow | US-018~021 | 1 | 1 | 2 |
|
||||
| Circulation | US-022~023 | 0 | 2 | 0 |
|
||||
| Search & Notify | US-024~025 | 0 | 2 | 0 |
|
||||
| File & Dashboard | US-026~027 | 0 | 2 | 0 |
|
||||
| **รวม** | **27** | **9** | **14** | **4** |
|
||||
|
||||
> **MVP Sprint Focus:** US-001~006, US-007~008, US-012~014, US-019 — ครอบคลุม Core Happy Path ทั้งหมด
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,17 +1,20 @@
|
||||
# 🛡️ Module Edge Cases & Business Rules — LCBP3-DMS v1.8.0
|
||||
|
||||
---
|
||||
|
||||
title: 'Edge Cases, Business Rules, and Anti-Bug Specifications'
|
||||
version: 1.0.0
|
||||
status: DRAFT
|
||||
owner: Nattanin Peancharoen (Product Owner / System Architect)
|
||||
last_updated: 2026-03-11
|
||||
related:
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
- specs/01-Requirements/01-02-business-rules/01-02-02-doc-numbering-rules.md
|
||||
- specs/06-Decision-Records/ADR-001-unified-workflow-engine.md
|
||||
- specs/06-Decision-Records/ADR-016-security-authentication.md
|
||||
- specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql
|
||||
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
- specs/01-Requirements/01-02-business-rules/01-02-02-doc-numbering-rules.md
|
||||
- specs/06-Decision-Records/ADR-001-unified-workflow-engine.md
|
||||
- specs/06-Decision-Records/ADR-016-security-authentication.md
|
||||
- specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql
|
||||
|
||||
---
|
||||
|
||||
> [!IMPORTANT]
|
||||
@@ -31,16 +34,19 @@ related:
|
||||
## Module 1: Document Numbering Edge Cases
|
||||
|
||||
### EC-DN-001 — Concurrent Submission (Race Condition)
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Concurrency, Data Integrity
|
||||
|
||||
**Scenario:** User A และ User B กด Submit Correspondence พร้อมกันทุก millisecond สำหรับ Project/Type/Sender/Receiver เดียวกัน
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ทั้งสองได้รับเลขเอกสาร **ต่างกัน** (เช่น 0001 และ 0002)
|
||||
- ไม่มีเลข Duplicate ในระบบ
|
||||
- API ทั้งสองตอบ 201 Created สำเร็จ
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```
|
||||
1. Redis Redlock acquire บน counterKey ก่อน
|
||||
2. ถ้า Lock ไม่ได้ใน 5 วินาที → 503 Service Unavailable (Retry-After: 3s)
|
||||
@@ -54,20 +60,24 @@ related:
|
||||
---
|
||||
|
||||
### EC-DN-002 — Yearly Reset Boundary Condition
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Business Rule, Data Integrity
|
||||
|
||||
**Scenario A:** Document ถูก Submit เวลา 23:59:59 วันที่ 31 ธันวาคม
|
||||
**Scenario B:** Cron Job Reset Counter ทำงานตอนเที่ยงคืน แต่มี Document ในสถานะ RESERVED อยู่
|
||||
|
||||
**Expected Behavior (A):**
|
||||
|
||||
- ได้รับเลขของปีเก่า (counter ปีเก่า) — เวลา Submit คือที่กำหนด
|
||||
- ถ้า Confirm หลังเที่ยงคืน → เลขยังเป็นของปีเก่า (ใช้เวลา Reserve ไม่ใช่ Confirm)
|
||||
|
||||
**Expected Behavior (B):**
|
||||
|
||||
- Cron Job ต้อง **Skip** เลขที่อยู่ใน RESERVED state — ไม่ Reset Counter จนกว่า Reservation จะ Expire หรือ Confirmed
|
||||
- ถ้า Reset รันก่อน Expiry: Counter ใหม่เริ่ม 0001 แต่ Reserved เลขยังคงอยู่ (ไม่ถูก Overwrite)
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```
|
||||
- Cron Job ติด Lock เดียวกับ Reserve Process ก่อน Reset
|
||||
- Reset scope = 'YEAR_2025' → Counter Key ใหม่ = 'YEAR_2026'
|
||||
@@ -77,11 +87,13 @@ related:
|
||||
---
|
||||
|
||||
### EC-DN-003 — Cancelled/Voided Number Must Not Reuse
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Business Rule, Data Integrity
|
||||
|
||||
**Scenario:** Document ถูก Submit → ได้เลข 0005 → Admin Cancel Document → User Submit ใหม่
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- เลขถัดไปต้อง **0006** ไม่ใช่ 0005
|
||||
- เลข 0005 อยู่ใน `document_number_reservations` สถานะ CANCELLED ตลอดไป
|
||||
- ไม่มีการ Reuse เลขที่ถูก Cancel เด็ดขาด
|
||||
@@ -91,16 +103,19 @@ related:
|
||||
---
|
||||
|
||||
### EC-DN-004 — Reservation TTL Expired Cleanup
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Data Integrity, UX
|
||||
|
||||
**Scenario:** User Reserve เลข (TTL 5 นาที) แต่ Browser ปิดก่อน Confirm
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- หลัง 5 นาที → `document_number_reservations.status` เปลี่ยนเป็น EXPIRED (by Cron/TTL)
|
||||
- Counter ไม่ถูก Decrement (เลขนั้นหายไปถาวร — ฟัน-หลอ-เลข เป็นที่ยอมรับ)
|
||||
- ถ้า User กลับมา Confirm Token ที่ Expired → 410 Gone (Token expired)
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```sql
|
||||
-- Cron ทุก 1 นาที
|
||||
UPDATE document_number_reservations
|
||||
@@ -111,11 +126,13 @@ WHERE status = 'RESERVED' AND expires_at < NOW();
|
||||
---
|
||||
|
||||
### EC-DN-005 — Idempotency Key Duplicate Submission
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Concurrency, UX
|
||||
|
||||
**Scenario:** Network ไม่เสถียร → User คลิก Submit 2 ครั้ง → Frontend ส่ง POST 2 ครั้งด้วย Idempotency-Key เดียวกัน
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Request แรก → ออกเลขใหม่ → 201 Created
|
||||
- Request ที่สอง (same Idempotency-Key) → **Return เลขเดิม** → 200 OK (ไม่ออกเลขใหม่)
|
||||
- ไม่ว่า Request ที่สองจะมาเร็วแค่ไหน
|
||||
@@ -127,17 +144,20 @@ WHERE status = 'RESERVED' AND expires_at < NOW();
|
||||
## Module 2: Workflow Engine Edge Cases
|
||||
|
||||
### EC-WF-001 — Concurrent Approval (Parallel Steps)
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Concurrency, Business Rule
|
||||
|
||||
**Scenario:** Workflow มี Parallel Approval (Engineer A **และ** Engineer B ต้อง Approve พร้อมกัน)
|
||||
Engineer A Approve พร้อมกับ Engineer B Approve ใน millisecond เดียวกัน
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Workflow System บันทึกทั้งสอง Action อย่างถูกต้อง
|
||||
- State เปลี่ยนเป็น "Approved" ก็ต่อเมื่อ **ทุก Parallel Branch** Complete แล้ว
|
||||
- ไม่เกิด State Corruption (เช่น State ถูก Override โดย Action หนึ่ง)
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```
|
||||
- DB Transaction Isolation: SERIALIZABLE สำหรับ State Transition
|
||||
- Check: all parallel branches completed → ถ้าใช่ → advance to next state
|
||||
@@ -147,16 +167,19 @@ Engineer A Approve พร้อมกับ Engineer B Approve ใน millisecon
|
||||
---
|
||||
|
||||
### EC-WF-002 — Action on Wrong Workflow State
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security, Business Rule
|
||||
|
||||
**Scenario A:** Reviewer พยายาม Approve เอกสารที่ถูก Cancel แล้ว
|
||||
**Scenario B:** Reviewer Approve เอกสารที่ Approve ไปแล้ว (Double-click)
|
||||
|
||||
**Expected Behavior (A):**
|
||||
|
||||
- `GET /correspondences/:id` → status: CANCELLED → ปุ่ม Approve ไม่แสดง (UI)
|
||||
- ถ้าโจมตีตรงๆ ผ่าน API → 422 Unprocessable Entity (Invalid state transition)
|
||||
|
||||
**Expected Behavior (B):**
|
||||
|
||||
- `workflow_state_transitions` ตรวจสอบ current_state + action ก่อน
|
||||
- ถ้า Action ไม่ Valid สำหรับ State ปัจจุบัน → 409 Conflict (Already processed)
|
||||
- Idempotency: ถ้า User กด Approve ซ้ำด้วย Action เดียวกัน → Return เดิม ไม่ Error
|
||||
@@ -164,27 +187,32 @@ Engineer A Approve พร้อมกับ Engineer B Approve ใน millisecon
|
||||
---
|
||||
|
||||
### EC-WF-003 — Force Proceed on Final State
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** Document Control กด "Force Proceed" บนเอกสารที่อยู่ใน APPROVED (Final State) แล้ว
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ถ้าไม่มี Next State ใน DSL → ปุ่ม Force Proceed ไม่แสดง (UI)
|
||||
- ถ้าเรียก API ตรงๆ → 422 (No next state available from current state)
|
||||
|
||||
---
|
||||
|
||||
### EC-WF-004 — Workflow Definition Changed During Execution
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, Data Integrity
|
||||
|
||||
**Scenario:** Admin แก้ไข Workflow DSL ขณะที่มี Workflow Instance กำลังดำเนินการอยู่
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Workflow Instance ที่กำลังเดินอยู่ **ใช้ DSL เวอร์ชันที่สร้าง Instance** (Snapshot at creation)
|
||||
- Instance ใหม่ที่สร้างหลังจากนั้นใช้ DSL เวอร์ชันใหม่
|
||||
- ไม่มีการ Interrupt Instance ที่กำลังเดินอยู่
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```
|
||||
workflow_instances.workflow_definition_snapshot (JSON) — บันทึก DSL ณ เวลาสร้าง
|
||||
ไม่ Reference workflow_definitions.id โดยตรงสำหรับ Active Instances
|
||||
@@ -193,11 +221,13 @@ workflow_instances.workflow_definition_snapshot (JSON) — บันทึก DS
|
||||
---
|
||||
|
||||
### EC-WF-005 — Deadline Passed — No Action Taken
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** Deadline ของ Organization ผ่านไปแล้ว แต่ User ยังไม่ Approve
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Workflow **ไม่ Auto-advance** (ต้องการ Human Decision เสมอ)
|
||||
- Dashboard แสดง "Overdue" Badge (สีแดง)
|
||||
- Notification Reminder ส่งซ้ำตาม Schedule (ไม่ใช่ตลอดเวลา — Anti-Spam)
|
||||
@@ -208,11 +238,13 @@ workflow_instances.workflow_definition_snapshot (JSON) — บันทึก DS
|
||||
## Module 3: File Storage Edge Cases
|
||||
|
||||
### EC-STOR-001 — File Upload During Network Interruption
|
||||
|
||||
**Severity:** 🟠 High | **Type:** UX, Data Integrity
|
||||
|
||||
**Scenario:** User Upload ไฟล์ 50MB ผ่าน Wi-Fi แล้วเน็ตหลุดระหว่าง Upload
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Partial upload ไม่ถูก Save ใน Temp Storage
|
||||
- User เห็น Error: "การอัปโหลดล้มเหลว กรุณาลองใหม่" + ปุ่ม Retry
|
||||
- Draft ข้อมูล Form (ที่ไม่ใช่ไฟล์) ยังอยู่ใน LocalStorage (Auto-saved)
|
||||
@@ -221,11 +253,13 @@ workflow_instances.workflow_definition_snapshot (JSON) — บันทึก DS
|
||||
---
|
||||
|
||||
### EC-STOR-002 — Virus Detected in Uploaded File
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** User พยายาม Upload ไฟล์ที่ ClamAV ตรวจพบ Malware
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ClamAV Scan ใน Temp Storage → พบ → ลบไฟล์ออกจาก Temp ทันที
|
||||
- API ตอบ 422 Unprocessable Entity: `{ "error": "FILE_VIRUS_DETECTED", "filename": "..." }`
|
||||
- Audit Log บันทึก: `VIRUS_DETECTED` + filename + user_id + ip_address
|
||||
@@ -235,17 +269,20 @@ workflow_instances.workflow_definition_snapshot (JSON) — บันทึก DS
|
||||
---
|
||||
|
||||
### EC-STOR-003 — File Type Mismatch (MIME Sniffing Attack)
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** Attacker เปลี่ยน Extension ไฟล์ `malware.exe` → `document.pdf` แล้ว Upload
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Backend ตรวจ MIME Type จาก **File Content** (ไม่ใช่ Extension)
|
||||
- ถ้า MIME Type ไม่ตรงกับ Whitelist (PDF, DWG, ZIP, DOCX) → 400 Bad Request
|
||||
- ถ้า Extension กับ MIME Type ไม่ตรงกัน → 400 Bad Request: "File type mismatch"
|
||||
- Audit Log บันทึก Security Event
|
||||
|
||||
**Whitelist:**
|
||||
|
||||
```
|
||||
PDF: application/pdf
|
||||
DWG: application/acad, image/vnd.dwg
|
||||
@@ -257,16 +294,19 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-STOR-004 — Orphan File Cleanup (Document Cancelled Before Confirm)
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Data Integrity, Storage
|
||||
|
||||
**Scenario:** User Reserve Document Number → อัปโหลดไฟล์ไป Temp → Cancel Document → ออกจากหน้า
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Temp files ต้องถูกลบออกจาก Storage ภายใน 1 ชั่วโมง (Cleanup Cron)
|
||||
- ไม่มี Orphan Files ใน Temp Storage เกิน TTL
|
||||
- Permanent Storage ไม่มีไฟล์ที่ไม่มี Document Reference
|
||||
|
||||
**Implementation Rule:**
|
||||
|
||||
```typescript
|
||||
// Cron ทุกชั่วโมง
|
||||
// ลบ Temp files ที่ older than 1 hour และ ไม่ได้ถูก Confirm
|
||||
@@ -275,11 +315,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-STOR-005 — Duplicate File Upload Detection
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** UX, Storage
|
||||
|
||||
**Scenario:** User อัปโหลดไฟล์เดิมซ้ำสองครั้ง (ลืมว่าอัปโหลดแล้ว)
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- **ไม่ Block** การ Upload ซ้ำ — เก็บเป็น 2 Attachment แยกกัน
|
||||
- แสดง Warning (ไม่ใช่ Error): "ไฟล์นี้อาจถูกอัปโหลดแล้ว — ชื่อเดียวกัน"
|
||||
- User สามารถลบ Duplicate ออกก่อน Submit
|
||||
@@ -289,11 +331,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
## Module 4: RFA & Drawing Edge Cases
|
||||
|
||||
### EC-RFA-001 — 1 Shop Drawing Revision = Max 1 RFA Constraint
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Business Rule, Data Integrity
|
||||
|
||||
**Scenario:** Document Control พยายามสร้าง RFA ที่สอง สำหรับ Shop Drawing Revision เดิม
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ตรวจสอบ: `rfas WHERE shop_drawing_revision_id = X AND status NOT IN ('REJECTED', 'CANCELLED')`
|
||||
- ถ้ามี Active RFA อยู่แล้ว → 409 Conflict: "Shop Drawing Revision นี้มี RFA อยู่แล้ว"
|
||||
- UI: Disable ปุ่ม "สร้าง RFA" ถ้า Revision มี Active RFA แล้ว
|
||||
@@ -303,11 +347,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-RFA-002 — RFA Revision While Previous Still Pending
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Business Rule
|
||||
|
||||
**Scenario:** RFA Rev.A ยัง Pending Review อยู่ แต่ Contractor พยายามสร้าง Rev.B
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ถ้า Rev.A ยังไม่มีคำตอบสุดท้าย (REJECTED/APPROVED/APPROVED_WITH_COMMENTS) → Block
|
||||
- 409 Conflict: "ต้องรอคำตอบของ Revision ก่อนหน้าก่อน"
|
||||
- ไม่อนุญาตให้มี 2 Active Revision พร้อมกัน
|
||||
@@ -315,11 +361,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-RFA-003 — Shop Drawing Uploaded to Wrong Category
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** User เลือก Discipline = "Structural" แต่ Upload Shop Drawing ที่เป็น Electrical
|
||||
|
||||
**Expected Behavior (MVP):**
|
||||
|
||||
- ไม่มี Auto-detection (AI Classification เป็น Phase 3)
|
||||
- Validation: Discipline ต้องเลือก (ไม่มี Default)
|
||||
- เตือนผู้ใช้ให้ตรวจสอบก่อน Submit (Review Mode)
|
||||
@@ -328,11 +376,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-RFA-004 — Transmittal Contains Mixed-Status RFAs
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Business Rule
|
||||
|
||||
**Scenario:** Transmittal ถูกสร้างโดยรวม RFA บางฉบับที่ยัง DRAFT และบางฉบับที่ READY
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Transmittal Submit ได้เฉพาะเมื่อ **ทุก RFA ใน Transmittal** อยู่ในสถานะ READY (ไม่ใช่ DRAFT)
|
||||
- ถ้ามี DRAFT อยู่ → 422: "RFA [เลข] ยังอยู่ใน Draft กรุณา Submit ก่อน"
|
||||
- UI: แสดง Status ของแต่ละ RFA ใน Transmittal ก่อน Submit
|
||||
@@ -342,12 +392,14 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
## Module 5: Authentication & Session Edge Cases
|
||||
|
||||
### EC-AUTH-001 — Token Refresh Race Condition
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Concurrency, Security
|
||||
|
||||
**Scenario:** Browser Tab A และ Tab B ทำ API Call พร้อมกันด้วย Access Token ที่ Expired
|
||||
ทั้งสองตรวจพบ 401 และพยายาม Refresh Token พร้อมกัน
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ใช้ **Single Refresh Promise Pattern**: Tab แรกที่ Refresh สำเร็จ → Tab ที่สองใช้ Token ใหม่ (ไม่ Refresh ซ้อน)
|
||||
- ถ้า Tab ที่สอง Refresh ก็ได้ Token ใหม่เหมือนกัน → ถือว่า OK (Refresh Token ยังใช้ได้)
|
||||
- Refresh Token ถูก Rotate ทุกครั้งที่ใช้ (Refresh Token Rotation)
|
||||
@@ -357,11 +409,13 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-AUTH-002 — Permission Changed While User is Logged In
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security, Business Rule
|
||||
|
||||
**Scenario:** Admin เปลี่ยน Role ของ User จาก Document Control → Viewer ขณะที่ User กำลัง Login อยู่
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Redis Permission Cache ของ User ถูกล้าง **ทันที** (ไม่รอ TTL)
|
||||
- Access Token เดิมยังใช้ได้จนหมดอายุ (15 นาที) — เป็นที่ยอมรับ
|
||||
- **Request ถัดไปหลัง Token Refresh** → Permission ใหม่มีผล
|
||||
@@ -370,31 +424,37 @@ XLSX: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
---
|
||||
|
||||
### EC-AUTH-003 — Concurrent Login (Same Account, Multiple Devices)
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Security, Business Rule
|
||||
|
||||
**Scenario:** User Login จาก 2 Device พร้อมกัน (PC และ Tablet)
|
||||
|
||||
**Expected Behavior (MVP):**
|
||||
|
||||
- อนุญาต (Session ทั้งสองทำงาน Independent)
|
||||
- แต่ละ Device มี Refresh Token แยกกัน
|
||||
- Logout จาก Device หนึ่ง → Revoke เฉพาะ Refresh Token ของ Device นั้น
|
||||
|
||||
**Future Enhancement (Phase 2):**
|
||||
|
||||
- Option: "Logout จาก Device อื่นทั้งหมด"
|
||||
|
||||
---
|
||||
|
||||
### EC-AUTH-004 — Account Deactivated While Logged In
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** Admin Deactivate User Account ขณะที่ User กำลัง Login อยู่
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Redis: Blacklist User ID (ทุก Token ของ User นั้นถือว่า Invalid ทันที)
|
||||
- Request ถัดไปของ User → 401 Unauthorized: "Account has been deactivated"
|
||||
- User ถูก Redirect ไปหน้า Login พร้อม Message ชัดเจน
|
||||
|
||||
**Implementation:**
|
||||
|
||||
```typescript
|
||||
// ใน JWT Guard: ตรวจ Redis Blacklist ก่อน Validate Token
|
||||
const isBlacklisted = await redis.get(`user:blacklist:${userId}`);
|
||||
@@ -406,11 +466,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
## Module 6: Permission & RBAC Edge Cases
|
||||
|
||||
### EC-PERM-001 — Direct Object Reference (IDOR Attack)
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** User A รู้ ID ของเอกสาร User B (เช่น `/correspondences/12345`) แล้วเรียกตรงๆ
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- CASL AbilityGuard ตรวจสอบทั้ง Action และ Resource Owner
|
||||
- ถ้าไม่มีสิทธิ์ → **403 Forbidden** (ไม่ใช่ 404 — เพราะ 404 บอกว่ามีอยู่แต่หาไม่เจอ)
|
||||
- **Exception:** ถ้าต้องการซ่อน Existence ของ Document → Return 404
|
||||
@@ -419,11 +481,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-PERM-002 — Super Admin Impersonation Prevention
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** User พยายาม Forge JWT payload เพิ่ม role: 'SUPERADMIN'
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- JWT ถูก Sign ด้วย Secret ที่ไม่เปิดเผย → Signature ไม่ตรง → 401 Invalid token
|
||||
- Role ไม่ถูก Read จาก Token โดยตรงสำหรับ Permission Check — ต้อง Verify จาก DB/Redis
|
||||
- JWT payload ใช้แค่ `user_id` → ดึง Permission จาก Redis Cache/DB
|
||||
@@ -431,11 +495,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-PERM-003 — Organization Switch Mid-session
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario (ถ้ามี):** User เป็นสมาชิกในหลาย Organization (กรณี Consultant ที่ทำงานหลายโครงการ)
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- User เห็นเฉพาะ Data ขององค์กรที่ Login อยู่ (Active Context)
|
||||
- ถ้าต้องการดูอีก Org → ต้อง "Switch Organization" (Session Context เปลี่ยน)
|
||||
- ไม่มี Cross-org Data Leak แม้ User เป็นสมาชิกทั้งสอง Org
|
||||
@@ -445,11 +511,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
## Module 7: Correspondence Edge Cases
|
||||
|
||||
### EC-CORR-001 — Cancel Correspondence with Downstream Circulation
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Business Rule, Data Integrity
|
||||
|
||||
**Scenario:** Correspondence ถูก Submit → ผู้รับสร้าง Circulation แล้ว → Originator ขอ Cancel
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ต้องแจ้งเตือน Admin ว่า "มี Circulation ที่เปิดอยู่ [X รายการ] สำหรับเอกสารนี้"
|
||||
- ต้องยืนยันก่อน Cancel: "การ Cancel จะส่งผลให้ Circulation ที่เกี่ยวข้องถูกปิดทั้งหมด"
|
||||
- เมื่อ Confirm → Correspondence = CANCELLED + Circulation ที่เกี่ยวข้อง = FORCE_CLOSED
|
||||
@@ -458,11 +526,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-CORR-002 — Reply to Cancel Correspondence
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule
|
||||
|
||||
**Scenario:** Document Control พยายามสร้าง Correspondence เพื่อ Reply ต่อ Correspondence ที่ถูก Cancel
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Reply ทำได้ — Reference ถึง CANCELLED เอกสารได้ (เพื่อ acknowledge การยกเลิก)
|
||||
- UI แสดง Warning: "กำลัง Reply ต่อเอกสารที่ถูกยกเลิกแล้ว"
|
||||
- ไม่ Block การ Reply (เป็น Business Decision ไม่ใช่ Technical Constraint)
|
||||
@@ -470,11 +540,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-CORR-003 — Correspondence to Self (Same Organization)
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule
|
||||
|
||||
**Scenario:** User พยายามสร้าง Correspondence ที่ Sender และ Receiver เป็นองค์กรเดียวกัน
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- External Correspondence (Letter/RFI) → Block: "ไม่สามารถส่งหาตัวเองได้"
|
||||
- Internal Communication → ใช้ Circulation Sheet แทน (ไม่ใช่ Correspondence)
|
||||
- UI Validation + Backend Validation (Double Check)
|
||||
@@ -484,11 +556,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
## Module 8: Circulation Edge Cases
|
||||
|
||||
### EC-CIRC-001 — Assignee Deactivated Before Completing Task
|
||||
|
||||
**Severity:** 🟠 High | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** User ถูก Deactivate หลังจากถูก Assign ใน Circulation แต่ก่อน Respond
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Circulation ยัง Active อยู่ — ไม่หยุดอัตโนมัติ
|
||||
- Document Control เห็น Warning: "Assignee [ชื่อ] ไม่ Active แล้ว"
|
||||
- Document Control สามารถ Re-assign ไปยัง User อื่นได้
|
||||
@@ -497,11 +571,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-CIRC-002 — Multi-Assignee: Partial Response
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** Circulation มี Action Assignees 3 คน — 2 คน Respond แล้ว แต่ 1 คนยังไม่ Respond
|
||||
|
||||
**Expected Behavior (MVP):**
|
||||
|
||||
- Document Control เห็นสถานะ "2/3 ตอบกลับแล้ว"
|
||||
- Document Control สามารถ Force Close ได้ (พร้อมระบุเหตุผล)
|
||||
- ถ้า Force Close → ทุก Partial Response ถูกบันทึก + หมายเหตุว่า Force Closed
|
||||
@@ -509,11 +585,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-CIRC-003 — Circulation Deadline = Today (Edge of Day)
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Business Rule, UX
|
||||
|
||||
**Scenario:** Deadline ถูกกำหนด = "วันนี้" แต่ User ดูตอนบ่ายสอง
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ถ้า Deadline = วันที่ X → หมดเขตเมื่อ X เวลา 23:59:59 (ไม่ใช่ 00:00:00)
|
||||
- Reminder: ส่ง Notification เวลา 08:00 ของวัน Deadline
|
||||
- Overdue Badge ขึ้นเมื่อ `NOW() > deadline_date + 1 day` (วันถัดไป 00:00)
|
||||
@@ -523,11 +601,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
## Module 9: Search & Elasticsearch Edge Cases
|
||||
|
||||
### EC-SRCH-001 — Search Index Lag (Eventual Consistency)
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Data Consistency, UX
|
||||
|
||||
**Scenario:** Document ถูก Submit แล้ว → User ค้นหาทันที แต่ไม่เจอ
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Index อาจ Lag 5–30 วินาที (BullMQ Async Job)
|
||||
- UI แสดง "เอกสารอาจใช้เวลาสักครู่ก่อนปรากฏในผลค้นหา"
|
||||
- **ไม่ถือว่า Bug** — เป็น By Design (Eventual Consistency)
|
||||
@@ -536,11 +616,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-SRCH-002 — Permission-filtered Search Results
|
||||
|
||||
**Severity:** 🔴 Critical | **Type:** Security
|
||||
|
||||
**Scenario:** Contractor A ค้นหา Keyword ที่มีใน Document ของ Contractor B
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Elasticsearch Index ต้องมี `organization_id` / `contract_id` Field
|
||||
- ทุก Search Query ต้อง Filter ด้วย `must: [{ term: { visible_to_org: userOrgId } }]`
|
||||
- Contractor A **ไม่เห็น** Document ของ Contractor B ในผลค้นหา
|
||||
@@ -549,11 +631,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-SRCH-003 — Special Characters in Search Query
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** Security, UX
|
||||
|
||||
**Scenario:** User ค้นหาด้วย `คคง. สค. - 2025` (มี `-`, `.`, ช่องว่าง)
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- ไม่ Crash — Elasticsearch รองรับ Special Characters
|
||||
- Sanitize Query ก่อนส่ง (กัน Elasticsearch Injection)
|
||||
- ผล Search ยังคง Relevance สูง
|
||||
@@ -563,11 +647,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
## Module 10: Notifications Edge Cases
|
||||
|
||||
### EC-NOTIF-001 — Notification Flood Prevention
|
||||
|
||||
**Severity:** 🟠 High | **Type:** UX, Anti-Spam
|
||||
|
||||
**Scenario:** Workflow มีหลาย Step ที่เปลี่ยนเร็ว → ส่ง Notification ทุก State Change → User ได้รับ Email 10 ฉบับในนาทีเดียว
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- **Notification Debounce/Batch:** รวม Notifications ภายใน 5 นาทีเป็น Summary Email เดียว
|
||||
- ถ้าเปลี่ยน State 5 ครั้งใน 5 นาที → Email เดียว: "เอกสาร X มี 5 การเปลี่ยนแปลง"
|
||||
- In-App Notifications ยังแสดงทุกรายการ (ไม่ Batch)
|
||||
@@ -575,11 +661,13 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
---
|
||||
|
||||
### EC-NOTIF-002 — User Unsubscribed from EMAIL but still needs In-App
|
||||
|
||||
**Severity:** 🟡 Medium | **Type:** UX, Business Rule
|
||||
|
||||
**Scenario:** User ปิด Email Notification แต่ยังต้องการ In-App Notification
|
||||
|
||||
**Expected Behavior:**
|
||||
|
||||
- Notification Settings: แยก Toggle สำหรับ Email / LINE / In-App
|
||||
- Core Workflow Assignments (ที่ User ต้อง Action) → **ไม่สามารถ Disable** ทุก Channel ได้
|
||||
- ต้องมี In-App อย่างน้อย 1 Channel สำหรับ Action Required Notifications
|
||||
@@ -588,34 +676,33 @@ if (isBlacklisted) throw new UnauthorizedException('Account deactivated');
|
||||
|
||||
## 📊 Edge Case Summary by Module
|
||||
|
||||
| Module | Critical | High | Medium | Total |
|
||||
|--------|----------|------|--------|-------|
|
||||
| Document Numbering | 2 | 2 | 1 | 5 |
|
||||
| Workflow Engine | 2 | 1 | 2 | 5 |
|
||||
| File Storage | 2 | 2 | 1 | 5 |
|
||||
| RFA & Drawing | 1 | 2 | 1 | 4 |
|
||||
| Auth & Session | 3 | 0 | 1 | 4 |
|
||||
| Permission & RBAC | 2 | 0 | 1 | 3 |
|
||||
| Correspondence | 1 | 0 | 2 | 3 |
|
||||
| Circulation | 0 | 1 | 2 | 3 |
|
||||
| Search | 1 | 0 | 2 | 3 |
|
||||
| Notifications | 0 | 1 | 1 | 2 |
|
||||
| **รวม** | **14** | **9** | **14** | **37** |
|
||||
| Module | Critical | High | Medium | Total |
|
||||
| ------------------ | -------- | ----- | ------ | ------ |
|
||||
| Document Numbering | 2 | 2 | 1 | 5 |
|
||||
| Workflow Engine | 2 | 1 | 2 | 5 |
|
||||
| File Storage | 2 | 2 | 1 | 5 |
|
||||
| RFA & Drawing | 1 | 2 | 1 | 4 |
|
||||
| Auth & Session | 3 | 0 | 1 | 4 |
|
||||
| Permission & RBAC | 2 | 0 | 1 | 3 |
|
||||
| Correspondence | 1 | 0 | 2 | 3 |
|
||||
| Circulation | 0 | 1 | 2 | 3 |
|
||||
| Search | 1 | 0 | 2 | 3 |
|
||||
| Notifications | 0 | 1 | 1 | 2 |
|
||||
| **รวม** | **14** | **9** | **14** | **37** |
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy for Edge Cases
|
||||
|
||||
### สำหรับ Unit Tests (Backend)
|
||||
|
||||
```typescript
|
||||
// ตัวอย่าง: EC-DN-001 — Concurrent Number Generation
|
||||
describe('DocumentNumberingService - Concurrency', () => {
|
||||
it('should generate unique numbers for concurrent requests', async () => {
|
||||
const promises = Array.from({ length: 50 }, () =>
|
||||
service.reserve({ projectId: 1, typeId: 2, orgId: 3 })
|
||||
);
|
||||
const promises = Array.from({ length: 50 }, () => service.reserve({ projectId: 1, typeId: 2, orgId: 3 }));
|
||||
const results = await Promise.all(promises);
|
||||
const numbers = results.map(r => r.documentNumber);
|
||||
const numbers = results.map((r) => r.documentNumber);
|
||||
const unique = new Set(numbers);
|
||||
expect(unique.size).toBe(50); // ไม่มีซ้ำ
|
||||
});
|
||||
@@ -623,11 +710,13 @@ describe('DocumentNumberingService - Concurrency', () => {
|
||||
```
|
||||
|
||||
### สำหรับ Integration Tests
|
||||
|
||||
- EC-DN-001: k6 Load Test Script (50 VUs, `/document-numbering/reserve`)
|
||||
- EC-AUTH-001: Cypress Multi-tab Token Refresh Test
|
||||
- EC-PERM-001: API Test Suite — Direct Object Reference สำหรับทุก Resource
|
||||
|
||||
### สำหรับ Manual UAT
|
||||
|
||||
- EC-WF-001: Test Parallel Approval ด้วย 2 Browser Session พร้อมกัน
|
||||
- EC-STOR-002: Upload EICAR Test File (ClamAV Test Virus)
|
||||
- EC-RFA-001: สร้าง RFA สำหรับ Revision เดิมที่มี Active RFA → Assert Block
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
# 🖼️ UI/UX Wireframes & Screen Inventory — LCBP3-DMS v1.8.0
|
||||
|
||||
---
|
||||
|
||||
title: 'UI/UX Screen Inventory, Navigation Map, and Wireframes'
|
||||
version: 1.0.0
|
||||
status: DRAFT
|
||||
owner: Nattanin Peancharoen (Product Owner)
|
||||
last_updated: 2026-03-11
|
||||
related:
|
||||
- specs/01-Requirements/01-02-business-rules/01-02-03-ui-ux-rules.md
|
||||
- specs/01-Requirements/01-04-user-stories.md
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
|
||||
- specs/01-Requirements/01-02-business-rules/01-02-03-ui-ux-rules.md
|
||||
- specs/01-Requirements/01-04-user-stories.md
|
||||
- specs/01-Requirements/01-05-acceptance-criteria.md
|
||||
|
||||
---
|
||||
|
||||
> [!NOTE]
|
||||
@@ -112,34 +115,34 @@ Mobile: Sidebar → Collapsible Hamburger Drawer (ตาม UI-Rule 5.11)
|
||||
|
||||
## 3. 📋 Screen Inventory
|
||||
|
||||
| Screen ID | Route | ชื่อหน้า | Primary Role | Priority |
|
||||
|-----------|-------|---------|-------------|---------|
|
||||
| SCR-001 | `/login` | Login | ทุก Role | 🔴 Must |
|
||||
| SCR-002 | `/login/change-password` | Force Password Change | ทุก Role | 🔴 Must |
|
||||
| SCR-003 | `/dashboard` | Dashboard | ทุก Role | 🔴 Must |
|
||||
| SCR-004 | `/correspondences` | Correspondence List | Doc Control | 🔴 Must |
|
||||
| SCR-005 | `/correspondences/new` | Create Correspondence | Doc Control | 🔴 Must |
|
||||
| SCR-006 | `/correspondences/:id` | Correspondence Detail + Workflow | ทุก Role | 🔴 Must |
|
||||
| SCR-007 | `/rfas` | RFA List | Doc Control | 🔴 Must |
|
||||
| SCR-008 | `/rfas/new` | Create RFA | Doc Control | 🔴 Must |
|
||||
| SCR-009 | `/rfas/:id` | RFA Detail + Workflow | ทุก Role | 🔴 Must |
|
||||
| SCR-010 | `/transmittals` | Transmittal List | Doc Control | 🟠 Should |
|
||||
| SCR-011 | `/transmittals/new` | Create Transmittal | Doc Control | 🟠 Should |
|
||||
| SCR-012 | `/transmittals/:id` | Transmittal Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-013 | `/drawings/contract` | Contract Drawing List | Doc Control | 🟠 Should |
|
||||
| SCR-014 | `/drawings/shop` | Shop Drawing List | Doc Control | 🟠 Should |
|
||||
| SCR-015 | `/drawings/shop/:id` | Shop Drawing Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-016 | `/circulations` | Circulation List | Doc Control | 🟠 Should |
|
||||
| SCR-017 | `/circulations/new` | Create Circulation | Doc Control | 🟠 Should |
|
||||
| SCR-018 | `/circulations/:id` | Circulation Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-019 | `/search` | Search Results | ทุก Role | 🟠 Should |
|
||||
| SCR-020 | `/notifications` | Notification Center | ทุก Role | 🟡 Could |
|
||||
| SCR-021 | `/profile` | Profile & Settings | ทุก Role | 🟠 Should |
|
||||
| SCR-022 | `/admin/users` | User Management | Org Admin+ | 🔴 Must |
|
||||
| SCR-023 | `/admin/organizations` | Organization Management | Superadmin | 🔴 Must |
|
||||
| SCR-024 | `/admin/projects` | Project & Contract Mgmt | Superadmin | 🔴 Must |
|
||||
| SCR-025 | `/admin/doc-numbering` | Document Number Config | Superadmin | 🟠 Should |
|
||||
| SCR-026 | `/admin/audit-logs` | Audit Log Viewer | Org Admin+ | 🟠 Should |
|
||||
| Screen ID | Route | ชื่อหน้า | Primary Role | Priority |
|
||||
| --------- | ------------------------ | -------------------------------- | ------------ | --------- |
|
||||
| SCR-001 | `/login` | Login | ทุก Role | 🔴 Must |
|
||||
| SCR-002 | `/login/change-password` | Force Password Change | ทุก Role | 🔴 Must |
|
||||
| SCR-003 | `/dashboard` | Dashboard | ทุก Role | 🔴 Must |
|
||||
| SCR-004 | `/correspondences` | Correspondence List | Doc Control | 🔴 Must |
|
||||
| SCR-005 | `/correspondences/new` | Create Correspondence | Doc Control | 🔴 Must |
|
||||
| SCR-006 | `/correspondences/:id` | Correspondence Detail + Workflow | ทุก Role | 🔴 Must |
|
||||
| SCR-007 | `/rfas` | RFA List | Doc Control | 🔴 Must |
|
||||
| SCR-008 | `/rfas/new` | Create RFA | Doc Control | 🔴 Must |
|
||||
| SCR-009 | `/rfas/:id` | RFA Detail + Workflow | ทุก Role | 🔴 Must |
|
||||
| SCR-010 | `/transmittals` | Transmittal List | Doc Control | 🟠 Should |
|
||||
| SCR-011 | `/transmittals/new` | Create Transmittal | Doc Control | 🟠 Should |
|
||||
| SCR-012 | `/transmittals/:id` | Transmittal Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-013 | `/drawings/contract` | Contract Drawing List | Doc Control | 🟠 Should |
|
||||
| SCR-014 | `/drawings/shop` | Shop Drawing List | Doc Control | 🟠 Should |
|
||||
| SCR-015 | `/drawings/shop/:id` | Shop Drawing Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-016 | `/circulations` | Circulation List | Doc Control | 🟠 Should |
|
||||
| SCR-017 | `/circulations/new` | Create Circulation | Doc Control | 🟠 Should |
|
||||
| SCR-018 | `/circulations/:id` | Circulation Detail | ทุก Role | 🟠 Should |
|
||||
| SCR-019 | `/search` | Search Results | ทุก Role | 🟠 Should |
|
||||
| SCR-020 | `/notifications` | Notification Center | ทุก Role | 🟡 Could |
|
||||
| SCR-021 | `/profile` | Profile & Settings | ทุก Role | 🟠 Should |
|
||||
| SCR-022 | `/admin/users` | User Management | Org Admin+ | 🔴 Must |
|
||||
| SCR-023 | `/admin/organizations` | Organization Management | Superadmin | 🔴 Must |
|
||||
| SCR-024 | `/admin/projects` | Project & Contract Mgmt | Superadmin | 🔴 Must |
|
||||
| SCR-025 | `/admin/doc-numbering` | Document Number Config | Superadmin | 🟠 Should |
|
||||
| SCR-026 | `/admin/audit-logs` | Audit Log Viewer | Org Admin+ | 🟠 Should |
|
||||
|
||||
**รวม:** 26 หน้า (9 Must / 13 Should / 1 Could)
|
||||
|
||||
@@ -536,58 +539,62 @@ User Edit Drawer (Slide in from right):
|
||||
## 5. 🎨 Design System Reference
|
||||
|
||||
### Color Tokens
|
||||
|
||||
```css
|
||||
/* Primary — ใช้กับ Action Buttons, Links */
|
||||
--primary: hsl(221, 83%, 53%); /* Blue-600 */
|
||||
--primary: hsl(221, 83%, 53%); /* Blue-600 */
|
||||
--primary-hover: hsl(221, 83%, 45%);
|
||||
|
||||
/* Status Colors */
|
||||
--status-draft: hsl(48, 96%, 53%); /* Yellow */
|
||||
--status-submitted: hsl(217, 91%, 60%); /* Blue */
|
||||
--status-review: hsl(24, 95%, 53%); /* Orange */
|
||||
--status-approved: hsl(142, 71%, 45%); /* Green */
|
||||
--status-rejected: hsl(0, 84%, 60%); /* Red */
|
||||
--status-cancelled: hsl(215, 14%, 55%); /* Gray */
|
||||
--status-overdue: hsl(0, 84%, 60%); /* Red (same as rejected) */
|
||||
--status-draft: hsl(48, 96%, 53%); /* Yellow */
|
||||
--status-submitted: hsl(217, 91%, 60%); /* Blue */
|
||||
--status-review: hsl(24, 95%, 53%); /* Orange */
|
||||
--status-approved: hsl(142, 71%, 45%); /* Green */
|
||||
--status-rejected: hsl(0, 84%, 60%); /* Red */
|
||||
--status-cancelled: hsl(215, 14%, 55%); /* Gray */
|
||||
--status-overdue: hsl(0, 84%, 60%); /* Red (same as rejected) */
|
||||
|
||||
/* Background */
|
||||
--bg-base: hsl(222, 47%, 11%); /* Dark Navy (dark mode base) */
|
||||
--bg-surface: hsl(222, 47%, 16%); /* Card surface */
|
||||
--bg-muted: hsl(215, 28%, 17%); /* Muted sections */
|
||||
--bg-base: hsl(222, 47%, 11%); /* Dark Navy (dark mode base) */
|
||||
--bg-surface: hsl(222, 47%, 16%); /* Card surface */
|
||||
--bg-muted: hsl(215, 28%, 17%); /* Muted sections */
|
||||
```
|
||||
|
||||
### Typography
|
||||
|
||||
```css
|
||||
font-family: 'Inter', 'Noto Sans Thai', sans-serif;
|
||||
|
||||
/* Scale */
|
||||
--text-xs: 0.75rem; /* 12px — Badge, Caption */
|
||||
--text-sm: 0.875rem; /* 14px — Table cell, Label */
|
||||
--text-base:1rem; /* 16px — Body */
|
||||
--text-lg: 1.125rem; /* 18px — Subheading */
|
||||
--text-xl: 1.25rem; /* 20px — Page title */
|
||||
--text-2xl: 1.5rem; /* 24px — Dashboard KPI */
|
||||
--text-xs: 0.75rem; /* 12px — Badge, Caption */
|
||||
--text-sm: 0.875rem; /* 14px — Table cell, Label */
|
||||
--text-base: 1rem; /* 16px — Body */
|
||||
--text-lg: 1.125rem; /* 18px — Subheading */
|
||||
--text-xl: 1.25rem; /* 20px — Page title */
|
||||
--text-2xl: 1.5rem; /* 24px — Dashboard KPI */
|
||||
```
|
||||
|
||||
### Component States
|
||||
| Component | Default | Hover | Active | Disabled | Error |
|
||||
|-----------|---------|-------|--------|----------|-------|
|
||||
| Button Primary | bg-primary | bg-primary-hover | scale-95 | opacity-50 | — |
|
||||
| Input | border-gray-300 | border-primary | border-primary ring | border-gray-200 | border-red-500 |
|
||||
| Table Row | bg-surface | bg-muted | — | opacity-60 | bg-red-50 |
|
||||
| Badge | per status color | — | — | — | — |
|
||||
|
||||
| Component | Default | Hover | Active | Disabled | Error |
|
||||
| -------------- | ---------------- | ---------------- | ------------------- | --------------- | -------------- |
|
||||
| Button Primary | bg-primary | bg-primary-hover | scale-95 | opacity-50 | — |
|
||||
| Input | border-gray-300 | border-primary | border-primary ring | border-gray-200 | border-red-500 |
|
||||
| Table Row | bg-surface | bg-muted | — | opacity-60 | bg-red-50 |
|
||||
| Badge | per status color | — | — | — | — |
|
||||
|
||||
---
|
||||
|
||||
## 6. 📱 Responsive Breakpoints
|
||||
|
||||
| Breakpoint | Width | Behavior |
|
||||
|-----------|-------|---------|
|
||||
| `sm` | < 640px | Mobile: Sidebar → Drawer, Table → Cards |
|
||||
| `md` | 640-1024px | Tablet: Collapsed Sidebar |
|
||||
| `lg` | > 1024px | Desktop: Full Sidebar |
|
||||
| Breakpoint | Width | Behavior |
|
||||
| ---------- | ---------- | --------------------------------------- |
|
||||
| `sm` | < 640px | Mobile: Sidebar → Drawer, Table → Cards |
|
||||
| `md` | 640-1024px | Tablet: Collapsed Sidebar |
|
||||
| `lg` | > 1024px | Desktop: Full Sidebar |
|
||||
|
||||
**Mobile-specific Rules (UI-Rule 5.11):**
|
||||
|
||||
- ตาราง → Card View อัตโนมัติ
|
||||
- Sidebar → Collapsible Hamburger Drawer
|
||||
- Action Panel → Bottom Sheet แทน Inline Panel
|
||||
@@ -597,6 +604,7 @@ font-family: 'Inter', 'Noto Sans Thai', sans-serif;
|
||||
## 7. ⚡ Interaction Patterns
|
||||
|
||||
### Optimistic Updates (UI-Rule 5.10)
|
||||
|
||||
```
|
||||
User กด "Approve" → UI เปลี่ยนสถานะทันที (ไม่รอ API)
|
||||
↓
|
||||
@@ -606,12 +614,14 @@ Rollback UI → แสดง Toast Error: "เกิดข้อผิดพล
|
||||
```
|
||||
|
||||
### Auto-save Draft (UI-Rule 5.12)
|
||||
|
||||
```
|
||||
User พิมพ์ใน Form → debounce 2 วินาที → บันทึกลง localStorage
|
||||
ปิด Browser → เปิดใหม่ → แสดง Banner: "พบ Draft ที่บันทึกไว้ [กู้คืน] [ทิ้ง]"
|
||||
```
|
||||
|
||||
### File Upload Progress
|
||||
|
||||
```
|
||||
เลือกไฟล์ → แสดง Progress Bar → ClamAV Scan → ✅/❌
|
||||
```
|
||||
|
||||
@@ -54,8 +54,6 @@ This directory contains the functional and non-functional requirements for the L
|
||||
- 📘 [Implementation Guide](../03-implementation/03-04-document-numbering.md) - NestJS, TypeORM, Redis code examples
|
||||
- 📗 [Operations Guide](../04-operations/04-08-document-numbering-operations.md) - Monitoring, troubleshooting, runbooks
|
||||
|
||||
|
||||
|
||||
### Cross-Cutting Concerns
|
||||
|
||||
4. [Access Control & RBAC](./01-01-business-rules/01-02-01-rbac-matrix.md) - 4-level hierarchical RBAC
|
||||
@@ -104,19 +102,19 @@ See [CHANGELOG.md](../../CHANGELOG.md) for detailed version history.
|
||||
|
||||
### By Feature Status
|
||||
|
||||
| Feature Area | Requirements Doc | Status | Implementation | Operations |
|
||||
| ------------------------- | ---------------------------------------------------- | ---------- | ----------------------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| Correspondence Management | [03.2](./01-03.2-correspondence.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| RFA Management | [03.3](./01-03.3-rfa.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Contract Drawing | [03.4](./01-03.4-contract-drawing.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Shop Drawing | [03.5](./01-03.5-shop-drawing.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Workflow Engine | [03.6](./01-03.6-unified-workflow.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Transmittals | [03.7](./01-03.7-transmittals.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Circulation Sheets | [03.8](./01-03.8-circulation-sheet.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Feature Area | Requirements Doc | Status | Implementation | Operations |
|
||||
| ------------------------- | ---------------------------------------------------- | ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------- |
|
||||
| Correspondence Management | [03.2](./01-03.2-correspondence.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| RFA Management | [03.3](./01-03.3-rfa.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Contract Drawing | [03.4](./01-03.4-contract-drawing.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Shop Drawing | [03.5](./01-03.5-shop-drawing.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Workflow Engine | [03.6](./01-03.6-unified-workflow.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Transmittals | [03.7](./01-03.7-transmittals.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Circulation Sheets | [03.8](./01-03.8-circulation-sheet.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| **Document Numbering** | [03.11](./01-03.11-document-numbering.md) | ✅ Complete | ✅ [Guide](../03-implementation/03-04-document-numbering.md) | ✅ [Guide](../04-operations/04-08-document-numbering-operations.md) |
|
||||
| Access Control (RBAC) | [04](./01-02-business-rules/01-02-01-rbac-matrix.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Search (Elasticsearch) | N/A | ✅ Complete | 🔄 95% | Available |
|
||||
| Dashboard & Analytics | N/A | ✅ Complete | ✅ Complete | Available |
|
||||
| Access Control (RBAC) | [04](./01-02-business-rules/01-02-01-rbac-matrix.md) | ✅ Complete | ✅ Complete | Available |
|
||||
| Search (Elasticsearch) | N/A | ✅ Complete | 🔄 95% | Available |
|
||||
| Dashboard & Analytics | N/A | ✅ Complete | ✅ Complete | Available |
|
||||
|
||||
### By Priority
|
||||
|
||||
|
||||
Reference in New Issue
Block a user