docs: add disbursement plan research implementation plan
This commit is contained in:
@@ -0,0 +1,641 @@
|
||||
# Disbursement Plan Research 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:** เพิ่มปุ่มไอคอนในตาราง `agency-report-research` (typeUrl=27) ต่อโครงการ เพื่อกรอก/แก้ไข "แผนการเบิกจ่ายเป็นงวด" 3 งวดผ่าน MatDialog popup พร้อมบันทึกลง DB
|
||||
|
||||
**Architecture:** เพิ่ม 6 field ใน `change_project_research_detail` model (backend) + EF Core migration + สร้าง Angular MatDialog component + ต่อ EventEmitter chain จาก `list20-research` → `request-budget-statistics-list` → container
|
||||
|
||||
**Tech Stack:** ASP.NET Core (EF Core migrations), Angular 13+, Angular Material (MatDialog, MatDatepicker), TypeScript, RxJS
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Backend schema: `BUDGET` (PostgreSQL)
|
||||
- Angular ChangeDetectionStrategy: `OnPush` ทุก component ใหม่
|
||||
- PUT endpoint: `PUT /api/request_budget/change_project_research_detail/s` (updateMany)
|
||||
- รวมเบิกจ่ายทั้งสิ้น = คำนวณใน frontend เท่านั้น ไม่เก็บใน DB
|
||||
- ใช้ `SweetalertService` (`swSV`) สำหรับ confirmSave / updateSuccess / errText ทุก save action
|
||||
- ชื่อ dialog selector: `app-disbursement-plan-research-dialog`
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
| File | Action |
|
||||
|------|--------|
|
||||
| `rmutr-api/Modules/RequestBudgets/Databases/Models/change_project_research_detail.cs` | Modify — เพิ่ม 6 field |
|
||||
| `rmutr-api/Migrations/20260617000002_disbursement_plan_research.cs` | Create — EF migration |
|
||||
| `rmutr-web/.../presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.ts` | Create |
|
||||
| `rmutr-web/.../presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.html` | Create |
|
||||
| `rmutr-web/.../presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.scss` | Create |
|
||||
| `rmutr-web/.../list20-research/list20-research.component.ts` | Modify — เพิ่ม Output |
|
||||
| `rmutr-web/.../list20-research/list20-research.component.html` | Modify — เพิ่มคอลัมน์ + ปุ่ม |
|
||||
| `rmutr-web/.../request-budget-statistics-list.component.ts` | Modify — เพิ่ม Output + relay method |
|
||||
| `rmutr-web/.../request-budget-statistics-list.component.html` | Modify — bind event บน app-list20-research |
|
||||
| `rmutr-web/.../request-budget-statistics.container.ts` | Modify — เพิ่ม handler + เปิด dialog |
|
||||
| `rmutr-web/.../request-budget-statistics.container.html` | Modify — bind event บน list component |
|
||||
| `rmutr-web/.../request-budget-statistics.module.ts` | Modify — import + declare component |
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Backend — เพิ่ม field ใน model + migration
|
||||
|
||||
**Files:**
|
||||
- Modify: `rmutr-api/Modules/RequestBudgets/Databases/Models/change_project_research_detail.cs`
|
||||
- Create: `rmutr-api/Migrations/20260617000002_disbursement_plan_research.cs`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: PUT `/api/request_budget/change_project_research_detail/s` รองรับ `disbursement_1_amount`, `disbursement_1_date`, `disbursement_2_amount`, `disbursement_2_date`, `disbursement_last_amount`, `disbursement_last_date`
|
||||
|
||||
- [ ] **Step 1: เพิ่ม 6 field ใน model**
|
||||
|
||||
เปิด `rmutr-api/Modules/RequestBudgets/Databases/Models/change_project_research_detail.cs`
|
||||
เพิ่ม field ก่อน `[NotMapped]` ท้าย class `change_project_research_detail`:
|
||||
|
||||
```csharp
|
||||
public decimal? disbursement_1_amount { get; set; }
|
||||
public DateTime? disbursement_1_date { get; set; }
|
||||
public decimal? disbursement_2_amount { get; set; }
|
||||
public DateTime? disbursement_2_date { get; set; }
|
||||
public decimal? disbursement_last_amount { get; set; }
|
||||
public DateTime? disbursement_last_date { get; set; }
|
||||
```
|
||||
|
||||
ตำแหน่งที่ถูกต้อง: ก่อนบรรทัด `[NotMapped] public string budget_unit_name_th { get; set; }`
|
||||
|
||||
- [ ] **Step 2: สร้าง migration file**
|
||||
|
||||
สร้างไฟล์ `rmutr-api/Migrations/20260617000002_disbursement_plan_research.cs`:
|
||||
|
||||
```csharp
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace rmutr_budget_api.Migrations
|
||||
{
|
||||
public partial class disbursement_plan_research : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "disbursement_1_amount",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "numeric",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "disbursement_1_date",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "disbursement_2_amount",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "numeric",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "disbursement_2_date",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<decimal>(
|
||||
name: "disbursement_last_amount",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "numeric",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.AddColumn<DateTime>(
|
||||
name: "disbursement_last_date",
|
||||
schema: "BUDGET",
|
||||
table: "change_project_research_detail",
|
||||
type: "timestamp with time zone",
|
||||
nullable: true);
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(name: "disbursement_1_amount", schema: "BUDGET", table: "change_project_research_detail");
|
||||
migrationBuilder.DropColumn(name: "disbursement_1_date", schema: "BUDGET", table: "change_project_research_detail");
|
||||
migrationBuilder.DropColumn(name: "disbursement_2_amount", schema: "BUDGET", table: "change_project_research_detail");
|
||||
migrationBuilder.DropColumn(name: "disbursement_2_date", schema: "BUDGET", table: "change_project_research_detail");
|
||||
migrationBuilder.DropColumn(name: "disbursement_last_amount", schema: "BUDGET", table: "change_project_research_detail");
|
||||
migrationBuilder.DropColumn(name: "disbursement_last_date", schema: "BUDGET", table: "change_project_research_detail");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: build + run migration**
|
||||
|
||||
```bash
|
||||
cd rmutr-api
|
||||
dotnet build
|
||||
dotnet ef database update
|
||||
```
|
||||
|
||||
Expected: build สำเร็จ, migration applied ไม่มี error
|
||||
|
||||
- [ ] **Step 4: commit**
|
||||
|
||||
```bash
|
||||
git add rmutr-api/Modules/RequestBudgets/Databases/Models/change_project_research_detail.cs \
|
||||
rmutr-api/Migrations/20260617000002_disbursement_plan_research.cs
|
||||
git commit -m "feat: add disbursement plan fields to change_project_research_detail"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Frontend — สร้าง DisbursementPlanResearchDialogComponent
|
||||
|
||||
**Files:**
|
||||
- Create: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.ts`
|
||||
- Create: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.html`
|
||||
- Create: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component.scss`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `MAT_DIALOG_DATA` = `change_project_research_detail` row (any) ที่มี field: `change_project_research_detail_uid`, `project_name_th`, `disbursement_1_amount`, `disbursement_1_date`, `disbursement_2_amount`, `disbursement_2_date`, `disbursement_last_amount`, `disbursement_last_date`
|
||||
- Consumes: `ChangeProjectResearchDetailService.updateMany(data[])` → PUT `/api/request_budget/change_project_research_detail/s`
|
||||
- Produces: `dialogRef.close()` หลัง save สำเร็จ
|
||||
|
||||
- [ ] **Step 1: สร้าง TypeScript component**
|
||||
|
||||
สร้างไฟล์ `disbursement-plan-research-dialog.component.ts`:
|
||||
|
||||
```typescript
|
||||
import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef, Inject } from '@angular/core';
|
||||
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
|
||||
import { of } from 'rxjs';
|
||||
import { filter, concatMap, tap, catchError } from 'rxjs/operators';
|
||||
import { ChangeProjectResearchDetailService } from 'src/app/core/service/request-budget/change-project-research-detail.service';
|
||||
import { SweetalertService } from 'src/app/core/service/sweetalert/sweetalert';
|
||||
|
||||
@Component({
|
||||
selector: 'app-disbursement-plan-research-dialog',
|
||||
templateUrl: './disbursement-plan-research-dialog.component.html',
|
||||
styleUrls: ['./disbursement-plan-research-dialog.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DisbursementPlanResearchDialogComponent implements OnInit {
|
||||
|
||||
form: any = {
|
||||
disbursement_1_amount: null,
|
||||
disbursement_1_date: null,
|
||||
disbursement_2_amount: null,
|
||||
disbursement_2_date: null,
|
||||
disbursement_last_amount: null,
|
||||
disbursement_last_date: null,
|
||||
}
|
||||
|
||||
constructor(
|
||||
public dialogRef: MatDialogRef<DisbursementPlanResearchDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: any,
|
||||
private cdRef: ChangeDetectorRef,
|
||||
private swSV: SweetalertService,
|
||||
private changeProjectResearchDetailSV: ChangeProjectResearchDetailService,
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.data) {
|
||||
this.form = {
|
||||
disbursement_1_amount: this.data.disbursement_1_amount ?? null,
|
||||
disbursement_1_date: this.data.disbursement_1_date ?? null,
|
||||
disbursement_2_amount: this.data.disbursement_2_amount ?? null,
|
||||
disbursement_2_date: this.data.disbursement_2_date ?? null,
|
||||
disbursement_last_amount: this.data.disbursement_last_amount ?? null,
|
||||
disbursement_last_date: this.data.disbursement_last_date ?? null,
|
||||
}
|
||||
}
|
||||
this.cdRef.detectChanges()
|
||||
}
|
||||
|
||||
get total(): number {
|
||||
const a1 = parseFloat(this.form.disbursement_1_amount) || 0
|
||||
const a2 = parseFloat(this.form.disbursement_2_amount) || 0
|
||||
const aL = parseFloat(this.form.disbursement_last_amount) || 0
|
||||
return a1 + a2 + aL
|
||||
}
|
||||
|
||||
cancel(): void {
|
||||
this.dialogRef.close()
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const payload = [{
|
||||
...this.data,
|
||||
disbursement_1_amount: this.form.disbursement_1_amount,
|
||||
disbursement_1_date: this.form.disbursement_1_date,
|
||||
disbursement_2_amount: this.form.disbursement_2_amount,
|
||||
disbursement_2_date: this.form.disbursement_2_date,
|
||||
disbursement_last_amount: this.form.disbursement_last_amount,
|
||||
disbursement_last_date: this.form.disbursement_last_date,
|
||||
}]
|
||||
|
||||
this.swSV.confirmSave().pipe(
|
||||
filter(x => x.isConfirmed),
|
||||
concatMap(() => this.changeProjectResearchDetailSV.updateMany(payload).pipe(
|
||||
catchError(err => {
|
||||
this.swSV.errText(err?.error?.description || 'บันทึกไม่สำเร็จ')
|
||||
return of(null)
|
||||
})
|
||||
)),
|
||||
filter(res => res !== null),
|
||||
tap(() => this.swSV.updateSuccess()),
|
||||
tap(() => this.dialogRef.close('saved')),
|
||||
).subscribe()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: สร้าง HTML template**
|
||||
|
||||
สร้างไฟล์ `disbursement-plan-research-dialog.component.html`:
|
||||
|
||||
```html
|
||||
<h2 mat-dialog-title>แผนการเบิกจ่ายเงินเป็นงวด</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<div class="project-info mb-3">
|
||||
<span class="info-label">ชื่อโครงการ:</span>
|
||||
<span class="info-value">{{ data?.project_name_th }}</span>
|
||||
</div>
|
||||
|
||||
<div class="table-scroll">
|
||||
<table class="table-excel" style="table-layout:fixed; min-width:700px;">
|
||||
<colgroup>
|
||||
<col style="width:130px">
|
||||
<col style="width:140px">
|
||||
<col style="width:130px">
|
||||
<col style="width:140px">
|
||||
<col style="width:130px">
|
||||
<col style="width:140px">
|
||||
<col style="width:140px">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr class="tr_class">
|
||||
<th class="th_class" colspan="6">แผนการเบิกจ่ายเงิน</th>
|
||||
<th class="th_class" rowspan="3">รวมเบิกจ่าย<br>ทั้งสิ้น (บาท)</th>
|
||||
</tr>
|
||||
<tr class="tr_class">
|
||||
<th class="th_class" colspan="2">งวดที่ 1</th>
|
||||
<th class="th_class" colspan="2">งวดที่ 2</th>
|
||||
<th class="th_class" colspan="2">งวดสุดท้าย</th>
|
||||
</tr>
|
||||
<tr class="tr_class">
|
||||
<th class="th_class">จำนวนเงิน (บาท)</th>
|
||||
<th class="th_class">ว/ด/ป</th>
|
||||
<th class="th_class">จำนวนเงิน (บาท)</th>
|
||||
<th class="th_class">ว/ด/ป</th>
|
||||
<th class="th_class">จำนวนเงิน (บาท)</th>
|
||||
<th class="th_class">ว/ด/ป</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="tr_p">
|
||||
<td class="td_class">
|
||||
<input type="text" appCurrencyInputMask [(ngModel)]="form.disbursement_1_amount" class="td-input">
|
||||
</td>
|
||||
<td class="td_class">
|
||||
<mat-form-field style="width:100%;">
|
||||
<input matInput (click)="dp1.open()" readonly [matDatepicker]="dp1" [(ngModel)]="form.disbursement_1_date" />
|
||||
<mat-datepicker-toggle matSuffix [for]="dp1"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dp1></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
<td class="td_class">
|
||||
<input type="text" appCurrencyInputMask [(ngModel)]="form.disbursement_2_amount" class="td-input">
|
||||
</td>
|
||||
<td class="td_class">
|
||||
<mat-form-field style="width:100%;">
|
||||
<input matInput (click)="dp2.open()" readonly [matDatepicker]="dp2" [(ngModel)]="form.disbursement_2_date" />
|
||||
<mat-datepicker-toggle matSuffix [for]="dp2"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dp2></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
<td class="td_class">
|
||||
<input type="text" appCurrencyInputMask [(ngModel)]="form.disbursement_last_amount" class="td-input">
|
||||
</td>
|
||||
<td class="td_class">
|
||||
<mat-form-field style="width:100%;">
|
||||
<input matInput (click)="dpL.open()" readonly [matDatepicker]="dpL" [(ngModel)]="form.disbursement_last_date" />
|
||||
<mat-datepicker-toggle matSuffix [for]="dpL"></mat-datepicker-toggle>
|
||||
<mat-datepicker #dpL></mat-datepicker>
|
||||
</mat-form-field>
|
||||
</td>
|
||||
<td class="td_class text-right total-cell">
|
||||
{{ total | number:'1.2-2' }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="total-summary mt-3">
|
||||
<span>รวมเบิกจ่ายทั้งสิ้น:</span>
|
||||
<strong>{{ total | number:'1.2-2' }} บาท</strong>
|
||||
</div>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-stroked-button (click)="cancel()">ยกเลิก</button>
|
||||
<button mat-raised-button class="buttonColorSave ml-2" (click)="save()">บันทึก</button>
|
||||
</mat-dialog-actions>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: สร้าง SCSS**
|
||||
|
||||
สร้างไฟล์ `disbursement-plan-research-dialog.component.scss`:
|
||||
|
||||
```scss
|
||||
.project-info {
|
||||
background: #f3f8ff;
|
||||
border-left: 4px solid #1976d2;
|
||||
padding: 8px 12px;
|
||||
border-radius: 0 4px 4px 0;
|
||||
.info-label { font-size: 12px; color: #666; margin-right: 6px; }
|
||||
.info-value { font-size: 14px; font-weight: 600; color: #1976d2; }
|
||||
}
|
||||
|
||||
.table-scroll { overflow-x: auto; }
|
||||
|
||||
.td-input {
|
||||
width: 100%;
|
||||
border: none;
|
||||
padding: 6px 4px;
|
||||
text-align: right;
|
||||
font-size: 13px;
|
||||
background: transparent;
|
||||
outline: none;
|
||||
font-family: inherit;
|
||||
&:focus { background: #fff8e1; }
|
||||
}
|
||||
|
||||
.total-cell {
|
||||
background: #fff3e0;
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
color: #e65100;
|
||||
}
|
||||
|
||||
.total-summary {
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
strong { color: #e65100; margin-left: 8px; font-size: 16px; }
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: commit**
|
||||
|
||||
```bash
|
||||
git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/popup/disbursement-plan-research-dialog/
|
||||
git commit -m "feat: create DisbursementPlanResearchDialogComponent"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Frontend — ต่อ EventEmitter chain (list → list-wrapper → container)
|
||||
|
||||
**Files:**
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list20-research/list20-research.component.ts`
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/list20-research/list20-research.component.html`
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.ts`
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/request-budget-statistics-list/request-budget-statistics-list.component.html`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `@Output() onDisbursementPlan` emit ค่า `change_project_research_detail` row (any) ทั้ง object
|
||||
|
||||
- [ ] **Step 1: เพิ่ม Output ใน list20-research.component.ts**
|
||||
|
||||
เปิด `list20-research.component.ts` — เพิ่ม `@Output() onDisbursementPlan` ต่อจาก `@Output() onedit`:
|
||||
|
||||
```typescript
|
||||
@Output() onDisbursementPlan = new EventEmitter<any>();
|
||||
```
|
||||
|
||||
เพิ่ม method ใน class:
|
||||
|
||||
```typescript
|
||||
openDisbursement(val: any) {
|
||||
this.onDisbursementPlan.emit(val);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: เพิ่มคอลัมน์ + ปุ่มใน list20-research.component.html**
|
||||
|
||||
**2a) เพิ่ม col ใน colgroup** — เพิ่มก่อน `<col style="width:60px">` (คอลัมน์เครื่องมือเดิม):
|
||||
```html
|
||||
<col style="width:80px">
|
||||
```
|
||||
|
||||
**2b) เพิ่ม th "แผนเบิกจ่าย"** ใน header row ที่ 1 ก่อน `<th class="th_class" rowspan="5">เครื่องมือ</th>`:
|
||||
```html
|
||||
<th class="th_class" rowspan="5">แผน<br>เบิกจ่าย</th>
|
||||
```
|
||||
|
||||
**2c) เพิ่ม td badge + ปุ่มใน tbody** — ใน block `<ng-container *ngIf="pIdx === 0">` ที่ห่อ td เครื่องมือ เพิ่ม td badge ก่อน td เครื่องมือ:
|
||||
|
||||
```html
|
||||
<td class="td_class text-center"
|
||||
[attr.rowspan]="(detail.change_project_research_p_details?.length || 1) + 1">
|
||||
<span *ngIf="detail.disbursement_1_amount"
|
||||
style="background:#e8f5e9;color:#388e3c;padding:2px 6px;border-radius:10px;font-size:11px;white-space:nowrap;">
|
||||
✓ กรอกแล้ว
|
||||
</span>
|
||||
<span *ngIf="!detail.disbursement_1_amount"
|
||||
style="background:#f5f5f5;color:#9e9e9e;padding:2px 6px;border-radius:10px;font-size:11px;white-space:nowrap;">
|
||||
ยังไม่กรอก
|
||||
</span>
|
||||
</td>
|
||||
```
|
||||
|
||||
**2d) เพิ่มปุ่ม 💰 ใน td เครื่องมือ** — เพิ่มหลังปุ่ม edit ที่มีอยู่แล้ว:
|
||||
|
||||
```html
|
||||
<button mat-icon-button
|
||||
[style.color]="detail.disbursement_1_amount ? '#388e3c' : '#81c784'"
|
||||
(click)="openDisbursement(detail)"
|
||||
title="แผนการเบิกจ่าย"
|
||||
style="width:28px;height:28px;line-height:28px;">
|
||||
<mat-icon style="font-size:18px;width:18px;height:18px;line-height:18px;">account_balance_wallet</mat-icon>
|
||||
</button>
|
||||
```
|
||||
|
||||
- [ ] **Step 3: เพิ่ม Output ใน request-budget-statistics-list.component.ts**
|
||||
|
||||
เปิด `request-budget-statistics-list.component.ts` — เพิ่มต่อจาก `@Output() onEditAgencyReportResearch = new EventEmitter()`:
|
||||
|
||||
```typescript
|
||||
@Output() onDisbursementPlan = new EventEmitter<any>();
|
||||
```
|
||||
|
||||
เพิ่ม relay method ใน class (ต่อจาก `onEditAgencyReportResearch_`):
|
||||
|
||||
```typescript
|
||||
onDisbursementPlan_(val: any) {
|
||||
this.onDisbursementPlan.emit(val);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: bind event ใน request-budget-statistics-list.component.html**
|
||||
|
||||
เปิด `request-budget-statistics-list.component.html` หา block:
|
||||
```html
|
||||
<app-list20-research
|
||||
[dataSource]="dataSource20Research"
|
||||
(onedit)="onEditAgencyReportResearch_($event)"
|
||||
(onchange)="change($event)"
|
||||
></app-list20-research>
|
||||
```
|
||||
|
||||
แก้เป็น:
|
||||
```html
|
||||
<app-list20-research
|
||||
[dataSource]="dataSource20Research"
|
||||
(onedit)="onEditAgencyReportResearch_($event)"
|
||||
(onDisbursementPlan)="onDisbursementPlan_($event)"
|
||||
(onchange)="change($event)"
|
||||
></app-list20-research>
|
||||
```
|
||||
|
||||
- [ ] **Step 5: commit**
|
||||
|
||||
```bash
|
||||
git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/presenter/list/
|
||||
git commit -m "feat: add onDisbursementPlan event chain to list20-research"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Frontend — Container handler + Module registration
|
||||
|
||||
**Files:**
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.ts`
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/request-other-expenses/request-budget-statistics.container.html`
|
||||
- Modify: `rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/request-budget-statistics.module.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `onDisbursementPlan` emit จาก `request-budget-statistics-list` (Task 3)
|
||||
- Consumes: `DisbursementPlanResearchDialogComponent` (Task 2)
|
||||
- Consumes: `this.dialog.open(...)` pattern เดิมในไฟล์ container
|
||||
|
||||
- [ ] **Step 1: เพิ่ม import ใน container.ts**
|
||||
|
||||
เปิด `request-budget-statistics.container.ts` — เพิ่ม import ต่อจาก CopyPopupComponent:
|
||||
|
||||
```typescript
|
||||
import { DisbursementPlanResearchDialogComponent } from '../../presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component';
|
||||
```
|
||||
|
||||
- [ ] **Step 2: เพิ่ม method `onDisbursementPlan` ใน container.ts**
|
||||
|
||||
เพิ่ม method ใน class ต่อจาก `onEditAgencyReportResearch`:
|
||||
|
||||
```typescript
|
||||
onDisbursementPlan(detail: any): void {
|
||||
const dialogRef = this.dialog.open(DisbursementPlanResearchDialogComponent, {
|
||||
width: '820px',
|
||||
disableClose: true,
|
||||
data: detail,
|
||||
});
|
||||
dialogRef.afterClosed().pipe(
|
||||
tap((result) => {
|
||||
if (result === 'saved') {
|
||||
this.getAll();
|
||||
this.cdRef.detectChanges();
|
||||
}
|
||||
})
|
||||
).subscribe();
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: bind event ใน container.html**
|
||||
|
||||
เปิด `request-budget-statistics.container.html` — หาบรรทัด:
|
||||
```
|
||||
(onEditAgencyReportResearch)="onEditAgencyReportResearch($event)"
|
||||
```
|
||||
|
||||
เพิ่มต่อท้าย (บรรทัดถัดไป):
|
||||
```html
|
||||
(onDisbursementPlan)="onDisbursementPlan($event)"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: เพิ่ม import + declare ใน module**
|
||||
|
||||
เปิด `request-budget-statistics.module.ts` — เพิ่ม import ต่อจาก `SendApproveTargetPopupComponent`:
|
||||
|
||||
```typescript
|
||||
import { DisbursementPlanResearchDialogComponent } from './presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component';
|
||||
```
|
||||
|
||||
ใน `declarations` array — เพิ่มต่อจาก `SendApproveTargetPopupComponent,`:
|
||||
|
||||
```typescript
|
||||
DisbursementPlanResearchDialogComponent,
|
||||
```
|
||||
|
||||
- [ ] **Step 5: build Angular เพื่อตรวจ compile error**
|
||||
|
||||
```bash
|
||||
cd rmutr-web
|
||||
ng build --configuration development 2>&1 | tail -20
|
||||
```
|
||||
|
||||
Expected: ไม่มี error
|
||||
|
||||
- [ ] **Step 6: commit**
|
||||
|
||||
```bash
|
||||
git add rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/container/ \
|
||||
rmutr-web/src/app/feature/budget-request/request/request-budget-statistics/request-budget-statistics.module.ts
|
||||
git commit -m "feat: wire disbursement plan dialog into container and module"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Manual Verification
|
||||
|
||||
- [ ] **Step 1: start dev server**
|
||||
|
||||
```bash
|
||||
cd rmutr-web
|
||||
ng serve --port 4200
|
||||
```
|
||||
|
||||
- [ ] **Step 2: ไปที่หน้า**
|
||||
|
||||
เปิด `http://localhost:4200/app/agency-report-research`
|
||||
|
||||
- [ ] **Step 3: ตรวจสอบ list**
|
||||
|
||||
- [ ] ตารางมีคอลัมน์ "แผนเบิกจ่าย" แสดง badge "ยังไม่กรอก" สำหรับโครงการที่ยังไม่มีข้อมูล
|
||||
- [ ] มีไอคอน 💰 (account_balance_wallet) ในคอลัมน์เครื่องมือทุกแถว
|
||||
|
||||
- [ ] **Step 4: กรอกข้อมูลใหม่**
|
||||
|
||||
- [ ] กดปุ่ม 💰 บนโครงการใดก็ได้
|
||||
- [ ] Dialog เปิดขึ้น แสดงชื่อโครงการถูกต้อง
|
||||
- [ ] กรอก: งวด 1 = 50000, วันที่ = 2567-11-01, งวด 2 = 30000, วันที่ = 2568-01-15, งวดสุดท้าย = 20000, วันที่ = 2568-03-31
|
||||
- [ ] "รวมเบิกจ่ายทั้งสิ้น" แสดง 100,000.00 ทันที (real-time)
|
||||
- [ ] กด "บันทึก" → confirm dialog → success toast
|
||||
- [ ] ตาราง reload → badge เปลี่ยนเป็น "✓ กรอกแล้ว"
|
||||
|
||||
- [ ] **Step 5: แก้ไขข้อมูล**
|
||||
|
||||
- [ ] กดปุ่ม 💰 อีกครั้ง → dialog เปิดพร้อมข้อมูลเดิม
|
||||
- [ ] แก้ไขงวด 1 เป็น 60000 → รวม = 110,000.00
|
||||
- [ ] กด "บันทึก" → สำเร็จ
|
||||
- [ ] verify ใน DB ว่า `disbursement_1_amount = 60000`
|
||||
|
||||
- [ ] **Step 6: ตรวจสอบ cancel**
|
||||
|
||||
- [ ] กด "ยกเลิก" → dialog ปิด ไม่มี save เกิดขึ้น
|
||||
Reference in New Issue
Block a user