251202:1700 Prepare to version 1.6

This commit is contained in:
admin
2025-12-02 17:29:42 +07:00
parent 5acc631994
commit 9dcdba0bb3
3 changed files with 407 additions and 77 deletions

View File

@@ -42,14 +42,14 @@ VALUES (1, 'กทท.', 'การท่าเรือแห่งประเ
'สคฉ.3-xx', 'สคฉ.3-xx',
'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4' 'ตรวจรับพัสดุ ที่ปรึกษาออกแบบ ส่วนที่ 4'
), ),
(21, 'TEAM', 'Designer Consulting Ltd.'), (21, 'TEAM', 'Designer Consulting Ltd.'),
(22, 'คคง.', 'Construction Supervision Ltd.'), (22, 'คคง.', 'Construction Supervision Ltd.'),
(41, 'ผรม.1', 'Contractor งานทางทะเล'), (41, 'ผรม.1', 'Contractor งานทางทะเล'),
(42, 'ผรม.2', 'Contractor อาคารและระบบ'), (42, 'ผรม.2', 'Contractor อาคารและระบบ'),
(43, 'ผรม.3', 'Contractor #3 Ltd.'), (43, 'ผรม.3', 'Contractor #3 Ltd.'),
(44, 'ผรม.4', 'Contractor #4 Ltd.'), (44, 'ผรม.4', 'Contractor #4 Ltd.'),
(31, 'EN', 'Third Party Environment'), (31, 'EN', 'Third Party Environment'),
(32, 'CAR', 'Third Party Fishery Care'); (32, 'CAR', 'Third Party Fishery Care');
-- Seed project -- Seed project
INSERT INTO projects (project_code, project_name) INSERT INTO projects (project_code, project_name)
VALUES ( VALUES (
@@ -156,7 +156,7 @@ VALUES (
); );
-- Seed user -- Seed user
-- Initial SUPER_ADMIN user -- Initial SUPER_ADMIN user
INSERT INTO `users` ( INSERT INTO users (
`user_id`, `user_id`,
`username`, `username`,
`password_hash`, `password_hash`,

View File

@@ -2,8 +2,8 @@
--- ---
title: 'Functional Requirements: Document Numbering Management' title: 'Functional Requirements: Document Numbering Management'
version: 1.5.0 version: 1.6.0
status: first-draft status: draft
owner: Nattanin Peancharoen owner: Nattanin Peancharoen
last_updated: 2025-12-02 last_updated: 2025-12-02
related: related:
@@ -22,104 +22,227 @@ related:
## 3.11.2. Logic การนับเลข (Counter Logic) ## 3.11.2. Logic การนับเลข (Counter Logic)
การนับเลขจะแยกตาม **Counter Key** ที่ประกอบด้วย: การนับเลขจะแยกตาม **Counter Key** ที่ประกอบด้วยหลายส่วน ขึ้นกับประเภทเอกสาร
- `project_id` - รหัสโครงการ ### Counter Key Components
- `doc_type_id` - ชนิดเอกสาร (Correspondence, RFA, Transmittal, Drawing)
- `sub_type_id` - ประเภทย่อยของเอกสาร (nullable)
- `discipline_id` - สาขาวิชา/งาน (nullable)
- `year` - ปี พ.ศ. หรือ ค.ศ. ตามที่กำหนดใน template
### ตัวอย่าง Counter Key | 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 (ปัจจุบัน) | - |
```text ### Counter Key แยกตามประเภทเอกสาร
Correspondence: project_id + doc_type_id + sub_type_id + year
RFA: project_id + doc_type_id + discipline_id + year **LETTER / RFI / MEMO / EMAIL / MOM / INSTRUCTION / NOTICE / OTHER**:
Transmittal: project_id + doc_type_id + recipient_type + year
Drawing: project_id + doc_type_id + discipline_id + year
``` ```
(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 ### Fallback สำหรับค่า NULL
- กรณีที่ `discipline_id` หรือ `sub_type_id` เป็น NULL ให้ใช้ค่า Default `0` ในการจัดกลุ่ม Counter - `discipline_id`: ใช้ `0` (ไม่ระบุสาขางาน)
- ป้องกัน Error และรับประกันความถูกต้องของ Running Number (Uniqueness Guarantee) - `sub_type_id`: ใช้ `0` (ไม่มีประเภทย่อย)
- `rfa_type_id`: ใช้ `0` (ไม่ระบุประเภท RFA)
- `recipient_organization_id`: ใช้ `NULL` สำหรับ RFA, Required สำหรับ LETTER/TRANSMITTAL
## 3.11.3. Format Templates by Document Type
ระบบรองรับการกำหนดรูปแบบด้วย **Token Replacement** ## 3.11.3. Format Templates by Correspondence Type
### 3.11.3.1. Correspondence (หนังสือราชการ) > **📝 หมายเหตุสำคัญ**
> - Templates ด้านล่างเป็น **ตัวอย่าง** สำหรับประเภทเอกสารหลัก
> - ระบบรองรับ **ทุกประเภทเอกสาร** ที่อยู่ใน `correspondence_types` table
> - หากมีการเพิ่มประเภทใหม่ในอนาคต สามารถใช้งานได้โดยอัตโนมัติ
> - Admin สามารถกำหนด Template เฉพาะสำหรับแต่ละประเภทผ่าน Admin Panel
#### Letter Type (TYPE = 03) ### 3.11.3.1. Letter (TYPE = LETTER)
- **Template**: `{ORG}-{ORG}-{TYPE}-{SEQ:4}-{YEAR:B.E.}` **Template**:
- **Example**: `คคง.-สคฉ.3-0985-2568` ```
- **Counter Key**: `project_id + doc_type_id + sub_type_id + year` {ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}
```
#### Other Correspondence Types **Example**: `คคง.-สคฉ.3-0001-2568`
- **Template**: `{ORG}-{ORG}-{TYPE}-{SEQ:4}-{YEAR:B.E.}` **Token Breakdown**:
- **Example**: `คคง.-สคฉ.3-STR-0001-2568` - `คคง.` = {ORIGINATOR} = รหัสองค์กรผู้ส่ง
- **Counter Key**: `project_id + doc_type_id + sub_type_id + year` - `สคฉ.3` = {RECIPIENT} = รหัสองค์กรผู้รับหลัก (TO)
- `0001` = {SEQ:4} = Running number (เริ่ม 0001, 0002, ...)
- `2568` = {YEAR:B.E.} = ปี พ.ศ.
### 3.11.3.2. Transmittal > **⚠️ Template vs Counter Separation**
> - {CORR_TYPE} **ไม่แสดง**ใน template เพื่อความกระชับ
> - แต่ระบบ**ยังใช้ correspondence_type_id ใน Counter Key** เพื่อแยก counter
> - LETTER, MEMO, RFI **มี counter แยกกัน** แม้ template format เหมือนกัน
#### Transmittal to Owner **Counter Key**: `(project_id, originator_org_id, recipient_org_id, corr_type_id, 0, 0, 0, year)`
- **Template**: `{ORG}-{ORG}-{TYPE}-{SUB_TYPE}-{SEQ:4}-{YEAR:B.E.}` ---
- **Example**: `คคง.-สคฉ.3-03-21-0117-2568`
- **Counter Key**: `project_id + doc_type_id + recipient_type + year`
- **Note**: `recipient_type = 'OWNER'`
#### Transmittal to Contractor/Others ### 3.11.3.2. Transmittal (TYPE = TRANSMITTAL)
- **Template**: `{ORG}-{ORG}-{TYPE}-{SEQ:4}-{YEAR:B.E.}` **Template**:
- **Example**: `ผรม.2-คคง.-0117-2568` ```
- **Counter Key**: `project_id + doc_type_id + recipient_type + year` {ORIGINATOR}-{RECIPIENT}-{SUB_TYPE}-{SEQ:4}-{YEAR:B.E.}
- **Note**: `recipient_type = 'CONTRACTOR' | 'CONSULTANT' | 'OTHER'` ```
#### Alternative Project-based Format **Example**: `คคง.-สคฉ.3-21-0117-2568`
- **Template**: `{PROJECT}-{ORG}-{TYPE}-{DISCIPLINE}-{SEQ:4}-{REV}` **Token Breakdown**:
- **Example**: `LCBP3-TR-STR-0001-A` - `คคง.` = {ORIGINATOR}
- **Counter Key**: `project_id + doc_type_id + discipline_id + year` - `สคฉ.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) ### 3.11.3.3. RFA (Request for Approval)
- **Template**: `{PROJECT}-{ORG}-{TYPE}-{DISCIPLINE}-{SEQ:4}-{REV}` **Template**:
- **Example**: `LCBP3-C2-RFI-ROW-0029-A` ```
- **Counter Key**: `project_id + doc_type_id + discipline_id + year` {PROJECT}-{CORR_TYPE}-{DISCIPLINE}-{RFA_TYPE}-{SEQ:4}-{REV}
- **Note**: `{REV}` คือ revision code (A, B, C, ..., AA, AB, ...) ```
**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 ### 3.11.3.4. Drawing
- **Template**: `{PROJECT}-{DISCIPLINE}-{CATEGORY}-{SEQ:4}-{REV}` **Status**: 🚧 **To Be Determined**
- **Example**: `LCBP3-STR-DRW-0001-A`
- **Counter Key**: `project_id + doc_type_id + discipline_id + category + year` 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 ## 3.11.4. Supported Token Types
| Token | Description | Example | | Token | Description | Example | Database Source |
|-------|-------------|---------| |-------|-------------|---------|-----------------|
| `{PROJECT}` | รหัสโครงการ | `LCBP3` | | `{PROJECT}` | รหัสโครงการ | `LCBP3`, `LCBP3-C2` | `projects.project_code` |
| `{ORG}` | รหัสหน่วยงาน | `คคง.`, `สคฉ.3` | | `{ORIGINATOR}` | รหัสองค์กรผู้ส่ง | `คคง.`, `ผรม.1` | `organizations.organization_code` via `correspondences.originator_id` |
| `{TYPE}` | รหัสชนิดเอกสาร | `RFI`, `03` | | `{RECIPIENT}` | รหัสองค์กรผู้รับหลัก (TO) | `สคฉ.3`, `กทท.` | `organizations.organization_code` via `correspondence_recipients` where `recipient_type = 'TO'` |
| `{SUB_TYPE}` | รหัสประเภทย่อย | `21` | | `{CORR_TYPE}` | รหัสประเภทเอกสาร | `RFA`, `TRANSMITTAL`, `LETTER` | `correspondence_types.type_code` |
| `{DISCIPLINE}` | รหัสสาขาวิชา | `STR`, `ROW` | | `{SUB_TYPE}` | หมายเลขประเภทย่อย | `11`, `12`, `21` | `correspondence_sub_types.sub_type_number` |
| `{CATEGORY}` | หมวดหมู่ | `DRW` | | `{RFA_TYPE}` | รหัสประเภท RFA | `SDW`, `RPT`, `MAT` | `rfa_types.type_code` |
| `{SEQ:n}` | Running number (n = จำนวนหลัก) | `0001`, `0029` | | `{DISCIPLINE}` | รหัสสาขาวิชา | `STR`, `TER`, `GEO` | `disciplines.discipline_code` |
| `{YEAR:B.E.}` | ปี พ.ศ. | `2568` | | `{SEQ:n}` | Running number (n = จำนวนหลัก) | `0001`, `0029`, `0985` | Based on `document_number_counters.last_number + 1` |
| `{YEAR:A.D.}` | ปี .ศ. | `2025` | | `{YEAR:B.E.}` | ปี .ศ. | `2568` | `document_number_counters.current_year + 543` |
| `{REV}` | Revision Code | `A`, `B`, `AA` | | `{YEAR:A.D.}` | ปี ค.ศ. | `2025` | `document_number_counters.current_year` |
| `{REV}` | Revision Code | `A`, `B`, `AA` | `correspondence_revisions.revision_label` |
## 3.11.5. Transmittal Special Logic ### Token Usage Notes
- Transmittal มีเงื่อนไขพิเศษที่เลขอาจเปลี่ยนตามผู้รับ: **{SEQ:n}**:
- **To Owner**: ใช้ format พิเศษที่มี sub_type รหัสโครงการ - `n` = จำนวนหลักที่ต้องการ (typically 4)
- **To Contractor/Others**: ใช้ format ทั่วไป - Counter **เริ่มจาก 0001** และเพิ่มทีละ 1 (0001, 0002, 0003, ...)
- Counter Key จะแยกตาม `recipient_type` เพื่อให้แต่ละประเภทมี running number อิสระ - Padding ด้วย 0 ทางซ้าย
- Reset ทุกปี (ตาม `current_year` ใน Counter Key)
## 3.11.6. กลไกความปลอดภัย (Concurrency Control) **{RECIPIENT}**:
- ใช้เฉพาะผู้รับที่มี `recipient_type = 'TO'` เท่านั้น
- ถ้ามีหลาย TO ให้ใช้คนแรก (ตาม sort order)
- **ไม่ใช้สำหรับ RFA** (RFA ไม่มี {RECIPIENT} ใน template)
**{CORR_TYPE}**:
- รองรับทุกค่าจาก `correspondence_types.type_code`
- ถ้าม<E0B8B2>การเพิ่มประเภทใหม่ จะใช้งานได้ทันที
- **แสดงใน 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 ### 3.11.6.1. Redis Distributed Lock
@@ -277,7 +400,111 @@ Drawing: project_id + doc_type_id + discipline_id + year
- `document_number_audit` - เก็บ audit trail - `document_number_audit` - เก็บ audit trail
- `documents` - เก็บ document number ที่ถูกสร้าง - `documents` - เก็บ document number ที่ถูกสร้าง
## 3.11.14. Security Considerations ## 3.11.14. Database Schema Requirements
### 3.11.14.1. Counter Table Schema
ตาราง `document_number_counters` ต้องมีโครงสร้างดังนี้:
```sql
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
```sql
-- 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
```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,
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 ### 3.11.14.1. Authorization

View File

@@ -0,0 +1,103 @@
## 3.11.15. Database Schema Requirements
### 3.11.15.1. Counter Table Schema
ตาราง `document_number_counters` ต้องมีโครงสร้างดังนี้:
```sql
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.15.2. Index Requirements
```sql
-- 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.15.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.15.4. 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,
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
);
```