docs: add design spec for post-approval central review stage

Design for auto-forwarding a fully-approved income-report request_expense
into a new lightweight review stage surfaced at income-budget-plan-01
(faculty status view) and income-budget-plan-02 (central reviewer actions:
ส่งแก้ไข/ตรวจสอบแล้ว + dashboard + correction history table).

Deliberately introduces a new income_budget_review table rather than
extending the existing approve_state round_no/resubmit mechanism, since
that's hardcoded to request_approve_type "03" (admin-preconfigured queue
per faculty/location) which conflicts with the confirmed requirement that
the central reviewer is a single role covering every faculty/location, not
a named per-unit approver.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0153wBt23EjW125ZsADwWVAs
This commit is contained in:
Nut.ไปเรื่อย
2026-09-07 17:29:39 +07:00
co-authored by Claude Sonnet 5
parent efbc4618c2
commit 5f634c1bf8
@@ -0,0 +1,114 @@
# ขั้นตอน "ตรวจสอบส่วนกลาง" หลังเชนอนุมัติคำขอเงินรายได้เสร็จสิ้น
**วันที่:** 2026-09-07
**อ้างอิง:** [[2026-09-07-approve-state-workflow-reference]] (โครงสร้าง `request_expense`/`approve_state` เดิม)
## 1. ปัญหา / เป้าหมาย
หน้า "ส่งอนุมัติคำขอเงินรายได้" (`/app/income-report`) มีเชนอนุมัติภายในคณะ/พื้นที่ตัวเอง (ผู้ส่ง →
ผู้อนุมัติ 1 → ผู้อนุมัติ 2 ...) ที่ทำงานอยู่แล้ว (ดูเอกสารอ้างอิงด้านบน) เมื่อผู้อนุมัติ**คนสุดท้าย**ของเชนนั้น
กด "อนุมัติ" (`request_expense.is_approve` เปลี่ยนเป็น `true`) ต้องการให้ระบบ:
1. ส่งข้อมูลนั้นเข้าสู่ขั้นตอน**ตรวจสอบส่วนกลาง**โดยอัตโนมัติ — ปรากฏที่เมนู
`/app/income-budget-plan-02` ("ตรวจสรุปคำของบประมาณเงินรายได้")
2. ฝั่งคณะเห็นสถานะของตัวเองที่เมนู `/app/income-budget-plan-01` ("สรุปคำของบประมาณเงินรายได้")
**ผู้ตรวจสอบ:** คนกลาง/ส่วนกลาง (เช่น กองนโยบายและแผน) คนเดียวตรวจได้ทุกคณะ/ทุกพื้นที่ — ไม่ได้ผูกกับ
รายชื่อเฉพาะเจาะจงแบบผู้อนุมัติในเชนเดิม สิทธิ์เข้าถึงอิงจากสิทธิ์เข้าเมนูตามระบบเดิม ไม่ใช่รายชื่อต่อเรื่อง
**การค้นหา/กรอง:** ที่ `income-budget-plan-02` ต้องกรองตาม **ปีงบประมาณ + หน่วยงาน/คณะ + พื้นที่**
เหมือนหน้า income-report เดิม (ยืนยันจากผู้ใช้)
## 2. ทำไมไม่ใช้กลไก `round_no` (resubmit) ที่มีอยู่แล้ว
ระบบมี "ส่งอนุมัติเพิ่มเติม" (`ResubmitGetEntity`, เพิ่ม `round_no`) อยู่แล้ว แต่ **ผูกตายตัวกับ
`request_approve_type = "03"`** ซึ่งต้องมีแอดมินตั้งคิวผู้อนุมัติล่วงหน้าเป็นรายคณะ/พื้นที่ในเมนู
"จัดการเมนูการอนุมัติ" ก่อน — ขัดกับข้อกำหนดว่าผู้ตรวจสอบเป็น "คนกลางคนเดียว ไม่ผูกคณะ/พื้นที่" และ
จะเพิ่มภาระแอดมินที่ code `"04"` เคยถูกสร้างมาเพื่อเลิกพึ่งพาอยู่แล้ว จึง **ไม่ต่อยอด `round_no` เดิม**
แต่สร้างชั้นสถานะใหม่แยกต่างหาก ผูกกับ `request_expense_uid` โดยตรง
## 3. โมเดลข้อมูลใหม่
ตารางใหม่ `income_budget_review` (1 แถวต่อ "รอบการตรวจสอบ" — มีได้หลายแถวถ้าโดนส่งแก้ไขแล้วส่งใหม่หลายรอบ
เพื่อเก็บประวัติ):
```csharp
public class income_budget_review : base_table
{
[Key] public Guid? income_budget_review_uid { get; set; }
public Guid? request_expense_uid { get; set; } // FK -> request_expense (เชนเดิมจาก income-report)
public short status_id { get; set; } // 1=รอตรวจสอบ, 2=ส่งแก้ไข, 3=ตรวจสอบแล้ว
public string remark { get; set; } // เหตุผล/รายละเอียดตอนกด "ส่งแก้ไข"
public string reviewed_by { get; set; } // login_name ผู้ตรวจ (ตอนกด ส่งแก้ไข/ตรวจสอบแล้ว)
public DateTime? reviewed_datetime { get; set; }
}
```
**Trigger สร้างแถวใหม่ (status_id=1, รอตรวจสอบ):**
ในจุดเดียวกับที่ `ApproveGetEntity` (ApproveState.cs) ตั้ง `request_expense.is_approve = true` (เคสที่ไม่มี
ลำดับถัดไปแล้ว — ดู [[2026-09-07-approve-state-workflow-reference]] ข้อ 5) ให้เพิ่ม insert
`income_budget_review` แถวใหม่ `status_id=1` **เฉพาะตอน `request_approve_type == "04"`** (ไม่กระทบ
`invest_asset`/`invest_construct`/`request_expense` code อื่นๆ ที่ใช้ engine เดียวกัน)
**สถานะปัจจุบันของเรื่องหนึ่งๆ** = แถว `income_budget_review` ที่มี `created_datetime` ล่าสุดของ
`request_expense_uid` นั้น (query `OrderByDescending(created_datetime).First()`)
## 4. Flow
```
income-report เชนอนุมัติผ่านครบ (is_approve=true)
│ (auto insert income_budget_review status_id=1)
▼
income-budget-plan-01 (ฝั่งคณะ) income-budget-plan-02 (ฝั่งส่วนกลาง)
เห็นสถานะ "รอตรวจสอบ" เห็นเรื่องนี้ใน dashboard "รอตรวจสอบ"
│ │
│ ผู้ตรวจสอบกด "ส่งแก้ไข" + พิมพ์ remark
│◄───────────────────────────────────────┘
เห็นสถานะ "ถูกส่งแก้ไข" + remark
กด "ส่งตรวจสอบใหม่" (แก้ไขข้อมูลผ่าน income-report/plan-01 ตามปกติ)
│ (insert income_budget_review row ใหม่ status_id=1)
▼
income-budget-plan-02 เห็นเรื่องนี้กลับมาที่ "รอตรวจสอบ" อีกครั้ง
│
ผู้ตรวจสอบกด "ตรวจสอบแล้ว" (status_id=3, จบกระบวนการ)
```
## 5. Backend API (ใหม่)
โมดูล `Modules/IncomeBudgetReview/` (ตามแพทเทิร์นโฟลเดอร์เดิมของโปรเจกต์):
| Endpoint | Method | หน้าที่ |
|---|---|---|
| `/api/income_budget_review/search` | GET | query `budget_year_uid`, `faculty_uid`, `budget_location_uid` → คืน `request_expense` 1 เรื่อง (ถ้ามี) + สถานะรีวิวล่าสุด + ประวัติ remark ทั้งหมด |
| `/api/income_budget_review/dashboard` | GET | นับจำนวนตามสถานะทั่วทั้งระบบ (ไม่กรองคณะ — ผู้ตรวจสอบเป็นส่วนกลาง): รอตรวจสอบ, ส่งแก้ไข(ที่ยังไม่ถูกแก้), ตรวจสอบแล้ว — พร้อม list รายละเอียด (คณะ/พื้นที่/ปีงบ) ต่อหมวด เหมือน `request_expense/type_2/my_dashboard` |
| `/api/income_budget_review/return_for_edit/{request_expense_uid}` | POST | body `{ remark }` → insert แถวใหม่ `status_id=2` |
| `/api/income_budget_review/mark_reviewed/{request_expense_uid}` | POST | insert แถวใหม่ `status_id=3` — เงื่อนไข: แถวล่าสุดต้องเป็น `status_id=1` เท่านั้น (กันกดซ้ำ/กดข้ามสถานะ) |
| `/api/income_budget_review/resubmit/{request_expense_uid}` | POST | ใช้ที่ฝั่งคณะ (plan-01) กด "ส่งตรวจสอบใหม่" หลังแก้ไข — เงื่อนไข: แถวล่าสุดต้องเป็น `status_id=2` → insert แถวใหม่ `status_id=1` |
ทุก endpoint ตรวจสอบเงื่อนไข "แถวล่าสุดของเรื่องนี้ต้องเป็นสถานะอะไร" ก่อน insert เสมอ กัน race condition/
กดปุ่มผิดจังหวะ (เช่น กด "ตรวจสอบแล้ว" ซ้ำ หรือกด "ส่งตรวจสอบใหม่" ทั้งที่ยังไม่เคยถูกส่งแก้ไข)
## 6. Frontend
**`income-budget-plan-02`** (เพิ่มเข้าไปในหน้าเดิม เหนือฟอร์มค้นหา — โครงเดียวกับ dashboard ที่ทำให้
income-report):
- การ์ด dashboard: chip กดได้ 3 อัน — "รอตรวจสอบ N", "ส่งแก้ไขไปแล้ว N" (ที่ยังไม่ถูกแก้กลับมา),
"ตรวจสอบแล้ว N" — กด chip กางลิสต์ (คณะ/พื้นที่/ปีงบ), กดแถวในลิสต์ = pre-fill ช่องค้นหาแล้วค้นหาทันที
(รูปแบบเดียวกับ `income-report-approve-dashboard.component.ts` ที่มีอยู่แล้ว — reuse โครงสร้างเดิม)
- หลังค้นหาแล้วเจอเรื่อง: แสดง **สถานะปัจจุบันชัดเจน** (badge: "รอตรวจสอบ" / "ส่งแก้ไขแล้ว รอคณะแก้ไข" /
"ตรวจสอบแล้ว") + ตาราง**ประวัติการส่งแก้ไข**ทั้งหมดของเรื่องนี้ (วันที่, remark, ผู้ตรวจ) — โครงตาราง
เดียวกับ "รายละเอียดสำหรับการส่งแก้ไข" ที่มีอยู่แล้วในหน้า income-report (`remark_history`)
- ปุ่ม "ส่งแก้ไข" (เปิด dialog ให้พิมพ์ remark ก่อนยืนยัน) และ "ตรวจสอบแล้ว" — โชว์เฉพาะตอนสถานะปัจจุบัน
เป็น "รอตรวจสอบ" เท่านั้น
**`income-budget-plan-01`** (เพิ่มเข้าไปในหน้าเดิม):
- Badge สถานะเดียวกัน ("รอตรวจสอบ" / "ถูกส่งแก้ไข" + remark ล่าสุด / "ตรวจสอบแล้ว") ต่อเรื่องที่ค้นหาอยู่
- ปุ่ม "ส่งตรวจสอบใหม่" — โชว์เฉพาะตอนสถานะเป็น "ถูกส่งแก้ไข" เท่านั้น
## 7. นอกขอบเขตรอบนี้ (YAGNI)
- แจ้งเตือนแบบ real-time/อีเมล/LINE — dashboard โหลดตอนเข้าเมนูเหมือนเดิมพอ
- สิทธิ์ผู้ตรวจสอบละเอียดกว่า "เข้าเมนูนี้ได้ = ตรวจได้" (เช่น แบ่งสิทธิ์ตามประเภทงบ) — ยังไม่มีความต้องการ
ตอนนี้ ถ้าจะเพิ่มทีหลังค่อยทำเป็นรอบใหม่
- แก้ไขข้อมูลตัวเลขจริงๆ ที่หน้า plan-01/02 — สมมติว่าคณะยังกลับไปแก้ที่ income-report/ฟอร์มเดิมตามปกติ
plan-01/02 รอบนี้ทำแค่ "แสดงสถานะ + ปุ่มเปลี่ยนสถานะ" ไม่ใช่หน้าแก้ไขตัวเลข