1358 lines
48 KiB
Markdown
1358 lines
48 KiB
Markdown
# 3.11 Document Numbering Management (การจัดการเลขที่เอกสาร)
|
||
|
||
---
|
||
title: 'Functional Requirements: Document Numbering Management'
|
||
version: 1.6.2
|
||
status: draft
|
||
owner: Nattanin Peancharoen
|
||
last_updated: 2025-12-17
|
||
related:
|
||
- specs/01-requirements/01-01-objectives.md
|
||
- specs/01-requirements/01-02-architecture.md
|
||
- specs/01-requirements/01-03-functional-requirements.md
|
||
- specs/01-requirements/01-03.11-document-numbering.md
|
||
- specs/03-implementation/03-04-document-numbering.md
|
||
- specs/04-operations/04-08-document-numbering-operations.md
|
||
- specs/07-database/07-01-data-dictionary-v1.7.0.md
|
||
- specs/05-decisions/ADR-002-document-numbering-strategy.md
|
||
Clean Version v1.6.2 – Scope of Changes:
|
||
- เลือกใช้ Single Numbering System (Option A)
|
||
- แก้ Primary Key design ให้ implement ได้จริง
|
||
- ปรับ Character Rule เป็น UTF‑8 printable
|
||
- Bind Reset Policy ชัดเจน (Yearly reset, RFA no reset)
|
||
- เพิ่ม Number State Machine
|
||
- เพิ่ม Idempotency Key
|
||
- Drawing ใช้ separate counter namespace
|
||
- เพิ่ม Formal Token Validation Grammar
|
||
---
|
||
|
||
> **📖 เอกสารที่เกี่ยวข้อง**
|
||
>
|
||
> - **Implementation Guide**: [03-implementation/03-04-document-numbering.md](../03-implementation/03-04-document-numbering.md) - รายละเอียดการ implement ด้วย NestJS, TypeORM, Redis
|
||
> - **Operations Guide**: [04-operations/04-08-document-numbering-operations.md](../04-operations/04-08-document-numbering-operations.md) - Monitoring, Troubleshooting, Maintenance Procedures
|
||
|
||
---
|
||
|
||
## 3.11.1 Overview & วัตถุประสงค์
|
||
|
||
### 3.11.1.1 Purpose
|
||
|
||
ระบบ Document Numbering สำหรับสร้างเลขที่เอกสารอัตโนมัติที่มีความเป็นเอกลักษณ์ (unique) และสามารถติดตามได้ (traceable) สำหรับเอกสารทุกประเภทในระบบ LCBP3-DMS
|
||
|
||
### 3.11.1.2 Requirements Summary
|
||
|
||
- ระบบต้องสามารถสร้างเลขที่เอกสาร (Running Number) ได้โดยอัตโนมัติ, ที่มีความเป็นเอกลักษณ์ (unique) และยืดหยุ่นสูง
|
||
- ระบบต้องสามารถกำหนดรูปแบบ (template) เลขที่เอกสารได้ สำหรับแต่ละโครงการ, ชนิดเอกสาร, ประเภทเอกสาร
|
||
- ระบบต้องรับประกัน Uniqueness ของเลขที่เอกสารในทุกสถานการณ์
|
||
- ระบบต้องรองรับการทำงานแบบ concurrent ได้อย่างปลอดภัย
|
||
|
||
### 3.11.1.3 Scope
|
||
|
||
- Auto-generation ของเลขที่เอกสารตามรูปแบบที่กำหนด
|
||
- Manual override สำหรับการ import เอกสารเก่า
|
||
- Cancelled number handling (ไม่ reuse)
|
||
- Void & Replace pattern สำหรับการแทนที่เอกสาร
|
||
- Distributed locking เพื่อป้องกัน race condition
|
||
- Complete audit trail สำหรับทุก operation
|
||
|
||
### 3.11.1.4 Document Types Supported
|
||
|
||
- Request for Approvals (RFA)
|
||
- Request for Information (RFI)
|
||
- Transmittal (TRANSMITTAL)
|
||
- Email (EMAIL)
|
||
- Instruction (INSTRUCTION)
|
||
- Letter (LETTER)
|
||
- Memorandum (MEMO)
|
||
- Minutes of Meeting (MOM)
|
||
- Notice (NOTICE)
|
||
- Other (OTHER)
|
||
- Contract Drawings (COD)
|
||
- Shop Drawings (SHD)
|
||
- Circulation Sheets (CIR)
|
||
|
||
### 3.11.1.5 Architectural Decision (Updated)
|
||
AD-DN-001: Single Source of Truth for Numbering ระบบ เลือกใช้ Option A:
|
||
- document_number_counters เป็น Core / Authoritative Counter System
|
||
- document_numbering_configs ใช้เฉพาะ:
|
||
- Template format
|
||
- Permission / policy
|
||
- ยกเลิกการใช้ document_numbering_sequences เป็น counter จริง
|
||
เหตุผล: ลดความซ้ำซ้อน, ป้องกัน counter mismatch, debug ง่าย, ops ชัดเจน
|
||
---
|
||
## 3.11.2 Counter Logic & Reset Policy
|
||
### 3.11.2 Counter Logic (Logic การนับเลข)
|
||
|
||
การนับเลขจะแยกตาม **Counter Key** ที่ประกอบด้วยหลายส่วน ขึ้นกับประเภทเอกสาร
|
||
|
||
| Document Type | Reset Policy |
|
||
| ------------- | ------------ |
|
||
| Correspondence (LETTER, MEMO, RFI, etc.) | Yearly reset |
|
||
| Transmittal | Yearly reset |
|
||
| RFA | No reset (continuous) |
|
||
| Drawing | Separate namespace (see 3.11.8) |
|
||
|
||
### 3.11.2.2 Counter Key Fields (Revised)
|
||
```
|
||
(project_id,
|
||
originator_organization_id,
|
||
recipient_organization_id,
|
||
correspondence_type_id,
|
||
sub_type_id,
|
||
rfa_type_id,
|
||
discipline_id,
|
||
reset_scope)
|
||
```
|
||
|
||
* `reset_scope`:
|
||
* `YEAR_2025`, `YEAR_2026`, ...
|
||
* `NONE` (สำหรับ RFA)
|
||
|
||
### 3.11.2.3 Counter Key Components
|
||
|
||
| Component | Required? | Description | Database Source | Default if NULL |
|
||
| ---------------------------- | ---------------- | ------------------- | --------------------------------------------------------- | --------------- |
|
||
| `project_id` | ✅ Yes | ID โครงการ | Derived from user context or organization | - |
|
||
| `originator_organization_id` | ✅ Yes | ID องค์กรผู้ส่ง | `correspondences.originator_id` | - |
|
||
| `recipient_organization_id` | Depends on type | ID องค์กรผู้รับหลัก (TO) | `correspondence_recipients` where `recipient_type = 'TO'` | 0 for RFA |
|
||
| `correspondence_type_id` | ✅ Yes | ID ประเภทเอกสาร | `correspondence_types.id` | 0 |
|
||
| `sub_type_id` | TRANSMITTAL only | ID ประเภทย่อย | `correspondence_sub_types.id` | 0 |
|
||
| `rfa_type_id` | RFA only | ID ประเภท RFA | `rfa_types.id` | 0 |
|
||
| `discipline_id` | RFA only | ID สาขางาน | `disciplines.id` | 0 |
|
||
| `reset_scope` | ✅ Yes | ขอบเขต reset | System derived | - |
|
||
|
||
### 3.11.2.4 Counter Key by Document Type
|
||
|
||
#### **Global (LETTER / MEMO / RFI / EMAIL / INSTRUCTION / NOTICE / OTHER)**:
|
||
```
|
||
(project_id, originator_organization_id, recipient_organization_id,
|
||
correspondence_type_id, 0, 0, 0, 'YEAR_2025')
|
||
```
|
||
|
||
**หมายเหตุ**:
|
||
- ไม่ใช้ `discipline_id`, `sub_type_id`, `rfa_type_id`
|
||
- ถ้ามีการเพิ่ม correspondence type ใหม่ใน `correspondence_types` table จะใช้ Template นี้โดยอัตโนมัติ
|
||
|
||
#### **TRANSMITTAL**:
|
||
```
|
||
(project_id, originator_organization_id, recipient_organization_id,
|
||
correspondence_type_id, sub_type_id, 0, 0, 'YEAR_2025')
|
||
```
|
||
|
||
*หมายเหตุ*: ใช้ `sub_type_id` เพิ่มเติม
|
||
|
||
#### **RFA**:
|
||
```
|
||
(project_id, originator_organization_id, 0,
|
||
correspondence_type_id, 0, rfa_type_id, discipline_id, 'NONE')
|
||
```
|
||
|
||
*หมายเหตุ*:
|
||
- RFA ไม่ใช้ `recipient_organization_id` (ใช้ 0) เพราะเป็นเอกสารโครงการ (CONTRACTOR → CONSULTANT → OWNER)
|
||
- ไม่มี yearly reset (`reset_scope = 'NONE'`)
|
||
|
||
### 3.11.2.5 วิธีการหา project_id
|
||
|
||
เนื่องจาก Template ของ LETTER/TRANSMITTAL ไม่มี `{PROJECT}` token ระบบจะหา `project_id` จาก:
|
||
|
||
1. **User Context** (แนะนำ):
|
||
- เมื่อ User สร้างเอกสาร UI จะให้เลือก Project/Contract ก่อน
|
||
- ใช้ `project_id` จาก Context ที่เลือก
|
||
|
||
2. **จาก Organization**:
|
||
- Query `project_organizations` หรือ `contract_organizations`
|
||
- ใช้ `originator_organization_id` หา project ที่เกี่ยวข้อง
|
||
- ถ้ามีหลาย project ให้ User เลือก
|
||
|
||
3. **Validation**:
|
||
- ตรวจสอบว่า organization มีสิทธิ์ใน project นั้น
|
||
- ตรวจสอบว่า project/contract เป็น active
|
||
|
||
### 3.11.2.6 Fallback สำหรับค่า NULL
|
||
|
||
- `correspondence_type_id`: ใช้ `0` (ไม่ระบุประเภทเอกสาร)
|
||
- `discipline_id`: ใช้ `0` (ไม่ระบุสาขางาน)
|
||
- `sub_type_id`: ใช้ `0` (ไม่มีประเภทย่อย)
|
||
- `rfa_type_id`: ใช้ `0` (ไม่ระบุประเภท RFA)
|
||
- `recipient_organization_id`: ใช้ `0` สำหรับ RFA, Required สำหรับ LETTER/TRANSMITTAL
|
||
|
||
---
|
||
|
||
## 3.11.3 Format Templates by Correspondence Type
|
||
|
||
> **📝 หมายเหตุสำคัญ**
|
||
> - Templates ด้านล่างเป็น **ตัวอย่าง** สำหรับประเภทเอกสารหลัก
|
||
> - ระบบรองรับ **ทุกประเภทเอกสาร** ที่อยู่ใน `correspondence_types` table
|
||
> - หากมีการเพิ่มประเภทใหม่ในอนาคต สามารถใช้งานได้โดยอัตโนมัติ
|
||
> - Admin สามารถกำหนด Template เฉพาะสำหรับแต่ละประเภทผ่าน Admin Panel
|
||
|
||
### 3.11.3.1 Global (correspondence_type_id = defined)
|
||
|
||
**Template**:
|
||
```
|
||
{ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}
|
||
```
|
||
|
||
**Example**: `คคง.-สคฉ.3-0001-2568`
|
||
|
||
**Token Breakdown**:
|
||
|
||
- `คคง.` = {ORIGINATOR} = รหัสองค์กรผู้ส่ง
|
||
- `สคฉ.3` = {RECIPIENT} = รหัสองค์กรผู้รับหลัก (TO)
|
||
- `0001` = {SEQ:4} = Running number (เริ่ม 0001, 0002, ...)
|
||
- `2568` = {YEAR:B.E.} = ปี พ.ศ.
|
||
|
||
> **⚠️ Template vs Counter Separation**
|
||
>
|
||
> - {CORR_TYPE} **ไม่แสดง**ใน template เพื่อความกระชับ
|
||
> - แต่ระบบ**ยังใช้ correspondence_type_id ใน Counter Key** เพื่อแยก counter
|
||
> - LETTER, MEMO, RFI **มี counter แยกกัน** แม้ template format เหมือนกัน
|
||
|
||
**Counter Key**: `(project_id, originator_org_id, recipient_org_id, correspondence_type_id, 0, 0, 0, 'YEAR_2025')`
|
||
|
||
---
|
||
|
||
### 3.11.3.2 Transmittal (TYPE = TRANSMITTAL)
|
||
|
||
**Template**:
|
||
```
|
||
{ORIGINATOR}-{RECIPIENT}-{SUB_TYPE}-{SEQ:4}-{YEAR:B.E.}
|
||
```
|
||
|
||
**Example**: `คคง.-สคฉ.3-21-0117-2568`
|
||
|
||
**Token Breakdown**:
|
||
|
||
- `คคง.` = {ORIGINATOR}
|
||
- `สคฉ.3` = {RECIPIENT}
|
||
- `21` = {SUB_TYPE} = หมายเลขประเภทย่อย (11=MAT, 12=SHP, 13=DWG, 14=MET, ...)
|
||
- `0117` = {SEQ:4}
|
||
- `2568` = {YEAR:B.E.}
|
||
|
||
**Counter Key**: `(project_id, originator_org_id, recipient_org_id, correspondence_type_id, sub_type_id, 0, 0, 'YEAR_2025')`
|
||
|
||
---
|
||
|
||
### 3.11.3.3 RFA (Request for Approval)
|
||
|
||
**Example**: `LCBP3-C2-RFA-TER-RPT-0001-A`
|
||
|
||
**Token Breakdown**:
|
||
|
||
- `LCBP3-C2` = {PROJECT} = รหัสโครงการ
|
||
- `RFA` = {CORR_TYPE} = ประเภทเอกสาร (**แสดง**ใน RFA template)
|
||
- `TER` = {DISCIPLINE} = รหัสสาขางาน (TER=Terminal, STR=Structure, ...)
|
||
- `RPT` = {RFA_TYPE} = ประเภท RFA (RPT=Report, SDW=Shop Drawing, ...)
|
||
- `0001` = {SEQ:4}
|
||
- `A` = {REV} = Revision code
|
||
|
||
> **📋 RFA Workflow**
|
||
>
|
||
> - RFA เป็น **เอกสารโครงการ** (Project-level document)
|
||
> - Workflow: **CONTRACTOR → CONSULTANT → OWNER**
|
||
> - ไม่มี specific `recipient_id` เพราะเป็น workflow ที่กำหนดไว้แล้ว
|
||
|
||
**Counter Key**: `(project_id, originator_org_id, 0, correspondence_type_id, 0, rfa_type_id, discipline_id, 'NONE')`
|
||
|
||
|
||
---
|
||
|
||
|
||
### 3.11.3.4 Drawing
|
||
|
||
**Status**: 🚧 **To Be Determined**
|
||
|
||
Drawing Numbering ยังไม่ได้กำหนด Template เนื่องจาก:
|
||
|
||
- มีความซับซ้อนสูง (Contract Drawing และ Shop Drawing มีกฎต่างกัน)
|
||
- อาจต้องใช้ระบบ Numbering แยกต่างหาก
|
||
- ต้องพิจารณาร่วมกับ RFA ที่เกี่ยวข้อง
|
||
|
||
---
|
||
|
||
## 3.11.4 Supported Token Types
|
||
|
||
| Token | Description | Example | Database Source |
|
||
| -------------- | ---------------------------- | ------------------------------ | ----------------------------------------------------------------------------------------------- |
|
||
| `{PROJECT}` | รหัสโครงการ | `LCBP3`, `LCBP3-C2` | `projects.project_code` |
|
||
| `{ORIGINATOR}` | รหัสองค์กรผู้ส่ง | `คคง.`, `ผรม.1` | `organizations.organization_code` via `correspondences.originator_id` |
|
||
| `{RECIPIENT}` | รหัสองค์กรผู้รับหลัก (TO) | `สคฉ.3`, `กทท.` | `organizations.organization_code` via `correspondence_recipients` where `recipient_type = 'TO'` |
|
||
| `{CORR_TYPE}` | รหัสประเภทเอกสาร | `RFA`, `TRANSMITTAL`, `LETTER` | `correspondence_types.type_code` |
|
||
| `{SUB_TYPE}` | หมายเลขประเภทย่อย | `11`, `12`, `21` | `correspondence_sub_types.sub_type_number` |
|
||
| `{RFA_TYPE}` | รหัสประเภท RFA | `SDW`, `RPT`, `MAT` | `rfa_types.type_code` |
|
||
| `{DISCIPLINE}` | รหัสสาขาวิชา | `STR`, `TER`, `GEO` | `disciplines.discipline_code` |
|
||
| `{SEQ:n}` | Running number (n = จำนวนหลัก) | `0001`, `0029`, `0985` | Based on `document_number_counters.last_number + 1` |
|
||
| `{YEAR:B.E.}` | ปี พ.ศ. | `2568` | `reset_scope` + 543 |
|
||
| `{YEAR:A.D.}` | ปี ค.ศ. | `2025` | `reset_scope` |
|
||
| `{REV}` | Revision Code | `A`, `B`, `AA` | `correspondence_revisions.revision_label` |
|
||
| `{PREFIX}` | คำนำหน้าตามประเภทเอกสาร | `COR`, `RFA` | Configurable prefix |
|
||
| `{YYYY}` | ปี 4 หลัก | `2025` | Current year |
|
||
| `{YY}` | ปี 2 หลัก | `25` | Current year (short) |
|
||
| `{MM}` | เดือน 2 หลัก | `01-12` | Current month |
|
||
| `{CONTRACT}` | รหัสสัญญา | `C001` | `contracts.contract_code` |
|
||
|
||
### Token Usage Notes
|
||
|
||
**{SEQ:n}**:
|
||
|
||
- `n` = จำนวนหลักที่ต้องการ (typically 4-6)
|
||
- Counter **เริ่มจาก 0001** และเพิ่มทีละ 1 (0001, 0002, 0003, ...)
|
||
- Padding ด้วย 0 ทางซ้าย
|
||
- Reset ทุกปี (ตาม `reset_scope` ใน Counter Key)
|
||
|
||
**{RECIPIENT}**:
|
||
|
||
- ใช้เฉพาะผู้รับที่มี `recipient_type = 'TO'` เท่านั้น
|
||
- ถ้ามีหลาย TO ให้ใช้คนแรก (ตาม sort order)
|
||
- **ไม่ใช้สำหรับ RFA** (RFA ไม่มี {RECIPIENT} ใน template)
|
||
|
||
**{CORR_TYPE}**:
|
||
|
||
- รองรับทุกค่าจาก `correspondence_types.type_code`
|
||
- ถ้ามีการเพิ่มประเภทใหม่ จะใช้งานได้ทันที
|
||
- **แสดงใน template**: RFA only
|
||
- **ไม่แสดงแต่ใช้ใน counter**: LETTER, TRANSMITTAL, และ Other types
|
||
|
||
**Deprecated Tokens** (ไม่ควรใช้):
|
||
|
||
- ~~`{ORG}`~~ → ใช้ `{ORIGINATOR}` หรือ `{RECIPIENT}` แทน
|
||
- ~~`{TYPE}`~~ → ใช้ `{CORR_TYPE}`, `{SUB_TYPE}`, หรือ `{RFA_TYPE}` แทน (ตามบริบท)
|
||
- ~~`{CATEGORY}`~~ → ไม่ได้ใช้งานในระบบปัจจุบัน
|
||
|
||
---
|
||
|
||
## 3.11.5 Character & Format Rules (Updated)
|
||
|
||
### BR-DN-002 (Revised)
|
||
|
||
* Document number **must be printable UTF‑8**
|
||
* Disallowed:
|
||
* Control characters
|
||
* Newlines / tabs
|
||
* Allowed:
|
||
* Thai
|
||
* English
|
||
* Numbers
|
||
* `-`, `_`, `.`
|
||
|
||
### BR-DN-003: Number Format Rules
|
||
- Min length: 10 characters
|
||
- Max length: 50 characters
|
||
- Must include {SEQ:n} token exactly once
|
||
|
||
---
|
||
|
||
## 3.11.6 Number State Machine (New)
|
||
|
||
### States
|
||
|
||
```text
|
||
RESERVED → CONFIRMED → VOID
|
||
↘ CANCELLED
|
||
```
|
||
|
||
### Rules
|
||
|
||
* **RESERVED**:
|
||
* TTL 5 minutes
|
||
* Auto-expire → CANCELLED
|
||
* **CONFIRMED**:
|
||
* Linked to document_id
|
||
* **VOID**:
|
||
* Only CONFIRMED numbers
|
||
* Replacement creates new number
|
||
|
||
---
|
||
|
||
## 3.11.7 Idempotency (New)
|
||
|
||
### API Requirement
|
||
|
||
* All number generation APIs **must** support:
|
||
|
||
```http
|
||
Idempotency-Key: UUID
|
||
```
|
||
|
||
### Behavior
|
||
|
||
* Same key + same payload → return same number
|
||
* Prevents double submit / retry duplication
|
||
|
||
---
|
||
|
||
## 3.11.8 Drawing Numbering (Clarified)
|
||
|
||
* Drawing numbering **does not use** this counter table
|
||
* Uses **separate counter namespace**:
|
||
|
||
```text
|
||
DRAWING::<project>::<contract>
|
||
```
|
||
|
||
* Prevents collision with correspondence/RFA
|
||
|
||
---
|
||
|
||
## 3.11.9 Token Validation Grammar (New)
|
||
|
||
### EBNF
|
||
|
||
```ebnf
|
||
TEMPLATE := TOKEN ("-" TOKEN)*
|
||
TOKEN := SIMPLE | PARAM
|
||
SIMPLE := "{PROJECT}" | "{ORIGINATOR}" | "{RECIPIENT}" |
|
||
"{CORR_TYPE}" | "{DISCIPLINE}" | "{RFA_TYPE}" |
|
||
"{REV}" | "{YYYY}" | "{YY}" | "{MM}"
|
||
PARAM := "{SEQ:" DIGIT+ "}"
|
||
DIGIT := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||
```
|
||
|
||
### Validation Rules
|
||
|
||
* Must include `{SEQ:n}` exactly once
|
||
* Unknown tokens → validation error
|
||
* Max template length: 50 chars
|
||
|
||
---
|
||
|
||
## 3.11.10 Functional Requirements
|
||
|
||
### 3.11.10.1 Auto Number Generation
|
||
|
||
#### FR-DN-001: Generate Sequential Number
|
||
**Priority**: CRITICAL | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องสามารถสร้างเลขที่เอกสารอัตโนมัติตามลำดับ (sequential) โดยไม่ซ้ำกัน
|
||
|
||
**Acceptance Criteria**:
|
||
- เลขที่เอกสารต้องเป็น unique ในscope ที่กำหนด
|
||
- ต้องเพิ่มขึ้นทีละ 1 (increment by 1)
|
||
- ต้องรองรับ concurrent requests โดยไม่มีเลขที่ซ้ำ
|
||
- Response time < 100ms (p95)
|
||
|
||
---
|
||
|
||
#### FR-DN-002: Configurable Number Format
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องรองรับการกำหนดรูปแบบเลขที่เอกสารที่หลากหลาย
|
||
|
||
**Acceptance Criteria**:
|
||
- รองรับ format tokens ที่ระบุ
|
||
- Admin สามารถกำหนด format ผ่าน UI ได้
|
||
- Validate format ก่อน save
|
||
- แสดง preview ของเลขที่ที่จะถูกสร้าง
|
||
|
||
---
|
||
|
||
#### FR-DN-003: Scope-based Sequences
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องรองรับการสร้าง sequence ที่แยกตาม scope ที่ต่างกัน
|
||
|
||
**Acceptance Criteria**:
|
||
- เลขที่ไม่ซ้ำภายใน scope เดียวกัน
|
||
- Scope ที่ต่างกันสามารถมีเลขที่เดียวกันได้
|
||
- Support multiple active scopes
|
||
|
||
---
|
||
|
||
### 3.11.10.2 Manual Override
|
||
|
||
#### FR-DN-004: Manual Number Assignment
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องรองรับการกำหนดเลขที่เอกสารด้วยตนเอง (manual override)
|
||
|
||
**Use Cases**:
|
||
1. Import เอกสารเก่าจากระบบเดิม
|
||
2. External documents จาก client/consultant
|
||
3. Correction หลังพบความผิดพลาด
|
||
|
||
**Acceptance Criteria**:
|
||
- ตรวจสอบ duplicate ก่อน save
|
||
- Validate format ตามรูปแบบที่กำหนด
|
||
- Auto-update sequence counter ถ้าเลขที่สูงกว่า current
|
||
- บันทึก audit log ว่าเป็น manual override
|
||
- ต้องมีสิทธิ์ Admin ขึ้นไปเท่านั้น
|
||
|
||
---
|
||
|
||
#### FR-DN-005: Bulk Import Support
|
||
**Priority**: MEDIUM | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องรองรับการ import เอกสารหลายรายการพร้อมกัน
|
||
|
||
**Acceptance Criteria**:
|
||
- รองรับไฟล์ CSV/Excel
|
||
- Validate ทุกรายการก่อน import
|
||
- แสดง preview ก่อน confirm
|
||
- Rollback ทั้งหมดถ้ามีรายการใดผิดพลาด (transactional)
|
||
- Auto-update sequence counters หลัง import
|
||
- Generate import report
|
||
|
||
---
|
||
|
||
### 3.11.10.3 Cancelled & Void Handling
|
||
|
||
#### FR-DN-006: Skip Cancelled Numbers
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
เลขที่เอกสารที่ถูกยกเลิกต้องไม่ถูก reuse
|
||
|
||
**Rationale**:
|
||
- รักษา audit trail ที่ชัดเจน
|
||
- ป้องกันความสับสน
|
||
- Legal compliance
|
||
|
||
**Acceptance Criteria**:
|
||
- Cancelled number ยังคงอยู่ในฐานข้อมูลพร้อม status
|
||
- ระบบข้าม (skip) cancelled number เมื่อสร้างเลขที่ใหม่
|
||
- บันทึกเหตุผลการยกเลิก
|
||
- แสดง cancelled numbers ใน audit trail
|
||
|
||
---
|
||
|
||
#### FR-DN-007: Void and Replace
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องรองรับการ void เอกสารและสร้างเอกสารใหม่แทน
|
||
|
||
**Workflow**:
|
||
1. User เลือกเอกสารที่ต้องการ void
|
||
2. ระบุเหตุผล (required)
|
||
3. ระบบเปลี่ยน status เอกสารเดิมเป็น VOID
|
||
4. สร้างเอกสารใหม่ด้วยเลขที่ใหม่
|
||
5. Link เอกสารใหม่กับเดิม (voided_from_id)
|
||
|
||
**Acceptance Criteria**:
|
||
- เอกสารเดิม status = VOID (ไม่ลบ)
|
||
- เอกสารใหม่ได้เลขที่ต่อเนื่องจาก sequence
|
||
- มี reference link ระหว่างเอกสาร
|
||
- บันทึก void reason
|
||
- แสดง void history chain (A→B→C)
|
||
|
||
---
|
||
|
||
### 3.11.10.4 Concurrency & Performance
|
||
|
||
#### FR-DN-008: Prevent Race Conditions
|
||
**Priority**: CRITICAL | **Status**: Required
|
||
|
||
**Description**:
|
||
ระบบต้องป้องกันการสร้างเลขที่ซ้ำเมื่อมีการ request พร้อมกัน
|
||
|
||
**Solution**:
|
||
- Distributed locking (Redlock)
|
||
- Database pessimistic locking
|
||
- Two-phase commit pattern
|
||
|
||
**Acceptance Criteria**:
|
||
- Zero duplicate numbers ภายใต้ concurrent load (1000 req/s)
|
||
- Lock acquisition time < 50ms (avg)
|
||
- Automatic retry on lock failure (max 3 times)
|
||
- Timeout handling (30 seconds)
|
||
|
||
---
|
||
|
||
#### FR-DN-009: Two-Phase Commit
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
ใช้ Two-phase commit pattern เพื่อความสมบูรณ์ของข้อมูล
|
||
|
||
**Phase 1: Reserve**
|
||
- ล็อกเลขที่และ reserve ไว้ชั่วคราว
|
||
- Set TTL 5 นาที
|
||
- Return reservation token
|
||
|
||
**Phase2: Confirm or Cancel**
|
||
- Confirm: บันทึกลงฐานข้อมูลถาวร
|
||
- Cancel: คืน lock และ reservation
|
||
|
||
**Acceptance Criteria**:
|
||
- Reservation ต้อง expire หลัง 5 นาที
|
||
- Auto-cleanup expired reservations
|
||
- Support explicit cancel
|
||
- Idempotent confirmation
|
||
|
||
---
|
||
### 3.11.10.5 Monitoring & Audit
|
||
|
||
#### FR-DN-010: Complete Audit Trail
|
||
**Priority**: HIGH | **Status**: Required
|
||
|
||
**Description**:
|
||
บันทึกทุก operation ที่เกิดขึ้นกับเลขที่เอกสาร
|
||
|
||
**Events to Log**:
|
||
- Number reserved
|
||
- Number confirmed
|
||
- Number cancelled
|
||
- Manual override
|
||
- Void document
|
||
- Sequence adjusted
|
||
- Format changed
|
||
|
||
**Acceptance Criteria**:
|
||
- Log ทุก operation
|
||
- Searchable by user, date, type
|
||
- Export to CSV
|
||
- Retain for 7 years
|
||
|
||
---
|
||
|
||
#### FR-DN-011: Metrics & Alerting
|
||
**Priority**: MEDIUM | **Status**: Required
|
||
|
||
**Description**:
|
||
แสดงสถิติและส่ง alert เมื่อเกิดปัญหา
|
||
|
||
**Metrics**:
|
||
- Sequence utilization (% of max)
|
||
- Average lock wait time
|
||
- Failed lock attempts
|
||
- Numbers generated per day
|
||
- Manual overrides per day
|
||
|
||
**Alerts**:
|
||
- Sequence >90% used (WARNING)
|
||
- Sequence >95% used (CRITICAL)
|
||
- Lock wait time >1s (WARNING)
|
||
- Redis unavailable (CRITICAL)
|
||
- High error rate (WARNING)
|
||
|
||
---
|
||
|
||
## 3.11.11 Database Schema (Corrected)
|
||
|
||
### 3.11.11.1 document_number_counters
|
||
|
||
```sql
|
||
CREATE TABLE document_number_counters (
|
||
project_id INT NOT NULL,
|
||
correspondence_type_id INT NULL,-- NULL = default format for project
|
||
originator_organization_id INT NOT NULL,
|
||
recipient_organization_id INT NOT NULL DEFAULT 0, -- 0 = no recipient (RFA)
|
||
sub_type_id INT DEFAULT 0,
|
||
rfa_type_id INT DEFAULT 0,
|
||
discipline_id INT DEFAULT 0,
|
||
reset_scope VARCHAR(20) NOT NULL,
|
||
last_number INT DEFAULT 0 NOT NULL,
|
||
version INT DEFAULT 0 NOT NULL,
|
||
created_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6),
|
||
updated_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||
PRIMARY KEY (
|
||
project_id,
|
||
originator_organization_id,
|
||
recipient_organization_id,
|
||
correspondence_type_id,
|
||
sub_type_id,
|
||
rfa_type_id,
|
||
discipline_id,
|
||
reset_scope
|
||
),
|
||
|
||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
|
||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci
|
||
COMMENT = 'ตารางเก็บ Running Number Counters';
|
||
```
|
||
|
||
### Rules
|
||
|
||
* RFA → `recipient_organization_id = 0`
|
||
* Reset:
|
||
* Yearly: `reset_scope = 'YEAR_2025'`
|
||
* No reset: `reset_scope = 'NONE'`
|
||
|
||
### 3.11.11.2 Index Requirements
|
||
|
||
```sql
|
||
-- Index สำหรับ Performance
|
||
CREATE INDEX idx_counter_lookup
|
||
ON document_number_counters (
|
||
project_id,
|
||
correspondence_type_id,
|
||
reset_scope
|
||
);
|
||
|
||
-- Index สำหรับ Originator lookup
|
||
CREATE INDEX idx_counter_org
|
||
ON document_number_counters (
|
||
originator_organization_id,
|
||
reset_scope
|
||
);
|
||
|
||
-- Index สำหรับ updated_at
|
||
CREATE INDEX idx_counter_updated
|
||
ON document_number_counters (
|
||
updated_at
|
||
);
|
||
```
|
||
|
||
### 3.11.11.3 Numbering Configuration Table
|
||
|
||
```sql
|
||
CREATE TABLE document_numbering_configs (
|
||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||
document_type VARCHAR(50) NOT NULL,
|
||
format VARCHAR(200) NOT NULL,
|
||
scope ENUM('GLOBAL','PROJECT','CONTRACT','YEARLY','MONTHLY'),
|
||
allow_manual_override BOOLEAN DEFAULT FALSE,
|
||
max_value INT DEFAULT 999999,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
UNIQUE KEY (document_type, scope)
|
||
);
|
||
```
|
||
|
||
### 3.11.11.4 Audit Log Table
|
||
|
||
```sql
|
||
CREATE TABLE document_number_audit (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
document_id INT NOT NULL,
|
||
document_type VARCHAR(50),
|
||
document_number VARCHAR(100) NOT NULL,
|
||
operation ENUM('RESERVE', 'CONFIRM', 'CANCEL', 'MANUAL_OVERRIDE', 'VOID', 'GENERATE') NOT NULL,
|
||
status ENUM('RESERVED', 'CONFIRMED', 'CANCELLED', 'VOID', 'MANUAL'),
|
||
counter_key JSON NOT NULL COMMENT 'Counter key used (JSON format)',
|
||
reservation_token VARCHAR(36) NULL,
|
||
originator_organization_id INT NULL,
|
||
recipient_organization_id INT NULL,
|
||
|
||
template_used VARCHAR(200) NOT NULL,
|
||
old_value TEXT NULL,
|
||
new_value TEXT NULL,
|
||
|
||
user_id INT NOT NULL,
|
||
ip_address VARCHAR(45),
|
||
user_agent TEXT,
|
||
is_success BOOLEAN DEFAULT TRUE,
|
||
|
||
-- Performance & Error Tracking
|
||
retry_count INT DEFAULT 0,
|
||
lock_wait_ms INT COMMENT 'Lock acquisition time in milliseconds',
|
||
total_duration_ms INT COMMENT 'Total generation time',
|
||
fallback_used ENUM('NONE', 'DB_LOCK', 'RETRY') DEFAULT 'NONE',
|
||
metadata JSON NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
|
||
INDEX idx_document_id (document_id),
|
||
INDEX idx_user_id (user_id),
|
||
INDEX idx_status (status),
|
||
INDEX idx_operation (operation),
|
||
INDEX idx_document_number (document_number),
|
||
INDEX idx_reservation_token (reservation_token);
|
||
INDEX idx_created_at (created_at),
|
||
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (user_id) REFERENCES users(id)
|
||
) ENGINE=InnoDB COMMENT='Document Number Generation Audit Trail';
|
||
```
|
||
|
||
### 3.11.11.5 Reservation Table
|
||
```sql
|
||
CREATE TABLE document_number_reservations (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
|
||
-- Reservation Details
|
||
token VARCHAR(36) NOT NULL UNIQUE COMMENT 'UUID v4',
|
||
document_number VARCHAR(100) NOT NULL UNIQUE,
|
||
status ENUM('RESERVED', 'CONFIRMED', 'CANCELLED', 'VOID') NOT NULL DEFAULT 'RESERVED',
|
||
|
||
-- Linkage
|
||
document_id INT NULL COMMENT 'FK to documents (NULL until confirmed)',
|
||
|
||
-- Context (for debugging)
|
||
project_id INT NOT NULL,
|
||
correspondence_type_id INT NOT NULL,
|
||
originator_organization_id INT NOT NULL,
|
||
recipient_organization_id INT DEFAULT 0,
|
||
user_id INT NOT NULL,
|
||
|
||
-- Timestamps
|
||
reserved_at DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6),
|
||
expires_at DATETIME(6) NOT NULL,
|
||
confirmed_at DATETIME(6) NULL,
|
||
cancelled_at DATETIME(6) NULL,
|
||
|
||
-- Audit
|
||
ip_address VARCHAR(45),
|
||
user_agent TEXT,
|
||
metadata JSON NULL COMMENT 'Additional context',
|
||
|
||
-- Indexes
|
||
INDEX idx_token (token),
|
||
INDEX idx_status (status),
|
||
INDEX idx_status_expires (status, expires_at),
|
||
INDEX idx_document_id (document_id),
|
||
INDEX idx_user_id (user_id),
|
||
INDEX idx_reserved_at (reserved_at),
|
||
|
||
-- Foreign Keys
|
||
FOREIGN KEY (document_id) REFERENCES documents(id) ON DELETE SET NULL,
|
||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE,
|
||
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
||
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
|
||
COMMENT='Document Number Reservations - Two-Phase Commit';
|
||
|
||
```
|
||
### 3.11.11.5 Error Log Table
|
||
|
||
```sql
|
||
CREATE TABLE document_number_errors (
|
||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||
error_type ENUM(
|
||
'LOCK_TIMEOUT',
|
||
'VERSION_CONFLICT',
|
||
'DB_ERROR',
|
||
'REDIS_ERROR',
|
||
'VALIDATION_ERROR'
|
||
) NOT NULL,
|
||
error_message TEXT,
|
||
stack_trace TEXT,
|
||
context_data JSON COMMENT 'Request context (user, project, etc.)',
|
||
user_id INT,
|
||
ip_address VARCHAR(45),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
resolved_at TIMESTAMP NULL,
|
||
|
||
INDEX idx_error_type (error_type),
|
||
INDEX idx_created_at (created_at),
|
||
INDEX idx_user_id (user_id)
|
||
) ENGINE=InnoDB COMMENT='Document Numbering Error Log';
|
||
```
|
||
|
||
### 3.11.11.6 Config History Table
|
||
|
||
```sql
|
||
CREATE TABLE document_number_config_history (
|
||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||
config_id INT NOT NULL,
|
||
template_before TEXT,
|
||
template_after TEXT NOT NULL,
|
||
changed_by INT NOT NULL,
|
||
changed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
change_reason TEXT,
|
||
|
||
FOREIGN KEY (config_id) REFERENCES document_number_configs(id),
|
||
FOREIGN KEY (changed_by) REFERENCES users(id)
|
||
) ENGINE=InnoDB COMMENT='Template Change History';
|
||
```
|
||
|
||
### 3.11.11.7 Important Notes
|
||
|
||
> **💡 Counter Key Design**
|
||
> - `recipient_organization_id` ใช้ `0` สำหรับ RFA (ไม่มี specific recipient)
|
||
> - `version` column สำหรับ Optimistic Locking (ป้องกัน race condition)
|
||
> - `last_number` เริ่มจาก 0 และเพิ่มขึ้นทีละ 1
|
||
> - Counter reset ทุกปี (เมื่อ `reset_scope` เปลี่ยน)
|
||
|
||
> **⚠️ Migration Notes**
|
||
> - ไม่มีข้อมูลเก่า ไม่ต้องทำ backward compatibility
|
||
> - สามารถสร้าง table ใหม่ได้เลยตาม schema ข้างต้น
|
||
> - ต้องมี seed data สำหรับ `correspondence_types`, `rfa_types`, `disciplines` ก่อน
|
||
|
||
### 3.11.11.8 Example Counter Records
|
||
|
||
```sql
|
||
-- Example: LETTER from คคง. to สคฉ.3 in LCBP3-C2 year 2025
|
||
INSERT INTO document_number_counters (
|
||
project_id, originator_organization_id, recipient_organization_id,
|
||
correspondence_type_id, sub_type_id, rfa_type_id, discipline_id,
|
||
reset_scope, version, last_number
|
||
) VALUES (
|
||
2, -- LCBP3-C2
|
||
22, -- คคง.
|
||
10, -- สคฉ.3
|
||
6, -- LETTER
|
||
0, 0, 0,
|
||
'YEAR_2025', 0, 0
|
||
);
|
||
|
||
-- Example: RFA from ผรม.2 in LCBP3-C2, discipline TER, type RPT, year 2025
|
||
INSERT INTO document_number_counters (
|
||
project_id, originator_organization_id, recipient_organization_id,
|
||
correspondence_type_id, sub_type_id, rfa_type_id, discipline_id,
|
||
reset_scope, version, last_number
|
||
) VALUES (
|
||
2, -- LCBP3-C2
|
||
42, -- ผรม.2
|
||
0, -- RFA ไม่มี specific recipient
|
||
1, -- RFA
|
||
0,
|
||
18, -- RPT (Report)
|
||
5, -- TER (Terminal)
|
||
'NONE', 0, 0 -- No yearly reset for RFA
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 3.11.12 Security & Data Integrity Requirements
|
||
|
||
### 3.11.12.1 Concurrency Control
|
||
|
||
**Requirements:**
|
||
|
||
- ระบบ**ต้อง**ป้องกัน race condition เมื่อมีการสร้างเลขที่เอกสารพร้อมกัน
|
||
- ระบบ**ต้อง**รับประกัน uniqueness ของเลขที่เอกสารในทุกสถานการณ์
|
||
- ระบบ**ควร**ใช้ Distributed Lock (Redis) เป็นกลไก primary
|
||
- ระบบ**ต้อง**มี fallback mechanism เมื่อ Redis ไม่พร้อมใช้งาน
|
||
|
||
### 3.11.12.2 Data Integrity
|
||
|
||
**Requirements:**
|
||
|
||
- ระบบ**ต้อง**ใช้ Optimistic Locking เพื่อตรวจจับ concurrent updates
|
||
- ระบบ**ต้อง**มี database constraints เพื่อป้องกันข้อมูลผิดพลาด:
|
||
- Unique constraint บน `document_number`
|
||
- Foreign key constraints ทุก relationship
|
||
- Check constraints สำหรับ business rules
|
||
|
||
### 3.11.12.3 Authorization
|
||
|
||
**Requirements:**
|
||
|
||
- เฉพาะ **authenticated users** เท่านั้นที่สามารถ request document number
|
||
- เฉพาะ **Project Admin** เท่านั้นที่แก้ไข template ได้
|
||
- เฉพาะ **Super Admin** เท่านั้นที่ reset counter ได้ (requires approval)
|
||
|
||
### 3.11.12.4 Rate Limiting
|
||
|
||
**Requirements:**
|
||
- Limit ต่อ user: **10 requests/minute** (prevent abuse)
|
||
- Limit ต่อ IP: **50 requests/minute**
|
||
|
||
---
|
||
|
||
## 3.11.13 Error Handling Requirements
|
||
|
||
### 3.11.13.1 Retry Mechanism
|
||
|
||
**Requirements:**
|
||
|
||
ระบบ**ต้อง**จัดการ error scenarios ต่อไปนี้:
|
||
|
||
| Scenario | Strategy | Max Retries | Expected Response |
|
||
| ------------------- | ------------------- | ----------- | ------------------------------- |
|
||
| Redis Unavailable | Fallback to DB Lock | 0 | Continue (degraded performance) |
|
||
| Lock Timeout | Exponential Backoff | 5 | HTTP 503 after final retry |
|
||
| Version Conflict | Immediate Retry | 2 | HTTP 409 after final retry |
|
||
| DB Connection Error | Exponential Backoff | 3 | HTTP 500 after final retry |
|
||
|
||
### 3.11.13.2 User Experience
|
||
|
||
**Requirements:**
|
||
|
||
- Error messages **ต้อง**เป็นภาษาไทย และเข้าใจง่าย
|
||
- HTTP status codes **ต้อง**สื่อความหมายที่ถูกต้อง
|
||
- Frontend **ควร**แสดง retry option สำหรับ transient errors
|
||
|
||
---
|
||
|
||
## 3.11.14 Configuration Management Requirements
|
||
|
||
### 3.11.14.1 Template Management
|
||
|
||
**Requirements:**
|
||
|
||
- Project Admin **ต้อง**สามารถกำหนด/แก้ไข template ผ่าน Admin Panel
|
||
- ระบบ**ต้อง**validate template ก่อนบันทึก
|
||
- การเปลี่ยนแปลง template **ต้องไม่**ส่งผลต่อเอกสารที่สร้างไว้แล้ว
|
||
|
||
### 3.11.14.2 Template Versioning
|
||
|
||
**Requirements:**
|
||
|
||
- ระบบ**ต้อง**เก็บ history ของ template changes
|
||
- ระบบ**ต้อง**บันทึก user, timestamp, และเหตุผลในการเปลี่ยนแปลง
|
||
- ระบบ**ควร**สามารถ rollback ไปเวอร์ชันก่อนหน้าได้
|
||
|
||
### 3.11.14.3 Counter Reset Policy
|
||
|
||
**Requirements:**
|
||
|
||
- Counter **ต้อง**reset ตามปี (อัตโนมัติ)
|
||
- Admin **ต้อง**สามารถ manual reset counter ได้ (require approval + audit log)
|
||
|
||
---
|
||
|
||
## 3.11.15 Audit Trail Requirements
|
||
|
||
### 3.11.15.1 Audit Logging
|
||
|
||
**Requirements:**
|
||
|
||
ระบบ**ต้อง**บันทึกข้อมูลต่อไปนี้สำหรับทุก document number generation:
|
||
|
||
- `document_id` - เอกสารที่ถูกสร้าง
|
||
- `generated_number` - เลขที่ถูกสร้าง
|
||
- `counter_key` - key ที่ใช้ในการนับ (JSON format)
|
||
- `template_used` - template ที่ใช้
|
||
- `user_id` - ผู้ที่ request
|
||
- `ip_address` - IP address ของผู้ request
|
||
- `timestamp` - เวลาที่สร้าง
|
||
- `retry_count` - จำนวนครั้งที่ retry
|
||
- `performance_metrics` - Lock wait time, total duration
|
||
|
||
### 3.11.15.2 Error Logging
|
||
|
||
**Requirements:**
|
||
|
||
- ระบบ**ต้อง**บันทึก error แยกต่างหาก พร้อม error type classification
|
||
- ระบบ**ควร**alert ops team สำหรับ critical errors
|
||
|
||
### 3.11.15.3 Retention Policy
|
||
|
||
**Requirements:**
|
||
|
||
- Audit log **ต้อง**เก็บอย่างน้อย **7 ปี** (ตาม พ.ร.บ. ข้อมูลอิเล็กทรอนิกส์)
|
||
|
||
---
|
||
|
||
## 3.11.16 Performance Requirements
|
||
|
||
### 3.11.16.1 Response Time
|
||
|
||
**SLA Targets:**
|
||
|
||
| Metric | Target | Notes |
|
||
| ----------------- | -------- | ------------------------ |
|
||
| 95th percentile | ≤ 2 วินาที | ตั้งแต่ request ถึง response |
|
||
| 99th percentile | ≤ 5 วินาที | รวม retry attempts |
|
||
| Normal operation | ≤ 500ms | ไม่มี retry |
|
||
| Number generation | < 100ms | (p95) |
|
||
| Lock acquisition | < 50ms | (avg) |
|
||
| Bulk import | < 5s | per 100 records |
|
||
|
||
### 3.11.16.2 Throughput
|
||
|
||
**Capacity Targets:**
|
||
|
||
| Load Level | Target | Notes |
|
||
| ----------- | ----------- | ------------------ |
|
||
| Normal load | ≥ 50 req/s | ใช้งานปกติ |
|
||
| Peak load | ≥ 100 req/s | ช่วงเร่งงาน |
|
||
| Burst | ≥ 200 req/s | short duration |
|
||
| Support | > 500 req/s | Scale horizontally |
|
||
|
||
### 3.11.16.3 Availability
|
||
|
||
**SLA Targets:**
|
||
|
||
- **Uptime**: ≥ 99.5% (excluding planned maintenance)
|
||
- **Maximum downtime**: ≤ 3.6 ชั่วโมง/เดือน
|
||
- **RTO**: ≤ 30 นาที
|
||
- **RPO**: ≤ 5 นาที
|
||
- **Graceful degradation**: Fallback to DB-only
|
||
- **Auto-recovery**: From Redis failure
|
||
|
||
---
|
||
|
||
## 3.11.17 Monitoring & Alerting Requirements
|
||
|
||
### 3.11.17.1 Metrics
|
||
|
||
**Requirements:**
|
||
|
||
ระบบ**ต้อง**collect metrics ต่อไปนี้:
|
||
|
||
- Lock acquisition time (p50, p95, p99)
|
||
- Lock acquisition success/failure rate
|
||
- Counter generation latency
|
||
- Retry count distribution
|
||
- Redis connection status
|
||
- Database connection pool usage
|
||
- Sequence utilization (% of max)
|
||
- Numbers generated per day
|
||
- Manual overrides per day
|
||
|
||
### 3.11.17.2 Alerts
|
||
|
||
**Requirements:**
|
||
|
||
ระบบ**ต้อง**alert สำหรับ conditions ต่อไปนี้:
|
||
|
||
| Severity | Condition | Action |
|
||
| ---------- | ---------------------------- | ----------------- |
|
||
| 🔴 Critical | Redis unavailable > 1 minute | PagerDuty + Slack |
|
||
| 🔴 Critical | Lock failures > 10% in 5 min | PagerDuty + Slack |
|
||
| 🔴 Critical | Sequence >95% used | PagerDuty + Slack |
|
||
| 🟡 Warning | Lock failures > 5% in 5 min | Slack |
|
||
| 🟡 Warning | Avg lock wait time > 1 sec | Slack |
|
||
| 🟡 Warning | Retry count > 100/hour | Slack |
|
||
| 🟡 Warning | Sequence >90% used | Slack |
|
||
| 🟡 Warning | High error rate | Slack |
|
||
|
||
### 3.11.17.3 Dashboard
|
||
|
||
**Requirements:**
|
||
|
||
- Ops team **ต้อง**มี real-time dashboard (Grafana) แสดง:
|
||
- Lock acquisition success rate
|
||
- Lock wait time percentiles
|
||
- Generation rate (per minute)
|
||
- Error rate by type
|
||
- Connection health status
|
||
- Retry distribution
|
||
|
||
---
|
||
|
||
## 3.11.18 API Reference
|
||
|
||
### Document Number Generation
|
||
|
||
```http
|
||
POST /api/v1/documents/{documentId}/generate-number
|
||
```
|
||
|
||
สร้างเลขที่เอกสารสำหรับ document ที่ระบุ
|
||
|
||
**Request Body:**
|
||
|
||
```json
|
||
{
|
||
"counterKey": {
|
||
"projectId": 2,
|
||
"originatorOrgId": 22,
|
||
"recipientOrgId": 10,
|
||
"correspondenceTypeId": 6,
|
||
"subTypeId": 0,
|
||
"rfaTypeId": 0,
|
||
"disciplineId": 0,
|
||
"resetScope": "YEAR_2025"
|
||
}
|
||
}
|
||
```
|
||
|
||
**Response:**
|
||
|
||
```json
|
||
{
|
||
"documentNumber": "คคง.-สคฉ.3-0001-2568",
|
||
"generatedAt": "2025-12-02T15:30:00Z"
|
||
}
|
||
```
|
||
|
||
### Reserve Number
|
||
|
||
```http
|
||
POST /api/document-numbering/reserve
|
||
```
|
||
|
||
**Request:**
|
||
```json
|
||
{
|
||
"document_type": "COR",
|
||
"project_id": 1,
|
||
"contract_id": null,
|
||
"metadata": {}
|
||
}
|
||
```
|
||
|
||
**Response 201:**
|
||
```json
|
||
{
|
||
"token": "uuid-v4",
|
||
"document_number": "COR-2025-00042",
|
||
"expires_at": "2025-01-16T10:30:00Z"
|
||
}
|
||
```
|
||
|
||
### Confirm Reservation
|
||
|
||
```http
|
||
POST /api/document-numbering/confirm
|
||
```
|
||
|
||
**Request:**
|
||
```json
|
||
{
|
||
"token": "uuid-v4"
|
||
}
|
||
```
|
||
|
||
**Response 200:**
|
||
```json
|
||
{
|
||
"document_number": "COR-2025-00042",
|
||
"confirmed_at": "2025-01-16T10:25:00Z"
|
||
}
|
||
```
|
||
|
||
### Manual Override
|
||
|
||
```http
|
||
POST /api/document-numbering/manual
|
||
Authorization: Bearer <admin-token>
|
||
```
|
||
|
||
**Request:**
|
||
```json
|
||
{
|
||
"document_type": "COR",
|
||
"document_number": "COR-2024-99999",
|
||
"reason": "Import from legacy system",
|
||
"skip_validation": false
|
||
}
|
||
```
|
||
|
||
**Response 201:**
|
||
```json
|
||
{
|
||
"document_number": "COR-2024-99999",
|
||
"is_manual": true,
|
||
"created_at": "2025-01-16T10:25:00Z"
|
||
}
|
||
```
|
||
|
||
### Template Management
|
||
|
||
```http
|
||
GET /api/v1/document-numbering/configs
|
||
```
|
||
|
||
ดูรายการ template configuration ทั้งหมด
|
||
|
||
```http
|
||
PUT /api/v1/document-numbering/configs/{configId}
|
||
```
|
||
|
||
แก้ไข template (Project Admin only)
|
||
|
||
```http
|
||
POST /api/v1/document-numbering/configs/{configId}/reset-counter
|
||
```
|
||
|
||
Reset counter (Super Admin only, requires approval)
|
||
|
||
---
|
||
|
||
## 3.11.19 Testing Requirements
|
||
|
||
### 3.11.19.1 Unit Tests
|
||
- Format parsing and validation
|
||
- Sequence increment logic
|
||
- Manual override validation
|
||
- Scope resolution
|
||
|
||
### 3.11.19.2 Integration Tests
|
||
- Redis locking mechanism
|
||
- Database transactions
|
||
- Two-phase commit flow
|
||
- Bulk import
|
||
|
||
### 3.11.19.3 Load Tests
|
||
|
||
```bash
|
||
# Must pass without duplicates
|
||
concurrent_users: 100
|
||
requests_per_second: 500
|
||
test_duration: 5 minutes
|
||
expected_duplicates: 0
|
||
```
|
||
|
||
- Concurrent number generation (1000 req/s)
|
||
- Lock contention under load
|
||
- Redis failover scenarios
|
||
- Database connection pool exhaustion
|
||
|
||
### 3.11.19.4 E2E Tests
|
||
- Complete document creation flow
|
||
- Void and replace workflow
|
||
- Bulk import with validation
|
||
- Admin configuration UI
|
||
|
||
---
|
||
|
||
## 3.11.20 Versioning Note
|
||
|
||
* Existing documents **not affected**
|
||
* New rules apply to documents generated after upgrade to v1.6.2
|
||
|
||
---
|
||
|
||
## 3.11.21 Migration Plan
|
||
|
||
### 3.11.21.1 Legacy Data Import
|
||
1. Export existing document numbers from old system
|
||
2. Validate format and detect duplicates
|
||
3. Bulk import using manual override API
|
||
4. Update sequence counters to max values
|
||
5. Verify data integrity
|
||
|
||
### 3.11.21.2 Rollout Strategy
|
||
- Week 1-2: Deploy to staging, test with dummy data
|
||
- Week 3: Deploy to production, enable for test project
|
||
- Week 4: Enable for all projects
|
||
- Week 5+: Monitor and optimize
|
||
|
||
---
|
||
|
||
## 3.11.22 Success Criteria
|
||
|
||
### 3.11.22.1 Functional Success
|
||
- ✅ All FRs implemented and tested
|
||
- ✅ Zero duplicate numbers in production
|
||
- ✅ Migration of 50,000+ legacy documents
|
||
- ✅ UAT approved by stakeholders
|
||
|
||
### 3.11.22.2 Performance Success
|
||
- ✅ Response time <100ms (p95)
|
||
- ✅ Throughput >500 req/s
|
||
- ✅ Lock acquisition <50ms (avg)
|
||
- ✅ Zero downtime during deployment
|
||
|
||
### 3.11.22.3 Business Success
|
||
- ✅ Document creation speed +30%
|
||
- ✅ Manual numbering errors -80%
|
||
- ✅ User satisfaction >4.5/5
|
||
- ✅ System stability >99.9%
|
||
|
||
---
|
||
|
||
## 3.11.23 References
|
||
|
||
- [Implementation Guide](../03-implementation/03-04-document-numbering.md)
|
||
- [Operations Guide](../04-operations/04-08-document-numbering-operations.md)
|
||
- [API Design](../02-architecture/02-02-api-design.md)
|
||
- [Data Dictionary](file:///d:/nap-dms.lcbp3/specs/04-data-dictionary/4_Data_Dictionary_V1_4_4.md)
|
||
- [ADR-018: Document Numbering Strategy](file:///d:/nap-dms.lcbp3/specs/05-decisions/adr-018-document-numbering.md)
|
||
- [Two-Phase Commit Pattern](https://en.wikipedia.org/wiki/Two-phase_commit_protocol)
|
||
- [Redlock Algorithm](https://redis.io/docs/manual/patterns/distributed-locks/)
|
||
|
||
---
|
||
|
||
## 3.11.24 Appendix
|
||
|
||
### 3.11.24.1 Glossary
|
||
- **Sequence**: ลำดับตัวเลขที่เพิ่มขึ้นอัตโนมัติ
|
||
- **Scope**: ขอบเขตที่ sequence แยกตาม (project, contract, etc.)
|
||
- **Token**: Format placeholder (e.g., {YYYY}, {SEQ})
|
||
- **Redlock**: Distributed locking algorithm สำหรับ Redis
|
||
|
||
---
|
||
|
||
**Approval Sign-off**:
|
||
|
||
| Role | Name | Date | Signature |
|
||
| ------------- | ----------- | ------- | --------- |
|
||
| Product Owner | ___________ | _______ | _________ |
|
||
| Tech Lead | ___________ | _______ | _________ |
|
||
| QA Lead | ___________ | _______ | _________ |
|
||
|
||
**End of Document v1.6.2**
|