48 KiB
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 - รายละเอียดการ implement ด้วย NestJS, TypeORM, Redis
- Operations Guide: 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_typestable จะใช้ 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 จาก:
-
User Context (แนะนำ):
- เมื่อ User สร้างเอกสาร UI จะให้เลือก Project/Contract ก่อน
- ใช้
project_idจาก Context ที่เลือก
-
จาก Organization:
- Query
project_organizationsหรือcontract_organizations - ใช้
originator_organization_idหา project ที่เกี่ยวข้อง - ถ้ามีหลาย project ให้ User เลือก
- Query
-
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_typestable- หากมีการเพิ่มประเภทใหม่ในอนาคต สามารถใช้งานได้โดยอัตโนมัติ
- 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
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:
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:
DRAWING::<project>::<contract>
- Prevents collision with correspondence/RFA
3.11.9 Token Validation Grammar (New)
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:
- Import เอกสารเก่าจากระบบเดิม
- External documents จาก client/consultant
- 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:
- User เลือกเอกสารที่ต้องการ void
- ระบุเหตุผล (required)
- ระบบเปลี่ยน status เอกสารเดิมเป็น VOID
- สร้างเอกสารใหม่ด้วยเลขที่ใหม่
- 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
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'
- Yearly:
3.11.11.2 Index Requirements
-- 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
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
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
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
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
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)versioncolumn สำหรับ 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
-- 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
- Unique constraint บน
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- ผู้ที่ requestip_address- IP address ของผู้ requesttimestamp- เวลาที่สร้างretry_count- จำนวนครั้งที่ retryperformance_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
POST /api/v1/documents/{documentId}/generate-number
สร้างเลขที่เอกสารสำหรับ document ที่ระบุ
Request Body:
{
"counterKey": {
"projectId": 2,
"originatorOrgId": 22,
"recipientOrgId": 10,
"correspondenceTypeId": 6,
"subTypeId": 0,
"rfaTypeId": 0,
"disciplineId": 0,
"resetScope": "YEAR_2025"
}
}
Response:
{
"documentNumber": "คคง.-สคฉ.3-0001-2568",
"generatedAt": "2025-12-02T15:30:00Z"
}
Reserve Number
POST /api/document-numbering/reserve
Request:
{
"document_type": "COR",
"project_id": 1,
"contract_id": null,
"metadata": {}
}
Response 201:
{
"token": "uuid-v4",
"document_number": "COR-2025-00042",
"expires_at": "2025-01-16T10:30:00Z"
}
Confirm Reservation
POST /api/document-numbering/confirm
Request:
{
"token": "uuid-v4"
}
Response 200:
{
"document_number": "COR-2025-00042",
"confirmed_at": "2025-01-16T10:25:00Z"
}
Manual Override
POST /api/document-numbering/manual
Authorization: Bearer <admin-token>
Request:
{
"document_type": "COR",
"document_number": "COR-2024-99999",
"reason": "Import from legacy system",
"skip_validation": false
}
Response 201:
{
"document_number": "COR-2024-99999",
"is_manual": true,
"created_at": "2025-01-16T10:25:00Z"
}
Template Management
GET /api/v1/document-numbering/configs
ดูรายการ template configuration ทั้งหมด
PUT /api/v1/document-numbering/configs/{configId}
แก้ไข template (Project Admin only)
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
# 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
- Export existing document numbers from old system
- Validate format and detect duplicates
- Bulk import using manual override API
- Update sequence counters to max values
- 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
- Operations Guide
- API Design
- Data Dictionary
- ADR-018: Document Numbering Strategy
- Two-Phase Commit Pattern
- Redlock Algorithm
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