diff --git a/exthernal-ttc-api/src/controllers/projectAddController.js b/exthernal-ttc-api/src/controllers/projectAddController.js index 3ee66ca..56c3278 100644 --- a/exthernal-ttc-api/src/controllers/projectAddController.js +++ b/exthernal-ttc-api/src/controllers/projectAddController.js @@ -59,7 +59,7 @@ export class projectAdd { prjnam: req.body.request.prjnam, prjusrseq: req.body.request.prjusrseq, prjwntbdg: req.body.request.prjwntbdg, - prjacpbdg: req.body.request.prjacpbdg, + prjacpbdg: '0.00', prjbdgcod: req.body.request.prjbdgcod, prjcomstt: req.body.request.prjcomstt, prjacpdtm: req.body.request.prjacpdtm diff --git a/exthernal-ttc-api/src/services/budgetExpenseService.js b/exthernal-ttc-api/src/services/budgetExpenseService.js index 6efad71..bb8ee77 100644 --- a/exthernal-ttc-api/src/services/budgetExpenseService.js +++ b/exthernal-ttc-api/src/services/budgetExpenseService.js @@ -1,7 +1,7 @@ import { GeneralService } from '../share/generalservice.js'; -import { connection } from '../config/db.js'; +import { connection } from '../config/db.js'; import { sendError } from '../utils/response.js'; -import { getDTM } from '../utils/date.js'; +import { getDTM } from '../utils/date.js'; // ✅ 1. Import ฟังก์ชันกลางเข้ามา export class BudgetExpenseService { @@ -9,7 +9,6 @@ export class BudgetExpenseService { this.generalService = new GeneralService(); } - // อันนี้ใช้ GeneralService ได้เพราะเป็น Query เดียวจบ async getBudgetExpense(database, name) { const sql = ` SELECT trnseq, trnprjnam @@ -23,18 +22,17 @@ export class BudgetExpenseService { // ฟังก์ชันอนุมัติและตัดงบ (Transaction) async approveBudgetExpense(database, projectSeq, expenseList, user) { - // ⚠️ จำเป็นต้องใช้ client โดยตรงเพื่อคุม Transaction (GeneralService ปกติจะใช้ pool ซึ่งอาจเปลี่ยน connection ระหว่างทางได้) const client = await connection.connect(); try { - await client.query('BEGIN'); // เริ่ม Transaction + await client.query('BEGIN'); - const currentDate = getDTM(); + // 2. เรียกใช้ getDTM() แทนโค้ดเดิม + const currentDTM = getDTM(); // ========================================================= // STEP 1: คืนเงินงบประมาณเดิมก่อน (กรณีแก้ไข/ลบรายการ) // ========================================================= - // ดึงรายการเดิมที่เคยบันทึกไว้ของ Project นี้ เพื่อเอามาคืนเงิน const oldExpenses = await client.query( `SELECT trnbdgcod, trnexpbdg FROM ${database}.trnmst WHERE trnprjseq = $1`, [projectSeq] @@ -46,52 +44,45 @@ export class BudgetExpenseService { UPDATE ${database}.bdgmst SET bdgttl = bdgttl + $1, bdgedtdtm = $2 WHERE bdgcod = $3 - `, [oldItem.trnexpbdg, currentDate, oldItem.trnbdgcod]); + `, [oldItem.trnexpbdg, currentDTM, oldItem.trnbdgcod]); // update เวลาแก้ไขล่าสุด } // ========================================================= - // STEP 2: ลบรายการเดิมทิ้งทั้งหมด (Clear Old Transactions) + // STEP 2: ลบรายการเดิมทิ้ง (เพื่อเตรียมลงใหม่) // ========================================================= - // การลบทั้งหมดแล้วลงใหม่ จะครอบคลุมทั้งกรณี แก้ไขยอด, เพิ่มรายการใหม่, และลบรายการออก await client.query(`DELETE FROM ${database}.trnmst WHERE trnprjseq = $1`, [projectSeq]); // ========================================================= - // STEP 3: บันทึกรายการใหม่ และตัดเงินใหม่ + // STEP 3: บันทึกรายการใหม่ และตัดเงินใหม่ // ========================================================= let totalApprovedAmount = 0; - // 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 หรือติดลบ + // 🛑 [Validation] ห้ามยอดเงินเป็น 0 หรือติดลบ if (!expense.amount || Number(expense.amount) <= 0) { - throw new sendError(`ยอดเงินต้องมากกว่า 0 (รายการรหัสงบ: ${expense.bdgcod})`); + return sendError(`ยอดเงินต้องมากกว่า 0 (รายการรหัสงบ: ${expense.bdgcod})`); } - // [Check 2] เช็คก่อนว่ามีรหัสงบประมาณนี้จริงหรือไม่ + // [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) { - // ⚠️ ต้อง throw error เพื่อให้ไปตกที่ catch แล้ว ROLLBACK - throw new sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`); + return sendError(`ไม่พบรหัสงบประมาณ: ${expense.bdgcod} ในระบบ`); } - // 1.1 หา trnseq ล่าสุด + // Get Next Seq const seqRes = await client.query(`SELECT COALESCE(MAX(trnseq), 0) + 1 as nextseq FROM ${database}.trnmst`); const nextTrnSeq = seqRes.rows[0].nextseq; - // แปลงยอดเงินเป็นทศนิยม 2 ตำแหน่งให้ชัวร์ก่อนบันทึก const expenseAmount = Number(expense.amount).toFixed(2); - // 1.3 Insert ลง trnmst (บันทึกยอดที่ตัดในรายการนี้) + // Insert รายการใหม่ (บันทึก currentDTM ลง trnacpdtm) const sqlTrn = ` INSERT INTO ${database}.trnmst (trnseq, trnprjnam, trnprjseq, trnexpbdg, trnbdgcod, trncomstt, trnacpdtm) @@ -101,47 +92,54 @@ export class BudgetExpenseService { nextTrnSeq, projectName, projectSeq, - expenseAmount, //ใช้ค่าที่ format แล้ว + expenseAmount, expense.bdgcod, - currentDate + currentDTM ]); - // 1.4 ตัดเงินจากงบประมาณ (bdgmst) - // Postgres จะจัดการลบเลขทศนิยมให้เอง แต่ส่งค่าที่ถูกต้องไปจะดีที่สุด + // ตัดเงินงบประมาณ (อัปเดตเวลาแก้ไข) const sqlUpdateBdg = ` UPDATE ${database}.bdgmst SET bdgttl = bdgttl - $1, bdgedtdtm = $2 WHERE bdgcod = $3 `; - await client.query(sqlUpdateBdg, [expenseAmount, currentDate, expense.bdgcod]); + await client.query(sqlUpdateBdg, [expenseAmount, currentDTM, expense.bdgcod]); - // สะสมยอดรวม (ระวังเรื่องทศนิยมใน JS) totalApprovedAmount += Number(expense.amount); } - // 2. อัปเดต Project Master (prjmst) - // Format ยอดรวมให้เป็นทศนิยม 2 ตำแหน่งเป๊ะๆ ก่อนบันทึก (เช่น 45000.00) + // --------------------------------------------------------- + // STEP 4: อัปเดต Project Master (เปลี่ยนสถานะตามยอดเงิน) + // --------------------------------------------------------- const formattedTotal = totalApprovedAmount.toFixed(2); + // [UPDATED] กำหนดสถานะ: ถ้ายอดรวมเป็น 0 ให้เป็น UAC (รออนุมัติ), ถ้ามีเงินให้เป็น BAP (อนุมัติแล้ว) + const projectStatus = totalApprovedAmount > 0 ? 'BAP' : 'UAC'; + const sqlUpdatePrj = ` UPDATE ${database}.prjmst SET prjacpbdg = $1, - prjcomstt = 'BAP', - prjacpdtm = $2 - WHERE prjseq = $3 + prjcomstt = $2, + prjacpdtm = $3 + WHERE prjseq = $4 `; - await client.query(sqlUpdatePrj, [formattedTotal, currentDate, projectSeq]); + await client.query(sqlUpdatePrj, [formattedTotal, projectStatus, currentDTM, projectSeq]); - await client.query('COMMIT'); // ยืนยัน (ทุกอย่างจะถูกบันทึกพร้อมกันเมื่อถึงบรรทัดนี้) - return { status: true, msg: 'Budget updated successfully', total: formattedTotal }; + await client.query('COMMIT'); + return { + status: true, + msg: 'Budget updated successfully', + total: formattedTotal, + projectStatus: projectStatus + }; } catch (error) { - await client.query('ROLLBACK'); // ยกเลิกทั้งหมด (ถ้ามี error ข้อมูลจะกลับไปเหมือนเดิมทุกประการ) + await client.query('ROLLBACK'); console.error('Transaction Error:', error); throw error; } finally { - client.release(); // คืน Connection กลับสู่ Pool + client.release(); } } } \ No newline at end of file