Files
rmutr/docs/superpowers/specs/2026-07-06-between-year-draft-send-design.md
2026-07-06 18:29:15 +07:00

12 KiB

คำขอระหว่างปี — ขั้นตอนบันทึกฉบับร่าง / ส่งกองนโยบายและแผน

บริบท

เมนู "คำขอระหว่างปี" (/budget-web/app/request-budget-between-year, routing type: 21) ปัจจุบันเมื่อกด "บันทึก" (ครั้งแรก) ระบบจะสร้างระเบียน request_budget (is_between_year=true) ด้วย status_id = 0 และ is_examine = true ทันที แล้วพากลับไปหน้ารายการ — ระเบียนนี้จะไปปรากฏในเมนู "ตรวจสอบคำเสนอขอโครงการ" (check-project-proposal, type: 2) แท็บ "จัดการคำเสนอขอ" โดยอัตโนมัติ เพราะแท็บนั้น query request_budget_and_strategic?status_id=1... แต่ทดสอบจริงพบว่าระเบียนที่ status_id=0 จากฟอร์มก็ไปโผล่ที่นั่นแล้ว (มีสถานะ "รอตรวจสอบ") กล่าวคือ ปัจจุบันไม่มีขั้นตอนกลาง — บันทึกปุ๊บ = ส่งงานแผนปั๊บ ผู้ใช้ไม่มีโอกาสร่างทิ้งไว้ก่อน

ผู้ใช้ (เจ้าหน้าที่หน่วยงาน/คณะ ผู้กรอกคำขอ) ต้องการขั้นตอนกลาง: ร่างไว้ก่อน แล้วค่อยเลือกส่งเป็นชุดเมื่อพร้อม

โค้ดที่มีอยู่แล้วบางส่วนสะท้อนว่าเคยมีความตั้งใจทำ flow นี้มาก่อนแต่ทำไม่เสร็จ:

  • list-between-year.component.html มีคอลัมน์สถานะที่ comment ไว้ ตรงกับความหมาย status_id==0 → "ร่าง", status_id==1 (และยังไม่ is_ok/is_modify) → รอดำเนินการ
  • request-budget-statistics.container.tsgetAll() query เริ่มต้นของ dataSourceBetweenYear$ มี filter &status_id=1 ค้างอยู่ (ไม่ตรงกับ onsearch() ที่ไม่กรอง) — เป็น bug ที่ต้องแก้ไปพร้อมกัน มิฉะนั้นร่าง (status_id=0) จะไม่โผล่ในรายการเริ่มต้นเลย

check-project-proposal แท็บ "จัดการคำเสนอขอ" (query status_id=1) เป็นเมนูที่งานแผนใช้ตรวจสอบอยู่แล้ว ไม่ต้องแก้ไขเมนูนั้น — งานนี้ทำเฉพาะฝั่ง "คำขอระหว่างปี" ให้ควบคุมได้ว่าจะยังไม่ส่ง (ร่าง) หรือส่งแล้ว (status_id: 1) เท่านั้น การไปโผล่ที่ check-project-proposal เป็นผลพลอยได้อัตโนมัติจากการเปลี่ยน status_id ซึ่งมีอยู่แล้ว

ขอบเขต

เฉพาะเมนู "คำขอระหว่างปี" (list + form ของ between-year เท่านั้น) ไม่แตะเมนู check-project-proposal หรือเมนูอื่นที่ใช้ container เดียวกัน (request-budget-statistics.container.ts) เกินความจำเป็น (จะเพิ่ม handler ใหม่ 1 ตัวในไฟล์นี้เท่านั้น)

การไหลของสถานะ

status_id is_ok / is_modify ป้ายในรายการ แก้ไข/ลบได้ไหม
0 - ร่าง ได้ปกติ
1 ทั้งคู่ false/null ส่งงานแผน ดูได้อย่างเดียว (form disable, ปุ่มลบปิด)
1 is_modify = true ส่งแก้ไข (ปรับปรุงจากงานแผนผ่านเมนูอื่นอยู่แล้ว นอกขอบเขตนี้)
1 is_ok = true ตรวจสอบแล้ว (นอกขอบเขตนี้)

หมายเหตุ: แถวสถานะ is_modify/is_ok เกิดจากการทำงานของงานแผนในเมนู check-project-proposal ซึ่งมีอยู่แล้วในโค้ดปัจจุบัน งานนี้แค่แสดงป้ายให้ถูกต้องตามข้อมูลจริงในรายการเดียวกัน ไม่ต้องสร้าง logic ใหม่สำหรับสองแถวนี้

การเปลี่ยนแปลงที่ต้องทำ

1. Bug fix: request-budget-statistics.container.tsgetAll()

ลบ &status_id=1 ออกจาก query เริ่มต้นของ dataSourceBetweenYear$ (บรรทัด ~199) ให้เหลือ ?is_between_year=true เท่านั้น เพื่อให้ทั้งร่างและที่ส่งแล้วแสดงในรายการเดียวกัน (ตรงกับพฤติกรรมของ onsearch() ที่มีอยู่แล้ว)

