23 KiB
3.11 Document Numbering Management (การจัดการเลขที่เอกสาร)
title: 'Functional Requirements: Document Numbering Management' version: 1.6.0 status: draft owner: Nattanin Peancharoen last_updated: 2025-12-02 related:
- specs/01-requirements/01-objectives.md
- specs/01-requirements/02-architecture.md
- specs/01-requirements/03-functional-requirements.md
- specs/04-data-dictionary/4_Data_Dictionary_V1_4_4.md
3.11.1. วัตถุประสงค์
- ระบบต้องสามารถสร้างเลขที่เอกสาร (Running Number) ได้โดยอัตโนมัติและยืดหยุ่นสูง
- ระบบต้องสามารถกำหนดรูปแบบ (template) เลขที่เอกสารได้ สำหรับแต่ละโครงการ, ชนิดเอกสาร, ประเภทเอกสาร
- ระบบต้องรับประกัน Uniqueness ของเลขที่เอกสารในทุกสถานการณ์
- ระบบต้องรองรับการทำงานแบบ concurrent ได้อย่างปลอดภัย
3.11.2. Logic การนับเลข (Counter Logic)
การนับเลขจะแยกตาม Counter Key ที่ประกอบด้วยหลายส่วน ขึ้นกับประเภทเอกสาร
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' |
NULL for RFA |
correspondence_type_id |
✅ Yes | ID ประเภทเอกสาร | correspondence_types.id |
- |
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 |
current_year |
✅ Yes | ปี ค.ศ. | System year (ปัจจุบัน) | - |
Counter Key แยกตามประเภทเอกสาร
LETTER / RFI / MEMO / EMAIL / MOM / INSTRUCTION / NOTICE / OTHER:
(project_id, originator_organization_id, recipient_organization_id,
correspondence_type_id, 0, 0, 0, current_year)
หมายเหตุ: ไม่ใช้ discipline_id, sub_type_id, rfa_type_id
TRANSMITTAL:
(project_id, originator_organization_id, recipient_organization_id,
correspondence_type_id, sub_type_id, 0, 0, current_year)
หมายเหตุ: ใช้ sub_type_id เพิ่มเติม
RFA:
(project_id, originator_organization_id, NULL,
correspondence_type_id, 0, rfa_type_id, discipline_id, current_year)
หมายเหตุ: RFA ไม่ใช้ recipient_organization_id เพราะเป็นเอกสารโครงการ (CONTRACTOR → CONSULTANT → OWNER)
วิธีการหา 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
Fallback สำหรับค่า NULL
discipline_id: ใช้0(ไม่ระบุสาขางาน)sub_type_id: ใช้0(ไม่มีประเภทย่อย)rfa_type_id: ใช้0(ไม่ระบุประเภท RFA)recipient_organization_id: ใช้NULLสำหรับ RFA, Required สำหรับ LETTER/TRANSMITTAL
3.11.3. Format Templates by Correspondence Type
📝 หมายเหตุสำคัญ
- Templates ด้านล่างเป็น ตัวอย่าง สำหรับประเภทเอกสารหลัก
- ระบบรองรับ ทุกประเภทเอกสาร ที่อยู่ใน
correspondence_typestable- หากมีการเพิ่มประเภทใหม่ในอนาคต สามารถใช้งานได้โดยอัตโนมัติ
- Admin สามารถกำหนด Template เฉพาะสำหรับแต่ละประเภทผ่าน Admin Panel
3.11.3.1. Letter (TYPE = LETTER)
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, corr_type_id, 0, 0, 0, year)
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.}
⚠️ Template vs Counter Separation
- {CORR_TYPE} ไม่แสดงใน template (เหมือน LETTER)
- TRANSMITTAL มี counter แยกจาก LETTER
Counter Key: (project_id, originator_org_id, recipient_org_id, corr_type_id, sub_type_id, 0, 0, year)
3.11.3.3. RFA (Request for Approval)
Template:
{PROJECT}-{CORR_TYPE}-{DISCIPLINE}-{RFA_TYPE}-{SEQ:4}-{REV}
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, NULL, corr_type_id, 0, rfa_type_id, discipline_id, year)
3.11.3.4. Drawing
Status: 🚧 To Be Determined
Drawing Numbering ยังไม่ได้กำหนด Template เนื่องจาก:
- มีความซับซ้อนสูง (Contract Drawing และ Shop Drawing มีกฎต่างกัน)
- อาจต้องใช้ระบบ Numbering แยกต่างหาก
- ต้องพิจารณาร่วมกับ RFA ที่เกี่ยวข้อง
3.11.3.5. Other Correspondence Types
Applicable to: RFI, MEMO, EMAIL, MOM, INSTRUCTION, NOTICE, OTHER
Template:
{ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}
Example (RFI): คคง.-สคฉ.3-0042-2568
Example (MEMO): คคง.-ผรม.1-0001-2568
🔑 Counter Separation
- แม้ template format เหมือนกับ LETTER
- แต่แต่ละ type มี counter แยกกัน ผ่าน
correspondence_type_id- RFI counter ≠ MEMO counter ≠ LETTER counter
Counter Key: (project_id, originator_org_id, recipient_org_id, corr_type_id, 0, 0, 0, year)
หมายเหตุ: ทุกประเภทที่ไม่ได้ระบุเฉพาะจะใช้ Template นี้ ถ้ามีการเพิ่ม correspondence type ใหม่ใน correspondence_types table จะใช้ Template นี้โดยอัตโนมัติ
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 |
document_number_counters.current_year + 543 |
{YEAR:A.D.} |
ปี ค.ศ. | 2025 |
document_number_counters.current_year |
{REV} |
Revision Code | A, B, AA |
correspondence_revisions.revision_label |
Token Usage Notes
{SEQ:n}:
n= จำนวนหลักที่ต้องการ (typically 4)- Counter เริ่มจาก 0001 และเพิ่มทีละ 1 (0001, 0002, 0003, ...)
- Padding ด้วย 0 ทางซ้าย
- Reset ทุกปี (ตาม
current_yearใน Counter Key)
{RECIPIENT}:
- ใช้เฉพาะผู้รับที่มี
recipient_type = 'TO'เท่านั้น - ถ้ามีหลาย TO ให้ใช้คนแรก (ตาม sort order)
- ไม่ใช้สำหรับ RFA (RFA ไม่มี {RECIPIENT} ใน template)
{CORR_TYPE}:
- รองรับทุกค่าจาก
correspondence_types.type_code - ถ้าม<EFBFBD>การเพิ่มประเภทใหม่ จะใช้งานได้ทันที
- แสดงใน template: RFA only
- ไม่แสดงแต่ใช้ใน counter: LETTER, TRANSMITTAL, และ Other types
Deprecated Tokens (ไม่ควรใช้):
→ ใช้{ORG}{ORIGINATOR}หรือ{RECIPIENT}แทน→ ใช้{TYPE}{CORR_TYPE},{SUB_TYPE}, หรือ{RFA_TYPE}แทน (ตามบริบท)→ ไม่ได้ใช้งานในระบบปัจจุบัน{CATEGORY}
3.11.5. กลไกความปลอดภัย (Concurrency Control)
3.11.6.1. Redis Distributed Lock
- ใช้ Redis Distributed Lock เพื่อป้องกัน race condition
- Lock key format:
lock:docnum:{project_id}:{doc_type_id}:{...counter_key_parts} - Lock TTL: 5 วินาที (auto-release เมื่อ timeout)
- Lock acquisition timeout: 10 วินาที
3.11.6.2. Optimistic Locking
- ใช้
versioncolumn ในตารางdocument_number_configs - ตรวจสอบ version ก่อน update counter
- หาก version conflict เกิดขึ้น → retry transaction
3.11.6.3. Database Constraints
- Unique constraint บน
document_numbercolumn - Foreign key constraints เพื่อความสัมพันธ์ข้อมูล
- Check constraints สำหรับ business rules
3.11.7. Retry Mechanism & Error Handling
3.11.7.1. Scenario 1: Redis Unavailable
- Fallback: ใช้ database-only locking (pessimistic lock)
- Action:
- ใช้
SELECT ... FOR UPDATEแทน Redis lock - Log warning พร้อม alert ops team
- ระบบยังใช้งานได้แต่ performance ลดลง
- ใช้
3.11.7.2. Scenario 2: Lock Acquisition Timeout
- Retry: 5 ครั้งด้วย exponential backoff
- Attempt 1: wait 1s
- Attempt 2: wait 2s
- Attempt 3: wait 4s
- Attempt 4: wait 8s
- Attempt 5: wait 16s (รวม ~31 วินาที)
- Failure: Return HTTP 503 "Service Temporarily Unavailable"
- Frontend: แสดงข้อความ "ระบบกำลังยุ่ง กรุณาลองใหม่ภายหลัง"
3.11.7.3. Scenario 3: Version Conflict After Lock
- Retry: 2 ครั้ง (reload counter + retry transaction)
- Failure: Log error พร้อม context และ return HTTP 409 Conflict
- Frontend: แสดงข้อความ "เลขที่เอกสารถูกเปลี่ยน กรุณาลองใหม่"
3.11.7.4. Scenario 4: Database Connection Error
- Retry: 3 ครั้งด้วย exponential backoff (1s, 2s, 4s)
- Failure: Return HTTP 500 "Internal Server Error"
- Frontend: แสดงข้อความ "เกิดข้อผิดพลาดในระบบ กรุณาติดต่อผู้ดูแลระบบ"
3.11.8. Configuration Management
3.11.8.1. Admin Panel Configuration
- Project Admin สามารถกำหนด/แก้ไข template ผ่าน Admin Panel
- การเปลี่ยนแปลง template จะไม่ส่งผลต่อเอกสารที่สร้างไว้แล้ว
- ต้องมีการ validate template ก่อนบันทึก (ตรวจสอบ token ที่ใช้ถูกต้อง)
3.11.8.2. Template Versioning
- เก็บ history ของ template changes
- บันทึก user, timestamp, และเหตุผลในการเปลี่ยนแปลง
- สามารถ rollback ไปเวอร์ชันก่อนหน้าได้
3.11.8.3. Counter Reset Policy
- Counter reset ตามปี (yearly reset)
- Counter reset ตาม project phase (optional)
- Admin สามารถ manual reset counter ได้ (require approval + audit log)
3.11.9. Audit Trail
3.11.9.1. การบันทึก Audit Log
บันทึกทุกการ generate เลขที่เอกสารใน document_number_audit table:
document_id- เอกสารที่ถูกสร้างgenerated_number- เลขที่ถูกสร้างcounter_key- key ที่ใช้ในการนับtemplate_used- template ที่ใช้user_id- ผู้ที่ requestip_address- IP address ของผู้ requesttimestamp- เวลาที่สร้างretry_count- จำนวนครั้งที่ retry (ถ้ามี)
3.11.9.2. Conflict & Error Logging
- บันทึก version conflicts และ กลไก retry ที่ใช้
- บันทึก lock timeouts และ failure reasons
- บันทึก fallback scenarios (เช่น Redis unavailable)
3.11.10. Performance Requirements
3.11.10.1. Response Time
- Document number generation ต้องเสร็จภายใน 2 วินาที (95th percentile)
- Document number generation ต้องเสร็จภายใน 5 วินาที (99th percentile)
- ในกรณี normal operation (ไม่มี retry) ควรเสร็จภายใน 500ms
3.11.10.2. Throughput
- ระบบรองรับ concurrent requests อย่างน้อย 50 requests/second
- Peak load รองรับได้ถึง 100 requests/second (ช่วงเวลาเร่งงาน)
3.11.10.3. Availability
- Uptime ≥ 99.5% (exclude planned maintenance)
- Maximum downtime ต่อเดือน ≤ 3.6 ชั่วโมง
3.11.11. Monitoring & Alerting
3.11.11.1. Metrics to Monitor
- Lock acquisition time (p50, p95, p99)
- Lock acquisition failure rate
- Counter generation latency
- Retry count distribution
- Redis connection status
- Database connection pool usage
3.11.11.2. Alert Conditions
- 🔴 Critical: Redis unavailable > 1 minute
- 🔴 Critical: Lock acquisition failures > 10% in 5 minutes
- 🟡 Warning: Lock acquisition failures > 5% in 5 minutes
- 🟡 Warning: Average lock wait time > 1 second
- 🟡 Warning: Retry count > 100 per hour
3.11.11.3. Dashboard
- Real-time lock acquisition success rate
- Lock wait time percentiles (p50, p95, p99)
- Counter generation rate (per minute)
- Error rate breakdown (by error type)
- Redis/Database health status
3.11.12. API Reference
เอกสารนี้อ้างอิงถึง API endpoints ต่อไปนี้ (รายละเอียดใน specs/02-architecture/api-design.md):
POST /api/v1/documents/{documentId}/generate-number- สร้างเลขที่เอกสารGET /api/v1/document-numbering/configs- ดูการตั้งค่า templatePUT /api/v1/document-numbering/configs/{configId}- แก้ไข template (Admin only)POST /api/v1/document-numbering/configs/{configId}/reset-counter- Reset counter (Admin only)
3.11.13. Database Schema Reference
เอกสารนี้อ้างอิงถึง tables ต่อไปนี้ (รายละเอียดใน specs/04-data-dictionary/4_Data_Dictionary_V1_4_4.md):
document_number_configs- เก็บ template และ counter configurationdocument_number_counters- เก็บ current counter valuedocument_number_audit- เก็บ audit traildocuments- เก็บ document number ที่ถูกสร้าง
3.11.14. Database Schema Requirements
3.11.14.1. Counter Table Schema
ตาราง document_number_counters ต้องมีโครงสร้างดังนี้:
CREATE TABLE document_number_counters (
project_id INT NOT NULL,
originator_organization_id INT NOT NULL,
recipient_organization_id INT NULL, -- NULL for RFA
correspondence_type_id INT NOT NULL,
sub_type_id INT DEFAULT 0, -- for TRANSMITTAL
rfa_type_id INT DEFAULT 0, -- for RFA
discipline_id INT DEFAULT 0, -- for RFA
current_year INT NOT NULL,
version INT DEFAULT 0 NOT NULL, -- Optimistic Lock
last_number INT DEFAULT 0,
PRIMARY KEY (
project_id,
originator_organization_id,
COALESCE(recipient_organization_id, 0),
correspondence_type_id,
sub_type_id,
rfa_type_id,
discipline_id,
current_year
),
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
FOREIGN KEY (originator_organization_id) REFERENCES organizations(id) ON DELETE CASCADE,
FOREIGN KEY (recipient_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';
3.11.14.2. Index Requirements
-- Index สำหรับ Performance
CREATE INDEX idx_counter_lookup
ON document_number_counters (
project_id,
correspondence_type_id,
current_year
);
-- Index สำหรับ Originator lookup
CREATE INDEX idx_counter_org
ON document_number_counters (
originator_organization_id,
current_year
);
3.11.14.3. Important Notes
💡 Counter Key Design
- ใช้
COALESCE(recipient_organization_id, 0)ใน Primary Key เพื่อรองรับ NULLversioncolumn สำหรับ Optimistic Locking (ป้องกัน race condition)last_numberเริ่มจาก 0 และเพิ่มขึ้นทีละ 1- Counter reset ทุกปี (เมื่อ
current_yearเปลี่ยน)
⚠️ Migration Notes
- ไม่มีข้อมูลเก่า ไม่ต้องทำ backward compatibility
- สามารถสร้าง table ใหม่ได้เลยตาม schema ข้างต้น
- ต้องมี seed data สำหรับ
correspondence_types,rfa_types,disciplinesก่อน
3.11.14.4. 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,
current_year, version, last_number
) VALUES (
2, -- LCBP3-C2
22, -- คคง.
10, -- สคฉ.3
6, -- LETTER
0, 0, 0,
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,
current_year, version, last_number
) VALUES (
2, -- LCBP3-C2
42, -- ผรม.2
NULL, -- RFA ไม่มี specific recipient
1, -- RFA
0,
18, -- RPT (Report)
5, -- TER (Terminal)
2025, 0, 0
);
3.11.15. Security Considerations
3.11.14.1. Authorization
- เฉพาะ authenticated users เท่านั้นที่สามารถ request document number
- เฉพาะ Project Admin เท่านั้นที่แก้ไข template ได้
- เฉพาะ Super Admin เท่านั้นที่ reset counter ได้
3.11.14.2. Rate Limiting
- Limit ต่อ user: 10 requests/minute (prevent abuse)
- Limit ต่อ IP: 50 requests/minute
3.11.14.3. Audit & Compliance
- บันทึกทุก API call ที่เกี่ยวข้องกับ document numbering
- เก็บ audit log อย่างน้อย 7 ปี (ตาม พ.ร.บ. ข้อมูลอิเล็กทรอนิกส์)