diff --git a/docs/superpowers/plans/2026-07-21-out-of-plan-report-review.md b/docs/superpowers/plans/2026-07-21-out-of-plan-report-review.md
new file mode 100644
index 0000000..45e4d87
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-21-out-of-plan-report-review.md
@@ -0,0 +1,1396 @@
+# Out-of-Plan Report Submit/Review Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Add a "ส่งงานแผน" submit action to the out-of-plan report list (`agency-out-of-plan/out-of-plan`) and a new reviewer menu "ตรวจสอบรายงานผล(นอกแผน)" with 3 status tabs, "ส่งกลับแก้ไข", and "ตรวจสอบผ่าน" actions.
+
+**Architecture:** No new tables/endpoints. Everything hinges on the existing `agency_report.status_id` column (0/null=draft, 1=รอตรวจสอบ, 2=ส่งแก้ไข, 3=ตรวจสอบแล้ว) and the existing `out_of_plan_report_detail_uid` link column on `agency_report`. The reviewer menu mirrors the existing `check-project-report` (`typeUrl == 16`) 3-tab pattern exactly, scoped by filtering `out_of_plan_report_detail_uid` client-side (the generic backend query filter is equality-only — it cannot express "not null" — so both the new list and the pre-existing `check-project-report` list filter client-side with RxJS `map()`). The review form reuses the big shared `agency-report-form.component.ts` (already used by 6+ other typeUrl variants), gated behind new `typeUrl` values (29, 29161, 29162, 29163) so no existing flow is touched. Bulk submit reuses the existing `AgencyReportService.updateStatusAgency()` method (already wired to `POST update_status/1`).
+
+**Tech Stack:** Angular 17, Angular Material (`mat-table`, `mat-tab-group`, `mat-checkbox`), Angular CDK `SelectionModel`, RxJS, SweetAlert2 (via `SweetalertService`).
+
+## Global Constraints
+
+- No backend (`rmutr-api`) changes in this plan — the two endpoints needed (`GET .../agency_report?status_id=N`, `POST .../agency_report/update_status/1`) already exist.
+- This repo has no meaningful unit-test culture for these Angular components (only CLI-boilerplate `.spec.ts` files exist project-wide). Per-task verification uses `ng build --configuration=production` (from `/Users/nut.looknut/Project/rmutr/rmutr-web`) to catch compile/template errors, plus a manual browser check via `npm start` for the final task.
+- Every new branch added to shared files (`request-budget-statistics.container.ts`, `request-budget-statistics-list.component.ts/html`, `agency-report-form.component.ts/html`) must be explicitly guarded by the new `typeUrl` values (29, 29161, 29162, 29163) or by `out_of_plan_report_detail_uid` truthiness — never change behavior for any other `typeUrl`.
+- Follow existing code conventions exactly: raw `Swal.fire(...)` / `this.swSV.confirmSave()` patterns already used in the touched files — don't introduce a new dialog library or pattern.
+- `AgencyReportService.updateStatusAgency(data)` (POST `{fullUrl}/update_status/1`) is the existing bulk-submit method — reuse it, don't add a new API method.
+- Reuse the existing shared search component (`request-budget-statistics.search.component.ts`, fields: `budget_year_name_th`, `project_name_th`, `budget_plan_name_th`, `budget_project_name_th`, `responsible_faculty_name_th`) for the new reviewer menu — do not build new search UI.
+
+---
+
+### Task 1: New routes and menu entry
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/app-routing.module.ts:3011-3016`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/core/data/navigator.ts:1111-1117`
+
+**Interfaces:**
+- Consumes: nothing new
+- Produces: route `type` values `29` (list), `29161`/`29162`/`29163` (review-edit sub-routes) and route paths `check-project-report-out-of-plan`, `agency-report-out-of-plan-edit-wait`, `agency-report-out-of-plan-edit-send`, `agency-report-out-of-plan-edit-pass` — every later task's `typeUrl` checks and `router.navigate()` calls target these exact values/paths.
+
+- [ ] **Step 1: Add the 4 new routes**
+
+Find, in `app-routing.module.ts` (the block right after the existing `agency-report-edit-pass` route):
+```ts
+ {
+ path: 'agency-report-edit-pass',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผลโครงการ`,
+ type: 202163
+ }
+ },
+ {
+ path: 'original-project-proposal1',
+```
+Change to:
+```ts
+ {
+ path: 'agency-report-edit-pass',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผลโครงการ`,
+ type: 202163
+ }
+ },
+ {
+ path: 'check-project-report-out-of-plan',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผล(นอกแผน)`,
+ type: 29
+ }
+ },
+ {
+ path: 'agency-report-out-of-plan-edit-wait',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผล(นอกแผน)`,
+ type: 29161
+ }
+ },
+ {
+ path: 'agency-report-out-of-plan-edit-send',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผล(นอกแผน)`,
+ type: 29162
+ }
+ },
+ {
+ path: 'agency-report-out-of-plan-edit-pass',
+ loadChildren: () => import('./feature/budget-request/request/request-budget-statistics/request-budget-statistics.module')
+ .then(m => m.RequestBudgetStatisticsModule),
+ data: {
+ menuName: `การบริหารและรายงานผล${seperation}ตรวจสอบรายงานผล(นอกแผน)`,
+ type: 29163
+ }
+ },
+ {
+ path: 'original-project-proposal1',
+```
+
+- [ ] **Step 2: Add the menu entry**
+
+Find, in `navigator.ts`:
+```ts
+ {
+ id: 'agency-report-out-of-plan',
+ code: 'administer-011-007-2',
+ title: 'หน่วยงานทำรายงานผล(นอกแผน)',
+ type: 'basic',
+ icon: 'fiber_manual_record',
+ link: '/app/agency-out-of-plan/out-of-plan',
+ },
+ {
+ id: 'check-project-report',
+```
+Change to:
+```ts
+ {
+ id: 'agency-report-out-of-plan',
+ code: 'administer-011-007-2',
+ title: 'หน่วยงานทำรายงานผล(นอกแผน)',
+ type: 'basic',
+ icon: 'fiber_manual_record',
+ link: '/app/agency-out-of-plan/out-of-plan',
+ },
+ {
+ id: 'check-project-report-out-of-plan',
+ code: 'administer-011-007-3',
+ title: 'ตรวจสอบรายงานผล(นอกแผน)',
+ type: 'basic',
+ icon: 'fiber_manual_record',
+ link: '/app/check-project-report-out-of-plan',
+ },
+ {
+ id: 'check-project-report',
+```
+
+- [ ] **Step 3: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 4: Commit**
+
+```bash
+git add rmutr-web/src/app/app-routing.module.ts rmutr-web/src/app/core/data/navigator.ts
+git commit -m "feat: add routes and menu entry for out-of-plan report review"
+```
+
+---
+
+### Task 2: `list29-1`/`list29-2`/`list29-3` components (review tabs)
+
+**Files:**
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-1/list29-1.component.ts`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-1/list29-1.component.html`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-1/list29-1.component.scss`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-2/list29-2.component.ts`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-2/list29-2.component.html`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-3/list29-3.component.ts`
+- Create: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-3/list29-3.component.html`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/request-budget-statistics.module.ts:46,190`
+
+**Interfaces:**
+- Consumes: `@Input() dataSource: any` — an array of `agency_report` rows (set by Task 3's container wiring). Each row has `agency_report_uid`, `budget_year_name_th`, `project_name_th`, `budget_project_name_th`(displayed via `text_6`... actually mirrors `list16-1` exactly, see below), `budget_topic_name_th`, `responsible_faculty_name_th`.
+- Produces: `app-list29-1`, `app-list29-2`, `app-list29-3` selectors — Task 3 wires these into `request-budget-statistics-list.component.html`. Each has `edit(val)` navigating to its dedicated review route from Task 1.
+
+These 3 components are a direct copy of the existing `list16-1`/`list16-2`/`list16-3` pattern (same table shape, same `BaseList`/`updateMatTable` usage), minus the unrelated P/A-send checkbox feature that lives only in `list16-3` (that's a different, unrelated feature bolted onto that specific tab — not part of this workflow). Each adds one static badge column ("นอกแผน") since every row here is out-of-plan by definition (per the approved design spec).
+
+- [ ] **Step 1: Create `list29-1.component.ts`**
+
+```ts
+import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Input, Output, OnChanges, SimpleChanges } from '@angular/core';
+import { PageEvent } from '@angular/material/paginator';
+import { Router } from '@angular/router';
+import { BaseList } from 'src/app/core/base/base-list';
+
+@Component({
+ selector: 'app-list29-1',
+ templateUrl: './list29-1.component.html',
+ styleUrls: ['./list29-1.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class List29_1Component extends BaseList implements OnInit, OnChanges {
+
+ @Input() dataSource: any = [];
+ @Output() onchange = new EventEmitter();
+ @Output() onedit = new EventEmitter();
+ @Output() ondelete = new EventEmitter();
+ @Output() onexcel = new EventEmitter();
+ data
+
+ constructor(
+ public router: Router
+ ) {
+ super();
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if ('dataSource' in changes && changes?.dataSource?.currentValue) {
+ this.data = this.dataSource
+ this.dataSource = this.updateMatTable(this.dataSource ? this.dataSource : []);
+ }
+ }
+
+ ngOnInit(): void {
+ }
+
+ edit(val) {
+ this.router.navigate(['app/agency-report-out-of-plan-edit-wait/edit-report', val.agency_report_uid])
+ }
+
+ delete(val) {
+ this.ondelete.emit(val)
+ }
+
+ change(event: PageEvent) {
+ let page: number = event.pageIndex + 1
+ let table: any = {
+ page: page,
+ size: event.pageSize
+ }
+ this.onchange.emit(table)
+ }
+
+ excel(val) {
+ this.onexcel.emit(val)
+ }
+
+}
+```
+
+- [ ] **Step 2: Create `list29-1.component.html`**
+
+```html
+
+
+
+
+
+
+ ประเภท |
+
+ นอกแผน
+ |
+
+
+ ปีงบประมาณ |
+
+ {{x.budget_year_name_th}}
+ |
+
+
+ ชื่อโครงการ |
+
+ {{x.project_name_th}}
+ |
+
+
+ ผลผลิต |
+
+ {{x.text_6}}
+ |
+
+
+ ด้าน |
+
+ {{x.text_7}}
+ |
+
+
+ ชื่อหน่วยงาน |
+
+ {{x.responsible_faculty_name_th}}
+ |
+
+
+ ดูรายงานผล |
+
+ create
+ |
+
+
+
+
+```
+
+- [ ] **Step 3: Create `list29-1.component.scss`**
+
+```scss
+```
+(empty — no extra styles needed, matches `list16-2`/`list16-3`'s empty/minimal scss)
+
+- [ ] **Step 4: Create `list29-2.component.ts`** (identical to `list29-1` except `edit()` target)
+
+```ts
+import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Input, Output, OnChanges, SimpleChanges } from '@angular/core';
+import { PageEvent } from '@angular/material/paginator';
+import { Router } from '@angular/router';
+import { BaseList } from 'src/app/core/base/base-list';
+
+@Component({
+ selector: 'app-list29-2',
+ templateUrl: './list29-2.component.html',
+ styleUrls: ['./list29-2.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class List29_2Component extends BaseList implements OnInit, OnChanges {
+
+ @Input() dataSource: any = [];
+ @Output() onchange = new EventEmitter();
+ @Output() onedit = new EventEmitter();
+ @Output() ondelete = new EventEmitter();
+ @Output() onexcel = new EventEmitter();
+ data
+
+ constructor(
+ public router: Router
+ ) {
+ super();
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if ('dataSource' in changes && changes?.dataSource?.currentValue) {
+ this.data = this.dataSource
+ this.dataSource = this.updateMatTable(this.dataSource ? this.dataSource : []);
+ }
+ }
+
+ ngOnInit(): void {
+ }
+
+ edit(val) {
+ this.router.navigate(['app/agency-report-out-of-plan-edit-send/edit-report', val.agency_report_uid])
+ }
+
+ delete(val) {
+ this.ondelete.emit(val)
+ }
+
+ change(event: PageEvent) {
+ let page: number = event.pageIndex + 1
+ let table: any = {
+ page: page,
+ size: event.pageSize
+ }
+ this.onchange.emit(table)
+ }
+
+ excel(val) {
+ this.onexcel.emit(val)
+ }
+
+}
+```
+
+- [ ] **Step 5: Create `list29-2.component.scss`**
+
+```scss
+```
+(empty file)
+
+- [ ] **Step 6: Create `list29-2.component.html`** (identical to `list29-1.component.html`)
+
+```html
+
+
+
+
+
+
+ ประเภท |
+
+ นอกแผน
+ |
+
+
+ ปีงบประมาณ |
+
+ {{x.budget_year_name_th}}
+ |
+
+
+ ชื่อโครงการ |
+
+ {{x.project_name_th}}
+ |
+
+
+ ผลผลิต |
+
+ {{x.text_6}}
+ |
+
+
+ ด้าน |
+
+ {{x.text_7}}
+ |
+
+
+ ชื่อหน่วยงาน |
+
+ {{x.responsible_faculty_name_th}}
+ |
+
+
+ ดูรายงานผล |
+
+ create
+ |
+
+
+
+
+```
+
+- [ ] **Step 7: Create `list29-3.component.ts`** (identical, `edit()` targets the "pass" route)
+
+```ts
+import { Component, OnInit, ChangeDetectionStrategy, EventEmitter, Input, Output, OnChanges, SimpleChanges } from '@angular/core';
+import { PageEvent } from '@angular/material/paginator';
+import { Router } from '@angular/router';
+import { BaseList } from 'src/app/core/base/base-list';
+
+@Component({
+ selector: 'app-list29-3',
+ templateUrl: './list29-3.component.html',
+ styleUrls: ['./list29-3.component.scss'],
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class List29_3Component extends BaseList implements OnInit, OnChanges {
+
+ @Input() dataSource: any = [];
+ @Output() onchange = new EventEmitter();
+ @Output() onedit = new EventEmitter();
+ @Output() ondelete = new EventEmitter();
+ @Output() onexcel = new EventEmitter();
+ data
+
+ constructor(
+ public router: Router
+ ) {
+ super();
+ }
+
+ ngOnChanges(changes: SimpleChanges): void {
+ if ('dataSource' in changes && changes?.dataSource?.currentValue) {
+ this.data = this.dataSource
+ this.dataSource = this.updateMatTable(this.dataSource ? this.dataSource : []);
+ }
+ }
+
+ ngOnInit(): void {
+ }
+
+ edit(val) {
+ this.router.navigate(['app/agency-report-out-of-plan-edit-pass/edit-report', val.agency_report_uid])
+ }
+
+ delete(val) {
+ this.ondelete.emit(val)
+ }
+
+ change(event: PageEvent) {
+ let page: number = event.pageIndex + 1
+ let table: any = {
+ page: page,
+ size: event.pageSize
+ }
+ this.onchange.emit(table)
+ }
+
+ excel(val) {
+ this.onexcel.emit(val)
+ }
+
+}
+```
+
+- [ ] **Step 8: Create `list29-3.component.scss`**
+
+```scss
+```
+(empty file)
+
+- [ ] **Step 9: Create `list29-3.component.html`**
+
+```html
+
+
+
+
+
+
+ ประเภท |
+
+ นอกแผน
+ |
+
+
+ ปีงบประมาณ |
+
+ {{x.budget_year_name_th}}
+ |
+
+
+ ชื่อโครงการ |
+
+ {{x.project_name_th}}
+ |
+
+
+ ผลผลิต |
+
+ {{x.text_6}}
+ |
+
+
+ ด้าน |
+
+ {{x.text_7}}
+ |
+
+
+ ชื่อหน่วยงาน |
+
+ {{x.responsible_faculty_name_th}}
+ |
+
+
+ ดูรายงานผล |
+
+ create
+ |
+
+
+
+
+```
+
+- [ ] **Step 10: Register the 3 components in the module**
+
+Find, in `request-budget-statistics.module.ts`:
+```ts
+import { List16_1Component } from './presenter/list/request-budget-statistics-list/list16-1/list16-1.component';
+import { List16_2Component } from './presenter/list/request-budget-statistics-list/list16-2/list16-2.component';
+import { List16_3Component } from './presenter/list/request-budget-statistics-list/list16-3/list16-3.component';
+import { List20Component } from './presenter/list/request-budget-statistics-list/list20/list20.component';
+```
+Change to:
+```ts
+import { List16_1Component } from './presenter/list/request-budget-statistics-list/list16-1/list16-1.component';
+import { List16_2Component } from './presenter/list/request-budget-statistics-list/list16-2/list16-2.component';
+import { List16_3Component } from './presenter/list/request-budget-statistics-list/list16-3/list16-3.component';
+import { List29_1Component } from './presenter/list/request-budget-statistics-list/list29-1/list29-1.component';
+import { List29_2Component } from './presenter/list/request-budget-statistics-list/list29-2/list29-2.component';
+import { List29_3Component } from './presenter/list/request-budget-statistics-list/list29-3/list29-3.component';
+import { List20Component } from './presenter/list/request-budget-statistics-list/list20/list20.component';
+```
+
+Find:
+```ts
+ List16_1Component,
+ List16_2Component,
+ List16_3Component,
+ List20Component,
+```
+Change to:
+```ts
+ List16_1Component,
+ List16_2Component,
+ List16_3Component,
+ List29_1Component,
+ List29_2Component,
+ List29_3Component,
+ List20Component,
+```
+
+- [ ] **Step 11: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 12: Commit**
+
+```bash
+git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-1/ rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-2/ rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list29-3/ rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/request-budget-statistics.module.ts
+git commit -m "feat: add list29-1/2/3 review tab components for out-of-plan reports"
+```
+
+---
+
+### Task 3: Wire the new reviewer list end-to-end + fix `check-project-report` leak
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.ts`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.html`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.ts`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.html`
+
+**Interfaces:**
+- Consumes: `app-list29-1/2/3` selectors (Task 2), route path `check-project-report-out-of-plan` (Task 1, `type: 29`)
+- Produces: `dataSource29_1$/29_2$/29_3$` observables on the container — final task in this chain, nothing downstream depends on these further.
+
+- [ ] **Step 1: Add the 3 new dataSource properties**
+
+Find, in `request-budget-statistics.container.ts`:
+```ts
+ dataSource16_1$ = new Observable()
+ dataSource16_2$ = new Observable()
+ dataSource16_3$ = new Observable()
+ dataSourceBetweenYear$ = new Observable()
+```
+Change to:
+```ts
+ dataSource16_1$ = new Observable()
+ dataSource16_2$ = new Observable()
+ dataSource16_3$ = new Observable()
+ dataSource29_1$ = new Observable()
+ dataSource29_2$ = new Observable()
+ dataSource29_3$ = new Observable()
+ dataSourceBetweenYear$ = new Observable()
+```
+
+- [ ] **Step 2: Fix `getAll()`'s initial (unfiltered) load — exclude out-of-plan from 16, include only out-of-plan in 29**
+
+Find, in `getAll()`:
+```ts
+ this.dataSource16_1$ = this.AgencyReportSV.queryString(`?status_id=1`)
+ this.dataSource16_2$ = this.AgencyReportSV.queryString(`?status_id=2`)
+ this.dataSource16_3$ = this.AgencyReportSV.queryString(`?status_id=3`)
+ this.dataSourceBetweenYear$ = this.requestBudgetSV.queryString(`?is_between_year=true`)
+```
+Change to:
+```ts
+ this.dataSource16_1$ = this.AgencyReportSV.queryString(`?status_id=1`).pipe(map((data: any[]) => (data || []).filter(d => !d.out_of_plan_report_detail_uid)))
+ this.dataSource16_2$ = this.AgencyReportSV.queryString(`?status_id=2`).pipe(map((data: any[]) => (data || []).filter(d => !d.out_of_plan_report_detail_uid)))
+ this.dataSource16_3$ = this.AgencyReportSV.queryString(`?status_id=3`).pipe(map((data: any[]) => (data || []).filter(d => !d.out_of_plan_report_detail_uid)))
+ this.dataSource29_1$ = this.AgencyReportSV.queryString(`?status_id=1`).pipe(map((data: any[]) => (data || []).filter(d => !!d.out_of_plan_report_detail_uid)))
+ this.dataSource29_2$ = this.AgencyReportSV.queryString(`?status_id=2`).pipe(map((data: any[]) => (data || []).filter(d => !!d.out_of_plan_report_detail_uid)))
+ this.dataSource29_3$ = this.AgencyReportSV.queryString(`?status_id=3`).pipe(map((data: any[]) => (data || []).filter(d => !!d.out_of_plan_report_detail_uid)))
+ this.dataSourceBetweenYear$ = this.requestBudgetSV.queryString(`?is_between_year=true`)
+```
+(`map` is already imported at the top of this file — used by many other branches — no new import needed.)
+
+- [ ] **Step 3: Fix the `onsearch()` query branch for `typeUrl == 16` and add the `typeUrl == 29` branch**
+
+Find, in `onsearch()`:
+```ts
+ else if(this.typeUrl == 16){
+ this.dataSource16_1$ = this.AgencyReportSV.queryString(`?status_id=1&${queryStr}`)
+ this.dataSource16_2$ = this.AgencyReportSV.queryString(`?status_id=2&${queryStr}`)
+ this.dataSource16_3$ = this.AgencyReportSV.queryString(`?status_id=3&${queryStr}`)
+
+ }
+```
+Change to:
+```ts
+ else if(this.typeUrl == 16){
+ const excludeOutOfPlan = (data: any[]) => (data || []).filter(d => !d.out_of_plan_report_detail_uid)
+ this.dataSource16_1$ = this.AgencyReportSV.queryString(`?status_id=1&${queryStr}`).pipe(map(excludeOutOfPlan))
+ this.dataSource16_2$ = this.AgencyReportSV.queryString(`?status_id=2&${queryStr}`).pipe(map(excludeOutOfPlan))
+ this.dataSource16_3$ = this.AgencyReportSV.queryString(`?status_id=3&${queryStr}`).pipe(map(excludeOutOfPlan))
+
+ }
+ else if(this.typeUrl == 29){
+ const onlyOutOfPlan = (data: any[]) => (data || []).filter(d => !!d.out_of_plan_report_detail_uid)
+ this.dataSource29_1$ = this.AgencyReportSV.queryString(`?status_id=1&${queryStr}`).pipe(map(onlyOutOfPlan))
+ this.dataSource29_2$ = this.AgencyReportSV.queryString(`?status_id=2&${queryStr}`).pipe(map(onlyOutOfPlan))
+ this.dataSource29_3$ = this.AgencyReportSV.queryString(`?status_id=3&${queryStr}`).pipe(map(onlyOutOfPlan))
+ }
+```
+
+- [ ] **Step 4: Bind the 3 new observables in the container template**
+
+Find, in `request-budget-statistics.container.html`:
+```html
+[dataSource16_1]="dataSource16_1$ | async"
+[dataSource16_2]="dataSource16_2$ | async"
+[dataSource16_3]="dataSource16_3$ | async"
+```
+Change to:
+```html
+[dataSource16_1]="dataSource16_1$ | async"
+[dataSource16_2]="dataSource16_2$ | async"
+[dataSource16_3]="dataSource16_3$ | async"
+[dataSource29_1]="dataSource29_1$ | async"
+[dataSource29_2]="dataSource29_2$ | async"
+[dataSource29_3]="dataSource29_3$ | async"
+```
+
+- [ ] **Step 5: Add the 3 new `@Input()`s to the presenter list component**
+
+Find, in `request-budget-statistics-list.component.ts`:
+```ts
+ @Input() dataSource16_1: any = [];
+ @Input() dataSource16_2: any = [];
+ @Input() dataSource16_3: any = [];
+```
+Change to:
+```ts
+ @Input() dataSource16_1: any = [];
+ @Input() dataSource16_2: any = [];
+ @Input() dataSource16_3: any = [];
+ @Input() dataSource29_1: any = [];
+ @Input() dataSource29_2: any = [];
+ @Input() dataSource29_3: any = [];
+```
+
+- [ ] **Step 6: Add the `typeUrl == 29` tab block to the presenter list template**
+
+Find, in `request-budget-statistics-list.component.html`:
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+Change to:
+```html
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+```
+
+- [ ] **Step 7: Build to confirm no errors**
+
+Run: `cd /Users/nut.looknut/Project/rmutr/rmutr-web && ng build --configuration=production`
+Expected: build succeeds with no new errors.
+
+- [ ] **Step 8: Commit**
+
+```bash
+git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.ts rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.html rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.ts rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.html
+git commit -m "feat: wire out-of-plan reviewer list (typeUrl 29) and stop out-of-plan reports leaking into check-project-report"
+```
+
+---
+
+### Task 4: Report form — lock, badge, review buttons, `close()` routing
+
+**Files:**
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.ts`
+- Modify: `/Users/nut.looknut/Project/rmutr/rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/form/agency-report-form/agency-report-form.component.html`
+
+**Interfaces:**
+- Consumes: `typeUrl` values `29161`/`29162`/`29163` (Task 1), `agency_report.out_of_plan_report_detail_uid`/`status_id` (existing fields, no new backend needed)
+- Produces: `isLocked: boolean` class field — the template reads it to hide the save button and show a locked banner. `close()` now routes `29161/29162/29163` back to `check-project-report-out-of-plan`.
+
+- [ ] **Step 1: Add the `isLocked` field**
+
+Find:
+```ts
+ urlPath
+ typeUrl
+ tabIndex
+```
+Change to:
+```ts
+ urlPath
+ typeUrl
+ tabIndex
+ isLocked: boolean = false
+```
+
+- [ ] **Step 2: Route the 3 new review typeUrls through the existing `typeUrl == 282` load branch, and lock the form when the loaded report is already submitted or done**
+
+Find the full `else if (this.typeUrl == 282)` block:
+```ts
+ // type 282: เปิดฟอร์มจากรายงานที่มีอยู่แล้ว → โหลด agency_report ตรงๆ แล้ว state='edit'
+ else if (this.typeUrl == 282) {
+ this.AgencyReportSV.get(this.uniquekey).pipe(
+ tap((x: any) => {
+ this.form.patchValue(x)
+ this.change_year(x.budget_year_name_th)
+ if (x.agency_report_items) {
+ const formArray = this.form.get('agency_report_items') as FormArray
+ x.agency_report_items.forEach((agency_report_item) => {
+ formArray.push(this.agency_report_item_form(agency_report_item))
+ })
+ }
+ if (x.agency_report_details) {
+ x.agency_report_details.forEach((agency_report_detail) => {
+ let form_agency_report_details = this.form.get('agency_report_details') as FormArray
+ form_agency_report_details.push(this.agency_report_details_form(agency_report_detail))
+ })
+ const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1)
+ if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1)
+ }
+ if (x.agency_report_popularities) {
+ x.agency_report_popularities.forEach((p) => {
+ const arr = this.form.get('agency_report_popularities') as FormArray
+ arr.push(this.fb.group(p))
+ })
+ this.cdRef.detectChanges()
+ }
+ Swal.close()
+ }),
+ catchError(err => {
+ Swal.close()
+ return of(null)
+ })
+ ).subscribe()
+ }
+```
+Change to:
+```ts
+ // type 282/29161/29162/29163: เปิดฟอร์มจากรายงานที่มีอยู่แล้ว → โหลด agency_report ตรงๆ แล้ว state='edit'
+ // (29161/29162/29163 = งานแผนเปิดดูจากเมนู "ตรวจสอบรายงานผล(นอกแผน)" — ใช้ loader เดียวกับ 282)
+ else if (this.typeUrl == 282 || this.typeUrl == 29161 || this.typeUrl == 29162 || this.typeUrl == 29163) {
+ this.AgencyReportSV.get(this.uniquekey).pipe(
+ tap((x: any) => {
+ this.form.patchValue(x)
+ this.change_year(x.budget_year_name_th)
+ if (x.agency_report_items) {
+ const formArray = this.form.get('agency_report_items') as FormArray
+ x.agency_report_items.forEach((agency_report_item) => {
+ formArray.push(this.agency_report_item_form(agency_report_item))
+ })
+ }
+ if (x.agency_report_details) {
+ x.agency_report_details.forEach((agency_report_detail) => {
+ let form_agency_report_details = this.form.get('agency_report_details') as FormArray
+ form_agency_report_details.push(this.agency_report_details_form(agency_report_detail))
+ })
+ const firstIssue = x.agency_report_details.find(d => d.type == 1 && d.text_1)
+ if (firstIssue) this.change_budget_strategy_faculty_strategic_edit(firstIssue.text_1)
+ }
+ if (x.agency_report_popularities) {
+ x.agency_report_popularities.forEach((p) => {
+ const arr = this.form.get('agency_report_popularities') as FormArray
+ arr.push(this.fb.group(p))
+ })
+ this.cdRef.detectChanges()
+ }
+ if (x.out_of_plan_report_detail_uid && (x.status_id === 1 || x.status_id === 3)) {
+ this.isLocked = true
+ this.form.disable()
+ }
+ Swal.close()
+ }),
+ catchError(err => {
+ Swal.close()
+ return of(null)
+ })
+ ).subscribe()
+ }
+```
+
+- [ ] **Step 3: Route `close()` for the 3 new review typeUrls back to the new menu**
+
+Find:
+```ts
+ close() {
+ if(this.typeUrl == 202161 || this.typeUrl == 202163 ){
+ this.router.navigate(['app/check-project-report'])
+ }
+ else if(this.typeUrl == 202162){
+ this.router.navigate(['app/send-edit-agency-report'])
+ }
+ else if(this.typeUrl == 281 || this.typeUrl == 282){
+ this.router.navigate(['app/agency-out-of-plan/out-of-plan'])
+ }
+ else{
+ this.router.navigate(['app/agency-report'])
+ }
+
+ }
+```
+Change to:
+```ts
+ close() {
+ if(this.typeUrl == 202161 || this.typeUrl == 202163 ){
+ this.router.navigate(['app/check-project-report'])
+ }
+ else if(this.typeUrl == 202162){
+ this.router.navigate(['app/send-edit-agency-report'])
+ }
+ else if(this.typeUrl == 29161 || this.typeUrl == 29162 || this.typeUrl == 29163){
+ this.router.navigate(['app/check-project-report-out-of-plan'])
+ }
+ else if(this.typeUrl == 281 || this.typeUrl == 282){
+ this.router.navigate(['app/agency-out-of-plan/out-of-plan'])
+ }
+ else{
+ this.router.navigate(['app/agency-report'])
+ }
+
+ }
+```
+
+- [ ] **Step 4: Add the "โครงการ"/"นอกแผน" badge at the top of the form**
+
+Find, at the very top of `agency-report-form.component.html`:
+```html
+