```
- [ ] **Step 3: Fix the `keyUrl==3` "only fully-approved" filter**
In `request-budget.container.ts`, replace the entire `if(this.keyUrl == 3){...}` block (the one using `state.display_name_th === 'พนักงานทั่วไป'`) with:
```typescript
if(this.keyUrl == 3){
this.RequestExpenseSV.queryString(`?budget_year_uid=${this.searchInput.budget_year_uid}&faculty_uid=${this.searchInput.faculty_uid}&budget_location_uid=${this.searchInput.budget_location_uid}`).pipe(
tap((x:any)=>{
const fullyApproved = (x || []).find((item: any) => item.is_approve === true)
if (!fullyApproved) {
this.is_general_staff_approved = false
this.approve_button = null
this.requestBudget = []
this.requestBudgetType2 = []
this.InvestAssetRequest = []
this.InvestConstructRequest = []
this.BudgetProgress = []
this.BudgetProgressType3 = []
Swal.fire({
icon: 'info',
title: 'ยังไม่พร้อมตรวจสอบ',
text: 'คำขอนี้ต้องผ่านการอนุมัติครบทุกลำดับก่อนจึงจะแสดงในหน้านี้ได้',
confirmButtonText: 'ตกลง'
});
this.cdRef.detectChanges()
return;
}
this.is_general_staff_approved = true
this.getApprove([fullyApproved])
}),
).subscribe()
}
```
This directly satisfies "ที่เมนูนี้จะต้องดึงข้อมูลที่ผู้อนุมัติลำดับสุดท้ายอนุมัติมาแล้วเท่านั้น ไม่มีสถานะอื่นปะปน" — the only condition checked is the entity's own `is_approve === true` (set exactly once, by `ApproveGetEntity`, only when there is no next `row_order` to advance to), replacing the old fragile approver-name string match.
- [ ] **Step 4: Type-check**
Run: `cd rmutr-web && npx tsc -p tsconfig.app.json --noEmit`
Expected: no errors. If `SetApproversDialogComponent`'s constructor `data` shape doesn't match what's passed here, read `src/app/shared/components/set-approvers-dialog/set-approvers-dialog.component.ts` and adjust the `data: { existing }` payload to match its actual `@Inject(MAT_DIALOG_DATA)` shape exactly (confirmed during research to seed 2 fixed rows from `existing[0]`/`existing[1]` — the payload key might be `data.existing` or something else; verify against the live file, don't guess).
- [ ] **Step 5: Manual browser verification**
Run the app locally (`npm start`), then:
1. Navigate to `/app/request-budget`, pick a budget year + faculty + location that has no prior `type_id=4` envelope.
2. Confirm a "ระบุผู้อนุมัติ" button appears; click it, pick 1-2 approvers, save — confirm success toast and that "ส่งอนุมัติ" now appears.
3. Click "ส่งอนุมัติ" — confirm the "รอ [ชื่อ] อนุมัติ" message appears.
4. Log in as that approver (or use their token), call `approve()` on the item — confirm the item appears at `/app/request-budget-plan-01` with the "ตรวจสอบแล้ว"/"ส่งแก้ไข" buttons visible and the dashboard chip count incrementing.
5. Click "ส่งแก้ไข", try submitting with an empty reason — confirm it's blocked; fill in a reason — confirm success, and that the item disappears from `request-budget-plan-01`'s "only fully-approved" view (since `is_approve` flips back to `false`).
6. Click "ตรวจสอบแล้ว" on a pending item — confirm it moves to the "ตรวจสอบแล้ว" dashboard bucket.
- [ ] **Step 6: Commit**
```bash
git add src/app/feature/budget-request/request-budget/container/request-budget/request-budget.container.ts \
src/app/feature/budget-request/request-budget/container/request-budget/request-budget.container.html \
src/app/feature/budget-request/request-budget/presenter/list/request-budget-list/request-budget-list.component.ts \
src/app/feature/budget-request/request-budget/presenter/list/request-budget-list/request-budget-list.component.html
git commit -m "feat: wire self-service approvers (key=1) + review stage (key=3) into request-budget pages"
```
---
## Explicitly out of scope (YAGNI, matching the income_budget_review precedent's own scope cuts)
- Real-time/email/LINE notifications — dashboard loads on page entry, same as `income_budget_review`.
- Fine-grained reviewer permissions beyond "can access this menu" — matches the existing `income_budget_review` scope cut exactly.
- Editing the underlying budget numbers from `request-budget-plan-01` — this plan only adds status display + status-changing buttons, not a data-editing UI.
- Migrating or touching the existing `type_id=1`/code `"03"` admin-config approval path — left completely untouched; historical records using it are unaffected.