diff --git a/exthernal-ttc-api/src/controllers/budgetExpenseController.js b/exthernal-ttc-api/src/controllers/budgetExpenseController.js index 89cf8fe..86d9e8b 100644 --- a/exthernal-ttc-api/src/controllers/budgetExpenseController.js +++ b/exthernal-ttc-api/src/controllers/budgetExpenseController.js @@ -57,11 +57,11 @@ export class BudgetExpenseController { // ถ้าเจอ Project (aryResult.length >= 1) if (aryResult.length > 0) { - // [Check 1] เช็คว่าโครงการนี้อนุมัติไปแล้วหรือยัง? - const project = aryResult[0]; - if (project.prjcomstt === 'BAP') { - return sendError('โครงการนี้ได้รับการอนุมัติงบประมาณไปแล้ว ไม่สามารถทำรายการซ้ำได้', 'Project Already Approved'); - } + // // [Check 1] เช็คว่าโครงการนี้อนุมัติไปแล้วหรือยัง? + // const project = aryResult[0]; + // if (project.prjcomstt === 'BAP') { + // return sendError('โครงการนี้ได้รับการอนุมัติงบประมาณไปแล้ว ไม่สามารถทำรายการซ้ำได้', 'Project Already Approved'); + // } // เรียก makeArySave เพื่อทำการตัดงบ const promise = await this.makeArySave(req, database); diff --git a/exthernal-ttc-api/src/controllers/projectSearchController.js b/exthernal-ttc-api/src/controllers/projectSearchController.js index 7b7376b..3aed154 100644 --- a/exthernal-ttc-api/src/controllers/projectSearchController.js +++ b/exthernal-ttc-api/src/controllers/projectSearchController.js @@ -40,18 +40,31 @@ export class projectSearch { // เรียก Service ตัวเดิม (Simple) aryResult = await this.projectSearchService.getProjectSearch(database, column, condition); - } else if (columnParams == 'result' || columnParams == undefined || columnParams == '') { - condition['prjseq'] = req.body.request.prjseq + } if (columnParams == 'result' || columnParams == undefined || columnParams == '') { + + // กำหนดเงื่อนไข (ถ้ามีส่งมา) + condition['prjseq'] = req.body.request.prjseq; + + // สร้าง Column String ที่มี Subquery ดึงงบจาก trnmst let column = ` - prjseq, - prjnam, - usrthinam as prjusrnam, - prjwntbdg, - bdgnam, - bdgcod, - prjacpbdg, - ${database}.translatedtl('COMSTT', prjcomstt) as prjcomstt, - prjacpdtm` + prjseq, + prjnam, + usrthinam as prjusrnam, + prjwntbdg, + bdgnam, + + ( + SELECT string_agg(DISTINCT trnbdgcod, ',') + FROM ${database}.trnmst + WHERE trnprjseq = p.prjseq + ) as approved_bdg_codes, + + p.prjacpbdg, + ${database}.translatedtl('COMSTT', prjcomstt) as prjcomstt, + p.prjacpdtm + `; + + // ใช้ Service ตัวใหม่ (Detail Search / Join) aryResult = await this.projectSearchService.getProjectDetailSearch(database, column, condition); } diff --git a/exthernal-ttc-api/src/services/budgetExpenseService.js b/exthernal-ttc-api/src/services/budgetExpenseService.js index 6dd32ae..6efad71 100644 --- a/exthernal-ttc-api/src/services/budgetExpenseService.js +++ b/exthernal-ttc-api/src/services/budgetExpenseService.js @@ -1,6 +1,7 @@ import { GeneralService } from '../share/generalservice.js'; -import { connection } from '../config/db.js'; // ต้อง import connection เพื่อทำ Transaction +import { connection } from '../config/db.js'; import { sendError } from '../utils/response.js'; +import { getDTM } from '../utils/date.js'; export class BudgetExpenseService { @@ -28,31 +29,65 @@ export class BudgetExpenseService { try { await client.query('BEGIN'); // เริ่ม Transaction + const currentDate = getDTM(); + + // ========================================================= + // STEP 1: คืนเงินงบประมาณเดิมก่อน (กรณีแก้ไข/ลบรายการ) + // ========================================================= + // ดึงรายการเดิมที่เคยบันทึกไว้ของ Project นี้ เพื่อเอามาคืนเงิน + const oldExpenses = await client.query( + `SELECT trnbdgcod, trnexpbdg FROM ${database}.trnmst WHERE trnprjseq = $1`, + [projectSeq] + ); + + // วนลูปคืนเงินกลับเข้าตาราง bdgmst (bdgttl + ยอดเดิม) + for (const oldItem of oldExpenses.rows) { + await client.query(` + UPDATE ${database}.bdgmst + SET bdgttl = bdgttl + $1, bdgedtdtm = $2 + WHERE bdgcod = $3 + `, [oldItem.trnexpbdg, currentDate, oldItem.trnbdgcod]); + } + + // ========================================================= + // STEP 2: ลบรายการเดิมทิ้งทั้งหมด (Clear Old Transactions) + // ========================================================= + // การลบทั้งหมดแล้วลงใหม่ จะครอบคลุมทั้งกรณี แก้ไขยอด, เพิ่มรายการใหม่, และลบรายการออก + await client.query(`DELETE FROM ${database}.trnmst WHERE trnprjseq = $1`, [projectSeq]); + + + // ========================================================= + // STEP 3: บันทึกรายการใหม่ และตัดเงินใหม่ + // ========================================================= let totalApprovedAmount = 0; - // Format วันที่: YYYYMMDD - const currentDate = new Date().toISOString().slice(0, 10).replace(/-/g, ''); + + // 1.2 หาชื่อโครงการ (ดึงครั้งเดียวนอกลูปเพื่อประสิทธิภาพ) + const prjRes = await client.query(`SELECT prjnam FROM ${database}.prjmst WHERE prjseq = $1`, [projectSeq]); + if (prjRes.rows.length === 0) throw new Error(`Project ${projectSeq} not found`); + const projectName = prjRes.rows[0].prjnam; // 1. วนลูปตัดงบและบันทึก Transaction for (const expense of expenseList) { // expense: { bdgcod: '33', amount: 5000 } + // [Validation] ห้ามยอดเงินเป็น 0 หรือติดลบ + if (!expense.amount || Number(expense.amount) <= 0) { + throw new sendError(`ยอดเงินต้องมากกว่า 0 (รายการรหัสงบ: ${expense.bdgcod})`); + } + // [Check 2] เช็คก่อนว่ามีรหัสงบประมาณนี้จริงหรือไม่ const checkBdgSql = `SELECT bdgseq FROM ${database}.bdgmst WHERE bdgcod = $1`; const checkBdgRes = await client.query(checkBdgSql, [expense.bdgcod]); if (checkBdgRes.rows.length === 0) { - return sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`, `Cannot Find Budget Code ${expense.bdgcod} in System`); + // ⚠️ ต้อง throw error เพื่อให้ไปตกที่ catch แล้ว ROLLBACK + throw new sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`); } // 1.1 หา trnseq ล่าสุด const seqRes = await client.query(`SELECT COALESCE(MAX(trnseq), 0) + 1 as nextseq FROM ${database}.trnmst`); const nextTrnSeq = seqRes.rows[0].nextseq; - // 1.2 หาชื่อโครงการ - const prjRes = await client.query(`SELECT prjnam FROM ${database}.prjmst WHERE prjseq = $1`, [projectSeq]); - if (prjRes.rows.length === 0) throw new Error(`Project ${projectSeq} not found`); - const projectName = prjRes.rows[0].prjnam; - // แปลงยอดเงินเป็นทศนิยม 2 ตำแหน่งให้ชัวร์ก่อนบันทึก const expenseAmount = Number(expense.amount).toFixed(2); @@ -99,7 +134,7 @@ export class BudgetExpenseService { await client.query(sqlUpdatePrj, [formattedTotal, currentDate, projectSeq]); await client.query('COMMIT'); // ยืนยัน (ทุกอย่างจะถูกบันทึกพร้อมกันเมื่อถึงบรรทัดนี้) - return { status: true, total: totalApprovedAmount }; + return { status: true, msg: 'Budget updated successfully', total: formattedTotal }; } catch (error) { await client.query('ROLLBACK'); // ยกเลิกทั้งหมด (ถ้ามี error ข้อมูลจะกลับไปเหมือนเดิมทุกประการ) diff --git a/exthernal-ttc-api/src/utils/date.js b/exthernal-ttc-api/src/utils/date.js new file mode 100644 index 0000000..6921688 --- /dev/null +++ b/exthernal-ttc-api/src/utils/date.js @@ -0,0 +1,16 @@ +/** + * getDTM (Get Date Time) + * คืนค่าเวลาปัจจุบันในรูปแบบ: YYYYMMDDHHmm (12 หลัก) + * ตัวอย่าง: 202511211445 + */ +export function getDTM() { + const now = new Date(); + + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); // เดือนเริ่มที่ 0 ต้อง +1 + const day = String(now.getDate()).padStart(2, '0'); + const hours = String(now.getHours()).padStart(2, '0'); + const minutes = String(now.getMinutes()).padStart(2, '0'); + + return `${year}${month}${day}${hours}${minutes}`; +} \ No newline at end of file