260311:1638 20260311:1700 n8n workflow
Build and Deploy / deploy (push) Successful in 4m24s

This commit is contained in:
admin
2026-03-11 16:38:49 +07:00
parent 15b447ceeb
commit 9c0978f3fa
27 changed files with 6512 additions and 178 deletions
+460
View File
@@ -0,0 +1,460 @@
# 📖 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/
---
## 📖 วิธีอ่าน
- **Format:** `As a [role] / I want to [goal] / So that [benefit]`
- **Priority:** 🔴 M (Must) | 🟠 S (Should) | 🟡 C (Could) | ⚫ W (Won't-Now)
- **SP:** Story Points (Fibonacci) — 1 SP ≈ ความซับซ้อนระดับ Login
- **AC Link:** → `[AC-XXX-YYY]` อ้างอิง `01-05-acceptance-criteria.md`
---
## 👥 Epic 1: Authentication & User Management
### US-001 — Login เข้าระบบ
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-AUTH-001, AC-AUTH-002
```
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
---
### US-002 — เปลี่ยนรหัสผ่านครั้งแรก
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-AUTH-003
```
As a ผู้ใช้งานใหม่ที่เพิ่งถูกสร้างโดย Admin
I want to ถูกบังคับเปลี่ยนรหัสผ่านใน Login ครั้งแรก
So that ฉันมีรหัสผ่านที่ฉันรู้คนเดียว
```
**Done When:**
- Login ครั้งแรก → Redirect หน้าเปลี่ยน Password ทันที (ผ่านไม่ได้)
- Password ใหม่ต้องผ่าน Policy (8+ ตัว, อักษรใหญ่-เล็ก-ตัวเลข)
---
### US-003 — จัดการ Profile ส่วนตัว
**Priority:** 🟠 S | **SP:** 2
```
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
```
As a Superadmin
I want to สร้าง Organization, Project, Contract และผูกความสัมพันธ์กัน
So that ระบบพร้อมให้แต่ละองค์กรเริ่มทำงานได้
```
**Done When:**
- สร้าง Org → แต่งตั้ง Org Admin ได้ทันที
- สร้าง Project → ผูก Organizations (หลายองค์กร)
- สร้าง Contract → 1 Contractor ต่อ 1 Contract
- กำหนด Document Number Template ต่อ Project
---
### US-005 — Org Admin จัดการ User ในองค์กร
**Priority:** 🔴 M | **SP:** 3 | **AC:** AC-ADMIN-003
```
As a Org Admin
I want to เพิ่ม/แก้ไข/Deactivate User และกำหนด Role
So that ทีมงานเข้าถึงระบบได้ตามหน้าที่
```
**Done When:**
- เพิ่ม User → Email แจ้ง Credentials อัตโนมัติ
- Deactivate ได้ (ไม่ Delete — Audit ยังอยู่)
- เห็นเฉพาะ User ในองค์กรตัวเอง
---
### US-006 — Permission Isolation ระหว่าง Contractor
**Priority:** 🔴 M | **SP:** 2 | **AC:** AC-ADMIN-004
```
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
---
## 📨 Epic 2: Correspondence Management
### US-007 — สร้าง Correspondence Draft
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-CORR-001
```
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 (กันข้อมูลสูญ)
- ผู้ใช้นอกองค์กรไม่เห็น Draft
---
### US-008 — Submit Correspondence + Auto Number
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-CORR-002
```
As a Document Control
I want to Submit Correspondence และให้ระบบออกเลขอัตโนมัติ
So that เอกสารได้รับเลขที่ไม่ซ้ำและส่งถึงผู้รับทันที
```
**Done When:**
- ออกเลขตาม Template (เช่น `LCBP3-สค.-กทท.-LETTER-0001-68`)
- เลขไม่ซ้ำแม้ Submit พร้อมกัน (Redis Redlock)
- สถานะ → SUBMITTED | Workflow Instance ถูกสร้าง
- ผู้รับ → Email + In-App Notification
---
### US-009 — ดู Correspondence ใน Inbox
**Priority:** 🔴 M | **SP:** 3 | **AC:** AC-CORR-003
```
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
```
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
```
As a Org Admin / Superadmin
I want to ยกเลิก Correspondence ที่ Submit แล้ว พร้อมระบุเหตุผล
So that เอกสารที่ส่งผิดถูกระงับโดยมีหลักฐาน Audit
```
**Done When:**
- ต้องกรอก cancel_reason | สถานะ → CANCELLED
- Editor/Viewer ทำ Cancel ไม่ได้ → 403
---
## 📋 Epic 3: RFA Management
### US-012 — สร้าง RFA + Shop Drawing
**Priority:** 🔴 M | **SP:** 8 | **AC:** AC-RFA-001
```
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 (ไม่เห็นข้ามองค์กร)
---
### US-013 — Submit RFA ผ่าน Transmittal
**Priority:** 🔴 M | **SP:** 8 | **AC:** AC-RFA-002, AC-TRM-001
```
As a Document Control ของ Contractor
I want to สร้าง Transmittal รวม RFA หลายฉบับ ส่งไปยังที่ปรึกษา
So that ส่งเอกสารเป็นชุด ที่ปรึกษาได้รับทุกฉบับพร้อมกัน
```
**Done When:**
- Transmittal → เลือก RFA หลายฉบับ | ออกเลขเองเป็น Correspondence
- RFA แต่ละฉบับ → ออกเลขตาม Format `LCBP3-RFA-{DISCIPLINE}-{SEQ}`
- ที่ปรึกษา → Notification
---
### US-014 — Review RFA (Approved / w.Comments / Rejected)
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-RFA-003~005
```
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 บันทึกครบ
---
### US-015 — ยื่น RFA Revision ใหม่หลัง Reject
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-RFA-006
```
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)
---
## 📐 Epic 4: Drawing Management
### US-016 — Upload Contract Drawing
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-DRW-001
```
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
```
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)
---
## ⚙️ Epic 5: Workflow Engine
### US-018 — ดู Workflow Diagram ของเอกสาร
**Priority:** 🟠 S | **SP:** 3 | **AC:** AC-WF-004
```
As a ผู้ใช้งานที่มีสิทธิ์ดูเอกสาร
I want to เห็น Workflow Diagram แบบ Visual
So that รู้ทันทีว่าเอกสารอยู่ขั้นตอนไหน ใครถืออยู่
```
**Done When:**
- Workflow Diagram แสดง State ปัจจุบัน (Highlight)
- คลิก Step ที่ผ่านมา → Audit Log ย่อย (ใคร/เมื่อไหร่/Comment)
- Step ที่ยังไม่ถึง → Disabled Style
---
### US-019 — Approve / Reject ผ่าน Workflow
**Priority:** 🔴 M | **SP:** 5 | **AC:** AC-WF-002, AC-WF-003
```
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
---
### US-020 — Force Proceed + Revert (Document Control)
**Priority:** 🟡 C | **SP:** 3 | **AC:** AC-WF-005
```
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
```
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
---
## 📄 Epic 6: Circulation Sheet
### US-022 — สร้าง Circulation Sheet
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-CIRC-001
```
As a Document Control ภายในองค์กร
I want to สร้างใบเวียนสำหรับ Correspondence พร้อมกำหนดผู้รับผิดชอบ
So that งานถูก Assign ชัดเจนและมีหลักฐาน
```
**Done When:**
- กำหนด Main / Action / Information Assignees (หลายคน)
- Assignees → In-App + Email Notification
- Internal Only (ไม่เห็นข้ามองค์กร)
---
### US-023 — ติดตาม Circulation Deadline + ปิด
**Priority:** 🟠 S | **SP:** 3 | **AC:** AC-CIRC-002, AC-CIRC-003
```
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 ลบรายการที่ปิดแล้ว
---
## 🔍 Epic 7: Search & Notifications
### US-024 — ค้นหาเอกสาร Full-text
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-SRCH-001
```
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
```
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
---
## 💾 Epic 8: File & Dashboard
### US-026 — Upload ไฟล์ + ดู PDF ในแอป
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-STOR-001, AC-STOR-003
```
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
---
### US-027 — Dashboard KPI ส่วนตัว
**Priority:** 🟠 S | **SP:** 5 | **AC:** AC-DASH-001
```
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
---
## 📊 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** |
> **MVP Sprint Focus:** US-001~006, US-007~008, US-012~014, US-019 — ครอบคลุม Core Happy Path ทั้งหมด
---
## 📝 Document Control
- **Version:** 1.0.0 | **Status:** DRAFT
- **Created:** 2026-03-11 | **Owner:** Nattanin Peancharoen
- **Classification:** Internal Use Only
@@ -0,0 +1,771 @@
# ✅ Acceptance Criteria — LCBP3-DMS MVP (UAT)
---
title: 'Acceptance Criteria & UAT Test Scenarios'
version: 1.8.0
status: DRAFT — Pending Stakeholder Sign-off
owner: Nattanin Peancharoen (Product Owner)
last_updated: 2026-03-11
related:
- specs/01-Requirements/01-01-objectives.md
- specs/01-Requirements/01-03-modules/
- specs/01-Requirements/01-02-business-rules/01-02-01-rbac-matrix.md
- specs/01-Requirements/01-02-business-rules/01-02-04-non-functional-rules.md
- specs/06-Decision-Records/ADR-001-unified-workflow-engine.md
- specs/06-Decision-Records/ADR-016-security-authentication.md
---
## 📖 วิธีอ่านเอกสารนี้
แต่ละ Scenario ใช้รูปแบบ **Given / When / Then** พร้อม:
- **ID**: รหัส Scenario ใช้อ้างอิง (AC-XXX-YYY)
- **Priority**: 🔴 Blocker | 🟠 Critical | 🟡 Major | 🟢 Minor
- **Role**: ผู้ใช้ที่ทดสอบ Scenario นี้
---
## 🏗️ Module 1: Authentication & Session Management
### AC-AUTH-001 — Login Success
**Priority:** 🔴 Blocker | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | User มีบัญชีในระบบ และ Password ถูกต้อง |
| **When** | กรอก Username + Password แล้วกด Login |
| **Then** | ✅ ได้รับ JWT Access Token (15 min TTL) + Refresh Token (7 days) |
| | ✅ ถูก Redirect ไปที่ Dashboard |
| | ✅ Audit Log บันทึก `LOGIN_SUCCESS` พร้อม IP Address |
### AC-AUTH-002 — Login Failed (Wrong Password)
**Priority:** 🔴 Blocker | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | User มีบัญชีในระบบ |
| **When** | กรอก Password ผิด |
| **Then** | ✅ แสดง Error "ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง" (ไม่บอกว่าอันไหนผิด) |
| | ✅ Audit Log บันทึก `LOGIN_FAILED` |
| | ✅ หลัง 5 ครั้งใน 1 นาที → Rate Limit 429 Too Many Requests |
### AC-AUTH-003 — First Login Force Password Change
**Priority:** 🔴 Blocker | **Role:** ทุก Role (new user)
| | Description |
|---|---|
| **Given** | User เพิ่งถูกสร้างโดย Admin (password_must_change = true) |
| **When** | Login สำเร็จครั้งแรก |
| **Then** | ✅ ถูก Redirect ไปยังหน้า "เปลี่ยนรหัสผ่าน" ทันที |
| | ✅ ไม่สามารถเข้าถึงหน้าอื่นได้ จนกว่าจะเปลี่ยนรหัสผ่าน |
| | ✅ Password ใหม่ต้องผ่าน Policy (ตัวใหญ่ + ตัวเล็ก + ตัวเลข + 8+ ตัวอักษร) |
### AC-AUTH-004 — Token Refresh
**Priority:** 🟠 Critical | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | Access Token หมดอายุ (15 min) แต่ Refresh Token ยังใช้งานได้ |
| **When** | Frontend ตรวจพบ 401 Response ระหว่างการใช้งาน |
| **Then** | ✅ Frontend auto-refresh token โดยผู้ใช้ไม่รู้สึก |
| | ✅ Request เดิม Retry สำเร็จหลัง Token Refresh |
### AC-AUTH-005 — Logout
**Priority:** 🟠 Critical | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | User กำลัง Login อยู่ |
| **When** | กด Logout |
| **Then** | ✅ Refresh Token ถูก Revoke ใน Database |
| | ✅ Redis Permission Cache ถูกลบ |
| | ✅ Redirect ไปหน้า Login |
| | ✅ Audit Log บันทึก `LOGOUT` |
---
## 🏢 Module 2: User & Organization Management (Admin)
### AC-ADMIN-001 — Superadmin สร้าง Organization
**Priority:** 🔴 Blocker | **Role:** Superadmin
| | Description |
|---|---|
| **Given** | Login ด้วย Superadmin Account |
| **When** | สร้าง Organization ใหม่ กรอกชื่อ + รหัสองค์กร |
| **Then** | ✅ Organization ถูกสร้างในฐานข้อมูล |
| | ✅ สามารถกำหนด Org Admin ได้ทันที |
| | ✅ Audit Log บันทึก `ORGANIZATION_CREATED` |
### AC-ADMIN-002 — Superadmin สร้าง Project + Contract
**Priority:** 🔴 Blocker | **Role:** Superadmin
| | Description |
|---|---|
| **Given** | มี Organization อย่างน้อย 1 ที่ |
| **When** | สร้าง Project → สร้าง Contract ภายใน Project |
| **Then** | ✅ Project ผูกกับ Organization ได้หลายองค์กร |
| | ✅ Contract ผูกกับ Project (1 Contract ต่อ Contractor) |
| | ✅ การตั้งค่า Document Number Format สำหรับ Project ทำได้ |
### AC-ADMIN-003 — Org Admin เพิ่ม User เข้า Organization
**Priority:** 🟠 Critical | **Role:** Org Admin
| | Description |
|---|---|
| **Given** | Login ด้วย Org Admin |
| **When** | เพิ่ม User พร้อม Role (Editor/Viewer/Document Control) |
| **Then** | ✅ User ได้รับ Email แจ้ง Credentials |
| | ✅ User Login แล้วเห็น Dashboard ขององค์กรตัวเอง |
| | ✅ Permission ถูก Cache ใน Redis (ภายใน 30 วินาที) |
### AC-ADMIN-004 — Permission Isolation ระหว่าง Contractor
**Priority:** 🔴 Blocker | **Role:** Document Control (Contractor A)
| | Description |
|---|---|
| **Given** | มี Contractor A (Contract 1) และ Contractor B (Contract 2) |
| **When** | Contractor A Login แล้วพยายาม เข้าดู Document ของ Contractor B |
| **Then** | ✅ ได้รับ 403 Forbidden |
| | ✅ Contractor A ไม่เห็น Document ของ Contractor B ในรายการ |
| | ✅ Audit Log บันทึกความพยายาม Unauthorized Access |
### AC-ADMIN-005 — Permission Cache Invalidation
**Priority:** 🟠 Critical | **Role:** Superadmin
| | Description |
|---|---|
| **Given** | User กำลัง Login อยู่และมี Permission Cache ใน Redis |
| **When** | Superadmin เปลี่ยน Role ของ User นั้น |
| **Then** | ✅ Redis Cache ของ User นั้นถูกล้างทันที |
| | ✅ Request ถัดไปของ User ใช้ Permission ใหม่ (ภายใน 1 request cycle) |
---
## 📨 Module 3: Correspondence Management
### AC-CORR-001 — สร้าง Correspondence (Draft)
**Priority:** 🔴 Blocker | **Role:** Document Control
| | Description |
|---|---|
| **Given** | Login ด้วย Document Control ขององค์กรใด |
| **When** | สร้าง Correspondence ใหม่ (Letter/RFI) กรอก Subject, To, CC, แนบไฟล์ PDF |
| **Then** | ✅ เอกสารถูกบันทึกในสถานะ `DRAFT` |
| | ✅ ผู้ใช้ขององค์กรอื่น **ไม่เห็น** Draft นี้ |
| | ✅ ผู้สร้างเห็นใน "เอกสารฉบับร่างของฉัน" |
### AC-CORR-002 — Submit Correspondence + Auto Document Number
**Priority:** 🔴 Blocker | **Role:** Document Control
| | Description |
|---|---|
| **Given** | มี Correspondence ในสถานะ Draft พร้อม Attachment |
| **When** | กด Submit |
| **Then** | ✅ ระบบออกเลขเอกสารอัตโนมัติตาม Format Template (เช่น `LCBP3-สคฉ-กทท-LETTER-0001-68`) |
| | ✅ เลขเอกสารไม่ซ้ำกัน แม้ Submit พร้อมกันหลาย Request |
| | ✅ สถานะเปลี่ยนเป็น `SUBMITTED` |
| | ✅ Workflow Instance ถูกสร้างใน `workflow_instances` |
| | ✅ องค์กรผู้รับ (To) ได้รับแจ้งเตือน (Email + In-App) |
### AC-CORR-003 — ผู้รับ Correspondence
**Priority:** 🔴 Blocker | **Role:** Document Control (Recipient Org)
| | Description |
|---|---|
| **Given** | Correspondence ถูก Submit มายังองค์กรเรา |
| **When** | Document Control ขององค์กรผู้รับ เปิดดู |
| **Then** | ✅ เห็นเอกสารใน Inbox/Received |
| | ✅ สามารถสร้าง Circulation Sheet ได้ |
| | ✅ สามารถดาวน์โหลดไฟล์แนบได้ |
### AC-CORR-004 — อ้างอิงเอกสารก่อนหน้า (References)
**Priority:** 🟡 Major | **Role:** Document Control
| | Description |
|---|---|
| **Given** | มีเอกสาร Correspondence เก่าในระบบ |
| **When** | สร้าง Correspondence ใหม่และอ้างอิงเอกสารเก่า |
| **Then** | ✅ Link เชื่อมระหว่างเอกสารทั้งสอง |
| | ✅ สามารถคลิก Navigate ไปดูเอกสารที่อ้างถึงได้ |
### AC-CORR-005 — Cancel Correspondence (Admin Only)
**Priority:** 🟡 Major | **Role:** Org Admin / Superadmin
| | Description |
|---|---|
| **Given** | Correspondence ถูก Submit แล้ว (สถานะ SUBMITTED+) |
| **When** | Admin กด Cancel พร้อมระบุเหตุผล |
| **Then** | ✅ สถานะเปลี่ยนเป็น `CANCELLED` |
| | ✅ ต้องระบุ `cancel_reason` (ไม่ยอม Empty) |
| | ✅ Audit Log บันทึกพร้อมเหตุผล |
| | ✅ User ระดับ Viewer/Editor ทำ Cancel ไม่ได้ → 403 |
---
## 📋 Module 4: RFA Management
### AC-RFA-001 — สร้าง RFA + แนบ Shop Drawing
**Priority:** 🔴 Blocker | **Role:** Document Control (Contractor)
| | Description |
|---|---|
| **Given** | Login ด้วย Document Control ของ Contractor |
| **When** | สร้าง RFA ใหม่ — กรอก RFA Type, Discipline, แนบ Shop Drawing (PDF/DWG/ZIP) |
| **Then** | ✅ RFA ถูกบันทึกในสถานะ `DRAFT` |
| | ✅ Shop Drawing ผูกกับ RFA (1 Revision = 1 RFA) |
| | ✅ ไฟล์ถูก Scan ด้วย ClamAV — ปฏิเสธไฟล์ที่ติด Virus |
### AC-RFA-002 — Submit RFA + Auto Number
**Priority:** 🔴 Blocker | **Role:** Document Control (Contractor)
| | Description |
|---|---|
| **Given** | RFA Draft พร้อม Shop Drawing |
| **When** | กด Submit |
| **Then** | ✅ เลขที่ RFA ถูกออกอัตโนมัติตาม Format (`LCBP3-RFA-STR-0001`) |
| | ✅ เลขไม่ reset ตามปี (RFA = No reset policy) |
| | ✅ สถานะ = `SUBMITTED` |
| | ✅ Workflow Routing เริ่มทำงาน (ส่งไปยัง Reviewer) |
### AC-RFA-003 — Review RFA (Approved)
**Priority:** 🔴 Blocker | **Role:** Engineer/Reviewer (Supervisor Org)
| | Description |
|---|---|
| **Given** | RFA อยู่ในสถานะรอ Review ที่องค์กรเรา |
| **When** | Engineer คลิก "Approved" |
| **Then** | ✅ สถานะ RFA เปลี่ยนเป็น `APPROVED` |
| | ✅ Originator (Contractor) ได้รับแจ้งเตือน |
| | ✅ Workflow History บันทึก Action + Timestamp + User |
### AC-RFA-004 — Review RFA (Approved with Comments)
**Priority:** 🟠 Critical | **Role:** Engineer/Reviewer
| | Description |
|---|---|
| **Given** | RFA อยู่ในสถานะรอ Review |
| **When** | Engineer คลิก "Approved with Comments" + กรอก Comment |
| **Then** | ✅ สถานะ = `APPROVED_WITH_COMMENTS` |
| | ✅ Comment ถูกบันทึกใน Workflow History |
| | ✅ Originator เห็น Comment ได้ |
### AC-RFA-005 — Review RFA (Rejected)
**Priority:** 🟠 Critical | **Role:** Engineer/Reviewer
| | Description |
|---|---|
| **Given** | RFA อยู่ในสถานะรอ Review |
| **When** | Engineer คลิก "Rejected" + ระบุเหตุผล |
| **Then** | ✅ สถานะ = `REJECTED` |
| | ✅ เหตุผลการ Reject บันทึกครบถ้วน |
| | ✅ Contractor สามารถยื่น RFA Revision ใหม่ได้ |
### AC-RFA-006 — Revision RFA
**Priority:** 🟠 Critical | **Role:** Document Control (Contractor)
| | Description |
|---|---|
| **Given** | RFA Rev.A ถูก Rejected แล้ว |
| **When** | Contractor สร้าง RFA Revision ใหม่ (Rev.B) |
| **Then** | ✅ Rev.B ผูกกับ shop_drawing revision ใหม่ |
| | ✅ เลขที่เอกสารเดิม — Revision Code เปลี่ยน (เช่น `-B`) |
| | ✅ Rev.A ยังอ่านได้ (ไม่ถูกลบ) |
---
## 📐 Module 5: Drawing Management
### AC-DRW-001 — Upload Contract Drawing
**Priority:** 🟠 Critical | **Role:** Document Control (Design Consultant)
| | Description |
|---|---|
| **Given** | Login ด้วย Document Control ที่มีสิทธิ์ Contract Drawing |
| **When** | Upload แบบคู่สัญญา (PDF) พร้อมระบุ หมวดหมู่ / Drawing Number |
| **Then** | ✅ Drawing ถูกบันทึกในระบบ |
| | ✅ สามารถอ้างอิงจาก Shop Drawing ได้ |
| | ✅ ไม่สามารถแก้ไขได้โดยไม่มีสิทธิ์ |
### AC-DRW-002 — Upload Shop Drawing + Link to Contract Drawing
**Priority:** 🟠 Critical | **Role:** Document Control (Contractor)
| | Description |
|---|---|
| **Given** | มี Contract Drawing ในระบบแล้ว |
| **When** | Upload Shop Drawing พร้อมระบุ Referenced Contract Drawing |
| **Then** | ✅ Shop Drawing ผูก Reference กับ Contract Drawing |
| | ✅ สามารถ Navigate ไปดู Contract Drawing ที่อ้างถึงได้ |
### AC-DRW-003 — Shop Drawing Revision Control
**Priority:** 🟠 Critical | **Role:** Document Control
| | Description |
|---|---|
| **Given** | Shop Drawing Rev.A มีอยู่แล้ว |
| **When** | Upload Shop Drawing Rev.B |
| **Then** | ✅ Rev.B ถือเป็น Current Revision |
| | ✅ Rev.A ยังสามารถดูได้ (ไม่ถูกลบ) |
| | ✅ 1 Shop Drawing Revision ผูกกับ RFA ได้ 1 ฉบับเท่านั้น |
---
## ⚙️ Module 6: Unified Workflow Engine
### AC-WF-001 — Workflow Instance Creation
**Priority:** 🔴 Blocker | **Role:** ระบบ (Auto)
| | Description |
|---|---|
| **Given** | Document ถูก Submit |
| **When** | ระบบสร้าง Workflow Instance |
| **Then** | ✅ `workflow_instances` record ถูกสร้าง |
| | ✅ `current_state` = Initial State ตาม DSL |
| | ✅ `entity_type` และ `entity_id` ถูกต้อง |
### AC-WF-002 — Workflow State Transition (Happy Path)
**Priority:** 🔴 Blocker | **Role:** ตาม Workflow Role
| | Description |
|---|---|
| **Given** | Workflow อยู่ใน State A |
| **When** | User ที่มีสิทธิ์ทำ Action ที่กำหนดใน DSL |
| **Then** | ✅ State เปลี่ยนเป็น State B ตาม Transition Rules |
| | ✅ `workflow_histories` บันทึก from_state, to_state, action, user_id, timestamp |
| | ✅ Event Notifications ถูก Dispatch (ถ้า DSL กำหนด) |
### AC-WF-003 — Workflow Unauthorized Transition
**Priority:** 🔴 Blocker | **Role:** User ไม่ถูก Role
| | Description |
|---|---|
| **Given** | Workflow รอ Approve โดย Role X |
| **When** | User ที่เป็น Role Y (ไม่ใช่ X) พยายาม Approve |
| **Then** | ✅ ได้รับ 403 Forbidden |
| | ✅ State ไม่เปลี่ยน |
### AC-WF-004 — Workflow Visualization (UI)
**Priority:** 🟡 Major | **Role:** ทุก Role ที่มีสิทธิ์ดูเอกสาร
| | Description |
|---|---|
| **Given** | Workflow กำลังดำเนินการอยู่ |
| **When** | เปิดดูหน้ารายละเอียดเอกสาร |
| **Then** | ✅ แสดง Workflow Diagram พร้อม State ปัจจุบัน |
| | ✅ State ที่ผ่านมาแล้วแสดง History (คลิกดูรายละเอียดได้) |
| | ✅ State ที่ยังไม่ถึง Disabled |
### AC-WF-005 — Force Proceed (Document Control Override)
**Priority:** 🟡 Major | **Role:** Document Control
| | Description |
|---|---|
| **Given** | Workflow ติดอยู่ที่ State ใด State หนึ่ง |
| **When** | Document Control กด "Force Proceed" พร้อมระบุเหตุผล |
| **Then** | ✅ Workflow ข้ามไป State ถัดไป |
| | ✅ Audit Log บันทึก `FORCE_PROCEED` + reason + user |
### AC-WF-006 — Workflow Deadline & Notification
**Priority:** 🟡 Major | **Role:** Document Control
| | Description |
|---|---|
| **Given** | Workflow ถูกกำหนด Deadline สำหรับ Organization |
| **When** | เวลาผ่าน Deadline ไปแล้ว |
| **Then** | ✅ ระบบส่ง Reminder Notification ให้ผู้รับผิดชอบ |
| | ✅ Dashboard แสดง "Overdue" indicator |
---
## 📧 Module 7: Transmittals Management
### AC-TRM-001 — สร้าง Transmittal (ส่ง RFA หลายฉบับพร้อมกัน)
**Priority:** 🟠 Critical | **Role:** Document Control
| | Description |
|---|---|
| **Given** | มี RFA Draft หลายฉบับต้องส่งให้ที่ปรึกษา |
| **When** | สร้าง Transmittal แนบ RFA หลายฉบับ |
| **Then** | ✅ Transmittal ผูกกับ RFA ทุกฉบับ |
| | ✅ Transmittal เป็นส่วนหนึ่งใน Correspondence (มีเลขเอกสาร) |
| | ✅ ผู้รับเห็น Transmittal พร้อม RFA ที่แนบ |
---
## 📄 Module 8: Circulation Sheet Management
### AC-CIRC-001 — สร้าง Circulation Sheet
**Priority:** 🟠 Critical | **Role:** Document Control (ภายในองค์กร)
| | Description |
|---|---|
| **Given** | Correspondence เข้ามาในองค์กร |
| **When** | Document Control สร้าง Circulation Sheet กำหนด Main/Action/Info Assignees |
| **Then** | ✅ Circulation ถูกสร้าง ผูกกับ Correspondence |
| | ✅ Assignees ได้รับแจ้งเตือน (In-App + Email) |
| | ✅ ผู้ใช้นอกองค์กรไม่เห็น Circulation Sheet นี้ |
### AC-CIRC-002 — กำหนด Deadline + แจ้งเตือนล่วงหน้า
**Priority:** 🟡 Major | **Role:** Document Control
| | Description |
|---|---|
| **Given** | Circulation ถูกกำหนด Deadline ให้ Main Assignee |
| **When** | เหลืออีก 2 วันก่อน Deadline |
| **Then** | ✅ ระบบส่งแจ้งเตือน Reminder |
| | ✅ Dashboard แสดง "ใกล้ Deadline" badge |
### AC-CIRC-003 — ปิด Circulation
**Priority:** 🟠 Critical | **Role:** Document Control
| | Description |
|---|---|
| **Given** | ดำเนินการตอบกลับ Originator แล้ว |
| **When** | กด "ปิด Circulation" |
| **Then** | ✅ Circulation สถานะ = `CLOSED` |
| | ✅ Audit Log บันทึก closed_by + timestamp |
---
## 🔢 Module 9: Document Numbering System
### AC-DN-001 — Auto Number Generation (Concurrent Safe)
**Priority:** 🔴 Blocker | **Role:** ระบบ (Auto)
| | Description |
|---|---|
| **Given** | User 2 คนกด Submit พร้อมกัน ใน Project/Type เดียวกัน |
| **When** | ทั้งสองส่ง Request พร้อมกัน |
| **Then** | ✅ เลขเอกสารไม่ซ้ำกัน (Redis Redlock + DB Optimistic Lock) |
| | ✅ Response Time < 100ms สำหรับ Generation |
| | ✅ ทั้งสอง Request สำเร็จ — ไม่มี Error |
> **Test Method:** Load Test ด้วย 50 concurrent requests ไปที่ `POST /document-numbering/reserve` — ตรวจสอบว่าเลขไม่ซ้ำ
### AC-DN-002 — Cancelled Number ไม่ถูกนำกลับมาใช้
**Priority:** 🔴 Blocker | **Role:** ระบบ
| | Description |
|---|---|
| **Given** | เลขที่ 0012 ถูก Reserved แต่ Document ถูก Cancel ก่อน Confirm |
| **When** | มีการ Submit Document ถัดไป |
| **Then** | ✅ เลขที่ถัดไปจะเป็น 0013 (ข้าม 0012) |
| | ✅ เลข 0012 อยู่ใน `document_number_reservations` สถานะ `CANCELLED` |
### AC-DN-003 — Number Format Template
**Priority:** 🟡 Major | **Role:** Superadmin
| | Description |
|---|---|
| **Given** | กำหนด Format Template: `{PROJECT}-{ORIGINATOR}-{RECIPIENT}-{CORR_TYPE}-{SEQ:4}-{YY}` |
| **When** | Submit Document ครั้งแรกของปีสำหรับ Pair นั้น |
| **Then** | ✅ เลขออกมาถูก Format (เช่น `LCBP3-สค.-กทท.-LETTER-0001-68`) |
| | ✅ Preview ตัวอย่างเลขได้ก่อน Save Template |
### AC-DN-004 — Yearly Reset
**Priority:** 🟡 Major | **Role:** ระบบ (Cron)
| | Description |
|---|---|
| **Given** | Correspondence ใช้ Yearly Reset Policy |
| **When** | ปีใหม่เปลี่ยน (Cron Job ทำงาน) |
| **Then** | ✅ Counter เริ่มนับใหม่จาก 0001 |
| | ✅ เลขของปีก่อนยังคงอยู่ในระบบ (ไม่ถูกลบ) |
---
## 🔍 Module 10: Search & Discovery
### AC-SRCH-001 — Full-text Search
**Priority:** 🟠 Critical | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | มีเอกสารในระบบ (Indexed ใน Elasticsearch) |
| **When** | พิมพ์ค้นหา Keyword ในช่อง Search |
| **Then** | ✅ ผล Search ปรากฏภายใน 500ms |
| | ✅ ค้นหาได้จาก: เลขเอกสาร, Subject, Tag |
| | ✅ แสดงเฉพาะเอกสารที่ User มีสิทธิ์เห็น |
### AC-SRCH-002 — Advanced Filter
**Priority:** 🟡 Major | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | อยู่ในหน้า Correspondence List |
| **When** | Filter ด้วย Document Type + Date Range + Status |
| **Then** | ✅ รายการ Filter ถูกต้อง |
| | ✅ URL อัปเดตให้ Shareable (Query Params) |
---
## 📧 Module 11: Notifications
### AC-NOTIF-001 — Email Notification (Document Submit)
**Priority:** 🟠 Critical | **Role:** ระบบ (Auto)
| | Description |
|---|---|
| **Given** | Document ถูก Submit มายังองค์กร |
| **When** | Workflow Transition เกิดขึ้น |
| **Then** | ✅ Email ถูกส่งไปยัง Recipient ภายใน 5 นาที |
| | ✅ Email มี: เลขเอกสาร, Subject, ลิงก์ไปยังเอกสาร |
| | ✅ BullMQ Job อยู่ใน Queue (สามารถ Monitor ได้) |
### AC-NOTIF-002 — In-App Notification
**Priority:** 🟡 Major | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | มี Action เกิดขึ้นที่เกี่ยวข้องกับ User |
| **When** | User เปิดแอป |
| **Then** | ✅ Bell Icon แสดง Unread Count |
| | ✅ คลิกดูรายการ Notifications ได้ |
| | ✅ คลิกที่ Notification นำทางไปเอกสารที่เกี่ยวข้อง |
### AC-NOTIF-003 — Notification Retry on Failure
**Priority:** 🟡 Major | **Role:** ระบบ
| | Description |
|---|---|
| **Given** | Email Server ไม่สามารถส่งได้ชั่วคราว |
| **When** | BullMQ Job ส่งไม่สำเร็จ |
| **Then** | ✅ Retry ด้วย Exponential Backoff (3 ครั้ง) |
| | ✅ เมื่อ Retry ครบแล้วยังล้มเหลว → Dead Letter Queue |
| | ✅ In-App Notification ยังส่งได้ (Fallback) |
---
## 📎 Module 12: File Storage & Security
### AC-STOR-001 — File Upload (Two-Phase Storage)
**Priority:** 🔴 Blocker | **Role:** Document Control
| | Description |
|---|---|
| **Given** | User อัปโหลดไฟล์ PDF ขนาด 50MB |
| **When** | เลือกไฟล์ + อัปโหลด |
| **Then** | ✅ ไฟล์ถูก Upload ไปยัง Temp Storage ก่อน |
| | ✅ ClamAV Scan ในเวลา < 30 วินาที |
| | ✅ หากผ่าน Scan → ย้ายไปยัง Permanent Storage เมื่อ Document Confirmed |
| | ✅ หากไม่ผ่าน Scan → ปฏิเสธพร้อมแสดง Security Warning |
### AC-STOR-002 — File Type Restriction
**Priority:** 🔴 Blocker | **Role:** Document Control
| | Description |
|---|---|
| **Given** | User พยายาม Upload ไฟล์ .exe หรือ .js |
| **When** | เลือกไฟล์นอก Whitelist |
| **Then** | ✅ Frontend Block ก่อน Upload |
| | ✅ Backend ยืนยันซ้ำ (Defense in Depth) — 400 Bad Request |
### AC-STOR-003 — Secure PDF Viewer (No Download)
**Priority:** 🟡 Major | **Role:** Viewer
| | Description |
|---|---|
| **Given** | User มีสิทธิ์ดูเอกสารแต่ไม่มีสิทธิ์ Download |
| **When** | คลิกดูเอกสาร PDF |
| **Then** | ✅ เปิดใน In-App PDF Viewer (ไม่ต้อง Download) |
| | ✅ ปุ่ม Download ถูก Disable |
| | ✅ Range Requests (Streaming) ใช้งานได้ |
---
## 📊 Module 13: Dashboard & Audit Log
### AC-DASH-001 — Dashboard KPI Cards
**Priority:** 🟡 Major | **Role:** ทุก Role
| | Description |
|---|---|
| **Given** | Login เสร็จ |
| **When** | เข้าหน้า Dashboard |
| **Then** | ✅ KPI Cards แสดง: จำนวน Correspondence, RFA Pending, งานของฉัน, Overdue |
| | ✅ "My Tasks" แสดง Circulation ที่ตัวเองต้องทำ |
| | ✅ ข้อมูลถูกต้องตามสิทธิ์ของตัวเอง |
### AC-AUDIT-001 — Audit Log Coverage
**Priority:** 🔴 Blocker | **Role:** Superadmin
| | Description |
|---|---|
| **Given** | ดำเนินการ Action ต่างๆ ในระบบ (Create, Update, Submit, Approve) |
| **When** | ตรวจสอบ Audit Log |
| **Then** | ✅ ทุก Action มีบันทึกใน `audit_logs` |
| | ✅ บันทึกมี: user_id, action, entity_type, entity_id, ip_address, timestamp |
| | ✅ ไม่สามารถแก้ไขหรือลบ Audit Log ได้ (Read-only) |
---
## 🔐 Section 14: Security Acceptance Criteria
### AC-SEC-001 — SQL Injection Prevention
**Priority:** 🔴 Blocker | **Role:** QA (Security Test)
| | Description |
|---|---|
| **Given** | ช่อง Input ใดๆ ในระบบ |
| **When** | ใส่ SQL Injection payload เช่น `'; DROP TABLE users; --` |
| **Then** | ✅ คำขอถูกปฏิเสธหรือ Sanitized โดยไม่ทำงาน |
| | ✅ Database ปลอดภัย ไม่เกิด Error จาก Injection |
### AC-SEC-002 — XSS Prevention
**Priority:** 🔴 Blocker | **Role:** QA (Security Test)
| | Description |
|---|---|
| **Given** | ช่อง Input เช่น Subject, Comment |
| **When** | ใส่ `<script>alert('XSS')</script>` |
| **Then** | ✅ Script ไม่ถูก Execute ใน Browser |
| | ✅ แสดงเป็น Plaintext หรือถูก Escaped |
### AC-SEC-003 — Authorization Boundary (IDOR Protection)
**Priority:** 🔴 Blocker | **Role:** QA (Security Test)
| | Description |
|---|---|
| **Given** | User A รู้ Document ID ของ User B |
| **When** | User A เรียก `GET /correspondences/:id` ของ User B โดยตรง |
| **Then** | ✅ ได้รับ 403 Forbidden (ไม่ใช่ 404) |
| | ✅ ข้อมูลของ User B ไม่ถูกเปิดเผย |
### AC-SEC-004 — Rate Limiting on Auth Endpoint
**Priority:** 🔴 Blocker | **Role:** QA (Security Test)
| | Description |
|---|---|
| **Given** | ไม่มี Session อยู่ |
| **When** | เรียก `POST /auth/login` เกิน 5 ครั้งใน 1 นาที จาก IP เดียว |
| **Then** | ✅ ครั้งที่ 6+ ได้รับ 429 Too Many Requests |
| | ✅ Audit Log บันทึก Rate Limit Event |
### AC-SEC-005 — Security Headers
**Priority:** 🟠 Critical | **Role:** QA
| | Description |
|---|---|
| **Given** | ระบบทำงานบน HTTPS |
| **When** | ตรวจสอบ HTTP Response Headers |
| **Then** | ✅ `X-Content-Type-Options: nosniff` |
| | ✅ `X-Frame-Options: DENY` |
| | ✅ `Strict-Transport-Security` present |
| | ✅ `Content-Security-Policy` defined |
---
## ⚡ Section 15: Performance Acceptance Criteria
### AC-PERF-001 — API Response Time
**Priority:** 🟠 Critical
| | Description |
|---|---|
| **Test Condition** | 50 concurrent users, Normal workload |
| **Then** | ✅ P90 Response Time < 200ms สำหรับ CRUD operations |
| | ✅ P90 Search Query < 500ms |
| | ✅ File Upload 50MB < 30 seconds |
> **Test Tool:** k6 หรือ Apache JMeter
> **Test Script:** `specs/05-Engineering-Guidelines/performance-test-script.js` (TODO: สร้าง)
### AC-PERF-002 — Concurrent Users
**Priority:** 🟠 Critical
| | Description |
|---|---|
| **Test Condition** | 100 concurrent active users |
| **Then** | ✅ ระบบไม่ crash หรือ Error Rate > 1% |
| | ✅ Memory ไม่เกิน 80% ของ Container Limit |
| | ✅ CPU ไม่เกิน 80% sustained |
### AC-PERF-003 — Document Number Concurrent
**Priority:** 🔴 Blocker
| | Description |
|---|---|
| **Test Condition** | 50 concurrent POST `/document-numbering/reserve` สำหรับ Project/Type เดียวกัน |
| **Then** | ✅ เลขเอกสารไม่ซ้ำกันทั้ง 50 Request |
| | ✅ ทุก Request สำเร็จ (ไม่มี 5xx Error) |
---
## 💾 Section 16: Data Integrity & Recovery
### AC-DATA-001 — Backup & Restore
**Priority:** 🔴 Blocker | **Role:** DevOps
| | Description |
|---|---|
| **Given** | Production Database มีข้อมูล 1 วันทำการ |
| **When** | ทดสอบ Restore จาก Backup ล่าสุด |
| **Then** | ✅ Restore สำเร็จภายใน RTO < 4 ชั่วโมง |
| | ✅ ข้อมูลสมบูรณ์ ไม่มี Data Loss เกิน RPO < 1 ชั่วโมง |
| | ✅ ระบบทำงานปกติหลัง Restore |
### AC-DATA-002 — Orphan File Prevention
**Priority:** 🟠 Critical | **Role:** ระบบ
| | Description |
|---|---|
| **Given** | User อัปโหลดไฟล์ไปยัง Temp แล้ว Cancel Document ก่อน Confirm |
| **When** | Cleanup Job ทำงาน |
| **Then** | ✅ ไฟล์ใน Temp Storage ถูกลบ |
| | ✅ Permanent Storage ไม่มี Orphan Files |
---
## ✅ UAT Sign-off Checklist
### Pre-UAT Conditions
- [ ] ระบบ Deploy บน Staging Environment (ใช้ Docker Compose เหมือน Production)
- [ ] Seed Data ตาม `lcbp3-v1.8.0-seed-basic.sql` และ `seed-permissions.sql`
- [ ] Test Users ทุก Role ถูกสร้าง (Superadmin, Org Admin, Document Control, Editor, Viewer)
- [ ] Email + LINE Notify Test Mode เปิดใช้งาน
### Go-Live Criteria (ต้องผ่านทั้งหมด)
| # | Criteria | Status |
|---|----------|--------|
| 1 | AC-AUTH-001 ~ AC-AUTH-005 ผ่านทั้งหมด | ⬜ |
| 2 | AC-ADMIN-001 ~ AC-ADMIN-005 ผ่านทั้งหมด | ⬜ |
| 3 | AC-CORR-001 ~ AC-CORR-002 (Happy Path) ผ่าน | ⬜ |
| 4 | AC-RFA-001 ~ AC-RFA-003 (Submit + Approve) ผ่าน | ⬜ |
| 5 | AC-WF-001 ~ AC-WF-003 (Workflow Engine Core) ผ่าน | ⬜ |
| 6 | AC-DN-001 (Concurrent Number) ผ่าน | ⬜ |
| 7 | AC-STOR-001 (Two-Phase Storage + ClamAV) ผ่าน | ⬜ |
| 8 | AC-SEC-001 ~ AC-SEC-004 (Security) ผ่านทั้งหมด | ⬜ |
| 9 | AC-PERF-001 (Response Time) ผ่าน | ⬜ |
| 10 | AC-DATA-001 (Backup & Restore DR Test) ผ่าน | ⬜ |
| 11 | AC-AUDIT-001 (Audit Log Coverage) ผ่าน | ⬜ |
| 12 | ไม่มี Bug Priority P0/P1 ค้างอยู่ | ⬜ |
### UAT Participant Sign-off
| Organization | Representative | Signature | Date |
|-------------|----------------|-----------|------|
| กทท. (Owner) | | | |
| สค. (Admin) | | | |
| TEAM (Design Consultant) | | | |
| คคง. (Supervisor) | | | |
| ผรม. (Contractor Rep.) | | | |
| NAP (Developer) | Nattanin P. | | |
---
## 📝 Document Control
- **Version:** 1.0.0
- **Status:** DRAFT — Pending Stakeholder Sign-off
- **Created:** 2026-03-11
- **Owner:** Nattanin Peancharoen (Product Owner / System Architect)
- **Next Review:** Prior to UAT Start
- **Classification:** Internal Use Only
---
> [!NOTE]
> เอกสารนี้ต้องได้รับการ Sign-off จาก Stakeholder ทุกฝ่ายก่อนเริ่ม UAT
> ดู Gap 5 (Stakeholder Sign-off) ใน `po-analysis.md` สำหรับ Process การอนุมัติ
@@ -0,0 +1,642 @@
# 🛡️ 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
---
> [!IMPORTANT]
> เอกสารนี้ระบุ **Edge Cases ที่ต้อง Implement และ Test อย่างชัดเจน** เพื่อป้องกัน Bug ในระบบ Prod
> ทุก Edge Case มี **Expected Behavior** ที่ Developer และ QA ต้องยึดถือ
---
## 📐 วิธีอ่าน
- **EC-[MODULE]-[NNN]** = Edge Case ID
- **Severity:** 🔴 Critical | 🟠 High | 🟡 Medium
- **Type:** `Data Integrity` | `Security` | `Concurrency` | `UX` | `Business Rule`
---
## 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)
3. DB SELECT FOR UPDATE อีกชั้น (Defense in Depth)
4. Increment counter → COMMIT → Release Lock
5. ห้ามใช้ AUTO_INCREMENT ของ DB โดยตรงสำหรับเลขเอกสาร
```
**Test Method:** Load Test 50 concurrent POST `/document-numbering/reserve` → Assert DISTINCT count = 50
---
### 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'
- ไม่ Delete counter เก่า — แค่ Key ใหม่
```
---
### 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 เด็ดขาด
**Business Rule:** "เลขที่ออกแล้วต้องไปข้างหน้าเท่านั้น — ห้ามถอยหลัง"
---
### 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
SET status = 'EXPIRED'
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 ที่สองจะมาเร็วแค่ไหน
**Implementation Rule:** Cache Idempotency-Key ใน Redis (TTL 24h) → ถ้า Key เจอ → Return Cached Response
---
## 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
- ถ้าไม่ใช่ → บันทึก partial approval เท่านั้น
```
---
### 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
---
### 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
```
---
### 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)
- Document Control สามารถ Force Proceed ได้ (กรณีฉุกเฉิน)
---
## 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)
- ถ้า Retry → อัปโหลดใหม่ทั้งหมด (ไม่มี Resume Upload ใน MVP)
---
### 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
- Security Metric Counter ใน Dashboard เพิ่มขึ้น
- ไม่ดำเนินการ Submit Document ต่อ (ไม่ว่าไฟล์อื่นจะผ่านแล้ว)
---
### 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
ZIP: application/zip, application/x-zip-compressed
DOCX: application/vnd.openxmlformats-officedocument.wordprocessingml.document
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
```
---
### 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
---
## 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 แล้ว
**Exception:** ถ้า RFA ก่อนหน้าถูก REJECTED หรือ CANCELLED → สร้างใหม่ได้
---
### 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 พร้อมกัน
---
### 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)
- Reviewer ที่ Reject สามารถระบุเหตุผล "Wrong Discipline" ได้
---
### 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
---
## 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)
**Implementation:** Frontend Singleton Refresh Promise ใน Axios Interceptor
---
### 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 ใหม่มีผล
- Maximum Lag: 15 นาที (= Access Token TTL)
---
### 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}`);
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
- ทุก API ต้องผ่าน Permission Check โดยไม่มีข้อยกเว้น
---
### 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
---
### 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
---
## 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
- Audit Log บันทึกทั้งหมด (CANCELLED + FORCE_CLOSED + reason + user)
---
### 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)
---
### 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)
---
## 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 อื่นได้
- Audit Log บันทึก Re-assign Event
---
### 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
---
### 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)
---
## 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 530 วินาที (BullMQ Async Job)
- UI แสดง "เอกสารอาจใช้เวลาสักครู่ก่อนปรากฏในผลค้นหา"
- **ไม่ถือว่า Bug** — เป็น By Design (Eventual Consistency)
- User สามารถ Navigate ไปยังเอกสารได้ทันทีผ่าน Notification Link (ไม่ต้องรอ Search)
---
### 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 ในผลค้นหา
- **ห้าม Filter ที่ Application Layer เท่านั้น** → ต้อง Filter ที่ Query Level
---
### 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 สูง
---
## 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)
---
### 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
---
## 📊 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** |
---
## 🧪 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 results = await Promise.all(promises);
const numbers = results.map(r => r.documentNumber);
const unique = new Set(numbers);
expect(unique.size).toBe(50); // ไม่มีซ้ำ
});
});
```
### สำหรับ 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
---
## 📝 Document Control
- **Version:** 1.0.0 | **Status:** DRAFT
- **Created:** 2026-03-11 | **Owner:** Nattanin Peancharoen
- **Next Review:** Pre-UAT (T-2 สัปดาห์ก่อน Go-Live)
- **Classification:** Internal Use Only — Developer & QA Reference
@@ -0,0 +1,627 @@
# 🖼️ 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
---
> [!NOTE]
> Wireframes ในเอกสารนี้เป็น **Low-fidelity ASCII/Text Wireframes** เพื่อสื่อสาร Layout และ Component Hierarchy
> สำหรับ High-fidelity Design ให้ใช้ Figma หรือ Shadcn/UI Components ตาม ADR-012
---
## 1. 🗺️ Navigation Map (Site Map)
```
[🔓 Public]
└── /login → หน้า Login (Anonymous)
└── /login/forgot-password → ลืมรหัสผ่าน
└── /login/change-password → เปลี่ยนรหัสผ่านครั้งแรก (Force)
[🔒 Authenticated — App Shell (Sidebar + Navbar)]
├── /dashboard → หน้าหลัก (My Tasks + KPI)
├── /correspondences → รายการ Correspondence
│ ├── /correspondences/new → สร้างใหม่
│ └── /correspondences/:id → รายละเอียด + Workflow
├── /rfas → รายการ RFA
│ ├── /rfas/new → สร้างใหม่
│ └── /rfas/:id → รายละเอียด + Workflow
├── /transmittals → รายการ Transmittal
│ ├── /transmittals/new → สร้างใหม่ (รวม RFAs)
│ └── /transmittals/:id → รายละเอียด
├── /drawings → Drawing Management
│ ├── /drawings/contract → Contract Drawings
│ │ ├── /drawings/contract/new → Upload ใหม่
│ │ └── /drawings/contract/:id → รายละเอียด
│ └── /drawings/shop → Shop Drawings
│ ├── /drawings/shop/new → Upload ใหม่
│ └── /drawings/shop/:id → รายละเอียด + RFA History
├── /circulations → Circulation Sheets (Internal)
│ ├── /circulations/new → สร้างใหม่
│ └── /circulations/:id → รายละเอียด + Assignees
├── /search → Full-text Search
├── /notifications → รายการ Notifications
├── /profile → ข้อมูลส่วนตัว + ตั้งค่า
[🔒 Admin Routes]
├── /admin/users → จัดการ Users (Org Admin+)
│ ├── /admin/users/new → เพิ่ม User
│ └── /admin/users/:id/edit → แก้ไข User + Role
├── /admin/organizations → จัดการ Orgs (Superadmin)
│ └── /admin/organizations/new
├── /admin/projects → จัดการ Projects (Superadmin)
│ └── /admin/projects/:id/contracts
├── /admin/doc-numbering → Document Number Config (Superadmin)
└── /admin/audit-logs → Audit Log Viewer (Superadmin + OrgAdmin)
```
---
## 2. 🧩 App Shell Layout
```
┌─────────────────────────────────────────────────────────────────┐
│ NAVBAR │
│ [🏗️ LCBP3-DMS] [Project: LCBP3 ▼] [🔔 3] [👤 สมชาย ▼] │
└─────────────────────────────────────────────────────────────────┘
┌──────────────┬──────────────────────────────────────────────────┐
│ SIDEBAR │ MAIN CONTENT AREA │
│ │ │
│ 📊 Dashboard │ │
│ 📨 Corres. │ [Page Content Here] │
│ 📋 RFA │ │
│ 📦 Transmit │ │
│ 📐 Drawings │ │
│ 📄 Circulat. │ │
│ 🔍 Search │ │
│ │ │
│ ─────────── │ │
│ [Admin ▼] │ │
│ 👥 Users │ │
│ 🏢 Orgs │ │
│ ⚙️ Config │ │
└──────────────┴──────────────────────────────────────────────────┘
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 |
**รวม:** 26 หน้า (9 Must / 13 Should / 1 Could)
---
## 4. 🖼️ Wireframes — Key Screens
### SCR-001: Login Page
```
┌─────────────────────────────────────────────────────────┐
│ │
│ 🏗️ LCBP3-DMS │
│ ท่าเรือแหลมฉบัง เฟส 3 │
│ Document Management System │
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ │ │
│ │ ชื่อผู้ใช้ (Username) │ │
│ │ [________________________________] │ │
│ │ │ │
│ │ รหัสผ่าน (Password) │ │
│ │ [________________________________] [👁️] │ │
│ │ │ │
│ │ [ เข้าสู่ระบบ (Login) ] ← Primary │ │
│ │ │ │
│ │ [ลืมรหัสผ่าน?] │ │
│ │ │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ ❌ Error Banner (แสดงเมื่อ Login ผิด): │
│ [⚠️ ชื่อผู้ใช้หรือรหัสผ่านไม่ถูกต้อง] │
│ │
│ v1.8.0 | Internal Use Only │
└─────────────────────────────────────────────────────────┘
Component Rules:
- Error Toast: แสดงทับ Form (ไม่บอกว่าอันไหนผิด — Security)
- Rate Limit: หลัง 5 ครั้ง → ปุ่ม Disabled 60 วินาที + Countdown
- Password: Toggle Show/Hide icon
- Auto-focus: Username field on load
- Enter key: Submit form
```
---
### SCR-003: Dashboard
```
┌─ Dashboard ─────────────────────────────────────────────────────────┐
│ │
│ 📊 ภาพรวม — [สมชาย จาก สค.] วันนี้ 11 มี.ค. 2569 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 📨 Corres. │ │ 📋 RFA │ │ ⏰ Overdue │ │ 🔒 Security │ │
│ │ │ │ │ │ │ │ │ │
│ │ Pending │ │ Pending │ │ Tasks │ │ Files │ │
│ │ [12] │ │ [5] │ │ [3] │ │ Scanned │ │
│ │ │ │ │ │ │ │ [847] │ │
│ │ ← ↑3 จากเมื่อ │ │ ← ↓1 จากเมื่อ │ │ ⚠️ เกินกำหนด │ │ 0 Threats │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │
│ ═══ งานของฉัน (My Tasks) ════════════════════════════════════════ │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ [Filter: ทั้งหมด ▼] [Status: Active ▼] 🔍 [ค้นหา...] │ │
│ ├────────┬──────────────────┬──────────┬──────────┬────────────┤ │
│ │ ประเภท │ เลขที่/Subject │ หน้าที่ │ กำหนด │ การดำเนินการ│ │
│ ├────────┼──────────────────┼──────────┼──────────┼────────────┤ │
│ │ 📄 RFA │ LCBP3-RFA-STR.. │ Approve │⚠️12 มี.ค.│ [ดำเนินการ]│ │
│ │ 📨 Cir │ สค.-กทท.-001... │ Review │ 15 มี.ค. │ [ดำเนินการ]│ │
│ │ 📨 Cir │ สค.-กทท.-002... │ Info │ 20 มี.ค. │ [ดำเนินการ]│ │
│ ├────────┴──────────────────┴──────────┴──────────┴────────────┤ │
│ │ แสดง 3 จาก 12 รายการ [ดูทั้งหมด →] │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
Mobile (Card View):
┌───────────────────────┐
│ 📄 RFA │
│ LCBP3-RFA-STR-0042 │
│ หน้าที่: Approve │
│ ⚠️ กำหนด: 12 มี.ค. │
│ [ดำเนินการ →] │
└───────────────────────┘
```
---
### SCR-004: Correspondence List
```
┌─ Correspondence ────────────────────────────────────────────────────┐
│ │
│ [+ สร้างใหม่] [📥 รับเข้า (12)] [📤 ส่งออก (8)] [ทั้งหมด (20)] │
│ │
│ [ Filter: ประเภท ▼ ] [ Filter: สถานะ ▼ ] [ Filter: วันที่ ▼ ] │
│ 🔍 [ค้นหาเอกสาร... ] [ล้าง Filter] │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │☐ │ เลขที่เอกสาร │ Subject │ ผู้ส่ง │วันที่│สถานะ │ │
│ ├──┼─────────────────────┼────────────┼────────┼──────┼───────┤ │
│ │☐ │ LCBP3-สค-กทท-L-001 │ ขอแบบงาน. │ สค. │11มีค│✅ ปิด │ │
│ │☐ │ LCBP3-กทท-สค-L-002 │ ตอบรับ... │ กทท. │10มีค│🔄 ดำเนิน│ │
│ │☐⚠️│ LCBP3-สค-กทท-L-003 │ ขอข้อมูล.. │ สค. │08มีค│⏰ เกิน │ │
│ │ │ ... │ │ │ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ [< 1 2 3 >] แสดง 15/47 รายการ │
│ │
└──────────────────────────────────────────────────────────────────────┘
Status Badges:
🟡 Draft | 🔵 Submitted | 🔄 In Review | ✅ Closed | ❌ Cancelled | ⏰ Overdue
```
---
### SCR-005: Create Correspondence (Form)
```
┌─ สร้าง Correspondence ─────────────────────────────────────────────┐
│ │
│ Step 1: ข้อมูลทั่วไป ●─────── Step 2: แนบไฟล์ ○─── Step 3: ตรวจสอบ ○│
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ ประเภทเอกสาร* │ จาก (Originator)* │ │
│ │ [Letter ▼] │ [สค. (กำหนดอัตโนมัติ)] │ │
│ │ │ │ │
│ │ ถึง (To)* │ สำเนา (CC) │ │
│ │ [เลือกองค์กร ▼][+ เพิ่ม]│ [เลือกองค์กร ▼][+ เพิ่ม] │ │
│ │ Tag: กทท. × │ │ │
│ │ │ │ │
│ │ Subject* │ │
│ │ [_____________________________________________] │ │
│ │ ○ ตัวอักษรที่เหลือ: 200 │ │
│ │ │ │
│ │ เอกสารอ้างอิง (References) │ │
│ │ [🔍 ค้นหาเลขที่เอกสาร... ] [+ เพิ่ม] │ │
│ │ หมวดหมู่ (Tags) │ │
│ │ [🔍 ค้นหา Tag...] [construction] × [rfa] × │ │
│ │ │ │
│ │ หมายเหตุ (Remark) │ │
│ │ [_____________________________________________] │ │
│ │ [_____________________________________________] │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ ⚠️ Auto-save: บันทึกล่าสุด 13:28:45 │
│ │
│ [← ยกเลิก] [บันทึก Draft] [ต่อไป: แนบไฟล์ →] │
└─────────────────────────────────────────────────────────────────────┘
```
---
### SCR-005b: File Attachment Step
```
┌─ สร้าง Correspondence — แนบไฟล์ ───────────────────────────────────┐
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ 📎 ลากและวางไฟล์ที่นี่ หรือ [เลือกไฟล์] │ │
│ │ │ │
│ │ รองรับ: PDF, DWG, ZIP, DOCX, XLSX (สูงสุด 100MB/ไฟล์) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ไฟล์ที่แนบ: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ☑️ เอกสารหลัก │ 📄 drawing-v2.pdf │ 2.3MB │ ✅ Scan OK│[🗑️]│
│ │ ☐ เอกสารหลัก │ 📐 detail.dwg │ 1.1MB │ 🔄 Scanning│[🗑️]│
│ │ ☐ เอกสารหลัก │ 📦 supporting-docs.zip │ 5.8MB │ ✅ Scan OK│[🗑️]│
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ℹ️ ☑️ = เอกสารหลัก (Main Document) — เลือกได้ 1 ไฟล์ │
│ │
│ ❌ Error File: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 🚨 malware-test.pdf │ ❌ VIRUS DETECTED — ไฟล์ถูกปฏิเสธ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [← ย้อนกลับ] [บันทึก Draft] [ต่อไป: ตรวจสอบ →] │
└─────────────────────────────────────────────────────────────────────┘
```
---
### SCR-006: Correspondence Detail + Workflow
```
┌─ LCBP3-สค.-กทท.-LETTER-0001-68 ────────────────────────────────────┐
│ Subject: ขอข้อมูลแบบก่อสร้างเพิ่มเติม │
│ ส่งโดย: สค. → ถึง: กทท. วันที่: 11 มี.ค. 2569 │
│ Status: 🔄 IN REVIEW │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ TAB: [ข้อมูล] [📎 เอกสารแนบ (3)] [🔄 Workflow] [📋 Circulation]│ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ ═══ Workflow Diagram ════════════════════════════════════════════ │
│ │
│ ✅ Submit ──→ 🔄 Review ──→ ○ ปิด │
│ (สค.) (กทท.) (สค.) │
│ 11 มี.ค. กำลังดำเนินการ รอ │
│ [คลิกดู Log] [คลิกดูรายละเอียด] │
│ │
│ ═══ Action Panel (แสดงเฉพาะ Step ที่ Active) ══════════════════ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ 📋 งานของคุณ: Review เอกสารนี้และตอบกลับ │ │
│ │ │ │
│ │ Comment / หมายเหตุ: │ │
│ │ [___________________________________________________] │ │
│ │ [___________________________________________________] │ │
│ │ │ │
│ │ [❌ ปฏิเสธ] [✅ รับทราบ] [📩 ตอบกลับ] │ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
│ Admin Only: [⚡ Force Proceed →] [↩️ Revert ←] │
└──────────────────────────────────────────────────────────────────────┘
Workflow Step Popup (คลิก Step ที่ผ่านแล้ว):
┌─────────────────────────────────┐
│ ✅ Submit — 11 มี.ค. 2569 │
│ โดย: สมชาย ก. (สค.) │
│ เวลา: 09:32 น. │
│ IP: 192.168.1.10 │
│ Comment: - │
└─────────────────────────────────┘
```
---
### SCR-008: Create RFA (Form)
```
┌─ สร้าง RFA ────────────────────────────────────────────────────────┐
│ │
│ Step 1: ข้อมูล RFA ●── Step 2: Shop Drawing ○── Step 3: Transmittal ○│
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ประเภท RFA* │ สาขา (Discipline)* │ │
│ │ [Shop Drawing ▼] │ [Structural (STR) ▼] │ │
│ │ │ │
│ │ ⚠️ เลขที่ RFA (Auto-generated) │ │
│ │ Preview: LCBP3-RFA-STR-0043 (จะออกเมื่อ Submit) │ │
│ │ │ │
│ │ Contract Drawing ที่อ้างอิง │ │
│ │ [🔍 ค้นหาแบบคู่สัญญา... ] [+ เลือก] │ │
│ │ → CD-STR-001: Foundation Plan × │ │
│ │ │ │
│ │ หมายเหตุ (Remark) │ │
│ │ [____________________________________________] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [← ยกเลิก] [บันทึก Draft] [ต่อไป: แนบแบบ →] │
└─────────────────────────────────────────────────────────────────────┘
```
---
### SCR-009: RFA Detail + Workflow
```
┌─ LCBP3-RFA-STR-0042 ───────────────────────────────────────────────┐
│ ประเภท: Shop Drawing | สาขา: Structural | Revision: A │
│ ยื่นโดย: ผรม.1 → ผ่าน Transmittal: LCBP3-TRM-0015 │
│ Status: 🔄 UNDER REVIEW (คคง.) │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ TAB: [ข้อมูล] [📎 Shop Drawing] [🔄 Workflow] [📝 Revision] │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ═══ Shop Drawing Viewer ════════════════════════════════════════ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ [ 📄 PDF Viewer — Streaming ] │ │
│ │ │ │
│ │ ⬅️ หน้า 2/15 ➡️ 🔍 80% [⬇️ ดาวน์โหลด] ← (ถ้ามี)│ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ ═══ Action Panel (เฉพาะ Reviewer) ════════════════════════════ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ ผลการพิจารณา: │ │
│ │ ○ Approved ● Approved with Comments ○ Rejected │ │
│ │ │ │
│ │ Comment (บังคับเมื่อ AW/C หรือ Rejected): │ │
│ │ [พบข้อผิดพลาดในรายละเอียด Connection Plate ...] │ │
│ │ │ │
│ │ [ยกเลิก] [✅ ยืนยันผลการพิจารณา]│ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Workflow Diagram สำหรับ RFA:
✅ Draft → ✅ Submitted → 🔄 TEAM Review → 🔄 คคง. Review → ○ APPROVED
(parallel) (sequential)
```
---
### SCR-017: Create Circulation Sheet
```
┌─ สร้างใบเวียน (Circulation Sheet) ─────────────────────────────────┐
│ อ้างอิง: LCBP3-กทท-สค-LETTER-0012 — ขอข้อมูลงวดงานที่ 3 │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ผู้รับผิดชอบหลัก (Main — ต้องดำเนินการ)* │ │
│ │ [🔍 ค้นหาชื่อผู้ใช้... ] [+ เพิ่ม] │ │
│ │ → [👤 สมชาย ก. (หัวหน้า)] × [📅 กำหนด: 15 มี.ค.] │ │
│ │ │ │
│ │ ผู้ดำเนินการ (Action — ร่วมดำเนินการ) │ │
│ │ [🔍 ค้นหาชื่อผู้ใช้... ] [+ เพิ่ม] │ │
│ │ → [👤 วิชัย ส. (วิศวกร)] × [📅 กำหนด: 20 มี.ค.] │ │
│ │ │ │
│ │ รับทราบ (Information — เพื่อทราบเท่านั้น) │ │
│ │ [🔍 ค้นหาชื่อผู้ใช้... ] [+ เพิ่ม] │ │
│ │ → [👤 มานะ พ. (ผจก.)] × │ │
│ │ │ │
│ │ หมายเหตุ / คำสั่งการ │ │
│ │ [โปรดตรวจสอบและเตรียมข้อมูลงวดงานที่ 3...] │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [← ยกเลิก] [✅ สร้างและส่ง Notify] │
└─────────────────────────────────────────────────────────────────────┘
```
---
### SCR-022: User Management (Admin)
```
┌─ จัดการผู้ใช้งาน ───────────────────────────────────────────────────┐
│ องค์กร: สค. (สำนักงานโครงการ) [+ เพิ่ม User ใหม่] │
│ │
│ 🔍 [ค้นหาชื่อ / อีเมล...] [Role: ทั้งหมด ▼] [สถานะ: Active ▼] │
│ │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ ชื่อ │ Username │ Role │ Status │ │ │
│ ├───────────────────┼─────────────┼────────────────┼────────┼───┤ │
│ │ สมชาย กิตติ │ somchai.k │ Document Ctrl │ ✅ │[⚙️]│ │
│ │ วิชัย สมศรี │ wichai.s │ Editor │ ✅ │[⚙️]│ │
│ │ มานะ พงษ์ดี │ mana.p │ Org Admin │ ✅ │[⚙️]│ │
│ │ สมหญิง รักดี │ somying.r │ Viewer │ ⛔ │[⚙️]│ │
│ └──────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
User Edit Drawer (Slide in from right):
┌──────────────────────────┐
│ แก้ไขผู้ใช้งาน │
│ ──────────────────── │
│ ชื่อ: [สมชาย กิตติ ] │
│ Email: [somchai@... ] │
│ Role: [Document Ctrl ▼] │
│ Status: ✅ Active [Toggle]│
│ │
│ [รีเซ็ตรหัสผ่าน] │
│ [❌ ยกเลิก] [✅ บันทึก] │
└──────────────────────────┘
```
---
### SCR-025: Document Number Config (Superadmin)
```
┌─ ตั้งค่าเลขที่เอกสาร ────────────────────────────────────────────────┐
│ Project: LCBP3 [+ เพิ่ม Format ใหม่] │
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ ประเภท │ Format Template │ Reset │ Actn │ │
│ ├─────────────────┼────────────────────────────────┼────────┼──────┤ │
│ │ Letter │ {PROJECT}-{ORIG}-{RECP}-L-{SEQ:4}-{YY}│ Yearly│[✏️]│ │
│ │ RFI │ {PROJECT}-{ORIG}-{RECP}-I-{SEQ:4}-{YY}│ Yearly│[✏️]│ │
│ │ RFA-Shop Dwg │ {PROJECT}-RFA-{DISC}-{SEQ:4} │ Never │[✏️]│ │
│ │ Transmittal │ {PROJECT}-{ORIG}-{RECP}-T-{SEQ:4}-{YY}│ Yearly│[✏️]│ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │
│ Edit Format Inline: │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Template: [{PROJECT}-{ORIG}-{RECP}-L-{SEQ:4}-{YY} ] │ │
│ │ Preview: [LCBP3-สค.-กทท.-L-0001-68] │ │
│ │ ✅ Format ถูกต้อง │ │
│ │ [ยกเลิก] [✅ บันทึก] │ │
│ └─────────────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘
```
---
## 5. 🎨 Design System Reference
### Color Tokens
```css
/* Primary — ใช้กับ Action Buttons, Links */
--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) */
/* 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 */
```
### 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 */
```
### 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 | — | — | — | — |
---
## 6. 📱 Responsive Breakpoints
| 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
---
## 7. ⚡ Interaction Patterns
### Optimistic Updates (UI-Rule 5.10)
```
User กด "Approve" → UI เปลี่ยนสถานะทันที (ไม่รอ API)
API ตอบกลับ Success → ยืนยัน UI ที่เปลี่ยนแล้ว
↓ (ถ้า API ล้มเหลว)
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 → ✅/❌
```
---
## 📝 Document Control
- **Version:** 1.0.0 | **Status:** DRAFT
- **Created:** 2026-03-11 | **Owner:** Nattanin Peancharoen
- **Next Step:** สร้าง High-fidelity Mockup ใน Figma ตามโครงสร้างนี้
- **Figma Link:** [TBD — สร้างใน Figma Community หรือ Self-hosted Penpot]
- **Classification:** Internal Use Only
+9 -4
View File
@@ -19,6 +19,9 @@ This directory contains the functional and non-functional requirements for the L
1. [Objectives & Goals](./01-01-objectives.md) - Project objectives and success criteria
2. [System Architecture & Technology](../02-Architecture/README.md) - High-level architecture requirements
3. [Functional Requirements](./01-02-modules/01-02-00-index.md) - Detailed feature specifications
4. **[📖 User Stories](./01-04-user-stories.md)** - 27 User Stories (8 Epics, MoSCoW priority, AC links)
5. **[🛡️ Edge Cases & Business Rules](./01-06-edge-cases-and-rules.md)** - 37 Edge Cases ป้องกัน Bug
6. **[🖼️ UI/UX Wireframes](./01-07-ui-wireframes.md)** - 26 Screens, Navigation Map, Design System
### Functional Areas
@@ -55,10 +58,12 @@ This directory contains the functional and non-functional requirements for the L
### Cross-Cutting Concerns
4. [Access Control & RBAC](./01-01-business-rules/01-01-01-rbac-matrix.md) - 4-level hierarchical RBAC
5. [UI/UX Requirements](./01-01-business-rules/01-01-03-ui-ux-rules.md) - User interface specifications
6. [Non-Functional Requirements](./01-01-business-rules/01-01-04-non-functional-rules.md) - Performance, security, scalability
7. [Testing Requirements](./01-01-business-rules/01-01-05-testing-rules.md) - Test strategy and coverage
4. [Access Control & RBAC](./01-01-business-rules/01-02-01-rbac-matrix.md) - 4-level hierarchical RBAC
5. [UI/UX Requirements](./01-02-business-rules/01-02-03-ui-ux-rules.md) - User interface specifications
6. [Non-Functional Requirements](./01-02-business-rules/01-02-04-non-functional-rules.md) - Performance, security, scalability
7. [Testing Requirements](./01-02-business-rules/01-02-05-testing-rules.md) - Test strategy and coverage
8. **[✅ Acceptance Criteria (UAT)](./01-05-acceptance-criteria.md)** - MVP Go-Live criteria, UAT Scenarios, Sign-off checklist
9. **[🎓 Training Plan](../00-Overview/00-06-training-plan.md)** - Training Curriculum, Change Management (see also `00-Overview/`)
---