Files
lcbp3/specs/01-requirements/03.11-document-numbering.md
admin 79344ef4b1
Some checks failed
Spec Validation / validate-markdown (push) Has been cancelled
Spec Validation / validate-diagrams (push) Has been cancelled
Spec Validation / check-todos (push) Has been cancelled
251202:1700 Prepare to version 1.6
2025-12-02 17:29:42 +07:00

23 KiB
Raw Blame History

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 จาก:

  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

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_types table
  • หากมีการเพิ่มประเภทใหม่ในอนาคต สามารถใช้งานได้โดยอัตโนมัติ
  • 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

  • ใช้ version column ในตาราง document_number_configs
  • ตรวจสอบ version ก่อน update counter
  • หาก version conflict เกิดขึ้น → retry transaction

3.11.6.3. Database Constraints

  • Unique constraint บน document_number column
  • 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 - ผู้ที่ request
  • ip_address - IP address ของผู้ request
  • timestamp - เวลาที่สร้าง
  • 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 - ดูการตั้งค่า template
  • PUT /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 configuration
  • document_number_counters - เก็บ current counter value
  • document_number_audit - เก็บ audit trail
  • documents - เก็บ 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 เพื่อรองรับ NULL
  • version column สำหรับ 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 ปี (ตาม พ.ร.บ. ข้อมูลอิเล็กทรอนิกส์)