2. ฟอร์ม request-budget-between-year-form.component.ts

  • ปุ่ม "บันทึก" (label ในเทมเพลต) เปลี่ยนเป็น "บันทึกฉบับร่าง"
  • ตอน save() กรณี state === 'add': คง status_id = 0 ไว้ (ร่าง) แต่เอา is_examine = true ออก จาก path นี้ (ย้ายไปตั้งตอนส่งแทน เพราะ "กำลังตรวจสอบ" ควรเป็นจริงก็ต่อเมื่อส่งไปงานแผนแล้วเท่านั้น)
  • ตอนโหลดข้อมูลกรณี state === 'edit' (ใน ngOnInit, หลัง this.form.patchValue(x)): ถ้า x.status_id !== 0 (ถูกส่งไปแล้ว) → เรียก this.form.disable() และตั้ง flag isLocked = true (component property ใหม่)
  • เทมเพลตฟอร์ม: ถ้า isLocked ให้ซ่อนปุ่ม "บันทึกฉบับร่าง" (เหลือแต่ "ยกเลิก"/กลับ) ฟิลด์ทั้งหมด disable อยู่แล้วจากการ form.disable()

3. List list-between-year.component.ts / .html

  • เพิ่ม selection = new SelectionModel<any>(true, []) (import จาก @angular/cdk/collections) ตามแพทเทิร์นเดียวกับ list6/list24
  • เพิ่มคอลัมน์ checkbox หน้าสุด: เช็คได้เฉพาะแถว status_id === 0 (ร่าง) แถวอื่น disable ล่วงหน้า (ไม่ให้เลือกส่งซ้ำ)
  • Uncomment คอลัมน์สถานะเดิม เพิ่มเข้า columns array ของ header/row/footer และแก้ข้อความ:
    • status_id == 0 → badge "ร่าง"
    • is_modify == true → badge "ส่งแก้ไข"
    • is_ok == true → badge "ตรวจสอบแล้ว"
    • status_id == 1 (ไม่เข้าเงื่อนไขบนสองข้อ) → badge "ส่งงานแผน" (เดิม comment ไว้ว่า "รอตรวจสอบ" เปลี่ยนข้อความ)
  • ไอคอน "ลบ" (delete): disable เมื่อ status_id !== 0
  • ไอคอน "แก้ไข" (edit): ไม่ disable — กดได้เสมอ (เข้าไปดู แต่ฟอร์มจะ read-only เองถ้าไม่ใช่ร่าง ตามข้อ 2)
  • เพิ่มปุ่ม toolbar ใหม่ "ส่งกองนโยบายและแผน" ข้าง "เพิ่มคำขอระหว่างปี": disabled เมื่อ selection.selected.length === 0, คลิกแล้ว emit ผ่าน @Output() onsend = new EventEmitter() ส่ง this.selection.selected (array ของแถวที่เลือก)

4. เดินสาย output ผ่านชั้น presenter กลาง

  • request-budget-statistics-list.component.html ส่วน *ngIf="typeUrl == 21": เพิ่ม (onsend)="onsend.emit($event)" (หรือชื่อ output ที่สอดคล้อง) ให้ bubble ต่อไปยัง container
  • request-budget-statistics.container.html: เพิ่ม (onsend)="sendBetweenYearToPlan($event)" บน <app-request-budget-statistics-list>

5. Handler ใหม่ใน request-budget-statistics.container.ts

เมธอดใหม่ sendBetweenYearToPlan(selected: any[]):

  • ถ้า selected.length === 0 return
  • confirm ด้วย this.swSV.confirmSave() (แพทเทิร์นเดียวกับ handler อื่นในไฟล์นี้)
  • เมื่อ confirm: map selected เป็น payload ที่ตั้ง status_id: 1, is_examine: true ทับค่าเดิมของแต่ละแถว แล้วเรียก this.requestBudgetSV.updateMany(payload)
  • สำเร็จ: this.swSV.updateSuccess() แล้ว this.getAll() (หรือเทียบเท่า) เพื่อ refresh dataSourceBetweenYear$, this.cdRef.detectChanges()
  • ผิดพลาด: catchErrorthis.swSV.errText(err?.error?.description || 'ส่งไม่สำเร็จ')

Error handling

  • กดส่งโดยไม่เลือกอะไร: ปุ่มถูก disable ไว้แล้ว ไม่ต้องมี error state เพิ่ม
  • API update ล้มเหลว: แสดง error ผ่าน swSV.errText ตามแพทเทิร์นเดิมของไฟล์ ไม่ rollback selection (ผู้ใช้กดส่งซ้ำได้)
  • แถวที่ถูกล็อกแล้วไม่มีทางถูกส่งซ้ำ เพราะ checkbox ถูก disable ไว้ตั้งแต่ต้น (client-side guard เท่านั้น ไม่ได้เพิ่ม guard ฝั่ง backend เพราะนอกขอบเขตงานนี้)

Testing

  • Manual/browser verification (ตาม superpowers:verify skill ของโปรเจกต์): เพิ่มคำขอระหว่างปีใหม่ → ยืนยันบันทึกเป็น "ร่าง", แก้ไข/ลบได้
  • ติ๊ก checkbox 1-2 แถวร่าง กด "ส่งกองนโยบายและแผน" → ยืนยันเปลี่ยนเป็น "ส่งงานแผน", แก้ไข/ลบไม่ได้ (เข้าไปดูฟอร์ม read-only ได้)
  • ยืนยันรายการที่ส่งแล้วไปปรากฏในเมนู "ตรวจสอบคำเสนอขอโครงการ" แท็บ "จัดการคำเสนอขอ" เหมือนเดิม (ไม่ต้องแก้โค้ดฝั่งนั้น)
  • ยืนยัน checkbox ของแถวที่ส่งแล้วไม่สามารถติ๊กเลือกซ้ำได้