Files
rmutr/docs/superpowers/plans/2026-06-17-disbursement-plan-research.md
2026-06-17 12:08:13 +07:00

26 KiB

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-researchrequest-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:

        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:

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
cd rmutr-api
dotnet build
dotnet ef database update

Expected: build สำเร็จ, migration applied ไม่มี error

  • Step 4: commit
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:

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:

<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:

.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
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:

@Output() onDisbursementPlan = new EventEmitter<any>();

เพิ่ม method ใน class:

openDisbursement(val: any) {
  this.onDisbursementPlan.emit(val);
}
  • Step 2: เพิ่มคอลัมน์ + ปุ่มใน list20-research.component.html

2a) เพิ่ม col ใน colgroup — เพิ่มก่อน <col style="width:60px"> (คอลัมน์เครื่องมือเดิม):

<col style="width:80px">

2b) เพิ่ม th "แผนเบิกจ่าย" ใน header row ที่ 1 ก่อน <th class="th_class" rowspan="5">เครื่องมือ</th>:

<th class="th_class" rowspan="5">แผน<br>เบิกจ่าย</th>

2c) เพิ่ม td badge + ปุ่มใน tbody — ใน block <ng-container *ngIf="pIdx === 0"> ที่ห่อ td เครื่องมือ เพิ่ม td badge ก่อน td เครื่องมือ:

<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 ที่มีอยู่แล้ว:

<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():

@Output() onDisbursementPlan = new EventEmitter<any>();

เพิ่ม relay method ใน class (ต่อจาก onEditAgencyReportResearch_):

onDisbursementPlan_(val: any) {
  this.onDisbursementPlan.emit(val);
}
  • Step 4: bind event ใน request-budget-statistics-list.component.html

เปิด request-budget-statistics-list.component.html หา block:

<app-list20-research
    [dataSource]="dataSource20Research"
    (onedit)="onEditAgencyReportResearch_($event)"
    (onchange)="change($event)"
></app-list20-research>

แก้เป็น:

<app-list20-research
    [dataSource]="dataSource20Research"
    (onedit)="onEditAgencyReportResearch_($event)"
    (onDisbursementPlan)="onDisbursementPlan_($event)"
    (onchange)="change($event)"
></app-list20-research>
  • Step 5: commit
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:

import { DisbursementPlanResearchDialogComponent } from '../../presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component';
  • Step 2: เพิ่ม method onDisbursementPlan ใน container.ts

เพิ่ม method ใน class ต่อจาก onEditAgencyReportResearch:

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)"

เพิ่มต่อท้าย (บรรทัดถัดไป):

(onDisbursementPlan)="onDisbursementPlan($event)"
  • Step 4: เพิ่ม import + declare ใน module

เปิด request-budget-statistics.module.ts — เพิ่ม import ต่อจาก SendApproveTargetPopupComponent:

import { DisbursementPlanResearchDialogComponent } from './presenter/popup/disbursement-plan-research-dialog/disbursement-plan-research-dialog.component';

ใน declarations array — เพิ่มต่อจาก SendApproveTargetPopupComponent,:

DisbursementPlanResearchDialogComponent,
  • Step 5: build Angular เพื่อตรวจ compile error
cd rmutr-web
ng build --configuration development 2>&1 | tail -20

Expected: ไม่มี error

  • Step 6: commit
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
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 เกิดขึ้น